Documentation menu

Devtools

Gyral's loop is data from end to end: an event becomes a message, a reducer turns it into state and commands, a driver answers with another message. So a timeline of exactly what happened is cheap to record. @gyral/devtools shows it in a panel inside your page.

npm install -D @gyral/devtools

Opening the panel

Load it in development only, behind a URL parameter, so production builds never include it:

// src/devtools.ts
// Add ?devtools to the URL to open the panel. `vite build` replaces import.meta.env.DEV with
// false, so the import below disappears from production bundles.
if (import.meta.env.DEV && new URLSearchParams(location.search).has('devtools')) {
  void import('@gyral/devtools').then(({ mountDevtools }) => {
    mountDevtools({ open: true });
  });
}

export {};

Import that module from your client entry. Every Gyral example works this way: add ?devtools to an example's URL to open it.

mountDevtools(options) returns { panel, unmount }. The options are open (start open), parent (where to put the panel; default document.body) and shortcutCode (the key used with Alt+Shift to toggle it; default KeyD).

What it shows

  • Timeline. Every event, newest first: components connecting, disconnecting and hydrating, and hydration mismatches; each message with the state before and after; each command as it's issued, dropped (exhaust), interrupted (switch, disconnect), settled with the driver's output, or failed with its error; and store messages. Filter by text or by kind. The last 500 events are kept.
  • Components. The live instances, with a preview of each one's current state. Components that connected before the panel loaded are listed too.
  • Command lanes. Each lane's owner, policy, last phase and how many commands are in flight, which makes a stuck request or an unexpected switch easy to spot.

Toggle the panel with its button or Alt+Shift+D. It is itself a Gyral component: a labelled region with a real heading and keyboard-operable controls, in light and dark.

Free in production

The instrumentation lives in @gyral/core, behind a package-internal import with a development condition. Vite resolves that condition in the dev server and in Vitest (gyral() passes it to the dependency pre-bundle of an installed @gyral/core too). A browser bundle built without it, by vite build or any other bundler, gets core's production build, which has neither the hook nor its event code: they are removed when Gyral is built.

With other bundlers, set the development resolve condition in development builds to get events (esbuild: conditions: ['development']). Without it you get the safe default: no events.

Building your own tools

The panel listens through a small hook. A development build of @gyral/core emits each event to globalThis.__GYRAL_DEVTOOLS__.emit(event) when that object exists (DEVTOOLS_GLOBAL names the property, and the DevEvent type describes the events). Both come from @gyral/core/internal, which has no stability promise: the event shapes may change in any release. Without a listener, Gyral returns before building an event. Event payloads hold live references to elements and state: read them, never change them.