memscope-rs 0.2.6

A memory tracking library for Rust applications.
Documentation
<!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&amp;family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<style>
    /* ============================================================
       Whole-theme system โ€” amber (yellow-dominant) vs indigo (purple-dominant).
       Each theme defines the same set of CSS variables; the override rules
       below map Tailwind utility classes to those variables. Flipping
       data-theme on <html> swaps EVERY surface (text, bg, border, glow) at
       once โ€” no leftover accent from the other palette.
       ============================================================ */
    html[data-theme="amber"] {
        /* Yellow-dominant: primary, accent, secondary, tertiary all amber-family. */
        --primary: #F59E0B;          /* main bright amber */
        --primary-soft: #ffb95f;     /* warm amber for secondary surfaces */
        --secondary: #ffb95f;        /* warm amber */
        --tertiary: #e7c365;         /* gold */
        --accent-amber: #F59E0B;     /* amber accent (same family) */
        --warning: #F59E0B;
        --success: #10B981;          /* semantic green kept for success */
        --error: #ef4444;            /* semantic red kept for errors */
        --ke-primary: #F59E0B;       /* legacy alias used by older rules */
        --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"] {
        /* Purple-dominant: primary, accent, secondary, tertiary all purple-family. */
        --primary: #cfbcff;          /* light purple for text on dark bg */
        --primary-soft: #b39ddb;     /* medium purple */
        --secondary: #b39ddb;        /* medium purple */
        --tertiary: #9575cd;         /* deeper purple */
        --accent-amber: #cfbcff;     /* purple replaces amber */
        --warning: #b39ddb;          /* purple warning */
        --success: #10B981;
        --error: #ef4444;
        --ke-primary: #cfbcff;
        --ke-accent: #6750a4;        /* deep purple for filled accents */
        --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);
    }

    /* ---- Global Tailwind utility overrides (apply in BOTH themes) ----
       These re-map every color utility to the theme's CSS variable, so
       flipping data-theme swaps all surfaces atomically. !important is
       required to beat Tailwind CDN's generated utilities. */
    .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; }
    /* Opacity-modified backgrounds โ€” use color-mix to keep theme variable. */
    .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; }
    /* Left-border accents (KPI strip, ownership stat cards). */
    .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; }
    /* Sidebar active item + glow boxes. */
    .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 interaction โ€” hover lift + click ripple, with tooltip via [title]. */
    .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; } }
    /* Drill-down toast */
    .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; } }
    /* Interactive chart elements โ€” cursor + transition for hover feedback.
       transform-box: fill-box is CRITICAL for SVG elements โ€” without it,
       transform-origin: center refers to the SVG canvas origin (0,0) instead
       of the element's own center, causing nodes to jump position on hover
       and creating an infinite hover/unhover flicker loop. */
    .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); }
    /* Hover tooltip โ€” used by all chart elements that don't have native title. */
    .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 row hover + click โ€” chart-interactive pattern applied to rows. */
    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">
    <!-- SideNavBar -->
    <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 bar -->
        <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 status bar -->
<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>

<!-- Hidden JSON data injection point (read by client-side JS) -->
<script id="dashboard-json-data" type="application/json">{{{json_data}}}</script>

<!-- Mode switching + basic client-side rendering -->
<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>