Yay a devtools!
This commit is contained in:
parent
9124abb749
commit
f7fedd25a6
@ -1,24 +1,54 @@
|
||||
console.log("background.js loaded");
|
||||
|
||||
// Listen for messages from the devtools panel
|
||||
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
// You could add a check to see if the message is from the devtools panel.
|
||||
if (message.type === "GET_CONTEXT_STACK") {
|
||||
// Forward the message to the content script running in the active tab.
|
||||
browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
|
||||
if (tabs.length) {
|
||||
browser.tabs.sendMessage(tabs[0].id, message).then(sendResponse);
|
||||
}
|
||||
});
|
||||
// Return true to indicate you wish to send a response asynchronously
|
||||
browser.tabs.sendMessage(message.tabId, message)
|
||||
.then((res) => {
|
||||
console.log("RESPONSE", res);
|
||||
sendResponse(res);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Error sending message to content script:", err);
|
||||
sendResponse({ error: err });
|
||||
});
|
||||
// browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
|
||||
// if (tabs.length) {
|
||||
// }
|
||||
// });
|
||||
return true;
|
||||
} else if (message.type === "UPDATE_CONTEXT_VALUE") {
|
||||
// Forward update messages similarly
|
||||
browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
|
||||
if (tabs.length) {
|
||||
browser.tabs.sendMessage(tabs[0].id, message).then(sendResponse);
|
||||
}
|
||||
});
|
||||
browser.tabs.sendMessage(message.tabId, message).then(sendResponse);
|
||||
// browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
|
||||
// if (tabs.length) {
|
||||
// }
|
||||
// });
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
let devtoolsPort = null;
|
||||
|
||||
browser.runtime.onConnect.addListener((port) => {
|
||||
if (port.name === "devtools") {
|
||||
devtoolsPort = port;
|
||||
console.log("Devtools panel connected.");
|
||||
|
||||
// port.onMessage.addListener((msg) => {
|
||||
// console.log("Received message from devtools panel:", msg);
|
||||
// });
|
||||
|
||||
port.onDisconnect.addListener(() => {
|
||||
devtoolsPort = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Relay messages from content scripts to the devtools panel.
|
||||
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.type === "PAGE_LOADED") {
|
||||
console.log("Background received PAGE_LOADED message from content script.");
|
||||
if (devtoolsPort) {
|
||||
devtoolsPort.postMessage({ type: "PAGE_LOADED" });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,20 +1,12 @@
|
||||
console.log("content.js loaded", window.wrappedJSObject);
|
||||
|
||||
// setTimeout(() => {
|
||||
const getContextStack = () => window.wrappedJSObject.getContextStack();
|
||||
const updateContextValue = (key, value, depth) =>
|
||||
window.wrappedJSObject.updateContextValue(key, value, depth);
|
||||
|
||||
console.log(getContextStack());
|
||||
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.type === "GET_CONTEXT_STACK") {
|
||||
const contextStack = getContextStack();
|
||||
const contextStack = window.wrappedJSObject.getContextStack();
|
||||
|
||||
sendResponse({ contextStack });
|
||||
} else if (message.type === "UPDATE_CONTEXT_VALUE") {
|
||||
const { key, value, depth } = message;
|
||||
if (glowindow.updateContextValue) {
|
||||
updateContextValue(key, value, depth);
|
||||
if (window.wrappedJSObject.updateContextValue) {
|
||||
window.wrappedJSObject.updateContextValue(key, value, depth);
|
||||
sendResponse({ success: true });
|
||||
} else {
|
||||
sendResponse({
|
||||
@ -24,4 +16,5 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
}
|
||||
}
|
||||
});
|
||||
// }, 0);
|
||||
|
||||
browser.runtime.sendMessage({ type: "PAGE_LOADED" });
|
||||
|
@ -16,7 +16,8 @@
|
||||
],
|
||||
"js": [
|
||||
"content.js"
|
||||
]
|
||||
],
|
||||
"all_frames": true
|
||||
}
|
||||
],
|
||||
"permissions": [
|
||||
|
@ -1,39 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Context Stack</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
/* margin: 10px; */
|
||||
/* width: 100%;
|
||||
height: 100%; */
|
||||
background-color: #302040;
|
||||
color: #fff;
|
||||
/* text-align: right; */
|
||||
}
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Context Stack</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
/* margin: 10px; */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #302040;
|
||||
}
|
||||
.context-row {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.context-row {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.context-key {
|
||||
width: 150px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.context-key {
|
||||
width: 150px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.context-value input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.context-value input {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
form {
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Context Stack</h1>
|
||||
<div id="contextContainer"></div>
|
||||
<button id="refresh">Refresh</button>
|
||||
<script src="panel.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body>
|
||||
<h1>Context Stack</h1>
|
||||
<div id="contextContainer"></div>
|
||||
<button id="refresh">Refresh</button>
|
||||
<script src="panel.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,52 +1,127 @@
|
||||
const port = browser.runtime.connect({ name: "devtools" });
|
||||
port.onMessage.addListener((message) => {
|
||||
if (message.type === "PAGE_LOADED") {
|
||||
loadContextStack(); // Refresh your context stack here.
|
||||
}
|
||||
});
|
||||
|
||||
const tabId = browser.devtools.inspectedWindow.tabId;
|
||||
|
||||
document.getElementById("refresh").addEventListener("click", loadContextStack);
|
||||
|
||||
function loadContextStack() {
|
||||
const container = document.getElementById("contextContainer");
|
||||
container.innerHTML = "";
|
||||
|
||||
browser.runtime.sendMessage({ type: "GET_CONTEXT_STACK" }).then(
|
||||
browser.runtime.sendMessage({ type: "GET_CONTEXT_STACK", tabId }).then(
|
||||
(response) => {
|
||||
if (response.error) {
|
||||
container.innerHTML = `<p>Error: ${response.error}</p>`;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response || !response.contextStack) {
|
||||
container.innerHTML = "<p>No context stack found.</p>";
|
||||
return;
|
||||
}
|
||||
|
||||
const contextStack = response.contextStack;
|
||||
for (const key in contextStack) {
|
||||
const row = document.createElement("div");
|
||||
row.classList.add("context-row");
|
||||
|
||||
const keyDiv = document.createElement("div");
|
||||
keyDiv.classList.add("context-key");
|
||||
keyDiv.textContent = key;
|
||||
|
||||
const valueDiv = document.createElement("div");
|
||||
valueDiv.classList.add("context-value");
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.value = contextStack[key];
|
||||
input.addEventListener("change", () => {
|
||||
browser.runtime.sendMessage({
|
||||
type: "UPDATE_CONTEXT_VALUE",
|
||||
key,
|
||||
value: input.value,
|
||||
}).then((updateResponse) => {
|
||||
if (updateResponse && updateResponse.success) {
|
||||
console.log(`Updated ${key} to ${input.value}`);
|
||||
} else {
|
||||
console.error(`Failed to update ${key}:`, updateResponse.error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
valueDiv.appendChild(input);
|
||||
row.appendChild(keyDiv);
|
||||
row.appendChild(valueDiv);
|
||||
container.appendChild(row);
|
||||
}
|
||||
const contextStack = coalesceContextStack(response.contextStack);
|
||||
const form = generateObjectForm(contextStack);
|
||||
container.appendChild(form);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Initial load when the panel opens.
|
||||
loadContextStack();
|
||||
function generateObjectForm(obj, path = "") {
|
||||
const detail = document.createElement("details");
|
||||
detail.open = path === "";
|
||||
const summary = document.createElement("summary");
|
||||
summary.textContent = path;
|
||||
detail.appendChild(summary);
|
||||
const form = document.createElement("form");
|
||||
let count = 0;
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value == undefined || value === "[Circular]") continue;
|
||||
const isObject = value.constructor === Object;
|
||||
const isArray = Array.isArray(value);
|
||||
if (isObject || isArray) {
|
||||
const nestedForm = generateObjectForm(
|
||||
value,
|
||||
path ? path + "." + key : key,
|
||||
);
|
||||
if (!nestedForm) {
|
||||
continue;
|
||||
}
|
||||
const div = document.createElement("div");
|
||||
div.appendChild(nestedForm);
|
||||
form.appendChild(div);
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
const div = document.createElement("div");
|
||||
const label = document.createElement("label");
|
||||
label.textContent = key;
|
||||
div.appendChild(label);
|
||||
const input = document.createElement("input");
|
||||
input.name = key;
|
||||
const isBoolean = typeof value === "boolean";
|
||||
if (isBoolean) {
|
||||
input.type = "checkbox";
|
||||
input.checked = value;
|
||||
input.addEventListener("change", () => {
|
||||
browser.runtime.sendMessage({
|
||||
type: "UPDATE_CONTEXT_VALUE",
|
||||
key: path ? path + "." + key : key,
|
||||
value: input.checked,
|
||||
tabId,
|
||||
}).then((updateResponse) => {
|
||||
if (updateResponse && updateResponse.success) {
|
||||
console.log(`Updated ${key} to ${input.checked}`);
|
||||
} else {
|
||||
console.error(`Failed to update ${key}:`, updateResponse.error);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
input.type = "text";
|
||||
input.value = value;
|
||||
input.addEventListener("change", () => {
|
||||
browser.runtime.sendMessage({
|
||||
type: "UPDATE_CONTEXT_VALUE",
|
||||
key: path ? path + "." + key : key,
|
||||
value: input.value,
|
||||
tabId,
|
||||
}).then((updateResponse) => {
|
||||
if (updateResponse && updateResponse.success) {
|
||||
console.log(`Updated ${key} to ${input.value}`);
|
||||
} else {
|
||||
console.error(`Failed to update ${key}:`, updateResponse.error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
div.appendChild(input);
|
||||
form.appendChild(div);
|
||||
count++;
|
||||
}
|
||||
if (count === 0) {
|
||||
const pre = document.createElement("pre");
|
||||
pre.textContent = JSON.stringify(obj, null, 2);
|
||||
detail.appendChild(pre);
|
||||
} else {
|
||||
detail.appendChild(form);
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
|
||||
function coalesceContextStack(contextStack) {
|
||||
const obj = {};
|
||||
for (const ctx of contextStack) {
|
||||
Object.assign(obj, ctx);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
loadContextStack();
|
||||
});
|
||||
|
115
lib/context.ts
115
lib/context.ts
@ -1,115 +0,0 @@
|
||||
type ContextStore = Record<string, any>;
|
||||
|
||||
const defaultContext: ContextStore = {};
|
||||
const contextStack: ContextStore[] = [defaultContext];
|
||||
|
||||
const debug = JSON.parse(localStorage.getItem("debug") || "false");
|
||||
|
||||
export function setDefaultContext(context: ContextStore) {
|
||||
Object.assign(defaultContext, context);
|
||||
}
|
||||
|
||||
export function withContext<T>(context: ContextStore, fn: () => T): T {
|
||||
contextStack.push(context);
|
||||
try {
|
||||
return fn();
|
||||
} finally {
|
||||
contextStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
export const ctx = new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_, prop: string) {
|
||||
for (let i = contextStack.length - 1; i >= 0; i--) {
|
||||
if (prop in contextStack[i]) return contextStack[i][prop];
|
||||
}
|
||||
if (prop in defaultContext) return defaultContext[prop];
|
||||
throw new Error(`Context variable '${prop}' is not defined.`);
|
||||
},
|
||||
},
|
||||
) as Record<string, unknown>;
|
||||
|
||||
export function getContext() {
|
||||
return ctx;
|
||||
}
|
||||
export function getContextItem<T>(prop: string): T {
|
||||
return ctx[prop] as T;
|
||||
}
|
||||
export function getContextItemOrDefault<T>(prop: string, defaultValue: T): T {
|
||||
try {
|
||||
return ctx[prop] as T;
|
||||
} catch {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
export function setContextItem<T>(prop: string, value: T) {
|
||||
Object.assign(contextStack[contextStack.length - 1] ?? defaultContext, {
|
||||
[prop]: value,
|
||||
});
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
setInterval(() => {
|
||||
let ctxEl = document.getElementById("context");
|
||||
if (!ctxEl) {
|
||||
ctxEl = document.createElement("div");
|
||||
ctxEl.id = "context";
|
||||
document.body.append(ctxEl);
|
||||
}
|
||||
ctxEl.innerHTML = "";
|
||||
const div = document.createElement("div");
|
||||
const pre = document.createElement("pre");
|
||||
const h3 = document.createElement("h3");
|
||||
h3.textContent = "Default";
|
||||
div.append(h3);
|
||||
pre.textContent = safeStringify(defaultContext);
|
||||
div.append(pre);
|
||||
ctxEl.append(div);
|
||||
for (const [idx, ctx] of contextStack.entries()) {
|
||||
const div = document.createElement("div");
|
||||
const pre = document.createElement("pre");
|
||||
const h3 = document.createElement("h3");
|
||||
h3.textContent = "CTX " + idx;
|
||||
div.append(h3);
|
||||
pre.textContent = safeStringify(ctx);
|
||||
div.append(pre);
|
||||
ctxEl.append(div);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function safeStringify(obj: any) {
|
||||
const seen = new WeakSet();
|
||||
return JSON.stringify(obj, (key, value) => {
|
||||
if (typeof value === "object" && value !== null) {
|
||||
if (seen.has(value)) {
|
||||
return "[Circular]"; // Replace circular references
|
||||
}
|
||||
seen.add(value);
|
||||
}
|
||||
return value;
|
||||
}, 2);
|
||||
}
|
||||
|
||||
const getContextStack = () => contextStack.slice();
|
||||
const updateContextValue = (
|
||||
key: string,
|
||||
value: unknown,
|
||||
depth = contextStack.length - 1,
|
||||
) => {
|
||||
const context = contextStack[depth] ?? defaultContext;
|
||||
context[key] = value;
|
||||
};
|
||||
if (location.hostname === "localhost") {
|
||||
globalThis.getContextStack = getContextStack;
|
||||
globalThis.updateContextValue = updateContextValue;
|
||||
}
|
||||
|
||||
console.log("globalThis.getContextStack", globalThis.getContextStack);
|
||||
|
||||
declare global {
|
||||
var getContextStack: () => ContextStore[];
|
||||
var updateContextValue: (key: string, value: unknown, depth?: number) => void;
|
||||
}
|
@ -92,3 +92,27 @@ function safeStringify(obj: any) {
|
||||
return value;
|
||||
}, 2);
|
||||
}
|
||||
const getContextStack = () => JSON.parse(safeStringify(contextStack));
|
||||
const updateContextValue = (
|
||||
key: string,
|
||||
value: unknown,
|
||||
depth = contextStack.length - 1,
|
||||
) => {
|
||||
console.log("updateContextValue", key, value, depth);
|
||||
const keys = key.split(".");
|
||||
let context = contextStack[depth] ?? defaultContext;
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
context = context[keys[i]];
|
||||
}
|
||||
context[keys.at(-1)!] = value;
|
||||
};
|
||||
if (location.hostname === "localhost") {
|
||||
globalThis.getContextStack = getContextStack;
|
||||
globalThis.updateContextValue = updateContextValue;
|
||||
}
|
||||
|
||||
declare global {
|
||||
var getContextStack: () => ContextStore[];
|
||||
var updateContextValue: (key: string, value: unknown, depth?: number) => void;
|
||||
var contextStack: ContextStore[];
|
||||
}
|
||||
|
@ -42,12 +42,12 @@ export class LoadState extends State<States> {
|
||||
});
|
||||
|
||||
const doodler = getContextItem<Doodler>("doodler");
|
||||
this.layers.push(doodler.createLayer((_, __, dTime) => {
|
||||
doodler.clearRect(new Vector(0, 0), doodler.width, doodler.height);
|
||||
doodler.fillRect(new Vector(0, 0), doodler.width, doodler.height, {
|
||||
color: "#302040",
|
||||
});
|
||||
}));
|
||||
// this.layers.push(doodler.createLayer((_, __, dTime) => {
|
||||
// doodler.clearRect(new Vector(0, 0), doodler.width, doodler.height);
|
||||
// doodler.fillRect(new Vector(0, 0), doodler.width, doodler.height, {
|
||||
// color: "#302040",
|
||||
// });
|
||||
// }));
|
||||
}
|
||||
override stop(): void {
|
||||
// noop
|
||||
|
Loading…
x
Reference in New Issue
Block a user