<!DOCTYPE html>
<html class="dark" data-theme="amber" lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>{{title}} โ MemScope V2</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/d3@7.8.5/dist/d3.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700;800&family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
<style>
html[data-theme="amber"] {
--primary: #F59E0B;
--primary-soft: #ffb95f;
--secondary: #ffb95f;
--tertiary: #e7c365;
--accent-amber: #F59E0B;
--warning: #F59E0B;
--success: #10B981;
--error: #ef4444;
--ke-primary: #F59E0B;
--ke-accent: #ffb95f;
--ke-accent-glow: rgba(245, 158, 11, 0.20);
--ke-on-accent: #3e2e00;
--ke-edge: #211f24;
--glow-color: rgba(245, 158, 11, 0.18);
--execution-tint: rgba(245, 158, 11, 0.18);
}
html[data-theme="indigo"] {
--primary: #cfbcff;
--primary-soft: #b39ddb;
--secondary: #b39ddb;
--tertiary: #9575cd;
--accent-amber: #cfbcff;
--warning: #b39ddb;
--success: #10B981;
--error: #ef4444;
--ke-primary: #cfbcff;
--ke-accent: #6750a4;
--ke-accent-glow: rgba(103, 80, 164, 0.22);
--ke-on-accent: #381e72;
--ke-edge: #2b2950;
--glow-color: rgba(103, 80, 164, 0.20);
--execution-tint: rgba(103, 80, 164, 0.18);
}
.text-primary { color: var(--primary) !important; }
.bg-primary { background-color: var(--primary) !important; }
.border-primary { border-color: var(--primary) !important; }
.text-secondary { color: var(--secondary) !important; }
.bg-secondary { background-color: var(--secondary) !important; }
.border-secondary { border-color: var(--secondary) !important; }
.text-tertiary { color: var(--tertiary) !important; }
.bg-tertiary { background-color: var(--tertiary) !important; }
.border-tertiary { border-color: var(--tertiary) !important; }
.text-accent-amber { color: var(--accent-amber) !important; }
.bg-accent-amber { background-color: var(--accent-amber) !important; }
.border-accent-amber { border-color: var(--accent-amber) !important; }
.text-warning { color: var(--warning) !important; }
.bg-warning { background-color: var(--warning) !important; }
.border-warning { border-color: var(--warning) !important; }
.bg-primary\/60 { background-color: color-mix(in srgb, var(--primary) 60%, transparent) !important; }
.bg-primary\/20 { background-color: color-mix(in srgb, var(--primary) 20%, transparent) !important; }
.bg-accent-amber\/20 { background-color: color-mix(in srgb, var(--accent-amber) 20%, transparent) !important; }
.bg-accent-amber\/40 { background-color: color-mix(in srgb, var(--accent-amber) 40%, transparent) !important; }
.bg-accent-amber\/60 { background-color: color-mix(in srgb, var(--accent-amber) 60%, transparent) !important; }
.border-l-4.border-primary { border-left-color: var(--primary) !important; }
.border-l-4.border-secondary { border-left-color: var(--secondary) !important; }
.border-l-4.border-tertiary { border-left-color: var(--tertiary) !important; }
.border-l-4.border-accent-amber { border-left-color: var(--accent-amber) !important; }
.border-l-4.border-success { border-left-color: var(--success) !important; }
.border-l-4.border-error { border-left-color: var(--error) !important; }
.border-l-4.border-warning { border-left-color: var(--warning) !important; }
.ke-side-item.active { background: #211f24; color: #e6e0e9; border-left: 2px solid var(--primary); }
.amber-glow { box-shadow: 0 0 15px -3px var(--glow-color); }
.indigo-glow { box-shadow: 0 0 15px 0 var(--glow-color); }
.execution-line { width: 1px; background: linear-gradient(to bottom, transparent, var(--execution-tint), transparent); }
.material-symbols-outlined { font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: #0f0d13; }
::-webkit-scrollbar-thumb { background: #36343a; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #494551; }
.rim-light { box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.05); }
.mode-section { display: none; }
.mode-section.active { display: block; }
.kpi-card { transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease; cursor: pointer; }
.kpi-card:hover { transform: translateY(-2px); box-shadow: 0 6px 18px -8px rgba(0,0,0,0.6); }
.kpi-card:active { transform: translateY(0); }
.kpi-flash { animation: kpi-flash .6s ease-out; }
@keyframes kpi-flash { 0% { box-shadow: 0 0 0 0 var(--primary); } 100% { box-shadow: 0 0 0 12px transparent; } }
.kpi-toast { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); background: #211f24; color: #e6e0e9; border: 1px solid var(--primary); border-radius: 8px; padding: 10px 16px; font-family: "JetBrains Mono", monospace; font-size: 12px; z-index: 100; opacity: 0; transition: opacity .18s ease; pointer-events: none; max-width: 80vw; }
.kpi-toast.visible { opacity: 1; }
.dashed-pulse { stroke-dasharray: 8 4; animation: dash 1s linear infinite; }
@keyframes dash { to { stroke-dashoffset: -12; } }
.chart-interactive { cursor: pointer; transition: opacity .15s ease, filter .15s ease, transform .15s ease; transform-box: fill-box; transform-origin: center; }
.chart-interactive:hover { opacity: 0.85; filter: brightness(1.25); transform: scale(1.03); }
.chart-tooltip { position: fixed; pointer-events: none; background: #211f24; color: #e6e0e9; border: 1px solid var(--primary); border-radius: 6px; padding: 8px 12px; font-family: "JetBrains Mono", monospace; font-size: 11px; z-index: 200; opacity: 0; transition: opacity .12s ease; max-width: 280px; box-shadow: 0 4px 16px -4px rgba(0,0,0,0.6); }
.chart-tooltip.visible { opacity: 1; }
.chart-tooltip .tt-label { color: var(--primary); font-weight: 700; letter-spacing: 0.05em; margin-bottom: 4px; }
.chart-tooltip .tt-row { display: flex; justify-content: space-between; gap: 12px; }
.chart-tooltip .tt-key { color: #948e9c; }
table tbody tr.chart-interactive:hover { background-color: color-mix(in srgb, var(--primary) 12%, transparent) !important; }
</style>
<script id="tailwind-config">
tailwind.config = {
darkMode: "class",
theme: {
extend: {
"colors": {
"primary-container": "#6750a4",
"inverse-surface": "#e6e0e9",
"surface-tint": "#cfbcff",
"surface-dim": "#141218",
"on-tertiary": "#3e2e00",
"on-error": "#690005",
"on-tertiary-container": "#503d00",
"on-surface-variant": "#cbc4d2",
"background": "#141218",
"secondary-fixed": "#ffddb8",
"primary-fixed": "#e9ddff",
"on-primary-fixed": "#22005d",
"inverse-primary": "#6750a4",
"on-tertiary-fixed": "#241a00",
"on-secondary": "#472a00",
"on-secondary-fixed-variant": "#653e00",
"on-error-container": "#ffdad6",
"surface-variant": "#36343a",
"secondary-container": "#ee9800",
"surface": "#141218",
"on-surface": "#e6e0e9",
"on-secondary-fixed": "#2a1700",
"on-primary-fixed-variant": "#4f378a",
"error-container": "#93000a",
"outline": "#948e9c",
"on-secondary-container": "#5b3800",
"surface-container-high": "#2b292f",
"secondary": "#ffb95f",
"on-tertiary-fixed-variant": "#594400",
"primary": "#cfbcff",
"tertiary-container": "#c9a74d",
"surface-container-low": "#1d1b20",
"tertiary-fixed-dim": "#e7c365",
"on-primary": "#381e72",
"inverse-on-surface": "#322f35",
"surface-container": "#211f24",
"primary-fixed-dim": "#cfbcff",
"outline-variant": "#494551",
"error": "#ffb4ab",
"surface-container-lowest": "#0f0d13",
"secondary-fixed-dim": "#ffb95f",
"on-primary-container": "#e0d2ff",
"on-background": "#e6e0e9",
"tertiary-fixed": "#ffdf93",
"surface-bright": "#3b383e",
"tertiary": "#e7c365",
"surface-container-highest": "#36343a",
"accent-amber": "#F59E0B",
"brick-brown": "#8E443D",
"success": "#10B981",
"warning": "#F59E0B"
},
"borderRadius": { "DEFAULT": "0.25rem", "lg": "0.5rem", "xl": "0.75rem", "full": "9999px" },
"spacing": { "container-max": "1440px", "margin-mobile": "16px", "base": "4px", "margin-desktop": "32px", "gutter": "16px" },
"fontFamily": {
"headline-md": ["JetBrains Mono"], "code-sm": ["JetBrains Mono"], "label-caps": ["JetBrains Mono"],
"headline-lg-mobile": ["JetBrains Mono"], "headline-lg": ["JetBrains Mono"],
"body-lg": ["JetBrains Mono"], "body-sm": ["JetBrains Mono"], "data-mono": ["JetBrains Mono"]
},
"fontSize": {
"headline-md": ["20px", {"lineHeight": "28px", "fontWeight": "600"}],
"code-sm": ["13px", {"lineHeight": "18px", "fontWeight": "400"}],
"label-caps": ["12px", {"lineHeight": "16px", "letterSpacing": "0.05em", "fontWeight": "700"}],
"headline-lg-mobile": ["24px", {"lineHeight": "32px", "letterSpacing": "-0.02em", "fontWeight": "700"}],
"headline-lg": ["32px", {"lineHeight": "40px", "letterSpacing": "-0.02em", "fontWeight": "700"}],
"body-lg": ["16px", {"lineHeight": "24px", "fontWeight": "400"}],
"body-sm": ["14px", {"lineHeight": "20px", "fontWeight": "400"}]
}
}
}
}
</script>
</head>
<body class="bg-surface-container-lowest text-on-surface font-body-sm selection:bg-primary-container selection:text-on-primary-container">
<div class="flex min-h-screen">
<nav class="fixed left-0 top-0 h-full flex flex-col bg-surface-container-lowest border-r border-outline-variant w-64 z-50">
<div class="p-6 flex flex-col gap-1">
<span class="font-label-caps text-label-caps text-on-surface-variant tracking-widest uppercase">memscope-rs</span>
<span class="font-body-sm text-body-sm text-outline">{{os_name}}</span>
</div>
<div class="flex-grow py-4">
<div class="ke-side-item active flex items-center gap-3 px-4 py-3 text-on-surface-variant hover:bg-surface-container-high hover:text-on-surface transition-all cursor-pointer" onclick="showMode('overview', this)">
<span class="material-symbols-outlined">dashboard</span><span class="font-body-sm">Overview</span>
</div>
<div class="ke-side-item flex items-center gap-3 px-4 py-3 text-on-surface-variant hover:bg-surface-container-high hover:text-on-surface transition-all cursor-pointer" onclick="showMode('thread', this)">
<span class="material-symbols-outlined">segment</span><span class="font-body-sm">Threads</span>
</div>
<div class="ke-side-item flex items-center gap-3 px-4 py-3 text-on-surface-variant hover:bg-surface-container-high hover:text-on-surface transition-all cursor-pointer" onclick="showMode('task', this)">
<span class="material-symbols-outlined">query_stats</span><span class="font-body-sm">Async Tasks</span>
</div>
<div class="ke-side-item flex items-center gap-3 px-4 py-3 text-on-surface-variant hover:bg-surface-container-high hover:text-on-surface transition-all cursor-pointer" onclick="showMode('taskgraph', this)">
<span class="material-symbols-outlined">account_tree</span><span class="font-body-sm">Task Graph</span>
</div>
<div class="ke-side-item flex items-center gap-3 px-4 py-3 text-on-surface-variant hover:bg-surface-container-high hover:text-on-surface transition-all cursor-pointer" onclick="showMode('variable', this)">
<span class="material-symbols-outlined">link</span><span class="font-body-sm">Variables</span>
</div>
<div class="ke-side-item flex items-center gap-3 px-4 py-3 text-on-surface-variant hover:bg-surface-container-high hover:text-on-surface transition-all cursor-pointer" onclick="showMode('passport', this)">
<span class="material-symbols-outlined">badge</span><span class="font-body-sm">Passports</span>
</div>
<div class="ke-side-item flex items-center gap-3 px-4 py-3 text-on-surface-variant hover:bg-surface-container-high hover:text-on-surface transition-all cursor-pointer" onclick="showMode('ffi', this)">
<span class="material-symbols-outlined">extension</span><span class="font-body-sm">FFI Bridge</span>
</div>
<div class="ke-side-item flex items-center gap-3 px-4 py-3 text-on-surface-variant hover:bg-surface-container-high hover:text-on-surface transition-all cursor-pointer" onclick="showMode('unsafe', this)">
<span class="material-symbols-outlined">warning</span><span class="font-body-sm">Unsafe / Time</span>
</div>
</div>
<div class="p-4 flex flex-col gap-2 border-t border-outline-variant">
<div class="flex items-center gap-2 text-[10px] font-label-caps text-on-surface-variant">
<span class="w-1.5 h-1.5 rounded-full bg-primary animate-pulse"></span>NODE_READY
</div>
<div class="text-[10px] text-outline">UP: {{system_uptime_formatted}} ยท {{cpu_cores}} cores</div>
<div class="flex items-center gap-1 mt-2 pt-2 border-t border-outline-variant/50">
<span class="text-[10px] font-label-caps text-on-surface-variant mr-1">THEME</span>
<button id="theme-amber" onclick="switchTheme('amber')" title="Amber accent" class="flex-1 py-1 rounded border border-accent-amber bg-accent-amber/20 text-accent-amber font-label-caps text-[10px] hover:bg-accent-amber/40 transition-colors">amber</button>
<button id="theme-indigo" onclick="switchTheme('indigo')" title="Indigo accent" class="flex-1 py-1 rounded border border-outline-variant text-on-surface-variant font-label-caps text-[10px] hover:bg-surface-container-high transition-colors">indigo</button>
</div>
</div>
</nav>
<main class="ml-64 flex-grow p-margin-desktop max-w-container-max">
<header class="w-full h-16 border-b border-outline-variant bg-surface sticky top-0 z-40 flex justify-between items-center px-margin-desktop">
<div class="flex items-center gap-8">
<span class="font-headline-md text-headline-md font-bold text-primary tracking-tighter">memscope-rs</span>
</div>
<div class="flex items-center gap-4">
<span class="font-data-mono text-xs text-on-surface-variant">{{export_timestamp}}</span>
</div>
</header>
{{> dashboard_modes}}
</main>
</div>
<footer class="fixed bottom-0 left-0 ml-64 w-[calc(100%-16rem)] h-6 bg-surface-container-lowest border-t border-outline-variant z-50 flex justify-between items-center px-4">
<div class="flex items-center gap-4">
<div class="flex items-center gap-1"><span class="w-1.5 h-1.5 bg-primary rounded-full animate-pulse"></span><span class="font-data-mono text-[9px] uppercase text-on-surface-variant">Telemetry Linked</span></div>
<div class="flex items-center gap-1 border-l border-outline-variant pl-4"><span class="font-data-mono text-[9px] text-on-surface-variant">CPU: {{system_resources.cpu_usage_pct}}%</span></div>
<div class="flex items-center gap-1 border-l border-outline-variant pl-4"><span class="font-data-mono text-[9px] text-on-surface-variant">MEM: {{system_resources.used_physical}} / {{system_resources.total_physical}}</span></div>
</div>
<div class="font-data-mono text-[9px] text-on-surface-variant">UTC: <span id="clock">{{export_timestamp}}</span></div>
</footer>
<script id="dashboard-json-data" type="application/json">{{{json_data}}}</script>
<script>
function showMode(mode, el) {
document.querySelectorAll('.mode-section').forEach(s => s.classList.remove('active'));
const target = document.getElementById('mode-' + mode);
if (target) target.classList.add('active');
document.querySelectorAll('.ke-side-item').forEach(i => i.classList.remove('active'));
if (el) el.classList.add('active');
}
{{> dashboard_runtime}}
{{> dashboard_charts}}
{{> dashboard_graphs}}
{{> dashboard_interactions}}
</script>
<div id="kpiToast" class="kpi-toast"></div>
</body>
</html>