{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "core-module-power-button",
  "type": "registry:ui",
  "title": "Module Power Button",
  "description": "Module bypass switch: ModulePowerHeadless for any skin, plus a discreet stock icon, a ToggleSwitch variant coloured by module CSS, and a labelled Live treatment.",
  "dependencies": [
    "maruaudio@>=0.0.3 <0.1.0"
  ],
  "registryDependencies": [
    "https://pluginui.com/r/core-toggle-headless.json",
    "https://pluginui.com/r/core-toggle-switch.json"
  ],
  "files": [
    {
      "path": "components/ui/module-power-button.tsx",
      "content": "/**\n * Source: https://maru.audio/ui-kit/module-power-button\n * Copyright © [Maru](https://maru.audio)\n *\n * Maru UI Kit — https://maru.audio/ui-kit\n *\n * Installed from the Maru UI registry. This file is yours now: edit it\n * freely, it will not be overwritten unless you re-run the add command.\n *\n * Keep this item’s companion files and CSS imports together. Dependencies\n * declared by the registry are installed with the source.\n */\nimport { forwardRef } from \"react\"\nimport type {\n  ButtonHTMLAttributes,\n  ComponentProps,\n  CSSProperties,\n  ReactNode,\n} from \"react\"\n\nimport { ToggleHeadless } from \"@/components/ui/toggle-headless\"\nimport { ToggleSwitch, type ToggleShape } from \"@/components/ui/toggle-switch\"\n\nexport interface ModulePowerControlOptions {\n  powered: boolean\n  onPoweredChange: (powered: boolean) => void\n  disabled?: boolean\n}\n\nexport interface ModulePowerControlSlot {\n  powered: boolean\n  disabled: boolean\n  setPowered: (powered: boolean) => void\n  toggle: () => void\n  buttonProps: ButtonHTMLAttributes<HTMLButtonElement> & {\n    \"data-module-power\": true\n    \"data-powered\": boolean\n  }\n}\n\nexport interface ModulePowerControlProps extends ModulePowerControlOptions {\n  children: (slot: ModulePowerControlSlot) => ReactNode\n}\n\nexport function useModulePowerControl({\n  powered,\n  onPoweredChange,\n  disabled = false,\n}: ModulePowerControlOptions): ModulePowerControlSlot {\n  const setPowered = (next: boolean) => {\n    if (!disabled) onPoweredChange(next)\n  }\n  const toggle = () => setPowered(!powered)\n\n  return {\n    powered,\n    disabled,\n    setPowered,\n    toggle,\n    buttonProps: {\n      type: \"button\",\n      role: \"switch\",\n      \"aria-checked\": powered,\n      \"aria-label\": powered ? \"Bypass module\" : \"Enable module\",\n      disabled,\n      \"data-powered\": powered,\n      \"data-module-power\": true,\n      onClick: toggle,\n    },\n  }\n}\n\nexport function ModulePowerControl({\n  children,\n  ...options\n}: ModulePowerControlProps) {\n  return children(useModulePowerControl(options))\n}\n\n/** What a custom power skin draws from. Same shape as the knob's headless state. */\nexport interface ModulePowerRenderState {\n  powered: boolean\n  /** Authoritative audible state; powered-but-not-running is `false`. */\n  live: boolean\n  /** True while the pointer (or Space/Enter) is held down. */\n  pressed: boolean\n  disabled: boolean\n}\n\ntype NativePowerButtonProps = Omit<\n  ComponentProps<\"button\">,\n  \"role\" | \"aria-checked\" | \"type\" | \"children\" | \"onChange\"\n>\n\nexport interface ModulePowerHeadlessProps extends NativePowerButtonProps {\n  powered: boolean\n  onPoweredChange: (powered: boolean) => void\n  live?: boolean\n  disabled?: boolean\n  /** Draw anything; switch semantics, events and data attributes are wired. */\n  children: (state: ModulePowerRenderState) => ReactNode\n}\n\n/**\n * Headless module power switch, like `KnobHeadless`/`ToggleHeadless`: one\n * `<button role=\"switch\">` carrying the power semantics (label, aria-checked,\n * `data-module-power`, `data-powered`, `data-live`) around any skin.\n */\nexport const ModulePowerHeadless = forwardRef<\n  HTMLButtonElement,\n  ModulePowerHeadlessProps\n>(function ModulePowerHeadless(\n  { powered, onPoweredChange, live = powered, disabled = false, children, ...rest },\n  ref\n) {\n  const label = powered ? \"Bypass module\" : \"Enable module\"\n  return (\n    <ToggleHeadless\n      ref={ref}\n      checked={powered}\n      onChange={onPoweredChange}\n      disabled={disabled}\n      aria-label={label}\n      data-module-power=\"\"\n      data-powered={powered}\n      data-live={live}\n      {...rest}\n    >\n      {({ on, pressed }) => children({ powered: on, live, pressed, disabled })}\n    </ToggleHeadless>\n  )\n})\n\n/**\n * - `icon` (default): the bare power glyph, no box. Inherits the face's text\n *   colour; powered uses `--module-power-accent` (fallback: current colour).\n * - `switch`: a `ToggleSwitch`; colour it from the module stylesheet with the\n *   usual `--uikit-cap`, `--uikit-track`, `--uikit-border` tokens.\n * - `live`: lamp and status label (host chrome).\n */\nexport type ModulePowerButtonVariant = \"icon\" | \"switch\" | \"live\"\nexport type ModulePowerButtonTone = \"light\" | \"dark\"\n\nexport interface ModulePowerButtonProps {\n  powered: ModulePowerControlOptions[\"powered\"]\n  onPoweredChange: ModulePowerControlOptions[\"onPoweredChange\"]\n  disabled?: ModulePowerControlOptions[\"disabled\"]\n  /** Authoritative audible state. Powered-but-not-running renders as On. */\n  live?: boolean\n  variant?: ModulePowerButtonVariant\n  /** Explicit ink for faces whose inherited colour is unreadable. Omit to\n   * inherit, which is what lets a module stylesheet colour the icon. */\n  tone?: ModulePowerButtonTone\n  /** Powered colour (any CSS colour). Same as setting `--module-power-accent`. */\n  accent?: string\n  /** `switch` variant: the ToggleSwitch shape and scale. */\n  switchShape?: ToggleShape\n  size?: number\n  /** Remove all default visual classes while retaining switch semantics. */\n  unstyled?: boolean\n  /** Fully replace the contents without reimplementing the behavior. */\n  children?: ReactNode | ((state: ModulePowerRenderState & {\n    statusLabel: ReturnType<typeof modulePowerStatusLabel>\n  }) => ReactNode)\n  className?: string\n  style?: CSSProperties\n}\n\n/** Kept for callers that read the old render-state name. */\nexport type ModulePowerButtonRenderState = ModulePowerRenderState & {\n  statusLabel: ReturnType<typeof modulePowerStatusLabel>\n}\n\nconst TONE_INK: Record<ModulePowerButtonTone, string> = {\n  light: \"#3f3f46\",\n  dark: \"#e4e4e7\",\n}\n\nexport function modulePowerStatusLabel(\n  powered: boolean,\n  live = powered\n): \"Live\" | \"On\" | \"Off\" {\n  return live ? \"Live\" : powered ? \"On\" : \"Off\"\n}\n\n/** The IEC power glyph, drawn in `currentColor`. */\nexport function ModulePowerIcon({ size = 16 }: { size?: number }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      viewBox=\"0 0 24 24\"\n      width={size}\n      height={size}\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2.2}\n      strokeLinecap=\"round\"\n    >\n      <path d=\"M12 3v8\" />\n      <path d=\"M6.3 6.8a8 8 0 1 0 11.4 0\" />\n    </svg>\n  )\n}\n\nfunction join(...parts: (string | false | undefined)[]) {\n  return parts.filter(Boolean).join(\" \")\n}\n\n/** Shared, discreet module power/bypass control. */\nexport function ModulePowerButton({\n  powered,\n  onPoweredChange,\n  disabled,\n  live = powered,\n  variant = \"icon\",\n  tone,\n  accent,\n  switchShape = \"rocker\",\n  size = 1,\n  unstyled = false,\n  children,\n  className,\n  style,\n}: ModulePowerButtonProps) {\n  const statusLabel = modulePowerStatusLabel(powered, live)\n  const themed: CSSProperties = {\n    ...(tone ? ({ \"--module-power-ink\": TONE_INK[tone] } as CSSProperties) : null),\n    ...(accent ? ({ \"--module-power-accent\": accent } as CSSProperties) : null),\n    ...style,\n  }\n\n  if (variant === \"switch\" && children === undefined) {\n    // ToggleSwitch owns its own role=switch button; the wrapper carries the\n    // power data attributes so face CSS and hosts find it the same way.\n    return (\n      <span\n        data-module-power=\"\"\n        data-powered={powered}\n        data-live={live}\n        data-variant=\"switch\"\n        className={join(\"inline-flex items-center\", className)}\n        style={themed}\n      >\n        <ToggleSwitch\n          checked={powered}\n          onChange={onPoweredChange}\n          disabled={disabled}\n          variant={switchShape}\n          skin=\"flat\"\n          size={size}\n          hideValue\n          aria-label={powered ? \"Bypass module\" : \"Enable module\"}\n        />\n      </span>\n    )\n  }\n\n  const isLive = variant === \"live\"\n  return (\n    <ModulePowerHeadless\n      powered={powered}\n      onPoweredChange={onPoweredChange}\n      disabled={disabled}\n      live={live}\n      data-variant={variant}\n      data-tone={tone}\n      style={themed}\n      className={join(\n        !unstyled &&\n          (isLive\n            ? \"uikit-control inline-flex h-8 min-w-[4.75rem] shrink-0 items-center justify-between gap-2 rounded-full px-2.5 text-[var(--module-power-ink,currentColor)] transition-opacity focus-visible:ring-2 focus-visible:ring-current/40 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-45\"\n            : \"uikit-control inline-flex shrink-0 items-center justify-center rounded-full bg-transparent p-1 text-[var(--module-power-ink,currentColor)] opacity-55 transition-[opacity,color] hover:opacity-90 focus-visible:ring-2 focus-visible:ring-current/40 focus-visible:outline-none active:translate-y-px disabled:cursor-not-allowed disabled:opacity-30 data-[powered=true]:text-[var(--module-power-accent,var(--module-power-ink,currentColor))] data-[powered=true]:opacity-100\"),\n        className\n      )}\n    >\n      {(state) =>\n        typeof children === \"function\" ? (\n          children({ ...state, statusLabel })\n        ) : children !== undefined ? (\n          children\n        ) : isLive ? (\n          <>\n            <span className=\"text-[10px] leading-none font-semibold tracking-[0.14em] uppercase\">\n              {statusLabel}\n            </span>\n            <span\n              aria-hidden=\"true\"\n              className=\"size-2 rounded-full bg-current opacity-30 data-[live=true]:opacity-100\"\n              data-live={live}\n            />\n          </>\n        ) : (\n          <ModulePowerIcon size={Math.round(16 * size)} />\n        )\n      }\n    </ModulePowerHeadless>\n  )\n}\n",
      "type": "registry:component",
      "target": "@ui/module-power-button.tsx"
    }
  ]
}
