From 727dbe2af160c9067ac1dd573441973bb6ef5317 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:25:31 +0200 Subject: [PATCH 01/20] Implement Groq backend with API integration and error handling --- groq-backend.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 groq-backend.js diff --git a/groq-backend.js b/groq-backend.js new file mode 100644 index 0000000..51250cb --- /dev/null +++ b/groq-backend.js @@ -0,0 +1,32 @@ +const express = require('express'); +const cors = require('cors'); +const axios = require('axios'); +require('dotenv').config(); + +const app = express(); +app.use(cors()); +app.use(express.json()); + +app.post('/ask-groq', async (req, res) => { + const { question } = req.body; + try { + const groqRes = await axios.post( + 'https://api.groq.com/v1/chat/completions', + { + model: 'llama-3-70b-8192', + messages: [{ role: 'user', content: question }] + }, + { + headers: { + 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`, + 'Content-Type': 'application/json' + } + } + ); + res.json({ answer: groqRes.data.choices[0].message.content }); + } catch (err) { + res.status(500).json({ error: 'Groq API error', details: err.message }); + } +}); + +app.listen(3001, () => console.log('Groq backend running on port 3001')); \ No newline at end of file From b03779dd6b6e4cdfd604de7894e9841be92791df Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:26:03 +0200 Subject: [PATCH 02/20] Add .env to .gitignore to prevent sensitive data exposure --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file From 2984c79114a285224c0358c925131fee761fcde5 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:28:45 +0200 Subject: [PATCH 03/20] Add async function to handle Groq API requests --- content.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/content.js b/content.js index cfea960..013759a 100644 --- a/content.js +++ b/content.js @@ -405,6 +405,16 @@ } } + async function askGroq(question) { + const response = await fetch('http://localhost:3001/ask-groq', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ question }) + }); + const data = await response.json(); + return data.answer; +} + // Listen for messages from background script if (chrome.runtime && chrome.runtime.onMessage) { chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { From e316e4eea8f0a047e64f5381a1dae35d771040cf Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:32:17 +0200 Subject: [PATCH 04/20] Add AI Q&A feature to sidebar for term inquiries --- content.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/content.js b/content.js index 013759a..2712e0f 100644 --- a/content.js +++ b/content.js @@ -230,6 +230,11 @@ - Resize sidebar by dragging its edge.
- Toggle with extension popup or floating button.
+
+ + +
+
`; document.body.appendChild(sidebar); @@ -258,6 +263,43 @@ } function setupInteractions(sidebar, floatBtn, matchesByTerm, tooltip) { + // --- AI Q&A logic --- + const aiInput = sidebar.querySelector('#hypeless-ai-input'); + const aiBtn = sidebar.querySelector('#hypeless-ai-btn'); + const aiAnswer = sidebar.querySelector('#hypeless-ai-answer'); + + // On highlight click, pre-fill the input with the term + document.body.addEventListener('click', e => { + if (e.target.classList && e.target.classList.contains('hypeless-highlight')) { + const term = e.target.getAttribute('data-term'); + const expl = e.target.getAttribute('data-expl'); + if (aiInput) { + aiInput.value = `What does "${term}" mean in academic writing? ${expl ? 'Explanation: ' + expl : ''}`; + aiInput.focus(); + } + } + }); + + // On AI button click, ask Groq + if (aiBtn && aiInput && aiAnswer) { + aiBtn.addEventListener('click', async () => { + const question = aiInput.value.trim(); + if (!question) return; + aiBtn.disabled = true; + aiAnswer.textContent = 'Thinking...'; + try { + const answer = await askGroq(question); + aiAnswer.textContent = answer; + } catch (err) { + aiAnswer.textContent = 'Error contacting AI.'; + } + aiBtn.disabled = false; + }); + // Enter key submits + aiInput.addEventListener('keydown', e => { + if (e.key === 'Enter') aiBtn.click(); + }); + } const termPositions = new Map(); for (const term of matchesByTerm.keys()) termPositions.set(term, 0); From 9aa05178d19737aa02041e605f61584af7bb72c7 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:35:04 +0200 Subject: [PATCH 05/20] Enhance AI Q&A input styling for better visibility and user experience --- content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content.js b/content.js index 2712e0f..37db278 100644 --- a/content.js +++ b/content.js @@ -231,7 +231,7 @@ - Toggle with extension popup or floating button.
- +
From 33d8f932f5ef214d36a2d248e5e4d9040429b6e5 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:36:43 +0200 Subject: [PATCH 06/20] Add package.json with initial dependencies for the project --- .gitignore | 3 +- package-lock.json | 976 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 7 + 3 files changed, 985 insertions(+), 1 deletion(-) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 2eea525..13dfa36 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.env \ No newline at end of file +.env +node_modules/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1e5b305 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,976 @@ +{ + "name": "HypeLessLi", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "axios": "^1.11.0", + "cors": "^2.8.5", + "express": "^5.1.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f860dbc --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "axios": "^1.11.0", + "cors": "^2.8.5", + "express": "^5.1.0" + } +} From d6a2b14881cb948b64d1bfdb574d2aa66ddfc24c Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:37:10 +0200 Subject: [PATCH 07/20] Add dotenv dependency to package.json and package-lock.json --- package-lock.json | 13 +++++++++++++ package.json | 1 + 2 files changed, 14 insertions(+) diff --git a/package-lock.json b/package-lock.json index 1e5b305..6141e15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "dependencies": { "axios": "^1.11.0", "cors": "^2.8.5", + "dotenv": "^17.2.1", "express": "^5.1.0" } }, @@ -197,6 +198,18 @@ "node": ">= 0.8" } }, + "node_modules/dotenv": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", + "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/package.json b/package.json index f860dbc..9a04f2e 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "dependencies": { "axios": "^1.11.0", "cors": "^2.8.5", + "dotenv": "^17.2.1", "express": "^5.1.0" } } From 5958cb0ec6b242536bae30170c53b6a884a26432 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:46:52 +0200 Subject: [PATCH 08/20] Refactor Groq API request handling and improve logging for better debugging --- groq-backend.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/groq-backend.js b/groq-backend.js index 51250cb..09f865d 100644 --- a/groq-backend.js +++ b/groq-backend.js @@ -9,22 +9,32 @@ app.use(express.json()); app.post('/ask-groq', async (req, res) => { const { question } = req.body; + console.log('[ask-groq] Received question:', question); + try { + console.log('[ask-groq] Sending request to Groq API...'); const groqRes = await axios.post( - 'https://api.groq.com/v1/chat/completions', - { - model: 'llama-3-70b-8192', + 'https://api.groq.com/openai/v1/chat/completions', + { + model: 'llama-3.3-70b-versatile', messages: [{ role: 'user', content: question }] - }, - { + }, + { headers: { - 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`, - 'Content-Type': 'application/json' + 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`, + 'Content-Type': 'application/json' } - } + } ); + console.log('[ask-groq] Groq API response status:', groqRes.status); + if (groqRes.data && groqRes.data.choices && groqRes.data.choices[0]) { + console.log('[ask-groq] Groq API answer:', groqRes.data.choices[0].message.content.slice(0, 100), '...'); + } else { + console.log('[ask-groq] Groq API response missing expected data:', groqRes.data); + } res.json({ answer: groqRes.data.choices[0].message.content }); } catch (err) { + console.error('[ask-groq] Error from Groq API:', err.response ? err.response.data : err.message); res.status(500).json({ error: 'Groq API error', details: err.message }); } }); From 1e4e46b2183239c510cd977ea5c009b8cf13c2db Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:47:31 +0200 Subject: [PATCH 09/20] Update AI Q&A answer styling for improved readability --- content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content.js b/content.js index 37db278..961f3b3 100644 --- a/content.js +++ b/content.js @@ -233,7 +233,7 @@
-
+
`; document.body.appendChild(sidebar); From 5d661d6a5d558c4605986d681e8d5d9744699a83 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:50:55 +0200 Subject: [PATCH 10/20] Refactor Groq API request to include system prompt and improve message structure --- groq-backend.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/groq-backend.js b/groq-backend.js index 09f865d..b5653c4 100644 --- a/groq-backend.js +++ b/groq-backend.js @@ -11,20 +11,26 @@ app.post('/ask-groq', async (req, res) => { const { question } = req.body; console.log('[ask-groq] Received question:', question); + // System prompt for Groq + const systemPrompt = `You are HypeLessLi, an assistant that helps users critically read scientific texts by highlighting hype-like, subjective, promotional, and vague terms in yellow. You provide clear, concise explanations for why a term is considered hype, and always suggest less hyped, more objective alternatives for any term or phrase the user asks about. If the user does not specify, always include a suggestion for a more objective or neutral alternative.`; + try { console.log('[ask-groq] Sending request to Groq API...'); const groqRes = await axios.post( - 'https://api.groq.com/openai/v1/chat/completions', - { + 'https://api.groq.com/openai/v1/chat/completions', + { model: 'llama-3.3-70b-versatile', - messages: [{ role: 'user', content: question }] - }, - { + messages: [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: question } + ] + }, + { headers: { - 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`, - 'Content-Type': 'application/json' + 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`, + 'Content-Type': 'application/json' } - } + } ); console.log('[ask-groq] Groq API response status:', groqRes.status); if (groqRes.data && groqRes.data.choices && groqRes.data.choices[0]) { From 81f84501c80a639386ddf7b29ac0ccd08a7f3a81 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:53:46 +0200 Subject: [PATCH 11/20] Enhance Q&A functionality by adding in-memory history tracking and improving error handling --- groq-backend.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/groq-backend.js b/groq-backend.js index b5653c4..3668341 100644 --- a/groq-backend.js +++ b/groq-backend.js @@ -3,10 +3,14 @@ const cors = require('cors'); const axios = require('axios'); require('dotenv').config(); + const app = express(); app.use(cors()); app.use(express.json()); +// In-memory history of last 7 Q&A +const aiHistory = []; + app.post('/ask-groq', async (req, res) => { const { question } = req.body; console.log('[ask-groq] Received question:', question); @@ -33,16 +37,26 @@ app.post('/ask-groq', async (req, res) => { } ); console.log('[ask-groq] Groq API response status:', groqRes.status); + let answer = ''; if (groqRes.data && groqRes.data.choices && groqRes.data.choices[0]) { - console.log('[ask-groq] Groq API answer:', groqRes.data.choices[0].message.content.slice(0, 100), '...'); + answer = groqRes.data.choices[0].message.content; + console.log('[ask-groq] Groq API answer:', answer.slice(0, 100), '...'); } else { console.log('[ask-groq] Groq API response missing expected data:', groqRes.data); + answer = '[No answer returned]'; } - res.json({ answer: groqRes.data.choices[0].message.content }); + // Add to history (keep only last 7) + aiHistory.push({ question, answer, ts: new Date().toISOString() }); + if (aiHistory.length > 7) aiHistory.shift(); + res.json({ answer }); } catch (err) { console.error('[ask-groq] Error from Groq API:', err.response ? err.response.data : err.message); res.status(500).json({ error: 'Groq API error', details: err.message }); } +// Endpoint to get last 7 Q&A +app.get('/ask-groq/history', (req, res) => { + res.json({ history: aiHistory }); +}); }); app.listen(3001, () => console.log('Groq backend running on port 3001')); \ No newline at end of file From c2d73f1a4c7ab8f57b377e0749f58f2faeac78fd Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:56:06 +0200 Subject: [PATCH 12/20] Refactor system prompt to remove redundant phrasing for clarity --- groq-backend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/groq-backend.js b/groq-backend.js index 3668341..1c14da5 100644 --- a/groq-backend.js +++ b/groq-backend.js @@ -16,7 +16,7 @@ app.post('/ask-groq', async (req, res) => { console.log('[ask-groq] Received question:', question); // System prompt for Groq - const systemPrompt = `You are HypeLessLi, an assistant that helps users critically read scientific texts by highlighting hype-like, subjective, promotional, and vague terms in yellow. You provide clear, concise explanations for why a term is considered hype, and always suggest less hyped, more objective alternatives for any term or phrase the user asks about. If the user does not specify, always include a suggestion for a more objective or neutral alternative.`; + const systemPrompt = `You are HypeLessLi, an assistant that helps users critically read scientific texts by highlighting hype-like, subjective, promotional, and vague terms. You provide clear, concise explanations for why a term is considered hype, and always suggest less hyped, more objective alternatives for any term or phrase the user asks about. If the user does not specify, always include a suggestion for a more objective or neutral alternative.`; try { console.log('[ask-groq] Sending request to Groq API...'); From b7c18659039e085a2e18e5e3c6022ce026dcbf8f Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:57:09 +0200 Subject: [PATCH 13/20] Refactor chat history construction to streamline message handling for Groq API requests --- groq-backend.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/groq-backend.js b/groq-backend.js index 1c14da5..534c57d 100644 --- a/groq-backend.js +++ b/groq-backend.js @@ -18,16 +18,23 @@ app.post('/ask-groq', async (req, res) => { // System prompt for Groq const systemPrompt = `You are HypeLessLi, an assistant that helps users critically read scientific texts by highlighting hype-like, subjective, promotional, and vague terms. You provide clear, concise explanations for why a term is considered hype, and always suggest less hyped, more objective alternatives for any term or phrase the user asks about. If the user does not specify, always include a suggestion for a more objective or neutral alternative.`; + // Build chat history for Groq + const chatHistory = [ + { role: 'system', content: systemPrompt }, + ...aiHistory.flatMap(pair => [ + { role: 'user', content: pair.question }, + { role: 'assistant', content: pair.answer } + ]), + { role: 'user', content: question } + ]; + try { console.log('[ask-groq] Sending request to Groq API...'); const groqRes = await axios.post( 'https://api.groq.com/openai/v1/chat/completions', { model: 'llama-3.3-70b-versatile', - messages: [ - { role: 'system', content: systemPrompt }, - { role: 'user', content: question } - ] + messages: chatHistory }, { headers: { From 7373ab8d83e41668f6847ba372aa0fd96fed75f4 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 17:59:46 +0200 Subject: [PATCH 14/20] Refactor Groq API handling to improve system prompt and increase Q&A history limit to 12 --- groq-backend.js | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/groq-backend.js b/groq-backend.js index 534c57d..724d1e1 100644 --- a/groq-backend.js +++ b/groq-backend.js @@ -8,25 +8,40 @@ const app = express(); app.use(cors()); app.use(express.json()); -// In-memory history of last 7 Q&A +// In-memory history of last 12 Q&A +const HISTORY_LIMIT = 12; const aiHistory = []; app.post('/ask-groq', async (req, res) => { const { question } = req.body; console.log('[ask-groq] Received question:', question); - // System prompt for Groq - const systemPrompt = `You are HypeLessLi, an assistant that helps users critically read scientific texts by highlighting hype-like, subjective, promotional, and vague terms. You provide clear, concise explanations for why a term is considered hype, and always suggest less hyped, more objective alternatives for any term or phrase the user asks about. If the user does not specify, always include a suggestion for a more objective or neutral alternative.`; - // Build chat history for Groq - const chatHistory = [ - { role: 'system', content: systemPrompt }, - ...aiHistory.flatMap(pair => [ + // Improved system prompt for Groq + const systemPrompt = `You are HypeLessLi, an assistant that helps users critically read scientific texts by highlighting hype-like, subjective, promotional, and vague terms. You provide clear, concise explanations for why a term is considered hype, and always suggest less hyped, more objective alternatives for any term or phrase the user asks about. If the user does not specify, always include a suggestion for a more objective or neutral alternative. If the user asks a follow-up, use the previous questions and answers in this conversation for context. Always try to resolve ambiguous or short follow-ups by referencing the last exchange.`; + + // Helper: is the question a likely follow-up (short or vague)? + function isLikelyFollowup(q) { + return q.trim().length < 20 || /^(what|which|and|also|more|how about|the second|the first|that one|this one|another|other|else|too|again|continue|next|previous|last|first|second|third|fourth|fifth|sixth|seventh|eighth|ninth|tenth|it|he|she|they|him|her|them|those|these|such|so|then|now|why|how|where|when|who|whose|whom|is|are|was|were|do|does|did|can|could|should|would|will|shall|may|might|must|has|have|had|does|did|doesn't|didn't|isn't|aren't|wasn't|weren't|hasn't|haven't|hadn't|won't|wouldn't|can't|couldn't|shouldn't|mightn't|mustn't|doesnt|didnt|isnt|arent|wasnt|werent|hasnt|havent|hadnt|wont|wouldnt|cant|couldnt|shouldnt|mightnt|mustnt)\b/i.test(q.trim()); + } + + // Build chat history for Groq (up to HISTORY_LIMIT) + let historyPairs = aiHistory.slice(-HISTORY_LIMIT); + + // If the new question is a likely follow-up, prepend the last Q&A as context + let chatHistory = [ { role: 'system', content: systemPrompt } ]; + if (isLikelyFollowup(question) && historyPairs.length > 0) { + const last = historyPairs[historyPairs.length - 1]; + chatHistory.push({ role: 'user', content: last.question }); + chatHistory.push({ role: 'assistant', content: last.answer }); + } + chatHistory = chatHistory.concat( + historyPairs.flatMap(pair => [ { role: 'user', content: pair.question }, { role: 'assistant', content: pair.answer } - ]), - { role: 'user', content: question } - ]; + ]) + ); + chatHistory.push({ role: 'user', content: question }); try { console.log('[ask-groq] Sending request to Groq API...'); @@ -52,10 +67,10 @@ app.post('/ask-groq', async (req, res) => { console.log('[ask-groq] Groq API response missing expected data:', groqRes.data); answer = '[No answer returned]'; } - // Add to history (keep only last 7) - aiHistory.push({ question, answer, ts: new Date().toISOString() }); - if (aiHistory.length > 7) aiHistory.shift(); - res.json({ answer }); + // Add to history (keep only last HISTORY_LIMIT) + aiHistory.push({ question, answer, ts: new Date().toISOString() }); + if (aiHistory.length > HISTORY_LIMIT) aiHistory.shift(); + res.json({ answer }); } catch (err) { console.error('[ask-groq] Error from Groq API:', err.response ? err.response.data : err.message); res.status(500).json({ error: 'Groq API error', details: err.message }); From 7f30d4e203d3969618e23035e73c1c6671dad7a4 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 18:03:31 +0200 Subject: [PATCH 15/20] Improve sidebar button layout and styling for better usability --- content.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content.js b/content.js index 961f3b3..3bf3b6a 100644 --- a/content.js +++ b/content.js @@ -216,9 +216,9 @@ sidebar.innerHTML = `
HypeLessLi v3.1 (${totalCount} found) -
- - +
+ +
${itemsHTML}
@@ -233,7 +233,7 @@
-
+
`; document.body.appendChild(sidebar); From 85c70943aaba7304458d1d1c8d394510aa4a1e8b Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 18:05:05 +0200 Subject: [PATCH 16/20] Update sidebar header layout to improve readability --- content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content.js b/content.js index 3bf3b6a..9ba7dc1 100644 --- a/content.js +++ b/content.js @@ -215,7 +215,7 @@ const totalCount = [...termCounts.values()].reduce((a, b) => a + b, 0); sidebar.innerHTML = `
- HypeLessLi v3.1 (${totalCount} found) + HypeLessLi v3.1
(${totalCount} found)
From 438e11a89ec2442c33590245303d5bbf3a928010 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 18:08:55 +0200 Subject: [PATCH 17/20] Update input placeholder to indicate local server requirement for AI queries --- content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content.js b/content.js index 9ba7dc1..0674b8b 100644 --- a/content.js +++ b/content.js @@ -231,7 +231,7 @@ - Toggle with extension popup or floating button.
- +
From fc4f160ab82d5b55159605fa95fb7c0b76013f5a Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 18:15:53 +0200 Subject: [PATCH 18/20] Add experimental AI Q&A feature with local server requirement and usage notes --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 83facce..653a3e7 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,18 @@ HypeLessLi has been updated with the following improvements over the original ve --- + +## Experimental: AI Q&A (Optional) + +HypeLessLi includes an **experimental AI Q&A feature** in the sidebar, allowing you to ask questions about hype terms and get suggestions for more objective alternatives. + +**Note:** +- This feature requires running a local backend server (see `groq-backend.js`) and a valid Groq API key (put into a local .env file). +- If the backend is not running, the AI Q&A section will not function. +- The main extension features work independently of the AI Q&A. + +--- + ## Authors Dr. Xhoela Bame, Dr. Gjylije Hoti, Dr. Adibe Kingsley Mbachu, Dr. Vasilis Nikolaou, Simon Nirenberg, Klara Krmpotic, Dr. Christian Kuttner, Dr. Sudha Shankar (in alphabetical order) From ef98a8dcab4bcee9f67e39dae2b4dac05687a092 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 18:22:29 +0200 Subject: [PATCH 19/20] Update AI Q&A section to clarify local server requirement in the UI --- content.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/content.js b/content.js index 0674b8b..c497ee1 100644 --- a/content.js +++ b/content.js @@ -231,9 +231,10 @@ - Toggle with extension popup or floating button.
- - -
+ + +
AI Q&A requires
local server
+
`; document.body.appendChild(sidebar); From 12855ddc8bcdcac404bc97f484fbdf487168a315 Mon Sep 17 00:00:00 2001 From: QuantumChemist Date: Sat, 30 Aug 2025 18:29:38 +0200 Subject: [PATCH 20/20] Reduce in-memory history limit for Q&A from 12 to 3 --- groq-backend.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/groq-backend.js b/groq-backend.js index 724d1e1..79389f3 100644 --- a/groq-backend.js +++ b/groq-backend.js @@ -8,8 +8,8 @@ const app = express(); app.use(cors()); app.use(express.json()); -// In-memory history of last 12 Q&A -const HISTORY_LIMIT = 12; +// In-memory history of last 3 Q&A +const HISTORY_LIMIT = 3; const aiHistory = []; app.post('/ask-groq', async (req, res) => {