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

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
@@ -409,18 +409,25 @@ function formatDuration(ms) {
409
409
  return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;
410
410
  }
411
411
  function Waveform({ levels, className = "" }) {
412
- const bars = levels.length > 0 ? levels : Array(16).fill(0.1);
412
+ const bars = levels.length > 0 ? levels : Array(16).fill(0.15);
413
413
  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
414
  "div",
415
415
  {
416
- 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",
417
417
  style: {
418
- height: `${Math.max(6, level * 40)}px`
418
+ height: `${Math.max(6, level * 40)}px`,
419
+ opacity: 0.6 + level * 0.4
419
420
  }
420
421
  },
421
422
  i
422
423
  )) });
423
424
  }
425
+ function RecordingPulse() {
426
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative flex h-3 w-3 mr-2", children: [
427
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75" }),
428
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex rounded-full h-3 w-3 bg-red-500" })
429
+ ] });
430
+ }
424
431
  function MicIcon({ className = "" }) {
425
432
  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
433
  }
@@ -428,7 +435,7 @@ function ArrowUpIcon({ className = "" }) {
428
435
  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
436
  }
430
437
  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" }) });
438
+ 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
439
  }
433
440
  function XIcon({ className = "" }) {
434
441
  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 +475,6 @@ function DefaultUI({
468
475
  stopRecording,
469
476
  cancelRecording,
470
477
  recordingDuration,
471
- maxRecordingDuration,
472
478
  audioLevels,
473
479
  cooldownRemaining,
474
480
  placeholder = "Ask anything...",
@@ -492,9 +498,9 @@ function DefaultUI({
492
498
  "div",
493
499
  {
494
500
  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
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"}
498
504
  ${disabled ? "opacity-50" : ""}
499
505
  `,
500
506
  children: [
@@ -514,26 +520,30 @@ function DefaultUI({
514
520
  disabled:cursor-not-allowed
515
521
  resize-none
516
522
  min-h-[56px]
523
+ transition-colors duration-200
517
524
  `,
518
525
  style: { height: "56px" }
519
526
  }
520
527
  ),
521
528
  /* @__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: [
529
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: isRecording ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
523
530
  /* @__PURE__ */ jsxRuntime.jsx(
524
531
  "button",
525
532
  {
526
533
  onClick: cancelRecording,
527
534
  disabled,
528
- 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",
529
536
  "aria-label": "Cancel recording",
530
537
  children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, { className: "h-5 w-5" })
531
538
  }
532
539
  ),
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 }),
540
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
541
+ /* @__PURE__ */ jsxRuntime.jsx(RecordingPulse, {}),
542
+ /* @__PURE__ */ jsxRuntime.jsx(Waveform, { levels: audioLevels })
543
+ ] }),
544
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-zinc-400 font-mono tabular-nums", children: formatDuration(recordingDuration) })
545
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm min-h-[28px] flex items-center", children: [
546
+ hasError && error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-400 animate-pulse", children: error.message }),
537
547
  isRateLimited && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-amber-400", children: [
538
548
  "Wait ",
539
549
  formatDuration(cooldownRemaining)
