{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "audio-meter",
  "type": "registry:component",
  "title": "Audio meter",
  "description": "Shows supplied channel levels and peaks in decibels, with bounded native meters and explicit missing readings.",
  "registryDependencies": [
    "https://vlak.dev/r/vlak-base.json",
    "https://vlak.dev/r/inter.json",
    "https://vlak.dev/r/vlak-lib.json"
  ],
  "dependencies": [
    "@stylexjs/stylex"
  ],
  "devDependencies": [
    "@stylexjs/babel-plugin"
  ],
  "docs": "Vlak leaves are StyleX. Compile them with @stylexjs/babel-plugin (Vite: @stylexjs/unplugin, Next: @stylexjs/nextjs-plugin). If you would rather not run a compiler, import @noorddev/vlak-react instead: it ships precompiled with one stylesheet.",
  "files": [
    {
      "path": "vlak/audio-meter.tsx",
      "content": "import * as React from \"react\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport { vlak, mq } from \"./tokens.stylex\";\nimport { rs } from \"./rs\";\n\nexport interface AudioMeterChannel { id: string; label: string; level: number | null; peak?: number | null }\nexport interface AudioMeterProps extends React.HTMLAttributes<HTMLDivElement> {\n  label: React.ReactNode;\n  channels: AudioMeterChannel[];\n  min?: number;\n  max?: number;\n}\n\nconst styles = stylex.create({\n  root: { display: \"grid\", gap: \"0.75rem\", minWidth: 0, color: vlak.ink },\n  label: { margin: 0, fontSize: vlak.controlFs, fontWeight: 600, lineHeight: 1.45 },\n  channel: { display: \"grid\", gap: \"0.375rem\" },\n  text: { display: \"flex\", flexWrap: \"wrap\", justifyContent: \"space-between\", gap: \"0.25rem 1rem\", fontSize: vlak.controlLabel, lineHeight: 1.45, fontVariantNumeric: \"tabular-nums\" },\n  meter: { display: \"block\", width: \"100%\", height: \"0.75rem\", appearance: \"none\", backgroundColor: vlak.paper, borderWidth: vlak.hairline, borderStyle: \"solid\", borderColor: vlak.controlBorder, boxSizing: \"border-box\", \"::-webkit-meter-bar\": { backgroundColor: vlak.paper, backgroundImage: \"none\", borderWidth: 0, borderRadius: 0, boxShadow: \"none\" }, \"::-webkit-meter-optimum-value\": { backgroundColor: { default: vlak.ink, [mq.forcedColors]: \"CanvasText\" }, backgroundImage: \"none\" }, \"::-moz-meter-bar\": { backgroundColor: { default: vlak.ink, [mq.forcedColors]: \"CanvasText\" }, backgroundImage: \"none\" } },\n  note: { color: vlak.gray, margin: 0, fontSize: vlak.controlLabel, lineHeight: 1.45 },\n});\n\nfunction decibels(value: number | null | undefined) { return value === Number.NEGATIVE_INFINITY ? \"−∞ dB\" : value != null && Number.isFinite(value) ? `${value} dB` : \"Unavailable\"; }\n\n/** Supplied channel levels and peaks; no audio analysis or peak holding. */\nexport const AudioMeter = React.forwardRef<HTMLDivElement, AudioMeterProps>(function AudioMeter({ label, channels, min = -60, max = 0, className, style, ...props }, ref) {\n  const id = React.useId();\n  const validRange = Number.isFinite(min) && Number.isFinite(max) && Number.isFinite(max - min) && max > min;\n  const root = rs([\"rs-audio-meter\", className], styles.root);\n  const heading = rs([\"rs-audio-meter-label\"], styles.label);\n  const channelStyle = rs([\"rs-audio-meter-channel\"], styles.channel);\n  const text = rs([\"rs-audio-meter-text\"], styles.text);\n  const meter = rs([\"rs-audio-meter-bar\"], styles.meter);\n  const note = rs([\"rs-audio-meter-note\"], styles.note);\n  return <div {...props} ref={ref} className={root.className} style={{ ...root.style, ...style }}>\n    <p id={`${id}-label`} className={heading.className} style={heading.style}>{label}</p>\n    {!validRange && <p className={note.className} style={note.style}>Meter range unavailable</p>}\n    {channels.length === 0 && <p className={note.className} style={note.style}>No channels supplied</p>}\n    {channels.map((channel, index) => {\n      const validLevel = channel.level != null && (Number.isFinite(channel.level) || channel.level === Number.NEGATIVE_INFINITY);\n      return <div key={channel.id} className={channelStyle.className} style={channelStyle.style}>\n        <div className={text.className} style={text.style}><span id={`${id}-${index}`}>{channel.label}</span><span>{decibels(channel.level)}</span></div>\n        {validRange && validLevel && <meter min={min} max={max} value={Math.min(max, Math.max(min, channel.level!))} aria-labelledby={`${id}-label ${id}-${index}`} aria-valuetext={decibels(channel.level)} className={meter.className} style={meter.style}>{decibels(channel.level)}</meter>}\n        {channel.peak !== undefined && <p className={note.className} style={note.style}>Peak: {decibels(channel.peak)}</p>}\n      </div>;\n    })}\n  </div>;\n});\n",
      "type": "registry:component",
      "target": "components/vlak/audio-meter.tsx"
    },
    {
      "path": "vlak/styles/audio-meter.css",
      "content": "/* ── audio-meter: generated from packages/react/src/components/audio-meter.tsx ── */\n.rs-audio-meter{display:grid;gap:0.75rem;min-width:0;color:var(--text)}\n.rs-audio-meter-label{margin:0;font-size:var(--control-fs);font-weight:600;line-height:1.45}\n.rs-audio-meter-channel{display:grid;gap:0.375rem}\n.rs-audio-meter-text{display:flex;flex-wrap:wrap;justify-content:space-between;gap:0.25rem 1rem;font-size:var(--control-label);line-height:1.45;font-variant-numeric:tabular-nums}\n.rs-audio-meter-bar{display:block;width:100%;height:0.75rem;appearance:none;background-color:var(--bg);border-width:1px;border-style:solid;border-color:var(--control-border);box-sizing:border-box}\n.rs-audio-meter-bar::-webkit-meter-bar{background-color:var(--bg);background-image:none;border-width:0;border-radius:0;box-shadow:none}\n.rs-audio-meter-bar::-webkit-meter-optimum-value{background-color:var(--text);background-image:none}\n.rs-audio-meter-bar::-moz-meter-bar{background-color:var(--text);background-image:none}\n@media (forced-colors: active){.rs-audio-meter-bar::-webkit-meter-optimum-value{background-color:CanvasText}.rs-audio-meter-bar::-moz-meter-bar{background-color:CanvasText}}\n.rs-audio-meter-note{color:var(--text-secondary);margin:0;font-size:var(--control-label);line-height:1.45}\n",
      "type": "registry:file",
      "target": "styles/vlak/audio-meter.css"
    }
  ],
  "meta": {
    "vlak": {
      "category": "creative",
      "classes": [
        "rs-audio-meter",
        "rs-audio-meter-label",
        "rs-audio-meter-channel",
        "rs-audio-meter-text",
        "rs-audio-meter-bar",
        "rs-audio-meter-note"
      ],
      "snippet": "<div class=\"rs-audio-meter\"><p class=\"rs-audio-meter-label\">Output</p><div class=\"rs-audio-meter-channel\"><div class=\"rs-audio-meter-text\"><span id=\"left-channel\">Left</span><span>-12 dB</span></div><meter class=\"rs-audio-meter-bar\" min=\"-60\" max=\"0\" value=\"-12\" aria-labelledby=\"left-channel\">-12 dB</meter><p class=\"rs-audio-meter-note\">Peak: -3 dB</p></div></div>",
      "cssOnly": false,
      "registryDependencies": [
        "vlak-lib"
      ],
      "aliases": [
        "AudioMeter",
        "Level meter",
        "Peak meter",
        "Decibel meter",
        "Channel meter"
      ],
      "example": "import { AudioMeter } from \"@noorddev/vlak-react\";\n\n<AudioMeter label=\"Output\" channels={[{ id: \"left\", label: \"Left\", level: -12, peak: -3 }, { id: \"right\", label: \"Right\", level: -15, peak: -4 }]} />",
      "usage": {
        "use": [
          "Live or recorded decibel readings supplied by an audio host.",
          "min and max to set the visible scale; supplied readings remain visible as text.",
          "Negative infinity for digital silence, and null for an unavailable level."
        ],
        "avoid": [
          "Expecting microphone access, audio analysis, or automatic peak holding.",
          "Treating a missing reading as silence or zero decibels."
        ]
      },
      "keyboard": [],
      "a11y": [
        "Each native meter has a channel name, minimum, maximum, and decibel value text.",
        "Supplied peaks are visible text; absent and invalid readings suppress the meter.",
        "Native meter styles remove browser gradients so the track and fill remain monochrome. System colors retain visible fill and boundaries in forced colors.",
        "The ref and native attributes reach the root div."
      ]
    }
  }
}
