Local Options Enabled

This commit is contained in:
c4ch3c4d3
2026-03-17 11:10:09 -06:00
commit d77e4d1192
17 changed files with 622007 additions and 0 deletions
+2
View File
File diff suppressed because one or more lines are too long
+116
View File
@@ -0,0 +1,116 @@
// src/background.ts
chrome.action.onClicked.addListener((tab) => {
const tabId = tab?.id;
if (tabId && chrome.sidePanel.open) {
chrome.sidePanel.open({ tabId });
}
});
console.log("[Background] onUserScriptMessage available:", !!chrome.runtime.onUserScriptMessage);
if (chrome.runtime.onUserScriptMessage) {
chrome.runtime.onUserScriptMessage.addListener((message, sender, sendResponse) => {
console.log("[Background] Received userScript message:", message, "from:", sender);
if (message.type === "abort-repl") {
console.log("[Background] Relaying abort-repl to sidepanels");
chrome.runtime.sendMessage(message);
sendResponse({ success: true });
return true;
}
});
console.log("[Background] onUserScriptMessage listener registered");
} else {
console.error("[Background] onUserScriptMessage NOT available!");
}
var SIDEPANEL_OPEN_KEY = "sidepanel_open_windows";
var SESSION_LOCKS_KEY = "session_locks";
var openSidepanels = /* @__PURE__ */ new Set();
chrome.storage.session.get(SIDEPANEL_OPEN_KEY, (data) => {
openSidepanels = new Set(data[SIDEPANEL_OPEN_KEY] || []);
console.log("[Background] Initialized openSidepanels cache:", Array.from(openSidepanels));
});
chrome.runtime.onConnect.addListener((port) => {
const match = /^sidepanel:(\d+)$/.exec(port.name);
if (!match) return;
const windowId = Number(match[1]);
openSidepanels.add(windowId);
chrome.storage.session.get(SIDEPANEL_OPEN_KEY, (data) => {
const openWindows = new Set(data[SIDEPANEL_OPEN_KEY] || []);
openWindows.add(windowId);
chrome.storage.session.set({ [SIDEPANEL_OPEN_KEY]: Array.from(openWindows) });
});
port.onMessage.addListener((msg) => {
if (msg.type === "acquireLock") {
const { sessionId, windowId: reqWindowId } = msg;
chrome.storage.session.get(SESSION_LOCKS_KEY, (data) => {
const sessionLocks = data[SESSION_LOCKS_KEY] || {};
const ownerWindowId = sessionLocks[sessionId];
const ownerSidepanelOpen = ownerWindowId !== void 0 && openSidepanels.has(ownerWindowId);
const success = !ownerWindowId || !ownerSidepanelOpen || ownerWindowId === reqWindowId;
const response = success ? {
type: "lockResult",
sessionId,
success: true
} : {
type: "lockResult",
sessionId,
success: false,
ownerWindowId
};
if (success) {
sessionLocks[sessionId] = reqWindowId;
chrome.storage.session.set({ [SESSION_LOCKS_KEY]: sessionLocks });
}
port.postMessage(response);
});
} else if (msg.type === "getLockedSessions") {
chrome.storage.session.get(SESSION_LOCKS_KEY, (data) => {
const locks = data[SESSION_LOCKS_KEY] || {};
const response = {
type: "lockedSessions",
locks
};
port.postMessage(response);
});
}
});
port.onDisconnect.addListener(() => {
closeSidepanel(windowId, false);
});
});
chrome.windows.onRemoved.addListener((windowId) => {
closeSidepanel(windowId, false);
});
chrome.commands.onCommand.addListener((command, sender) => {
if (command === "toggle-sidepanel") {
if (!sender?.windowId) {
console.log("[Background] Cannot toggle sidepanel: sender windowId not available");
return;
}
const windowId = sender.windowId;
if (openSidepanels.has(windowId)) {
closeSidepanel(windowId);
} else {
chrome.sidePanel.open({ windowId });
}
}
});
function closeSidepanel(windowId, callCloseOnSidePanelAPI = true) {
if (callCloseOnSidePanelAPI) {
chrome.sidePanel.close({ windowId });
}
openSidepanels.delete(windowId);
chrome.storage.session.get([SESSION_LOCKS_KEY, SIDEPANEL_OPEN_KEY], (data) => {
const sessionLocks = data[SESSION_LOCKS_KEY] || {};
for (const sessionId in sessionLocks) {
if (sessionLocks[sessionId] === windowId) {
delete sessionLocks[sessionId];
}
}
const openWindows = new Set(data[SIDEPANEL_OPEN_KEY] || []);
openWindows.delete(windowId);
chrome.storage.session.set({
[SESSION_LOCKS_KEY]: sessionLocks,
[SIDEPANEL_OPEN_KEY]: Array.from(openWindows)
});
});
}
//# sourceMappingURL=background.js.map
+13
View File
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Debug</title>
<script src="theme-loader.js"></script>
<link rel="stylesheet" href="app.css" />
</head>
<body class="h-full w-full m-0 overflow-hidden bg-background">
<script type="module" src="debug.js"></script>
</body>
</html>
+259996
View File
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 757 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+14
View File
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sitegeist Icon Generator</title>
<link rel="stylesheet" href="app.css" />
<script src="theme-loader.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="icons.js"></script>
</body>
</html>
+26038
View File
File diff suppressed because one or more lines are too long
+54
View File
@@ -0,0 +1,54 @@
{
"manifest_version": 3,
"minimum_chrome_version": "141",
"name": "sitegeist",
"description": "Your AI companion for the web - Research, automate, create",
"version": "1.0.0",
"action": {
"default_title": "Click to open side panel"
},
"background": {
"service_worker": "background.js",
"type": "module"
},
"icons": {
"16": "icon-16.png",
"48": "icon-48.png",
"128": "icon-128.png"
},
"side_panel": {
"default_path": "sidepanel.html"
},
"commands": {
"toggle-sidepanel": {
"suggested_key": {
"default": "Ctrl+Shift+S",
"mac": "Command+Shift+S"
},
"description": "Toggle side panel"
}
},
"permissions": [
"storage",
"unlimitedStorage",
"activeTab",
"scripting",
"sidePanel",
"userScripts",
"webNavigation",
"debugger"
],
"host_permissions": [
"http://*/*",
"https://*/*",
"http://localhost/*",
"http://127.0.0.1/*"
],
"sandbox": {
"pages": ["sandbox.html"]
},
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'",
"sandbox": "sandbox allow-scripts allow-modals; default-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net/ https://unpkg.com/ https://cdnjs.cloudflare.com/ https://esm.sh/ https://esm.run/ https://cdn.skypack.dev/ https://cdn.tailwindcss.com; script-src-elem 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net/ https://unpkg.com/ https://cdnjs.cloudflare.com/ https://esm.sh/ https://esm.run/ https://cdn.skypack.dev/ https://cdn.tailwindcss.com; connect-src https://cdn.jsdelivr.net/ https://unpkg.com/ https://cdnjs.cloudflare.com/ https://esm.sh/ https://esm.run/ https://cdn.skypack.dev/ https://cdn.tailwindcss.com https://t1.gstatic.com/; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net/ https://cdnjs.cloudflare.com/ https://cdn.tailwindcss.com; img-src 'self' data: blob: https:; font-src 'self' data: https://cdn.jsdelivr.net/ https://cdnjs.cloudflare.com/; object-src 'none'; base-uri 'none'; form-action 'none'"
}
}
File diff suppressed because one or more lines are too long
+15
View File
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sandboxed Content</title>
<style>
html { height: 100%; }
body { min-height: 100%; margin: 0; }
</style>
</head>
<body>
<script src="sandbox.js"></script>
</body>
</html>
+57
View File
@@ -0,0 +1,57 @@
// Minimal sandbox.js - just listens for sandbox-load and writes the content
window.addEventListener("message", (event) => {
if (event.data.type === "sandbox-load") {
// Validate HTML and JavaScript syntax before document.write()
try {
// Parse HTML to extract script tags
const parser = new DOMParser();
const doc = parser.parseFromString(event.data.code, "text/html");
// Check for HTML parser errors
const parserError = doc.querySelector("parsererror");
if (parserError) {
throw new Error(`HTML parse error: ${parserError.textContent}`);
}
// Validate JavaScript in all script tags (except type="module")
const scriptTags = Array.from(doc.querySelectorAll("script"));
for (let i = 0; i < scriptTags.length; i++) {
const scriptContent = scriptTags[i].textContent || "";
const scriptType = scriptTags[i].getAttribute("type");
// Skip validation for module scripts - new Function() can't validate module syntax
if (scriptType === "module") {
continue;
}
if (scriptContent.trim()) {
try {
// Use Function constructor to validate syntax without executing
new Function(scriptContent);
} catch (jsError) {
throw new Error(`JavaScript syntax error in <script> tag ${i + 1}: ${jsError.message}`);
}
}
}
} catch (validationError) {
// Send validation error back to parent
window.parent.postMessage(
{
type: "sandbox-error",
error: validationError.message || String(validationError),
stack: validationError.stack || "",
},
"*"
);
return;
}
// Write the complete HTML (which includes runtime + user code)
document.open();
document.write(event.data.code);
document.close();
}
});
// Signal ready to parent
window.parent.postMessage({ type: "sandbox-ready" }, "*");
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>pi-ai</title>
<script src="theme-loader.js"></script>
<link rel="stylesheet" href="app.css" />
</head>
<body class="h-full w-full m-0 overflow-hidden bg-background">
<script type="module" src="sidepanel.js"></script>
</body>
</html>
+335629
View File
File diff suppressed because one or more lines are too long
+24
View File
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test Sidebar</title>
<style>
body {
font-family: system-ui;
padding: 20px;
background: #f0f0f0;
}
h1 {
color: #333;
}
</style>
</head>
<body>
<h1>Test Sidebar</h1>
<p>If you can see this, the Firefox sidebar is working!</p>
<script>
console.log("Test sidebar loaded");
</script>
</body>
</html>
+8
View File
@@ -0,0 +1,8 @@
// Apply theme immediately to prevent white flash
// This runs before any other scripts or CSS
(function() {
const theme = localStorage.getItem('theme') || 'system';
if (theme === 'dark' || (theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
})();