ai-input-react 1.0.0-beta.1 → 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -102,18 +102,20 @@ function useAudioRecorder(options = {}) {
102
102
  const updateAudioLevels = react.useCallback(() => {
103
103
  if (!analyserRef.current) return;
104
104
  const analyser = analyserRef.current;
105
- const bufferLength = analyser.frequencyBinCount;
105
+ const bufferLength = analyser.fftSize;
106
106
  const dataArray = new Uint8Array(bufferLength);
107
- analyser.getByteFrequencyData(dataArray);
108
- const bars = 12;
107
+ analyser.getByteTimeDomainData(dataArray);
108
+ const bars = 16;
109
109
  const step = Math.floor(bufferLength / bars);
110
110
  const levels = [];
111
111
  for (let i = 0; i < bars; i++) {
112
- let sum = 0;
112
+ let maxDeviation = 0;
113
113
  for (let j = 0; j < step; j++) {
114
- sum += dataArray[i * step + j];
114
+ const value = dataArray[i * step + j];
115
+ const deviation = Math.abs(value - 128);
116
+ maxDeviation = Math.max(maxDeviation, deviation);
115
117
  }
116
- levels.push(sum / step / 255);
118
+ levels.push(Math.min(1, maxDeviation / 128 * 2.5));
117
119
  }
118
120
  setAudioLevels(levels);
119
121
  animationFrameRef.current = requestAnimationFrame(updateAudioLevels);
@@ -409,18 +411,25 @@ function formatDuration(ms) {
409
411
  return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;
410
412
  }
411
413
  function Waveform({ levels, className = "" }) {
412
- const bars = levels.length > 0 ? levels : Array(16).fill(0.1);
414
+ const bars = levels.length > 0 ? levels : Array(16).fill(0.15);
413
415
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center justify-center gap-1 h-10 ${className}`, children: bars.map((level, i) => /* @__PURE__ */ jsxRuntime.jsx(
414
416
  "div",
415
417
  {
416
- className: "w-1.5 bg-amber-500 rounded-full transition-all duration-75",
418
+ className: "w-1.5 bg-gradient-to-t from-amber-600 to-amber-400 rounded-full transition-all duration-100 ease-out",
417
419
  style: {
418
- height: `${Math.max(6, level * 40)}px`
420
+ height: `${Math.max(6, level * 40)}px`,
421
+ opacity: 0.6 + level * 0.4
419
422
  }
420
423
  },
421
424
  i
422
425
  )) });
423
426
  }
427
+ function RecordingPulse() {
428
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative flex h-3 w-3 mr-2", children: [
429
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75" }),
430
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex rounded-full h-3 w-3 bg-red-500" })
431
+ ] });
432
+ }
424
433
  function MicIcon({ className = "" }) {
425
434
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className, viewBox: "0 0 256 256", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M128,176a48.05,48.05,0,0,0,48-48V64a48,48,0,0,0-96,0v64A48.05,48.05,0,0,0,128,176ZM96,64a32,32,0,0,1,64,0v64a32,32,0,0,1-64,0Zm40,143.6V232a8,8,0,0,1-16,0V207.6A80.11,80.11,0,0,1,48,128a8,8,0,0,1,16,0,64,64,0,0,0,128,0,8,8,0,0,1,16,0A80.11,80.11,0,0,1,136,207.6Z" }) });
426
435
  }
@@ -428,7 +437,7 @@ function ArrowUpIcon({ className = "" }) {
428
437
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className, viewBox: "0 0 256 256", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M205.66,117.66a8,8,0,0,1-11.32,0L136,59.31V216a8,8,0,0,1-16,0V59.31L61.66,117.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,205.66,117.66Z" }) });
429
438
  }
430
439
  function StopIcon({ className = "" }) {
431
- return /* @__PURE__ */ jsxRuntime.jsx("svg", { className, viewBox: "0 0 256 256", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M200,40H56A16,16,0,0,0,40,56V200a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,160H56V56H200V200Z" }) });
440
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { className, viewBox: "0 0 256 256", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "64", y: "64", width: "128", height: "128", rx: "8" }) });
432
441
  }
433
442
  function XIcon({ className = "" }) {
434
443
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className, viewBox: "0 0 256 256", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z" }) });
@@ -468,7 +477,6 @@ function DefaultUI({
468
477
  stopRecording,
469
478
  cancelRecording,
470
479
  recordingDuration,
471
- maxRecordingDuration,
472
480
  audioLevels,
473
481
  cooldownRemaining,
474
482
  placeholder = "Ask anything...",
@@ -492,9 +500,9 @@ function DefaultUI({
492
500
  "div",
493
501
  {
494
502
  className: `
495
- bg-zinc-900 border border-zinc-800 rounded-xl
496
- focus-within:ring-1 focus-within:ring-amber-500/50 focus-within:border-amber-500/50
497
- transition-all duration-200
503
+ bg-zinc-900 border rounded-xl
504
+ transition-all duration-300 ease-out
505
+ ${isRecording ? "border-red-500/50 shadow-lg shadow-red-500/10" : "border-zinc-800 focus-within:border-amber-500/50 focus-within:shadow-lg focus-within:shadow-amber-500/5"}
498
506
  ${disabled ? "opacity-50" : ""}
499
507
  `,
500
508
  children: [
@@ -514,26 +522,30 @@ function DefaultUI({
514
522
  disabled:cursor-not-allowed
515
523
  resize-none
516
524
  min-h-[56px]
525
+ transition-colors duration-200
517
526
  `,
518
527
  style: { height: "56px" }
519
528
  }
520
529
  ),
521
530
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-3 pb-3 pt-1", children: [
522
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: isRecording ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
531
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: isRecording ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
523
532
  /* @__PURE__ */ jsxRuntime.jsx(
524
533
  "button",
525
534
  {
526
535
  onClick: cancelRecording,
527
536
  disabled,
528
- className: "p-2 text-zinc-400 hover:text-zinc-200 transition-colors",
537
+ className: "p-2 text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 rounded-lg transition-all duration-200 active:scale-95",
529
538
  "aria-label": "Cancel recording",
530
539
  children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, { className: "h-5 w-5" })
531
540
  }
532
541
  ),
533
- /* @__PURE__ */ jsxRuntime.jsx(Waveform, { levels: audioLevels }),
534
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-zinc-400 font-mono", children: formatDuration(recordingDuration) })
535
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm", children: [
536
- hasError && error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-400", children: error.message }),
542
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
543
+ /* @__PURE__ */ jsxRuntime.jsx(RecordingPulse, {}),
544
+ /* @__PURE__ */ jsxRuntime.jsx(Waveform, { levels: audioLevels })
545
+ ] }),
546
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-zinc-400 font-mono tabular-nums", children: formatDuration(recordingDuration) })
547
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm min-h-[28px] flex items-center", children: [
548
+ hasError && error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-400 animate-pulse", children: error.message }),
537
549
  isRateLimited && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-amber-400", children: [
538
550
  "Wait ",
539
551
  formatDuration(cooldownRemaining)
