treesearchselectionautosize
Code Explorer
Browse vlist's own source: a virtualized file
tree on the left, a searchable list on the right. Pick
a file to list its exported symbols (functions,
constants, types…) and filter them; or switch to
History to scrub the commits that touched that file
in navigate mode. clears.
Code Explorer
vlist
0%
0.00 /
0.00
px/ms
0 /
0
rendered
0 / 0
matches
Source
// Code Explorer — browse vlist's own source with two coordinated virtual lists.
// • Left: a file tree (tree plugin) of the vlist source, with Zed-style icons.
// • Center: a vlist + search showing either the selected file's exported
// symbols (filter) or its commit history (navigate). With no file
// selected, History shows the full git log.
// • Right: a controls panel demonstrating the search plugin's configuration.
import { createVList, tree, search, selection, autosize, scrollbar } from "vlist";
import { VLIST_TREE } from "../../src/data/vlist-tree.js";
import { VLIST_HISTORY } from "../../src/data/vlist-history.js";
import { getIcon, getChevron } from "../tree/icons.js";
import { createStats } from "../stats.js";
import { createInfoUpdater } from "../info.js";
// =============================================================================
// Derived data
// =============================================================================
const SYMBOL_KINDS = new Set(["function", "const", "class", "interface", "type", "enum", "re-export"]);
const esc = (s) =>
String(s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
const basename = (p) => p.split("/").pop();
function timeAgo(dateStr) {
const sec = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1000);
if (sec < 60) return "just now";
const min = Math.floor(sec / 60);
if (min < 60) return `${min}m ago`;
const hr = Math.floor(min / 60);
if (hr < 24) return `${hr}h ago`;
const day = Math.floor(hr / 24);
if (day < 30) return `${day}d ago`;
const mo = Math.floor(day / 30);
if (mo < 12) return `${mo}mo ago`;
return `${Math.floor(mo / 12)}y ago`;
}
const KW = new Set([
"export", "function", "const", "let", "var", "interface", "type", "class",
"enum", "extends", "implements", "readonly", "async", "abstract", "static",
"declare", "import", "from", "new", "return", "default", "of", "in",
]);
const TYPES = new Set([
"string", "number", "boolean", "void", "null", "undefined", "any",
"unknown", "never", "true", "false", "Promise", "Array", "Record",
"Set", "Map", "Partial", "Required", "Omit", "Pick",
]);
const TOKEN_RE = /("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`)|(\b[A-Za-z_$][\w$]*\b)|(\d+(?:\.\d+)?)|([<>()[\]{};:,=|&?.]|=>|\.\.\.)/g;
function syntaxHighlight(raw) {
let out = "";
let last = 0;
let m;
TOKEN_RE.lastIndex = 0;
while ((m = TOKEN_RE.exec(raw)) !== null) {
if (m.index > last) out += esc(raw.slice(last, m.index));
const [tok, str, ident, num, punct] = m;
const e = esc(tok);
if (str) out += `<span class="syn-str">${e}</span>`;
else if (num) out += `<span class="syn-num">${e}</span>`;
else if (punct) out += `<span class="syn-punct">${e}</span>`;
else if (ident && KW.has(ident)) out += `<span class="syn-kw">${e}</span>`;
else if (ident && TYPES.has(ident)) out += `<span class="syn-type">${e}</span>`;
else out += e;
last = m.index + tok.length;
}
if (last < raw.length) out += esc(raw.slice(last));
return out;
}
const TREE_NODES = VLIST_TREE.filter((n) => n.kind === "dir" || n.kind === "file");
// All symbols indexed by their parent file.
const ALL_SYMBOLS = VLIST_TREE.filter((n) => SYMBOL_KINDS.has(n.kind))
.sort((a, b) => (b.exported ? 1 : 0) - (a.exported ? 1 : 0));
const symbolsByFile = new Map();
for (const n of ALL_SYMBOLS) {
const arr = symbolsByFile.get(n.parentId) ?? [];
arr.push(n);
symbolsByFile.set(n.parentId, arr);
}
for (const [, arr] of symbolsByFile) {
arr.sort((a, b) => (b.exported ? 1 : 0) - (a.exported ? 1 : 0));
}
// All file paths (for dir → files lookup).
const ALL_FILES = VLIST_TREE.filter((n) => n.kind === "file").map((n) => n.id);
// Collect symbols for a selection: null → all, file → its symbols, dir → all files under it.
function symbolsFor(nodeId) {
if (!nodeId) return ALL_SYMBOLS;
if (symbolsByFile.has(nodeId)) return symbolsByFile.get(nodeId);
// Directory — collect from all files whose path starts with this dir.
const prefix = nodeId + "/";
const syms = [];
for (const file of ALL_FILES) {
if (file === nodeId || file.startsWith(prefix)) {
const s = symbolsByFile.get(file);
if (s) syms.push(...s);
}
}
return syms;
}
// Collect commits for a selection: null → all, file → its commits, dir → commits touching any file under it.
function commitsFor(nodeId) {
if (!nodeId) return VLIST_HISTORY;
const prefix = nodeId + "/";
return VLIST_HISTORY.filter((c) =>
c.files.some((f) => f === nodeId || f.startsWith(prefix)),
);
}
// =============================================================================
// Configurable state
// =============================================================================
let treeList = null;
let rightList = null;
let selectedFile = null;
let rightView = "symbols"; // "symbols" | "history"
// Search config (driven by control panel)
let searchEnabled = true;
let searchMode = "filter";
let searchVariant = "default";
let highlightEnabled = true;
let highlightScoped = false;
let caseSensitive = false;
// =============================================================================
// Stats
// =============================================================================
export const stats = createStats({
getScrollPosition: () => rightList?.getScrollPosition() ?? 0,
getTotal: () => rightList?.total ?? 0,
getItemSize: () => (rightView === "history" ? 56 : 52),
getContainerSize: () => document.querySelector("#symbol-container")?.clientHeight ?? 0,
});
const updateInfo = createInfoUpdater(stats);
// =============================================================================
// Left pane — file tree
// =============================================================================
const renderTreeNode = (item, _index, state) => {
const t = state.tree ?? {};
const isFolder = item.kind === "dir";
const count = isFolder ? 0 : symbolsFor(item.id).length;
const meta = count ? `<span class="tree-node__meta">${count}</span>` : "";
return `
<div class="tree-node tree-node--${item.kind}">
<span class="tree-node__icon">${getIcon(item.name, isFolder, t.expanded)}</span>
<span class="tree-node__label">${esc(item.name)}</span>
${meta}
</div>
`;
};
function buildTree() {
treeList = createVList(
{
container: "#tree-container",
ariaLabel: "vlist source files",
item: { height: 30, template: renderTreeNode },
items: TREE_NODES,
},
[
tree({
parentId: "parentId",
label: "name",
indent: 18,
expanded: ["src"],
expandOnClick: true,
connectorLines: true,
}),
selection({ mode: "single", followFocus: true, focusOnClick: true }),
scrollbar({ autoHide: false, padding: 0 }),
],
);
const onTreeSelect = (id) => {
if (id !== selectedFile) {
selectedFile = id;
buildRight();
}
};
treeList.on("selection:change", ({ items }) => {
if (items[0]) onTreeSelect(items[0].id);
});
treeList.on("tree:expand", ({ id }) => { treeList.select(id); onTreeSelect(id); });
treeList.on("tree:collapse", ({ id }) => { treeList.select(id); onTreeSelect(id); });
}
// =============================================================================
// Right pane — symbols (filter) | history (navigate)
// =============================================================================
const renderSymbol = (item) => `
<div class="sym sym--${item.kind}${item.exported ? "" : " sym--internal"}">
<div class="sym__row">
<span class="sym__kind">${item.kind}</span>
<span class="sym__name">${esc(item.name)}</span>
</div>
${item.signature ? `<pre class="sym__sig">${syntaxHighlight(item.signature)}</pre>` : ""}
</div>
`;
const renderCommit = (item) => `
<div class="commit commit--${item.type}">
<div class="commit__left">
<span class="commit__type">${item.type}</span>
${item.tag ? `<span class="commit__tag">${esc(item.tag)}</span>` : ""}
</div>
<div class="commit__body">
<div class="commit__subject">${esc(item.subject)}</div>
<div class="commit__meta">
<code>${item.shortHash}</code> · ${esc(item.author)} · <span class="commit__ago">${timeAgo(item.date)}</span>
</div>
</div>
</div>
`;
function showEmpty(message) {
const c = document.getElementById("symbol-container");
if (rightList) {
rightList.destroy();
rightList = null;
}
c.innerHTML = `<div class="explorer-empty">${esc(message)}</div>`;
updateInfo();
updateMatchInfo(0, 0);
}
function resolveHighlight() {
if (!highlightEnabled) return false;
if (!highlightScoped) return true;
return rightView === "symbols"
? { within: ".sym__name" }
: { within: ".commit__subject" };
}
function buildRight() {
updateContext();
const prevQuery = rightList?.getQuery?.() ?? "";
if (rightList) {
rightList.destroy();
rightList = null;
}
const c = document.getElementById("symbol-container");
c.innerHTML = "";
const isSymbols = rightView === "symbols";
const items = isSymbols ? symbolsFor(selectedFile) : commitsFor(selectedFile);
if (isSymbols && !items.length) {
return showEmpty(selectedFile ? `No exported symbols in ${basename(selectedFile)}` : "No symbols found");
}
if (!isSymbols && !items.length) {
return showEmpty(selectedFile ? `No commits touched ${basename(selectedFile)}` : "No commits");
}
const label = selectedFile ? basename(selectedFile) : "vlist";
const mode = searchMode;
const placeholderText = isSymbols
? `${searchMode === "filter" ? "Filter" : "Find"} symbols in ${label}…`
: `Search ${selectedFile ? "history of " + label : "all commits"}…`;
const plugins = [];
if (searchEnabled) {
plugins.push(
search({
mode,
text: { placeholder: placeholderText },
field: isSymbols
? "name"
: (c) => `${c.subject} ${c.author} ${c.shortHash}`,
highlight: resolveHighlight(),
caseSensitive,
variant: searchVariant,
}),
);
}
plugins.push(autosize());
plugins.push(scrollbar({ autoHide: false, padding: 0 }));
plugins.push(selection({ mode: "single" }));
rightList = createVList(
{
container: "#symbol-container",
ariaLabel: isSymbols ? `Symbols in ${label}` : `${selectedFile ? "History of " + label : "All commits"}`,
item: { height: isSymbols ? 52 : 56, template: isSymbols ? renderSymbol : renderCommit },
items,
},
plugins,
);
wireRight(items.length);
reflectControls();
if (prevQuery && searchEnabled) {
rightList.setQuery?.(prevQuery);
}
}
function wireRight(total) {
rightList.on("scroll", updateInfo);
rightList.on("range:change", updateInfo);
rightList.on("velocity:change", ({ velocity }) => {
stats.onVelocity(velocity);
updateInfo();
});
if (searchEnabled) {
rightList.on("search:change", ({ matches, total: t }) => {
updateMatchInfo(matches, t);
updateInfo();
});
rightList.on("search:match", ({ matchIndex, matches }) => {
updateMatchInfo(matches, rightList?.total ?? 0, matchIndex);
});
}
updateInfo();
updateMatchInfo(0, total);
}
// =============================================================================
// Toolbar
// =============================================================================
function updateContext() {
const el = document.getElementById("cx-context");
if (!el) return;
el.innerHTML = selectedFile
? `<code>${esc(selectedFile)}</code>`
: `<span class="explorer-context-all">All files</span>`;
}
function setActiveView(view) {
document
.getElementById("cx-view-toggle")
?.querySelectorAll("button")
.forEach((b) => b.classList.toggle("ui-segmented__btn--active", b.dataset.value === view));
}
function updateMatchInfo(matches, total, currentIndex) {
const el = document.getElementById("info-matches");
if (!el) return;
if (!searchEnabled) {
el.textContent = "off";
return;
}
if (rightView === "history" && matches > 0 && currentIndex != null) {
el.textContent = `${currentIndex + 1} / ${matches}`;
} else {
el.textContent = `${matches} / ${total}`;
}
}
function reflectControls() {
const off = !searchEnabled;
const isHistory = rightView === "history";
for (const id of ["cx-mode-section", "cx-style-section", "cx-highlight-section", "cx-options-section"]) {
const el = document.getElementById(id);
if (el) el.classList.toggle("ui-section--disabled", off);
}
const activeMode = searchMode;
document.getElementById("cx-mode-toggle")
?.querySelectorAll("button")
.forEach((b) => b.classList.toggle("ui-segmented__btn--active", b.dataset.value === activeMode));
}
// =============================================================================
// Boot + controls (all ids prefixed cx- to avoid shell collision)
// =============================================================================
buildTree();
buildRight();
// View toggle: Symbols | History — clear the query when switching views
// since the data is entirely different.
document.getElementById("cx-view-toggle")?.addEventListener("click", (e) => {
const btn = e.target.closest("[data-value]");
if (!btn || btn.dataset.value === rightView) return;
if (rightList?.getQuery?.()) rightList.setQuery?.("");
rightView = btn.dataset.value;
setActiveView(rightView);
buildRight();
});
// Tree expand / collapse
let treeExpanded = false;
const treeToggle = document.getElementById("cx-tree-toggle");
treeToggle?.addEventListener("click", () => {
treeExpanded = !treeExpanded;
if (treeExpanded) treeList?.expandAll?.();
else treeList?.collapseAll?.();
treeToggle.textContent = treeExpanded ? "Collapse all" : "Expand all";
});
// Search enable
document.getElementById("cx-search-toggle")?.addEventListener("change", (e) => {
searchEnabled = e.target.checked;
buildRight();
});
// Mode: filter / navigate
wireSegment("cx-mode-toggle", (v) => { searchMode = v; buildRight(); });
// Bar style: default / md3
wireSegment("cx-style-toggle", (v) => { searchVariant = v; buildRight(); });
// Highlight enable
document.getElementById("cx-highlight-toggle")?.addEventListener("change", (e) => {
highlightEnabled = e.target.checked;
buildRight();
});
// Highlight scope: name only / whole row
wireSegment("cx-scope-toggle", (v) => { highlightScoped = v === "scoped"; buildRight(); });
// Case sensitive
document.getElementById("cx-case-toggle")?.addEventListener("change", (e) => {
caseSensitive = e.target.checked;
buildRight();
});
function wireSegment(id, setter) {
const el = document.getElementById(id);
if (!el) return;
el.addEventListener("click", (e) => {
const btn = e.target.closest("[data-value]");
if (!btn) return;
el.querySelectorAll("button").forEach((b) =>
b.classList.toggle("ui-segmented__btn--active", b === btn),
);
setter(btn.dataset.value);
});
}
/* Code Explorer — file tree (left) + searchable symbols/history (right). */
.split-main.split-main--full {
height: 580px;
}
.ui-section--disabled {
opacity: 0.4;
pointer-events: none;
}
/* ── Explorer layout inside split-main ─────────────────────────────────── */
.explorer {
display: flex;
gap: 0;
height: 100%;
}
.explorer-tree {
flex: 0 0 260px;
display: flex;
flex-direction: column;
border: 1px solid var(--vlist-border, #e5e7eb);
border-right: none;
border-radius: 10px 0 0 10px;
overflow: hidden;
}
.explorer-tree .tree-node--file .tree-node__label {
font-size: 15px;
}
.explorer-tree .vlist-content {
font-family: var(--font-mono);
}
.explorer-list {
flex: 1 1 auto;
min-width: 0;
display: flex;
flex-direction: column;
border: 1px solid var(--vlist-border, #e5e7eb);
border-radius: 0 10px 10px 0;
overflow: hidden;
}
.explorer-list .vlist-content {
font-family: var(--font-mono);
}
.explorer-list .vlist-content .vlist-item {
font-size: 0.8em;
}
#tree-container,
#symbol-container {
flex: 1 1 auto;
min-height: 0;
}
.explorer-tree .vlist,
.explorer-list .vlist {
border: none;
border-radius: 0;
}
/* Custom scrollbar plugin — always visible, flat. */
.explorer-tree .vlist {
--vlist-custom-scrollbar-width: 4px;
--vlist-custom-scrollbar-radius: 0;
}
.explorer-list .vlist {
--vlist-custom-scrollbar-width: 16px;
--vlist-custom-scrollbar-radius: 0;
}
.explorer-header {
flex: none;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 5px 8px 5px 12px;
border-bottom: 1px solid var(--vlist-border, #e5e7eb);
}
.explorer-title {
font-size: 13px;
font-weight: 600;
color: var(--vlist-text, #111827);
}
.explorer-btn {
flex: 0 0 auto;
padding: 3px 8px;
border: none;
border-radius: 6px;
background: transparent;
color: var(--vlist-text-muted, #94a3b8);
font: inherit;
font-size: 11px;
cursor: pointer;
}
.explorer-btn:hover {
background: var(--vlist-bg-hover, rgba(127, 127, 127, 0.12));
color: var(--vlist-text, #111827);
}
.explorer-context {
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 12px;
}
.explorer-context code {
font-size: 12px;
}
.explorer-context-all {
color: var(--vlist-text-muted, #94a3b8);
}
.explorer-empty {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: 24px;
text-align: center;
color: var(--vlist-text-muted, #94a3b8);
font-size: 13px;
}
/* ── File tree row (shared with the Tree View example) ─────────────────── */
.tree-node {
display: flex;
align-items: center;
gap: 4px;
height: 100%;
width: 100%;
padding-right: 14px;
cursor: default;
user-select: none;
}
.tree-node__chevron {
display: flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
margin-left: 2px;
flex-shrink: 0;
border-radius: 3px;
}
.tree-node__chevron:hover {
color: var(--vlist-text, #111827);
background: var(--vlist-bg-hover, rgba(0, 0, 0, 0.06));
}
.tree-node__chevron--leaf {
visibility: hidden;
}
.tree-node__icon {
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
flex-shrink: 0;
overflow: visible;
}
.tree-node__icon svg {
overflow: visible;
}
.tree-node__label {
flex: 1;
min-width: 0;
font-size: 15px;
font-weight: 500;
color: var(--vlist-text, #111827);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tree-node--dir .tree-node__label {
font-weight: 500;
}
.tree-node--file .tree-node__label {
font-weight: 500;
}
.tree-node__meta {
font-size: 11px;
color: var(--vlist-text-muted, #94a3b8);
font-variant-numeric: tabular-nums;
flex-shrink: 0;
}
/* ── Symbol row ────────────────────────────────────────────────────────── */
.sym {
display: flex;
flex-direction: column;
justify-content: center;
gap: 4px;
padding: 8px 12px;
box-sizing: border-box;
}
.sym__row {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.sym__kind {
flex: 0 0 auto;
padding: 1px 6px;
border-radius: 5px;
font-size: 9px;
text-transform: uppercase;
letter-spacing: 0.05em;
background: var(--vlist-bg-hover, rgba(127, 127, 127, 0.14));
color: var(--vlist-text-muted, #94a3b8);
}
.sym__name {
min-width: 0;
font-size: 10px;
font-weight: 600;
color: var(--vlist-text, #e5e7eb);
}
.sym__sig {
margin: 0;
padding: 0;
font-family: inherit;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
color: var(--vlist-text-muted, #94a3b8);
}
/* ── Syntax highlighting (One Dark / One Light) ───────────────────────── */
.syn-kw {
color: #c678dd;
}
.syn-type {
color: #e6c07b;
}
.syn-str {
color: #98c379;
}
.syn-num {
color: #d19a66;
}
.syn-punct {
color: #abb2bf;
}
[data-theme-mode="light"] .syn-kw {
color: #a626a4;
}
[data-theme-mode="light"] .syn-type {
color: #c18401;
}
[data-theme-mode="light"] .syn-str {
color: #50a14f;
}
[data-theme-mode="light"] .syn-num {
color: #986801;
}
[data-theme-mode="light"] .syn-punct {
color: #383a42;
}
.sym--internal {
opacity: 0.6;
}
.sym--function .sym__kind {
color: #38bdf8;
}
.sym--const .sym__kind {
color: #a78bfa;
}
.sym--class .sym__kind {
color: #fb923c;
}
.sym--interface .sym__kind {
color: #34d399;
}
.sym--type .sym__kind {
color: #f472b6;
}
.sym--enum .sym__kind {
color: #facc15;
}
.sym--re-export .sym__kind {
color: #94a3b8;
}
/* ── Commit row ────────────────────────────────────────────────────────── */
.commit {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 8px 12px;
box-sizing: border-box;
}
.commit__left {
flex: 0 0 auto;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
min-width: 66px;
}
.commit__type {
width: 100%;
padding: 2px 7px;
border-radius: 6px;
text-align: center;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.04em;
background: var(--vlist-bg-hover, rgba(127, 127, 127, 0.14));
color: var(--vlist-text-muted, #94a3b8);
box-sizing: border-box;
}
.commit__body {
min-width: 0;
}
.commit__subject {
font-size: 13px;
line-height: 1.4;
color: var(--vlist-text, #e5e7eb);
}
.commit__meta {
margin-top: 3px;
font-size: 11px;
color: var(--vlist-text-muted, #94a3b8);
}
.commit__meta code {
font-size: 11px;
}
.commit__tag {
padding: 1px 6px;
font-size: 10px;
font-weight: 600;
line-height: 1.4;
text-align: center;
color: #a78bfa;
background: rgba(167, 139, 250, 0.12);
border: 1px solid rgba(167, 139, 250, 0.25);
border-radius: 4px;
}
.commit--feat .commit__type {
color: #34d399;
}
.commit--fix .commit__type {
color: #f87171;
}
.commit--docs .commit__type {
color: #38bdf8;
}
.commit--refactor .commit__type {
color: #a78bfa;
}
.commit--perf .commit__type {
color: #22d3ee;
}
.commit--test .commit__type {
color: #facc15;
}
.commit--style .commit__type {
color: #f472b6;
}
.commit--chore .commit__type,
.commit--ci .commit__type {
color: #94a3b8;
}
.commit--revert .commit__type {
color: #fb923c;
}
<div class="container">
<header>
<h1>Code Explorer</h1>
<p class="description">
Browse <strong>vlist's own source</strong>: a virtualized file
<code>tree</code> on the left, a searchable list on the right. Pick
a file to list its exported <strong>symbols</strong> (functions,
constants, types…) and filter them; or switch to
<strong>History</strong> to scrub the commits that touched that file
in <em>navigate</em> mode. clears.
</p>
</header>
<div class="split-layout">
<div class="split-main split-main--full">
<h2 class="sr-only">Code Explorer</h2>
<div class="explorer">
<div class="explorer-tree">
<div class="explorer-header">
<span class="explorer-title">vlist</span>
<button
class="explorer-btn"
id="cx-tree-toggle"
type="button"
>
Expand all
</button>
</div>
<div id="tree-container"></div>
</div>
<div class="explorer-list">
<div class="explorer-header">
<span class="explorer-context" id="cx-context"></span>
<div class="ui-segmented" id="cx-view-toggle">
<button
class="ui-segmented__btn ui-segmented__btn--active"
data-value="symbols"
>
Symbols
</button>
<button
class="ui-segmented__btn"
data-value="history"
>
History
</button>
</div>
</div>
<div id="symbol-container"></div>
</div>
</div>
</div>
<aside class="split-panel">
<!-- Search config -->
<section class="ui-section">
<h3 class="ui-title">Search</h3>
<div class="ui-row">
<label class="ui-label" for="cx-search-toggle"
>Enable</label
>
<label class="ui-switch">
<input type="checkbox" id="cx-search-toggle" checked />
<span class="ui-switch__track"></span>
</label>
</div>
</section>
<!-- Mode -->
<section class="ui-section" id="cx-mode-section">
<h3 class="ui-title">Mode</h3>
<div class="ui-row">
<div class="ui-segmented" id="cx-mode-toggle">
<button
class="ui-segmented__btn ui-segmented__btn--active"
data-value="filter"
>
Filter
</button>
<button class="ui-segmented__btn" data-value="navigate">
Navigate
</button>
</div>
</div>
</section>
<!-- Variant -->
<section class="ui-section" id="cx-style-section">
<h3 class="ui-title">Variant</h3>
<div class="ui-row">
<div class="ui-segmented" id="cx-style-toggle">
<button
class="ui-segmented__btn ui-segmented__btn--active"
data-value="default"
>
Default
</button>
<button class="ui-segmented__btn" data-value="md3">
MD3
</button>
</div>
</div>
</section>
<!-- Highlight -->
<section class="ui-section" id="cx-highlight-section">
<h3 class="ui-title">Highlight</h3>
<div class="ui-row">
<label class="ui-label" for="cx-highlight-toggle"
>Enable</label
>
<label class="ui-switch">
<input
type="checkbox"
id="cx-highlight-toggle"
checked
/>
<span class="ui-switch__track"></span>
</label>
</div>
<div class="ui-row">
<div class="ui-segmented" id="cx-scope-toggle">
<button class="ui-segmented__btn" data-value="scoped">
Name only
</button>
<button
class="ui-segmented__btn ui-segmented__btn--active"
data-value="full"
>
Whole row
</button>
</div>
</div>
</section>
<!-- Options -->
<section class="ui-section" id="cx-options-section">
<h3 class="ui-title">Options</h3>
<div class="ui-row">
<label class="ui-label" for="cx-case-toggle"
>Case sensitive</label
>
<label class="ui-switch">
<input type="checkbox" id="cx-case-toggle" />
<span class="ui-switch__track"></span>
</label>
</div>
</section>
</aside>
</div>
<div class="example-info" id="example-info">
<div class="example-info__left">
<span class="example-info__stat">
<strong id="info-progress">0%</strong>
</span>
<span class="example-info__stat">
<span id="info-velocity">0.00</span> /
<strong id="info-velocity-avg">0.00</strong>
<span class="example-info__unit">px/ms</span>
</span>
<span class="example-info__stat">
<span id="info-dom">0</span> /
<strong id="info-total">0</strong>
<span class="example-info__unit">rendered</span>
</span>
</div>
<div class="example-info__right">
<span class="example-info__stat">
<strong id="info-matches">0 / 0</strong>
<span class="example-info__unit">matches</span>
</span>
</div>
</div>
</div>