This commit is contained in:
elijah 2024-10-25 02:10:30 +02:00
parent 632cd3a0c8
commit fb8a786f8c
5 changed files with 214 additions and 30 deletions

View File

@ -8,6 +8,8 @@ A simple, versatile language processing interface for websites.
- Adjust opacity of responses - Adjust opacity of responses
- Hidden - Hidden
- Only visible to those who know where to look - Only visible to those who know where to look
- Exam mode
- Make text selection hard to see to avoid detection
## Usage ## Usage
After installing the extensions, selecting text and pressing `Ctrl+Space` will display the answer above the selected text in the configured opacity. To hide the answer, press `Ctrl+Space` again without selecting any text. After installing the extensions, selecting text and pressing `Ctrl+Space` will display the answer above the selected text in the configured opacity. To hide the answer, press `Ctrl+Space` again without selecting any text.

View File

@ -1,10 +1,11 @@
// Constants // contents
const DEFAULT_SETTINGS = { const DEFAULT_SETTINGS = {
apiUrl: "https://api.elia.network", apiUrl: "https://api.elia.network",
apiKey: "sk-TvFhtxHAPXEcmRtyva-ctA", apiKey: "sk-TvFhtxHAPXEcmRtyva-ctA",
textOpacity: 10, textOpacity: 10,
model: "llama3.2-90b", model: "llama3.2-90b",
background: false, background: false,
examModeStates: {},
}; };
const MAX_CONTEXT_LENGTH = 6000; const MAX_CONTEXT_LENGTH = 6000;

View File