@@ -546,10 +558,12 @@ function DefaultUI({
546
558
  disabled,
547
559
  className: `
548
560
  p-2.5 rounded-full
549
- bg-red-500 hover:bg-red-600
561
+ bg-red-500 hover:bg-red-400
550
562
  text-white
551
- transition-colors
563
+ transition-all duration-200
564
+ hover:scale-105 active:scale-95
552
565
  disabled:opacity-50 disabled:cursor-not-allowed
566
+ shadow-lg shadow-red-500/25
553
567
  `,
554
568
  "aria-label": "Stop recording",
555
569
  children: /* @__PURE__ */ jsxRuntime.jsx(StopIcon, { className: "h-5 w-5" })
@@ -561,9 +575,12 @@ function DefaultUI({
561
575
  onClick: startRecording,
562
576
  disabled: disabled || isLoading || isRateLimited,
563
577
  className: `
564
- p-2 text-zinc-400 hover:text-zinc-200
565
- transition-colors
566
- disabled:opacity-50 disabled:cursor-not-allowed
578
+ p-2 text-zinc-400
579
+ hover:text-amber-400 hover:bg-zinc-800
580
+ rounded-lg
581
+ transition-all duration-200
582
+ hover:scale-105 active:scale-95
583
+ disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100
567
584
  `,
568
585
  "aria-label": "Start recording",
569
586
  children: /* @__PURE__ */ jsxRuntime.jsx(MicIcon, { className: "h-5 w-5" })
@@ -576,9 +593,9 @@ function DefaultUI({
576
593
  disabled: !canSubmit || disabled,
577
594
  className: `
578
595
  p-2.5 rounded-full
579
- transition-colors
580
- disabled:opacity-50 disabled:cursor-not-allowed
581
- ${canSubmit ? "bg-amber-500 hover:bg-amber-600 text-zinc-900" : "bg-zinc-700 text-zinc-500"}
596
+ transition-all duration-200
597
+ disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100
598
+ ${canSubmit ? "bg-amber-500 hover:bg-amber-400 text-zinc-900 hover:scale-105 active:scale-95 shadow-lg shadow-amber-500/25" : "bg-zinc-800 text-zinc-500"}
582
599
  `,
583
600
  "aria-label": "Send message",
584
601
  children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, { className: "h-5 w-5" }) : /* @__PURE__ */ jsxRuntime.jsx(ArrowUpIcon, { className: "h-5 w-5" })
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/useRateLimiter.ts","../src/hooks/useAudioRecorder.ts","../src/hooks/useAiInput.ts","../src/components/AiInput.tsx"],"names":["useRef","useState","useCallback","useEffect","DEFAULT_OPTIONS","error","jsx","jsxs","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseRateLimiterOptions, UseRateLimiterReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseRateLimiterOptions = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\n/**\r\n * Hook for soft rate limiting at the UI level.\r\n * Provides UX protection by tracking requests and enforcing cooldowns.\r\n * \r\n * Note: This is not a security measure. Actual rate limiting \r\n * should be handled by the AI provider or backend.\r\n * \r\n * @param options - Rate limiting configuration\r\n * @returns Rate limiter state and controls\r\n */\r\nexport function useRateLimiter(\r\n options: Partial<UseRateLimiterOptions> = {}\r\n): UseRateLimiterReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n // Track request timestamps within the window\r\n const requestTimestamps = useRef<number[]>([])\r\n\r\n // Cooldown state\r\n const [cooldownEnd, setCooldownEnd] = useState<number>(0)\r\n const [cooldownRemaining, setCooldownRemaining] = useState<number>(0)\r\n\r\n // Force re-render for requestsRemaining updates\r\n const [, forceUpdate] = useState({})\r\n\r\n // Cleanup old timestamps and calculate remaining requests\r\n const cleanupAndCount = useCallback(() => {\r\n const now = Date.now()\r\n const windowStart = now - config.windowMs\r\n\r\n // Remove timestamps outside the window\r\n requestTimestamps.current = requestTimestamps.current.filter(\r\n (ts) => ts > windowStart\r\n )\r\n\r\n return config.maxRequests - requestTimestamps.current.length\r\n }, [config.windowMs, config.maxRequests])\r\n\r\n // Update cooldown remaining\r\n useEffect(() => {\r\n if (cooldownEnd <= Date.now()) {\r\n setCooldownRemaining(0)\r\n return\r\n }\r\n\r\n const interval = setInterval(() => {\r\n const remaining = Math.max(0, cooldownEnd - Date.now())\r\n setCooldownRemaining(remaining)\r\n\r\n if (remaining === 0) {\r\n clearInterval(interval)\r\n }\r\n }, 100)\r\n\r\n return () => clearInterval(interval)\r\n }, [cooldownEnd])\r\n\r\n // Check if request is allowed\r\n const canRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Check cooldown\r\n if (now < cooldownEnd) {\r\n return false\r\n }\r\n\r\n // Check request count\r\n return cleanupAndCount() > 0\r\n }, [cooldownEnd, cleanupAndCount])\r\n\r\n // Record a request\r\n const recordRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Add timestamp\r\n requestTimestamps.current.push(now)\r\n\r\n // Start cooldown\r\n const newCooldownEnd = now + config.cooldownMs\r\n setCooldownEnd(newCooldownEnd)\r\n setCooldownRemaining(config.cooldownMs)\r\n\r\n // Trigger re-render\r\n forceUpdate({})\r\n }, [config.cooldownMs])\r\n\r\n // Reset rate limiter\r\n const reset = useCallback(() => {\r\n requestTimestamps.current = []\r\n setCooldownEnd(0)\r\n setCooldownRemaining(0)\r\n forceUpdate({})\r\n }, [])\r\n\r\n return {\r\n canRequest: canRequest(),\r\n cooldownRemaining,\r\n requestsRemaining: cleanupAndCount(),\r\n recordRequest,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseAudioRecorderOptions, UseAudioRecorderReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseAudioRecorderOptions = {\r\n maxDurationMs: 60000, // 1 minute\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Get the best supported MIME type for MediaRecorder\r\n */\r\nfunction getSupportedMimeType(preferredTypes: string[]): string | null {\r\n if (typeof MediaRecorder === 'undefined') {\r\n return null\r\n }\r\n\r\n for (const mimeType of preferredTypes) {\r\n if (MediaRecorder.isTypeSupported(mimeType)) {\r\n return mimeType\r\n }\r\n }\r\n\r\n // Fallback to default\r\n return ''\r\n}\r\n\r\n/**\r\n * Hook for audio recording using Web APIs.\r\n * Uses navigator.mediaDevices, MediaRecorder, and Web Audio API for visualization.\r\n * \r\n * @param options - Audio recording configuration\r\n * @returns Audio recorder state and controls\r\n */\r\nexport function useAudioRecorder(\r\n options: Partial<UseAudioRecorderOptions> = {}\r\n): UseAudioRecorderReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n const [isRecording, setIsRecording] = useState(false)\r\n const [duration, setDuration] = useState(0)\r\n const [audioBlob, setAudioBlob] = useState<Blob | null>(null)\r\n const [error, setError] = useState<Error | null>(null)\r\n const [audioLevels, setAudioLevels] = useState<number[]>([])\r\n\r\n const mediaRecorderRef = useRef<MediaRecorder | null>(null)\r\n const streamRef = useRef<MediaStream | null>(null)\r\n const chunksRef = useRef<Blob[]>([])\r\n const startTimeRef = useRef<number>(0)\r\n const durationIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\r\n const maxDurationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\r\n\r\n // Web Audio API refs for visualization\r\n const audioContextRef = useRef<AudioContext | null>(null)\r\n const analyserRef = useRef<AnalyserNode | null>(null)\r\n const animationFrameRef = useRef<number | null>(null)\r\n\r\n // Check if audio recording is supported\r\n const isSupported = typeof navigator !== 'undefined'\r\n && 'mediaDevices' in navigator\r\n && 'getUserMedia' in navigator.mediaDevices\r\n && typeof MediaRecorder !== 'undefined'\r\n\r\n // Update audio levels from analyser\r\n const updateAudioLevels = useCallback(() => {\r\n if (!analyserRef.current) return\r\n\r\n const analyser = analyserRef.current\r\n const bufferLength = analyser.frequencyBinCount\r\n const dataArray = new Uint8Array(bufferLength)\r\n analyser.getByteFrequencyData(dataArray)\r\n\r\n // Sample 12 bars from the frequency data\r\n const bars = 12\r\n const step = Math.floor(bufferLength / bars)\r\n const levels: number[] = []\r\n\r\n for (let i = 0; i < bars; i++) {\r\n // Average a range of frequencies for each bar\r\n let sum = 0\r\n for (let j = 0; j < step; j++) {\r\n sum += dataArray[i * step + j]\r\n }\r\n // Normalize to 0-1 range\r\n levels.push((sum / step) / 255)\r\n }\r\n\r\n setAudioLevels(levels)\r\n\r\n // Continue animation loop\r\n animationFrameRef.current = requestAnimationFrame(updateAudioLevels)\r\n }, [])\r\n\r\n // Cleanup function\r\n const cleanup = useCallback(() => {\r\n if (durationIntervalRef.current) {\r\n clearInterval(durationIntervalRef.current)\r\n durationIntervalRef.current = null\r\n }\r\n\r\n if (maxDurationTimeoutRef.current) {\r\n clearTimeout(maxDurationTimeoutRef.current)\r\n maxDurationTimeoutRef.current = null\r\n }\r\n\r\n if (animationFrameRef.current) {\r\n cancelAnimationFrame(animationFrameRef.current)\r\n animationFrameRef.current = null\r\n }\r\n\r\n if (audioContextRef.current) {\r\n audioContextRef.current.close()\r\n audioContextRef.current = null\r\n }\r\n\r\n if (streamRef.current) {\r\n streamRef.current.getTracks().forEach((track) => track.stop())\r\n streamRef.current = null\r\n }\r\n\r\n analyserRef.current = null\r\n mediaRecorderRef.current = null\r\n chunksRef.current = []\r\n setAudioLevels([])\r\n }, [])\r\n\r\n // Stop recording\r\n const stopRecording = useCallback(() => {\r\n if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {\r\n mediaRecorderRef.current.stop()\r\n }\r\n setIsRecording(false)\r\n }, [])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!isSupported) {\r\n setError(new Error('Audio recording is not supported in this browser'))\r\n return\r\n }\r\n\r\n // Reset state\r\n setError(null)\r\n setAudioBlob(null)\r\n setDuration(0)\r\n setAudioLevels([])\r\n chunksRef.current = []\r\n\r\n try {\r\n // Get microphone access\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\r\n streamRef.current = stream\r\n\r\n // Set up Web Audio API for visualization\r\n const audioContext = new AudioContext()\r\n audioContextRef.current = audioContext\r\n\r\n const source = audioContext.createMediaStreamSource(stream)\r\n const analyser = audioContext.createAnalyser()\r\n analyser.fftSize = 256\r\n analyser.smoothingTimeConstant = 0.8\r\n source.connect(analyser)\r\n analyserRef.current = analyser\r\n\r\n // Get supported MIME type\r\n const mimeType = getSupportedMimeType(config.mimeTypes)\r\n\r\n // Create MediaRecorder\r\n const mediaRecorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined)\r\n mediaRecorderRef.current = mediaRecorder\r\n\r\n // Handle data available\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n chunksRef.current.push(event.data)\r\n }\r\n }\r\n\r\n // Handle recording stop\r\n mediaRecorder.onstop = () => {\r\n const blob = new Blob(chunksRef.current, {\r\n type: mimeType || 'audio/webm'\r\n })\r\n setAudioBlob(blob)\r\n\r\n // Call callback if provided\r\n if (config.onRecordingComplete) {\r\n config.onRecordingComplete(blob)\r\n }\r\n\r\n cleanup()\r\n }\r\n\r\n // Handle errors\r\n mediaRecorder.onerror = () => {\r\n setError(new Error('Recording error occurred'))\r\n setIsRecording(false)\r\n cleanup()\r\n }\r\n\r\n // Start recording\r\n mediaRecorder.start(100) // Collect data every 100ms\r\n startTimeRef.current = Date.now()\r\n setIsRecording(true)\r\n\r\n // Start audio level visualization\r\n updateAudioLevels()\r\n\r\n // Update duration every 100ms\r\n durationIntervalRef.current = setInterval(() => {\r\n setDuration(Date.now() - startTimeRef.current)\r\n }, 100)\r\n\r\n // Auto-stop at max duration\r\n maxDurationTimeoutRef.current = setTimeout(() => {\r\n stopRecording()\r\n }, config.maxDurationMs)\r\n\r\n } catch (err) {\r\n const errorMessage = err instanceof Error\r\n ? err.message\r\n : 'Failed to access microphone'\r\n setError(new Error(errorMessage))\r\n cleanup()\r\n }\r\n }, [isSupported, config.mimeTypes, config.maxDurationMs, config.onRecordingComplete, cleanup, stopRecording, updateAudioLevels])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n }, [cleanup])\r\n\r\n // Reset hook state\r\n const reset = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n setError(null)\r\n setAudioLevels([])\r\n }, [cleanup])\r\n\r\n // Cleanup on unmount\r\n useEffect(() => {\r\n return () => {\r\n cleanup()\r\n }\r\n }, [cleanup])\r\n\r\n return {\r\n isRecording,\r\n isSupported,\r\n duration,\r\n audioBlob,\r\n audioLevels,\r\n error,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from 'react'\r\nimport { useRateLimiter } from './useRateLimiter'\r\nimport { useAudioRecorder } from './useAudioRecorder'\r\nimport type {\r\n UseAiInputOptions,\r\n UseAiInputReturn,\r\n AiInputState,\r\n RateLimitConfig,\r\n AudioConfig,\r\n} from '../types'\r\n\r\nconst DEFAULT_RATE_LIMIT: RateLimitConfig = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\nconst DEFAULT_AUDIO_CONFIG: AudioConfig = {\r\n maxDurationMs: 60000,\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Main hook for AI input functionality.\r\n * Combines rate limiting, audio recording, and API communication.\r\n * Unified design - text and audio in single component.\r\n * \r\n * @param options - Configuration options\r\n * @returns Complete state and controls for AI input\r\n */\r\nexport function useAiInput(options: UseAiInputOptions): UseAiInputReturn {\r\n const {\r\n send,\r\n sendAudio,\r\n rateLimit = {},\r\n audioConfig = {},\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n } = options\r\n\r\n const rateLimitConfig = { ...DEFAULT_RATE_LIMIT, ...rateLimit }\r\n const audioConfigMerged = { ...DEFAULT_AUDIO_CONFIG, ...audioConfig }\r\n\r\n // State\r\n const [state, setState] = useState<AiInputState>('idle')\r\n const [text, setText] = useState('')\r\n const [error, setError] = useState<Error | null>(null)\r\n const [result, setResult] = useState<unknown>(null)\r\n\r\n // Ref to track if we're waiting to submit audio after recording stops\r\n const pendingAudioSubmitRef = useRef(false)\r\n\r\n // Rate limiter\r\n const rateLimiter = useRateLimiter(rateLimitConfig)\r\n\r\n // Audio recorder\r\n const audioRecorder = useAudioRecorder({\r\n ...audioConfigMerged,\r\n })\r\n\r\n // Update state based on rate limiter\r\n useEffect(() => {\r\n if (!rateLimiter.canRequest && state === 'idle') {\r\n setState('rate-limited')\r\n } else if (rateLimiter.canRequest && state === 'rate-limited') {\r\n setState('idle')\r\n }\r\n }, [rateLimiter.canRequest, state])\r\n\r\n // Update state when recording\r\n useEffect(() => {\r\n if (audioRecorder.isRecording && state !== 'recording') {\r\n setState('recording')\r\n }\r\n }, [audioRecorder.isRecording, state])\r\n\r\n // Handle audio recorder errors\r\n useEffect(() => {\r\n if (audioRecorder.error) {\r\n setError(audioRecorder.error)\r\n setState('error')\r\n onError?.(audioRecorder.error)\r\n }\r\n }, [audioRecorder.error, onError])\r\n\r\n // Submit text\r\n const submitText = useCallback(async () => {\r\n if (!text.trim() || !rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n const response = await send(text)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n // Clear text after successful send\r\n setText('')\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [text, rateLimiter, send, onSuccess, onError])\r\n\r\n // Submit audio\r\n const submitAudio = useCallback(async (blob: Blob) => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n // Use sendAudio if provided, otherwise use send\r\n const sendFn = sendAudio || send\r\n const response = await sendFn(blob)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n\r\n // Handle transcription if callback provided\r\n if (onTranscription && response && typeof response === 'object') {\r\n const res = response as Record<string, unknown>\r\n // Try common transcription response formats\r\n const transcriptionText = res.text || res.transcription || res.transcript\r\n if (typeof transcriptionText === 'string') {\r\n setText(transcriptionText)\r\n onTranscription(transcriptionText)\r\n }\r\n }\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [rateLimiter, send, sendAudio, onSuccess, onError, onTranscription])\r\n\r\n // Handle audio blob ready - submit if we were waiting\r\n useEffect(() => {\r\n if (pendingAudioSubmitRef.current && audioRecorder.audioBlob && !audioRecorder.isRecording) {\r\n pendingAudioSubmitRef.current = false\r\n submitAudio(audioRecorder.audioBlob)\r\n }\r\n }, [audioRecorder.audioBlob, audioRecorder.isRecording, submitAudio])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n await audioRecorder.startRecording()\r\n }, [rateLimiter.canRequest, audioRecorder])\r\n\r\n // Stop recording and submit\r\n const stopRecording = useCallback(() => {\r\n // Mark that we want to submit audio when blob is ready\r\n pendingAudioSubmitRef.current = true\r\n audioRecorder.stopRecording()\r\n }, [audioRecorder])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n audioRecorder.cancelRecording()\r\n setState('idle')\r\n }, [audioRecorder])\r\n\r\n // Submit based on current state\r\n const submit = useCallback(() => {\r\n if (audioRecorder.isRecording) {\r\n stopRecording()\r\n } else if (text.trim()) {\r\n submitText()\r\n }\r\n }, [audioRecorder.isRecording, text, stopRecording, submitText])\r\n\r\n // Reset all state\r\n const reset = useCallback(() => {\r\n setState('idle')\r\n setText('')\r\n setError(null)\r\n setResult(null)\r\n rateLimiter.reset()\r\n audioRecorder.reset()\r\n }, [rateLimiter, audioRecorder])\r\n\r\n // Can submit check\r\n const canSubmit =\r\n rateLimiter.canRequest &&\r\n state !== 'loading' &&\r\n (audioRecorder.isRecording || text.trim().length > 0)\r\n\r\n return {\r\n // State\r\n state,\r\n error,\r\n result,\r\n\r\n // Text\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n\r\n // Audio\r\n isRecording: audioRecorder.isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration: audioRecorder.duration,\r\n maxRecordingDuration: audioConfigMerged.maxDurationMs,\r\n audioLevels: audioRecorder.audioLevels,\r\n\r\n // Rate limiting\r\n cooldownRemaining: rateLimiter.cooldownRemaining,\r\n requestsRemaining: rateLimiter.requestsRemaining,\r\n\r\n // Utils\r\n reset,\r\n }\r\n}\r\n","import React from 'react'\r\nimport { useAiInput } from '../hooks/useAiInput'\r\nimport type { AiInputProps, AiInputRenderProps } from '../types'\r\n\r\n/**\r\n * Format milliseconds to MM:SS display\r\n */\r\nfunction formatDuration(ms: number): string {\r\n const seconds = Math.floor(ms / 1000)\r\n const minutes = Math.floor(seconds / 60)\r\n const remainingSeconds = seconds % 60\r\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`\r\n}\r\n\r\n/**\r\n * Waveform visualization component - larger for toolbar\r\n */\r\nfunction Waveform({ levels, className = '' }: { levels: number[]; className?: string }) {\r\n // Generate 16 bars if no levels provided\r\n const bars = levels.length > 0 ? levels : Array(16).fill(0.1)\r\n\r\n return (\r\n <div className={`flex items-center justify-center gap-1 h-10 ${className}`}>\r\n {bars.map((level, i) => (\r\n <div\r\n key={i}\r\n className=\"w-1.5 bg-amber-500 rounded-full transition-all duration-75\"\r\n style={{\r\n height: `${Math.max(6, level * 40)}px`,\r\n }}\r\n />\r\n ))}\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Microphone icon (Phosphor style)\r\n */\r\nfunction MicIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M128,176a48.05,48.05,0,0,0,48-48V64a48,48,0,0,0-96,0v64A48.05,48.05,0,0,0,128,176ZM96,64a32,32,0,0,1,64,0v64a32,32,0,0,1-64,0Zm40,143.6V232a8,8,0,0,1-16,0V207.6A80.11,80.11,0,0,1,48,128a8,8,0,0,1,16,0,64,64,0,0,0,128,0,8,8,0,0,1,16,0A80.11,80.11,0,0,1,136,207.6Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Arrow up icon for submit\r\n */\r\nfunction ArrowUpIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,117.66a8,8,0,0,1-11.32,0L136,59.31V216a8,8,0,0,1-16,0V59.31L61.66,117.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,205.66,117.66Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Stop icon\r\n */\r\nfunction StopIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M200,40H56A16,16,0,0,0,40,56V200a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,160H56V56H200V200Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * X icon for cancel\r\n */\r\nfunction XIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Spinner for loading state\r\n */\r\nfunction Spinner({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={`animate-spin ${className}`} viewBox=\"0 0 24 24\" fill=\"none\">\r\n <circle\r\n className=\"opacity-25\"\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"4\"\r\n />\r\n <path\r\n className=\"opacity-75\"\r\n fill=\"currentColor\"\r\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\r\n />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Default UI for the unified AiInput component\r\n */\r\nfunction DefaultUI({\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n state,\r\n error,\r\n isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration,\r\n maxRecordingDuration,\r\n audioLevels,\r\n cooldownRemaining,\r\n placeholder = 'Ask anything...',\r\n disabled = false,\r\n}: AiInputRenderProps & {\r\n placeholder?: string\r\n disabled?: boolean\r\n}) {\r\n const isLoading = state === 'loading'\r\n const isRateLimited = state === 'rate-limited'\r\n const hasError = state === 'error'\r\n\r\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\r\n if (e.key === 'Enter' && !e.shiftKey && canSubmit && !isRecording) {\r\n e.preventDefault()\r\n submit()\r\n }\r\n }\r\n\r\n // Auto-resize textarea\r\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\r\n setText(e.target.value)\r\n // Reset height to auto to get the correct scrollHeight\r\n e.target.style.height = 'auto'\r\n // Set height to scrollHeight, with min and max constraints\r\n e.target.style.height = `${Math.min(Math.max(e.target.scrollHeight, 56), 200)}px`\r\n }\r\n\r\n return (\r\n <div className=\"w-full\">\r\n {/* Main container */}\r\n <div\r\n className={`\r\n bg-zinc-900 border border-zinc-800 rounded-xl\r\n focus-within:ring-1 focus-within:ring-amber-500/50 focus-within:border-amber-500/50\r\n transition-all duration-200\r\n ${disabled ? 'opacity-50' : ''}\r\n `}\r\n >\r\n {/* Text input area - always visible for live transcription */}\r\n <textarea\r\n value={text}\r\n onChange={handleInput}\r\n onKeyDown={handleKeyDown}\r\n placeholder={isRecording ? 'Listening...' : placeholder}\r\n disabled={disabled || isLoading || isRateLimited}\r\n rows={1}\r\n className={`\r\n w-full px-4 pt-4 pb-2\r\n bg-transparent text-zinc-100 placeholder:text-zinc-500\r\n focus:outline-none\r\n disabled:cursor-not-allowed\r\n resize-none\r\n min-h-[56px]\r\n `}\r\n style={{ height: '56px' }}\r\n />\r\n\r\n {/* Toolbar */}\r\n <div className=\"flex items-center justify-between px-3 pb-3 pt-1\">\r\n {/* Left side - error/status or waveform during recording */}\r\n <div className=\"flex items-center gap-3\">\r\n {isRecording ? (\r\n <>\r\n {/* Cancel recording */}\r\n <button\r\n onClick={cancelRecording}\r\n disabled={disabled}\r\n className=\"p-2 text-zinc-400 hover:text-zinc-200 transition-colors\"\r\n aria-label=\"Cancel recording\"\r\n >\r\n <XIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Waveform visualization */}\r\n <Waveform levels={audioLevels} />\r\n\r\n {/* Timer - just elapsed time */}\r\n <span className=\"text-sm text-zinc-400 font-mono\">\r\n {formatDuration(recordingDuration)}\r\n </span>\r\n </>\r\n ) : (\r\n <div className=\"text-sm\">\r\n {hasError && error && (\r\n <span className=\"text-red-400\">{error.message}</span>\r\n )}\r\n {isRateLimited && (\r\n <span className=\"text-amber-400\">\r\n Wait {formatDuration(cooldownRemaining)}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Right side - action buttons */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n {/* Stop and send */}\r\n <button\r\n onClick={stopRecording}\r\n disabled={disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n bg-red-500 hover:bg-red-600\r\n text-white\r\n transition-colors\r\n disabled:opacity-50 disabled:cursor-not-allowed\r\n `}\r\n aria-label=\"Stop recording\"\r\n >\r\n <StopIcon className=\"h-5 w-5\" />\r\n </button>\r\n </>\r\n ) : (\r\n <>\r\n {/* Mic button */}\r\n <button\r\n onClick={startRecording}\r\n disabled={disabled || isLoading || isRateLimited}\r\n className={`\r\n p-2 text-zinc-400 hover:text-zinc-200\r\n transition-colors\r\n disabled:opacity-50 disabled:cursor-not-allowed\r\n `}\r\n aria-label=\"Start recording\"\r\n >\r\n <MicIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Submit button */}\r\n <button\r\n onClick={submit}\r\n disabled={!canSubmit || disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n transition-colors\r\n disabled:opacity-50 disabled:cursor-not-allowed\r\n ${canSubmit\r\n ? 'bg-amber-500 hover:bg-amber-600 text-zinc-900'\r\n : 'bg-zinc-700 text-zinc-500'\r\n }\r\n `}\r\n aria-label=\"Send message\"\r\n >\r\n {isLoading ? (\r\n <Spinner className=\"h-5 w-5\" />\r\n ) : (\r\n <ArrowUpIcon className=\"h-5 w-5\" />\r\n )}\r\n </button>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * AiInput Component\r\n * \r\n * A React component for text/audio input with AI API integration.\r\n * Unified design with text input and audio recording in a single component.\r\n * \r\n * @example\r\n * // Basic usage\r\n * <AiInput\r\n * send={async (input) => {\r\n * const response = await fetch('/api/chat', {\r\n * method: 'POST',\r\n * body: JSON.stringify({ message: input }),\r\n * })\r\n * return response.json()\r\n * }}\r\n * onSuccess={(result) => console.log(result)}\r\n * />\r\n * \r\n * @example\r\n * // With separate audio handler and transcription\r\n * <AiInput\r\n * send={sendTextFn}\r\n * sendAudio={sendAudioFn}\r\n * onTranscription={(text) => console.log('Transcribed:', text)}\r\n * />\r\n * \r\n * @example\r\n * // Headless mode with custom UI\r\n * <AiInput send={sendFn}>\r\n * {({ text, setText, submit, state, isRecording, audioLevels }) => (\r\n * <div>\r\n * {isRecording ? (\r\n * <MyWaveform levels={audioLevels} />\r\n * ) : (\r\n * <input value={text} onChange={(e) => setText(e.target.value)} />\r\n * )}\r\n * <button onClick={submit}>Send</button>\r\n * </div>\r\n * )}\r\n * </AiInput>\r\n */\r\nexport function AiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n children,\r\n placeholder,\r\n className,\r\n disabled = false,\r\n}: AiInputProps) {\r\n const inputState = useAiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n })\r\n\r\n // Headless mode - render prop\r\n if (children) {\r\n return <>{children(inputState)}</>\r\n }\r\n\r\n // Default UI\r\n return (\r\n <div className={`w-full ${className || ''}`}>\r\n <DefaultUI\r\n {...inputState}\r\n placeholder={placeholder}\r\n disabled={disabled}\r\n />\r\n </div>\r\n )\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/hooks/useRateLimiter.ts","../src/hooks/useAudioRecorder.ts","../src/hooks/useAiInput.ts","../src/components/AiInput.tsx"],"names":["useRef","useState","useCallback","useEffect","DEFAULT_OPTIONS","error","jsx","jsxs","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseRateLimiterOptions, UseRateLimiterReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseRateLimiterOptions = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\n/**\r\n * Hook for soft rate limiting at the UI level.\r\n * Provides UX protection by tracking requests and enforcing cooldowns.\r\n * \r\n * Note: This is not a security measure. Actual rate limiting \r\n * should be handled by the AI provider or backend.\r\n * \r\n * @param options - Rate limiting configuration\r\n * @returns Rate limiter state and controls\r\n */\r\nexport function useRateLimiter(\r\n options: Partial<UseRateLimiterOptions> = {}\r\n): UseRateLimiterReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n // Track request timestamps within the window\r\n const requestTimestamps = useRef<number[]>([])\r\n\r\n // Cooldown state\r\n const [cooldownEnd, setCooldownEnd] = useState<number>(0)\r\n const [cooldownRemaining, setCooldownRemaining] = useState<number>(0)\r\n\r\n // Force re-render for requestsRemaining updates\r\n const [, forceUpdate] = useState({})\r\n\r\n // Cleanup old timestamps and calculate remaining requests\r\n const cleanupAndCount = useCallback(() => {\r\n const now = Date.now()\r\n const windowStart = now - config.windowMs\r\n\r\n // Remove timestamps outside the window\r\n requestTimestamps.current = requestTimestamps.current.filter(\r\n (ts) => ts > windowStart\r\n )\r\n\r\n return config.maxRequests - requestTimestamps.current.length\r\n }, [config.windowMs, config.maxRequests])\r\n\r\n // Update cooldown remaining\r\n useEffect(() => {\r\n if (cooldownEnd <= Date.now()) {\r\n setCooldownRemaining(0)\r\n return\r\n }\r\n\r\n const interval = setInterval(() => {\r\n const remaining = Math.max(0, cooldownEnd - Date.now())\r\n setCooldownRemaining(remaining)\r\n\r\n if (remaining === 0) {\r\n clearInterval(interval)\r\n }\r\n }, 100)\r\n\r\n return () => clearInterval(interval)\r\n }, [cooldownEnd])\r\n\r\n // Check if request is allowed\r\n const canRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Check cooldown\r\n if (now < cooldownEnd) {\r\n return false\r\n }\r\n\r\n // Check request count\r\n return cleanupAndCount() > 0\r\n }, [cooldownEnd, cleanupAndCount])\r\n\r\n // Record a request\r\n const recordRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Add timestamp\r\n requestTimestamps.current.push(now)\r\n\r\n // Start cooldown\r\n const newCooldownEnd = now + config.cooldownMs\r\n setCooldownEnd(newCooldownEnd)\r\n setCooldownRemaining(config.cooldownMs)\r\n\r\n // Trigger re-render\r\n forceUpdate({})\r\n }, [config.cooldownMs])\r\n\r\n // Reset rate limiter\r\n const reset = useCallback(() => {\r\n requestTimestamps.current = []\r\n setCooldownEnd(0)\r\n setCooldownRemaining(0)\r\n forceUpdate({})\r\n }, [])\r\n\r\n return {\r\n canRequest: canRequest(),\r\n cooldownRemaining,\r\n requestsRemaining: cleanupAndCount(),\r\n recordRequest,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseAudioRecorderOptions, UseAudioRecorderReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseAudioRecorderOptions = {\r\n maxDurationMs: 60000, // 1 minute\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Get the best supported MIME type for MediaRecorder\r\n */\r\nfunction getSupportedMimeType(preferredTypes: string[]): string | null {\r\n if (typeof MediaRecorder === 'undefined') {\r\n return null\r\n }\r\n\r\n for (const mimeType of preferredTypes) {\r\n if (MediaRecorder.isTypeSupported(mimeType)) {\r\n return mimeType\r\n }\r\n }\r\n\r\n // Fallback to default\r\n return ''\r\n}\r\n\r\n/**\r\n * Hook for audio recording using Web APIs.\r\n * Uses navigator.mediaDevices, MediaRecorder, and Web Audio API for visualization.\r\n * \r\n * @param options - Audio recording configuration\r\n * @returns Audio recorder state and controls\r\n */\r\nexport function useAudioRecorder(\r\n options: Partial<UseAudioRecorderOptions> = {}\r\n): UseAudioRecorderReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n const [isRecording, setIsRecording] = useState(false)\r\n const [duration, setDuration] = useState(0)\r\n const [audioBlob, setAudioBlob] = useState<Blob | null>(null)\r\n const [error, setError] = useState<Error | null>(null)\r\n const [audioLevels, setAudioLevels] = useState<number[]>([])\r\n\r\n const mediaRecorderRef = useRef<MediaRecorder | null>(null)\r\n const streamRef = useRef<MediaStream | null>(null)\r\n const chunksRef = useRef<Blob[]>([])\r\n const startTimeRef = useRef<number>(0)\r\n const durationIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\r\n const maxDurationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\r\n\r\n // Web Audio API refs for visualization\r\n const audioContextRef = useRef<AudioContext | null>(null)\r\n const analyserRef = useRef<AnalyserNode | null>(null)\r\n const animationFrameRef = useRef<number | null>(null)\r\n\r\n // Check if audio recording is supported\r\n const isSupported = typeof navigator !== 'undefined'\r\n && 'mediaDevices' in navigator\r\n && 'getUserMedia' in navigator.mediaDevices\r\n && typeof MediaRecorder !== 'undefined'\r\n\r\n // Update audio levels from analyser - uses time domain for better voice visualization\r\n const updateAudioLevels = useCallback(() => {\r\n if (!analyserRef.current) return\r\n\r\n const analyser = analyserRef.current\r\n const bufferLength = analyser.fftSize\r\n const dataArray = new Uint8Array(bufferLength)\r\n\r\n // Use time domain data for even distribution across bars\r\n analyser.getByteTimeDomainData(dataArray)\r\n\r\n // Sample 16 bars from the waveform data\r\n const bars = 16\r\n const step = Math.floor(bufferLength / bars)\r\n const levels: number[] = []\r\n\r\n for (let i = 0; i < bars; i++) {\r\n // Get amplitude variation from center (128) for each segment\r\n let maxDeviation = 0\r\n for (let j = 0; j < step; j++) {\r\n const value = dataArray[i * step + j]\r\n const deviation = Math.abs(value - 128)\r\n maxDeviation = Math.max(maxDeviation, deviation)\r\n }\r\n // Normalize to 0-1 range (max deviation is 128)\r\n // Apply some amplification for better visibility\r\n levels.push(Math.min(1, (maxDeviation / 128) * 2.5))\r\n }\r\n\r\n setAudioLevels(levels)\r\n\r\n // Continue animation loop\r\n animationFrameRef.current = requestAnimationFrame(updateAudioLevels)\r\n }, [])\r\n\r\n // Cleanup function\r\n const cleanup = useCallback(() => {\r\n if (durationIntervalRef.current) {\r\n clearInterval(durationIntervalRef.current)\r\n durationIntervalRef.current = null\r\n }\r\n\r\n if (maxDurationTimeoutRef.current) {\r\n clearTimeout(maxDurationTimeoutRef.current)\r\n maxDurationTimeoutRef.current = null\r\n }\r\n\r\n if (animationFrameRef.current) {\r\n cancelAnimationFrame(animationFrameRef.current)\r\n animationFrameRef.current = null\r\n }\r\n\r\n if (audioContextRef.current) {\r\n audioContextRef.current.close()\r\n audioContextRef.current = null\r\n }\r\n\r\n if (streamRef.current) {\r\n streamRef.current.getTracks().forEach((track) => track.stop())\r\n streamRef.current = null\r\n }\r\n\r\n analyserRef.current = null\r\n mediaRecorderRef.current = null\r\n chunksRef.current = []\r\n setAudioLevels([])\r\n }, [])\r\n\r\n // Stop recording\r\n const stopRecording = useCallback(() => {\r\n if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {\r\n mediaRecorderRef.current.stop()\r\n }\r\n setIsRecording(false)\r\n }, [])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!isSupported) {\r\n setError(new Error('Audio recording is not supported in this browser'))\r\n return\r\n }\r\n\r\n // Reset state\r\n setError(null)\r\n setAudioBlob(null)\r\n setDuration(0)\r\n setAudioLevels([])\r\n chunksRef.current = []\r\n\r\n try {\r\n // Get microphone access\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\r\n streamRef.current = stream\r\n\r\n // Set up Web Audio API for visualization\r\n const audioContext = new AudioContext()\r\n audioContextRef.current = audioContext\r\n\r\n const source = audioContext.createMediaStreamSource(stream)\r\n const analyser = audioContext.createAnalyser()\r\n analyser.fftSize = 256\r\n analyser.smoothingTimeConstant = 0.8\r\n source.connect(analyser)\r\n analyserRef.current = analyser\r\n\r\n // Get supported MIME type\r\n const mimeType = getSupportedMimeType(config.mimeTypes)\r\n\r\n // Create MediaRecorder\r\n const mediaRecorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined)\r\n mediaRecorderRef.current = mediaRecorder\r\n\r\n // Handle data available\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n chunksRef.current.push(event.data)\r\n }\r\n }\r\n\r\n // Handle recording stop\r\n mediaRecorder.onstop = () => {\r\n const blob = new Blob(chunksRef.current, {\r\n type: mimeType || 'audio/webm'\r\n })\r\n setAudioBlob(blob)\r\n\r\n // Call callback if provided\r\n if (config.onRecordingComplete) {\r\n config.onRecordingComplete(blob)\r\n }\r\n\r\n cleanup()\r\n }\r\n\r\n // Handle errors\r\n mediaRecorder.onerror = () => {\r\n setError(new Error('Recording error occurred'))\r\n setIsRecording(false)\r\n cleanup()\r\n }\r\n\r\n // Start recording\r\n mediaRecorder.start(100) // Collect data every 100ms\r\n startTimeRef.current = Date.now()\r\n setIsRecording(true)\r\n\r\n // Start audio level visualization\r\n updateAudioLevels()\r\n\r\n // Update duration every 100ms\r\n durationIntervalRef.current = setInterval(() => {\r\n setDuration(Date.now() - startTimeRef.current)\r\n }, 100)\r\n\r\n // Auto-stop at max duration\r\n maxDurationTimeoutRef.current = setTimeout(() => {\r\n stopRecording()\r\n }, config.maxDurationMs)\r\n\r\n } catch (err) {\r\n const errorMessage = err instanceof Error\r\n ? err.message\r\n : 'Failed to access microphone'\r\n setError(new Error(errorMessage))\r\n cleanup()\r\n }\r\n }, [isSupported, config.mimeTypes, config.maxDurationMs, config.onRecordingComplete, cleanup, stopRecording, updateAudioLevels])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n }, [cleanup])\r\n\r\n // Reset hook state\r\n const reset = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n setError(null)\r\n setAudioLevels([])\r\n }, [cleanup])\r\n\r\n // Cleanup on unmount\r\n useEffect(() => {\r\n return () => {\r\n cleanup()\r\n }\r\n }, [cleanup])\r\n\r\n return {\r\n isRecording,\r\n isSupported,\r\n duration,\r\n audioBlob,\r\n audioLevels,\r\n error,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from 'react'\r\nimport { useRateLimiter } from './useRateLimiter'\r\nimport { useAudioRecorder } from './useAudioRecorder'\r\nimport type {\r\n UseAiInputOptions,\r\n UseAiInputReturn,\r\n AiInputState,\r\n RateLimitConfig,\r\n AudioConfig,\r\n} from '../types'\r\n\r\nconst DEFAULT_RATE_LIMIT: RateLimitConfig = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\nconst DEFAULT_AUDIO_CONFIG: AudioConfig = {\r\n maxDurationMs: 60000,\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Main hook for AI input functionality.\r\n * Combines rate limiting, audio recording, and API communication.\r\n * Unified design - text and audio in single component.\r\n * \r\n * @param options - Configuration options\r\n * @returns Complete state and controls for AI input\r\n */\r\nexport function useAiInput(options: UseAiInputOptions): UseAiInputReturn {\r\n const {\r\n send,\r\n sendAudio,\r\n rateLimit = {},\r\n audioConfig = {},\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n } = options\r\n\r\n const rateLimitConfig = { ...DEFAULT_RATE_LIMIT, ...rateLimit }\r\n const audioConfigMerged = { ...DEFAULT_AUDIO_CONFIG, ...audioConfig }\r\n\r\n // State\r\n const [state, setState] = useState<AiInputState>('idle')\r\n const [text, setText] = useState('')\r\n const [error, setError] = useState<Error | null>(null)\r\n const [result, setResult] = useState<unknown>(null)\r\n\r\n // Ref to track if we're waiting to submit audio after recording stops\r\n const pendingAudioSubmitRef = useRef(false)\r\n\r\n // Rate limiter\r\n const rateLimiter = useRateLimiter(rateLimitConfig)\r\n\r\n // Audio recorder\r\n const audioRecorder = useAudioRecorder({\r\n ...audioConfigMerged,\r\n })\r\n\r\n // Update state based on rate limiter\r\n useEffect(() => {\r\n if (!rateLimiter.canRequest && state === 'idle') {\r\n setState('rate-limited')\r\n } else if (rateLimiter.canRequest && state === 'rate-limited') {\r\n setState('idle')\r\n }\r\n }, [rateLimiter.canRequest, state])\r\n\r\n // Update state when recording\r\n useEffect(() => {\r\n if (audioRecorder.isRecording && state !== 'recording') {\r\n setState('recording')\r\n }\r\n }, [audioRecorder.isRecording, state])\r\n\r\n // Handle audio recorder errors\r\n useEffect(() => {\r\n if (audioRecorder.error) {\r\n setError(audioRecorder.error)\r\n setState('error')\r\n onError?.(audioRecorder.error)\r\n }\r\n }, [audioRecorder.error, onError])\r\n\r\n // Submit text\r\n const submitText = useCallback(async () => {\r\n if (!text.trim() || !rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n const response = await send(text)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n // Clear text after successful send\r\n setText('')\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [text, rateLimiter, send, onSuccess, onError])\r\n\r\n // Submit audio\r\n const submitAudio = useCallback(async (blob: Blob) => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n // Use sendAudio if provided, otherwise use send\r\n const sendFn = sendAudio || send\r\n const response = await sendFn(blob)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n\r\n // Handle transcription if callback provided\r\n if (onTranscription && response && typeof response === 'object') {\r\n const res = response as Record<string, unknown>\r\n // Try common transcription response formats\r\n const transcriptionText = res.text || res.transcription || res.transcript\r\n if (typeof transcriptionText === 'string') {\r\n setText(transcriptionText)\r\n onTranscription(transcriptionText)\r\n }\r\n }\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [rateLimiter, send, sendAudio, onSuccess, onError, onTranscription])\r\n\r\n // Handle audio blob ready - submit if we were waiting\r\n useEffect(() => {\r\n if (pendingAudioSubmitRef.current && audioRecorder.audioBlob && !audioRecorder.isRecording) {\r\n pendingAudioSubmitRef.current = false\r\n submitAudio(audioRecorder.audioBlob)\r\n }\r\n }, [audioRecorder.audioBlob, audioRecorder.isRecording, submitAudio])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n await audioRecorder.startRecording()\r\n }, [rateLimiter.canRequest, audioRecorder])\r\n\r\n // Stop recording and submit\r\n const stopRecording = useCallback(() => {\r\n // Mark that we want to submit audio when blob is ready\r\n pendingAudioSubmitRef.current = true\r\n audioRecorder.stopRecording()\r\n }, [audioRecorder])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n audioRecorder.cancelRecording()\r\n setState('idle')\r\n }, [audioRecorder])\r\n\r\n // Submit based on current state\r\n const submit = useCallback(() => {\r\n if (audioRecorder.isRecording) {\r\n stopRecording()\r\n } else if (text.trim()) {\r\n submitText()\r\n }\r\n }, [audioRecorder.isRecording, text, stopRecording, submitText])\r\n\r\n // Reset all state\r\n const reset = useCallback(() => {\r\n setState('idle')\r\n setText('')\r\n setError(null)\r\n setResult(null)\r\n rateLimiter.reset()\r\n audioRecorder.reset()\r\n }, [rateLimiter, audioRecorder])\r\n\r\n // Can submit check\r\n const canSubmit =\r\n rateLimiter.canRequest &&\r\n state !== 'loading' &&\r\n (audioRecorder.isRecording || text.trim().length > 0)\r\n\r\n return {\r\n // State\r\n state,\r\n error,\r\n result,\r\n\r\n // Text\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n\r\n // Audio\r\n isRecording: audioRecorder.isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration: audioRecorder.duration,\r\n maxRecordingDuration: audioConfigMerged.maxDurationMs,\r\n audioLevels: audioRecorder.audioLevels,\r\n\r\n // Rate limiting\r\n cooldownRemaining: rateLimiter.cooldownRemaining,\r\n requestsRemaining: rateLimiter.requestsRemaining,\r\n\r\n // Utils\r\n reset,\r\n }\r\n}\r\n","import React from 'react'\r\nimport { useAiInput } from '../hooks/useAiInput'\r\nimport type { AiInputProps, AiInputRenderProps } from '../types'\r\n\r\n/**\r\n * Format milliseconds to MM:SS display\r\n */\r\nfunction formatDuration(ms: number): string {\r\n const seconds = Math.floor(ms / 1000)\r\n const minutes = Math.floor(seconds / 60)\r\n const remainingSeconds = seconds % 60\r\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`\r\n}\r\n\r\n/**\r\n * Waveform visualization component with smooth animations\r\n */\r\nfunction Waveform({ levels, className = '' }: { levels: number[]; className?: string }) {\r\n // Generate 16 bars if no levels provided\r\n const bars = levels.length > 0 ? levels : Array(16).fill(0.15)\r\n\r\n return (\r\n <div className={`flex items-center justify-center gap-1 h-10 ${className}`}>\r\n {bars.map((level, i) => (\r\n <div\r\n key={i}\r\n className=\"w-1.5 bg-gradient-to-t from-amber-600 to-amber-400 rounded-full transition-all duration-100 ease-out\"\r\n style={{\r\n height: `${Math.max(6, level * 40)}px`,\r\n opacity: 0.6 + level * 0.4,\r\n }}\r\n />\r\n ))}\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Recording pulse indicator\r\n */\r\nfunction RecordingPulse() {\r\n return (\r\n <span className=\"relative flex h-3 w-3 mr-2\">\r\n <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75\"></span>\r\n <span className=\"relative inline-flex rounded-full h-3 w-3 bg-red-500\"></span>\r\n </span>\r\n )\r\n}\r\n\r\n/**\r\n * Microphone icon (Phosphor style)\r\n */\r\nfunction MicIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M128,176a48.05,48.05,0,0,0,48-48V64a48,48,0,0,0-96,0v64A48.05,48.05,0,0,0,128,176ZM96,64a32,32,0,0,1,64,0v64a32,32,0,0,1-64,0Zm40,143.6V232a8,8,0,0,1-16,0V207.6A80.11,80.11,0,0,1,48,128a8,8,0,0,1,16,0,64,64,0,0,0,128,0,8,8,0,0,1,16,0A80.11,80.11,0,0,1,136,207.6Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Arrow up icon for submit\r\n */\r\nfunction ArrowUpIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,117.66a8,8,0,0,1-11.32,0L136,59.31V216a8,8,0,0,1-16,0V59.31L61.66,117.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,205.66,117.66Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Stop icon (filled square)\r\n */\r\nfunction StopIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <rect x=\"64\" y=\"64\" width=\"128\" height=\"128\" rx=\"8\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * X icon for cancel\r\n */\r\nfunction XIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Spinner for loading state\r\n */\r\nfunction Spinner({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={`animate-spin ${className}`} viewBox=\"0 0 24 24\" fill=\"none\">\r\n <circle\r\n className=\"opacity-25\"\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"4\"\r\n />\r\n <path\r\n className=\"opacity-75\"\r\n fill=\"currentColor\"\r\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\r\n />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Default UI for the unified AiInput component\r\n */\r\nfunction DefaultUI({\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n state,\r\n error,\r\n isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration,\r\n audioLevels,\r\n cooldownRemaining,\r\n placeholder = 'Ask anything...',\r\n disabled = false,\r\n}: AiInputRenderProps & {\r\n placeholder?: string\r\n disabled?: boolean\r\n}) {\r\n const isLoading = state === 'loading'\r\n const isRateLimited = state === 'rate-limited'\r\n const hasError = state === 'error'\r\n\r\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\r\n if (e.key === 'Enter' && !e.shiftKey && canSubmit && !isRecording) {\r\n e.preventDefault()\r\n submit()\r\n }\r\n }\r\n\r\n // Auto-resize textarea\r\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\r\n setText(e.target.value)\r\n // Reset height to auto to get the correct scrollHeight\r\n e.target.style.height = 'auto'\r\n // Set height to scrollHeight, with min and max constraints\r\n e.target.style.height = `${Math.min(Math.max(e.target.scrollHeight, 56), 200)}px`\r\n }\r\n\r\n return (\r\n <div className=\"w-full\">\r\n {/* Main container */}\r\n <div\r\n className={`\r\n bg-zinc-900 border rounded-xl\r\n transition-all duration-300 ease-out\r\n ${isRecording\r\n ? 'border-red-500/50 shadow-lg shadow-red-500/10'\r\n : 'border-zinc-800 focus-within:border-amber-500/50 focus-within:shadow-lg focus-within:shadow-amber-500/5'\r\n }\r\n ${disabled ? 'opacity-50' : ''}\r\n `}\r\n >\r\n {/* Text input area - always visible for live transcription */}\r\n <textarea\r\n value={text}\r\n onChange={handleInput}\r\n onKeyDown={handleKeyDown}\r\n placeholder={isRecording ? 'Listening...' : placeholder}\r\n disabled={disabled || isLoading || isRateLimited}\r\n rows={1}\r\n className={`\r\n w-full px-4 pt-4 pb-2\r\n bg-transparent text-zinc-100 placeholder:text-zinc-500\r\n focus:outline-none\r\n disabled:cursor-not-allowed\r\n resize-none\r\n min-h-[56px]\r\n transition-colors duration-200\r\n `}\r\n style={{ height: '56px' }}\r\n />\r\n\r\n {/* Toolbar */}\r\n <div className=\"flex items-center justify-between px-3 pb-3 pt-1\">\r\n {/* Left side - error/status or waveform during recording */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n {/* Cancel recording */}\r\n <button\r\n onClick={cancelRecording}\r\n disabled={disabled}\r\n className=\"p-2 text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 rounded-lg transition-all duration-200 active:scale-95\"\r\n aria-label=\"Cancel recording\"\r\n >\r\n <XIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Recording indicator + Waveform */}\r\n <div className=\"flex items-center\">\r\n <RecordingPulse />\r\n <Waveform levels={audioLevels} />\r\n </div>\r\n\r\n {/* Timer */}\r\n <span className=\"text-sm text-zinc-400 font-mono tabular-nums\">\r\n {formatDuration(recordingDuration)}\r\n </span>\r\n </>\r\n ) : (\r\n <div className=\"text-sm min-h-[28px] flex items-center\">\r\n {hasError && error && (\r\n <span className=\"text-red-400 animate-pulse\">{error.message}</span>\r\n )}\r\n {isRateLimited && (\r\n <span className=\"text-amber-400\">\r\n Wait {formatDuration(cooldownRemaining)}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Right side - action buttons */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n {/* Stop and send */}\r\n <button\r\n onClick={stopRecording}\r\n disabled={disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n bg-red-500 hover:bg-red-400\r\n text-white\r\n transition-all duration-200\r\n hover:scale-105 active:scale-95\r\n disabled:opacity-50 disabled:cursor-not-allowed\r\n shadow-lg shadow-red-500/25\r\n `}\r\n aria-label=\"Stop recording\"\r\n >\r\n <StopIcon className=\"h-5 w-5\" />\r\n </button>\r\n </>\r\n ) : (\r\n <>\r\n {/* Mic button */}\r\n <button\r\n onClick={startRecording}\r\n disabled={disabled || isLoading || isRateLimited}\r\n className={`\r\n p-2 text-zinc-400 \r\n hover:text-amber-400 hover:bg-zinc-800\r\n rounded-lg\r\n transition-all duration-200\r\n hover:scale-105 active:scale-95\r\n disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\r\n `}\r\n aria-label=\"Start recording\"\r\n >\r\n <MicIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Submit button */}\r\n <button\r\n onClick={submit}\r\n disabled={!canSubmit || disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n transition-all duration-200\r\n disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\r\n ${canSubmit\r\n ? 'bg-amber-500 hover:bg-amber-400 text-zinc-900 hover:scale-105 active:scale-95 shadow-lg shadow-amber-500/25'\r\n : 'bg-zinc-800 text-zinc-500'\r\n }\r\n `}\r\n aria-label=\"Send message\"\r\n >\r\n {isLoading ? (\r\n <Spinner className=\"h-5 w-5\" />\r\n ) : (\r\n <ArrowUpIcon className=\"h-5 w-5\" />\r\n )}\r\n </button>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * AiInput Component\r\n * \r\n * A React component for text/audio input with AI API integration.\r\n * Unified design with text input and audio recording in a single component.\r\n * \r\n * @example\r\n * // Basic usage\r\n * <AiInput\r\n * send={async (input) => {\r\n * const response = await fetch('/api/chat', {\r\n * method: 'POST',\r\n * body: JSON.stringify({ message: input }),\r\n * })\r\n * return response.json()\r\n * }}\r\n * onSuccess={(result) => console.log(result)}\r\n * />\r\n * \r\n * @example\r\n * // With separate audio handler and transcription\r\n * <AiInput\r\n * send={sendTextFn}\r\n * sendAudio={sendAudioFn}\r\n * onTranscription={(text) => console.log('Transcribed:', text)}\r\n * />\r\n * \r\n * @example\r\n * // Headless mode with custom UI\r\n * <AiInput send={sendFn}>\r\n * {({ text, setText, submit, state, isRecording, audioLevels }) => (\r\n * <div>\r\n * {isRecording ? (\r\n * <MyWaveform levels={audioLevels} />\r\n * ) : (\r\n * <input value={text} onChange={(e) => setText(e.target.value)} />\r\n * )}\r\n * <button onClick={submit}>Send</button>\r\n * </div>\r\n * )}\r\n * </AiInput>\r\n */\r\nexport function AiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n children,\r\n placeholder,\r\n className,\r\n disabled = false,\r\n}: AiInputProps) {\r\n const inputState = useAiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n })\r\n\r\n // Headless mode - render prop\r\n if (children) {\r\n return <>{children(inputState)}</>\r\n }\r\n\r\n // Default UI\r\n return (\r\n <div className={`w-full ${className || ''}`}>\r\n <DefaultUI\r\n {...inputState}\r\n placeholder={placeholder}\r\n disabled={disabled}\r\n />\r\n </div>\r\n )\r\n}\r\n"]}
package/dist/index.mjs CHANGED
@@ -100,18 +100,20 @@ function useAudioRecorder(options = {}) {
100
100
  const updateAudioLevels = useCallback(() => {
101
101
  if (!analyserRef.current) return;
102
102
  const analyser = analyserRef.current;
103
- const bufferLength = analyser.frequencyBinCount;
103
+ const bufferLength = analyser.fftSize;
104
104
  const dataArray = new Uint8Array(bufferLength);
105
- analyser.getByteFrequencyData(dataArray);
106
- const bars = 12;
105
+ analyser.getByteTimeDomainData(dataArray);
106
+ const bars = 16;
107
107
  const step = Math.floor(bufferLength / bars);
108
108
  const levels = [];
109
109
  for (let i = 0; i < bars; i++) {
110
- let sum = 0;
110
+ let maxDeviation = 0;
111
111
  for (let j = 0; j < step; j++) {
112
- sum += dataArray[i * step + j];
112
+ const value = dataArray[i * step + j];
113
+ const deviation = Math.abs(value - 128);
114
+ maxDeviation = Math.max(maxDeviation, deviation);
113
115
  }
114
- levels.push(sum / step / 255);
116
+ levels.push(Math.min(1, maxDeviation / 128 * 2.5));
115
117
  }
116
118
  setAudioLevels(levels);
117
119
  animationFrameRef.current = requestAnimationFrame(updateAudioLevels);
@@ -407,18 +409,25 @@ function formatDuration(ms) {
407
409
  return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;
408
410
  }
409
411
  function Waveform({ levels, className = "" }) {
410
- const bars = levels.length > 0 ? levels : Array(16).fill(0.1);
412
+ const bars = levels.length > 0 ? levels : Array(16).fill(0.15);
411
413
  return /* @__PURE__ */ jsx("div", { className: `flex items-center justify-center gap-1 h-10 ${className}`, children: bars.map((level, i) => /* @__PURE__ */ jsx(
412
414
  "div",
413
415
  {
414
- className: "w-1.5 bg-amber-500 rounded-full transition-all duration-75",
416
+ className: "w-1.5 bg-gradient-to-t from-amber-600 to-amber-400 rounded-full transition-all duration-100 ease-out",
415
417
  style: {
416
- height: `${Math.max(6, level * 40)}px`
418
+ height: `${Math.max(6, level * 40)}px`,
419
+ opacity: 0.6 + level * 0.4
417
420
  }
418
421
  },
419
422
  i
420
423
  )) });
421
424
  }
425
+ function RecordingPulse() {
426
+ return /* @__PURE__ */ jsxs("span", { className: "relative flex h-3 w-3 mr-2", children: [
427
+ /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75" }),
428
+ /* @__PURE__ */ jsx("span", { className: "relative inline-flex rounded-full h-3 w-3 bg-red-500" })
429
+ ] });
430
+ }
422
431
  function MicIcon({ className = "" }) {
423
432
  return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 256 256", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M128,176a48.05,48.05,0,0,0,48-48V64a48,48,0,0,0-96,0v64A48.05,48.05,0,0,0,128,176ZM96,64a32,32,0,0,1,64,0v64a32,32,0,0,1-64,0Zm40,143.6V232a8,8,0,0,1-16,0V207.6A80.11,80.11,0,0,1,48,128a8,8,0,0,1,16,0,64,64,0,0,0,128,0,8,8,0,0,1,16,0A80.11,80.11,0,0,1,136,207.6Z" }) });
424
433
  }
@@ -426,7 +435,7 @@ function ArrowUpIcon({ className = "" }) {
426
435
  return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 256 256", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M205.66,117.66a8,8,0,0,1-11.32,0L136,59.31V216a8,8,0,0,1-16,0V59.31L61.66,117.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,205.66,117.66Z" }) });
427
436
  }
