Diagnostic Panel

diagnostic-panelViewers

What the host told the plugin, what reached it and what it produced, shown in eleven tabs.

$ pnpm dlx shadcn@latest add @pluginui/core-diagnostic-panel
Loading interactive preview…

Install

Copies the source into your project. Anything it depends on comes with it. Run this in your React project with shadcn initialized; no registry configuration is needed.

Pulls in

use-diagnostic-sessioncontrol-thememidi-message-decodelevel-meteroscilloscopegoniometerspectrumpiano-keyboardstft
Install using the registry URL
$ pnpm dlx shadcn@latest add https://pluginui.com/r/core-diagnostic-panel.json

Usage

Connect the documented props to your application or host.

example.tsx
import { DiagnosticPanel } from "@/components/ui/diagnostic-panel"
import { createDiagnosticSession } from "maruaudio/ui/shared/maru/diagnostic-session"
import { createFaceApiClient } from "maruaudio/ui/shared/maru/face-api-client"

const host = createFaceApiClient()
const session = createDiagnosticSession(host)
host.ready()
session.open()             // nothing streams until this is called

// Each read re-derives feed ages because silence sends no message.
// Wrap the session before useSyncExternalStore so React sees a stable
// store value across renders.
const readSnapshot = useMemo(() => createSnapshotStabilizer(session.snapshot), [session])
const snapshot = useSyncExternalStore(session.subscribe, readSnapshot, readSnapshot)

<DiagnosticPanel session={session} snapshot={snapshot} />

// Controlled tab, when the surrounding chrome owns the selection.
<DiagnosticPanel session={session} snapshot={snapshot} tab={tab} onTabChange={setTab} />
About Diagnostic Panel

The developer view of a running plugin, fed by one `DiagnosticSession` snapshot: Overview, Host, Transport/Clock, MIDI, Audio, Parameters, Presets/State, Events, Stimulus, Performance and Package. The component is controlled and presentational. It owns no bridge and opens no feed, so the same view serves a drawer over a plugin face, a dev-runner pane and this docs page. Its editorial rule makes it trustworthy: every value comes from the snapshot or appears as an explicit Unreported cell. There is no `?? 0`, because an unreported latency is not zero latency and a frame that never arrived is not silence. Filling those gaps with plausible numbers is worse than leaving them blank; a developer may act on them. The stereo verdict checks per-channel RMS before correlation. A digitally silent pair correlates at exactly 1.0 by meter convention, so correlation alone would call dead air perfect mono. Audio taps use `session.readScope` inside the draw loop rather than the snapshot, keeping each 512-sample frame out of the values React diffs. Overview leads with a status strip for health, feed verdicts, build, host rate and block, latency, and connection. Its one-word verdict overrides the host's claim when a promised feed goes silent, since a dead feed is a broken host bridge regardless of DSP health. A dropped-frame count that grows with callbacks is labelled “the host is not draining” rather than shown as a number without context. Copy report puts a ten-line plain-text summary on the clipboard before the full JSON, because JSON is unreadable in the chat window where it is pasted. The closed panel costs no frames: its four Audio viewers share an animation frame scheduled only while the tab and document are visible; each tab is memoised over only the snapshot slice it reads; and tables past 200 rows render only the visible band, with spacer rows for the remaining height.

Props

PropTypeDefaultDescription
sessionDiagnosticSession—The SDK session the panel reads. Used for stimulus commands, `buildLocalReport()` and `readScope` in the audio tab's draw loop; the panel never opens or closes it, so visibility stays the caller's decision.
snapshotDiagnosticSnapshot—The value to render. Read it with `useSyncExternalStore` over `createSnapshotStabilizer(session.snapshot)` rather than over `session.snapshot` directly: the session re-derives feed-health ages at read time, so its raw snapshot is not referentially stable enough for that hook. Any field left `undefined` is one this host never reported, and renders as an Unreported cell rather than a default.
tabDiagnosticTabId—Controlled tab selection. Omit to let the panel own it. One of overview, host, transport, midi, audio, parameters, presets, events, stimulus, performance, package.
onTabChange(tab: DiagnosticTabId) => void—Fires when the rail selection changes, in both controlled and uncontrolled modes.
headerActionsReactNode—Rendered to the right of the header, where the overlay places its close button.
liveboolean—Defaults to true. Set false while the panel is mounted but not visible: the shared animation frame behind the Audio tab's viewers is then never scheduled at all, so a hidden panel costs nothing rather than merely idling.

Interaction

  • Drag with pointer capture, so the gesture keeps tracking past the edge of the control.
  • Shift while dragging for a fine pass; release it mid-gesture and coarse tracking resumes.
  • Double-click resets to defaultValue.
  • Arrow keys step normally, Shift+arrow steps finely, PageUp/PageDown jump by ten, Home/End go to the extremes.
  • Proper slider and switch roles with live aria-valuetext, so a screen reader announces the formatted value rather than the raw number.
  • The scroll wheel adjusts the value while the pointer is over the control; stepped ranges move at least one declared step per notch by default.