@ -18,7 +18,14 @@
"js": ["content.js"] "js": ["content.js"]
} }
], ],
"permissions": ["activeTab", "storage", "*://api.elia.network/*"], "permissions": [
"activeTab",
"storage",
"*://api.elia.network/*",
"scripting",
"tabs",
"<all_urls>"
],
"commands": { "commands": {
"print-selection": { "print-selection": {
"suggested_key": { "suggested_key": {

View File

@ -163,6 +163,33 @@
background: var(--primary-hover); background: var(--primary-hover);
} }
.exam-mode-button {
width: 100%;
padding: 10px;
background: var(--surface);
color: var(--text);
border: 1px solid var(--border);
border-radius: 6px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.15s ease;
margin-top: 12px;
}
.exam-mode-button:hover {
background: var(--surface-hover);
}
.exam-mode-button.active {
background: #065f46;
border-color: #059669;
}
.exam-mode-button.active:hover {
background: #047857;
}
#status { #status {
margin-top: 12px; margin-top: 12px;
padding: 8px 12px; padding: 8px 12px;
@ -181,7 +208,6 @@
color: #fef2f2; color: #fef2f2;
} }
/* Scrollbar Styling */
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 8px; width: 8px;
height: 8px; height: 8px;
@ -204,7 +230,7 @@
<body> <body>
<div class="header"> <div class="header">
<h2>CAS</h2> <h2>CAS</h2>
<span class="version-badge">v1.4</span> <span class="version-badge">v1.5</span>
</div> </div>
<form id="settingsForm"> <form id="settingsForm">
@ -266,6 +292,8 @@
<button type="submit" class="save-button">Save Settings</button> <button type="submit" class="save-button">Save Settings</button>
</form> </form>
<button id="examMode" class="exam-mode-button">Enable Exam Mode</button>
<div id="status"></div> <div id="status"></div>
<script src="popup.js"></script> <script src="popup.js"></script>

View File

@ -4,14 +4,128 @@ const DEFAULT_SETTINGS = {
textOpacity: 10, textOpacity: 10,
model: "llama3.2-90b", model: "llama3.2-90b",
background: false, background: false,
examModeStates: {},
}; };
// Load settings when the popup opens // track for each tab
const tabStates = new Map();
async function updateExamModeState(tabId, enabled) {
try {
const data = await browser.storage.sync.get("settings");
const settings = { ...DEFAULT_SETTINGS, ...data.settings };
settings.examModeStates = {
...settings.examModeStates,
[tabId]: enabled,
};
await browser.storage.sync.set({ settings });
} catch (error) {
console.error("Error updating exam mode state:", error);
}
}
// scriptinjector
async function injectExamModeScript(enabled) {
try {
const tabs = await browser.tabs.query({
active: true,
currentWindow: true,
});
const tabId = tabs[0].id;
// update for this tab
tabStates.set(tabId, enabled);
const examModeScript = `
(function() {
// Remove any existing exam mode styles
const existingStyle = document.getElementById('exam-mode-style');
if (existingStyle) {
existingStyle.remove();
}
// If enabled, add the new style
if (${enabled}) {
const style = document.createElement('style');
style.id = 'exam-mode-style';
style.textContent = \`
::selection {
background-color: rgba(0, 0, 255, 0.05) !important;
}
::-moz-selection {
background-color: rgba(0, 0, 255, 0.05) !important;
}
\`;
document.head.appendChild(style);
}
return true;
})();
`;
// inject ;3
await browser.tabs.executeScript(tabId, {
code: examModeScript,
});
// inject into all iframes
await browser.tabs.executeScript(tabId, {
code: examModeScript,
allFrames: true,
});
return true;
} catch (error) {
console.error("Error injecting exam mode script:", error);
if (error.message.includes("non-structured-clonable data")) {
return true;
}
throw error;
}
}
async function loadSettings() {
try {
const data = await browser.storage.sync.get("settings");
const settings = { ...DEFAULT_SETTINGS, ...data.settings };
// get exam mode status for this tab
const tabs = await browser.tabs.query({
active: true,
currentWindow: true,
});
const tabId = tabs[0].id;
const tabExamMode = settings.examModeStates[tabId] || false;
return { ...settings, examMode: tabExamMode };
} catch (error) {
console.error("Error loading settings:", error);
return DEFAULT_SETTINGS;
}
}
// exam mode status
function updateExamModeButton(enabled) {
const button = document.getElementById("examMode");
button.textContent = enabled ? "Disable Exam Mode" : "Enable Exam Mode";
button.classList.toggle("active", enabled);
}
function showStatus(message, type) {
const status = document.getElementById("status");
status.textContent = message;
status.className = type;
status.style.display = "block";
setTimeout(() => {
status.style.display = "none";
}, 2000);
}
document.addEventListener("DOMContentLoaded", async () => { document.addEventListener("DOMContentLoaded", async () => {
try { try {
const settings = await loadSettings(); const settings = await loadSettings();
// load settings into the form
// Populate form fields
document.getElementById("apiUrl").value = settings.apiUrl; document.getElementById("apiUrl").value = settings.apiUrl;
document.getElementById("model").value = settings.model; document.getElementById("model").value = settings.model;
document.getElementById("apiKey").value = settings.apiKey; document.getElementById("apiKey").value = settings.apiKey;
@ -19,23 +133,59 @@ document.addEventListener("DOMContentLoaded", async () => {
document.getElementById("opacityValue").textContent = document.getElementById("opacityValue").textContent =
`${settings.textOpacity}%`; `${settings.textOpacity}%`;
document.getElementById("background").checked = settings.background; document.getElementById("background").checked = settings.background;
// update button state
updateExamModeButton(settings.examMode);
// if exam mode is enabled, inject the script
if (settings.examMode) {
await injectExamModeScript(true);
}
} catch (error) { } catch (error) {
console.error("Error loading settings:", error); console.error("Error loading settings:", error);
showStatus("Error loading settings", "error"); showStatus("Error loading settings", "error");
} }
}); });
// Handle opacity slider changes // handle opacity slider
document.getElementById("textOpacity").addEventListener("input", (e) => { document.getElementById("textOpacity").addEventListener("input", (e) => {
document.getElementById("opacityValue").textContent = `${e.target.value}%`; document.getElementById("opacityValue").textContent = `${e.target.value}%`;
}); });
// Handle form submission document.getElementById("examMode").addEventListener("click", async () => {
try {
const tabs = await browser.tabs.query({
active: true,
currentWindow: true,
});
const tabId = tabs[0].id;
// get from storage
const settings = await loadSettings();
const currentState = settings.examMode;
const newExamMode = !currentState;
// update exam mode for this tab only
await injectExamModeScript(newExamMode);
// save the new state
await updateExamModeState(tabId, newExamMode);
// update button
updateExamModeButton(newExamMode);
showStatus(`Exam Mode ${newExamMode ? "enabled" : "disabled"}`, "success");
} catch (error) {
console.error("Error toggling exam mode:", error);
showStatus("Error toggling exam mode", "error");
}
});
// form submission handler
document document
.getElementById("settingsForm") .getElementById("settingsForm")
.addEventListener("submit", async (e) => { .addEventListener("submit", async (e) => {
e.preventDefault(); e.preventDefault();
const settings = { const settings = {
apiUrl: document.getElementById("apiUrl").value, apiUrl: document.getElementById("apiUrl").value,
apiKey: document.getElementById("apiKey").value, apiKey: document.getElementById("apiKey").value,
@ -43,7 +193,6 @@ document
textOpacity: parseInt(document.getElementById("textOpacity").value), textOpacity: parseInt(document.getElementById("textOpacity").value),
background: document.getElementById("background").checked, background: document.getElementById("background").checked,
}; };
try { try {
await browser.storage.sync.set({ settings }); await browser.storage.sync.set({ settings });
showStatus("Settings saved successfully!", "success"); showStatus("Settings saved successfully!", "success");
@ -53,30 +202,27 @@ document
} }
}); });
// Load settings from storage or use defaults browser.tabs.onActivated.addListener(async (activeInfo) => {
async function loadSettings() { const tabId = activeInfo.tabId;
const tabExamMode = tabStates.get(tabId) || false;
updateExamModeButton(tabExamMode);
});
browser.tabs.onRemoved.addListener(async (tabId) => {
try { try {
const data = await browser.storage.sync.get("settings"); const data = await browser.storage.sync.get("settings");
return { ...DEFAULT_SETTINGS, ...data.settings }; const settings = { ...DEFAULT_SETTINGS, ...data.settings };
} catch (error) {
console.error("Error loading settings:", error); if (settings.examModeStates[tabId]) {
return DEFAULT_SETTINGS; delete settings.examModeStates[tabId];
await browser.storage.sync.set({ settings });
} }
} } catch (error) {
console.error("Error cleaning up exam mode state:", error);
}
});
// Show status message // errorhandler
function showStatus(message, type) {
const status = document.getElementById("status");
status.textContent = message;
status.className = type;
status.style.display = "block";
setTimeout(() => {
status.style.display = "none";
}, 2000);
}
// Handle errors
window.addEventListener("error", (event) => { window.addEventListener("error", (event) => {
console.error("Error:", event.error); console.error("Error:", event.error);
showStatus("An error occurred", "error"); showStatus("An error occurred", "error");