428
437
  function StopIcon({ className = "" }) {
429
- return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 256 256", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M200,40H56A16,16,0,0,0,40,56V200a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,160H56V56H200V200Z" }) });
438
+ return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 256 256", fill: "currentColor", children: /* @__PURE__ */ jsx("rect", { x: "64", y: "64", width: "128", height: "128", rx: "8" }) });
430
439
  }
431
440
  function XIcon({ className = "" }) {
432
441
  return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 256 256", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z" }) });
@@ -466,7 +475,6 @@ function DefaultUI({
466
475
  stopRecording,
467
476
  cancelRecording,
468
477
  recordingDuration,
469
- maxRecordingDuration,
470
478
  audioLevels,
471
479
  cooldownRemaining,
472
480
  placeholder = "Ask anything...",
@@ -490,9 +498,9 @@ function DefaultUI({
490
498
  "div",
491
499
  {
492
500
  className: `
493
- bg-zinc-900 border border-zinc-800 rounded-xl
494
- focus-within:ring-1 focus-within:ring-amber-500/50 focus-within:border-amber-500/50
495
- transition-all duration-200
501
+ bg-zinc-900 border rounded-xl
502
+ transition-all duration-300 ease-out
503
+ ${isRecording ? "border-red-500/50 shadow-lg shadow-red-500/10" : "border-zinc-800 focus-within:border-amber-500/50 focus-within:shadow-lg focus-within:shadow-amber-500/5"}
496
504
  ${disabled ? "opacity-50" : ""}
497
505
  `,
498
506
  children: [
@@ -512,26 +520,30 @@ function DefaultUI({
512
520
  disabled:cursor-not-allowed
513
521
  resize-none
514
522
  min-h-[56px]
523
+ transition-colors duration-200
515
524
  `,
516
525
  style: { height: "56px" }
517
526
  }
518
527
  ),
519
528
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 pb-3 pt-1", children: [
520
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: isRecording ? /* @__PURE__ */ jsxs(Fragment, { children: [
529
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: isRecording ? /* @__PURE__ */ jsxs(Fragment, { children: [
521
530
  /* @__PURE__ */ jsx(
522
531
  "button",
523
532
  {
524
533
  onClick: cancelRecording,
525
534
  disabled,
526
- className: "p-2 text-zinc-400 hover:text-zinc-200 transition-colors",
535
+ className: "p-2 text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 rounded-lg transition-all duration-200 active:scale-95",
527
536
  "aria-label": "Cancel recording",
528
537
  children: /* @__PURE__ */ jsx(XIcon, { className: "h-5 w-5" })
529
538
  }
530
539
  ),
531
- /* @__PURE__ */ jsx(Waveform, { levels: audioLevels }),
532
- /* @__PURE__ */ jsx("span", { className: "text-sm text-zinc-400 font-mono", children: formatDuration(recordingDuration) })
533
- ] }) : /* @__PURE__ */ jsxs("div", { className: "text-sm", children: [
534
- hasError && error && /* @__PURE__ */ jsx("span", { className: "text-red-400", children: error.message }),
540
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
541
+ /* @__PURE__ */ jsx(RecordingPulse, {}),
542
+ /* @__PURE__ */ jsx(Waveform, { levels: audioLevels })
543
+ ] }),
544
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-zinc-400 font-mono tabular-nums", children: formatDuration(recordingDuration) })
545
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "text-sm min-h-[28px] flex items-center", children: [
546
+ hasError && error && /* @__PURE__ */ jsx("span", { className: "text-red-400 animate-pulse", children: error.message }),
535
547
  isRateLimited && /* @__PURE__ */ jsxs("span", { className: "text-amber-400", children: [
536
548
  "Wait ",
537
549
  formatDuration(cooldownRemaining)
@@ -544,10 +556,12 @@ function DefaultUI({
544
556
  disabled,
545
557
  className: `
546
558
  p-2.5 rounded-full
547
- bg-red-500 hover:bg-red-600
559
+ bg-red-500 hover:bg-red-400
548
560
  text-white
549
- transition-colors
561
+ transition-all duration-200
562
+ hover:scale-105 active:scale-95
550
563
  disabled:opacity-50 disabled:cursor-not-allowed
564
+ shadow-lg shadow-red-500/25
551
565
  `,
552
566
  "aria-label": "Stop recording",
553
567
  children: /* @__PURE__ */ jsx(StopIcon, { className: "h-5 w-5" })
@@ -559,9 +573,12 @@ function DefaultUI({
559
573
  onClick: startRecording,
560
574
  disabled: disabled || isLoading || isRateLimited,
561
575
  className: `
562
- p-2 text-zinc-400 hover:text-zinc-200
563
- transition-colors
564
- disabled:opacity-50 disabled:cursor-not-allowed
576
+ p-2 text-zinc-400
577
+ hover:text-amber-400 hover:bg-zinc-800
578
+ rounded-lg
579
+ transition-all duration-200
580
+ hover:scale-105 active:scale-95
581
+ disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100
565
582
  `,
566
583
  "aria-label": "Start recording",
567
584
  children: /* @__PURE__ */ jsx(MicIcon, { className: "h-5 w-5" })
@@ -574,9 +591,9 @@ function DefaultUI({
574
591
  disabled: !canSubmit || disabled,
575
592
  className: `
576
593
  p-2.5 rounded-full
577
- transition-colors
578
- disabled:opacity-50 disabled:cursor-not-allowed
579
- ${canSubmit ? "bg-amber-500 hover:bg-amber-600 text-zinc-900" : "bg-zinc-700 text-zinc-500"}
594
+ transition-all duration-200
595
+ disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100
596
+ ${canSubmit ? "bg-amber-500 hover:bg-amber-400 text-zinc-900 hover:scale-105 active:scale-95 shadow-lg shadow-amber-500/25" : "bg-zinc-800 text-zinc-500"}
580
597
  `,
581
598
  "aria-label": "Send message",
582
599
  children: isLoading ? /* @__PURE__ */ jsx(Spinner, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx(ArrowUpIcon, { className: "h-5 w-5" })
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/useRateLimiter.ts","../src/hooks/useAudioRecorder.ts","../src/hooks/useAiInput.ts","../src/components/AiInput.tsx"],"names":["DEFAULT_OPTIONS","useState","useRef","useCallback","useEffect","error"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseRateLimiterOptions, UseRateLimiterReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseRateLimiterOptions = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\n/**\r\n * Hook for soft rate limiting at the UI level.\r\n * Provides UX protection by tracking requests and enforcing cooldowns.\r\n * \r\n * Note: This is not a security measure. Actual rate limiting \r\n * should be handled by the AI provider or backend.\r\n * \r\n * @param options - Rate limiting configuration\r\n * @returns Rate limiter state and controls\r\n */\r\nexport function useRateLimiter(\r\n options: Partial<UseRateLimiterOptions> = {}\r\n): UseRateLimiterReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n // Track request timestamps within the window\r\n const requestTimestamps = useRef<number[]>([])\r\n\r\n // Cooldown state\r\n const [cooldownEnd, setCooldownEnd] = useState<number>(0)\r\n const [cooldownRemaining, setCooldownRemaining] = useState<number>(0)\r\n\r\n // Force re-render for requestsRemaining updates\r\n const [, forceUpdate] = useState({})\r\n\r\n // Cleanup old timestamps and calculate remaining requests\r\n const cleanupAndCount = useCallback(() => {\r\n const now = Date.now()\r\n const windowStart = now - config.windowMs\r\n\r\n // Remove timestamps outside the window\r\n requestTimestamps.current = requestTimestamps.current.filter(\r\n (ts) => ts > windowStart\r\n )\r\n\r\n return config.maxRequests - requestTimestamps.current.length\r\n }, [config.windowMs, config.maxRequests])\r\n\r\n // Update cooldown remaining\r\n useEffect(() => {\r\n if (cooldownEnd <= Date.now()) {\r\n setCooldownRemaining(0)\r\n return\r\n }\r\n\r\n const interval = setInterval(() => {\r\n const remaining = Math.max(0, cooldownEnd - Date.now())\r\n setCooldownRemaining(remaining)\r\n\r\n if (remaining === 0) {\r\n clearInterval(interval)\r\n }\r\n }, 100)\r\n\r\n return () => clearInterval(interval)\r\n }, [cooldownEnd])\r\n\r\n // Check if request is allowed\r\n const canRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Check cooldown\r\n if (now < cooldownEnd) {\r\n return false\r\n }\r\n\r\n // Check request count\r\n return cleanupAndCount() > 0\r\n }, [cooldownEnd, cleanupAndCount])\r\n\r\n // Record a request\r\n const recordRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Add timestamp\r\n requestTimestamps.current.push(now)\r\n\r\n // Start cooldown\r\n const newCooldownEnd = now + config.cooldownMs\r\n setCooldownEnd(newCooldownEnd)\r\n setCooldownRemaining(config.cooldownMs)\r\n\r\n // Trigger re-render\r\n forceUpdate({})\r\n }, [config.cooldownMs])\r\n\r\n // Reset rate limiter\r\n const reset = useCallback(() => {\r\n requestTimestamps.current = []\r\n setCooldownEnd(0)\r\n setCooldownRemaining(0)\r\n forceUpdate({})\r\n }, [])\r\n\r\n return {\r\n canRequest: canRequest(),\r\n cooldownRemaining,\r\n requestsRemaining: cleanupAndCount(),\r\n recordRequest,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseAudioRecorderOptions, UseAudioRecorderReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseAudioRecorderOptions = {\r\n maxDurationMs: 60000, // 1 minute\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Get the best supported MIME type for MediaRecorder\r\n */\r\nfunction getSupportedMimeType(preferredTypes: string[]): string | null {\r\n if (typeof MediaRecorder === 'undefined') {\r\n return null\r\n }\r\n\r\n for (const mimeType of preferredTypes) {\r\n if (MediaRecorder.isTypeSupported(mimeType)) {\r\n return mimeType\r\n }\r\n }\r\n\r\n // Fallback to default\r\n return ''\r\n}\r\n\r\n/**\r\n * Hook for audio recording using Web APIs.\r\n * Uses navigator.mediaDevices, MediaRecorder, and Web Audio API for visualization.\r\n * \r\n * @param options - Audio recording configuration\r\n * @returns Audio recorder state and controls\r\n */\r\nexport function useAudioRecorder(\r\n options: Partial<UseAudioRecorderOptions> = {}\r\n): UseAudioRecorderReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n const [isRecording, setIsRecording] = useState(false)\r\n const [duration, setDuration] = useState(0)\r\n const [audioBlob, setAudioBlob] = useState<Blob | null>(null)\r\n const [error, setError] = useState<Error | null>(null)\r\n const [audioLevels, setAudioLevels] = useState<number[]>([])\r\n\r\n const mediaRecorderRef = useRef<MediaRecorder | null>(null)\r\n const streamRef = useRef<MediaStream | null>(null)\r\n const chunksRef = useRef<Blob[]>([])\r\n const startTimeRef = useRef<number>(0)\r\n const durationIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\r\n const maxDurationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\r\n\r\n // Web Audio API refs for visualization\r\n const audioContextRef = useRef<AudioContext | null>(null)\r\n const analyserRef = useRef<AnalyserNode | null>(null)\r\n const animationFrameRef = useRef<number | null>(null)\r\n\r\n // Check if audio recording is supported\r\n const isSupported = typeof navigator !== 'undefined'\r\n && 'mediaDevices' in navigator\r\n && 'getUserMedia' in navigator.mediaDevices\r\n && typeof MediaRecorder !== 'undefined'\r\n\r\n // Update audio levels from analyser\r\n const updateAudioLevels = useCallback(() => {\r\n if (!analyserRef.current) return\r\n\r\n const analyser = analyserRef.current\r\n const bufferLength = analyser.frequencyBinCount\r\n const dataArray = new Uint8Array(bufferLength)\r\n analyser.getByteFrequencyData(dataArray)\r\n\r\n // Sample 12 bars from the frequency data\r\n const bars = 12\r\n const step = Math.floor(bufferLength / bars)\r\n const levels: number[] = []\r\n\r\n for (let i = 0; i < bars; i++) {\r\n // Average a range of frequencies for each bar\r\n let sum = 0\r\n for (let j = 0; j < step; j++) {\r\n sum += dataArray[i * step + j]\r\n }\r\n // Normalize to 0-1 range\r\n levels.push((sum / step) / 255)\r\n }\r\n\r\n setAudioLevels(levels)\r\n\r\n // Continue animation loop\r\n animationFrameRef.current = requestAnimationFrame(updateAudioLevels)\r\n }, [])\r\n\r\n // Cleanup function\r\n const cleanup = useCallback(() => {\r\n if (durationIntervalRef.current) {\r\n clearInterval(durationIntervalRef.current)\r\n durationIntervalRef.current = null\r\n }\r\n\r\n if (maxDurationTimeoutRef.current) {\r\n clearTimeout(maxDurationTimeoutRef.current)\r\n maxDurationTimeoutRef.current = null\r\n }\r\n\r\n if (animationFrameRef.current) {\r\n cancelAnimationFrame(animationFrameRef.current)\r\n animationFrameRef.current = null\r\n }\r\n\r\n if (audioContextRef.current) {\r\n audioContextRef.current.close()\r\n audioContextRef.current = null\r\n }\r\n\r\n if (streamRef.current) {\r\n streamRef.current.getTracks().forEach((track) => track.stop())\r\n streamRef.current = null\r\n }\r\n\r\n analyserRef.current = null\r\n mediaRecorderRef.current = null\r\n chunksRef.current = []\r\n setAudioLevels([])\r\n }, [])\r\n\r\n // Stop recording\r\n const stopRecording = useCallback(() => {\r\n if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {\r\n mediaRecorderRef.current.stop()\r\n }\r\n setIsRecording(false)\r\n }, [])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!isSupported) {\r\n setError(new Error('Audio recording is not supported in this browser'))\r\n return\r\n }\r\n\r\n // Reset state\r\n setError(null)\r\n setAudioBlob(null)\r\n setDuration(0)\r\n setAudioLevels([])\r\n chunksRef.current = []\r\n\r\n try {\r\n // Get microphone access\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\r\n streamRef.current = stream\r\n\r\n // Set up Web Audio API for visualization\r\n const audioContext = new AudioContext()\r\n audioContextRef.current = audioContext\r\n\r\n const source = audioContext.createMediaStreamSource(stream)\r\n const analyser = audioContext.createAnalyser()\r\n analyser.fftSize = 256\r\n analyser.smoothingTimeConstant = 0.8\r\n source.connect(analyser)\r\n analyserRef.current = analyser\r\n\r\n // Get supported MIME type\r\n const mimeType = getSupportedMimeType(config.mimeTypes)\r\n\r\n // Create MediaRecorder\r\n const mediaRecorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined)\r\n mediaRecorderRef.current = mediaRecorder\r\n\r\n // Handle data available\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n chunksRef.current.push(event.data)\r\n }\r\n }\r\n\r\n // Handle recording stop\r\n mediaRecorder.onstop = () => {\r\n const blob = new Blob(chunksRef.current, {\r\n type: mimeType || 'audio/webm'\r\n })\r\n setAudioBlob(blob)\r\n\r\n // Call callback if provided\r\n if (config.onRecordingComplete) {\r\n config.onRecordingComplete(blob)\r\n }\r\n\r\n cleanup()\r\n }\r\n\r\n // Handle errors\r\n mediaRecorder.onerror = () => {\r\n setError(new Error('Recording error occurred'))\r\n setIsRecording(false)\r\n cleanup()\r\n }\r\n\r\n // Start recording\r\n mediaRecorder.start(100) // Collect data every 100ms\r\n startTimeRef.current = Date.now()\r\n setIsRecording(true)\r\n\r\n // Start audio level visualization\r\n updateAudioLevels()\r\n\r\n // Update duration every 100ms\r\n durationIntervalRef.current = setInterval(() => {\r\n setDuration(Date.now() - startTimeRef.current)\r\n }, 100)\r\n\r\n // Auto-stop at max duration\r\n maxDurationTimeoutRef.current = setTimeout(() => {\r\n stopRecording()\r\n }, config.maxDurationMs)\r\n\r\n } catch (err) {\r\n const errorMessage = err instanceof Error\r\n ? err.message\r\n : 'Failed to access microphone'\r\n setError(new Error(errorMessage))\r\n cleanup()\r\n }\r\n }, [isSupported, config.mimeTypes, config.maxDurationMs, config.onRecordingComplete, cleanup, stopRecording, updateAudioLevels])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n }, [cleanup])\r\n\r\n // Reset hook state\r\n const reset = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n setError(null)\r\n setAudioLevels([])\r\n }, [cleanup])\r\n\r\n // Cleanup on unmount\r\n useEffect(() => {\r\n return () => {\r\n cleanup()\r\n }\r\n }, [cleanup])\r\n\r\n return {\r\n isRecording,\r\n isSupported,\r\n duration,\r\n audioBlob,\r\n audioLevels,\r\n error,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from 'react'\r\nimport { useRateLimiter } from './useRateLimiter'\r\nimport { useAudioRecorder } from './useAudioRecorder'\r\nimport type {\r\n UseAiInputOptions,\r\n UseAiInputReturn,\r\n AiInputState,\r\n RateLimitConfig,\r\n AudioConfig,\r\n} from '../types'\r\n\r\nconst DEFAULT_RATE_LIMIT: RateLimitConfig = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\nconst DEFAULT_AUDIO_CONFIG: AudioConfig = {\r\n maxDurationMs: 60000,\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Main hook for AI input functionality.\r\n * Combines rate limiting, audio recording, and API communication.\r\n * Unified design - text and audio in single component.\r\n * \r\n * @param options - Configuration options\r\n * @returns Complete state and controls for AI input\r\n */\r\nexport function useAiInput(options: UseAiInputOptions): UseAiInputReturn {\r\n const {\r\n send,\r\n sendAudio,\r\n rateLimit = {},\r\n audioConfig = {},\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n } = options\r\n\r\n const rateLimitConfig = { ...DEFAULT_RATE_LIMIT, ...rateLimit }\r\n const audioConfigMerged = { ...DEFAULT_AUDIO_CONFIG, ...audioConfig }\r\n\r\n // State\r\n const [state, setState] = useState<AiInputState>('idle')\r\n const [text, setText] = useState('')\r\n const [error, setError] = useState<Error | null>(null)\r\n const [result, setResult] = useState<unknown>(null)\r\n\r\n // Ref to track if we're waiting to submit audio after recording stops\r\n const pendingAudioSubmitRef = useRef(false)\r\n\r\n // Rate limiter\r\n const rateLimiter = useRateLimiter(rateLimitConfig)\r\n\r\n // Audio recorder\r\n const audioRecorder = useAudioRecorder({\r\n ...audioConfigMerged,\r\n })\r\n\r\n // Update state based on rate limiter\r\n useEffect(() => {\r\n if (!rateLimiter.canRequest && state === 'idle') {\r\n setState('rate-limited')\r\n } else if (rateLimiter.canRequest && state === 'rate-limited') {\r\n setState('idle')\r\n }\r\n }, [rateLimiter.canRequest, state])\r\n\r\n // Update state when recording\r\n useEffect(() => {\r\n if (audioRecorder.isRecording && state !== 'recording') {\r\n setState('recording')\r\n }\r\n }, [audioRecorder.isRecording, state])\r\n\r\n // Handle audio recorder errors\r\n useEffect(() => {\r\n if (audioRecorder.error) {\r\n setError(audioRecorder.error)\r\n setState('error')\r\n onError?.(audioRecorder.error)\r\n }\r\n }, [audioRecorder.error, onError])\r\n\r\n // Submit text\r\n const submitText = useCallback(async () => {\r\n if (!text.trim() || !rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n const response = await send(text)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n // Clear text after successful send\r\n setText('')\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [text, rateLimiter, send, onSuccess, onError])\r\n\r\n // Submit audio\r\n const submitAudio = useCallback(async (blob: Blob) => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n // Use sendAudio if provided, otherwise use send\r\n const sendFn = sendAudio || send\r\n const response = await sendFn(blob)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n\r\n // Handle transcription if callback provided\r\n if (onTranscription && response && typeof response === 'object') {\r\n const res = response as Record<string, unknown>\r\n // Try common transcription response formats\r\n const transcriptionText = res.text || res.transcription || res.transcript\r\n if (typeof transcriptionText === 'string') {\r\n setText(transcriptionText)\r\n onTranscription(transcriptionText)\r\n }\r\n }\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [rateLimiter, send, sendAudio, onSuccess, onError, onTranscription])\r\n\r\n // Handle audio blob ready - submit if we were waiting\r\n useEffect(() => {\r\n if (pendingAudioSubmitRef.current && audioRecorder.audioBlob && !audioRecorder.isRecording) {\r\n pendingAudioSubmitRef.current = false\r\n submitAudio(audioRecorder.audioBlob)\r\n }\r\n }, [audioRecorder.audioBlob, audioRecorder.isRecording, submitAudio])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n await audioRecorder.startRecording()\r\n }, [rateLimiter.canRequest, audioRecorder])\r\n\r\n // Stop recording and submit\r\n const stopRecording = useCallback(() => {\r\n // Mark that we want to submit audio when blob is ready\r\n pendingAudioSubmitRef.current = true\r\n audioRecorder.stopRecording()\r\n }, [audioRecorder])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n audioRecorder.cancelRecording()\r\n setState('idle')\r\n }, [audioRecorder])\r\n\r\n // Submit based on current state\r\n const submit = useCallback(() => {\r\n if (audioRecorder.isRecording) {\r\n stopRecording()\r\n } else if (text.trim()) {\r\n submitText()\r\n }\r\n }, [audioRecorder.isRecording, text, stopRecording, submitText])\r\n\r\n // Reset all state\r\n const reset = useCallback(() => {\r\n setState('idle')\r\n setText('')\r\n setError(null)\r\n setResult(null)\r\n rateLimiter.reset()\r\n audioRecorder.reset()\r\n }, [rateLimiter, audioRecorder])\r\n\r\n // Can submit check\r\n const canSubmit =\r\n rateLimiter.canRequest &&\r\n state !== 'loading' &&\r\n (audioRecorder.isRecording || text.trim().length > 0)\r\n\r\n return {\r\n // State\r\n state,\r\n error,\r\n result,\r\n\r\n // Text\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n\r\n // Audio\r\n isRecording: audioRecorder.isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration: audioRecorder.duration,\r\n maxRecordingDuration: audioConfigMerged.maxDurationMs,\r\n audioLevels: audioRecorder.audioLevels,\r\n\r\n // Rate limiting\r\n cooldownRemaining: rateLimiter.cooldownRemaining,\r\n requestsRemaining: rateLimiter.requestsRemaining,\r\n\r\n // Utils\r\n reset,\r\n }\r\n}\r\n","import React from 'react'\r\nimport { useAiInput } from '../hooks/useAiInput'\r\nimport type { AiInputProps, AiInputRenderProps } from '../types'\r\n\r\n/**\r\n * Format milliseconds to MM:SS display\r\n */\r\nfunction formatDuration(ms: number): string {\r\n const seconds = Math.floor(ms / 1000)\r\n const minutes = Math.floor(seconds / 60)\r\n const remainingSeconds = seconds % 60\r\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`\r\n}\r\n\r\n/**\r\n * Waveform visualization component - larger for toolbar\r\n */\r\nfunction Waveform({ levels, className = '' }: { levels: number[]; className?: string }) {\r\n // Generate 16 bars if no levels provided\r\n const bars = levels.length > 0 ? levels : Array(16).fill(0.1)\r\n\r\n return (\r\n <div className={`flex items-center justify-center gap-1 h-10 ${className}`}>\r\n {bars.map((level, i) => (\r\n <div\r\n key={i}\r\n className=\"w-1.5 bg-amber-500 rounded-full transition-all duration-75\"\r\n style={{\r\n height: `${Math.max(6, level * 40)}px`,\r\n }}\r\n />\r\n ))}\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Microphone icon (Phosphor style)\r\n */\r\nfunction MicIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M128,176a48.05,48.05,0,0,0,48-48V64a48,48,0,0,0-96,0v64A48.05,48.05,0,0,0,128,176ZM96,64a32,32,0,0,1,64,0v64a32,32,0,0,1-64,0Zm40,143.6V232a8,8,0,0,1-16,0V207.6A80.11,80.11,0,0,1,48,128a8,8,0,0,1,16,0,64,64,0,0,0,128,0,8,8,0,0,1,16,0A80.11,80.11,0,0,1,136,207.6Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Arrow up icon for submit\r\n */\r\nfunction ArrowUpIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,117.66a8,8,0,0,1-11.32,0L136,59.31V216a8,8,0,0,1-16,0V59.31L61.66,117.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,205.66,117.66Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Stop icon\r\n */\r\nfunction StopIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M200,40H56A16,16,0,0,0,40,56V200a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,160H56V56H200V200Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * X icon for cancel\r\n */\r\nfunction XIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Spinner for loading state\r\n */\r\nfunction Spinner({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={`animate-spin ${className}`} viewBox=\"0 0 24 24\" fill=\"none\">\r\n <circle\r\n className=\"opacity-25\"\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"4\"\r\n />\r\n <path\r\n className=\"opacity-75\"\r\n fill=\"currentColor\"\r\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\r\n />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Default UI for the unified AiInput component\r\n */\r\nfunction DefaultUI({\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n state,\r\n error,\r\n isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration,\r\n maxRecordingDuration,\r\n audioLevels,\r\n cooldownRemaining,\r\n placeholder = 'Ask anything...',\r\n disabled = false,\r\n}: AiInputRenderProps & {\r\n placeholder?: string\r\n disabled?: boolean\r\n}) {\r\n const isLoading = state === 'loading'\r\n const isRateLimited = state === 'rate-limited'\r\n const hasError = state === 'error'\r\n\r\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\r\n if (e.key === 'Enter' && !e.shiftKey && canSubmit && !isRecording) {\r\n e.preventDefault()\r\n submit()\r\n }\r\n }\r\n\r\n // Auto-resize textarea\r\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\r\n setText(e.target.value)\r\n // Reset height to auto to get the correct scrollHeight\r\n e.target.style.height = 'auto'\r\n // Set height to scrollHeight, with min and max constraints\r\n e.target.style.height = `${Math.min(Math.max(e.target.scrollHeight, 56), 200)}px`\r\n }\r\n\r\n return (\r\n <div className=\"w-full\">\r\n {/* Main container */}\r\n <div\r\n className={`\r\n bg-zinc-900 border border-zinc-800 rounded-xl\r\n focus-within:ring-1 focus-within:ring-amber-500/50 focus-within:border-amber-500/50\r\n transition-all duration-200\r\n ${disabled ? 'opacity-50' : ''}\r\n `}\r\n >\r\n {/* Text input area - always visible for live transcription */}\r\n <textarea\r\n value={text}\r\n onChange={handleInput}\r\n onKeyDown={handleKeyDown}\r\n placeholder={isRecording ? 'Listening...' : placeholder}\r\n disabled={disabled || isLoading || isRateLimited}\r\n rows={1}\r\n className={`\r\n w-full px-4 pt-4 pb-2\r\n bg-transparent text-zinc-100 placeholder:text-zinc-500\r\n focus:outline-none\r\n disabled:cursor-not-allowed\r\n resize-none\r\n min-h-[56px]\r\n `}\r\n style={{ height: '56px' }}\r\n />\r\n\r\n {/* Toolbar */}\r\n <div className=\"flex items-center justify-between px-3 pb-3 pt-1\">\r\n {/* Left side - error/status or waveform during recording */}\r\n <div className=\"flex items-center gap-3\">\r\n {isRecording ? (\r\n <>\r\n {/* Cancel recording */}\r\n <button\r\n onClick={cancelRecording}\r\n disabled={disabled}\r\n className=\"p-2 text-zinc-400 hover:text-zinc-200 transition-colors\"\r\n aria-label=\"Cancel recording\"\r\n >\r\n <XIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Waveform visualization */}\r\n <Waveform levels={audioLevels} />\r\n\r\n {/* Timer - just elapsed time */}\r\n <span className=\"text-sm text-zinc-400 font-mono\">\r\n {formatDuration(recordingDuration)}\r\n </span>\r\n </>\r\n ) : (\r\n <div className=\"text-sm\">\r\n {hasError && error && (\r\n <span className=\"text-red-400\">{error.message}</span>\r\n )}\r\n {isRateLimited && (\r\n <span className=\"text-amber-400\">\r\n Wait {formatDuration(cooldownRemaining)}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Right side - action buttons */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n {/* Stop and send */}\r\n <button\r\n onClick={stopRecording}\r\n disabled={disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n bg-red-500 hover:bg-red-600\r\n text-white\r\n transition-colors\r\n disabled:opacity-50 disabled:cursor-not-allowed\r\n `}\r\n aria-label=\"Stop recording\"\r\n >\r\n <StopIcon className=\"h-5 w-5\" />\r\n </button>\r\n </>\r\n ) : (\r\n <>\r\n {/* Mic button */}\r\n <button\r\n onClick={startRecording}\r\n disabled={disabled || isLoading || isRateLimited}\r\n className={`\r\n p-2 text-zinc-400 hover:text-zinc-200\r\n transition-colors\r\n disabled:opacity-50 disabled:cursor-not-allowed\r\n `}\r\n aria-label=\"Start recording\"\r\n >\r\n <MicIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Submit button */}\r\n <button\r\n onClick={submit}\r\n disabled={!canSubmit || disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n transition-colors\r\n disabled:opacity-50 disabled:cursor-not-allowed\r\n ${canSubmit\r\n ? 'bg-amber-500 hover:bg-amber-600 text-zinc-900'\r\n : 'bg-zinc-700 text-zinc-500'\r\n }\r\n `}\r\n aria-label=\"Send message\"\r\n >\r\n {isLoading ? (\r\n <Spinner className=\"h-5 w-5\" />\r\n ) : (\r\n <ArrowUpIcon className=\"h-5 w-5\" />\r\n )}\r\n </button>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * AiInput Component\r\n * \r\n * A React component for text/audio input with AI API integration.\r\n * Unified design with text input and audio recording in a single component.\r\n * \r\n * @example\r\n * // Basic usage\r\n * <AiInput\r\n * send={async (input) => {\r\n * const response = await fetch('/api/chat', {\r\n * method: 'POST',\r\n * body: JSON.stringify({ message: input }),\r\n * })\r\n * return response.json()\r\n * }}\r\n * onSuccess={(result) => console.log(result)}\r\n * />\r\n * \r\n * @example\r\n * // With separate audio handler and transcription\r\n * <AiInput\r\n * send={sendTextFn}\r\n * sendAudio={sendAudioFn}\r\n * onTranscription={(text) => console.log('Transcribed:', text)}\r\n * />\r\n * \r\n * @example\r\n * // Headless mode with custom UI\r\n * <AiInput send={sendFn}>\r\n * {({ text, setText, submit, state, isRecording, audioLevels }) => (\r\n * <div>\r\n * {isRecording ? (\r\n * <MyWaveform levels={audioLevels} />\r\n * ) : (\r\n * <input value={text} onChange={(e) => setText(e.target.value)} />\r\n * )}\r\n * <button onClick={submit}>Send</button>\r\n * </div>\r\n * )}\r\n * </AiInput>\r\n */\r\nexport function AiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n children,\r\n placeholder,\r\n className,\r\n disabled = false,\r\n}: AiInputProps) {\r\n const inputState = useAiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n })\r\n\r\n // Headless mode - render prop\r\n if (children) {\r\n return <>{children(inputState)}</>\r\n }\r\n\r\n // Default UI\r\n return (\r\n <div className={`w-full ${className || ''}`}>\r\n <DefaultUI\r\n {...inputState}\r\n placeholder={placeholder}\r\n disabled={disabled}\r\n />\r\n </div>\r\n )\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/hooks/useRateLimiter.ts","../src/hooks/useAudioRecorder.ts","../src/hooks/useAiInput.ts","../src/components/AiInput.tsx"],"names":["DEFAULT_OPTIONS","useState","useRef","useCallback","useEffect","error"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseRateLimiterOptions, UseRateLimiterReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseRateLimiterOptions = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\n/**\r\n * Hook for soft rate limiting at the UI level.\r\n * Provides UX protection by tracking requests and enforcing cooldowns.\r\n * \r\n * Note: This is not a security measure. Actual rate limiting \r\n * should be handled by the AI provider or backend.\r\n * \r\n * @param options - Rate limiting configuration\r\n * @returns Rate limiter state and controls\r\n */\r\nexport function useRateLimiter(\r\n options: Partial<UseRateLimiterOptions> = {}\r\n): UseRateLimiterReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n // Track request timestamps within the window\r\n const requestTimestamps = useRef<number[]>([])\r\n\r\n // Cooldown state\r\n const [cooldownEnd, setCooldownEnd] = useState<number>(0)\r\n const [cooldownRemaining, setCooldownRemaining] = useState<number>(0)\r\n\r\n // Force re-render for requestsRemaining updates\r\n const [, forceUpdate] = useState({})\r\n\r\n // Cleanup old timestamps and calculate remaining requests\r\n const cleanupAndCount = useCallback(() => {\r\n const now = Date.now()\r\n const windowStart = now - config.windowMs\r\n\r\n // Remove timestamps outside the window\r\n requestTimestamps.current = requestTimestamps.current.filter(\r\n (ts) => ts > windowStart\r\n )\r\n\r\n return config.maxRequests - requestTimestamps.current.length\r\n }, [config.windowMs, config.maxRequests])\r\n\r\n // Update cooldown remaining\r\n useEffect(() => {\r\n if (cooldownEnd <= Date.now()) {\r\n setCooldownRemaining(0)\r\n return\r\n }\r\n\r\n const interval = setInterval(() => {\r\n const remaining = Math.max(0, cooldownEnd - Date.now())\r\n setCooldownRemaining(remaining)\r\n\r\n if (remaining === 0) {\r\n clearInterval(interval)\r\n }\r\n }, 100)\r\n\r\n return () => clearInterval(interval)\r\n }, [cooldownEnd])\r\n\r\n // Check if request is allowed\r\n const canRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Check cooldown\r\n if (now < cooldownEnd) {\r\n return false\r\n }\r\n\r\n // Check request count\r\n return cleanupAndCount() > 0\r\n }, [cooldownEnd, cleanupAndCount])\r\n\r\n // Record a request\r\n const recordRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Add timestamp\r\n requestTimestamps.current.push(now)\r\n\r\n // Start cooldown\r\n const newCooldownEnd = now + config.cooldownMs\r\n setCooldownEnd(newCooldownEnd)\r\n setCooldownRemaining(config.cooldownMs)\r\n\r\n // Trigger re-render\r\n forceUpdate({})\r\n }, [config.cooldownMs])\r\n\r\n // Reset rate limiter\r\n const reset = useCallback(() => {\r\n requestTimestamps.current = []\r\n setCooldownEnd(0)\r\n setCooldownRemaining(0)\r\n forceUpdate({})\r\n }, [])\r\n\r\n return {\r\n canRequest: canRequest(),\r\n cooldownRemaining,\r\n requestsRemaining: cleanupAndCount(),\r\n recordRequest,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseAudioRecorderOptions, UseAudioRecorderReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseAudioRecorderOptions = {\r\n maxDurationMs: 60000, // 1 minute\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Get the best supported MIME type for MediaRecorder\r\n */\r\nfunction getSupportedMimeType(preferredTypes: string[]): string | null {\r\n if (typeof MediaRecorder === 'undefined') {\r\n return null\r\n }\r\n\r\n for (const mimeType of preferredTypes) {\r\n if (MediaRecorder.isTypeSupported(mimeType)) {\r\n return mimeType\r\n }\r\n }\r\n\r\n // Fallback to default\r\n return ''\r\n}\r\n\r\n/**\r\n * Hook for audio recording using Web APIs.\r\n * Uses navigator.mediaDevices, MediaRecorder, and Web Audio API for visualization.\r\n * \r\n * @param options - Audio recording configuration\r\n * @returns Audio recorder state and controls\r\n */\r\nexport function useAudioRecorder(\r\n options: Partial<UseAudioRecorderOptions> = {}\r\n): UseAudioRecorderReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n const [isRecording, setIsRecording] = useState(false)\r\n const [duration, setDuration] = useState(0)\r\n const [audioBlob, setAudioBlob] = useState<Blob | null>(null)\r\n const [error, setError] = useState<Error | null>(null)\r\n const [audioLevels, setAudioLevels] = useState<number[]>([])\r\n\r\n const mediaRecorderRef = useRef<MediaRecorder | null>(null)\r\n const streamRef = useRef<MediaStream | null>(null)\r\n const chunksRef = useRef<Blob[]>([])\r\n const startTimeRef = useRef<number>(0)\r\n const durationIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\r\n const maxDurationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\r\n\r\n // Web Audio API refs for visualization\r\n const audioContextRef = useRef<AudioContext | null>(null)\r\n const analyserRef = useRef<AnalyserNode | null>(null)\r\n const animationFrameRef = useRef<number | null>(null)\r\n\r\n // Check if audio recording is supported\r\n const isSupported = typeof navigator !== 'undefined'\r\n && 'mediaDevices' in navigator\r\n && 'getUserMedia' in navigator.mediaDevices\r\n && typeof MediaRecorder !== 'undefined'\r\n\r\n // Update audio levels from analyser - uses time domain for better voice visualization\r\n const updateAudioLevels = useCallback(() => {\r\n if (!analyserRef.current) return\r\n\r\n const analyser = analyserRef.current\r\n const bufferLength = analyser.fftSize\r\n const dataArray = new Uint8Array(bufferLength)\r\n\r\n // Use time domain data for even distribution across bars\r\n analyser.getByteTimeDomainData(dataArray)\r\n\r\n // Sample 16 bars from the waveform data\r\n const bars = 16\r\n const step = Math.floor(bufferLength / bars)\r\n const levels: number[] = []\r\n\r\n for (let i = 0; i < bars; i++) {\r\n // Get amplitude variation from center (128) for each segment\r\n let maxDeviation = 0\r\n for (let j = 0; j < step; j++) {\r\n const value = dataArray[i * step + j]\r\n const deviation = Math.abs(value - 128)\r\n maxDeviation = Math.max(maxDeviation, deviation)\r\n }\r\n // Normalize to 0-1 range (max deviation is 128)\r\n // Apply some amplification for better visibility\r\n levels.push(Math.min(1, (maxDeviation / 128) * 2.5))\r\n }\r\n\r\n setAudioLevels(levels)\r\n\r\n // Continue animation loop\r\n animationFrameRef.current = requestAnimationFrame(updateAudioLevels)\r\n }, [])\r\n\r\n // Cleanup function\r\n const cleanup = useCallback(() => {\r\n if (durationIntervalRef.current) {\r\n clearInterval(durationIntervalRef.current)\r\n durationIntervalRef.current = null\r\n }\r\n\r\n if (maxDurationTimeoutRef.current) {\r\n clearTimeout(maxDurationTimeoutRef.current)\r\n maxDurationTimeoutRef.current = null\r\n }\r\n\r\n if (animationFrameRef.current) {\r\n cancelAnimationFrame(animationFrameRef.current)\r\n animationFrameRef.current = null\r\n }\r\n\r\n if (audioContextRef.current) {\r\n audioContextRef.current.close()\r\n audioContextRef.current = null\r\n }\r\n\r\n if (streamRef.current) {\r\n streamRef.current.getTracks().forEach((track) => track.stop())\r\n streamRef.current = null\r\n }\r\n\r\n analyserRef.current = null\r\n mediaRecorderRef.current = null\r\n chunksRef.current = []\r\n setAudioLevels([])\r\n }, [])\r\n\r\n // Stop recording\r\n const stopRecording = useCallback(() => {\r\n if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {\r\n mediaRecorderRef.current.stop()\r\n }\r\n setIsRecording(false)\r\n }, [])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!isSupported) {\r\n setError(new Error('Audio recording is not supported in this browser'))\r\n return\r\n }\r\n\r\n // Reset state\r\n setError(null)\r\n setAudioBlob(null)\r\n setDuration(0)\r\n setAudioLevels([])\r\n chunksRef.current = []\r\n\r\n try {\r\n // Get microphone access\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\r\n streamRef.current = stream\r\n\r\n // Set up Web Audio API for visualization\r\n const audioContext = new AudioContext()\r\n audioContextRef.current = audioContext\r\n\r\n const source = audioContext.createMediaStreamSource(stream)\r\n const analyser = audioContext.createAnalyser()\r\n analyser.fftSize = 256\r\n analyser.smoothingTimeConstant = 0.8\r\n source.connect(analyser)\r\n analyserRef.current = analyser\r\n\r\n // Get supported MIME type\r\n const mimeType = getSupportedMimeType(config.mimeTypes)\r\n\r\n // Create MediaRecorder\r\n const mediaRecorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined)\r\n mediaRecorderRef.current = mediaRecorder\r\n\r\n // Handle data available\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n chunksRef.current.push(event.data)\r\n }\r\n }\r\n\r\n // Handle recording stop\r\n mediaRecorder.onstop = () => {\r\n const blob = new Blob(chunksRef.current, {\r\n type: mimeType || 'audio/webm'\r\n })\r\n setAudioBlob(blob)\r\n\r\n // Call callback if provided\r\n if (config.onRecordingComplete) {\r\n config.onRecordingComplete(blob)\r\n }\r\n\r\n cleanup()\r\n }\r\n\r\n // Handle errors\r\n mediaRecorder.onerror = () => {\r\n setError(new Error('Recording error occurred'))\r\n setIsRecording(false)\r\n cleanup()\r\n }\r\n\r\n // Start recording\r\n mediaRecorder.start(100) // Collect data every 100ms\r\n startTimeRef.current = Date.now()\r\n setIsRecording(true)\r\n\r\n // Start audio level visualization\r\n updateAudioLevels()\r\n\r\n // Update duration every 100ms\r\n durationIntervalRef.current = setInterval(() => {\r\n setDuration(Date.now() - startTimeRef.current)\r\n }, 100)\r\n\r\n // Auto-stop at max duration\r\n maxDurationTimeoutRef.current = setTimeout(() => {\r\n stopRecording()\r\n }, config.maxDurationMs)\r\n\r\n } catch (err) {\r\n const errorMessage = err instanceof Error\r\n ? err.message\r\n : 'Failed to access microphone'\r\n setError(new Error(errorMessage))\r\n cleanup()\r\n }\r\n }, [isSupported, config.mimeTypes, config.maxDurationMs, config.onRecordingComplete, cleanup, stopRecording, updateAudioLevels])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n }, [cleanup])\r\n\r\n // Reset hook state\r\n const reset = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n setError(null)\r\n setAudioLevels([])\r\n }, [cleanup])\r\n\r\n // Cleanup on unmount\r\n useEffect(() => {\r\n return () => {\r\n cleanup()\r\n }\r\n }, [cleanup])\r\n\r\n return {\r\n isRecording,\r\n isSupported,\r\n duration,\r\n audioBlob,\r\n audioLevels,\r\n error,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from 'react'\r\nimport { useRateLimiter } from './useRateLimiter'\r\nimport { useAudioRecorder } from './useAudioRecorder'\r\nimport type {\r\n UseAiInputOptions,\r\n UseAiInputReturn,\r\n AiInputState,\r\n RateLimitConfig,\r\n AudioConfig,\r\n} from '../types'\r\n\r\nconst DEFAULT_RATE_LIMIT: RateLimitConfig = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\nconst DEFAULT_AUDIO_CONFIG: AudioConfig = {\r\n maxDurationMs: 60000,\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Main hook for AI input functionality.\r\n * Combines rate limiting, audio recording, and API communication.\r\n * Unified design - text and audio in single component.\r\n * \r\n * @param options - Configuration options\r\n * @returns Complete state and controls for AI input\r\n */\r\nexport function useAiInput(options: UseAiInputOptions): UseAiInputReturn {\r\n const {\r\n send,\r\n sendAudio,\r\n rateLimit = {},\r\n audioConfig = {},\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n } = options\r\n\r\n const rateLimitConfig = { ...DEFAULT_RATE_LIMIT, ...rateLimit }\r\n const audioConfigMerged = { ...DEFAULT_AUDIO_CONFIG, ...audioConfig }\r\n\r\n // State\r\n const [state, setState] = useState<AiInputState>('idle')\r\n const [text, setText] = useState('')\r\n const [error, setError] = useState<Error | null>(null)\r\n const [result, setResult] = useState<unknown>(null)\r\n\r\n // Ref to track if we're waiting to submit audio after recording stops\r\n const pendingAudioSubmitRef = useRef(false)\r\n\r\n // Rate limiter\r\n const rateLimiter = useRateLimiter(rateLimitConfig)\r\n\r\n // Audio recorder\r\n const audioRecorder = useAudioRecorder({\r\n ...audioConfigMerged,\r\n })\r\n\r\n // Update state based on rate limiter\r\n useEffect(() => {\r\n if (!rateLimiter.canRequest && state === 'idle') {\r\n setState('rate-limited')\r\n } else if (rateLimiter.canRequest && state === 'rate-limited') {\r\n setState('idle')\r\n }\r\n }, [rateLimiter.canRequest, state])\r\n\r\n // Update state when recording\r\n useEffect(() => {\r\n if (audioRecorder.isRecording && state !== 'recording') {\r\n setState('recording')\r\n }\r\n }, [audioRecorder.isRecording, state])\r\n\r\n // Handle audio recorder errors\r\n useEffect(() => {\r\n if (audioRecorder.error) {\r\n setError(audioRecorder.error)\r\n setState('error')\r\n onError?.(audioRecorder.error)\r\n }\r\n }, [audioRecorder.error, onError])\r\n\r\n // Submit text\r\n const submitText = useCallback(async () => {\r\n if (!text.trim() || !rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n const response = await send(text)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n // Clear text after successful send\r\n setText('')\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [text, rateLimiter, send, onSuccess, onError])\r\n\r\n // Submit audio\r\n const submitAudio = useCallback(async (blob: Blob) => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n // Use sendAudio if provided, otherwise use send\r\n const sendFn = sendAudio || send\r\n const response = await sendFn(blob)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n\r\n // Handle transcription if callback provided\r\n if (onTranscription && response && typeof response === 'object') {\r\n const res = response as Record<string, unknown>\r\n // Try common transcription response formats\r\n const transcriptionText = res.text || res.transcription || res.transcript\r\n if (typeof transcriptionText === 'string') {\r\n setText(transcriptionText)\r\n onTranscription(transcriptionText)\r\n }\r\n }\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [rateLimiter, send, sendAudio, onSuccess, onError, onTranscription])\r\n\r\n // Handle audio blob ready - submit if we were waiting\r\n useEffect(() => {\r\n if (pendingAudioSubmitRef.current && audioRecorder.audioBlob && !audioRecorder.isRecording) {\r\n pendingAudioSubmitRef.current = false\r\n submitAudio(audioRecorder.audioBlob)\r\n }\r\n }, [audioRecorder.audioBlob, audioRecorder.isRecording, submitAudio])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n await audioRecorder.startRecording()\r\n }, [rateLimiter.canRequest, audioRecorder])\r\n\r\n // Stop recording and submit\r\n const stopRecording = useCallback(() => {\r\n // Mark that we want to submit audio when blob is ready\r\n pendingAudioSubmitRef.current = true\r\n audioRecorder.stopRecording()\r\n }, [audioRecorder])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n audioRecorder.cancelRecording()\r\n setState('idle')\r\n }, [audioRecorder])\r\n\r\n // Submit based on current state\r\n const submit = useCallback(() => {\r\n if (audioRecorder.isRecording) {\r\n stopRecording()\r\n } else if (text.trim()) {\r\n submitText()\r\n }\r\n }, [audioRecorder.isRecording, text, stopRecording, submitText])\r\n\r\n // Reset all state\r\n const reset = useCallback(() => {\r\n setState('idle')\r\n setText('')\r\n setError(null)\r\n setResult(null)\r\n rateLimiter.reset()\r\n audioRecorder.reset()\r\n }, [rateLimiter, audioRecorder])\r\n\r\n // Can submit check\r\n const canSubmit =\r\n rateLimiter.canRequest &&\r\n state !== 'loading' &&\r\n (audioRecorder.isRecording || text.trim().length > 0)\r\n\r\n return {\r\n // State\r\n state,\r\n error,\r\n result,\r\n\r\n // Text\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n\r\n // Audio\r\n isRecording: audioRecorder.isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration: audioRecorder.duration,\r\n maxRecordingDuration: audioConfigMerged.maxDurationMs,\r\n audioLevels: audioRecorder.audioLevels,\r\n\r\n // Rate limiting\r\n cooldownRemaining: rateLimiter.cooldownRemaining,\r\n requestsRemaining: rateLimiter.requestsRemaining,\r\n\r\n // Utils\r\n reset,\r\n }\r\n}\r\n","import React from 'react'\r\nimport { useAiInput } from '../hooks/useAiInput'\r\nimport type { AiInputProps, AiInputRenderProps } from '../types'\r\n\r\n/**\r\n * Format milliseconds to MM:SS display\r\n */\r\nfunction formatDuration(ms: number): string {\r\n const seconds = Math.floor(ms / 1000)\r\n const minutes = Math.floor(seconds / 60)\r\n const remainingSeconds = seconds % 60\r\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`\r\n}\r\n\r\n/**\r\n * Waveform visualization component with smooth animations\r\n */\r\nfunction Waveform({ levels, className = '' }: { levels: number[]; className?: string }) {\r\n // Generate 16 bars if no levels provided\r\n const bars = levels.length > 0 ? levels : Array(16).fill(0.15)\r\n\r\n return (\r\n <div className={`flex items-center justify-center gap-1 h-10 ${className}`}>\r\n {bars.map((level, i) => (\r\n <div\r\n key={i}\r\n className=\"w-1.5 bg-gradient-to-t from-amber-600 to-amber-400 rounded-full transition-all duration-100 ease-out\"\r\n style={{\r\n height: `${Math.max(6, level * 40)}px`,\r\n opacity: 0.6 + level * 0.4,\r\n }}\r\n />\r\n ))}\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Recording pulse indicator\r\n */\r\nfunction RecordingPulse() {\r\n return (\r\n <span className=\"relative flex h-3 w-3 mr-2\">\r\n <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75\"></span>\r\n <span className=\"relative inline-flex rounded-full h-3 w-3 bg-red-500\"></span>\r\n </span>\r\n )\r\n}\r\n\r\n/**\r\n * Microphone icon (Phosphor style)\r\n */\r\nfunction MicIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M128,176a48.05,48.05,0,0,0,48-48V64a48,48,0,0,0-96,0v64A48.05,48.05,0,0,0,128,176ZM96,64a32,32,0,0,1,64,0v64a32,32,0,0,1-64,0Zm40,143.6V232a8,8,0,0,1-16,0V207.6A80.11,80.11,0,0,1,48,128a8,8,0,0,1,16,0,64,64,0,0,0,128,0,8,8,0,0,1,16,0A80.11,80.11,0,0,1,136,207.6Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Arrow up icon for submit\r\n */\r\nfunction ArrowUpIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,117.66a8,8,0,0,1-11.32,0L136,59.31V216a8,8,0,0,1-16,0V59.31L61.66,117.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,205.66,117.66Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Stop icon (filled square)\r\n */\r\nfunction StopIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <rect x=\"64\" y=\"64\" width=\"128\" height=\"128\" rx=\"8\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * X icon for cancel\r\n */\r\nfunction XIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Spinner for loading state\r\n */\r\nfunction Spinner({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={`animate-spin ${className}`} viewBox=\"0 0 24 24\" fill=\"none\">\r\n <circle\r\n className=\"opacity-25\"\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"4\"\r\n />\r\n <path\r\n className=\"opacity-75\"\r\n fill=\"currentColor\"\r\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\r\n />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Default UI for the unified AiInput component\r\n */\r\nfunction DefaultUI({\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n state,\r\n error,\r\n isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration,\r\n audioLevels,\r\n cooldownRemaining,\r\n placeholder = 'Ask anything...',\r\n disabled = false,\r\n}: AiInputRenderProps & {\r\n placeholder?: string\r\n disabled?: boolean\r\n}) {\r\n const isLoading = state === 'loading'\r\n const isRateLimited = state === 'rate-limited'\r\n const hasError = state === 'error'\r\n\r\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\r\n if (e.key === 'Enter' && !e.shiftKey && canSubmit && !isRecording) {\r\n e.preventDefault()\r\n submit()\r\n }\r\n }\r\n\r\n // Auto-resize textarea\r\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\r\n setText(e.target.value)\r\n // Reset height to auto to get the correct scrollHeight\r\n e.target.style.height = 'auto'\r\n // Set height to scrollHeight, with min and max constraints\r\n e.target.style.height = `${Math.min(Math.max(e.target.scrollHeight, 56), 200)}px`\r\n }\r\n\r\n return (\r\n <div className=\"w-full\">\r\n {/* Main container */}\r\n <div\r\n className={`\r\n bg-zinc-900 border rounded-xl\r\n transition-all duration-300 ease-out\r\n ${isRecording\r\n ? 'border-red-500/50 shadow-lg shadow-red-500/10'\r\n : 'border-zinc-800 focus-within:border-amber-500/50 focus-within:shadow-lg focus-within:shadow-amber-500/5'\r\n }\r\n ${disabled ? 'opacity-50' : ''}\r\n `}\r\n >\r\n {/* Text input area - always visible for live transcription */}\r\n <textarea\r\n value={text}\r\n onChange={handleInput}\r\n onKeyDown={handleKeyDown}\r\n placeholder={isRecording ? 'Listening...' : placeholder}\r\n disabled={disabled || isLoading || isRateLimited}\r\n rows={1}\r\n className={`\r\n w-full px-4 pt-4 pb-2\r\n bg-transparent text-zinc-100 placeholder:text-zinc-500\r\n focus:outline-none\r\n disabled:cursor-not-allowed\r\n resize-none\r\n min-h-[56px]\r\n transition-colors duration-200\r\n `}\r\n style={{ height: '56px' }}\r\n />\r\n\r\n {/* Toolbar */}\r\n <div className=\"flex items-center justify-between px-3 pb-3 pt-1\">\r\n {/* Left side - error/status or waveform during recording */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n {/* Cancel recording */}\r\n <button\r\n onClick={cancelRecording}\r\n disabled={disabled}\r\n className=\"p-2 text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 rounded-lg transition-all duration-200 active:scale-95\"\r\n aria-label=\"Cancel recording\"\r\n >\r\n <XIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Recording indicator + Waveform */}\r\n <div className=\"flex items-center\">\r\n <RecordingPulse />\r\n <Waveform levels={audioLevels} />\r\n </div>\r\n\r\n {/* Timer */}\r\n <span className=\"text-sm text-zinc-400 font-mono tabular-nums\">\r\n {formatDuration(recordingDuration)}\r\n </span>\r\n </>\r\n ) : (\r\n <div className=\"text-sm min-h-[28px] flex items-center\">\r\n {hasError && error && (\r\n <span className=\"text-red-400 animate-pulse\">{error.message}</span>\r\n )}\r\n {isRateLimited && (\r\n <span className=\"text-amber-400\">\r\n Wait {formatDuration(cooldownRemaining)}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Right side - action buttons */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n {/* Stop and send */}\r\n <button\r\n onClick={stopRecording}\r\n disabled={disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n bg-red-500 hover:bg-red-400\r\n text-white\r\n transition-all duration-200\r\n hover:scale-105 active:scale-95\r\n disabled:opacity-50 disabled:cursor-not-allowed\r\n shadow-lg shadow-red-500/25\r\n `}\r\n aria-label=\"Stop recording\"\r\n >\r\n <StopIcon className=\"h-5 w-5\" />\r\n </button>\r\n </>\r\n ) : (\r\n <>\r\n {/* Mic button */}\r\n <button\r\n onClick={startRecording}\r\n disabled={disabled || isLoading || isRateLimited}\r\n className={`\r\n p-2 text-zinc-400 \r\n hover:text-amber-400 hover:bg-zinc-800\r\n rounded-lg\r\n transition-all duration-200\r\n hover:scale-105 active:scale-95\r\n disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\r\n `}\r\n aria-label=\"Start recording\"\r\n >\r\n <MicIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Submit button */}\r\n <button\r\n onClick={submit}\r\n disabled={!canSubmit || disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n transition-all duration-200\r\n disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\r\n ${canSubmit\r\n ? 'bg-amber-500 hover:bg-amber-400 text-zinc-900 hover:scale-105 active:scale-95 shadow-lg shadow-amber-500/25'\r\n : 'bg-zinc-800 text-zinc-500'\r\n }\r\n `}\r\n aria-label=\"Send message\"\r\n >\r\n {isLoading ? (\r\n <Spinner className=\"h-5 w-5\" />\r\n ) : (\r\n <ArrowUpIcon className=\"h-5 w-5\" />\r\n )}\r\n </button>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * AiInput Component\r\n * \r\n * A React component for text/audio input with AI API integration.\r\n * Unified design with text input and audio recording in a single component.\r\n * \r\n * @example\r\n * // Basic usage\r\n * <AiInput\r\n * send={async (input) => {\r\n * const response = await fetch('/api/chat', {\r\n * method: 'POST',\r\n * body: JSON.stringify({ message: input }),\r\n * })\r\n * return response.json()\r\n * }}\r\n * onSuccess={(result) => console.log(result)}\r\n * />\r\n * \r\n * @example\r\n * // With separate audio handler and transcription\r\n * <AiInput\r\n * send={sendTextFn}\r\n * sendAudio={sendAudioFn}\r\n * onTranscription={(text) => console.log('Transcribed:', text)}\r\n * />\r\n * \r\n * @example\r\n * // Headless mode with custom UI\r\n * <AiInput send={sendFn}>\r\n * {({ text, setText, submit, state, isRecording, audioLevels }) => (\r\n * <div>\r\n * {isRecording ? (\r\n * <MyWaveform levels={audioLevels} />\r\n * ) : (\r\n * <input value={text} onChange={(e) => setText(e.target.value)} />\r\n * )}\r\n * <button onClick={submit}>Send</button>\r\n * </div>\r\n * )}\r\n * </AiInput>\r\n */\r\nexport function AiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n children,\r\n placeholder,\r\n className,\r\n disabled = false,\r\n}: AiInputProps) {\r\n const inputState = useAiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n })\r\n\r\n // Headless mode - render prop\r\n if (children) {\r\n return <>{children(inputState)}</>\r\n }\r\n\r\n // Default UI\r\n return (\r\n <div className={`w-full ${className || ''}`}>\r\n <DefaultUI\r\n {...inputState}\r\n placeholder={placeholder}\r\n disabled={disabled}\r\n />\r\n </div>\r\n )\r\n}\r\n"]}
package/dist/styles.css CHANGED
@@ -1,2 +1,2 @@
1
1
  /*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-duration:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-400:#f87171;--color-red-500:#ef4444;--color-red-600:#dc2626;--color-amber-400:#fbbf24;--color-amber-500:#f59e0b;--color-amber-600:#d97706;--color-zinc-100:#f4f4f5;--color-zinc-200:#e4e4e7;--color-zinc-400:#a1a1aa;--color-zinc-500:#71717a;--color-zinc-700:#3f3f46;--color-zinc-800:#27272a;--color-zinc-900:#18181b;--color-white:#fff;--spacing:.25rem;--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--radius-xl:.75rem;--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.flex{display:flex}.h-5{height:calc(var(--spacing)*5)}.h-10{height:calc(var(--spacing)*10)}.min-h-\[56px\]{min-height:56px}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-5{width:calc(var(--spacing)*5)}.w-full{width:100%}.animate-spin{animation:var(--animate-spin)}.resize-none{resize:none}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing)*1)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.rounded-full{border-radius:3.40282e38px}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-zinc-800{border-color:var(--color-zinc-800)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-red-500{background-color:var(--color-red-500)}.bg-transparent{background-color:#0000}.bg-zinc-700{background-color:var(--color-zinc-700)}.bg-zinc-900{background-color:var(--color-zinc-900)}.p-2{padding:calc(var(--spacing)*2)}.p-2\.5{padding:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-4{padding-top:calc(var(--spacing)*4)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.font-mono{font-family:var(--font-mono)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-amber-400{color:var(--color-amber-400)}.text-red-400{color:var(--color-red-400)}.text-white{color:var(--color-white)}.text-zinc-100{color:var(--color-zinc-100)}.text-zinc-400{color:var(--color-zinc-400)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-900{color:var(--color-zinc-900)}.opacity-25{opacity:.25}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-75{--tw-duration:75ms;transition-duration:75ms}.duration-200{--tw-duration:.2s;transition-duration:.2s}.placeholder\:text-zinc-500::placeholder{color:var(--color-zinc-500)}.focus-within\:border-amber-500\/50:focus-within{border-color:#f59e0b80}@supports (color:color-mix(in lab, red, red)){.focus-within\:border-amber-500\/50:focus-within{border-color:color-mix(in oklab,var(--color-amber-500)50%,transparent)}}.focus-within\:ring-1:focus-within{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-within\:ring-amber-500\/50:focus-within{--tw-ring-color:#f59e0b80}@supports (color:color-mix(in lab, red, red)){.focus-within\:ring-amber-500\/50:focus-within{--tw-ring-color:color-mix(in oklab,var(--color-amber-500)50%,transparent)}}@media (hover:hover){.hover\:bg-amber-600:hover{background-color:var(--color-amber-600)}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:text-zinc-200:hover{color:var(--color-zinc-200)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}}@keyframes wave{0%,to{transform:scaleY(.5)}50%{transform:scaleY(1)}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-duration{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@keyframes spin{to{transform:rotate(360deg)}}
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-400:#f87171;--color-red-500:#ef4444;--color-amber-400:#fbbf24;--color-amber-500:#f59e0b;--color-amber-600:#d97706;--color-zinc-100:#f4f4f5;--color-zinc-200:#e4e4e7;--color-zinc-400:#a1a1aa;--color-zinc-500:#71717a;--color-zinc-800:#27272a;--color-zinc-900:#18181b;--color-white:#fff;--spacing:.25rem;--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--radius-lg:.5rem;--radius-xl:.75rem;--ease-out:cubic-bezier(0,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0,0,.2,1)infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mr-2{margin-right:calc(var(--spacing)*2)}.flex{display:flex}.inline-flex{display:inline-flex}.h-3{height:calc(var(--spacing)*3)}.h-5{height:calc(var(--spacing)*5)}.h-10{height:calc(var(--spacing)*10)}.h-full{height:100%}.min-h-\[28px\]{min-height:28px}.min-h-\[56px\]{min-height:56px}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-3{width:calc(var(--spacing)*3)}.w-5{width:calc(var(--spacing)*5)}.w-full{width:100%}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.resize-none{resize:none}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing)*1)}.gap-2{gap:calc(var(--spacing)*2)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-red-500\/50{border-color:#ef444480}@supports (color:color-mix(in lab, red, red)){.border-red-500\/50{border-color:color-mix(in oklab,var(--color-red-500)50%,transparent)}}.border-zinc-800{border-color:var(--color-zinc-800)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-red-400{background-color:var(--color-red-400)}.bg-red-500{background-color:var(--color-red-500)}.bg-transparent{background-color:#0000}.bg-zinc-800{background-color:var(--color-zinc-800)}.bg-zinc-900{background-color:var(--color-zinc-900)}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-amber-600{--tw-gradient-from:var(--color-amber-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-amber-400{--tw-gradient-to:var(--color-amber-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.p-2{padding:calc(var(--spacing)*2)}.p-2\.5{padding:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-4{padding-top:calc(var(--spacing)*4)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.font-mono{font-family:var(--font-mono)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-amber-400{color:var(--color-amber-400)}.text-red-400{color:var(--color-red-400)}.text-white{color:var(--color-white)}.text-zinc-100{color:var(--color-zinc-100)}.text-zinc-400{color:var(--color-zinc-400)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-900{color:var(--color-zinc-900)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.opacity-25{opacity:.25}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-amber-500\/25{--tw-shadow-color:#f59e0b40}@supports (color:color-mix(in lab, red, red)){.shadow-amber-500\/25{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-amber-500)25%,transparent)var(--tw-shadow-alpha),transparent)}}.shadow-red-500\/10{--tw-shadow-color:#ef44441a}@supports (color:color-mix(in lab, red, red)){.shadow-red-500\/10{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-red-500)10%,transparent)var(--tw-shadow-alpha),transparent)}}.shadow-red-500\/25{--tw-shadow-color:#ef444440}@supports (color:color-mix(in lab, red, red)){.shadow-red-500\/25{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-red-500)25%,transparent)var(--tw-shadow-alpha),transparent)}}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.placeholder\:text-zinc-500::placeholder{color:var(--color-zinc-500)}.focus-within\:border-amber-500\/50:focus-within{border-color:#f59e0b80}@supports (color:color-mix(in lab, red, red)){.focus-within\:border-amber-500\/50:focus-within{border-color:color-mix(in oklab,var(--color-amber-500)50%,transparent)}}.focus-within\:shadow-lg:focus-within{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-within\:shadow-amber-500\/5:focus-within{--tw-shadow-color:#f59e0b0d}@supports (color:color-mix(in lab, red, red)){.focus-within\:shadow-amber-500\/5:focus-within{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-amber-500)5%,transparent)var(--tw-shadow-alpha),transparent)}}@media (hover:hover){.hover\:scale-105:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x)var(--tw-scale-y)}.hover\:bg-amber-400:hover{background-color:var(--color-amber-400)}.hover\:bg-red-400:hover{background-color:var(--color-red-400)}.hover\:bg-zinc-800:hover{background-color:var(--color-zinc-800)}.hover\:text-amber-400:hover{color:var(--color-amber-400)}.hover\:text-zinc-200:hover{color:var(--color-zinc-200)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.active\:scale-95:active{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x)var(--tw-scale-y)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media (hover:hover){.disabled\:hover\:scale-100:disabled:hover{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x)var(--tw-scale-y)}}}@keyframes wave{0%,to{transform:scaleY(.5)}50%{transform:scaleY(1)}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";inherits:false;initial-value:100%}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-input-react",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.3",
4
4
  "description": "React component for text/audio input with AI API integration. Framework-agnostic, works with Next.js, Vite, PHP, and any React setup.",
5
5
  "keywords": [
6
6
  "react",