dev-multi-env: modularized environments (#1)
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
const extractBtn = document.getElementById("extractBtn");
|
||||
const analyzeBtn = document.getElementById("analyzeBtn");
|
||||
const runBtn = document.getElementById("runBtn");
|
||||
const abortBtn = document.getElementById("abortBtn");
|
||||
const extractRunBtn = document.getElementById("extractRunBtn");
|
||||
const stopRow = document.getElementById("stopRow");
|
||||
const buttonRow = document.querySelector(".button-row");
|
||||
const taskSelect = document.getElementById("taskSelect");
|
||||
const envSelect = document.getElementById("envSelect");
|
||||
const outputEl = document.getElementById("output");
|
||||
const statusEl = document.getElementById("status");
|
||||
const postingCountEl = document.getElementById("postingCount");
|
||||
@@ -16,14 +13,19 @@ const clearOutputBtn = document.getElementById("clearOutputBtn");
|
||||
|
||||
const OUTPUT_STORAGE_KEY = "lastOutput";
|
||||
const AUTO_RUN_KEY = "autoRunDefaultTask";
|
||||
const LAST_TASK_KEY = "lastSelectedTaskId";
|
||||
const LAST_ENV_KEY = "lastSelectedEnvId";
|
||||
|
||||
const state = {
|
||||
postingText: "",
|
||||
tasks: [],
|
||||
envs: [],
|
||||
port: null,
|
||||
isAnalyzing: false,
|
||||
outputRaw: "",
|
||||
autoRunPending: false
|
||||
autoRunPending: false,
|
||||
selectedTaskId: "",
|
||||
selectedEnvId: ""
|
||||
};
|
||||
|
||||
function getStorage(keys) {
|
||||
@@ -245,15 +247,12 @@ function applyTheme(theme) {
|
||||
|
||||
function setAnalyzing(isAnalyzing) {
|
||||
state.isAnalyzing = isAnalyzing;
|
||||
analyzeBtn.disabled = isAnalyzing;
|
||||
runBtn.disabled = isAnalyzing;
|
||||
abortBtn.disabled = !isAnalyzing;
|
||||
extractBtn.disabled = isAnalyzing;
|
||||
extractRunBtn.disabled = isAnalyzing;
|
||||
if (buttonRow && stopRow) {
|
||||
buttonRow.classList.toggle("hidden", isAnalyzing);
|
||||
stopRow.classList.toggle("hidden", !isAnalyzing);
|
||||
}
|
||||
runBtn.classList.toggle("hidden", isAnalyzing);
|
||||
abortBtn.classList.toggle("hidden", !isAnalyzing);
|
||||
updateTaskSelectState();
|
||||
updateEnvSelectState();
|
||||
}
|
||||
|
||||
function updatePostingCount() {
|
||||
@@ -286,11 +285,70 @@ function renderTasks(tasks) {
|
||||
updateTaskSelectState();
|
||||
}
|
||||
|
||||
function renderEnvironments(envs) {
|
||||
state.envs = envs;
|
||||
envSelect.innerHTML = "";
|
||||
|
||||
if (!envs.length) {
|
||||
const option = document.createElement("option");
|
||||
option.textContent = "No environments configured";
|
||||
option.value = "";
|
||||
envSelect.appendChild(option);
|
||||
updateEnvSelectState();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const env of envs) {
|
||||
const option = document.createElement("option");
|
||||
option.value = env.id;
|
||||
option.textContent = env.name || "Default";
|
||||
envSelect.appendChild(option);
|
||||
}
|
||||
updateEnvSelectState();
|
||||
}
|
||||
|
||||
function updateTaskSelectState() {
|
||||
const hasTasks = state.tasks.length > 0;
|
||||
taskSelect.disabled = state.isAnalyzing || !hasTasks;
|
||||
}
|
||||
|
||||
function updateEnvSelectState() {
|
||||
const hasEnvs = state.envs.length > 0;
|
||||
envSelect.disabled = state.isAnalyzing || !hasEnvs;
|
||||
}
|
||||
|
||||
function getTaskDefaultEnvId(task) {
|
||||
return task?.defaultEnvId || state.envs[0]?.id || "";
|
||||
}
|
||||
|
||||
function setEnvironmentSelection(envId) {
|
||||
const target =
|
||||
envId && state.envs.some((env) => env.id === envId)
|
||||
? envId
|
||||
: state.envs[0]?.id || "";
|
||||
if (target) {
|
||||
envSelect.value = target;
|
||||
}
|
||||
state.selectedEnvId = target;
|
||||
}
|
||||
|
||||
function selectTask(taskId, { resetEnv } = { resetEnv: false }) {
|
||||
if (!taskId) return;
|
||||
taskSelect.value = taskId;
|
||||
state.selectedTaskId = taskId;
|
||||
const task = state.tasks.find((item) => item.id === taskId);
|
||||
if (resetEnv) {
|
||||
setEnvironmentSelection(getTaskDefaultEnvId(task));
|
||||
}
|
||||
}
|
||||
|
||||
async function persistSelections() {
|
||||
await chrome.storage.local.set({
|
||||
[LAST_TASK_KEY]: state.selectedTaskId,
|
||||
[LAST_ENV_KEY]: state.selectedEnvId
|
||||
});
|
||||
}
|
||||
|
||||
function isWaterlooWorksUrl(url) {
|
||||
try {
|
||||
return new URL(url).hostname === "waterlooworks.uwaterloo.ca";
|
||||
@@ -377,9 +435,45 @@ function ensurePort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
async function loadTasks() {
|
||||
const { tasks = [] } = await getStorage(["tasks"]);
|
||||
async function loadConfig() {
|
||||
const stored = await getStorage([
|
||||
"tasks",
|
||||
"envConfigs",
|
||||
LAST_TASK_KEY,
|
||||
LAST_ENV_KEY
|
||||
]);
|
||||
const tasks = Array.isArray(stored.tasks) ? stored.tasks : [];
|
||||
const envs = Array.isArray(stored.envConfigs) ? stored.envConfigs : [];
|
||||
renderTasks(tasks);
|
||||
renderEnvironments(envs);
|
||||
|
||||
if (!tasks.length) {
|
||||
state.selectedTaskId = "";
|
||||
state.selectedEnvId = envs[0]?.id || "";
|
||||
return;
|
||||
}
|
||||
|
||||
const storedTaskId = stored[LAST_TASK_KEY];
|
||||
const storedEnvId = stored[LAST_ENV_KEY];
|
||||
const initialTaskId = tasks.some((task) => task.id === storedTaskId)
|
||||
? storedTaskId
|
||||
: tasks[0].id;
|
||||
selectTask(initialTaskId, { resetEnv: false });
|
||||
|
||||
const task = tasks.find((item) => item.id === initialTaskId);
|
||||
if (storedEnvId && envs.some((env) => env.id === storedEnvId)) {
|
||||
setEnvironmentSelection(storedEnvId);
|
||||
} else {
|
||||
setEnvironmentSelection(getTaskDefaultEnvId(task));
|
||||
}
|
||||
|
||||
if (
|
||||
storedTaskId !== state.selectedTaskId ||
|
||||
storedEnvId !== state.selectedEnvId
|
||||
) {
|
||||
await persistSelections();
|
||||
}
|
||||
|
||||
maybeRunDefaultTask();
|
||||
}
|
||||
|
||||
@@ -428,30 +522,96 @@ async function handleAnalyze() {
|
||||
return;
|
||||
}
|
||||
|
||||
const { apiKey, apiBaseUrl, apiKeyHeader, apiKeyPrefix, model, systemPrompt, resume } =
|
||||
await getStorage([
|
||||
"apiKey",
|
||||
"apiBaseUrl",
|
||||
"apiKeyHeader",
|
||||
"apiKeyPrefix",
|
||||
"model",
|
||||
"systemPrompt",
|
||||
"resume"
|
||||
]);
|
||||
const {
|
||||
apiKeys = [],
|
||||
activeApiKeyId = "",
|
||||
apiConfigs = [],
|
||||
activeApiConfigId = "",
|
||||
envConfigs = [],
|
||||
apiBaseUrl,
|
||||
apiKeyHeader,
|
||||
apiKeyPrefix,
|
||||
model,
|
||||
systemPrompt,
|
||||
resume
|
||||
} = await getStorage([
|
||||
"apiKeys",
|
||||
"activeApiKeyId",
|
||||
"apiConfigs",
|
||||
"activeApiConfigId",
|
||||
"envConfigs",
|
||||
"apiBaseUrl",
|
||||
"apiKeyHeader",
|
||||
"apiKeyPrefix",
|
||||
"model",
|
||||
"systemPrompt",
|
||||
"resume"
|
||||
]);
|
||||
|
||||
if (!apiBaseUrl) {
|
||||
setStatus("Set an API base URL in Settings.");
|
||||
const resolvedConfigs = Array.isArray(apiConfigs) ? apiConfigs : [];
|
||||
const resolvedEnvs = Array.isArray(envConfigs) ? envConfigs : [];
|
||||
const selectedEnvId = envSelect.value;
|
||||
const activeEnv =
|
||||
resolvedEnvs.find((entry) => entry.id === selectedEnvId) ||
|
||||
resolvedEnvs[0];
|
||||
if (!activeEnv) {
|
||||
setStatus("Add an environment in Settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (apiKeyHeader && !apiKey) {
|
||||
setStatus("Add your API key in Settings.");
|
||||
const resolvedSystemPrompt =
|
||||
activeEnv.systemPrompt ?? systemPrompt ?? "";
|
||||
const resolvedApiConfigId =
|
||||
activeEnv.apiConfigId || activeApiConfigId || resolvedConfigs[0]?.id || "";
|
||||
const activeConfig =
|
||||
resolvedConfigs.find((entry) => entry.id === resolvedApiConfigId) ||
|
||||
resolvedConfigs[0];
|
||||
if (!activeConfig) {
|
||||
setStatus("Add an API configuration in Settings.");
|
||||
return;
|
||||
}
|
||||
const isAdvanced = Boolean(activeConfig?.advanced);
|
||||
const resolvedApiUrl = activeConfig?.apiUrl || "";
|
||||
const resolvedTemplate = activeConfig?.requestTemplate || "";
|
||||
const resolvedApiBaseUrl = activeConfig?.apiBaseUrl || apiBaseUrl || "";
|
||||
const resolvedApiKeyHeader = activeConfig?.apiKeyHeader ?? apiKeyHeader ?? "";
|
||||
const resolvedApiKeyPrefix = activeConfig?.apiKeyPrefix ?? apiKeyPrefix ?? "";
|
||||
const resolvedModel = activeConfig?.model || model || "";
|
||||
|
||||
if (!model) {
|
||||
setStatus("Set a model name in Settings.");
|
||||
return;
|
||||
const resolvedKeys = Array.isArray(apiKeys) ? apiKeys : [];
|
||||
const resolvedKeyId =
|
||||
activeConfig?.apiKeyId || activeApiKeyId || resolvedKeys[0]?.id || "";
|
||||
const activeKey = resolvedKeys.find((entry) => entry.id === resolvedKeyId);
|
||||
const apiKey = activeKey?.key || "";
|
||||
|
||||
if (isAdvanced) {
|
||||
if (!resolvedApiUrl) {
|
||||
setStatus("Set an API URL in Settings.");
|
||||
return;
|
||||
}
|
||||
if (!resolvedTemplate) {
|
||||
setStatus("Set a request template in Settings.");
|
||||
return;
|
||||
}
|
||||
const needsKey =
|
||||
Boolean(resolvedApiKeyHeader) ||
|
||||
resolvedTemplate.includes("API_KEY_GOES_HERE");
|
||||
if (needsKey && !apiKey) {
|
||||
setStatus("Add an API key in Settings.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!resolvedApiBaseUrl) {
|
||||
setStatus("Set an API base URL in Settings.");
|
||||
return;
|
||||
}
|
||||
if (resolvedApiKeyHeader && !apiKey) {
|
||||
setStatus("Add an API key in Settings.");
|
||||
return;
|
||||
}
|
||||
if (!resolvedModel) {
|
||||
setStatus("Set a model name in Settings.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const promptText = buildUserMessage(resume || "", task.text || "", state.postingText);
|
||||
@@ -467,11 +627,14 @@ async function handleAnalyze() {
|
||||
type: "START_ANALYSIS",
|
||||
payload: {
|
||||
apiKey,
|
||||
apiBaseUrl,
|
||||
apiKeyHeader,
|
||||
apiKeyPrefix,
|
||||
model,
|
||||
systemPrompt: systemPrompt || "",
|
||||
apiMode: isAdvanced ? "advanced" : "basic",
|
||||
apiUrl: resolvedApiUrl,
|
||||
requestTemplate: resolvedTemplate,
|
||||
apiBaseUrl: resolvedApiBaseUrl,
|
||||
apiKeyHeader: resolvedApiKeyHeader,
|
||||
apiKeyPrefix: resolvedApiKeyPrefix,
|
||||
model: resolvedModel,
|
||||
systemPrompt: resolvedSystemPrompt,
|
||||
resume: resume || "",
|
||||
taskText: task.text || "",
|
||||
postingText: state.postingText,
|
||||
@@ -527,20 +690,26 @@ function handleCopyRaw() {
|
||||
void copyTextToClipboard(text, "Markdown");
|
||||
}
|
||||
|
||||
extractBtn.addEventListener("click", handleExtract);
|
||||
analyzeBtn.addEventListener("click", handleAnalyze);
|
||||
extractRunBtn.addEventListener("click", handleExtractAndAnalyze);
|
||||
runBtn.addEventListener("click", handleExtractAndAnalyze);
|
||||
abortBtn.addEventListener("click", handleAbort);
|
||||
settingsBtn.addEventListener("click", () => chrome.runtime.openOptionsPage());
|
||||
copyRenderedBtn.addEventListener("click", handleCopyRendered);
|
||||
copyRawBtn.addEventListener("click", handleCopyRaw);
|
||||
clearOutputBtn.addEventListener("click", () => void handleClearOutput());
|
||||
taskSelect.addEventListener("change", () => {
|
||||
selectTask(taskSelect.value, { resetEnv: true });
|
||||
void persistSelections();
|
||||
});
|
||||
envSelect.addEventListener("change", () => {
|
||||
setEnvironmentSelection(envSelect.value);
|
||||
void persistSelections();
|
||||
});
|
||||
|
||||
updatePostingCount();
|
||||
updatePromptCount(0);
|
||||
renderOutput();
|
||||
setAnalyzing(false);
|
||||
loadTasks();
|
||||
loadConfig();
|
||||
loadTheme();
|
||||
|
||||
async function loadSavedOutput() {
|
||||
@@ -562,7 +731,8 @@ function maybeRunDefaultTask() {
|
||||
if (!state.autoRunPending) return;
|
||||
if (state.isAnalyzing) return;
|
||||
if (!state.tasks.length) return;
|
||||
taskSelect.value = state.tasks[0].id;
|
||||
selectTask(state.tasks[0].id, { resetEnv: true });
|
||||
void persistSelections();
|
||||
state.autoRunPending = false;
|
||||
void handleExtractAndAnalyze();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user