{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "core-fixed-velocity-control",
  "type": "registry:ui",
  "title": "Fixed Velocity Control",
  "description": "A compact fixed-velocity override for playable instrument surfaces: switch between touch dynamics and a repeatable normalised strike value.",
  "dependencies": [
    "maruaudio@>=0.0.3 <0.1.0"
  ],
  "registryDependencies": [
    "https://pluginui.com/r/core-toggle-switch.json"
  ],
  "files": [
    {
      "path": "components/ui/fixed-velocity-control.tsx",
      "content": "/**\n * Source: https://maru.audio/ui-kit/fixed-velocity-control\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 */\n/** A compact override for repeatable instrument-face velocity. */\nimport { useId } from \"react\"\nimport type { CSSProperties } from \"react\"\nimport { token } from \"maruaudio/ui/shared/theme\"\nimport { ToggleSwitch } from \"@/components/ui/toggle-switch\"\n\nexport interface FixedVelocityControlProps {\n  enabled: boolean\n  velocity: number\n  onEnabledChange: (enabled: boolean) => void\n  onVelocityChange: (velocity: number) => void\n  label?: string\n  disabled?: boolean\n  className?: string\n  style?: CSSProperties\n}\n\nexport function clampVelocity(value: number): number {\n  if (!Number.isFinite(value)) return 0.8\n  return Math.max(0, Math.min(1, value))\n}\n\nexport function resolveStrikeVelocity(\n  gestureVelocity: number,\n  fixedEnabled: boolean,\n  fixedVelocity: number,\n): number {\n  return clampVelocity(fixedEnabled ? fixedVelocity : gestureVelocity)\n}\n\n/** Harder strikes carry more energy and therefore receive a longer ring. */\nexport function velocityToDuration(\n  velocity: number,\n  minimumSeconds: number,\n  maximumSeconds: number,\n): number {\n  const low = Math.max(0, Math.min(minimumSeconds, maximumSeconds))\n  const high = Math.max(low, Math.max(minimumSeconds, maximumSeconds))\n  return low + (high - low) * Math.sqrt(clampVelocity(velocity))\n}\n\nexport function FixedVelocityControl({\n  enabled,\n  velocity,\n  onEnabledChange,\n  onVelocityChange,\n  label = \"Fixed velocity\",\n  disabled = false,\n  className,\n  style,\n}: FixedVelocityControlProps) {\n  const sliderId = useId()\n  const value = clampVelocity(velocity)\n\n  return (\n    <div\n      className={className}\n      style={{\n        display: \"grid\",\n        gridTemplateColumns: \"auto minmax(92px, 1fr)\",\n        alignItems: \"end\",\n        gap: 12,\n        padding: 10,\n        border: `1px solid ${token.border}`,\n        borderRadius: token.radius,\n        background: token.surface,\n        ...style,\n      }}\n      data-fixed-velocity={enabled ? \"enabled\" : \"disabled\"}\n    >\n      <ToggleSwitch\n        checked={enabled}\n        onChange={onEnabledChange}\n        label={label}\n        onLabel=\"FIXED\"\n        offLabel=\"TOUCH\"\n        disabled={disabled}\n        size={0.82}\n      />\n      <label\n        htmlFor={sliderId}\n        style={{\n          display: \"grid\",\n          gap: 6,\n          minWidth: 0,\n          color: enabled ? token.text : token.textMuted,\n          fontFamily: token.fontMono,\n          fontSize: 10,\n          letterSpacing: \"0.08em\",\n          textTransform: \"uppercase\",\n        }}\n      >\n        <span style={{ display: \"flex\", justifyContent: \"space-between\", gap: 8 }}>\n          <span>Strike</span>\n          <span style={{ color: enabled ? token.accent : token.textMuted }}>\n            {Math.round(value * 127)}\n          </span>\n        </span>\n        <input\n          id={sliderId}\n          type=\"range\"\n          min={0.01}\n          max={1}\n          step={0.01}\n          value={value}\n          disabled={disabled || !enabled}\n          aria-label={`${label} value`}\n          onChange={(event) => onVelocityChange(clampVelocity(Number(event.target.value)))}\n          style={{ width: \"100%\", accentColor: token.accent }}\n        />\n      </label>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@ui/fixed-velocity-control.tsx"
    }
  ]
}
