Files
MyFastHtml/examples/profiler_mockup_2.html

921 lines
35 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Profiler — UI Mockup 2</title>
<style>
/* ------------------------------------------------------------------ */
/* Base — mirrors DaisyUI CSS variables */
/* ------------------------------------------------------------------ */
:root {
--hcg-bg-main: #0d1117;
--hcg-bg-button: rgba(22, 27, 34, 0.92);
--hcg-border: #30363d;
--hcg-text-muted: rgba(230, 237, 243, 0.45);
--hcg-text-primary: #e6edf3;
--hcg-node-bg: #1c2128;
--hcg-node-bg-selected: color-mix(in oklab, #1c2128 70%, #f0883e 30%);
--profiler-danger: #f85149;
--profiler-warn: #e3b341;
--profiler-ok: #3fb950;
--profiler-accent: #58a6ff;
/* Fonts — mirrors myfasthtml.css */
--font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
--text-xs: 0.6875rem;
--text-sm: 0.8125rem;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: var(--hcg-bg-main);
color: var(--hcg-text-primary);
font-family: var(--font-sans);
font-size: var(--text-sm);
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* ------------------------------------------------------------------ */
/* Toolbar — icon-only, no Menu control */
/* ------------------------------------------------------------------ */
.mf-profiler-toolbar {
display: flex;
align-items: center;
gap: 2px;
padding: 5px 10px;
background: var(--hcg-node-bg);
border-bottom: 1px solid var(--hcg-border);
flex-shrink: 0;
}
.mf-profiler-toolbar-sep {
width: 1px;
height: 18px;
background: var(--hcg-border);
margin: 0 6px;
}
/* Icon button — matches mk.icon() style */
.mf-icon-btn {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
border: none;
background: transparent;
color: var(--hcg-text-muted);
cursor: pointer;
transition: background 0.12s, color 0.12s;
position: relative;
}
.mf-icon-btn:hover {
background: color-mix(in oklab, var(--hcg-node-bg) 60%, var(--hcg-text-primary) 15%);
color: var(--hcg-text-primary);
}
.mf-icon-btn.active {
color: var(--profiler-ok);
}
.mf-icon-btn.active:hover {
background: color-mix(in oklab, var(--hcg-node-bg) 60%, var(--profiler-ok) 20%);
}
.mf-icon-btn.danger {
color: var(--profiler-danger);
}
.mf-icon-btn.danger:hover {
background: color-mix(in oklab, var(--hcg-node-bg) 70%, var(--profiler-danger) 20%);
}
.mf-icon-btn.view-active {
background: color-mix(in oklab, var(--hcg-node-bg) 60%, var(--profiler-accent) 25%);
color: var(--profiler-accent);
}
/* Tooltip */
.mf-icon-btn[data-tip]:hover::after {
content: attr(data-tip);
position: absolute;
top: calc(100% + 6px);
left: 50%;
transform: translateX(-50%);
background: #2d333b;
border: 1px solid var(--hcg-border);
border-radius: 4px;
padding: 3px 8px;
font-size: var(--text-xs);
white-space: nowrap;
color: var(--hcg-text-primary);
pointer-events: none;
z-index: 100;
font-family: var(--font-sans);
}
.mf-profiler-overhead {
margin-left: auto;
color: var(--hcg-text-muted);
font-size: var(--text-xs);
font-family: var(--font-sans);
display: flex;
gap: 16px;
}
.mf-profiler-overhead span b {
font-family: var(--font-mono);
color: var(--profiler-warn);
font-weight: 500;
}
/* ------------------------------------------------------------------ */
/* Split layout */
/* ------------------------------------------------------------------ */
.mf-profiler-body {
display: flex;
flex: 1;
overflow: hidden;
}
/* ------------------------------------------------------------------ */
/* Trace list (left) */
/* ------------------------------------------------------------------ */
.mf-profiler-list {
width: 360px;
flex-shrink: 0;
border-right: 1px solid var(--hcg-border);
display: flex;
flex-direction: column;
overflow: hidden;
}
.mf-profiler-list-header {
display: grid;
grid-template-columns: 1fr 76px 100px;
padding: 5px 10px;
border-bottom: 1px solid var(--hcg-border);
color: var(--hcg-text-muted);
font-size: var(--text-xs);
text-transform: uppercase;
letter-spacing: 0.06em;
background: var(--hcg-node-bg);
}
.mf-profiler-list-body {
overflow-y: auto;
flex: 1;
}
.mf-profiler-row {
display: grid;
grid-template-columns: 1fr 76px 100px;
padding: 6px 10px;
border-bottom: 1px solid rgba(48, 54, 61, 0.5);
cursor: pointer;
transition: background 0.1s;
align-items: center;
}
.mf-profiler-row:hover {
background: color-mix(in oklab, var(--hcg-node-bg) 50%, var(--profiler-accent) 5%);
}
.mf-profiler-row.selected {
background: var(--hcg-node-bg-selected);
border-left: 2px solid #f0883e;
padding-left: 8px;
}
.mf-profiler-cmd {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-family: var(--font-sans);
font-size: var(--text-sm);
}
.mf-profiler-duration {
text-align: right;
font-family: var(--font-mono);
font-size: var(--text-xs);
}
.mf-profiler-duration.fast {
color: var(--profiler-ok);
}
.mf-profiler-duration.medium {
color: var(--profiler-warn);
}
.mf-profiler-duration.slow {
color: var(--profiler-danger);
}
.mf-profiler-ts {
text-align: right;
font-family: var(--font-mono);
font-size: var(--text-xs);
color: var(--hcg-text-muted);
}
/* ------------------------------------------------------------------ */
/* Detail panel (right) */
/* ------------------------------------------------------------------ */
.mf-profiler-detail {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.mf-profiler-detail-header {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 10px;
border-bottom: 1px solid var(--hcg-border);
background: var(--hcg-node-bg);
}
.mf-profiler-detail-title {
font-size: var(--text-sm);
font-family: var(--font-sans);
color: var(--hcg-text-primary);
font-weight: 500;
flex: 1;
}
.mf-profiler-detail-title span {
font-family: var(--font-mono);
color: var(--profiler-accent);
}
/* View toggle in detail header */
.mf-profiler-view-toggle {
display: flex;
gap: 2px;
}
.mf-profiler-detail-body {
flex: 1;
overflow-y: auto;
padding: 10px;
display: flex;
flex-direction: column;
gap: 10px;
}
/* ------------------------------------------------------------------ */
/* Properties-style cards (reuses properties.css variables) */
/* ------------------------------------------------------------------ */
.mf-properties-group-card {
background: var(--hcg-node-bg);
border: 1px solid var(--hcg-border);
border-radius: 5px;
overflow: hidden;
}
.mf-properties-group-header {
padding: 4px 10px;
background: linear-gradient(135deg,
color-mix(in oklab, var(--profiler-accent) 40%, var(--hcg-node-bg)) 0%,
var(--hcg-node-bg) 100%
);
color: var(--hcg-text-primary);
font-size: var(--text-xs);
font-family: var(--font-sans);
font-weight: 600;
border-bottom: 1px solid var(--hcg-border);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.mf-properties-row {
display: grid;
grid-template-columns: 130px 1fr;
border-bottom: 1px solid rgba(48, 54, 61, 0.4);
}
.mf-properties-row:last-child {
border-bottom: none;
}
.mf-properties-row:hover {
background: color-mix(in oklab, var(--hcg-node-bg) 60%, var(--hcg-text-primary) 3%);
}
.mf-properties-key {
padding: 4px 10px;
color: var(--hcg-text-muted);
font-family: var(--font-sans);
font-size: var(--text-xs);
border-right: 1px solid var(--hcg-border);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mf-properties-value {
padding: 4px 10px;
color: var(--hcg-text-primary);
font-family: var(--font-mono); /* monospace for values */
font-size: var(--text-xs);
word-break: break-all;
}
.mf-properties-value.danger {
color: var(--profiler-danger);
}
/* ------------------------------------------------------------------ */
/* Span tree view */
/* ------------------------------------------------------------------ */
.mf-profiler-span-tree {
border: 1px solid var(--hcg-border);
border-radius: 5px;
overflow: hidden;
}
.mf-profiler-span-tree-header {
padding: 4px 10px;
background: linear-gradient(135deg,
color-mix(in oklab, var(--profiler-accent) 40%, var(--hcg-node-bg)) 0%,
var(--hcg-node-bg) 100%
);
color: var(--hcg-text-primary);
font-size: var(--text-xs);
font-family: var(--font-sans);
font-weight: 600;
border-bottom: 1px solid var(--hcg-border);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.mf-profiler-span-row {
display: flex;
align-items: center;
padding: 4px 10px;
border-bottom: 1px solid rgba(48, 54, 61, 0.4);
gap: 0;
}
.mf-profiler-span-row:last-child {
border-bottom: none;
}
.mf-profiler-span-row:hover {
background: color-mix(in oklab, var(--hcg-node-bg) 60%, var(--hcg-text-primary) 3%);
}
.mf-profiler-span-indent {
flex-shrink: 0;
border-left: 1px solid rgba(48, 54, 61, 0.6);
align-self: stretch;
}
.mf-profiler-span-body {
flex: 1;
display: flex;
align-items: center;
gap: 8px;
padding-left: 6px;
}
.mf-profiler-span-name {
min-width: 130px;
font-family: var(--font-sans);
font-size: var(--text-xs);
white-space: nowrap;
}
.mf-profiler-span-name.root {
font-weight: 600;
color: var(--hcg-text-primary);
}
.mf-profiler-span-bar-bg {
flex: 1;
height: 5px;
background: rgba(48, 54, 61, 0.7);
border-radius: 3px;
overflow: hidden;
}
.mf-profiler-span-bar {
height: 100%;
border-radius: 3px;
background: var(--profiler-accent);
}
.mf-profiler-span-bar.slow {
background: var(--profiler-danger);
}
.mf-profiler-span-bar.medium {
background: var(--profiler-warn);
}
.mf-profiler-span-ms {
font-family: var(--font-mono);
font-size: var(--text-xs);
color: var(--hcg-text-muted);
min-width: 58px;
text-align: right;
}
.mf-profiler-span-ms.slow {
color: var(--profiler-danger);
}
.mf-profiler-span-ms.medium {
color: var(--profiler-warn);
}
.mf-profiler-cumulative-badge {
font-family: var(--font-mono);
font-size: 10px;
padding: 1px 5px;
border-radius: 3px;
background: rgba(88, 166, 255, 0.1);
border: 1px solid rgba(88, 166, 255, 0.25);
color: var(--profiler-accent);
flex-shrink: 0;
white-space: nowrap;
}
/* ------------------------------------------------------------------ */
/* Pie chart view (placeholder) */
/* ------------------------------------------------------------------ */
.mf-profiler-pie-view {
border: 1px solid var(--hcg-border);
border-radius: 5px;
overflow: hidden;
display: none;
}
.mf-profiler-pie-view.visible {
display: block;
}
.mf-profiler-pie-view-header {
padding: 4px 10px;
background: linear-gradient(135deg,
color-mix(in oklab, var(--profiler-accent) 40%, var(--hcg-node-bg)) 0%,
var(--hcg-node-bg) 100%
);
color: var(--hcg-text-primary);
font-size: var(--text-xs);
font-family: var(--font-sans);
font-weight: 600;
border-bottom: 1px solid var(--hcg-border);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.mf-profiler-pie-placeholder {
padding: 20px;
display: flex;
gap: 24px;
align-items: center;
justify-content: center;
}
/* SVG pie slices — static mockup */
.mf-profiler-pie-legend {
display: flex;
flex-direction: column;
gap: 6px;
}
.mf-profiler-pie-legend-item {
display: flex;
align-items: center;
gap: 7px;
font-size: var(--text-xs);
font-family: var(--font-sans);
}
.mf-profiler-pie-legend-color {
width: 10px;
height: 10px;
border-radius: 2px;
flex-shrink: 0;
}
.mf-profiler-pie-legend-pct {
font-family: var(--font-mono);
color: var(--hcg-text-muted);
margin-left: auto;
padding-left: 12px;
}
/* Empty state */
.mf-profiler-empty {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: var(--hcg-text-muted);
font-size: var(--text-sm);
}
/* Scrollbar */
::-webkit-scrollbar {
width: 5px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--hcg-border);
border-radius: 3px;
}
</style>
</head>
<body>
<!-- ================================================================== -->
<!-- Toolbar — icon-only, no Menu -->
<!-- ================================================================== -->
<div class="mf-profiler-toolbar">
<!-- Enable / Disable toggle -->
<button class="mf-icon-btn active" data-tip="Disable profiler" onclick="toggleEnabled(this)">
<!-- Fluent: record_stop (enabled state) -->
<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
<circle cx="10" cy="10" r="5"/>
</svg>
</button>
<!-- Clear traces -->
<button class="mf-icon-btn danger" data-tip="Clear traces" onclick="clearTraces()">
<!-- Fluent: delete -->
<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
<path d="M8.5 4h3a.5.5 0 0 0-1 0h-1a.5.5 0 0 0-1 0Zm-1 0a1.5 1.5 0 0 1 3 0h3a.5.5 0 0 1 0 1h-.554l-.853 8.533A1.5 1.5 0 0 1 10.606 15H9.394a1.5 1.5 0 0 1-1.487-1.467L7.054 5H6.5a.5.5 0 0 1 0-1h1Z"/>
</svg>
</button>
<div class="mf-profiler-toolbar-sep"></div>
<!-- Overhead metrics -->
<div class="mf-profiler-overhead">
<span>Overhead/span: <b>1.2 µs</b></span>
<span>Total overhead: <b>0.04 ms</b></span>
<span>Traces: <b id="trace-count">8</b> / 500</span>
</div>
</div>
<!-- ================================================================== -->
<!-- Body: list + detail -->
<!-- ================================================================== -->
<div class="mf-profiler-body">
<!-- ---------------------------------------------------------------- -->
<!-- Trace list -->
<!-- ---------------------------------------------------------------- -->
<div class="mf-profiler-list">
<div class="mf-profiler-list-header">
<span>Command</span>
<span style="text-align:right">Duration</span>
<span style="text-align:right">Time</span>
</div>
<div class="mf-profiler-list-body" id="trace-list">
<div class="mf-profiler-row selected" onclick="selectRow(this)">
<span class="mf-profiler-cmd">NavigateCell</span>
<span class="mf-profiler-duration slow">173.4 ms</span>
<span class="mf-profiler-ts">14:32:07.881</span>
</div>
<div class="mf-profiler-row" onclick="selectRow(this)">
<span class="mf-profiler-cmd">NavigateCell</span>
<span class="mf-profiler-duration slow">168.1 ms</span>
<span class="mf-profiler-ts">14:32:07.712</span>
</div>
<div class="mf-profiler-row" onclick="selectRow(this)">
<span class="mf-profiler-cmd">SelectRow</span>
<span class="mf-profiler-duration medium">42.7 ms</span>
<span class="mf-profiler-ts">14:32:06.501</span>
</div>
<div class="mf-profiler-row" onclick="selectRow(this)">
<span class="mf-profiler-cmd">FilterChanged</span>
<span class="mf-profiler-duration medium">38.2 ms</span>
<span class="mf-profiler-ts">14:32:05.334</span>
</div>
<div class="mf-profiler-row" onclick="selectRow(this)">
<span class="mf-profiler-cmd">NavigateCell</span>
<span class="mf-profiler-duration fast">12.0 ms</span>
<span class="mf-profiler-ts">14:32:04.102</span>
</div>
<div class="mf-profiler-row" onclick="selectRow(this)">
<span class="mf-profiler-cmd">SortColumn</span>
<span class="mf-profiler-duration fast">8.4 ms</span>
<span class="mf-profiler-ts">14:32:03.770</span>
</div>
<div class="mf-profiler-row" onclick="selectRow(this)">
<span class="mf-profiler-cmd">SelectRow</span>
<span class="mf-profiler-duration fast">5.1 ms</span>
<span class="mf-profiler-ts">14:32:02.441</span>
</div>
<div class="mf-profiler-row" onclick="selectRow(this)">
<span class="mf-profiler-cmd">NavigateCell</span>
<span class="mf-profiler-duration fast">4.8 ms</span>
<span class="mf-profiler-ts">14:32:01.003</span>
</div>
</div>
</div>
<!-- ---------------------------------------------------------------- -->
<!-- Detail panel -->
<!-- ---------------------------------------------------------------- -->
<div class="mf-profiler-detail">
<!-- Header with tree/pie toggle -->
<div class="mf-profiler-detail-header">
<span class="mf-profiler-detail-title">
<span>NavigateCell</span> — 173.4 ms
</span>
<div class="mf-profiler-view-toggle">
<!-- Tree view -->
<button class="mf-icon-btn view-active" id="btn-tree" data-tip="Span tree"
onclick="switchView('tree')">
<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor">
<path d="M3 4.5A1.5 1.5 0 0 1 4.5 3h11A1.5 1.5 0 0 1 17 4.5v1A1.5 1.5 0 0 1 15.5 7h-11A1.5 1.5 0 0 1 3 5.5v-1ZM3 10a1.5 1.5 0 0 1 1.5-1.5h6A1.5 1.5 0 0 1 12 10v1a1.5 1.5 0 0 1-1.5 1.5h-6A1.5 1.5 0 0 1 3 11v-1Zm0 5.5A1.5 1.5 0 0 1 4.5 14h4a1.5 1.5 0 0 1 1.5 1.5v1A1.5 1.5 0 0 1 8.5 18h-4A1.5 1.5 0 0 1 3 16.5v-1Z"/>
</svg>
</button>
<!-- Pie view -->
<button class="mf-icon-btn" id="btn-pie" data-tip="Pie chart"
onclick="switchView('pie')">
<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor">
<path d="M10 2a8 8 0 1 1 0 16A8 8 0 0 1 10 2Zm0 1.5A6.5 6.5 0 1 0 16.5 10H10a.5.5 0 0 1-.5-.5V3.5Zm1 .07V9h5.43A6.51 6.51 0 0 0 11 3.57Z"/>
</svg>
</button>
</div>
</div>
<div class="mf-profiler-detail-body">
<!-- Metadata -->
<div class="mf-properties-group-card">
<div class="mf-properties-group-header">Metadata</div>
<div class="mf-properties-row">
<div class="mf-properties-key">command</div>
<div class="mf-properties-value">NavigateCell</div>
</div>
<div class="mf-properties-row">
<div class="mf-properties-key">total_duration_ms</div>
<div class="mf-properties-value danger">173.4</div>
</div>
<div class="mf-properties-row">
<div class="mf-properties-key">timestamp</div>
<div class="mf-properties-value">2026-03-21 14:32:07.881</div>
</div>
</div>
<!-- kwargs -->
<div class="mf-properties-group-card">
<div class="mf-properties-group-header">kwargs</div>
<div class="mf-properties-row">
<div class="mf-properties-key">row</div>
<div class="mf-properties-value">12</div>
</div>
<div class="mf-properties-row">
<div class="mf-properties-key">col</div>
<div class="mf-properties-value">3</div>
</div>
<div class="mf-properties-row">
<div class="mf-properties-key">direction</div>
<div class="mf-properties-value">down</div>
</div>
</div>
<!-- ============================================================ -->
<!-- Span tree view -->
<!-- ============================================================ -->
<div class="mf-profiler-span-tree" id="view-tree">
<div class="mf-profiler-span-tree-header">Span breakdown</div>
<!-- Root -->
<div class="mf-profiler-span-row">
<div class="mf-profiler-span-body">
<span class="mf-profiler-span-name root">NavigateCell</span>
<div class="mf-profiler-span-bar-bg">
<div class="mf-profiler-span-bar slow" style="width:100%"></div>
</div>
<span class="mf-profiler-span-ms slow">173.4 ms</span>
</div>
</div>
<!-- before_commands — depth 1 -->
<div class="mf-profiler-span-row">
<div class="mf-profiler-span-indent" style="width:14px"></div>
<div class="mf-profiler-span-body">
<span class="mf-profiler-span-name">before_commands</span>
<div class="mf-profiler-span-bar-bg">
<div class="mf-profiler-span-bar" style="width:0.5%"></div>
</div>
<span class="mf-profiler-span-ms">0.8 ms</span>
</div>
</div>
<!-- callback — depth 1 -->
<div class="mf-profiler-span-row">
<div class="mf-profiler-span-indent" style="width:14px"></div>
<div class="mf-profiler-span-body">
<span class="mf-profiler-span-name">callback</span>
<div class="mf-profiler-span-bar-bg">
<div class="mf-profiler-span-bar slow" style="width:88%"></div>
</div>
<span class="mf-profiler-span-ms slow">152.6 ms</span>
</div>
</div>
<!-- navigate_cell — depth 2 -->
<div class="mf-profiler-span-row">
<div class="mf-profiler-span-indent" style="width:14px"></div>
<div class="mf-profiler-span-indent" style="width:14px"></div>
<div class="mf-profiler-span-body">
<span class="mf-profiler-span-name">navigate_cell</span>
<div class="mf-profiler-span-bar-bg">
<div class="mf-profiler-span-bar slow" style="width:86%"></div>
</div>
<span class="mf-profiler-span-ms slow">149.0 ms</span>
</div>
</div>
<!-- process_row cumulative — depth 3 -->
<div class="mf-profiler-span-row">
<div class="mf-profiler-span-indent" style="width:14px"></div>
<div class="mf-profiler-span-indent" style="width:14px"></div>
<div class="mf-profiler-span-indent" style="width:14px"></div>
<div class="mf-profiler-span-body">
<span class="mf-profiler-span-name">process_row</span>
<div class="mf-profiler-span-bar-bg">
<div class="mf-profiler-span-bar medium" style="width:80%"></div>
</div>
<span class="mf-profiler-span-ms medium">138.5 ms</span>
</div>
<span class="mf-profiler-cumulative-badge">×1000 · min 0.10 · avg 0.14 · max 0.40 ms</span>
</div>
<!-- after_commands — depth 1 -->
<div class="mf-profiler-span-row">
<div class="mf-profiler-span-indent" style="width:14px"></div>
<div class="mf-profiler-span-body">
<span class="mf-profiler-span-name">after_commands</span>
<div class="mf-profiler-span-bar-bg">
<div class="mf-profiler-span-bar" style="width:6%"></div>
</div>
<span class="mf-profiler-span-ms">10.3 ms</span>
</div>
</div>
<!-- oob_swap — depth 1 -->
<div class="mf-profiler-span-row">
<div class="mf-profiler-span-indent" style="width:14px"></div>
<div class="mf-profiler-span-body">
<span class="mf-profiler-span-name">oob_swap</span>
<div class="mf-profiler-span-bar-bg">
<div class="mf-profiler-span-bar" style="width:5.6%"></div>
</div>
<span class="mf-profiler-span-ms">9.7 ms</span>
</div>
</div>
</div><!-- /#view-tree -->
<!-- ============================================================ -->
<!-- Pie chart view (placeholder for ProfilerPieChart) -->
<!-- ============================================================ -->
<div class="mf-profiler-pie-view" id="view-pie">
<div class="mf-profiler-pie-view-header">Distribution</div>
<div class="mf-profiler-pie-placeholder">
<!-- Static SVG pie mockup -->
<svg width="160" height="160" viewBox="0 0 32 32">
<!-- process_row: 80% -->
<circle r="16" cx="16" cy="16" fill="transparent"
stroke="#e3b341" stroke-width="32"
stroke-dasharray="80 100"
transform="rotate(-90) translate(-32)"/>
<!-- callback overhead: 8% -->
<circle r="16" cx="16" cy="16" fill="transparent"
stroke="#58a6ff" stroke-width="32"
stroke-dasharray="8 100"
stroke-dashoffset="-80"
transform="rotate(-90) translate(-32)"/>
<!-- after_commands: 6% -->
<circle r="16" cx="16" cy="16" fill="transparent"
stroke="#3fb950" stroke-width="32"
stroke-dasharray="6 100"
stroke-dashoffset="-88"
transform="rotate(-90) translate(-32)"/>
<!-- oob_swap: 5.6% -->
<circle r="16" cx="16" cy="16" fill="transparent"
stroke="#8b949e" stroke-width="32"
stroke-dasharray="5.6 100"
stroke-dashoffset="-94"
transform="rotate(-90) translate(-32)"/>
<!-- before_commands: ~0.4% -->
<circle r="16" cx="16" cy="16" fill="transparent"
stroke="#6e7681" stroke-width="32"
stroke-dasharray="0.4 100"
stroke-dashoffset="-99.6"
transform="rotate(-90) translate(-32)"/>
</svg>
<div class="mf-profiler-pie-legend">
<div class="mf-profiler-pie-legend-item">
<div class="mf-profiler-pie-legend-color" style="background:#e3b341"></div>
<span>process_row</span>
<span class="mf-profiler-pie-legend-pct">80.0%</span>
</div>
<div class="mf-profiler-pie-legend-item">
<div class="mf-profiler-pie-legend-color" style="background:#58a6ff"></div>
<span>callback</span>
<span class="mf-profiler-pie-legend-pct">8.0%</span>
</div>
<div class="mf-profiler-pie-legend-item">
<div class="mf-profiler-pie-legend-color" style="background:#3fb950"></div>
<span>after_commands</span>
<span class="mf-profiler-pie-legend-pct">6.0%</span>
</div>
<div class="mf-profiler-pie-legend-item">
<div class="mf-profiler-pie-legend-color" style="background:#8b949e"></div>
<span>oob_swap</span>
<span class="mf-profiler-pie-legend-pct">5.6%</span>
</div>
<div class="mf-profiler-pie-legend-item">
<div class="mf-profiler-pie-legend-color" style="background:#6e7681"></div>
<span>before_commands</span>
<span class="mf-profiler-pie-legend-pct">0.4%</span>
</div>
</div>
</div>
</div><!-- /#view-pie -->
</div><!-- /.mf-profiler-detail-body -->
</div><!-- /.mf-profiler-detail -->
</div><!-- /.mf-profiler-body -->
<script>
function selectRow(el) {
document.querySelectorAll('.mf-profiler-row').forEach(r => r.classList.remove('selected'));
el.classList.add('selected');
}
function toggleEnabled(btn) {
const isEnabled = btn.classList.toggle('active');
btn.setAttribute('data-tip', isEnabled ? 'Disable profiler' : 'Enable profiler');
// Icon swap: filled circle = recording, ring = stopped
btn.querySelector('svg').innerHTML = isEnabled
? '<circle cx="10" cy="10" r="5"/>'
: '<circle cx="10" cy="10" r="5" fill="none" stroke="currentColor" stroke-width="2"/>';
}
function clearTraces() {
document.getElementById('trace-list').innerHTML =
'<div class="mf-profiler-empty">No traces recorded.</div>';
document.getElementById('trace-count').textContent = '0';
}
function switchView(view) {
const treeEl = document.getElementById('view-tree');
const pieEl = document.getElementById('view-pie');
const btnTree = document.getElementById('btn-tree');
const btnPie = document.getElementById('btn-pie');
if (view === 'tree') {
treeEl.style.display = '';
pieEl.classList.remove('visible');
btnTree.classList.add('view-active');
btnPie.classList.remove('view-active');
} else {
treeEl.style.display = 'none';
pieEl.classList.add('visible');
btnPie.classList.add('view-active');
btnTree.classList.remove('view-active');
}
}
</script>
</body>
</html>