added quality-of-life improvements, persistent output buffer and better error messages
This commit is contained in:
@@ -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/*"],
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
46
popup.js
46
popup.js
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user