{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "VoicePill-TS-CSS",
	"title": "VoicePill",
	"description": "Mic button that swells into a capsule of live equalizer bars and a timer while recording.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "VoicePill.css",
			"target": "@components/VoicePill.css",
			"content": ".voice-pill {\n  --vp-accent: #f5f5f5;\n  --vp-icon: #a1a1aa;\n  --vp-bg: #27272a;\n  --vp-size: 28px;\n  --vp-radius: 14px;\n  --vp-reach: 8px;\n  --vp-extra: 0px;\n  --vp-clock-w: 0px;\n  --vp-wave-w: 0px;\n  --vp-slide: 0px;\n  --vp-cancel: 0;\n  --vp-stop: 9px;\n  --vp-icon-size: 15px;\n  --vp-time-size: 10px;\n  --vp-open: 200ms;\n  --vp-press: 0.95;\n  --vp-hit: 8px;\n  --vp-ease-out: cubic-bezier(0.23, 1, 0.32, 1);\n\n  position: relative;\n  display: inline-grid;\n  place-items: center;\n  isolation: isolate;\n  width: var(--vp-size);\n  height: var(--vp-size);\n  margin: 0;\n  padding: 0;\n  border: 0;\n  border-radius: var(--vp-radius);\n  background: transparent;\n  color: var(--vp-icon);\n  font-family: inherit;\n  cursor: pointer;\n  outline: none;\n  user-select: none;\n  -webkit-user-select: none;\n  -webkit-touch-callout: none;\n  -webkit-tap-highlight-color: transparent;\n  touch-action: none;\n  transition:\n    transform 160ms var(--vp-ease-out),\n    color 150ms ease;\n}\n\n.voice-pill::before {\n  content: '';\n  position: absolute;\n  inset: calc(-1 * var(--vp-hit));\n}\n\n.voice-pill[data-state='listening'] {\n  z-index: 1;\n  color: var(--vp-accent);\n}\n\n.voice-pill[data-pressed] {\n  transform: scale(var(--vp-press));\n}\n\n.voice-pill:disabled {\n  opacity: 0.55;\n  cursor: default;\n  pointer-events: none;\n}\n\n.voice-pill__capsule {\n  position: absolute;\n  inset: 0 0 0 calc(-1 * (var(--vp-reach) + var(--vp-extra)));\n  border-radius: var(--vp-radius);\n  background: var(--vp-bg);\n  clip-path: inset(0 0 0 calc(var(--vp-reach) + var(--vp-extra)) round var(--vp-radius));\n  pointer-events: none;\n  transition:\n    clip-path var(--vp-open) var(--vp-ease-out),\n    background-color 150ms ease;\n}\n\n@media (hover: hover) and (pointer: fine) {\n  .voice-pill[data-state='idle']:not(:disabled):hover .voice-pill__capsule {\n    background: color-mix(in srgb, var(--vp-icon) 10%, var(--vp-bg));\n  }\n}\n\n.voice-pill[data-state='listening'] .voice-pill__capsule {\n  clip-path: inset(0 0 round var(--vp-radius));\n}\n\n.voice-pill__time {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  right: 100%;\n  display: grid;\n  place-items: center;\n  width: var(--vp-clock-w);\n  box-sizing: border-box;\n  color: var(--vp-accent);\n  font-size: var(--vp-time-size);\n  font-weight: 500;\n  font-variant-numeric: tabular-nums;\n  line-height: 1;\n  opacity: 0;\n  filter: blur(2px);\n  transform: translateX(var(--vp-slide));\n  pointer-events: none;\n  transition:\n    opacity 200ms ease,\n    filter 200ms ease,\n    transform 240ms var(--vp-ease-out);\n}\n\n.voice-pill[data-state='listening'] .voice-pill__time {\n  opacity: calc(1 - var(--vp-cancel));\n  filter: blur(0);\n}\n\n.voice-pill__wave {\n  position: absolute;\n  top: 18%;\n  right: calc(100% + var(--vp-clock-w));\n  width: var(--vp-wave-w);\n  height: 64%;\n  opacity: 0;\n  filter: blur(2px);\n  transform: translateX(var(--vp-slide));\n  pointer-events: none;\n  transition:\n    opacity 200ms ease,\n    filter 200ms ease,\n    transform 240ms var(--vp-ease-out);\n}\n\n.voice-pill[data-state='listening'] .voice-pill__wave {\n  opacity: calc(1 - var(--vp-cancel));\n  filter: blur(0);\n}\n\n.voice-pill__cancel {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  right: 100%;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  gap: 3px;\n  width: var(--vp-extra);\n  color: var(--vp-accent);\n  font-size: var(--vp-time-size);\n  font-weight: 500;\n  line-height: 1;\n  opacity: var(--vp-cancel);\n  pointer-events: none;\n}\n\n.voice-pill[data-sliding] .voice-pill__time,\n.voice-pill[data-sliding] .voice-pill__wave,\n.voice-pill[data-sliding] .voice-pill__glyph {\n  transition-property: opacity, filter;\n}\n\n.voice-pill__glyph {\n  position: relative;\n  display: grid;\n  place-items: center;\n  width: var(--vp-size);\n  height: var(--vp-size);\n  transform: translateX(var(--vp-slide));\n  transition: transform 240ms var(--vp-ease-out);\n}\n\n.voice-pill__glyph > * {\n  grid-area: 1 / 1;\n  transition:\n    opacity 200ms ease,\n    filter 200ms ease,\n    transform 200ms var(--vp-ease-out);\n}\n\n.voice-pill__mic {\n  display: inline-flex;\n}\n\n.voice-pill__stop {\n  width: var(--vp-stop);\n  height: var(--vp-stop);\n  border-radius: 22%;\n  background: currentColor;\n  opacity: 0;\n  filter: blur(2px);\n  transform: scale(0.6);\n}\n\n.voice-pill[data-state='listening'] .voice-pill__mic {\n  opacity: 0;\n  filter: blur(2px);\n  transform: scale(0.7);\n}\n\n.voice-pill[data-state='listening'] .voice-pill__stop {\n  opacity: 1;\n  filter: blur(0);\n  transform: scale(1);\n}\n\n.voice-pill[data-input='key'] .voice-pill__capsule,\n.voice-pill[data-input='key'] .voice-pill__time,\n.voice-pill[data-input='key'] .voice-pill__glyph > * {\n  transition-duration: 0ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .voice-pill {\n    transform: none !important;\n    transition: color 150ms ease;\n  }\n\n  .voice-pill__capsule {\n    transition: background-color 150ms ease;\n  }\n\n  .voice-pill__glyph > * {\n    filter: none !important;\n    transform: none !important;\n    transition: opacity 200ms ease;\n  }\n\n  .voice-pill__time,\n  .voice-pill__wave,\n  .voice-pill__glyph {\n    filter: none !important;\n    transition-property: opacity;\n  }\n}\n"
		},
		{
			"type": "registry:component",
			"path": "VoicePill.tsx",
			"content": "'use client';\n\nimport React, {\n  useEffect,\n  useRef,\n  useState,\n  type CSSProperties,\n  type KeyboardEvent,\n  type MouseEvent,\n  type PointerEvent\n} from 'react';\nimport { HugeiconsIcon } from '@hugeicons/react';\nimport { ArrowLeft01Icon, Mic01Icon } from '@hugeicons/core-free-icons';\nimport './VoicePill.css';\n\nexport type VoicePillShape = 'pill' | 'rounded';\nexport type VoicePillMode = 'auto' | 'hold' | 'toggle';\nexport type VoicePillSource = 'simulated' | 'mic';\nexport type VoicePillStopReason =\n  | 'release'\n  | 'tap'\n  | 'key'\n  | 'escape'\n  | 'blur'\n  | 'disabled'\n  | 'mic-denied'\n  | 'cancel'\n  | 'unmount';\n\nexport interface VoicePillProps {\n  accentColor?: string;\n  iconColor?: string;\n  background?: string;\n  size?: number;\n  shape?: VoicePillShape;\n  reach?: number;\n  showTime?: boolean;\n  waveform?: boolean;\n  slideToCancel?: boolean;\n  cancelDistance?: number;\n  attack?: number;\n  release?: number;\n  sensitivity?: number;\n  floor?: number;\n  openDuration?: number;\n  pressScale?: number;\n  mode?: VoicePillMode;\n  holdAfter?: number;\n  reactive?: VoicePillSource;\n  disabled?: boolean;\n  ariaLabel?: string;\n  onStart?: (info: { source: VoicePillSource }) => void;\n  onStop?: (info: { reason: VoicePillStopReason; duration: number }) => void;\n  className?: string;\n}\n\ninterface AudioBits {\n  ctx: AudioContext;\n  stream?: MediaStream | null;\n  src?: MediaStreamAudioSourceNode | null;\n  analyser?: AnalyserNode | null;\n  buf?: Uint8Array<ArrayBuffer> | null;\n}\n\ninterface State {\n  listening: boolean;\n  pointerId: number | null;\n  ownPress: boolean;\n  downX: number;\n  sliding: boolean;\n  hist: number[];\n  tick: number;\n  acc: number;\n  downAt: number;\n  startedAt: number;\n  raf: number;\n  last: number;\n  env: number;\n  t0: number;\n  audio: AudioBits | null;\n}\n\ninterface Cfg {\n  attack: number;\n  release: number;\n  sensitivity: number;\n  floor: number;\n  mode: VoicePillMode;\n  holdAfter: number;\n  reactive: VoicePillSource;\n  showTime: boolean;\n  waveform: boolean;\n  slideToCancel: boolean;\n  cancelDistance: number;\n  accentColor: string;\n  onStart?: VoicePillProps['onStart'];\n  onStop?: VoicePillProps['onStop'];\n}\n\ndeclare global {\n  interface Window {\n    webkitAudioContext?: typeof AudioContext;\n  }\n}\n\nconst LOOP = 4.8;\nconst SYLLABLES = [\n  [0.1, 0.16, 0.9],\n  [0.3, 0.12, 0.7],\n  [0.5, 0.2, 1],\n  [0.95, 0.14, 0.8],\n  [1.15, 0.1, 0.6],\n  [1.3, 0.22, 0.95],\n  [1.9, 0.16, 0.85],\n  [2.12, 0.12, 0.7],\n  [2.3, 0.18, 0.9],\n  [2.55, 0.1, 0.5],\n  [3.05, 0.24, 1],\n  [3.4, 0.12, 0.75],\n  [3.6, 0.16, 0.9]\n];\nconst MIC_BINS = [\n  [1, 4],\n  [4, 11],\n  [11, 33]\n];\nconst MIC_GAIN = 2.2;\nconst DT_MAX = 0.05;\nconst SLIDE_MIN = 4;\nconst WAVE_EVERY = 4;\nconst WAVE_MAX = 80;\n\nconst simulatedLevel = (t: number) => {\n  const u = t % LOOP;\n  let a = 0.06;\n  for (const [s, d, p] of SYLLABLES) {\n    const x = (u - s) / d;\n    if (x >= 0 && x <= 1) a = Math.max(a, p * 0.5 * (1 - Math.cos(2 * Math.PI * x)));\n  }\n  return a * (0.7 + 0.3 * Math.abs(Math.sin(2 * Math.PI * 7.1 * u)));\n};\nconst micLevel = (analyser: AnalyserNode, buf: Uint8Array<ArrayBuffer>) => {\n  analyser.getByteFrequencyData(buf);\n  let total = 0;\n  for (const [lo, hi] of MIC_BINS) {\n    let s = 0;\n    for (let i = lo; i < hi; i += 1) s += buf[i];\n    total += s / ((hi - lo) * 255);\n  }\n  return (total / MIC_BINS.length) * MIC_GAIN;\n};\nconst drawWave = (s: State, canvas: HTMLCanvasElement, level: number, color: string, floor: number) => {\n  const dpr = Math.min(2, window.devicePixelRatio || 1);\n  const rect = canvas.getBoundingClientRect();\n  const W = Math.max(1, Math.round(rect.width * dpr));\n  const H = Math.max(1, Math.round(rect.height * dpr));\n  if (canvas.width !== W || canvas.height !== H) {\n    canvas.width = W;\n    canvas.height = H;\n  }\n  const ctx = canvas.getContext('2d');\n  if (!ctx) return;\n  s.acc = Math.max(s.acc, level);\n  s.tick = (s.tick + 1) % WAVE_EVERY;\n  if (s.tick === 0) {\n    s.hist.push(s.acc);\n    s.acc = 0;\n    if (s.hist.length > WAVE_MAX) s.hist.shift();\n  }\n  const bw = 2 * dpr;\n  const step = 3 * dpr;\n  const shift = (s.tick / WAVE_EVERY) * step;\n  ctx.clearRect(0, 0, W, H);\n  ctx.fillStyle = color;\n  for (let i = 0; i < s.hist.length; i += 1) {\n    const v = s.hist[s.hist.length - 1 - i];\n    const x = W - (i + 1) * step - shift;\n    if (x + bw < 0) break;\n    const h = Math.max(bw, (floor + (1 - floor) * v) * H);\n    const t = Math.min(1, Math.max(0, (x + bw / 2) / (W * 0.55)));\n    const fade = t * t * (3 - 2 * t);\n    ctx.globalAlpha = (0.35 + 0.65 * v) * fade;\n    ctx.beginPath();\n    ctx.roundRect(x, (H - h) / 2, bw, h, bw / 2);\n    ctx.fill();\n  }\n  ctx.globalAlpha = 1;\n};\nconst clock = (ms: number) => {\n  const s = Math.floor(ms / 1000);\n  return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`;\n};\n\nconst openMic = async (s: State) => {\n  const Ctx = window.AudioContext || window.webkitAudioContext;\n  if (!Ctx || !navigator.mediaDevices?.getUserMedia) throw new Error('unsupported');\n  s.audio ??= { ctx: new Ctx() };\n  const a = s.audio;\n  if (a.ctx.state === 'suspended') a.ctx.resume();\n  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n  if (!s.listening) {\n    stream.getTracks().forEach(t => t.stop());\n    return;\n  }\n  a.stream = stream;\n  a.src = a.ctx.createMediaStreamSource(stream);\n  a.analyser = a.ctx.createAnalyser();\n  a.analyser.fftSize = 256;\n  a.analyser.smoothingTimeConstant = 0;\n  a.src.connect(a.analyser);\n  a.buf = new Uint8Array(a.analyser.frequencyBinCount);\n};\nconst closeMic = (s: State) => {\n  const a = s.audio;\n  if (!a?.stream) return;\n  a.stream.getTracks().forEach(t => t.stop());\n  a.src?.disconnect();\n  a.stream = null;\n  a.src = null;\n  a.analyser = null;\n  a.buf = null;\n};\n\nconst VoicePill: React.FC<VoicePillProps> = ({\n  accentColor = '#f5f5f5',\n  iconColor = '#a1a1aa',\n  background = '#27272a',\n  size = 28,\n  shape = 'pill',\n  reach = 8,\n  showTime = true,\n  waveform = true,\n  slideToCancel = true,\n  cancelDistance = 64,\n  attack = 40,\n  release = 240,\n  sensitivity = 1,\n  floor = 0.1,\n  openDuration = 200,\n  pressScale = 0.95,\n  mode = 'auto',\n  holdAfter = 300,\n  reactive = 'simulated',\n  disabled = false,\n  ariaLabel = 'Dictate',\n  onStart,\n  onStop,\n  className = ''\n}) => {\n  const [listening, setListening] = useState(false);\n  const [pressed, setPressed] = useState(false);\n  const [input, setInput] = useState('pointer');\n  const timeRef = useRef<HTMLSpanElement>(null);\n  const rootRef = useRef<HTMLButtonElement>(null);\n  const waveRef = useRef<HTMLCanvasElement>(null);\n  const st = useRef<State>({\n    listening: false,\n    pointerId: null,\n    ownPress: false,\n    downX: 0,\n    sliding: false,\n    hist: [],\n    tick: 0,\n    acc: 0,\n    downAt: 0,\n    startedAt: 0,\n    raf: 0,\n    last: 0,\n    env: 0,\n    t0: 0,\n    audio: null\n  });\n  const cfg = useRef<Cfg>({} as Cfg);\n  cfg.current = {\n    attack,\n    release,\n    sensitivity,\n    floor,\n    mode,\n    holdAfter,\n    reactive,\n    showTime,\n    waveform,\n    slideToCancel,\n    cancelDistance,\n    accentColor,\n    onStart,\n    onStop\n  };\n\n  const frame = (now: number) => {\n    const s = st.current;\n    const c = cfg.current;\n    const dt = Math.min((now - s.last) / 1000, DT_MAX);\n    s.last = now;\n    let target = 0;\n    if (s.listening) {\n      if (s.audio?.analyser) target = micLevel(s.audio.analyser, s.audio.buf as Uint8Array<ArrayBuffer>);\n      else if (c.reactive !== 'mic') target = simulatedLevel((now - s.t0) / 1000);\n    }\n    target = Math.min(1, target * c.sensitivity);\n    const tau = Math.max(1, target > s.env ? c.attack : c.release) / 1000;\n    s.env += (target - s.env) * (1 - Math.exp(-dt / tau));\n    if (s.listening && c.showTime && timeRef.current) {\n      const text = clock(now - s.startedAt);\n      if (timeRef.current.textContent !== text) timeRef.current.textContent = text;\n    }\n    if (s.listening && c.waveform && waveRef.current) drawWave(s, waveRef.current, s.env, c.accentColor, c.floor);\n    s.raf = s.listening ? requestAnimationFrame(frame) : 0;\n  };\n\n  const begin = (kind: 'pointer' | 'key') => {\n    const s = st.current;\n    const c = cfg.current;\n    if (s.listening || disabled) return;\n    s.listening = true;\n    s.hist = [];\n    s.tick = 0;\n    s.acc = 0;\n    s.env = 0;\n    s.startedAt = performance.now();\n    s.t0 = s.startedAt;\n    s.last = s.startedAt;\n    if (timeRef.current) timeRef.current.textContent = '0:00';\n    setListening(true);\n    setInput(kind);\n    if (!s.raf) s.raf = requestAnimationFrame(frame);\n    c.onStart?.({ source: c.reactive });\n    if (c.reactive === 'mic') openMic(s).catch(() => end('mic-denied'));\n  };\n  const end = (reason: VoicePillStopReason) => {\n    const s = st.current;\n    const c = cfg.current;\n    if (!s.listening) return;\n    s.listening = false;\n    closeMic(s);\n    setListening(false);\n    setInput(reason === 'key' || reason === 'escape' ? 'key' : 'pointer');\n    c.onStop?.({ reason, duration: Math.round(performance.now() - s.startedAt) });\n  };\n\n  const settleSlide = () => {\n    const s = st.current;\n    const root = rootRef.current;\n    s.sliding = false;\n    if (!root) return;\n    delete root.dataset.sliding;\n    root.style.setProperty('--vp-slide', '0px');\n    root.style.setProperty('--vp-cancel', '0');\n  };\n  const onPointerMove = (e: PointerEvent<HTMLButtonElement>) => {\n    const s = st.current;\n    const c = cfg.current;\n    const root = rootRef.current;\n    if (!root || s.pointerId !== e.pointerId || !c.slideToCancel || !s.listening || !s.ownPress) return;\n    const dx = e.clientX - s.downX;\n    if (!s.sliding && dx > -SLIDE_MIN) return;\n    s.sliding = true;\n    root.dataset.sliding = '';\n    const pull = Math.min(c.cancelDistance + 24, Math.max(0, -dx));\n    root.style.setProperty('--vp-slide', `${-pull}px`);\n    const progress = Math.min(1, pull / c.cancelDistance);\n    root.style.setProperty('--vp-cancel', progress.toFixed(3));\n    if (progress >= 1) {\n      settleSlide();\n      end('cancel');\n    }\n  };\n  const onPointerDown = (e: PointerEvent<HTMLButtonElement>) => {\n    const s = st.current;\n    if (disabled || e.button !== 0 || !e.isPrimary || s.pointerId !== null) return;\n    s.pointerId = e.pointerId;\n    s.downX = e.clientX;\n    s.downAt = performance.now();\n    try {\n      e.currentTarget.setPointerCapture(e.pointerId);\n    } catch {}\n    setPressed(true);\n    s.ownPress = !s.listening;\n    if (!s.listening) begin('pointer');\n  };\n  const onPointerUp = (e: PointerEvent<HTMLButtonElement>) => {\n    const s = st.current;\n    const c = cfg.current;\n    if (e.pointerId !== s.pointerId) return;\n    s.pointerId = null;\n    setPressed(false);\n    if (s.sliding) settleSlide();\n    try {\n      if (e.currentTarget.hasPointerCapture(e.pointerId)) e.currentTarget.releasePointerCapture(e.pointerId);\n    } catch {}\n    if (!s.listening) return;\n    const held = performance.now() - s.downAt;\n    const isHold = c.mode === 'hold' || (c.mode === 'auto' && held >= c.holdAfter);\n    if (s.ownPress) {\n      if (isHold) end('release');\n    } else {\n      end(held < c.holdAfter ? 'tap' : 'release');\n    }\n  };\n  const onKeyDown = (e: KeyboardEvent<HTMLButtonElement>) => {\n    if (e.key === 'Escape') {\n      end('escape');\n      return;\n    }\n    if ((e.key === ' ' || e.key === 'Enter') && !e.repeat) {\n      e.preventDefault();\n      if (st.current.listening) end('key');\n      else begin('key');\n    }\n  };\n  const onClick = (e: MouseEvent<HTMLButtonElement>) => {\n    if (e.detail === 0 && st.current.pointerId === null && !(e.nativeEvent as globalThis.PointerEvent).pointerType) {\n      if (st.current.listening) end('key');\n      else begin('key');\n    }\n  };\n\n  useEffect(() => {\n    if (!pressed) return undefined;\n    const stop = () => end('blur');\n    const onVis = () => {\n      if (document.hidden) stop();\n    };\n    window.addEventListener('blur', stop);\n    document.addEventListener('visibilitychange', onVis);\n    return () => {\n      window.removeEventListener('blur', stop);\n      document.removeEventListener('visibilitychange', onVis);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [pressed]);\n  useEffect(() => {\n    if (disabled) end('disabled');\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [disabled]);\n  useEffect(() => {\n    const s = st.current;\n    return () => {\n      end('unmount');\n      cancelAnimationFrame(s.raf);\n      s.audio?.ctx.close();\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const radius = shape === 'rounded' ? Math.round(size * 0.29) : size / 2;\n  const hit = Math.max(0, Math.min(10, (44 - size) / 2));\n  const timeSize = Math.max(10, Math.round(size * 0.36));\n  const clockW = showTime ? Math.round(timeSize * 2.5) + 4 : 0;\n  const waveW = waveform ? Math.round(size * 1.9) : 0;\n  const extra = clockW + waveW;\n\n  return (\n    <button\n      type=\"button\"\n      disabled={disabled}\n      aria-label={ariaLabel}\n      aria-pressed={listening}\n      className={`voice-pill${className ? ` ${className}` : ''}`}\n      data-state={listening ? 'listening' : 'idle'}\n      data-pressed={pressed ? '' : undefined}\n      data-input={input}\n      data-time={showTime ? '' : undefined}\n      ref={rootRef}\n      onPointerDown={onPointerDown}\n      onPointerMove={onPointerMove}\n      onPointerUp={onPointerUp}\n      onPointerCancel={onPointerUp}\n      onLostPointerCapture={onPointerUp}\n      onKeyDown={onKeyDown}\n      onClick={onClick}\n      onContextMenu={e => e.preventDefault()}\n      style={\n        {\n          '--vp-accent': accentColor,\n          '--vp-icon': iconColor,\n          '--vp-bg': background,\n          '--vp-size': `${size}px`,\n          '--vp-radius': `${radius}px`,\n          '--vp-reach': `${reach}px`,\n          '--vp-extra': `${extra}px`,\n          '--vp-clock-w': `${clockW}px`,\n          '--vp-wave-w': `${waveW}px`,\n          '--vp-stop': `${Math.round(size * 0.32)}px`,\n          '--vp-icon-size': `${Math.round(size * 0.54)}px`,\n          '--vp-time-size': `${timeSize}px`,\n          '--vp-open': `${openDuration}ms`,\n          '--vp-press': pressScale,\n          '--vp-hit': `${hit}px`\n        } as CSSProperties\n      }\n    >\n      <span className=\"voice-pill__capsule\" aria-hidden=\"true\" />\n      {waveform ? <canvas ref={waveRef} className=\"voice-pill__wave\" aria-hidden=\"true\" /> : null}\n      {slideToCancel ? (\n        <span className=\"voice-pill__cancel\" aria-hidden=\"true\">\n          <HugeiconsIcon icon={ArrowLeft01Icon} size={12} strokeWidth={2.2} />\n          <span>Cancel</span>\n        </span>\n      ) : null}\n      {showTime ? (\n        <span ref={timeRef} className=\"voice-pill__time\" aria-hidden=\"true\">\n          0:00\n        </span>\n      ) : null}\n      <span className=\"voice-pill__glyph\">\n        <span className=\"voice-pill__mic\">\n          <HugeiconsIcon icon={Mic01Icon} size={Math.round(size * 0.54)} strokeWidth={2} />\n        </span>\n        <span className=\"voice-pill__stop\" aria-hidden=\"true\" />\n      </span>\n    </button>\n  );\n};\n\nexport default VoicePill;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"@hugeicons/react@^1.1.10",
		"@hugeicons/core-free-icons@^4.3.3"
	]
}