From ad05b632852995f344715458f44f8c69dc68f7c8 Mon Sep 17 00:00:00 2001 From: Peisong Xiao Date: Fri, 16 Jan 2026 23:15:18 -0500 Subject: [PATCH] added quality-of-life improvements, persistent output buffer and better error messages --- manifest.json | 2 +- popup.html | 1 + popup.js | 46 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/manifest.json b/manifest.json index fd64fab..78a3336 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "WWCompanion", - "version": "0.1.2", + "version": "0.1.3", "description": "Manual reasoning companion for WaterlooWorks job postings.", "permissions": ["storage", "activeTab"], "host_permissions": ["https://waterlooworks.uwaterloo.ca/*"], diff --git a/popup.html b/popup.html index a8f85c6..8b807c6 100644 --- a/popup.html +++ b/popup.html @@ -46,6 +46,7 @@ diff --git a/popup.js b/popup.js index 809d874..598c5d9 100644 --- a/popup.js +++ b/popup.js @@ -12,6 +12,10 @@ const promptCountEl = document.getElementById("promptCount"); const settingsBtn = document.getElementById("settingsBtn"); const copyRenderedBtn = document.getElementById("copyRenderedBtn"); const copyRawBtn = document.getElementById("copyRawBtn"); +const clearOutputBtn = document.getElementById("clearOutputBtn"); + +const OUTPUT_STORAGE_KEY = "lastOutput"; +let persistTimer = null; const state = { postingText: "", @@ -217,6 +221,21 @@ function renderOutput() { outputEl.scrollTop = outputEl.scrollHeight; } +function persistOutputNow() { + if (persistTimer) { + clearTimeout(persistTimer); + persistTimer = null; + } + return chrome.storage.local.set({ [OUTPUT_STORAGE_KEY]: state.outputRaw }); +} + +function schedulePersistOutput() { + if (persistTimer) clearTimeout(persistTimer); + persistTimer = setTimeout(() => { + void persistOutputNow(); + }, 250); +} + function setStatus(message) { statusEl.textContent = message; } @@ -280,7 +299,11 @@ function sendToActiveTab(message) { chrome.tabs.sendMessage(tab.id, message, (response) => { const error = chrome.runtime.lastError; if (error) { - reject(new Error(error.message)); + const msg = + error.message && error.message.includes("Receiving end does not exist") + ? "Couldn't reach the page. Try refreshing WaterlooWorks and retry." + : error.message; + reject(new Error(msg)); return; } resolve(response); @@ -297,24 +320,28 @@ function ensurePort() { if (message?.type === "DELTA") { state.outputRaw += message.text; renderOutput(); + schedulePersistOutput(); return; } if (message?.type === "DONE") { setAnalyzing(false); setStatus("Done"); + void persistOutputNow(); return; } if (message?.type === "ABORTED") { setAnalyzing(false); setStatus("Aborted."); + void persistOutputNow(); return; } if (message?.type === "ERROR") { setAnalyzing(false); setStatus(message.message || "Error during analysis."); + void persistOutputNow(); } }); @@ -401,6 +428,7 @@ async function handleAnalyze() { state.outputRaw = ""; renderOutput(); + void persistOutputNow(); setAnalyzing(true); setStatus("Analyzing..."); @@ -434,6 +462,13 @@ function handleAbort() { setStatus("Aborted."); } +async function handleClearOutput() { + state.outputRaw = ""; + renderOutput(); + await persistOutputNow(); + setStatus("Output cleared."); +} + async function copyTextToClipboard(text, label) { try { await navigator.clipboard.writeText(text); @@ -468,6 +503,7 @@ abortBtn.addEventListener("click", handleAbort); settingsBtn.addEventListener("click", () => chrome.runtime.openOptionsPage()); copyRenderedBtn.addEventListener("click", handleCopyRendered); copyRawBtn.addEventListener("click", handleCopyRaw); +clearOutputBtn.addEventListener("click", () => void handleClearOutput()); updatePostingCount(); updatePromptCount(0); @@ -476,6 +512,14 @@ setAnalyzing(false); loadTasks(); loadTheme(); +async function loadSavedOutput() { + const stored = await getStorage([OUTPUT_STORAGE_KEY]); + state.outputRaw = stored[OUTPUT_STORAGE_KEY] || ""; + renderOutput(); +} + +loadSavedOutput(); + chrome.storage.onChanged.addListener((changes) => { if (changes.theme) { applyTheme(changes.theme.newValue || "system");