added quality-of-life improvements, persistent output buffer and better error messages

This commit is contained in:
2026-01-16 23:15:18 -05:00
parent 743b83deb3
commit ad05b63285
3 changed files with 47 additions and 2 deletions

View File

@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "WWCompanion", "name": "WWCompanion",
"version": "0.1.2", "version": "0.1.3",
"description": "Manual reasoning companion for WaterlooWorks job postings.", "description": "Manual reasoning companion for WaterlooWorks job postings.",
"permissions": ["storage", "activeTab"], "permissions": ["storage", "activeTab"],
"host_permissions": ["https://waterlooworks.uwaterloo.ca/*"], "host_permissions": ["https://waterlooworks.uwaterloo.ca/*"],

View File

@@ -46,6 +46,7 @@
<div class="footer-left"> <div class="footer-left">
<button id="copyRenderedBtn" class="ghost" type="button">Copy</button> <button id="copyRenderedBtn" class="ghost" type="button">Copy</button>
<button id="copyRawBtn" class="ghost" type="button">Copy Markdown</button> <button id="copyRawBtn" class="ghost" type="button">Copy Markdown</button>
<button id="clearOutputBtn" class="ghost" type="button">Clear</button>
</div> </div>
<button id="settingsBtn" class="link">Open Settings</button> <button id="settingsBtn" class="link">Open Settings</button>
</footer> </footer>

View File

@@ -12,6 +12,10 @@ const promptCountEl = document.getElementById("promptCount");
const settingsBtn = document.getElementById("settingsBtn"); const settingsBtn = document.getElementById("settingsBtn");
const copyRenderedBtn = document.getElementById("copyRenderedBtn"); const copyRenderedBtn = document.getElementById("copyRenderedBtn");
const copyRawBtn = document.getElementById("copyRawBtn"); const copyRawBtn = document.getElementById("copyRawBtn");
const clearOutputBtn = document.getElementById("clearOutputBtn");
const OUTPUT_STORAGE_KEY = "lastOutput";
let persistTimer = null;
const state = { const state = {
postingText: "", postingText: "",
@@ -217,6 +221,21 @@ function renderOutput() {
outputEl.scrollTop = outputEl.scrollHeight; 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) { function setStatus(message) {
statusEl.textContent = message; statusEl.textContent = message;
} }
@@ -280,7 +299,11 @@ function sendToActiveTab(message) {
chrome.tabs.sendMessage(tab.id, message, (response) => { chrome.tabs.sendMessage(tab.id, message, (response) => {
const error = chrome.runtime.lastError; const error = chrome.runtime.lastError;
if (error) { 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; return;
} }
resolve(response); resolve(response);
@@ -297,24 +320,28 @@ function ensurePort() {
if (message?.type === "DELTA") { if (message?.type === "DELTA") {
state.outputRaw += message.text; state.outputRaw += message.text;
renderOutput(); renderOutput();
schedulePersistOutput();
return; return;
} }
if (message?.type === "DONE") { if (message?.type === "DONE") {
setAnalyzing(false); setAnalyzing(false);
setStatus("Done"); setStatus("Done");
void persistOutputNow();
return; return;
} }
if (message?.type === "ABORTED") { if (message?.type === "ABORTED") {
setAnalyzing(false); setAnalyzing(false);
setStatus("Aborted."); setStatus("Aborted.");
void persistOutputNow();
return; return;
} }
if (message?.type === "ERROR") { if (message?.type === "ERROR") {
setAnalyzing(false); setAnalyzing(false);
setStatus(message.message || "Error during analysis."); setStatus(message.message || "Error during analysis.");
void persistOutputNow();
} }
}); });
@@ -401,6 +428,7 @@ async function handleAnalyze() {
state.outputRaw = ""; state.outputRaw = "";
renderOutput(); renderOutput();
void persistOutputNow();
setAnalyzing(true); setAnalyzing(true);
setStatus("Analyzing..."); setStatus("Analyzing...");
@@ -434,6 +462,13 @@ function handleAbort() {
setStatus("Aborted."); setStatus("Aborted.");
} }
async function handleClearOutput() {
state.outputRaw = "";
renderOutput();
await persistOutputNow();
setStatus("Output cleared.");
}
async function copyTextToClipboard(text, label) { async function copyTextToClipboard(text, label) {
try { try {
await navigator.clipboard.writeText(text); await navigator.clipboard.writeText(text);
@@ -468,6 +503,7 @@ abortBtn.addEventListener("click", handleAbort);
settingsBtn.addEventListener("click", () => chrome.runtime.openOptionsPage()); settingsBtn.addEventListener("click", () => chrome.runtime.openOptionsPage());
copyRenderedBtn.addEventListener("click", handleCopyRendered); copyRenderedBtn.addEventListener("click", handleCopyRendered);
copyRawBtn.addEventListener("click", handleCopyRaw); copyRawBtn.addEventListener("click", handleCopyRaw);
clearOutputBtn.addEventListener("click", () => void handleClearOutput());
updatePostingCount(); updatePostingCount();
updatePromptCount(0); updatePromptCount(0);
@@ -476,6 +512,14 @@ setAnalyzing(false);
loadTasks(); loadTasks();
loadTheme(); loadTheme();
async function loadSavedOutput() {
const stored = await getStorage([OUTPUT_STORAGE_KEY]);
state.outputRaw = stored[OUTPUT_STORAGE_KEY] || "";
renderOutput();
}
loadSavedOutput();
chrome.storage.onChanged.addListener((changes) => { chrome.storage.onChanged.addListener((changes) => {
if (changes.theme) { if (changes.theme) {
applyTheme(changes.theme.newValue || "system"); applyTheme(changes.theme.newValue || "system");