Local Options Enabled
This commit is contained in:
+116
@@ -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
@@ -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>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 757 B |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
+14
@@ -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>
|
||||
@@ -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
@@ -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
@@ -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" }, "*");
|
||||
@@ -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
File diff suppressed because one or more lines are too long
@@ -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>
|
||||
@@ -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');
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user