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();