@@ -546,10 +556,12 @@ function DefaultUI({
546
556
  disabled,
547
557
  className: `
548
558
  p-2.5 rounded-full
549
- bg-red-500 hover:bg-red-600
559
+ bg-red-500 hover:bg-red-400
550
560
  text-white
551
- transition-colors
561
+ transition-all duration-200
562
+ hover:scale-105 active:scale-95
552
563
  disabled:opacity-50 disabled:cursor-not-allowed
564
+ shadow-lg shadow-red-500/25
553
565
  `,
554
566
  "aria-label": "Stop recording",
555
567
  children: /* @__PURE__ */ jsxRuntime.jsx(StopIcon, { className: "h-5 w-5" })
@@ -561,9 +573,12 @@ function DefaultUI({
561
573
  onClick: startRecording,
562
574
  disabled: disabled || isLoading || isRateLimited,
563
575
  className: `
564
- p-2 text-zinc-400 hover:text-zinc-200
565
- transition-colors
566
- 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
567
582
  `,
568
583
  "aria-label": "Start recording",
569
584
  children: /* @__PURE__ */ jsxRuntime.jsx(MicIcon, { className: "h-5 w-5" })
@@ -576,9 +591,9 @@ function DefaultUI({
576
591
  disabled: !canSubmit || disabled,
577
592
  className: `
578
593
  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"}
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"}
582
597
  `,
583
598
  "aria-label": "Send message",
584
599
  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\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 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
@@ -407,18 +407,25 @@ function formatDuration(ms) {
407
407
  return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;
408
408
  }
409
409
  function Waveform({ levels, className = "" }) {
410
- const bars = levels.length > 0 ? levels : Array(16).fill(0.1);
410
+ const bars = levels.length > 0 ? levels : Array(16).fill(0.15);
411
411
  return /* @__PURE__ */ jsx("div", { className: `flex items-center justify-center gap-1 h-10 ${className}`, children: bars.map((level, i) => /* @__PURE__ */ jsx(
412
412
  "div",
413
413
  {
414
- className: "w-1.5 bg-amber-500 rounded-full transition-all duration-75",
414
+ className: "w-1.5 bg-gradient-to-t from-amber-600 to-amber-400 rounded-full transition-all duration-100 ease-out",
415
415
  style: {
416
- height: `${Math.max(6, level * 40)}px`
416
+ height: `${Math.max(6, level * 40)}px`,
417
+ opacity: 0.6 + level * 0.4
417
418
  }
418
419
  },
419
420
  i
420
421
  )) });
421
422
  }
423
+ function RecordingPulse() {
424
+ return /* @__PURE__ */ jsxs("span", { className: "relative flex h-3 w-3 mr-2", children: [
425
+ /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75" }),
426
+ /* @__PURE__ */ jsx("span", { className: "relative inline-flex rounded-full h-3 w-3 bg-red-500" })
427
+ ] });
428
+ }
422
429
  function MicIcon({ className = "" }) {
423
430
  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
431
  }
@@ -426,7 +433,7 @@ function ArrowUpIcon({ className = "" }) {
426
433
  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
434
  }
428
435
  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" }) });
436
+ 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
437
  }
431
438
  function XIcon({ className = "" }) {
432
439
  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 +473,6 @@ function DefaultUI({
466
473
  stopRecording,
467
474
  cancelRecording,
468
475
  recordingDuration,
469
- maxRecordingDuration,
470
476
  audioLevels,
471
477
  cooldownRemaining,
472
478
  placeholder = "Ask anything...",
@@ -490,9 +496,9 @@ function DefaultUI({
490
496
  "div",
491
497
  {
492
498
  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
499
+ bg-zinc-900 border rounded-xl
500
+ transition-all duration-300 ease-out
501
+ ${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
502
  ${disabled ? "opacity-50" : ""}
497
503
  `,
498
504
  children: [
@@ -512,26 +518,30 @@ function DefaultUI({
512
518
  disabled:cursor-not-allowed
513
519
  resize-none
514
520
  min-h-[56px]
521
+ transition-colors duration-200
515
522
  `,
516
523
  style: { height: "56px" }
517
524
  }
518
525
  ),
519
526
  /* @__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: [
527
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: isRecording ? /* @__PURE__ */ jsxs(Fragment, { children: [
521
528
  /* @__PURE__ */ jsx(
522
529
  "button",
523
530
  {
524
531
  onClick: cancelRecording,
525
532
  disabled,
526
- className: "p-2 text-zinc-400 hover:text-zinc-200 transition-colors",
533
+ className: "p-2 text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 rounded-lg transition-all duration-200 active:scale-95",
527
534
  "aria-label": "Cancel recording",
528
535
  children: /* @__PURE__ */ jsx(XIcon, { className: "h-5 w-5" })
529
536
  }
530
537
  ),
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 }),
538
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
539
+ /* @__PURE__ */ jsx(RecordingPulse, {}),
540
+ /* @__PURE__ */ jsx(Waveform, { levels: audioLevels })
541
+ ] }),
542
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-zinc-400 font-mono tabular-nums", children: formatDuration(recordingDuration) })
543
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "text-sm min-h-[28px] flex items-center", children: [
544
+ hasError && error && /* @__PURE__ */ jsx("span", { className: "text-red-400 animate-pulse", children: error.message }),
535
545
  isRateLimited && /* @__PURE__ */ jsxs("span", { className: "text-amber-400", children: [
536
546
  "Wait ",
537
547
  formatDuration(cooldownRemaining)
@@ -544,10 +554,12 @@ function DefaultUI({
544
554
  disabled,
545
555
  className: `
546
556
  p-2.5 rounded-full
547
- bg-red-500 hover:bg-red-600
557
+ bg-red-500 hover:bg-red-400
548
558
  text-white
549
- transition-colors
559
+ transition-all duration-200
560
+ hover:scale-105 active:scale-95
550
561
  disabled:opacity-50 disabled:cursor-not-allowed
562
+ shadow-lg shadow-red-500/25
551
563
  `,
552
564
  "aria-label": "Stop recording",
553
565
  children: /* @__PURE__ */ jsx(StopIcon, { className: "h-5 w-5" })
@@ -559,9 +571,12 @@ function DefaultUI({
559
571
  onClick: startRecording,
560
572
  disabled: disabled || isLoading || isRateLimited,
561
573
  className: `
562
- p-2 text-zinc-400 hover:text-zinc-200
563
- transition-colors
564
- disabled:opacity-50 disabled:cursor-not-allowed
574
+ p-2 text-zinc-400
575
+ hover:text-amber-400 hover:bg-zinc-800
576
+ rounded-lg
577
+ transition-all duration-200
578
+ hover:scale-105 active:scale-95
579
+ disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100
565
580
  `,
566
581
  "aria-label": "Start recording",
567
582
  children: /* @__PURE__ */ jsx(MicIcon, { className: "h-5 w-5" })
@@ -574,9 +589,9 @@ function DefaultUI({
574
589
  disabled: !canSubmit || disabled,
575
590
  className: `
576
591
  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"}
592
+ transition-all duration-200
593
+ disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100
594
+ ${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
595
  `,
581
596
  "aria-label": "Send message",
582
597
  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\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 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.2",
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",