Added multi-env support
This commit is contained in:
@@ -19,6 +19,8 @@ const DEFAULT_SETTINGS = {
|
|||||||
activeApiKeyId: "",
|
activeApiKeyId: "",
|
||||||
apiConfigs: [],
|
apiConfigs: [],
|
||||||
activeApiConfigId: "",
|
activeApiConfigId: "",
|
||||||
|
envConfigs: [],
|
||||||
|
activeEnvConfigId: "",
|
||||||
apiBaseUrl: "https://api.openai.com/v1",
|
apiBaseUrl: "https://api.openai.com/v1",
|
||||||
apiKeyHeader: "Authorization",
|
apiKeyHeader: "Authorization",
|
||||||
apiKeyPrefix: "Bearer ",
|
apiKeyPrefix: "Bearer ",
|
||||||
@@ -188,6 +190,58 @@ chrome.runtime.onInstalled.addListener(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resolvedApiConfigs = updates.apiConfigs || stored.apiConfigs || [];
|
||||||
|
const resolvedActiveApiConfigId =
|
||||||
|
updates.activeApiConfigId ||
|
||||||
|
stored.activeApiConfigId ||
|
||||||
|
resolvedApiConfigs[0]?.id ||
|
||||||
|
"";
|
||||||
|
const hasEnvConfigs =
|
||||||
|
Array.isArray(stored.envConfigs) && stored.envConfigs.length > 0;
|
||||||
|
|
||||||
|
if (!hasEnvConfigs) {
|
||||||
|
const id = crypto?.randomUUID
|
||||||
|
? crypto.randomUUID()
|
||||||
|
: `env-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
||||||
|
updates.envConfigs = [
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
name: "Default",
|
||||||
|
apiConfigId: resolvedActiveApiConfigId,
|
||||||
|
systemPrompt: stored.systemPrompt || DEFAULT_SETTINGS.systemPrompt
|
||||||
|
}
|
||||||
|
];
|
||||||
|
updates.activeEnvConfigId = id;
|
||||||
|
} else {
|
||||||
|
const normalizedEnvs = stored.envConfigs.map((config) => ({
|
||||||
|
...config,
|
||||||
|
apiConfigId: config.apiConfigId || resolvedActiveApiConfigId,
|
||||||
|
systemPrompt: config.systemPrompt ?? ""
|
||||||
|
}));
|
||||||
|
const envNeedsUpdate = normalizedEnvs.some((config, index) => {
|
||||||
|
const original = stored.envConfigs[index];
|
||||||
|
return (
|
||||||
|
config.apiConfigId !== original.apiConfigId ||
|
||||||
|
(config.systemPrompt || "") !== (original.systemPrompt || "")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (envNeedsUpdate) {
|
||||||
|
updates.envConfigs = normalizedEnvs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const envActiveId = updates.activeEnvConfigId || stored.activeEnvConfigId;
|
||||||
|
if (envActiveId) {
|
||||||
|
const exists = stored.envConfigs.some(
|
||||||
|
(config) => config.id === envActiveId
|
||||||
|
);
|
||||||
|
if (!exists) {
|
||||||
|
updates.activeEnvConfigId = stored.envConfigs[0].id;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updates.activeEnvConfigId = stored.envConfigs[0].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.keys(updates).length) {
|
if (Object.keys(updates).length) {
|
||||||
await chrome.storage.local.set(updates);
|
await chrome.storage.local.set(updates);
|
||||||
}
|
}
|
||||||
@@ -307,6 +361,10 @@ async function handleAnalysisRequest(port, payload, signal) {
|
|||||||
safePost(port, { type: "ERROR", message: "Missing request template." });
|
safePost(port, { type: "ERROR", message: "Missing request template." });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (apiKeyHeader && !apiKey) {
|
||||||
|
safePost(port, { type: "ERROR", message: "Missing API key." });
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!apiBaseUrl) {
|
if (!apiBaseUrl) {
|
||||||
safePost(port, { type: "ERROR", message: "Missing API base URL." });
|
safePost(port, { type: "ERROR", message: "Missing API base URL." });
|
||||||
@@ -345,6 +403,10 @@ async function handleAnalysisRequest(port, payload, signal) {
|
|||||||
apiKey,
|
apiKey,
|
||||||
apiUrl,
|
apiUrl,
|
||||||
requestTemplate,
|
requestTemplate,
|
||||||
|
apiKeyHeader,
|
||||||
|
apiKeyPrefix,
|
||||||
|
apiBaseUrl,
|
||||||
|
model,
|
||||||
systemPrompt: systemPrompt || "",
|
systemPrompt: systemPrompt || "",
|
||||||
userMessage,
|
userMessage,
|
||||||
signal,
|
signal,
|
||||||
@@ -511,6 +573,10 @@ async function streamCustomCompletion({
|
|||||||
apiKey,
|
apiKey,
|
||||||
apiUrl,
|
apiUrl,
|
||||||
requestTemplate,
|
requestTemplate,
|
||||||
|
apiKeyHeader,
|
||||||
|
apiKeyPrefix,
|
||||||
|
apiBaseUrl,
|
||||||
|
model,
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
userMessage,
|
userMessage,
|
||||||
signal,
|
signal,
|
||||||
@@ -519,16 +585,24 @@ async function streamCustomCompletion({
|
|||||||
const replacements = {
|
const replacements = {
|
||||||
PROMPT_GOES_HERE: userMessage,
|
PROMPT_GOES_HERE: userMessage,
|
||||||
SYSTEM_PROMPT_GOES_HERE: systemPrompt,
|
SYSTEM_PROMPT_GOES_HERE: systemPrompt,
|
||||||
API_KEY_GOES_HERE: apiKey
|
API_KEY_GOES_HERE: apiKey,
|
||||||
|
MODEL_GOES_HERE: model || "",
|
||||||
|
API_BASE_URL_GOES_HERE: apiBaseUrl || ""
|
||||||
};
|
};
|
||||||
const resolvedUrl = replaceUrlTokens(apiUrl, replacements);
|
const resolvedUrl = replaceUrlTokens(apiUrl, replacements);
|
||||||
const body = buildTemplateBody(requestTemplate, replacements);
|
const body = buildTemplateBody(requestTemplate, replacements);
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
};
|
||||||
|
const authHeader = buildAuthHeader(apiKeyHeader, apiKeyPrefix, apiKey);
|
||||||
|
if (authHeader) {
|
||||||
|
headers[authHeader.name] = authHeader.value;
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(resolvedUrl, {
|
const response = await fetch(resolvedUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers,
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
signal
|
signal
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -433,6 +433,8 @@ async function handleAnalyze() {
|
|||||||
activeApiKeyId = "",
|
activeApiKeyId = "",
|
||||||
apiConfigs = [],
|
apiConfigs = [],
|
||||||
activeApiConfigId = "",
|
activeApiConfigId = "",
|
||||||
|
envConfigs = [],
|
||||||
|
activeEnvConfigId = "",
|
||||||
apiBaseUrl,
|
apiBaseUrl,
|
||||||
apiKeyHeader,
|
apiKeyHeader,
|
||||||
apiKeyPrefix,
|
apiKeyPrefix,
|
||||||
@@ -444,6 +446,8 @@ async function handleAnalyze() {
|
|||||||
"activeApiKeyId",
|
"activeApiKeyId",
|
||||||
"apiConfigs",
|
"apiConfigs",
|
||||||
"activeApiConfigId",
|
"activeApiConfigId",
|
||||||
|
"envConfigs",
|
||||||
|
"activeEnvConfigId",
|
||||||
"apiBaseUrl",
|
"apiBaseUrl",
|
||||||
"apiKeyHeader",
|
"apiKeyHeader",
|
||||||
"apiKeyPrefix",
|
"apiKeyPrefix",
|
||||||
@@ -453,22 +457,28 @@ async function handleAnalyze() {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const resolvedConfigs = Array.isArray(apiConfigs) ? apiConfigs : [];
|
const resolvedConfigs = Array.isArray(apiConfigs) ? apiConfigs : [];
|
||||||
|
const resolvedEnvs = Array.isArray(envConfigs) ? envConfigs : [];
|
||||||
|
const activeEnv =
|
||||||
|
resolvedEnvs.find((entry) => entry.id === activeEnvConfigId) ||
|
||||||
|
resolvedEnvs[0];
|
||||||
|
const resolvedSystemPrompt =
|
||||||
|
activeEnv?.systemPrompt ?? systemPrompt ?? "";
|
||||||
|
const resolvedApiConfigId =
|
||||||
|
activeEnv?.apiConfigId || activeApiConfigId || resolvedConfigs[0]?.id || "";
|
||||||
const activeConfig =
|
const activeConfig =
|
||||||
resolvedConfigs.find((entry) => entry.id === activeApiConfigId) ||
|
resolvedConfigs.find((entry) => entry.id === resolvedApiConfigId) ||
|
||||||
resolvedConfigs[0];
|
resolvedConfigs[0];
|
||||||
|
if (!activeConfig) {
|
||||||
|
setStatus("Add an API configuration in Settings.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
const isAdvanced = Boolean(activeConfig?.advanced);
|
const isAdvanced = Boolean(activeConfig?.advanced);
|
||||||
const resolvedApiUrl = activeConfig?.apiUrl || "";
|
const resolvedApiUrl = activeConfig?.apiUrl || "";
|
||||||
const resolvedTemplate = activeConfig?.requestTemplate || "";
|
const resolvedTemplate = activeConfig?.requestTemplate || "";
|
||||||
const resolvedApiBaseUrl = isAdvanced
|
const resolvedApiBaseUrl = activeConfig?.apiBaseUrl || apiBaseUrl || "";
|
||||||
? ""
|
const resolvedApiKeyHeader = activeConfig?.apiKeyHeader ?? apiKeyHeader ?? "";
|
||||||
: activeConfig?.apiBaseUrl || apiBaseUrl || "";
|
const resolvedApiKeyPrefix = activeConfig?.apiKeyPrefix ?? apiKeyPrefix ?? "";
|
||||||
const resolvedApiKeyHeader = isAdvanced
|
const resolvedModel = activeConfig?.model || model || "";
|
||||||
? ""
|
|
||||||
: activeConfig?.apiKeyHeader ?? apiKeyHeader ?? "";
|
|
||||||
const resolvedApiKeyPrefix = isAdvanced
|
|
||||||
? ""
|
|
||||||
: activeConfig?.apiKeyPrefix ?? apiKeyPrefix ?? "";
|
|
||||||
const resolvedModel = isAdvanced ? "" : activeConfig?.model || model || "";
|
|
||||||
|
|
||||||
const resolvedKeys = Array.isArray(apiKeys) ? apiKeys : [];
|
const resolvedKeys = Array.isArray(apiKeys) ? apiKeys : [];
|
||||||
const resolvedKeyId =
|
const resolvedKeyId =
|
||||||
@@ -485,7 +495,10 @@ async function handleAnalyze() {
|
|||||||
setStatus("Set a request template in Settings.");
|
setStatus("Set a request template in Settings.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (resolvedTemplate.includes("API_KEY_GOES_HERE") && !apiKey) {
|
const needsKey =
|
||||||
|
Boolean(resolvedApiKeyHeader) ||
|
||||||
|
resolvedTemplate.includes("API_KEY_GOES_HERE");
|
||||||
|
if (needsKey && !apiKey) {
|
||||||
setStatus("Add an API key in Settings.");
|
setStatus("Add an API key in Settings.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -524,7 +537,7 @@ async function handleAnalyze() {
|
|||||||
apiKeyHeader: resolvedApiKeyHeader,
|
apiKeyHeader: resolvedApiKeyHeader,
|
||||||
apiKeyPrefix: resolvedApiKeyPrefix,
|
apiKeyPrefix: resolvedApiKeyPrefix,
|
||||||
model: resolvedModel,
|
model: resolvedModel,
|
||||||
systemPrompt: systemPrompt || "",
|
systemPrompt: resolvedSystemPrompt,
|
||||||
resume: resume || "",
|
resume: resume || "",
|
||||||
taskText: task.text || "",
|
taskText: task.text || "",
|
||||||
postingText: state.postingText,
|
postingText: state.postingText,
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ button:active {
|
|||||||
|
|
||||||
.api-key-actions .delete,
|
.api-key-actions .delete,
|
||||||
.api-config-actions .delete,
|
.api-config-actions .delete,
|
||||||
|
.env-config-actions .delete,
|
||||||
.task-actions .delete {
|
.task-actions .delete {
|
||||||
background: #c0392b;
|
background: #c0392b;
|
||||||
border-color: #c0392b;
|
border-color: #c0392b;
|
||||||
@@ -290,6 +291,26 @@ button:active {
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.env-configs {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.env-config-card {
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: var(--card-bg);
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.env-config-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root:not([data-theme]),
|
:root:not([data-theme]),
|
||||||
:root[data-theme="system"] {
|
:root[data-theme="system"] {
|
||||||
|
|||||||
@@ -16,46 +16,6 @@
|
|||||||
<button id="saveBtn" class="accent">Save Settings</button>
|
<button id="saveBtn" class="accent">Save Settings</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<details class="panel">
|
|
||||||
<summary class="panel-summary">
|
|
||||||
<span class="panel-caret" aria-hidden="true">
|
|
||||||
<span class="caret-closed">▸</span>
|
|
||||||
<span class="caret-open">▾</span>
|
|
||||||
</span>
|
|
||||||
<h2>API</h2>
|
|
||||||
</summary>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="row">
|
|
||||||
<div></div>
|
|
||||||
<div class="row-actions">
|
|
||||||
<button id="addApiConfigBtn" class="ghost" type="button">Add Config</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="activeApiConfigSelect">Active config</label>
|
|
||||||
<select id="activeApiConfigSelect"></select>
|
|
||||||
</div>
|
|
||||||
<div id="apiConfigs" class="api-configs"></div>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details class="panel">
|
|
||||||
<summary class="panel-summary">
|
|
||||||
<span class="panel-caret" aria-hidden="true">
|
|
||||||
<span class="caret-closed">▸</span>
|
|
||||||
<span class="caret-open">▾</span>
|
|
||||||
</span>
|
|
||||||
<h2>API KEYS</h2>
|
|
||||||
</summary>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="row">
|
|
||||||
<div></div>
|
|
||||||
<button id="addApiKeyBtn" class="ghost" type="button">Add Key</button>
|
|
||||||
</div>
|
|
||||||
<div id="apiKeys" class="api-keys"></div>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details class="panel">
|
<details class="panel">
|
||||||
<summary class="panel-summary">
|
<summary class="panel-summary">
|
||||||
<span class="panel-caret" aria-hidden="true">
|
<span class="panel-caret" aria-hidden="true">
|
||||||
@@ -82,14 +42,54 @@
|
|||||||
<span class="caret-closed">▸</span>
|
<span class="caret-closed">▸</span>
|
||||||
<span class="caret-open">▾</span>
|
<span class="caret-open">▾</span>
|
||||||
</span>
|
</span>
|
||||||
<h2>System Prompt</h2>
|
<h2>API KEYS</h2>
|
||||||
</summary>
|
</summary>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<textarea
|
<div class="row">
|
||||||
id="systemPrompt"
|
<div></div>
|
||||||
rows="8"
|
<button id="addApiKeyBtn" class="ghost" type="button">Add Key</button>
|
||||||
placeholder="Define tone and standards..."
|
</div>
|
||||||
></textarea>
|
<div id="apiKeys" class="api-keys"></div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details class="panel">
|
||||||
|
<summary class="panel-summary">
|
||||||
|
<span class="panel-caret" aria-hidden="true">
|
||||||
|
<span class="caret-closed">▸</span>
|
||||||
|
<span class="caret-open">▾</span>
|
||||||
|
</span>
|
||||||
|
<h2>API</h2>
|
||||||
|
</summary>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="row">
|
||||||
|
<div></div>
|
||||||
|
<div class="row-actions">
|
||||||
|
<button id="addApiConfigBtn" class="ghost" type="button">Add Config</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="apiConfigs" class="api-configs"></div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details class="panel">
|
||||||
|
<summary class="panel-summary">
|
||||||
|
<span class="panel-caret" aria-hidden="true">
|
||||||
|
<span class="caret-closed">▸</span>
|
||||||
|
<span class="caret-open">▾</span>
|
||||||
|
</span>
|
||||||
|
<h2>Environment</h2>
|
||||||
|
</summary>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="row">
|
||||||
|
<div></div>
|
||||||
|
<button id="addEnvConfigBtn" class="ghost" type="button">Add Config</button>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="activeEnvConfigSelect">Active environment</label>
|
||||||
|
<select id="activeEnvConfigSelect"></select>
|
||||||
|
</div>
|
||||||
|
<div id="envConfigs" class="env-configs"></div>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
const systemPromptInput = document.getElementById("systemPrompt");
|
|
||||||
const resumeInput = document.getElementById("resume");
|
const resumeInput = document.getElementById("resume");
|
||||||
const saveBtn = document.getElementById("saveBtn");
|
const saveBtn = document.getElementById("saveBtn");
|
||||||
const addApiConfigBtn = document.getElementById("addApiConfigBtn");
|
const addApiConfigBtn = document.getElementById("addApiConfigBtn");
|
||||||
const apiConfigsContainer = document.getElementById("apiConfigs");
|
const apiConfigsContainer = document.getElementById("apiConfigs");
|
||||||
const activeApiConfigSelect = document.getElementById("activeApiConfigSelect");
|
|
||||||
const addApiKeyBtn = document.getElementById("addApiKeyBtn");
|
const addApiKeyBtn = document.getElementById("addApiKeyBtn");
|
||||||
const apiKeysContainer = document.getElementById("apiKeys");
|
const apiKeysContainer = document.getElementById("apiKeys");
|
||||||
|
const addEnvConfigBtn = document.getElementById("addEnvConfigBtn");
|
||||||
|
const envConfigsContainer = document.getElementById("envConfigs");
|
||||||
|
const activeEnvConfigSelect = document.getElementById("activeEnvConfigSelect");
|
||||||
const addTaskBtn = document.getElementById("addTaskBtn");
|
const addTaskBtn = document.getElementById("addTaskBtn");
|
||||||
const tasksContainer = document.getElementById("tasks");
|
const tasksContainer = document.getElementById("tasks");
|
||||||
const statusEl = document.getElementById("status");
|
const statusEl = document.getElementById("status");
|
||||||
@@ -17,6 +18,8 @@ const OPENAI_DEFAULTS = {
|
|||||||
apiKeyPrefix: "Bearer "
|
apiKeyPrefix: "Bearer "
|
||||||
};
|
};
|
||||||
const DEFAULT_MODEL = "gpt-4o-mini";
|
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.";
|
||||||
|
|
||||||
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));
|
||||||
@@ -50,6 +53,18 @@ function newApiConfigId() {
|
|||||||
return `config-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
return `config-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function newEnvConfigId() {
|
||||||
|
if (crypto?.randomUUID) return crypto.randomUUID();
|
||||||
|
return `env-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildChatUrlFromBase(baseUrl) {
|
||||||
|
const trimmed = (baseUrl || "").trim().replace(/\/+$/, "");
|
||||||
|
if (!trimmed) return "";
|
||||||
|
if (trimmed.endsWith("/chat/completions")) return trimmed;
|
||||||
|
return `${trimmed}/chat/completions`;
|
||||||
|
}
|
||||||
|
|
||||||
function collectNames(container, selector) {
|
function collectNames(container, selector) {
|
||||||
if (!container) return [];
|
if (!container) return [];
|
||||||
return [...container.querySelectorAll(selector)]
|
return [...container.querySelectorAll(selector)]
|
||||||
@@ -94,10 +109,10 @@ function setApiConfigAdvanced(card, isAdvanced) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const resetBtn = card.querySelector(".reset-openai");
|
const resetBtn = card.querySelector(".reset-openai");
|
||||||
if (resetBtn) resetBtn.disabled = isAdvanced;
|
if (resetBtn) resetBtn.disabled = false;
|
||||||
|
|
||||||
const advancedBtn = card.querySelector(".advanced-toggle");
|
const advancedBtn = card.querySelector(".advanced-toggle");
|
||||||
if (advancedBtn && isAdvanced) advancedBtn.remove();
|
if (advancedBtn) advancedBtn.disabled = isAdvanced;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readApiConfigFromCard(card) {
|
function readApiConfigFromCard(card) {
|
||||||
@@ -165,7 +180,7 @@ function buildApiConfigCard(config) {
|
|||||||
baseField.appendChild(baseInput);
|
baseField.appendChild(baseInput);
|
||||||
|
|
||||||
const headerField = document.createElement("div");
|
const headerField = document.createElement("div");
|
||||||
headerField.className = "field basic-only";
|
headerField.className = "field advanced-only";
|
||||||
const headerLabel = document.createElement("label");
|
const headerLabel = document.createElement("label");
|
||||||
headerLabel.textContent = "API Key Header";
|
headerLabel.textContent = "API Key Header";
|
||||||
const headerInput = document.createElement("input");
|
const headerInput = document.createElement("input");
|
||||||
@@ -177,7 +192,7 @@ function buildApiConfigCard(config) {
|
|||||||
headerField.appendChild(headerInput);
|
headerField.appendChild(headerInput);
|
||||||
|
|
||||||
const prefixField = document.createElement("div");
|
const prefixField = document.createElement("div");
|
||||||
prefixField.className = "field basic-only";
|
prefixField.className = "field advanced-only";
|
||||||
const prefixLabel = document.createElement("label");
|
const prefixLabel = document.createElement("label");
|
||||||
prefixLabel.textContent = "API Key Prefix";
|
prefixLabel.textContent = "API Key Prefix";
|
||||||
const prefixInput = document.createElement("input");
|
const prefixInput = document.createElement("input");
|
||||||
@@ -235,17 +250,28 @@ function buildApiConfigCard(config) {
|
|||||||
|
|
||||||
const actions = document.createElement("div");
|
const actions = document.createElement("div");
|
||||||
actions.className = "api-config-actions";
|
actions.className = "api-config-actions";
|
||||||
if (!isAdvanced) {
|
const advancedBtn = document.createElement("button");
|
||||||
const advancedBtn = document.createElement("button");
|
advancedBtn.type = "button";
|
||||||
advancedBtn.type = "button";
|
advancedBtn.className = "ghost advanced-toggle";
|
||||||
advancedBtn.className = "ghost advanced-toggle";
|
advancedBtn.textContent = "Advanced Mode";
|
||||||
advancedBtn.textContent = "Advanced Mode";
|
advancedBtn.addEventListener("click", () => {
|
||||||
advancedBtn.addEventListener("click", () => {
|
if (card.classList.contains("is-advanced")) return;
|
||||||
setApiConfigAdvanced(card, true);
|
urlInput.value = buildChatUrlFromBase(baseInput.value);
|
||||||
updateApiConfigSelect(activeApiConfigSelect.value);
|
templateInput.value = [
|
||||||
});
|
"{",
|
||||||
actions.appendChild(advancedBtn);
|
` \"model\": \"${modelInput.value || DEFAULT_MODEL}\",`,
|
||||||
}
|
" \"stream\": true,",
|
||||||
|
" \"messages\": [",
|
||||||
|
" { \"role\": \"system\", \"content\": \"SYSTEM_PROMPT_GOES_HERE\" },",
|
||||||
|
" { \"role\": \"user\", \"content\": \"PROMPT_GOES_HERE\" }",
|
||||||
|
" ],",
|
||||||
|
" \"api_key\": \"API_KEY_GOES_HERE\"",
|
||||||
|
"}"
|
||||||
|
].join("\n");
|
||||||
|
setApiConfigAdvanced(card, true);
|
||||||
|
updateEnvApiOptions();
|
||||||
|
});
|
||||||
|
actions.appendChild(advancedBtn);
|
||||||
|
|
||||||
const duplicateBtn = document.createElement("button");
|
const duplicateBtn = document.createElement("button");
|
||||||
duplicateBtn.type = "button";
|
duplicateBtn.type = "button";
|
||||||
@@ -259,7 +285,7 @@ function buildApiConfigCard(config) {
|
|||||||
const newCard = buildApiConfigCard(copy);
|
const newCard = buildApiConfigCard(copy);
|
||||||
card.insertAdjacentElement("afterend", newCard);
|
card.insertAdjacentElement("afterend", newCard);
|
||||||
updateApiConfigKeyOptions();
|
updateApiConfigKeyOptions();
|
||||||
updateApiConfigSelect(newCard.dataset.id);
|
updateEnvApiOptions();
|
||||||
});
|
});
|
||||||
actions.appendChild(duplicateBtn);
|
actions.appendChild(duplicateBtn);
|
||||||
|
|
||||||
@@ -268,14 +294,14 @@ function buildApiConfigCard(config) {
|
|||||||
resetBtn.className = "ghost reset-openai";
|
resetBtn.className = "ghost reset-openai";
|
||||||
resetBtn.textContent = "Reset to OpenAI";
|
resetBtn.textContent = "Reset to OpenAI";
|
||||||
resetBtn.addEventListener("click", () => {
|
resetBtn.addEventListener("click", () => {
|
||||||
if (card.classList.contains("is-advanced")) {
|
|
||||||
setStatus("Advanced mode cannot be reset to OpenAI.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
baseInput.value = OPENAI_DEFAULTS.apiBaseUrl;
|
baseInput.value = OPENAI_DEFAULTS.apiBaseUrl;
|
||||||
headerInput.value = OPENAI_DEFAULTS.apiKeyHeader;
|
headerInput.value = OPENAI_DEFAULTS.apiKeyHeader;
|
||||||
prefixInput.value = OPENAI_DEFAULTS.apiKeyPrefix;
|
prefixInput.value = OPENAI_DEFAULTS.apiKeyPrefix;
|
||||||
updateApiConfigSelect(activeApiConfigSelect.value);
|
modelInput.value = DEFAULT_MODEL;
|
||||||
|
urlInput.value = "";
|
||||||
|
templateInput.value = "";
|
||||||
|
setApiConfigAdvanced(card, false);
|
||||||
|
updateEnvApiOptions();
|
||||||
});
|
});
|
||||||
actions.appendChild(resetBtn);
|
actions.appendChild(resetBtn);
|
||||||
|
|
||||||
@@ -285,11 +311,11 @@ function buildApiConfigCard(config) {
|
|||||||
deleteBtn.textContent = "Delete";
|
deleteBtn.textContent = "Delete";
|
||||||
deleteBtn.addEventListener("click", () => {
|
deleteBtn.addEventListener("click", () => {
|
||||||
card.remove();
|
card.remove();
|
||||||
updateApiConfigSelect(activeApiConfigSelect.value);
|
updateEnvApiOptions();
|
||||||
});
|
});
|
||||||
actions.appendChild(deleteBtn);
|
actions.appendChild(deleteBtn);
|
||||||
|
|
||||||
const updateSelect = () => updateApiConfigSelect(activeApiConfigSelect.value);
|
const updateSelect = () => updateEnvApiOptions();
|
||||||
nameInput.addEventListener("input", updateSelect);
|
nameInput.addEventListener("input", updateSelect);
|
||||||
baseInput.addEventListener("input", updateSelect);
|
baseInput.addEventListener("input", updateSelect);
|
||||||
headerInput.addEventListener("input", updateSelect);
|
headerInput.addEventListener("input", updateSelect);
|
||||||
@@ -318,35 +344,6 @@ function collectApiConfigs() {
|
|||||||
return cards.map((card) => readApiConfigFromCard(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) {
|
function buildApiKeyCard(entry) {
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = "api-key-card";
|
card.className = "api-key-card";
|
||||||
@@ -458,6 +455,170 @@ function updateApiConfigKeyOptions() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildEnvConfigCard(config) {
|
||||||
|
const card = document.createElement("div");
|
||||||
|
card.className = "env-config-card";
|
||||||
|
card.dataset.id = config.id || newEnvConfigId();
|
||||||
|
|
||||||
|
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 = "env-config-name";
|
||||||
|
nameField.appendChild(nameLabel);
|
||||||
|
nameField.appendChild(nameInput);
|
||||||
|
|
||||||
|
const apiField = document.createElement("div");
|
||||||
|
apiField.className = "field";
|
||||||
|
const apiLabel = document.createElement("label");
|
||||||
|
apiLabel.textContent = "API config";
|
||||||
|
const apiSelect = document.createElement("select");
|
||||||
|
apiSelect.className = "env-config-api-select";
|
||||||
|
apiSelect.dataset.preferred = config.apiConfigId || "";
|
||||||
|
apiField.appendChild(apiLabel);
|
||||||
|
apiField.appendChild(apiSelect);
|
||||||
|
|
||||||
|
const promptField = document.createElement("div");
|
||||||
|
promptField.className = "field";
|
||||||
|
const promptLabel = document.createElement("label");
|
||||||
|
promptLabel.textContent = "System prompt";
|
||||||
|
const promptInput = document.createElement("textarea");
|
||||||
|
promptInput.rows = 8;
|
||||||
|
promptInput.value = config.systemPrompt || "";
|
||||||
|
promptInput.className = "env-config-prompt";
|
||||||
|
promptField.appendChild(promptLabel);
|
||||||
|
promptField.appendChild(promptInput);
|
||||||
|
|
||||||
|
const actions = document.createElement("div");
|
||||||
|
actions.className = "env-config-actions";
|
||||||
|
|
||||||
|
const duplicateBtn = document.createElement("button");
|
||||||
|
duplicateBtn.type = "button";
|
||||||
|
duplicateBtn.className = "ghost duplicate";
|
||||||
|
duplicateBtn.textContent = "Duplicate";
|
||||||
|
duplicateBtn.addEventListener("click", () => {
|
||||||
|
const names = collectNames(envConfigsContainer, ".env-config-name");
|
||||||
|
const copy = collectEnvConfigs().find((entry) => entry.id === card.dataset.id) || {
|
||||||
|
id: card.dataset.id,
|
||||||
|
name: nameInput.value || "Default",
|
||||||
|
apiConfigId: apiSelect.value || "",
|
||||||
|
systemPrompt: promptInput.value || ""
|
||||||
|
};
|
||||||
|
const newCard = buildEnvConfigCard({
|
||||||
|
id: newEnvConfigId(),
|
||||||
|
name: ensureUniqueName(`${copy.name || "Default"} Copy`, names),
|
||||||
|
apiConfigId: copy.apiConfigId,
|
||||||
|
systemPrompt: copy.systemPrompt
|
||||||
|
});
|
||||||
|
card.insertAdjacentElement("afterend", newCard);
|
||||||
|
updateEnvApiOptions();
|
||||||
|
updateEnvConfigSelect(newCard.dataset.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteBtn = document.createElement("button");
|
||||||
|
deleteBtn.type = "button";
|
||||||
|
deleteBtn.className = "ghost delete";
|
||||||
|
deleteBtn.textContent = "Delete";
|
||||||
|
deleteBtn.addEventListener("click", () => {
|
||||||
|
card.remove();
|
||||||
|
updateEnvConfigSelect(activeEnvConfigSelect.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
actions.appendChild(duplicateBtn);
|
||||||
|
actions.appendChild(deleteBtn);
|
||||||
|
|
||||||
|
nameInput.addEventListener("input", () =>
|
||||||
|
updateEnvConfigSelect(activeEnvConfigSelect.value)
|
||||||
|
);
|
||||||
|
|
||||||
|
card.appendChild(nameField);
|
||||||
|
card.appendChild(apiField);
|
||||||
|
card.appendChild(promptField);
|
||||||
|
card.appendChild(actions);
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectEnvConfigs() {
|
||||||
|
const cards = [...envConfigsContainer.querySelectorAll(".env-config-card")];
|
||||||
|
return cards.map((card) => {
|
||||||
|
const nameInput = card.querySelector(".env-config-name");
|
||||||
|
const apiSelect = card.querySelector(".env-config-api-select");
|
||||||
|
const promptInput = card.querySelector(".env-config-prompt");
|
||||||
|
return {
|
||||||
|
id: card.dataset.id || newEnvConfigId(),
|
||||||
|
name: (nameInput?.value || "Default").trim(),
|
||||||
|
apiConfigId: apiSelect?.value || "",
|
||||||
|
systemPrompt: (promptInput?.value || "").trim()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateEnvConfigSelect(preferredId) {
|
||||||
|
const configs = collectEnvConfigs();
|
||||||
|
activeEnvConfigSelect.innerHTML = "";
|
||||||
|
|
||||||
|
if (!configs.length) {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = "";
|
||||||
|
option.textContent = "No environments configured";
|
||||||
|
activeEnvConfigSelect.appendChild(option);
|
||||||
|
activeEnvConfigSelect.disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeEnvConfigSelect.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";
|
||||||
|
activeEnvConfigSelect.appendChild(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
activeEnvConfigSelect.value = selectedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateEnvApiOptions() {
|
||||||
|
const apiConfigs = collectApiConfigs();
|
||||||
|
const selects = envConfigsContainer.querySelectorAll(".env-config-api-select");
|
||||||
|
selects.forEach((select) => {
|
||||||
|
const preferred = select.dataset.preferred || select.value;
|
||||||
|
select.innerHTML = "";
|
||||||
|
if (!apiConfigs.length) {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = "";
|
||||||
|
option.textContent = "No API configs configured";
|
||||||
|
select.appendChild(option);
|
||||||
|
select.disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.disabled = false;
|
||||||
|
for (const config of apiConfigs) {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = config.id;
|
||||||
|
option.textContent = config.name || "Default";
|
||||||
|
select.appendChild(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preferred && apiConfigs.some((config) => config.id === preferred)) {
|
||||||
|
select.value = preferred;
|
||||||
|
} else {
|
||||||
|
select.value = apiConfigs[0].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.dataset.preferred = select.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function buildTaskCard(task) {
|
function buildTaskCard(task) {
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = "task-card";
|
card.className = "task-card";
|
||||||
@@ -619,6 +780,8 @@ async function loadSettings() {
|
|||||||
activeApiKeyId = "",
|
activeApiKeyId = "",
|
||||||
apiConfigs = [],
|
apiConfigs = [],
|
||||||
activeApiConfigId = "",
|
activeApiConfigId = "",
|
||||||
|
envConfigs = [],
|
||||||
|
activeEnvConfigId = "",
|
||||||
apiBaseUrl = "",
|
apiBaseUrl = "",
|
||||||
apiKeyHeader = "",
|
apiKeyHeader = "",
|
||||||
apiKeyPrefix = "",
|
apiKeyPrefix = "",
|
||||||
@@ -633,6 +796,8 @@ async function loadSettings() {
|
|||||||
"activeApiKeyId",
|
"activeApiKeyId",
|
||||||
"apiConfigs",
|
"apiConfigs",
|
||||||
"activeApiConfigId",
|
"activeApiConfigId",
|
||||||
|
"envConfigs",
|
||||||
|
"activeEnvConfigId",
|
||||||
"apiBaseUrl",
|
"apiBaseUrl",
|
||||||
"apiKeyHeader",
|
"apiKeyHeader",
|
||||||
"apiKeyPrefix",
|
"apiKeyPrefix",
|
||||||
@@ -643,7 +808,6 @@ async function loadSettings() {
|
|||||||
"theme"
|
"theme"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
systemPromptInput.value = systemPrompt;
|
|
||||||
resumeInput.value = resume;
|
resumeInput.value = resume;
|
||||||
themeSelect.value = theme;
|
themeSelect.value = theme;
|
||||||
applyTheme(theme);
|
applyTheme(theme);
|
||||||
@@ -727,7 +891,57 @@ async function loadSettings() {
|
|||||||
apiConfigsContainer.appendChild(buildApiConfigCard(config));
|
apiConfigsContainer.appendChild(buildApiConfigCard(config));
|
||||||
}
|
}
|
||||||
updateApiConfigKeyOptions();
|
updateApiConfigKeyOptions();
|
||||||
updateApiConfigSelect(resolvedActiveConfigId);
|
|
||||||
|
let resolvedEnvConfigs = Array.isArray(envConfigs) ? envConfigs : [];
|
||||||
|
let resolvedActiveEnvId = activeEnvConfigId;
|
||||||
|
const fallbackApiConfigId =
|
||||||
|
resolvedActiveConfigId || resolvedConfigs[0]?.id || "";
|
||||||
|
|
||||||
|
if (!resolvedEnvConfigs.length) {
|
||||||
|
const migrated = {
|
||||||
|
id: newEnvConfigId(),
|
||||||
|
name: "Default",
|
||||||
|
apiConfigId: fallbackApiConfigId,
|
||||||
|
systemPrompt: systemPrompt || DEFAULT_SYSTEM_PROMPT
|
||||||
|
};
|
||||||
|
resolvedEnvConfigs = [migrated];
|
||||||
|
resolvedActiveEnvId = migrated.id;
|
||||||
|
await chrome.storage.local.set({
|
||||||
|
envConfigs: resolvedEnvConfigs,
|
||||||
|
activeEnvConfigId: resolvedActiveEnvId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const withDefaults = resolvedEnvConfigs.map((config) => ({
|
||||||
|
...config,
|
||||||
|
apiConfigId: config.apiConfigId || fallbackApiConfigId,
|
||||||
|
systemPrompt: config.systemPrompt ?? ""
|
||||||
|
}));
|
||||||
|
const needsUpdate = withDefaults.some((config, index) => {
|
||||||
|
const original = resolvedEnvConfigs[index];
|
||||||
|
return (
|
||||||
|
config.apiConfigId !== original.apiConfigId ||
|
||||||
|
(config.systemPrompt || "") !== (original.systemPrompt || "")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (needsUpdate) {
|
||||||
|
resolvedEnvConfigs = withDefaults;
|
||||||
|
await chrome.storage.local.set({ envConfigs: resolvedEnvConfigs });
|
||||||
|
}
|
||||||
|
const hasActive = resolvedEnvConfigs.some(
|
||||||
|
(config) => config.id === resolvedActiveEnvId
|
||||||
|
);
|
||||||
|
if (!hasActive) {
|
||||||
|
resolvedActiveEnvId = resolvedEnvConfigs[0].id;
|
||||||
|
await chrome.storage.local.set({ activeEnvConfigId: resolvedActiveEnvId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
envConfigsContainer.innerHTML = "";
|
||||||
|
for (const config of resolvedEnvConfigs) {
|
||||||
|
envConfigsContainer.appendChild(buildEnvConfigCard(config));
|
||||||
|
}
|
||||||
|
updateEnvApiOptions();
|
||||||
|
updateEnvConfigSelect(resolvedActiveEnvId);
|
||||||
|
|
||||||
tasksContainer.innerHTML = "";
|
tasksContainer.innerHTML = "";
|
||||||
if (!tasks.length) {
|
if (!tasks.length) {
|
||||||
@@ -748,10 +962,14 @@ async function saveSettings() {
|
|||||||
const tasks = collectTasks();
|
const tasks = collectTasks();
|
||||||
const apiKeys = collectApiKeys();
|
const apiKeys = collectApiKeys();
|
||||||
const apiConfigs = collectApiConfigs();
|
const apiConfigs = collectApiConfigs();
|
||||||
const activeApiConfigId =
|
const envConfigs = collectEnvConfigs();
|
||||||
apiConfigs.find((entry) => entry.id === activeApiConfigSelect.value)?.id ||
|
const activeEnvConfigId =
|
||||||
apiConfigs[0]?.id ||
|
envConfigs.find((entry) => entry.id === activeEnvConfigSelect.value)?.id ||
|
||||||
|
envConfigs[0]?.id ||
|
||||||
"";
|
"";
|
||||||
|
const activeEnv = envConfigs.find((entry) => entry.id === activeEnvConfigId);
|
||||||
|
const activeApiConfigId =
|
||||||
|
activeEnv?.apiConfigId || apiConfigs[0]?.id || "";
|
||||||
const activeConfig = apiConfigs.find((entry) => entry.id === activeApiConfigId);
|
const activeConfig = apiConfigs.find((entry) => entry.id === activeApiConfigId);
|
||||||
const activeApiKeyId =
|
const activeApiKeyId =
|
||||||
activeConfig?.apiKeyId ||
|
activeConfig?.apiKeyId ||
|
||||||
@@ -762,7 +980,9 @@ async function saveSettings() {
|
|||||||
activeApiKeyId,
|
activeApiKeyId,
|
||||||
apiConfigs,
|
apiConfigs,
|
||||||
activeApiConfigId,
|
activeApiConfigId,
|
||||||
systemPrompt: systemPromptInput.value,
|
envConfigs,
|
||||||
|
activeEnvConfigId,
|
||||||
|
systemPrompt: activeEnv?.systemPrompt || "",
|
||||||
resume: resumeInput.value,
|
resume: resumeInput.value,
|
||||||
tasks,
|
tasks,
|
||||||
theme: themeSelect.value
|
theme: themeSelect.value
|
||||||
@@ -821,11 +1041,32 @@ addApiConfigBtn.addEventListener("click", () => {
|
|||||||
apiConfigsContainer.appendChild(newCard);
|
apiConfigsContainer.appendChild(newCard);
|
||||||
}
|
}
|
||||||
updateApiConfigKeyOptions();
|
updateApiConfigKeyOptions();
|
||||||
updateApiConfigSelect(activeApiConfigSelect.value);
|
updateEnvApiOptions();
|
||||||
});
|
});
|
||||||
|
|
||||||
activeApiConfigSelect.addEventListener("change", () => {
|
addEnvConfigBtn.addEventListener("click", () => {
|
||||||
updateApiConfigSelect(activeApiConfigSelect.value);
|
const name = buildUniqueDefaultName(
|
||||||
|
collectNames(envConfigsContainer, ".env-config-name")
|
||||||
|
);
|
||||||
|
const fallbackApiConfigId = collectApiConfigs()[0]?.id || "";
|
||||||
|
const newCard = buildEnvConfigCard({
|
||||||
|
id: newEnvConfigId(),
|
||||||
|
name,
|
||||||
|
apiConfigId: fallbackApiConfigId,
|
||||||
|
systemPrompt: DEFAULT_SYSTEM_PROMPT
|
||||||
|
});
|
||||||
|
const first = envConfigsContainer.firstElementChild;
|
||||||
|
if (first) {
|
||||||
|
envConfigsContainer.insertBefore(newCard, first);
|
||||||
|
} else {
|
||||||
|
envConfigsContainer.appendChild(newCard);
|
||||||
|
}
|
||||||
|
updateEnvApiOptions();
|
||||||
|
updateEnvConfigSelect(newCard.dataset.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
activeEnvConfigSelect.addEventListener("change", () => {
|
||||||
|
updateEnvConfigSelect(activeEnvConfigSelect.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
themeSelect.addEventListener("change", () => applyTheme(themeSelect.value));
|
themeSelect.addEventListener("change", () => applyTheme(themeSelect.value));
|
||||||
|
|||||||
Reference in New Issue
Block a user