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", tabId }).then( (response) => { if (response.error) { container.innerHTML = `
Error: ${response.error}
`; return; } if (!response || !response.contextStack) { container.innerHTML = "No context stack found.
"; return; } const contextStack = coalesceContextStack(response.contextStack); const form = generateObjectForm(contextStack); container.appendChild(form); }, ); } function generateObjectForm(obj, path = "") { const detail = document.createElement("details"); detail.open = path === ""; const summary = document.createElement("summary"); summary.textContent = path.split(".").at(-1); 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(); });