// code.js — extends the shared AppItems array with a "System Info" item
(function(){
// Fallback if AppItems isn't defined yet (should be, per index.php)
window.AppItems = window.AppItems || [];
const escapeHTML = (s) =>
String(s).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
const codeBlock = (title, obj, id = "") => {
const json = escapeHTML(JSON.stringify(obj, null, 2));
const idAttr = id ? ` id="${id}"` : "";
return `
<section class="app-code-block"${idAttr}>
<h3 style="margin:.25rem 0 .5rem 0; font-size:1rem;">${escapeHTML(title)}</h3>
<pre style="background:#0b1320;border:1px solid #1f2a3a;border-radius:12px;padding:1rem;overflow:auto;margin:0;">
<code style="font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace; font-size:.9rem; line-height:1.4;">
${json}
</code></pre>
</section>
`;
};
const tryGet = (fn, fallback = null) => { try { return fn(); } catch { return fallback; } };
function collectEnvironment() {
const nav = navigator;
return {
userAgent: nav.userAgent || null,
platform: nav.platform || null,
language: nav.language || null,
languages: nav.languages || null,
hardwareConcurrency: nav.hardwareConcurrency ?? null,
deviceMemory: nav.deviceMemory ?? null,
cookieEnabled: nav.cookieEnabled ?? null,
doNotTrack: nav.doNotTrack ?? null
};
}
function collectWindow() {
return {
inner: { width: window.innerWidth, height: window.innerHeight },
outer: { width: window.outerWidth, height: window.outerHeight },
devicePixelRatio: window.devicePixelRatio || 1,
page: { url: location.href, origin: location.origin, pathname: location.pathname }
};
}
function collectPerformance() {
const pf = performance;
const navEntries = tryGet(() => pf.getEntriesByType('navigation')[0], null);
const mem = tryGet(() => pf.memory, null); // Chrome-only
const timing = pf.timing && pf.timing.toJSON ? pf.timing.toJSON() : pf.timing;
return {
navigationType: navEntries ? navEntries.type : tryGet(() => performance.navigation.type, null),
timeOrigin: pf.timeOrigin,
now: pf.now(),
memory: mem ? {
jsHeapSizeLimit: mem.jsHeapSizeLimit,
totalJSHeapSize: mem.totalJSHeapSize,
usedJSHeapSize: mem.usedJSHeapSize
} : null,
timing: timing || null
};
}
function collectNetworkLite() {
const c = navigator.connection || navigator.webkitConnection || navigator.mozConnection;
return c ? {
downlink: c.downlink ?? null,
effectiveType: c.effectiveType ?? null,
rtt: c.rtt ?? null,
saveData: c.saveData ?? null
} : null;
}
// Build the HTML once (keeps this item simple + deterministic)
const env = collectEnvironment();
const win = collectWindow();
const perf = collectPerformance();
const net = collectNetworkLite();
const systemInfoHTML = `
${codeBlock('navigator / environment', env)}
${codeBlock('window / viewport', win)}
${codeBlock('performance', perf)}
${codeBlock('network (navigator.connection)', net)}
`;
// Push the new item into the shared array
window.AppItems.push({
title: 'System Info',
html: systemInfoHTML
});
// Optional: if you later need async updates (like battery), you can enhance
// after open by listening for a custom event you dispatch from overlay.js.
// For now, keeping it static makes it robust and modular.
})();