Files
wwcompanion/popup.js

224 lines
5.3 KiB
JavaScript

const extractBtn = document.getElementById("extractBtn");
const analyzeBtn = document.getElementById("analyzeBtn");
const abortBtn = document.getElementById("abortBtn");
const taskSelect = document.getElementById("taskSelect");
const outputEl = document.getElementById("output");
const statusEl = document.getElementById("status");
const postingCountEl = document.getElementById("postingCount");
const promptCountEl = document.getElementById("promptCount");
const settingsBtn = document.getElementById("settingsBtn");
const state = {
postingText: "",
tasks: [],
port: null,
isAnalyzing: false
};
function getStorage(keys) {
return new Promise((resolve) => chrome.storage.local.get(keys, resolve));
}
function buildUserMessage(resume, task, posting) {
return [
"=== RESUME ===",
resume || "",
"",
"=== TASK ===",
task || "",
"",
"=== JOB POSTING ===",
posting || ""
].join("\n");
}
function setStatus(message) {
statusEl.textContent = message;
}
function setAnalyzing(isAnalyzing) {
state.isAnalyzing = isAnalyzing;
analyzeBtn.disabled = isAnalyzing;
abortBtn.disabled = !isAnalyzing;
extractBtn.disabled = isAnalyzing;
}
function updatePostingCount() {
postingCountEl.textContent = `Posting: ${state.postingText.length} chars`;
}
function updatePromptCount(count) {
promptCountEl.textContent = `Prompt: ${count} chars`;
}
function renderTasks(tasks) {
state.tasks = tasks;
taskSelect.innerHTML = "";
if (!tasks.length) {
const option = document.createElement("option");
option.textContent = "No tasks configured";
option.value = "";
taskSelect.appendChild(option);
taskSelect.disabled = true;
return;
}
taskSelect.disabled = false;
for (const task of tasks) {
const option = document.createElement("option");
option.value = task.id;
option.textContent = task.name || "Untitled task";
taskSelect.appendChild(option);
}
}
function sendToActiveTab(message) {
return new Promise((resolve, reject) => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const tab = tabs[0];
if (!tab?.id) {
reject(new Error("No active tab found."));
return;
}
chrome.tabs.sendMessage(tab.id, message, (response) => {
const error = chrome.runtime.lastError;
if (error) {
reject(new Error(error.message));
return;
}
resolve(response);
});
});
});
}
function ensurePort() {
if (state.port) return state.port;
const port = chrome.runtime.connect({ name: "analysis" });
port.onMessage.addListener((message) => {
if (message?.type === "DELTA") {
outputEl.textContent += message.text;
outputEl.scrollTop = outputEl.scrollHeight;
return;
}
if (message?.type === "DONE") {
setAnalyzing(false);
setStatus("Done");
return;
}
if (message?.type === "ABORTED") {
setAnalyzing(false);
setStatus("Aborted.");
return;
}
if (message?.type === "ERROR") {
setAnalyzing(false);
setStatus(message.message || "Error during analysis.");
}
});
port.onDisconnect.addListener(() => {
state.port = null;
setAnalyzing(false);
});
state.port = port;
return port;
}
async function loadTasks() {
const { tasks = [] } = await getStorage(["tasks"]);
renderTasks(tasks);
}
async function handleExtract() {
setStatus("Extracting...");
try {
const response = await sendToActiveTab({ type: "EXTRACT_POSTING" });
if (!response?.ok) {
setStatus(response?.error || "No posting detected.");
return;
}
state.postingText = response.sanitized || "";
updatePostingCount();
updatePromptCount(0);
setStatus("Posting extracted.");
} catch (error) {
setStatus(error.message || "Unable to extract posting.");
}
}
async function handleAnalyze() {
if (!state.postingText) {
setStatus("Extract a job posting first.");
return;
}
const taskId = taskSelect.value;
const task = state.tasks.find((item) => item.id === taskId);
if (!task) {
setStatus("Select a task prompt.");
return;
}
const { apiKey, model, systemPrompt, resume } = await getStorage([
"apiKey",
"model",
"systemPrompt",
"resume"
]);
if (!apiKey) {
setStatus("Add your OpenAI API key in Settings.");
return;
}
if (!model) {
setStatus("Set a model name in Settings.");
return;
}
const promptText = buildUserMessage(resume || "", task.text || "", state.postingText);
updatePromptCount(promptText.length);
outputEl.textContent = "";
setAnalyzing(true);
setStatus("Analyzing...");
const port = ensurePort();
port.postMessage({
type: "START_ANALYSIS",
payload: {
apiKey,
model,
systemPrompt: systemPrompt || "",
resume: resume || "",
taskText: task.text || "",
postingText: state.postingText
}
});
}
function handleAbort() {
if (!state.port) return;
state.port.postMessage({ type: "ABORT_ANALYSIS" });
setAnalyzing(false);
setStatus("Aborted.");
}
extractBtn.addEventListener("click", handleExtract);
analyzeBtn.addEventListener("click", handleAnalyze);
abortBtn.addEventListener("click", handleAbort);
settingsBtn.addEventListener("click", () => chrome.runtime.openOptionsPage());
updatePostingCount();
updatePromptCount(0);
loadTasks();