const iframeStorageBridge = (nonce) => (
/* js */
`
(function() {
const memoryStore = {};
const NONCE = ${JSON.stringify(nonce)}
const mockStorage = {
getItem: function(key) {
return memoryStore[key] !== undefined ? memoryStore[key] : null;
},
setItem: function(key, value) {
memoryStore[key] = String(value);
window.parent.postMessage({
type: 'storage-set',
key: key,
value: String(value),
nonce: NONCE
}, '*');
},
removeItem: function(key) {
delete memoryStore[key];
window.parent.postMessage({
type: 'storage-remove',
key: key,
nonce: NONCE
}, '*');
},
clear: function() {
for (const key in memoryStore) {
delete memoryStore[key];
}
window.parent.postMessage({
type: 'storage-clear',
nonce: NONCE
}, '*');
},
key: function(index) {
const keys = Object.keys(memoryStore);
return keys[index] !== undefined ? keys[index] : null;
},
get length() {
return Object.keys(memoryStore).length;
}
};
try {
Object.defineProperty(window, 'localStorage', {
value: mockStorage,
writable: false,
configurable: true
});
} catch (e) {
window.localStorage = mockStorage;
}
window.addEventListener('message', function(event) {
if (event.data.type === 'storage-sync-data' && event.data.nonce === NONCE) {
const data = event.data.data;
for (const key in data) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
memoryStore[key] = data[key];
}
}
if (typeof window.initTheme === 'function') {
window.initTheme();
}
window.dispatchEvent(new Event('storage-ready'));
}
});
window.parent.postMessage({
type: 'storage-sync-request',
nonce: NONCE
}, '*');
})();
`
);
const parentStorageBridge = (nonce) => (
/* js */
`
(function() {
const host = document.querySelector('nuxt-error-overlay');
if (!host) return;
// Wait for shadow root to be attached
const checkShadow = setInterval(function() {
if (host.shadowRoot) {
clearInterval(checkShadow);
const iframe = host.shadowRoot.getElementById('frame');
if (!iframe) return;
const NONCE = ${JSON.stringify(nonce)}
window.addEventListener('message', function(event) {
if (!event.data || event.data.nonce !== NONCE) return;
const data = event.data;
if (data.type === 'storage-set') {
localStorage.setItem(data.key, data.value);
} else if (data.type === 'storage-remove') {
localStorage.removeItem(data.key);
} else if (data.type === 'storage-clear') {
localStorage.clear();
} else if (data.type === 'storage-sync-request') {
const allData = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
allData[key] = localStorage.getItem(key);
}
iframe.contentWindow.postMessage({
type: 'storage-sync-data',
data: allData,
nonce: NONCE
}, '*');
}
});
}
}, 10);
})();
`
);
const errorCSS = (
/* css */
`
:host {
--preview-width: 240px;
--preview-height: 180px;
--base-width: 1200px;
--base-height: 900px;
--z-base: 999999998;
all: initial;
display: contents;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
#frame {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
border: none;
z-index: var(--z-base);
}
#frame[inert] {
right: 5px;
bottom: 5px;
left: auto;
top: auto;
width: var(--base-width);
height: var(--base-height);
transform: scale(calc(240 / 1200));
transform-origin: bottom right;
overflow: hidden;
border-radius: calc(1200 * 8px / 240);
}
#preview {
position: fixed;
right: 5px;
bottom: 5px;
width: var(--preview-width);
height: var(--preview-height);
overflow: hidden;
border-radius: 8px;
pointer-events: none;
z-index: var(--z-base);
background: white;
display: none;
}
#frame:not([inert]) + #preview {
display: block;
}
#toggle {
position: fixed;
right: 5px;
bottom: 5px;
width: var(--preview-width);
height: var(--preview-height);
background: none;
border: 3px solid #00DC82;
border-radius: 8px;
cursor: pointer;
opacity: 0.8;
transition: opacity 0.2s, box-shadow 0.2s;
z-index: calc(var(--z-base) + 1);
}
#toggle:hover,
#toggle:focus {
opacity: 1;
box-shadow: 0 0 20px rgba(0, 220, 130, 0.6);
}
#toggle:focus-visible {
outline: 3px solid #00DC82;
outline-offset: 3px;
box-shadow: 0 0 24px rgba(0, 220, 130, 0.8);
}
@media (prefers-reduced-motion: reduce) {
#toggle {
transition: none;
}
}
`
);
function webComponentScript(base64HTML, startMinimized) {
return (
/* js */
`
(function() {
try {
const host = document.querySelector('nuxt-error-overlay');
if (!host) return;
const shadow = host.attachShadow({ mode: 'open' });
// Create elements
const style = document.createElement('style');
style.textContent = ${JSON.stringify(errorCSS)};
const iframe = document.createElement('iframe');
iframe.id = 'frame';
iframe.src = 'data:text/html;base64,${base64HTML}';
iframe.title = 'Detailed error stack trace';
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
const preview = document.createElement('div');
preview.id = 'preview';
const button = document.createElement('button');
button.id = 'toggle';
button.setAttribute('aria-expanded', 'true');
button.setAttribute('type', 'button');
button.innerHTML = 'Toggle detailed error view';
const liveRegion = document.createElement('div');
liveRegion.setAttribute('role', 'status');
liveRegion.setAttribute('aria-live', 'polite');
liveRegion.className = 'sr-only';
// Update preview snapshot
function updatePreview() {
try {
let previewIframe = preview.querySelector('iframe');
if (!previewIframe) {
previewIframe = document.createElement('iframe');
previewIframe.style.cssText = 'width: 1200px; height: 900px; transform: scale(0.2); transform-origin: top left; border: none;';
previewIframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
preview.appendChild(previewIframe);
}
const doctype = document.doctype ? '' : '';
const cleanedHTML = document.documentElement.outerHTML
.replace(/]*>.*?<\\/nuxt-error-overlay>/gs, '')
.replace(/