initial commit: wwcompanion v1
This commit is contained in:
223
popup.js
Normal file
223
popup.js
Normal file
@@ -0,0 +1,223 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user