Files
logstream/web/index.html
T
cedricandClaude Opus 5.5 194edee6de Settings: Save and Cancel buttons instead of saving each change
Changes made in the Settings window show at once but are only kept with
the Save button, placed under the tab labels; it takes the accent color
while changes are waiting. Cancel goes back to the saved settings, and
closing the window (or the page) with unsaved changes asks first.

Tags are now sent as a whole list (PUT /api/tags, nothing saved when one
is invalid, the error gives its position) and "Restore default tags"
shows GET /api/tags/defaults until Save.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 11:03:01 +02:00

458 lines
26 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LogStream</title>
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%236366f1'/%3E%3Cpath d='M8 10h16M8 16h11M8 22h14' stroke='white' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E">
<link rel="stylesheet" href="style.css">
<script>
// Apply the saved theme and language before first paint to avoid a flash.
try {
var t = localStorage.getItem('logstream.theme');
if (t === 'light' || t === 'dark') document.documentElement.dataset.theme = t;
var l = localStorage.getItem('logstream.lang');
if (l) document.documentElement.lang = l;
} catch (e) {}
</script>
</head>
<body>
<header class="topbar">
<div class="brand">
<svg viewBox="0 0 32 32" aria-hidden="true"><rect width="32" height="32" rx="8"/><path d="M8 10h16M8 16h11M8 22h14"/></svg>
<span>LogStream</span>
</div>
<div class="search">
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
<input id="q" type="search" autocomplete="off" spellcheck="false" data-i18n-aria="searchAria">
<button id="mode" class="chip" type="button" data-i18n-title="modeTitle">Simple</button>
</div>
<div class="actions">
<button id="live" class="btn live" type="button">
<span class="dot"></span><span class="lbl" data-i18n="live">Live</span>
</button>
<button id="settingsBtn" class="icon-btn" type="button" data-i18n-title="settings" data-i18n-aria="settings">
<!-- Gear icon (Lucide "settings", ISC license) -->
<svg viewBox="0 0 24 24"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
</button>
<button id="themeBtn" class="icon-btn" type="button" data-i18n-title="theme" data-i18n-aria="theme">
<svg class="sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
<svg class="moon" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
</button>
<span id="whoami" class="whoami" hidden><span class="who-name"></span><span class="who-badge"></span></span>
<a id="logoutBtn" class="icon-btn" href="/auth/logout" hidden data-i18n-title="logout" data-i18n-aria="logout">
<!-- Log out icon (Lucide "log-out", ISC license) -->
<svg viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><path d="m16 17 5-5-5-5"/><path d="M21 12H9"/></svg>
</a>
</div>
<div class="progress" aria-hidden="true"></div>
</header>
<section class="filters">
<div id="viewMode" class="seg view-mode" role="radiogroup" data-i18n-aria="viewAria">
<button type="button" role="radio" data-view="stream" data-i18n-title="viewStreamTitle">
<!-- Lucide "radio" (ISC license) -->
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4.9 19.1C1 15.2 1 8.8 4.9 4.9"/><path d="M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5"/><circle cx="12" cy="12" r="2"/><path d="M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5"/><path d="M19.1 4.9C23 8.8 23 15.1 19.1 19"/></svg>
<span data-i18n="viewStream">Stream</span>
</button>
<button type="button" role="radio" data-view="period" data-i18n-title="viewPeriodTitle">
<!-- Lucide "calendar-range" (ISC license) -->
<svg viewBox="0 0 24 24" aria-hidden="true"><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M16 2v4M3 10h18M8 2v4M17 14h-6M13 18H7M7 14h.01M17 18h.01"/></svg>
<span data-i18n="viewPeriod">Time range</span>
</button>
</div>
<div id="period" class="period" hidden>
<button id="perPrev" class="btn tool" type="button" data-i18n-title="perPrev" data-i18n-aria="perPrev">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
</button>
<input id="perFrom" type="datetime-local" data-i18n-title="perFrom" data-i18n-aria="perFrom">
<span class="muted" aria-hidden="true">→</span>
<input id="perTo" type="datetime-local" data-i18n-title="perTo" data-i18n-aria="perTo">
<button id="perNext" class="btn tool" type="button" data-i18n-title="perNext" data-i18n-aria="perNext">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m9 18 6-6-6-6"/></svg>
</button>
<button id="perOut" class="btn tool" type="button" data-i18n-title="perOut" data-i18n-aria="perOut">
<!-- Lucide "zoom-out" (ISC license) -->
<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3M8 11h6"/></svg>
</button>
</div>
<select id="range" data-i18n-aria="rangeAria">
<option value="5m" data-i18n="r5m">5 min</option>
<option value="15m" data-i18n="r15m">15 min</option>
<option value="1h" data-i18n="r1h" selected>1 hour</option>
<option value="6h" data-i18n="r6h">6 hours</option>
<option value="24h" data-i18n="r24h">24 hours</option>
<option value="7d" data-i18n="r7d">7 days</option>
<option value="30d" data-i18n="r30d">30 days</option>
<option value="all" data-i18n="rAll">All</option>
</select>
<select id="severity" data-i18n-aria="severityAria">
<option value="" data-i18n="sevAll">All severities</option>
<option value="2" data-i18n="sev2">Critical and above</option>
<option value="3" data-i18n="sev3">Errors and above</option>
<option value="4" data-i18n="sev4">Warnings and above</option>
<option value="5" data-i18n="sev5">Notice and above</option>
<option value="6" data-i18n="sev6">Info and above</option>
</select>
<select id="host" data-i18n-aria="hostAria"><option value="" data-i18n="allHosts">All hosts</option></select>
<select id="app" data-i18n-aria="appAria"><option value="" data-i18n="allApps">All apps</option></select>
<select id="source" data-i18n-aria="sourceAria">
<option value="" data-i18n="srcAll">All sources</option>
<option value="syslog">Syslog</option>
<option value="docker">Docker</option>
<option value="host" data-i18n="srcHost">Host system</option>
<option value="kernel" data-i18n="srcKernel">Kernel (dmesg)</option>
<option value="audit">Audit</option>
<option value="file" data-i18n="srcFile">Files</option>
<option value="logstream">LogStream</option>
</select>
<span class="spacer"></span>
<span id="count" class="muted"></span>
<div class="export">
<button id="exportBtn" class="btn tool" type="button" aria-haspopup="menu" aria-expanded="false">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v12M7 10l5 5 5-5M4 17v3h16v-3"/></svg>
<span class="lbl" data-i18n="export">Export</span>
</button>
<div id="exportMenu" class="menu" role="menu" hidden>
<button type="button" role="menuitem" data-export="csv">
<strong>CSV</strong><small data-i18n="exportCsvHint">Comma separated, UTF-8</small>
</button>
<button type="button" role="menuitem" data-export="excel">
<strong data-i18n="exportExcel">CSV for Excel</strong><small data-i18n="exportExcelHint">Semicolon separated, for Excel in French</small>
</button>
<p id="exportNote" class="menu-note"></p>
</div>
</div>
</section>
<section id="histo" class="histo" data-i18n-aria="histoAria" hidden></section>
<div id="authWarn" class="auth-warn" role="status" hidden>
<span data-i18n="authOff"></span>
<button id="authWarnClose" class="icon-btn" type="button" data-i18n-aria="close">
<svg viewBox="0 0 24 24"><path d="M6 6l12 12M18 6 6 18"/></svg>
</button>
</div>
<div id="error" class="error-banner" hidden></div>
<button id="newPill" class="pill" type="button" hidden></button>
<div id="table" class="table" hidden>
<div id="listHead" class="list-head" role="presentation">
<span data-col="rcv"><span class="lbl" data-i18n="colRcv">Received</span><i class="grip" data-i18n-title="colGrip"></i></span>
<span data-col="mt"><span class="lbl" data-i18n="colMt">Message time</span><i class="grip" data-i18n-title="colGrip"></i></span>
<span data-col="sev"><span class="lbl" data-i18n="colSev">Severity</span><i class="grip" data-i18n-title="colGrip"></i></span>
<span data-col="host"><span class="lbl" data-i18n="colHost">Host</span><i class="grip" data-i18n-title="colGrip"></i></span>
<span data-col="app"><span class="lbl" data-i18n="colApp">App</span><i class="grip" data-i18n-title="colGrip"></i></span>
<span data-col="codes"><span class="lbl" data-i18n="colCodes" data-i18n-title="codesTitle">Pattern</span></span>
<span data-col="msg"><span class="lbl" data-i18n="colMsg">Message</span></span>
</div>
<main id="list" class="list"></main>
</div>
<div id="empty" class="empty" hidden>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h10M4 18h7"/></svg>
<p><strong data-i18n="emptyTitle">No matching logs.</strong></p>
<p class="muted" data-i18n="emptyHint">Widen the time range or send syslog messages to port 514 (UDP or TCP).</p>
</div>
<footer class="status">
<span id="conn" class="conn"></span>
<span id="stats" class="muted"></span>
<button id="toTop" class="icon-btn to-top" type="button" data-i18n-title="toTop" data-i18n-aria="toTop" hidden>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg>
</button>
</footer>
<dialog id="settingsDlg" class="settings" aria-labelledby="settingsTitle">
<div class="dlg">
<header>
<h2 id="settingsTitle" data-i18n="settings">Settings</h2>
<button id="closeSettings" class="icon-btn" type="button" data-i18n-aria="close">
<svg viewBox="0 0 24 24"><path d="M6 6l12 12M18 6 6 18"/></svg>
</button>
</header>
<div class="set-body">
<div class="set-side">
<nav id="setNav" class="set-nav" role="tablist" aria-orientation="vertical">
<button type="button" role="tab" data-tab="locale">
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>
<span data-i18n="tabLocale">Localization</span>
</button>
<button type="button" role="tab" data-tab="filters">
<svg viewBox="0 0 24 24"><path d="M3 12V4a1 1 0 0 1 1-1h8l9 9-9 9z"/><circle cx="8" cy="8" r="1.5"/></svg>
<span data-i18n="tabFilters">Patterns</span>
</button>
<button type="button" role="tab" data-tab="sources">
<svg viewBox="0 0 24 24"><path d="M21 8 12 3 3 8v8l9 5 9-5z"/><path d="m3 8 9 5 9-5M12 13v8"/></svg>
<span data-i18n="tabSources">Log sources</span>
</button>
<button type="button" role="tab" data-tab="interface">
<svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="12" rx="2"/><path d="M8 20h8M12 16v4"/></svg>
<span data-i18n="tabInterface">Appearance</span>
</button>
<button type="button" role="tab" data-tab="data">
<svg viewBox="0 0 24 24"><ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v14c0 1.7 3.6 3 8 3s8-1.3 8-3V5M4 12c0 1.7 3.6 3 8 3s8-1.3 8-3"/></svg>
<span data-i18n="tabData">Database</span>
</button>
</nav>
<div id="setActions" class="set-actions">
<button id="setSave" class="btn save" type="button" disabled>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 10 17l9-10"/></svg>
<span data-i18n="saveSettings">Save</span>
</button>
<button id="setCancel" class="btn ghost" type="button" disabled data-i18n="cancelSettings">Cancel</button>
<p id="setState" class="set-state" role="status"></p>
</div>
</div>
<div class="set-panels">
<!-- Localization -->
<div class="set-panel" role="tabpanel" data-panel="locale">
<section class="set-section">
<h3 data-i18n="language">Language</h3>
<div id="langSwitch" class="seg" role="radiogroup" data-i18n-aria="language">
<button type="button" role="radio" data-lang="en">English</button>
<button type="button" role="radio" data-lang="fr">Français</button>
</div>
</section>
<section class="set-section">
<h3 data-i18n="dateTime">Date &amp; time</h3>
<div class="field-grid">
<label for="tzSelect" data-i18n="tzLabel">Time zone</label>
<select id="tzSelect" class="field"></select>
<label for="fmtSelect" data-i18n="fmtLabel">Reception time format</label>
<select id="fmtSelect" class="field"></select>
</div>
<p id="timePreview" class="time-preview"></p>
<p class="muted small" data-i18n="timeHelp"></p>
</section>
</div>
<!-- Pattern (color tags; the "filters" tab id is kept for saved settings) -->
<div class="set-panel wide" role="tabpanel" data-panel="filters" hidden>
<section class="set-section tags-section">
<div class="sec-head">
<h3 data-i18n="tagsTitle">Color tags</h3>
<span id="tagCount" class="muted small"></span>
</div>
<p class="muted small" data-i18n="tagsHelp"></p>
<div class="tag-toolbar">
<button id="addTag" class="btn primary" type="button" data-i18n="addTag">+ Add tag</button>
<select id="presetTags" class="field" aria-label="Presets" data-i18n-aria="presetAria">
<option value="" data-i18n="presetPick">+ Preset…</option>
</select>
<input id="tagFilter" type="search" class="dk-filter" autocomplete="off" spellcheck="false" data-i18n-ph="tagFilter" data-i18n-aria="tagFilter">
<button id="resetTags" class="btn ghost" type="button" data-i18n="resetTags">Restore default tags</button>
</div>
<div id="tagList" class="tag-list"></div>
</section>
</div>
<!-- Sources -->
<div class="set-panel" role="tabpanel" data-panel="sources" hidden>
<section class="set-section">
<h3>Syslog</h3>
<p id="syslogStatus" class="docker-status"></p>
<label class="switch-row">
<input id="syslogEnabled" type="checkbox" class="switch">
<span data-i18n="syslogEnabled">Receive syslog messages</span>
</label>
<div class="sl-grid">
<span class="lbl" data-i18n="syslogProtocols">Protocols</span>
<div id="syslogProtos" class="chip-field sl-protos">
<button type="button" class="chip" data-proto="udp" style="--h:205">UDP</button>
<button type="button" class="chip" data-proto="tcp" style="--h:265">TCP</button>
</div>
<span class="lbl" data-i18n="syslogPort">Port</span>
<div class="sl-port"><code id="syslogPortVal">514</code><span id="syslogPortHelp" class="muted small"></span></div>
</div>
</section>
<section class="set-section">
<h3 data-i18n="hostTitle">Host system logs</h3>
<p id="hostStatus" class="docker-status"></p>
<label class="switch-row">
<input id="hostEnabled" type="checkbox" class="switch">
<span data-i18n="hostEnabled">Collect the system logs of this machine</span>
</label>
<p class="muted small" data-i18n="hostHelp"></p>
</section>
<section class="set-section">
<h3 data-i18n="dmesgTitle">Kernel messages (dmesg)</h3>
<p id="dmesgStatus" class="docker-status"></p>
<label class="switch-row">
<input id="dmesgEnabled" type="checkbox" class="switch">
<span data-i18n="dmesgEnabled">Collect the kernel messages of this machine</span>
</label>
<p class="muted small" data-i18n="dmesgHelp"></p>
</section>
<section class="set-section">
<h3 data-i18n="auditTitle">Linux audit (auditd)</h3>
<p id="auditStatus" class="docker-status"></p>
<label class="switch-row">
<input id="auditEnabled" type="checkbox" class="switch">
<span data-i18n="auditEnabled">Collect the audit events of this machine</span>
</label>
<div class="sl-grid">
<label class="lbl" for="auditPath" data-i18n="auditPath">File</label>
<input id="auditPath" type="text" class="dk-filter" autocomplete="off" spellcheck="false">
</div>
<p class="muted small src-help" data-i18n="auditHelp"></p>
</section>
<section class="set-section">
<h3 data-i18n="selfTitle">LogStream internal logs</h3>
<p id="selfStatus" class="docker-status"></p>
<label class="switch-row">
<input id="selfEnabled" type="checkbox" class="switch">
<span data-i18n="selfEnabled">Record the events of LogStream itself</span>
</label>
<div class="sl-grid">
<label class="lbl" for="selfLevel" data-i18n="selfLevel">Minimum level</label>
<select id="selfLevel" class="field self-level">
<option value="err" data-i18n="sev3">Errors and above</option>
<option value="warning" data-i18n="sev4">Warnings and above</option>
<option value="notice" data-i18n="sev5">Notice and above</option>
<option value="info" data-i18n="sev6">Info and above</option>
</select>
</div>
<p class="muted small src-help" data-i18n="selfHelp"></p>
</section>
<section class="set-section span">
<h3 data-i18n="filesTitle">Log files</h3>
<div id="fileList" class="file-list"></div>
<div class="file-toolbar">
<button id="addFile" class="btn primary" type="button" data-i18n="addFile">+ Add a file</button>
</div>
<p class="muted small src-help" data-i18n="filesHelp"></p>
</section>
<section class="set-section span">
<h3 data-i18n="dockerTitle">Docker containers</h3>
<p id="dockerStatus" class="docker-status"></p>
<div id="dockerBody" hidden>
<div class="dk-opts">
<label class="switch-row">
<input id="dockerDefault" type="checkbox" class="switch">
<span data-i18n="dockerDefault">Follow new containers automatically</span>
</label>
<label class="switch-row">
<input id="dockerStopped" type="checkbox" class="switch">
<span id="dockerStoppedLbl">Show stopped containers</span>
</label>
</div>
<div class="dk-toolbar">
<input id="dockerFilter" type="search" class="dk-filter" autocomplete="off" spellcheck="false" data-i18n-ph="dockerFilter" data-i18n-aria="dockerFilter">
<button id="dockerAll" class="link-btn" type="button" data-i18n="dockerAll">Enable all</button>
<button id="dockerNone" class="link-btn" type="button" data-i18n="dockerNone">Disable all</button>
</div>
<div id="dockerList" class="chip-field"></div>
<p class="dk-legend" data-i18n="dockerLegend"></p>
</div>
<p class="muted small" data-i18n="dockerHelp"></p>
</section>
</div>
<!-- Interface -->
<div class="set-panel" role="tabpanel" data-panel="interface" hidden>
<section class="set-section">
<h3 data-i18n="themeTitle">Theme</h3>
<div id="themeSwitch" class="seg" role="radiogroup" data-i18n-aria="themeTitle">
<button type="button" role="radio" data-theme-opt="system" data-i18n="themeSystem">System</button>
<button type="button" role="radio" data-theme-opt="light" data-i18n="themeLight">Light</button>
<button type="button" role="radio" data-theme-opt="dark" data-i18n="themeDark">Dark</button>
</div>
<p class="muted small hint" data-i18n="themeHelp"></p>
</section>
<section class="set-section span">
<h3 data-i18n="logDisplay">Log display</h3>
<div class="field-grid">
<span class="lbl" data-i18n="fontSize">Font size</span>
<div id="sizeSwitch" class="seg" role="radiogroup" data-i18n-aria="fontSize">
<button type="button" role="radio" data-size="tiny" data-i18n="sizeTiny">Tiny</button>
<button type="button" role="radio" data-size="small" data-i18n="sizeSmall">Small</button>
<button type="button" role="radio" data-size="medium" data-i18n="sizeMedium">Medium</button>
<button type="button" role="radio" data-size="large" data-i18n="sizeLarge">Large</button>
</div>
<span class="lbl" data-i18n="density">Density</span>
<div id="densitySwitch" class="seg" role="radiogroup" data-i18n-aria="density">
<button type="button" role="radio" data-density="normal" data-i18n="densityNormal">Normal</button>
<button type="button" role="radio" data-density="compact" data-i18n="densityCompact">Compact</button>
</div>
<label for="fontSelect" data-i18n="fontLabel">Font</label>
<select id="fontSelect" class="field"></select>
</div>
<div id="fontPreview" class="list preview-list" aria-hidden="true"></div>
<p class="muted small" data-i18n="fontHelp"></p>
<div class="cols-row">
<button id="resetCols" class="btn ghost" type="button" data-i18n="resetCols">Reset column widths</button>
<span class="muted small" data-i18n="colsHelp"></span>
</div>
</section>
<section class="set-section">
<h3 data-i18n="histoTitle">Timeline</h3>
<div class="field-grid">
<span class="lbl" data-i18n="hScale">Scale</span>
<div class="seg" role="radiogroup" data-histo="scale" data-i18n-aria="hScale">
<button type="button" role="radio" data-v="linear" data-i18n="hLinear">Linear</button>
<button type="button" role="radio" data-v="sqrt">√</button>
<button type="button" role="radio" data-v="log">Log</button>
</div>
<span class="lbl" data-i18n="hHeight">Height</span>
<div class="seg" role="radiogroup" data-histo="height" data-i18n-aria="hHeight">
<button type="button" role="radio" data-v="s">S</button>
<button type="button" role="radio" data-v="m">M</button>
<button type="button" role="radio" data-v="l">L</button>
</div>
<span class="lbl" data-i18n="hColor">Color</span>
<div class="seg" role="radiogroup" data-histo="color" data-i18n-aria="hColor">
<button type="button" role="radio" data-v="severity" data-i18n="hColSeverity">Severity</button>
<button type="button" role="radio" data-v="intensity" data-i18n="hColIntensity">Intensity</button>
<button type="button" role="radio" data-v="none" data-i18n="hColNone">None</button>
</div>
<span class="lbl" data-i18n="hRender">Display</span>
<div class="seg" role="radiogroup" data-histo="render" data-i18n-aria="hRender">
<button type="button" role="radio" data-v="bars" data-i18n="hBars">Bars</button>
<button type="button" role="radio" data-v="area" data-i18n="hArea">Area</button>
</div>
<label for="histoStep" data-i18n="hStep">Division</label>
<select id="histoStep" class="field" data-histo="step"></select>
<label for="histoRefresh" data-i18n="hRefresh">Refresh</label>
<select id="histoRefresh" class="field" data-histo="refresh"></select>
</div>
<p class="muted small" data-i18n="histoHelp"></p>
</section>
</div>
<!-- Data -->
<div class="set-panel wide" role="tabpanel" data-panel="data" hidden>
<section class="set-section danger">
<h3 data-i18n="dangerZone">Danger zone</h3>
<div class="db-head">
<span class="lbl" data-i18n="dbTitle">Database</span>
<button id="dbRefresh" class="link-btn" type="button" data-i18n="dbRefresh">Refresh</button>
</div>
<dl id="dbStats" class="db-stats"></dl>
<p class="muted small" data-i18n="purgeHelp"></p>
<div class="purge-row">
<button id="purgeBtn" class="btn danger" type="button" data-i18n="purgeBtn">Delete all logs…</button>
<span id="purgeStatus" class="purge-status muted" hidden></span>
</div>
</section>
</div>
</div>
</div>
</div>
</dialog>
<div id="toast" class="toast" hidden></div>
<script src="app.js"></script>
</body>
</html>