Overview

mates-devtools is an in-app panel (FAB + resizable inspector) for the running Mates tree — components, atoms, logs, network, and performance.

Call renderMatesDevTools() before renderApp() so hooks are installed when the first components mount.

Core keeps tiny no-op stubs in lib/devtools. The panel package fills them via installDevToolsHooks — production bundles stay clean when you omit the package.

Panel vs bridge

Piece
Package
Role
renderMatesDevTools mates-devtools FAB + panel — installs hooks on call
installDevToolsHooks mates (core) Bridge API — partial replace of no-ops
isDevToolsInstalled mates (core) Gate expensive debug-only work

Counter + DevTools panel

A simple Counter. Click the gear FAB in the preview, open Components or Logs, then press + / − to watch atoms and renders update live.

import { html, atom, x, renderApp } from 'mates';
import { renderMatesDevTools } from 'mates-devtools';

// Install hooks + FAB / panel before any component mounts
renderMatesDevTools();

const Counter = () => {
const count = atom(0);

return () => html`
<div class="m-col m-gap m-items-center">
<p class="overline">count</p>
<h1>${count()}</h1>
<div class="m-flex m-gap-sm">
<button class="btn" @click=${() => count.set(n => n - 1)}>−</button>
<button class="btn-ghost" @click=${() => count.set(0)}>Reset</button>
<button class="btn-primary" @click=${() => count.set(n => n + 1)}>+</button>
</div>
<p class="hint">Open the ⚙️ FAB → Components / Logs to inspect this Counter.</p>
</div>
`;
};

const App = () => () => html`
<div class="m-col m-gap">
<h1>DevTools counter</h1>
<p class="muted">Click + / −, then inspect atoms and renders in the panel.</p>
${x(Counter)}
</div>
`;

renderApp(App, document.getElementById('app'));

/* Extra bottom pad so the DevTools FAB doesn't cover actions */
#app { padding-bottom: 4.5rem; }

installDevToolsHooks (bridge)

Low-level demo: wire a partial hook set and flip isDevToolsInstalled. Prefer the panel demo above for day-to-day debugging.

import { html, atom, renderApp, installDevToolsHooks, isDevToolsInstalled } from 'mates';

const App = () => {
const installed = atom(isDevToolsInstalled());
const log = atom([]);

const wire = () => {
installDevToolsHooks({
logAtomSet: (name, value) => {
log.update(rows => {
rows.push('atom set ' + name + ' = ' + String(value));
});
},
registerComponent: () => {},
});
installed.set(isDevToolsInstalled());
};

const count = atom(0);

return () => html`
<div class="m-col m-gap">
<p class="muted">installed=${installed() ? 'yes' : 'no'}</p>
<div class="m-flex m-items-center m-justify-center m-gap-sm">
<button class="btn-primary" @click=${wire}>installDevToolsHooks</button>
<button @click=${() => count.set(c => c + 1)}>
count=${count()}
</button>
</div>
<div class="card card-body">
<span class="overline">Hook log</span>
${log().map(line => html`<div class="mono">${line}</div>`)}
</div>
<p class="muted m-t-5">Prefer renderMatesDevTools() for the full panel — this only shows the bridge.</p>
</div>
`;