ai-input-react 1.0.0-beta.3 → 1.0.0-beta.4
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.d.mts +5 -37
- package/dist/index.d.ts +5 -37
- package/dist/index.js +18 -65
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +18 -65
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -180,44 +180,12 @@ type AiInputMode = 'text' | 'audio';
|
|
|
180
180
|
/**
|
|
181
181
|
* AiInput Component
|
|
182
182
|
*
|
|
183
|
-
*
|
|
184
|
-
* Unified design with text input and audio recording in a single component.
|
|
183
|
+
* Text/audio input with automatic light/dark mode detection.
|
|
185
184
|
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* const response = await fetch('/api/chat', {
|
|
191
|
-
* method: 'POST',
|
|
192
|
-
* body: JSON.stringify({ message: input }),
|
|
193
|
-
* })
|
|
194
|
-
* return response.json()
|
|
195
|
-
* }}
|
|
196
|
-
* onSuccess={(result) => console.log(result)}
|
|
197
|
-
* />
|
|
198
|
-
*
|
|
199
|
-
* @example
|
|
200
|
-
* // With separate audio handler and transcription
|
|
201
|
-
* <AiInput
|
|
202
|
-
* send={sendTextFn}
|
|
203
|
-
* sendAudio={sendAudioFn}
|
|
204
|
-
* onTranscription={(text) => console.log('Transcribed:', text)}
|
|
205
|
-
* />
|
|
206
|
-
*
|
|
207
|
-
* @example
|
|
208
|
-
* // Headless mode with custom UI
|
|
209
|
-
* <AiInput send={sendFn}>
|
|
210
|
-
* {({ text, setText, submit, state, isRecording, audioLevels }) => (
|
|
211
|
-
* <div>
|
|
212
|
-
* {isRecording ? (
|
|
213
|
-
* <MyWaveform levels={audioLevels} />
|
|
214
|
-
* ) : (
|
|
215
|
-
* <input value={text} onChange={(e) => setText(e.target.value)} />
|
|
216
|
-
* )}
|
|
217
|
-
* <button onClick={submit}>Send</button>
|
|
218
|
-
* </div>
|
|
219
|
-
* )}
|
|
220
|
-
* </AiInput>
|
|
185
|
+
* **Theme Detection (in order of priority):**
|
|
186
|
+
* 1. `.dark` class on html/body (Tailwind/Next.js)
|
|
187
|
+
* 2. `data-theme="dark"` attribute
|
|
188
|
+
* 3. `prefers-color-scheme` system preference
|
|
221
189
|
*/
|
|
222
190
|
declare function AiInput({ send, sendAudio, rateLimit, audioConfig, onSuccess, onError, onTranscription, children, placeholder, className, disabled, }: AiInputProps): react_jsx_runtime.JSX.Element;
|
|
223
191
|
|
package/dist/index.d.ts
CHANGED
|
@@ -180,44 +180,12 @@ type AiInputMode = 'text' | 'audio';
|
|
|
180
180
|
/**
|
|
181
181
|
* AiInput Component
|
|
182
182
|
*
|
|
183
|
-
*
|
|
184
|
-
* Unified design with text input and audio recording in a single component.
|
|
183
|
+
* Text/audio input with automatic light/dark mode detection.
|
|
185
184
|
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* const response = await fetch('/api/chat', {
|
|
191
|
-
* method: 'POST',
|
|
192
|
-
* body: JSON.stringify({ message: input }),
|
|
193
|
-
* })
|
|
194
|
-
* return response.json()
|
|
195
|
-
* }}
|
|
196
|
-
* onSuccess={(result) => console.log(result)}
|
|
197
|
-
* />
|
|
198
|
-
*
|
|
199
|
-
* @example
|
|
200
|
-
* // With separate audio handler and transcription
|
|
201
|
-
* <AiInput
|
|
202
|
-
* send={sendTextFn}
|
|
203
|
-
* sendAudio={sendAudioFn}
|
|
204
|
-
* onTranscription={(text) => console.log('Transcribed:', text)}
|
|
205
|
-
* />
|
|
206
|
-
*
|
|
207
|
-
* @example
|
|
208
|
-
* // Headless mode with custom UI
|
|
209
|
-
* <AiInput send={sendFn}>
|
|
210
|
-
* {({ text, setText, submit, state, isRecording, audioLevels }) => (
|
|
211
|
-
* <div>
|
|
212
|
-
* {isRecording ? (
|
|
213
|
-
* <MyWaveform levels={audioLevels} />
|
|
214
|
-
* ) : (
|
|
215
|
-
* <input value={text} onChange={(e) => setText(e.target.value)} />
|
|
216
|
-
* )}
|
|
217
|
-
* <button onClick={submit}>Send</button>
|
|
218
|
-
* </div>
|
|
219
|
-
* )}
|
|
220
|
-
* </AiInput>
|
|
185
|
+
* **Theme Detection (in order of priority):**
|
|
186
|
+
* 1. `.dark` class on html/body (Tailwind/Next.js)
|
|
187
|
+
* 2. `data-theme="dark"` attribute
|
|
188
|
+
* 3. `prefers-color-scheme` system preference
|
|
221
189
|
*/
|
|
222
190
|
declare function AiInput({ send, sendAudio, rateLimit, audioConfig, onSuccess, onError, onTranscription, children, placeholder, className, disabled, }: AiInputProps): react_jsx_runtime.JSX.Element;
|
|
223
191
|
|
package/dist/index.js
CHANGED
|
@@ -444,25 +444,8 @@ function XIcon({ className = "" }) {
|
|
|
444
444
|
}
|
|
445
445
|
function Spinner({ className = "" }) {
|
|
446
446
|
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: `animate-spin ${className}`, viewBox: "0 0 24 24", fill: "none", children: [
|
|
447
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
448
|
-
|
|
449
|
-
{
|
|
450
|
-
className: "opacity-25",
|
|
451
|
-
cx: "12",
|
|
452
|
-
cy: "12",
|
|
453
|
-
r: "10",
|
|
454
|
-
stroke: "currentColor",
|
|
455
|
-
strokeWidth: "4"
|
|
456
|
-
}
|
|
457
|
-
),
|
|
458
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
459
|
-
"path",
|
|
460
|
-
{
|
|
461
|
-
className: "opacity-75",
|
|
462
|
-
fill: "currentColor",
|
|
463
|
-
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"
|
|
464
|
-
}
|
|
465
|
-
)
|
|
447
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
|
|
448
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { className: "opacity-75", fill: "currentColor", 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" })
|
|
466
449
|
] });
|
|
467
450
|
}
|
|
468
451
|
function DefaultUI({
|
|
@@ -496,13 +479,14 @@ function DefaultUI({
|
|
|
496
479
|
e.target.style.height = "auto";
|
|
497
480
|
e.target.style.height = `${Math.min(Math.max(e.target.scrollHeight, 56), 200)}px`;
|
|
498
481
|
};
|
|
499
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
482
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ai-input w-full", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
500
483
|
"div",
|
|
501
484
|
{
|
|
502
485
|
className: `
|
|
503
|
-
|
|
486
|
+
ai-input-container
|
|
487
|
+
border rounded-xl
|
|
504
488
|
transition-all duration-300 ease-out
|
|
505
|
-
${isRecording ? "
|
|
489
|
+
${isRecording ? "ai-input-recording" : ""}
|
|
506
490
|
${disabled ? "opacity-50" : ""}
|
|
507
491
|
`,
|
|
508
492
|
children: [
|
|
@@ -515,15 +499,7 @@ function DefaultUI({
|
|
|
515
499
|
placeholder: isRecording ? "Listening..." : placeholder,
|
|
516
500
|
disabled: disabled || isLoading || isRateLimited,
|
|
517
501
|
rows: 1,
|
|
518
|
-
className:
|
|
519
|
-
w-full px-4 pt-4 pb-2
|
|
520
|
-
bg-transparent text-zinc-100 placeholder:text-zinc-500
|
|
521
|
-
focus:outline-none
|
|
522
|
-
disabled:cursor-not-allowed
|
|
523
|
-
resize-none
|
|
524
|
-
min-h-[56px]
|
|
525
|
-
transition-colors duration-200
|
|
526
|
-
`,
|
|
502
|
+
className: "ai-input-textarea w-full px-4 pt-4 pb-2 bg-transparent focus:outline-none disabled:cursor-not-allowed resize-none min-h-[56px] transition-colors duration-200",
|
|
527
503
|
style: { height: "56px" }
|
|
528
504
|
}
|
|
529
505
|
),
|
|
@@ -534,7 +510,7 @@ function DefaultUI({
|
|
|
534
510
|
{
|
|
535
511
|
onClick: cancelRecording,
|
|
536
512
|
disabled,
|
|
537
|
-
className: "
|
|
513
|
+
className: "ai-input-btn-secondary p-2 rounded-lg transition-all duration-200 active:scale-95",
|
|
538
514
|
"aria-label": "Cancel recording",
|
|
539
515
|
children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, { className: "h-5 w-5" })
|
|
540
516
|
}
|
|
@@ -543,45 +519,30 @@ function DefaultUI({
|
|
|
543
519
|
/* @__PURE__ */ jsxRuntime.jsx(RecordingPulse, {}),
|
|
544
520
|
/* @__PURE__ */ jsxRuntime.jsx(Waveform, { levels: audioLevels })
|
|
545
521
|
] }),
|
|
546
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-
|
|
522
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ai-input-text-muted text-sm font-mono tabular-nums", children: formatDuration(recordingDuration) })
|
|
547
523
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm min-h-[28px] flex items-center", children: [
|
|
548
|
-
hasError && error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-
|
|
549
|
-
isRateLimited && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-
|
|
524
|
+
hasError && error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ai-input-text-error animate-pulse", children: error.message }),
|
|
525
|
+
isRateLimited && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ai-input-text-warning", children: [
|
|
550
526
|
"Wait ",
|
|
551
527
|
formatDuration(cooldownRemaining)
|
|
552
528
|
] })
|
|
553
529
|
] }) }),
|
|
554
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: isRecording ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
530
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: isRecording ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
555
531
|
"button",
|
|
556
532
|
{
|
|
557
533
|
onClick: stopRecording,
|
|
558
534
|
disabled,
|
|
559
|
-
className:
|
|
560
|
-
p-2.5 rounded-full
|
|
561
|
-
bg-red-500 hover:bg-red-400
|
|
562
|
-
text-white
|
|
563
|
-
transition-all duration-200
|
|
564
|
-
hover:scale-105 active:scale-95
|
|
565
|
-
disabled:opacity-50 disabled:cursor-not-allowed
|
|
566
|
-
shadow-lg shadow-red-500/25
|
|
567
|
-
`,
|
|
535
|
+
className: "p-2.5 rounded-full bg-red-500 hover:bg-red-400 text-white transition-all duration-200 hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed shadow-lg shadow-red-500/25",
|
|
568
536
|
"aria-label": "Stop recording",
|
|
569
537
|
children: /* @__PURE__ */ jsxRuntime.jsx(StopIcon, { className: "h-5 w-5" })
|
|
570
538
|
}
|
|
571
|
-
)
|
|
539
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
572
540
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
573
541
|
"button",
|
|
574
542
|
{
|
|
575
543
|
onClick: startRecording,
|
|
576
544
|
disabled: disabled || isLoading || isRateLimited,
|
|
577
|
-
className:
|
|
578
|
-
p-2 text-zinc-400
|
|
579
|
-
hover:text-amber-400 hover:bg-zinc-800
|
|
580
|
-
rounded-lg
|
|
581
|
-
transition-all duration-200
|
|
582
|
-
hover:scale-105 active:scale-95
|
|
583
|
-
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100
|
|
584
|
-
`,
|
|
545
|
+
className: "ai-input-btn-secondary p-2 rounded-lg transition-all duration-200 hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100",
|
|
585
546
|
"aria-label": "Start recording",
|
|
586
547
|
children: /* @__PURE__ */ jsxRuntime.jsx(MicIcon, { className: "h-5 w-5" })
|
|
587
548
|
}
|
|
@@ -592,10 +553,9 @@ function DefaultUI({
|
|
|
592
553
|
onClick: submit,
|
|
593
554
|
disabled: !canSubmit || disabled,
|
|
594
555
|
className: `
|
|
595
|
-
p-2.5 rounded-full
|
|
596
|
-
transition-all duration-200
|
|
556
|
+
p-2.5 rounded-full transition-all duration-200
|
|
597
557
|
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100
|
|
598
|
-
${canSubmit ? "
|
|
558
|
+
${canSubmit ? "ai-input-btn-primary hover:scale-105 active:scale-95 shadow-lg" : "ai-input-btn-disabled"}
|
|
599
559
|
`,
|
|
600
560
|
"aria-label": "Send message",
|
|
601
561
|
children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, { className: "h-5 w-5" }) : /* @__PURE__ */ jsxRuntime.jsx(ArrowUpIcon, { className: "h-5 w-5" })
|
|
@@ -632,14 +592,7 @@ function AiInput({
|
|
|
632
592
|
if (children) {
|
|
633
593
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children(inputState) });
|
|
634
594
|
}
|
|
635
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className || ""}`, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
636
|
-
DefaultUI,
|
|
637
|
-
{
|
|
638
|
-
...inputState,
|
|
639
|
-
placeholder,
|
|
640
|
-
disabled
|
|
641
|
-
}
|
|
642
|
-
) });
|
|
595
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className || ""}`, children: /* @__PURE__ */ jsxRuntime.jsx(DefaultUI, { ...inputState, placeholder, disabled }) });
|
|
643
596
|
}
|
|
644
597
|
|
|
645
598
|
exports.AiInput = AiInput;
|
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 - uses time domain for better voice visualization\r\n const updateAudioLevels = useCallback(() => {\r\n if (!analyserRef.current) return\r\n\r\n const analyser = analyserRef.current\r\n const bufferLength = analyser.fftSize\r\n const dataArray = new Uint8Array(bufferLength)\r\n\r\n // Use time domain data for even distribution across bars\r\n analyser.getByteTimeDomainData(dataArray)\r\n\r\n // Sample 16 bars from the waveform data\r\n const bars = 16\r\n const step = Math.floor(bufferLength / bars)\r\n const levels: number[] = []\r\n\r\n for (let i = 0; i < bars; i++) {\r\n // Get amplitude variation from center (128) for each segment\r\n let maxDeviation = 0\r\n for (let j = 0; j < step; j++) {\r\n const value = dataArray[i * step + j]\r\n const deviation = Math.abs(value - 128)\r\n maxDeviation = Math.max(maxDeviation, deviation)\r\n }\r\n // Normalize to 0-1 range (max deviation is 128)\r\n // Apply some amplification for better visibility\r\n levels.push(Math.min(1, (maxDeviation / 128) * 2.5))\r\n }\r\n\r\n setAudioLevels(levels)\r\n\r\n // Continue animation loop\r\n animationFrameRef.current = requestAnimationFrame(updateAudioLevels)\r\n }, [])\r\n\r\n // Cleanup function\r\n const cleanup = useCallback(() => {\r\n if (durationIntervalRef.current) {\r\n clearInterval(durationIntervalRef.current)\r\n durationIntervalRef.current = null\r\n }\r\n\r\n if (maxDurationTimeoutRef.current) {\r\n clearTimeout(maxDurationTimeoutRef.current)\r\n maxDurationTimeoutRef.current = null\r\n }\r\n\r\n if (animationFrameRef.current) {\r\n cancelAnimationFrame(animationFrameRef.current)\r\n animationFrameRef.current = null\r\n }\r\n\r\n if (audioContextRef.current) {\r\n audioContextRef.current.close()\r\n audioContextRef.current = null\r\n }\r\n\r\n if (streamRef.current) {\r\n streamRef.current.getTracks().forEach((track) => track.stop())\r\n streamRef.current = null\r\n }\r\n\r\n analyserRef.current = null\r\n mediaRecorderRef.current = null\r\n chunksRef.current = []\r\n setAudioLevels([])\r\n }, [])\r\n\r\n // Stop recording\r\n const stopRecording = useCallback(() => {\r\n if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {\r\n mediaRecorderRef.current.stop()\r\n }\r\n setIsRecording(false)\r\n }, [])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!isSupported) {\r\n setError(new Error('Audio recording is not supported in this browser'))\r\n return\r\n }\r\n\r\n // Reset state\r\n setError(null)\r\n setAudioBlob(null)\r\n setDuration(0)\r\n setAudioLevels([])\r\n chunksRef.current = []\r\n\r\n try {\r\n // Get microphone access\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\r\n streamRef.current = stream\r\n\r\n // Set up Web Audio API for visualization\r\n const audioContext = new AudioContext()\r\n audioContextRef.current = audioContext\r\n\r\n const source = audioContext.createMediaStreamSource(stream)\r\n const analyser = audioContext.createAnalyser()\r\n analyser.fftSize = 256\r\n analyser.smoothingTimeConstant = 0.8\r\n source.connect(analyser)\r\n analyserRef.current = analyser\r\n\r\n // Get supported MIME type\r\n const mimeType = getSupportedMimeType(config.mimeTypes)\r\n\r\n // Create MediaRecorder\r\n const mediaRecorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined)\r\n mediaRecorderRef.current = mediaRecorder\r\n\r\n // Handle data available\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n chunksRef.current.push(event.data)\r\n }\r\n }\r\n\r\n // Handle recording stop\r\n mediaRecorder.onstop = () => {\r\n const blob = new Blob(chunksRef.current, {\r\n type: mimeType || 'audio/webm'\r\n })\r\n setAudioBlob(blob)\r\n\r\n // Call callback if provided\r\n if (config.onRecordingComplete) {\r\n config.onRecordingComplete(blob)\r\n }\r\n\r\n cleanup()\r\n }\r\n\r\n // Handle errors\r\n mediaRecorder.onerror = () => {\r\n setError(new Error('Recording error occurred'))\r\n setIsRecording(false)\r\n cleanup()\r\n }\r\n\r\n // Start recording\r\n mediaRecorder.start(100) // Collect data every 100ms\r\n startTimeRef.current = Date.now()\r\n setIsRecording(true)\r\n\r\n // Start audio level visualization\r\n updateAudioLevels()\r\n\r\n // Update duration every 100ms\r\n durationIntervalRef.current = setInterval(() => {\r\n setDuration(Date.now() - startTimeRef.current)\r\n }, 100)\r\n\r\n // Auto-stop at max duration\r\n maxDurationTimeoutRef.current = setTimeout(() => {\r\n stopRecording()\r\n }, config.maxDurationMs)\r\n\r\n } catch (err) {\r\n const errorMessage = err instanceof Error\r\n ? err.message\r\n : 'Failed to access microphone'\r\n setError(new Error(errorMessage))\r\n cleanup()\r\n }\r\n }, [isSupported, config.mimeTypes, config.maxDurationMs, config.onRecordingComplete, cleanup, stopRecording, updateAudioLevels])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n }, [cleanup])\r\n\r\n // Reset hook state\r\n const reset = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n setError(null)\r\n setAudioLevels([])\r\n }, [cleanup])\r\n\r\n // Cleanup on unmount\r\n useEffect(() => {\r\n return () => {\r\n cleanup()\r\n }\r\n }, [cleanup])\r\n\r\n return {\r\n isRecording,\r\n isSupported,\r\n duration,\r\n audioBlob,\r\n audioLevels,\r\n error,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from 'react'\r\nimport { useRateLimiter } from './useRateLimiter'\r\nimport { useAudioRecorder } from './useAudioRecorder'\r\nimport type {\r\n UseAiInputOptions,\r\n UseAiInputReturn,\r\n AiInputState,\r\n RateLimitConfig,\r\n AudioConfig,\r\n} from '../types'\r\n\r\nconst DEFAULT_RATE_LIMIT: RateLimitConfig = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\nconst DEFAULT_AUDIO_CONFIG: AudioConfig = {\r\n maxDurationMs: 60000,\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Main hook for AI input functionality.\r\n * Combines rate limiting, audio recording, and API communication.\r\n * Unified design - text and audio in single component.\r\n * \r\n * @param options - Configuration options\r\n * @returns Complete state and controls for AI input\r\n */\r\nexport function useAiInput(options: UseAiInputOptions): UseAiInputReturn {\r\n const {\r\n send,\r\n sendAudio,\r\n rateLimit = {},\r\n audioConfig = {},\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n } = options\r\n\r\n const rateLimitConfig = { ...DEFAULT_RATE_LIMIT, ...rateLimit }\r\n const audioConfigMerged = { ...DEFAULT_AUDIO_CONFIG, ...audioConfig }\r\n\r\n // State\r\n const [state, setState] = useState<AiInputState>('idle')\r\n const [text, setText] = useState('')\r\n const [error, setError] = useState<Error | null>(null)\r\n const [result, setResult] = useState<unknown>(null)\r\n\r\n // Ref to track if we're waiting to submit audio after recording stops\r\n const pendingAudioSubmitRef = useRef(false)\r\n\r\n // Rate limiter\r\n const rateLimiter = useRateLimiter(rateLimitConfig)\r\n\r\n // Audio recorder\r\n const audioRecorder = useAudioRecorder({\r\n ...audioConfigMerged,\r\n })\r\n\r\n // Update state based on rate limiter\r\n useEffect(() => {\r\n if (!rateLimiter.canRequest && state === 'idle') {\r\n setState('rate-limited')\r\n } else if (rateLimiter.canRequest && state === 'rate-limited') {\r\n setState('idle')\r\n }\r\n }, [rateLimiter.canRequest, state])\r\n\r\n // Update state when recording\r\n useEffect(() => {\r\n if (audioRecorder.isRecording && state !== 'recording') {\r\n setState('recording')\r\n }\r\n }, [audioRecorder.isRecording, state])\r\n\r\n // Handle audio recorder errors\r\n useEffect(() => {\r\n if (audioRecorder.error) {\r\n setError(audioRecorder.error)\r\n setState('error')\r\n onError?.(audioRecorder.error)\r\n }\r\n }, [audioRecorder.error, onError])\r\n\r\n // Submit text\r\n const submitText = useCallback(async () => {\r\n if (!text.trim() || !rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n const response = await send(text)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n // Clear text after successful send\r\n setText('')\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [text, rateLimiter, send, onSuccess, onError])\r\n\r\n // Submit audio\r\n const submitAudio = useCallback(async (blob: Blob) => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n // Use sendAudio if provided, otherwise use send\r\n const sendFn = sendAudio || send\r\n const response = await sendFn(blob)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n\r\n // Handle transcription if callback provided\r\n if (onTranscription && response && typeof response === 'object') {\r\n const res = response as Record<string, unknown>\r\n // Try common transcription response formats\r\n const transcriptionText = res.text || res.transcription || res.transcript\r\n if (typeof transcriptionText === 'string') {\r\n setText(transcriptionText)\r\n onTranscription(transcriptionText)\r\n }\r\n }\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [rateLimiter, send, sendAudio, onSuccess, onError, onTranscription])\r\n\r\n // Handle audio blob ready - submit if we were waiting\r\n useEffect(() => {\r\n if (pendingAudioSubmitRef.current && audioRecorder.audioBlob && !audioRecorder.isRecording) {\r\n pendingAudioSubmitRef.current = false\r\n submitAudio(audioRecorder.audioBlob)\r\n }\r\n }, [audioRecorder.audioBlob, audioRecorder.isRecording, submitAudio])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n await audioRecorder.startRecording()\r\n }, [rateLimiter.canRequest, audioRecorder])\r\n\r\n // Stop recording and submit\r\n const stopRecording = useCallback(() => {\r\n // Mark that we want to submit audio when blob is ready\r\n pendingAudioSubmitRef.current = true\r\n audioRecorder.stopRecording()\r\n }, [audioRecorder])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n audioRecorder.cancelRecording()\r\n setState('idle')\r\n }, [audioRecorder])\r\n\r\n // Submit based on current state\r\n const submit = useCallback(() => {\r\n if (audioRecorder.isRecording) {\r\n stopRecording()\r\n } else if (text.trim()) {\r\n submitText()\r\n }\r\n }, [audioRecorder.isRecording, text, stopRecording, submitText])\r\n\r\n // Reset all state\r\n const reset = useCallback(() => {\r\n setState('idle')\r\n setText('')\r\n setError(null)\r\n setResult(null)\r\n rateLimiter.reset()\r\n audioRecorder.reset()\r\n }, [rateLimiter, audioRecorder])\r\n\r\n // Can submit check\r\n const canSubmit =\r\n rateLimiter.canRequest &&\r\n state !== 'loading' &&\r\n (audioRecorder.isRecording || text.trim().length > 0)\r\n\r\n return {\r\n // State\r\n state,\r\n error,\r\n result,\r\n\r\n // Text\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n\r\n // Audio\r\n isRecording: audioRecorder.isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration: audioRecorder.duration,\r\n maxRecordingDuration: audioConfigMerged.maxDurationMs,\r\n audioLevels: audioRecorder.audioLevels,\r\n\r\n // Rate limiting\r\n cooldownRemaining: rateLimiter.cooldownRemaining,\r\n requestsRemaining: rateLimiter.requestsRemaining,\r\n\r\n // Utils\r\n reset,\r\n }\r\n}\r\n","import React from 'react'\r\nimport { useAiInput } from '../hooks/useAiInput'\r\nimport type { AiInputProps, AiInputRenderProps } from '../types'\r\n\r\n/**\r\n * Format milliseconds to MM:SS display\r\n */\r\nfunction formatDuration(ms: number): string {\r\n const seconds = Math.floor(ms / 1000)\r\n const minutes = Math.floor(seconds / 60)\r\n const remainingSeconds = seconds % 60\r\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`\r\n}\r\n\r\n/**\r\n * Waveform visualization component with smooth animations\r\n */\r\nfunction Waveform({ levels, className = '' }: { levels: number[]; className?: string }) {\r\n // Generate 16 bars if no levels provided\r\n const bars = levels.length > 0 ? levels : Array(16).fill(0.15)\r\n\r\n return (\r\n <div className={`flex items-center justify-center gap-1 h-10 ${className}`}>\r\n {bars.map((level, i) => (\r\n <div\r\n key={i}\r\n className=\"w-1.5 bg-gradient-to-t from-amber-600 to-amber-400 rounded-full transition-all duration-100 ease-out\"\r\n style={{\r\n height: `${Math.max(6, level * 40)}px`,\r\n opacity: 0.6 + level * 0.4,\r\n }}\r\n />\r\n ))}\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Recording pulse indicator\r\n */\r\nfunction RecordingPulse() {\r\n return (\r\n <span className=\"relative flex h-3 w-3 mr-2\">\r\n <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75\"></span>\r\n <span className=\"relative inline-flex rounded-full h-3 w-3 bg-red-500\"></span>\r\n </span>\r\n )\r\n}\r\n\r\n/**\r\n * Microphone icon (Phosphor style)\r\n */\r\nfunction MicIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M128,176a48.05,48.05,0,0,0,48-48V64a48,48,0,0,0-96,0v64A48.05,48.05,0,0,0,128,176ZM96,64a32,32,0,0,1,64,0v64a32,32,0,0,1-64,0Zm40,143.6V232a8,8,0,0,1-16,0V207.6A80.11,80.11,0,0,1,48,128a8,8,0,0,1,16,0,64,64,0,0,0,128,0,8,8,0,0,1,16,0A80.11,80.11,0,0,1,136,207.6Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Arrow up icon for submit\r\n */\r\nfunction ArrowUpIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,117.66a8,8,0,0,1-11.32,0L136,59.31V216a8,8,0,0,1-16,0V59.31L61.66,117.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,205.66,117.66Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Stop icon (filled square)\r\n */\r\nfunction StopIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <rect x=\"64\" y=\"64\" width=\"128\" height=\"128\" rx=\"8\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * X icon for cancel\r\n */\r\nfunction XIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Spinner for loading state\r\n */\r\nfunction Spinner({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={`animate-spin ${className}`} viewBox=\"0 0 24 24\" fill=\"none\">\r\n <circle\r\n className=\"opacity-25\"\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"4\"\r\n />\r\n <path\r\n className=\"opacity-75\"\r\n fill=\"currentColor\"\r\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\r\n />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Default UI for the unified AiInput component\r\n */\r\nfunction DefaultUI({\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n state,\r\n error,\r\n isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration,\r\n audioLevels,\r\n cooldownRemaining,\r\n placeholder = 'Ask anything...',\r\n disabled = false,\r\n}: AiInputRenderProps & {\r\n placeholder?: string\r\n disabled?: boolean\r\n}) {\r\n const isLoading = state === 'loading'\r\n const isRateLimited = state === 'rate-limited'\r\n const hasError = state === 'error'\r\n\r\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\r\n if (e.key === 'Enter' && !e.shiftKey && canSubmit && !isRecording) {\r\n e.preventDefault()\r\n submit()\r\n }\r\n }\r\n\r\n // Auto-resize textarea\r\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\r\n setText(e.target.value)\r\n // Reset height to auto to get the correct scrollHeight\r\n e.target.style.height = 'auto'\r\n // Set height to scrollHeight, with min and max constraints\r\n e.target.style.height = `${Math.min(Math.max(e.target.scrollHeight, 56), 200)}px`\r\n }\r\n\r\n return (\r\n <div className=\"w-full\">\r\n {/* Main container */}\r\n <div\r\n className={`\r\n bg-zinc-900 border rounded-xl\r\n transition-all duration-300 ease-out\r\n ${isRecording\r\n ? 'border-red-500/50 shadow-lg shadow-red-500/10'\r\n : 'border-zinc-800 focus-within:border-amber-500/50 focus-within:shadow-lg focus-within:shadow-amber-500/5'\r\n }\r\n ${disabled ? 'opacity-50' : ''}\r\n `}\r\n >\r\n {/* Text input area - always visible for live transcription */}\r\n <textarea\r\n value={text}\r\n onChange={handleInput}\r\n onKeyDown={handleKeyDown}\r\n placeholder={isRecording ? 'Listening...' : placeholder}\r\n disabled={disabled || isLoading || isRateLimited}\r\n rows={1}\r\n className={`\r\n w-full px-4 pt-4 pb-2\r\n bg-transparent text-zinc-100 placeholder:text-zinc-500\r\n focus:outline-none\r\n disabled:cursor-not-allowed\r\n resize-none\r\n min-h-[56px]\r\n transition-colors duration-200\r\n `}\r\n style={{ height: '56px' }}\r\n />\r\n\r\n {/* Toolbar */}\r\n <div className=\"flex items-center justify-between px-3 pb-3 pt-1\">\r\n {/* Left side - error/status or waveform during recording */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n {/* Cancel recording */}\r\n <button\r\n onClick={cancelRecording}\r\n disabled={disabled}\r\n className=\"p-2 text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 rounded-lg transition-all duration-200 active:scale-95\"\r\n aria-label=\"Cancel recording\"\r\n >\r\n <XIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Recording indicator + Waveform */}\r\n <div className=\"flex items-center\">\r\n <RecordingPulse />\r\n <Waveform levels={audioLevels} />\r\n </div>\r\n\r\n {/* Timer */}\r\n <span className=\"text-sm text-zinc-400 font-mono tabular-nums\">\r\n {formatDuration(recordingDuration)}\r\n </span>\r\n </>\r\n ) : (\r\n <div className=\"text-sm min-h-[28px] flex items-center\">\r\n {hasError && error && (\r\n <span className=\"text-red-400 animate-pulse\">{error.message}</span>\r\n )}\r\n {isRateLimited && (\r\n <span className=\"text-amber-400\">\r\n Wait {formatDuration(cooldownRemaining)}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Right side - action buttons */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n {/* Stop and send */}\r\n <button\r\n onClick={stopRecording}\r\n disabled={disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n bg-red-500 hover:bg-red-400\r\n text-white\r\n transition-all duration-200\r\n hover:scale-105 active:scale-95\r\n disabled:opacity-50 disabled:cursor-not-allowed\r\n shadow-lg shadow-red-500/25\r\n `}\r\n aria-label=\"Stop recording\"\r\n >\r\n <StopIcon className=\"h-5 w-5\" />\r\n </button>\r\n </>\r\n ) : (\r\n <>\r\n {/* Mic button */}\r\n <button\r\n onClick={startRecording}\r\n disabled={disabled || isLoading || isRateLimited}\r\n className={`\r\n p-2 text-zinc-400 \r\n hover:text-amber-400 hover:bg-zinc-800\r\n rounded-lg\r\n transition-all duration-200\r\n hover:scale-105 active:scale-95\r\n disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\r\n `}\r\n aria-label=\"Start recording\"\r\n >\r\n <MicIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Submit button */}\r\n <button\r\n onClick={submit}\r\n disabled={!canSubmit || disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n transition-all duration-200\r\n disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\r\n ${canSubmit\r\n ? 'bg-amber-500 hover:bg-amber-400 text-zinc-900 hover:scale-105 active:scale-95 shadow-lg shadow-amber-500/25'\r\n : 'bg-zinc-800 text-zinc-500'\r\n }\r\n `}\r\n aria-label=\"Send message\"\r\n >\r\n {isLoading ? (\r\n <Spinner className=\"h-5 w-5\" />\r\n ) : (\r\n <ArrowUpIcon className=\"h-5 w-5\" />\r\n )}\r\n </button>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * AiInput Component\r\n * \r\n * A React component for text/audio input with AI API integration.\r\n * Unified design with text input and audio recording in a single component.\r\n * \r\n * @example\r\n * // Basic usage\r\n * <AiInput\r\n * send={async (input) => {\r\n * const response = await fetch('/api/chat', {\r\n * method: 'POST',\r\n * body: JSON.stringify({ message: input }),\r\n * })\r\n * return response.json()\r\n * }}\r\n * onSuccess={(result) => console.log(result)}\r\n * />\r\n * \r\n * @example\r\n * // With separate audio handler and transcription\r\n * <AiInput\r\n * send={sendTextFn}\r\n * sendAudio={sendAudioFn}\r\n * onTranscription={(text) => console.log('Transcribed:', text)}\r\n * />\r\n * \r\n * @example\r\n * // Headless mode with custom UI\r\n * <AiInput send={sendFn}>\r\n * {({ text, setText, submit, state, isRecording, audioLevels }) => (\r\n * <div>\r\n * {isRecording ? (\r\n * <MyWaveform levels={audioLevels} />\r\n * ) : (\r\n * <input value={text} onChange={(e) => setText(e.target.value)} />\r\n * )}\r\n * <button onClick={submit}>Send</button>\r\n * </div>\r\n * )}\r\n * </AiInput>\r\n */\r\nexport function AiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n children,\r\n placeholder,\r\n className,\r\n disabled = false,\r\n}: AiInputProps) {\r\n const inputState = useAiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n })\r\n\r\n // Headless mode - render prop\r\n if (children) {\r\n return <>{children(inputState)}</>\r\n }\r\n\r\n // Default UI\r\n return (\r\n <div className={`w-full ${className || ''}`}>\r\n <DefaultUI\r\n {...inputState}\r\n placeholder={placeholder}\r\n disabled={disabled}\r\n />\r\n </div>\r\n )\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useRateLimiter.ts","../src/hooks/useAudioRecorder.ts","../src/hooks/useAiInput.ts","../src/components/AiInput.tsx"],"names":["useRef","useState","useCallback","useEffect","DEFAULT_OPTIONS","error","jsx","jsxs","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseRateLimiterOptions, UseRateLimiterReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseRateLimiterOptions = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\n/**\r\n * Hook for soft rate limiting at the UI level.\r\n * Provides UX protection by tracking requests and enforcing cooldowns.\r\n * \r\n * Note: This is not a security measure. Actual rate limiting \r\n * should be handled by the AI provider or backend.\r\n * \r\n * @param options - Rate limiting configuration\r\n * @returns Rate limiter state and controls\r\n */\r\nexport function useRateLimiter(\r\n options: Partial<UseRateLimiterOptions> = {}\r\n): UseRateLimiterReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n // Track request timestamps within the window\r\n const requestTimestamps = useRef<number[]>([])\r\n\r\n // Cooldown state\r\n const [cooldownEnd, setCooldownEnd] = useState<number>(0)\r\n const [cooldownRemaining, setCooldownRemaining] = useState<number>(0)\r\n\r\n // Force re-render for requestsRemaining updates\r\n const [, forceUpdate] = useState({})\r\n\r\n // Cleanup old timestamps and calculate remaining requests\r\n const cleanupAndCount = useCallback(() => {\r\n const now = Date.now()\r\n const windowStart = now - config.windowMs\r\n\r\n // Remove timestamps outside the window\r\n requestTimestamps.current = requestTimestamps.current.filter(\r\n (ts) => ts > windowStart\r\n )\r\n\r\n return config.maxRequests - requestTimestamps.current.length\r\n }, [config.windowMs, config.maxRequests])\r\n\r\n // Update cooldown remaining\r\n useEffect(() => {\r\n if (cooldownEnd <= Date.now()) {\r\n setCooldownRemaining(0)\r\n return\r\n }\r\n\r\n const interval = setInterval(() => {\r\n const remaining = Math.max(0, cooldownEnd - Date.now())\r\n setCooldownRemaining(remaining)\r\n\r\n if (remaining === 0) {\r\n clearInterval(interval)\r\n }\r\n }, 100)\r\n\r\n return () => clearInterval(interval)\r\n }, [cooldownEnd])\r\n\r\n // Check if request is allowed\r\n const canRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Check cooldown\r\n if (now < cooldownEnd) {\r\n return false\r\n }\r\n\r\n // Check request count\r\n return cleanupAndCount() > 0\r\n }, [cooldownEnd, cleanupAndCount])\r\n\r\n // Record a request\r\n const recordRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Add timestamp\r\n requestTimestamps.current.push(now)\r\n\r\n // Start cooldown\r\n const newCooldownEnd = now + config.cooldownMs\r\n setCooldownEnd(newCooldownEnd)\r\n setCooldownRemaining(config.cooldownMs)\r\n\r\n // Trigger re-render\r\n forceUpdate({})\r\n }, [config.cooldownMs])\r\n\r\n // Reset rate limiter\r\n const reset = useCallback(() => {\r\n requestTimestamps.current = []\r\n setCooldownEnd(0)\r\n setCooldownRemaining(0)\r\n forceUpdate({})\r\n }, [])\r\n\r\n return {\r\n canRequest: canRequest(),\r\n cooldownRemaining,\r\n requestsRemaining: cleanupAndCount(),\r\n recordRequest,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseAudioRecorderOptions, UseAudioRecorderReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseAudioRecorderOptions = {\r\n maxDurationMs: 60000, // 1 minute\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Get the best supported MIME type for MediaRecorder\r\n */\r\nfunction getSupportedMimeType(preferredTypes: string[]): string | null {\r\n if (typeof MediaRecorder === 'undefined') {\r\n return null\r\n }\r\n\r\n for (const mimeType of preferredTypes) {\r\n if (MediaRecorder.isTypeSupported(mimeType)) {\r\n return mimeType\r\n }\r\n }\r\n\r\n // Fallback to default\r\n return ''\r\n}\r\n\r\n/**\r\n * Hook for audio recording using Web APIs.\r\n * Uses navigator.mediaDevices, MediaRecorder, and Web Audio API for visualization.\r\n * \r\n * @param options - Audio recording configuration\r\n * @returns Audio recorder state and controls\r\n */\r\nexport function useAudioRecorder(\r\n options: Partial<UseAudioRecorderOptions> = {}\r\n): UseAudioRecorderReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n const [isRecording, setIsRecording] = useState(false)\r\n const [duration, setDuration] = useState(0)\r\n const [audioBlob, setAudioBlob] = useState<Blob | null>(null)\r\n const [error, setError] = useState<Error | null>(null)\r\n const [audioLevels, setAudioLevels] = useState<number[]>([])\r\n\r\n const mediaRecorderRef = useRef<MediaRecorder | null>(null)\r\n const streamRef = useRef<MediaStream | null>(null)\r\n const chunksRef = useRef<Blob[]>([])\r\n const startTimeRef = useRef<number>(0)\r\n const durationIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\r\n const maxDurationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\r\n\r\n // Web Audio API refs for visualization\r\n const audioContextRef = useRef<AudioContext | null>(null)\r\n const analyserRef = useRef<AnalyserNode | null>(null)\r\n const animationFrameRef = useRef<number | null>(null)\r\n\r\n // Check if audio recording is supported\r\n const isSupported = typeof navigator !== 'undefined'\r\n && 'mediaDevices' in navigator\r\n && 'getUserMedia' in navigator.mediaDevices\r\n && typeof MediaRecorder !== 'undefined'\r\n\r\n // Update audio levels from analyser - uses time domain for better voice visualization\r\n const updateAudioLevels = useCallback(() => {\r\n if (!analyserRef.current) return\r\n\r\n const analyser = analyserRef.current\r\n const bufferLength = analyser.fftSize\r\n const dataArray = new Uint8Array(bufferLength)\r\n\r\n // Use time domain data for even distribution across bars\r\n analyser.getByteTimeDomainData(dataArray)\r\n\r\n // Sample 16 bars from the waveform data\r\n const bars = 16\r\n const step = Math.floor(bufferLength / bars)\r\n const levels: number[] = []\r\n\r\n for (let i = 0; i < bars; i++) {\r\n // Get amplitude variation from center (128) for each segment\r\n let maxDeviation = 0\r\n for (let j = 0; j < step; j++) {\r\n const value = dataArray[i * step + j]\r\n const deviation = Math.abs(value - 128)\r\n maxDeviation = Math.max(maxDeviation, deviation)\r\n }\r\n // Normalize to 0-1 range (max deviation is 128)\r\n // Apply some amplification for better visibility\r\n levels.push(Math.min(1, (maxDeviation / 128) * 2.5))\r\n }\r\n\r\n setAudioLevels(levels)\r\n\r\n // Continue animation loop\r\n animationFrameRef.current = requestAnimationFrame(updateAudioLevels)\r\n }, [])\r\n\r\n // Cleanup function\r\n const cleanup = useCallback(() => {\r\n if (durationIntervalRef.current) {\r\n clearInterval(durationIntervalRef.current)\r\n durationIntervalRef.current = null\r\n }\r\n\r\n if (maxDurationTimeoutRef.current) {\r\n clearTimeout(maxDurationTimeoutRef.current)\r\n maxDurationTimeoutRef.current = null\r\n }\r\n\r\n if (animationFrameRef.current) {\r\n cancelAnimationFrame(animationFrameRef.current)\r\n animationFrameRef.current = null\r\n }\r\n\r\n if (audioContextRef.current) {\r\n audioContextRef.current.close()\r\n audioContextRef.current = null\r\n }\r\n\r\n if (streamRef.current) {\r\n streamRef.current.getTracks().forEach((track) => track.stop())\r\n streamRef.current = null\r\n }\r\n\r\n analyserRef.current = null\r\n mediaRecorderRef.current = null\r\n chunksRef.current = []\r\n setAudioLevels([])\r\n }, [])\r\n\r\n // Stop recording\r\n const stopRecording = useCallback(() => {\r\n if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {\r\n mediaRecorderRef.current.stop()\r\n }\r\n setIsRecording(false)\r\n }, [])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!isSupported) {\r\n setError(new Error('Audio recording is not supported in this browser'))\r\n return\r\n }\r\n\r\n // Reset state\r\n setError(null)\r\n setAudioBlob(null)\r\n setDuration(0)\r\n setAudioLevels([])\r\n chunksRef.current = []\r\n\r\n try {\r\n // Get microphone access\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\r\n streamRef.current = stream\r\n\r\n // Set up Web Audio API for visualization\r\n const audioContext = new AudioContext()\r\n audioContextRef.current = audioContext\r\n\r\n const source = audioContext.createMediaStreamSource(stream)\r\n const analyser = audioContext.createAnalyser()\r\n analyser.fftSize = 256\r\n analyser.smoothingTimeConstant = 0.8\r\n source.connect(analyser)\r\n analyserRef.current = analyser\r\n\r\n // Get supported MIME type\r\n const mimeType = getSupportedMimeType(config.mimeTypes)\r\n\r\n // Create MediaRecorder\r\n const mediaRecorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined)\r\n mediaRecorderRef.current = mediaRecorder\r\n\r\n // Handle data available\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n chunksRef.current.push(event.data)\r\n }\r\n }\r\n\r\n // Handle recording stop\r\n mediaRecorder.onstop = () => {\r\n const blob = new Blob(chunksRef.current, {\r\n type: mimeType || 'audio/webm'\r\n })\r\n setAudioBlob(blob)\r\n\r\n // Call callback if provided\r\n if (config.onRecordingComplete) {\r\n config.onRecordingComplete(blob)\r\n }\r\n\r\n cleanup()\r\n }\r\n\r\n // Handle errors\r\n mediaRecorder.onerror = () => {\r\n setError(new Error('Recording error occurred'))\r\n setIsRecording(false)\r\n cleanup()\r\n }\r\n\r\n // Start recording\r\n mediaRecorder.start(100) // Collect data every 100ms\r\n startTimeRef.current = Date.now()\r\n setIsRecording(true)\r\n\r\n // Start audio level visualization\r\n updateAudioLevels()\r\n\r\n // Update duration every 100ms\r\n durationIntervalRef.current = setInterval(() => {\r\n setDuration(Date.now() - startTimeRef.current)\r\n }, 100)\r\n\r\n // Auto-stop at max duration\r\n maxDurationTimeoutRef.current = setTimeout(() => {\r\n stopRecording()\r\n }, config.maxDurationMs)\r\n\r\n } catch (err) {\r\n const errorMessage = err instanceof Error\r\n ? err.message\r\n : 'Failed to access microphone'\r\n setError(new Error(errorMessage))\r\n cleanup()\r\n }\r\n }, [isSupported, config.mimeTypes, config.maxDurationMs, config.onRecordingComplete, cleanup, stopRecording, updateAudioLevels])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n }, [cleanup])\r\n\r\n // Reset hook state\r\n const reset = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n setError(null)\r\n setAudioLevels([])\r\n }, [cleanup])\r\n\r\n // Cleanup on unmount\r\n useEffect(() => {\r\n return () => {\r\n cleanup()\r\n }\r\n }, [cleanup])\r\n\r\n return {\r\n isRecording,\r\n isSupported,\r\n duration,\r\n audioBlob,\r\n audioLevels,\r\n error,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from 'react'\r\nimport { useRateLimiter } from './useRateLimiter'\r\nimport { useAudioRecorder } from './useAudioRecorder'\r\nimport type {\r\n UseAiInputOptions,\r\n UseAiInputReturn,\r\n AiInputState,\r\n RateLimitConfig,\r\n AudioConfig,\r\n} from '../types'\r\n\r\nconst DEFAULT_RATE_LIMIT: RateLimitConfig = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\nconst DEFAULT_AUDIO_CONFIG: AudioConfig = {\r\n maxDurationMs: 60000,\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Main hook for AI input functionality.\r\n * Combines rate limiting, audio recording, and API communication.\r\n * Unified design - text and audio in single component.\r\n * \r\n * @param options - Configuration options\r\n * @returns Complete state and controls for AI input\r\n */\r\nexport function useAiInput(options: UseAiInputOptions): UseAiInputReturn {\r\n const {\r\n send,\r\n sendAudio,\r\n rateLimit = {},\r\n audioConfig = {},\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n } = options\r\n\r\n const rateLimitConfig = { ...DEFAULT_RATE_LIMIT, ...rateLimit }\r\n const audioConfigMerged = { ...DEFAULT_AUDIO_CONFIG, ...audioConfig }\r\n\r\n // State\r\n const [state, setState] = useState<AiInputState>('idle')\r\n const [text, setText] = useState('')\r\n const [error, setError] = useState<Error | null>(null)\r\n const [result, setResult] = useState<unknown>(null)\r\n\r\n // Ref to track if we're waiting to submit audio after recording stops\r\n const pendingAudioSubmitRef = useRef(false)\r\n\r\n // Rate limiter\r\n const rateLimiter = useRateLimiter(rateLimitConfig)\r\n\r\n // Audio recorder\r\n const audioRecorder = useAudioRecorder({\r\n ...audioConfigMerged,\r\n })\r\n\r\n // Update state based on rate limiter\r\n useEffect(() => {\r\n if (!rateLimiter.canRequest && state === 'idle') {\r\n setState('rate-limited')\r\n } else if (rateLimiter.canRequest && state === 'rate-limited') {\r\n setState('idle')\r\n }\r\n }, [rateLimiter.canRequest, state])\r\n\r\n // Update state when recording\r\n useEffect(() => {\r\n if (audioRecorder.isRecording && state !== 'recording') {\r\n setState('recording')\r\n }\r\n }, [audioRecorder.isRecording, state])\r\n\r\n // Handle audio recorder errors\r\n useEffect(() => {\r\n if (audioRecorder.error) {\r\n setError(audioRecorder.error)\r\n setState('error')\r\n onError?.(audioRecorder.error)\r\n }\r\n }, [audioRecorder.error, onError])\r\n\r\n // Submit text\r\n const submitText = useCallback(async () => {\r\n if (!text.trim() || !rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n const response = await send(text)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n // Clear text after successful send\r\n setText('')\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [text, rateLimiter, send, onSuccess, onError])\r\n\r\n // Submit audio\r\n const submitAudio = useCallback(async (blob: Blob) => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n // Use sendAudio if provided, otherwise use send\r\n const sendFn = sendAudio || send\r\n const response = await sendFn(blob)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n\r\n // Handle transcription if callback provided\r\n if (onTranscription && response && typeof response === 'object') {\r\n const res = response as Record<string, unknown>\r\n // Try common transcription response formats\r\n const transcriptionText = res.text || res.transcription || res.transcript\r\n if (typeof transcriptionText === 'string') {\r\n setText(transcriptionText)\r\n onTranscription(transcriptionText)\r\n }\r\n }\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [rateLimiter, send, sendAudio, onSuccess, onError, onTranscription])\r\n\r\n // Handle audio blob ready - submit if we were waiting\r\n useEffect(() => {\r\n if (pendingAudioSubmitRef.current && audioRecorder.audioBlob && !audioRecorder.isRecording) {\r\n pendingAudioSubmitRef.current = false\r\n submitAudio(audioRecorder.audioBlob)\r\n }\r\n }, [audioRecorder.audioBlob, audioRecorder.isRecording, submitAudio])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n await audioRecorder.startRecording()\r\n }, [rateLimiter.canRequest, audioRecorder])\r\n\r\n // Stop recording and submit\r\n const stopRecording = useCallback(() => {\r\n // Mark that we want to submit audio when blob is ready\r\n pendingAudioSubmitRef.current = true\r\n audioRecorder.stopRecording()\r\n }, [audioRecorder])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n audioRecorder.cancelRecording()\r\n setState('idle')\r\n }, [audioRecorder])\r\n\r\n // Submit based on current state\r\n const submit = useCallback(() => {\r\n if (audioRecorder.isRecording) {\r\n stopRecording()\r\n } else if (text.trim()) {\r\n submitText()\r\n }\r\n }, [audioRecorder.isRecording, text, stopRecording, submitText])\r\n\r\n // Reset all state\r\n const reset = useCallback(() => {\r\n setState('idle')\r\n setText('')\r\n setError(null)\r\n setResult(null)\r\n rateLimiter.reset()\r\n audioRecorder.reset()\r\n }, [rateLimiter, audioRecorder])\r\n\r\n // Can submit check\r\n const canSubmit =\r\n rateLimiter.canRequest &&\r\n state !== 'loading' &&\r\n (audioRecorder.isRecording || text.trim().length > 0)\r\n\r\n return {\r\n // State\r\n state,\r\n error,\r\n result,\r\n\r\n // Text\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n\r\n // Audio\r\n isRecording: audioRecorder.isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration: audioRecorder.duration,\r\n maxRecordingDuration: audioConfigMerged.maxDurationMs,\r\n audioLevels: audioRecorder.audioLevels,\r\n\r\n // Rate limiting\r\n cooldownRemaining: rateLimiter.cooldownRemaining,\r\n requestsRemaining: rateLimiter.requestsRemaining,\r\n\r\n // Utils\r\n reset,\r\n }\r\n}\r\n","import React from 'react'\r\nimport { useAiInput } from '../hooks/useAiInput'\r\nimport type { AiInputProps, AiInputRenderProps } from '../types'\r\n\r\n/**\r\n * Format milliseconds to MM:SS display\r\n */\r\nfunction formatDuration(ms: number): string {\r\n const seconds = Math.floor(ms / 1000)\r\n const minutes = Math.floor(seconds / 60)\r\n const remainingSeconds = seconds % 60\r\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`\r\n}\r\n\r\n/**\r\n * Waveform visualization component with smooth animations\r\n */\r\nfunction Waveform({ levels, className = '' }: { levels: number[]; className?: string }) {\r\n 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\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\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 <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\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\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 className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\r\n <path className=\"opacity-75\" fill=\"currentColor\" 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 </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Default UI - uses CSS variables for automatic theme detection\r\n * The CSS variables are defined in styles.css and automatically switch\r\n * based on prefers-color-scheme, .dark class, or data-theme attribute\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 const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\r\n setText(e.target.value)\r\n e.target.style.height = 'auto'\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=\"ai-input w-full\">\r\n {/* Main container - uses CSS variables for theming */}\r\n <div\r\n className={`\r\n ai-input-container\r\n border rounded-xl\r\n transition-all duration-300 ease-out\r\n ${isRecording ? 'ai-input-recording' : ''}\r\n ${disabled ? 'opacity-50' : ''}\r\n `}\r\n >\r\n {/* Text input */}\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=\"ai-input-textarea w-full px-4 pt-4 pb-2 bg-transparent focus:outline-none disabled:cursor-not-allowed resize-none min-h-[56px] transition-colors duration-200\"\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 */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n <button\r\n onClick={cancelRecording}\r\n disabled={disabled}\r\n className=\"ai-input-btn-secondary p-2 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 <div className=\"flex items-center\">\r\n <RecordingPulse />\r\n <Waveform levels={audioLevels} />\r\n </div>\r\n <span className=\"ai-input-text-muted text-sm 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=\"ai-input-text-error animate-pulse\">{error.message}</span>\r\n )}\r\n {isRateLimited && (\r\n <span className=\"ai-input-text-warning\">\r\n Wait {formatDuration(cooldownRemaining)}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Right side */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <button\r\n onClick={stopRecording}\r\n disabled={disabled}\r\n className=\"p-2.5 rounded-full bg-red-500 hover:bg-red-400 text-white transition-all duration-200 hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed shadow-lg shadow-red-500/25\"\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 <button\r\n onClick={startRecording}\r\n disabled={disabled || isLoading || isRateLimited}\r\n className=\"ai-input-btn-secondary p-2 rounded-lg transition-all duration-200 hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\"\r\n aria-label=\"Start recording\"\r\n >\r\n <MicIcon className=\"h-5 w-5\" />\r\n </button>\r\n <button\r\n onClick={submit}\r\n disabled={!canSubmit || disabled}\r\n className={`\r\n p-2.5 rounded-full transition-all duration-200\r\n disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\r\n ${canSubmit\r\n ? 'ai-input-btn-primary hover:scale-105 active:scale-95 shadow-lg'\r\n : 'ai-input-btn-disabled'\r\n }\r\n `}\r\n aria-label=\"Send message\"\r\n >\r\n {isLoading ? <Spinner className=\"h-5 w-5\" /> : <ArrowUpIcon className=\"h-5 w-5\" />}\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 * Text/audio input with automatic light/dark mode detection.\r\n * \r\n * **Theme Detection (in order of priority):**\r\n * 1. `.dark` class on html/body (Tailwind/Next.js)\r\n * 2. `data-theme=\"dark\"` attribute\r\n * 3. `prefers-color-scheme` system preference\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 if (children) {\r\n return <>{children(inputState)}</>\r\n }\r\n\r\n return (\r\n <div className={`w-full ${className || ''}`}>\r\n <DefaultUI {...inputState} placeholder={placeholder} disabled={disabled} />\r\n </div>\r\n )\r\n}\r\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -442,25 +442,8 @@ function XIcon({ className = "" }) {
|
|
|
442
442
|
}
|
|
443
443
|
function Spinner({ className = "" }) {
|
|
444
444
|
return /* @__PURE__ */ jsxs("svg", { className: `animate-spin ${className}`, viewBox: "0 0 24 24", fill: "none", children: [
|
|
445
|
-
/* @__PURE__ */ jsx(
|
|
446
|
-
|
|
447
|
-
{
|
|
448
|
-
className: "opacity-25",
|
|
449
|
-
cx: "12",
|
|
450
|
-
cy: "12",
|
|
451
|
-
r: "10",
|
|
452
|
-
stroke: "currentColor",
|
|
453
|
-
strokeWidth: "4"
|
|
454
|
-
}
|
|
455
|
-
),
|
|
456
|
-
/* @__PURE__ */ jsx(
|
|
457
|
-
"path",
|
|
458
|
-
{
|
|
459
|
-
className: "opacity-75",
|
|
460
|
-
fill: "currentColor",
|
|
461
|
-
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"
|
|
462
|
-
}
|
|
463
|
-
)
|
|
445
|
+
/* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
|
|
446
|
+
/* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", 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" })
|
|
464
447
|
] });
|
|
465
448
|
}
|
|
466
449
|
function DefaultUI({
|
|
@@ -494,13 +477,14 @@ function DefaultUI({
|
|
|
494
477
|
e.target.style.height = "auto";
|
|
495
478
|
e.target.style.height = `${Math.min(Math.max(e.target.scrollHeight, 56), 200)}px`;
|
|
496
479
|
};
|
|
497
|
-
return /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsxs(
|
|
480
|
+
return /* @__PURE__ */ jsx("div", { className: "ai-input w-full", children: /* @__PURE__ */ jsxs(
|
|
498
481
|
"div",
|
|
499
482
|
{
|
|
500
483
|
className: `
|
|
501
|
-
|
|
484
|
+
ai-input-container
|
|
485
|
+
border rounded-xl
|
|
502
486
|
transition-all duration-300 ease-out
|
|
503
|
-
${isRecording ? "
|
|
487
|
+
${isRecording ? "ai-input-recording" : ""}
|
|
504
488
|
${disabled ? "opacity-50" : ""}
|
|
505
489
|
`,
|
|
506
490
|
children: [
|
|
@@ -513,15 +497,7 @@ function DefaultUI({
|
|
|
513
497
|
placeholder: isRecording ? "Listening..." : placeholder,
|
|
514
498
|
disabled: disabled || isLoading || isRateLimited,
|
|
515
499
|
rows: 1,
|
|
516
|
-
className:
|
|
517
|
-
w-full px-4 pt-4 pb-2
|
|
518
|
-
bg-transparent text-zinc-100 placeholder:text-zinc-500
|
|
519
|
-
focus:outline-none
|
|
520
|
-
disabled:cursor-not-allowed
|
|
521
|
-
resize-none
|
|
522
|
-
min-h-[56px]
|
|
523
|
-
transition-colors duration-200
|
|
524
|
-
`,
|
|
500
|
+
className: "ai-input-textarea w-full px-4 pt-4 pb-2 bg-transparent focus:outline-none disabled:cursor-not-allowed resize-none min-h-[56px] transition-colors duration-200",
|
|
525
501
|
style: { height: "56px" }
|
|
526
502
|
}
|
|
527
503
|
),
|
|
@@ -532,7 +508,7 @@ function DefaultUI({
|
|
|
532
508
|
{
|
|
533
509
|
onClick: cancelRecording,
|
|
534
510
|
disabled,
|
|
535
|
-
className: "
|
|
511
|
+
className: "ai-input-btn-secondary p-2 rounded-lg transition-all duration-200 active:scale-95",
|
|
536
512
|
"aria-label": "Cancel recording",
|
|
537
513
|
children: /* @__PURE__ */ jsx(XIcon, { className: "h-5 w-5" })
|
|
538
514
|
}
|
|
@@ -541,45 +517,30 @@ function DefaultUI({
|
|
|
541
517
|
/* @__PURE__ */ jsx(RecordingPulse, {}),
|
|
542
518
|
/* @__PURE__ */ jsx(Waveform, { levels: audioLevels })
|
|
543
519
|
] }),
|
|
544
|
-
/* @__PURE__ */ jsx("span", { className: "text-
|
|
520
|
+
/* @__PURE__ */ jsx("span", { className: "ai-input-text-muted text-sm font-mono tabular-nums", children: formatDuration(recordingDuration) })
|
|
545
521
|
] }) : /* @__PURE__ */ jsxs("div", { className: "text-sm min-h-[28px] flex items-center", children: [
|
|
546
|
-
hasError && error && /* @__PURE__ */ jsx("span", { className: "text-
|
|
547
|
-
isRateLimited && /* @__PURE__ */ jsxs("span", { className: "text-
|
|
522
|
+
hasError && error && /* @__PURE__ */ jsx("span", { className: "ai-input-text-error animate-pulse", children: error.message }),
|
|
523
|
+
isRateLimited && /* @__PURE__ */ jsxs("span", { className: "ai-input-text-warning", children: [
|
|
548
524
|
"Wait ",
|
|
549
525
|
formatDuration(cooldownRemaining)
|
|
550
526
|
] })
|
|
551
527
|
] }) }),
|
|
552
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: isRecording ? /* @__PURE__ */ jsx(
|
|
528
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: isRecording ? /* @__PURE__ */ jsx(
|
|
553
529
|
"button",
|
|
554
530
|
{
|
|
555
531
|
onClick: stopRecording,
|
|
556
532
|
disabled,
|
|
557
|
-
className:
|
|
558
|
-
p-2.5 rounded-full
|
|
559
|
-
bg-red-500 hover:bg-red-400
|
|
560
|
-
text-white
|
|
561
|
-
transition-all duration-200
|
|
562
|
-
hover:scale-105 active:scale-95
|
|
563
|
-
disabled:opacity-50 disabled:cursor-not-allowed
|
|
564
|
-
shadow-lg shadow-red-500/25
|
|
565
|
-
`,
|
|
533
|
+
className: "p-2.5 rounded-full bg-red-500 hover:bg-red-400 text-white transition-all duration-200 hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed shadow-lg shadow-red-500/25",
|
|
566
534
|
"aria-label": "Stop recording",
|
|
567
535
|
children: /* @__PURE__ */ jsx(StopIcon, { className: "h-5 w-5" })
|
|
568
536
|
}
|
|
569
|
-
)
|
|
537
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
570
538
|
/* @__PURE__ */ jsx(
|
|
571
539
|
"button",
|
|
572
540
|
{
|
|
573
541
|
onClick: startRecording,
|
|
574
542
|
disabled: disabled || isLoading || isRateLimited,
|
|
575
|
-
className:
|
|
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
|
|
582
|
-
`,
|
|
543
|
+
className: "ai-input-btn-secondary p-2 rounded-lg transition-all duration-200 hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100",
|
|
583
544
|
"aria-label": "Start recording",
|
|
584
545
|
children: /* @__PURE__ */ jsx(MicIcon, { className: "h-5 w-5" })
|
|
585
546
|
}
|
|
@@ -590,10 +551,9 @@ function DefaultUI({
|
|
|
590
551
|
onClick: submit,
|
|
591
552
|
disabled: !canSubmit || disabled,
|
|
592
553
|
className: `
|
|
593
|
-
p-2.5 rounded-full
|
|
594
|
-
transition-all duration-200
|
|
554
|
+
p-2.5 rounded-full transition-all duration-200
|
|
595
555
|
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100
|
|
596
|
-
${canSubmit ? "
|
|
556
|
+
${canSubmit ? "ai-input-btn-primary hover:scale-105 active:scale-95 shadow-lg" : "ai-input-btn-disabled"}
|
|
597
557
|
`,
|
|
598
558
|
"aria-label": "Send message",
|
|
599
559
|
children: isLoading ? /* @__PURE__ */ jsx(Spinner, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx(ArrowUpIcon, { className: "h-5 w-5" })
|
|
@@ -630,14 +590,7 @@ function AiInput({
|
|
|
630
590
|
if (children) {
|
|
631
591
|
return /* @__PURE__ */ jsx(Fragment, { children: children(inputState) });
|
|
632
592
|
}
|
|
633
|
-
return /* @__PURE__ */ jsx("div", { className: `w-full ${className || ""}`, children: /* @__PURE__ */ jsx(
|
|
634
|
-
DefaultUI,
|
|
635
|
-
{
|
|
636
|
-
...inputState,
|
|
637
|
-
placeholder,
|
|
638
|
-
disabled
|
|
639
|
-
}
|
|
640
|
-
) });
|
|
593
|
+
return /* @__PURE__ */ jsx("div", { className: `w-full ${className || ""}`, children: /* @__PURE__ */ jsx(DefaultUI, { ...inputState, placeholder, disabled }) });
|
|
641
594
|
}
|
|
642
595
|
|
|
643
596
|
export { AiInput, useAiInput, useAudioRecorder, useRateLimiter };
|
package/dist/index.mjs.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":["DEFAULT_OPTIONS","useState","useRef","useCallback","useEffect","error"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseRateLimiterOptions, UseRateLimiterReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseRateLimiterOptions = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\n/**\r\n * Hook for soft rate limiting at the UI level.\r\n * Provides UX protection by tracking requests and enforcing cooldowns.\r\n * \r\n * Note: This is not a security measure. Actual rate limiting \r\n * should be handled by the AI provider or backend.\r\n * \r\n * @param options - Rate limiting configuration\r\n * @returns Rate limiter state and controls\r\n */\r\nexport function useRateLimiter(\r\n options: Partial<UseRateLimiterOptions> = {}\r\n): UseRateLimiterReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n // Track request timestamps within the window\r\n const requestTimestamps = useRef<number[]>([])\r\n\r\n // Cooldown state\r\n const [cooldownEnd, setCooldownEnd] = useState<number>(0)\r\n const [cooldownRemaining, setCooldownRemaining] = useState<number>(0)\r\n\r\n // Force re-render for requestsRemaining updates\r\n const [, forceUpdate] = useState({})\r\n\r\n // Cleanup old timestamps and calculate remaining requests\r\n const cleanupAndCount = useCallback(() => {\r\n const now = Date.now()\r\n const windowStart = now - config.windowMs\r\n\r\n // Remove timestamps outside the window\r\n requestTimestamps.current = requestTimestamps.current.filter(\r\n (ts) => ts > windowStart\r\n )\r\n\r\n return config.maxRequests - requestTimestamps.current.length\r\n }, [config.windowMs, config.maxRequests])\r\n\r\n // Update cooldown remaining\r\n useEffect(() => {\r\n if (cooldownEnd <= Date.now()) {\r\n setCooldownRemaining(0)\r\n return\r\n }\r\n\r\n const interval = setInterval(() => {\r\n const remaining = Math.max(0, cooldownEnd - Date.now())\r\n setCooldownRemaining(remaining)\r\n\r\n if (remaining === 0) {\r\n clearInterval(interval)\r\n }\r\n }, 100)\r\n\r\n return () => clearInterval(interval)\r\n }, [cooldownEnd])\r\n\r\n // Check if request is allowed\r\n const canRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Check cooldown\r\n if (now < cooldownEnd) {\r\n return false\r\n }\r\n\r\n // Check request count\r\n return cleanupAndCount() > 0\r\n }, [cooldownEnd, cleanupAndCount])\r\n\r\n // Record a request\r\n const recordRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Add timestamp\r\n requestTimestamps.current.push(now)\r\n\r\n // Start cooldown\r\n const newCooldownEnd = now + config.cooldownMs\r\n setCooldownEnd(newCooldownEnd)\r\n setCooldownRemaining(config.cooldownMs)\r\n\r\n // Trigger re-render\r\n forceUpdate({})\r\n }, [config.cooldownMs])\r\n\r\n // Reset rate limiter\r\n const reset = useCallback(() => {\r\n requestTimestamps.current = []\r\n setCooldownEnd(0)\r\n setCooldownRemaining(0)\r\n forceUpdate({})\r\n }, [])\r\n\r\n return {\r\n canRequest: canRequest(),\r\n cooldownRemaining,\r\n requestsRemaining: cleanupAndCount(),\r\n recordRequest,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseAudioRecorderOptions, UseAudioRecorderReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseAudioRecorderOptions = {\r\n maxDurationMs: 60000, // 1 minute\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Get the best supported MIME type for MediaRecorder\r\n */\r\nfunction getSupportedMimeType(preferredTypes: string[]): string | null {\r\n if (typeof MediaRecorder === 'undefined') {\r\n return null\r\n }\r\n\r\n for (const mimeType of preferredTypes) {\r\n if (MediaRecorder.isTypeSupported(mimeType)) {\r\n return mimeType\r\n }\r\n }\r\n\r\n // Fallback to default\r\n return ''\r\n}\r\n\r\n/**\r\n * Hook for audio recording using Web APIs.\r\n * Uses navigator.mediaDevices, MediaRecorder, and Web Audio API for visualization.\r\n * \r\n * @param options - Audio recording configuration\r\n * @returns Audio recorder state and controls\r\n */\r\nexport function useAudioRecorder(\r\n options: Partial<UseAudioRecorderOptions> = {}\r\n): UseAudioRecorderReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n const [isRecording, setIsRecording] = useState(false)\r\n const [duration, setDuration] = useState(0)\r\n const [audioBlob, setAudioBlob] = useState<Blob | null>(null)\r\n const [error, setError] = useState<Error | null>(null)\r\n const [audioLevels, setAudioLevels] = useState<number[]>([])\r\n\r\n const mediaRecorderRef = useRef<MediaRecorder | null>(null)\r\n const streamRef = useRef<MediaStream | null>(null)\r\n const chunksRef = useRef<Blob[]>([])\r\n const startTimeRef = useRef<number>(0)\r\n const durationIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\r\n const maxDurationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\r\n\r\n // Web Audio API refs for visualization\r\n const audioContextRef = useRef<AudioContext | null>(null)\r\n const analyserRef = useRef<AnalyserNode | null>(null)\r\n const animationFrameRef = useRef<number | null>(null)\r\n\r\n // Check if audio recording is supported\r\n const isSupported = typeof navigator !== 'undefined'\r\n && 'mediaDevices' in navigator\r\n && 'getUserMedia' in navigator.mediaDevices\r\n && typeof MediaRecorder !== 'undefined'\r\n\r\n // Update audio levels from analyser - uses time domain for better voice visualization\r\n const updateAudioLevels = useCallback(() => {\r\n if (!analyserRef.current) return\r\n\r\n const analyser = analyserRef.current\r\n const bufferLength = analyser.fftSize\r\n const dataArray = new Uint8Array(bufferLength)\r\n\r\n // Use time domain data for even distribution across bars\r\n analyser.getByteTimeDomainData(dataArray)\r\n\r\n // Sample 16 bars from the waveform data\r\n const bars = 16\r\n const step = Math.floor(bufferLength / bars)\r\n const levels: number[] = []\r\n\r\n for (let i = 0; i < bars; i++) {\r\n // Get amplitude variation from center (128) for each segment\r\n let maxDeviation = 0\r\n for (let j = 0; j < step; j++) {\r\n const value = dataArray[i * step + j]\r\n const deviation = Math.abs(value - 128)\r\n maxDeviation = Math.max(maxDeviation, deviation)\r\n }\r\n // Normalize to 0-1 range (max deviation is 128)\r\n // Apply some amplification for better visibility\r\n levels.push(Math.min(1, (maxDeviation / 128) * 2.5))\r\n }\r\n\r\n setAudioLevels(levels)\r\n\r\n // Continue animation loop\r\n animationFrameRef.current = requestAnimationFrame(updateAudioLevels)\r\n }, [])\r\n\r\n // Cleanup function\r\n const cleanup = useCallback(() => {\r\n if (durationIntervalRef.current) {\r\n clearInterval(durationIntervalRef.current)\r\n durationIntervalRef.current = null\r\n }\r\n\r\n if (maxDurationTimeoutRef.current) {\r\n clearTimeout(maxDurationTimeoutRef.current)\r\n maxDurationTimeoutRef.current = null\r\n }\r\n\r\n if (animationFrameRef.current) {\r\n cancelAnimationFrame(animationFrameRef.current)\r\n animationFrameRef.current = null\r\n }\r\n\r\n if (audioContextRef.current) {\r\n audioContextRef.current.close()\r\n audioContextRef.current = null\r\n }\r\n\r\n if (streamRef.current) {\r\n streamRef.current.getTracks().forEach((track) => track.stop())\r\n streamRef.current = null\r\n }\r\n\r\n analyserRef.current = null\r\n mediaRecorderRef.current = null\r\n chunksRef.current = []\r\n setAudioLevels([])\r\n }, [])\r\n\r\n // Stop recording\r\n const stopRecording = useCallback(() => {\r\n if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {\r\n mediaRecorderRef.current.stop()\r\n }\r\n setIsRecording(false)\r\n }, [])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!isSupported) {\r\n setError(new Error('Audio recording is not supported in this browser'))\r\n return\r\n }\r\n\r\n // Reset state\r\n setError(null)\r\n setAudioBlob(null)\r\n setDuration(0)\r\n setAudioLevels([])\r\n chunksRef.current = []\r\n\r\n try {\r\n // Get microphone access\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\r\n streamRef.current = stream\r\n\r\n // Set up Web Audio API for visualization\r\n const audioContext = new AudioContext()\r\n audioContextRef.current = audioContext\r\n\r\n const source = audioContext.createMediaStreamSource(stream)\r\n const analyser = audioContext.createAnalyser()\r\n analyser.fftSize = 256\r\n analyser.smoothingTimeConstant = 0.8\r\n source.connect(analyser)\r\n analyserRef.current = analyser\r\n\r\n // Get supported MIME type\r\n const mimeType = getSupportedMimeType(config.mimeTypes)\r\n\r\n // Create MediaRecorder\r\n const mediaRecorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined)\r\n mediaRecorderRef.current = mediaRecorder\r\n\r\n // Handle data available\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n chunksRef.current.push(event.data)\r\n }\r\n }\r\n\r\n // Handle recording stop\r\n mediaRecorder.onstop = () => {\r\n const blob = new Blob(chunksRef.current, {\r\n type: mimeType || 'audio/webm'\r\n })\r\n setAudioBlob(blob)\r\n\r\n // Call callback if provided\r\n if (config.onRecordingComplete) {\r\n config.onRecordingComplete(blob)\r\n }\r\n\r\n cleanup()\r\n }\r\n\r\n // Handle errors\r\n mediaRecorder.onerror = () => {\r\n setError(new Error('Recording error occurred'))\r\n setIsRecording(false)\r\n cleanup()\r\n }\r\n\r\n // Start recording\r\n mediaRecorder.start(100) // Collect data every 100ms\r\n startTimeRef.current = Date.now()\r\n setIsRecording(true)\r\n\r\n // Start audio level visualization\r\n updateAudioLevels()\r\n\r\n // Update duration every 100ms\r\n durationIntervalRef.current = setInterval(() => {\r\n setDuration(Date.now() - startTimeRef.current)\r\n }, 100)\r\n\r\n // Auto-stop at max duration\r\n maxDurationTimeoutRef.current = setTimeout(() => {\r\n stopRecording()\r\n }, config.maxDurationMs)\r\n\r\n } catch (err) {\r\n const errorMessage = err instanceof Error\r\n ? err.message\r\n : 'Failed to access microphone'\r\n setError(new Error(errorMessage))\r\n cleanup()\r\n }\r\n }, [isSupported, config.mimeTypes, config.maxDurationMs, config.onRecordingComplete, cleanup, stopRecording, updateAudioLevels])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n }, [cleanup])\r\n\r\n // Reset hook state\r\n const reset = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n setError(null)\r\n setAudioLevels([])\r\n }, [cleanup])\r\n\r\n // Cleanup on unmount\r\n useEffect(() => {\r\n return () => {\r\n cleanup()\r\n }\r\n }, [cleanup])\r\n\r\n return {\r\n isRecording,\r\n isSupported,\r\n duration,\r\n audioBlob,\r\n audioLevels,\r\n error,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from 'react'\r\nimport { useRateLimiter } from './useRateLimiter'\r\nimport { useAudioRecorder } from './useAudioRecorder'\r\nimport type {\r\n UseAiInputOptions,\r\n UseAiInputReturn,\r\n AiInputState,\r\n RateLimitConfig,\r\n AudioConfig,\r\n} from '../types'\r\n\r\nconst DEFAULT_RATE_LIMIT: RateLimitConfig = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\nconst DEFAULT_AUDIO_CONFIG: AudioConfig = {\r\n maxDurationMs: 60000,\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Main hook for AI input functionality.\r\n * Combines rate limiting, audio recording, and API communication.\r\n * Unified design - text and audio in single component.\r\n * \r\n * @param options - Configuration options\r\n * @returns Complete state and controls for AI input\r\n */\r\nexport function useAiInput(options: UseAiInputOptions): UseAiInputReturn {\r\n const {\r\n send,\r\n sendAudio,\r\n rateLimit = {},\r\n audioConfig = {},\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n } = options\r\n\r\n const rateLimitConfig = { ...DEFAULT_RATE_LIMIT, ...rateLimit }\r\n const audioConfigMerged = { ...DEFAULT_AUDIO_CONFIG, ...audioConfig }\r\n\r\n // State\r\n const [state, setState] = useState<AiInputState>('idle')\r\n const [text, setText] = useState('')\r\n const [error, setError] = useState<Error | null>(null)\r\n const [result, setResult] = useState<unknown>(null)\r\n\r\n // Ref to track if we're waiting to submit audio after recording stops\r\n const pendingAudioSubmitRef = useRef(false)\r\n\r\n // Rate limiter\r\n const rateLimiter = useRateLimiter(rateLimitConfig)\r\n\r\n // Audio recorder\r\n const audioRecorder = useAudioRecorder({\r\n ...audioConfigMerged,\r\n })\r\n\r\n // Update state based on rate limiter\r\n useEffect(() => {\r\n if (!rateLimiter.canRequest && state === 'idle') {\r\n setState('rate-limited')\r\n } else if (rateLimiter.canRequest && state === 'rate-limited') {\r\n setState('idle')\r\n }\r\n }, [rateLimiter.canRequest, state])\r\n\r\n // Update state when recording\r\n useEffect(() => {\r\n if (audioRecorder.isRecording && state !== 'recording') {\r\n setState('recording')\r\n }\r\n }, [audioRecorder.isRecording, state])\r\n\r\n // Handle audio recorder errors\r\n useEffect(() => {\r\n if (audioRecorder.error) {\r\n setError(audioRecorder.error)\r\n setState('error')\r\n onError?.(audioRecorder.error)\r\n }\r\n }, [audioRecorder.error, onError])\r\n\r\n // Submit text\r\n const submitText = useCallback(async () => {\r\n if (!text.trim() || !rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n const response = await send(text)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n // Clear text after successful send\r\n setText('')\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [text, rateLimiter, send, onSuccess, onError])\r\n\r\n // Submit audio\r\n const submitAudio = useCallback(async (blob: Blob) => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n // Use sendAudio if provided, otherwise use send\r\n const sendFn = sendAudio || send\r\n const response = await sendFn(blob)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n\r\n // Handle transcription if callback provided\r\n if (onTranscription && response && typeof response === 'object') {\r\n const res = response as Record<string, unknown>\r\n // Try common transcription response formats\r\n const transcriptionText = res.text || res.transcription || res.transcript\r\n if (typeof transcriptionText === 'string') {\r\n setText(transcriptionText)\r\n onTranscription(transcriptionText)\r\n }\r\n }\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [rateLimiter, send, sendAudio, onSuccess, onError, onTranscription])\r\n\r\n // Handle audio blob ready - submit if we were waiting\r\n useEffect(() => {\r\n if (pendingAudioSubmitRef.current && audioRecorder.audioBlob && !audioRecorder.isRecording) {\r\n pendingAudioSubmitRef.current = false\r\n submitAudio(audioRecorder.audioBlob)\r\n }\r\n }, [audioRecorder.audioBlob, audioRecorder.isRecording, submitAudio])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n await audioRecorder.startRecording()\r\n }, [rateLimiter.canRequest, audioRecorder])\r\n\r\n // Stop recording and submit\r\n const stopRecording = useCallback(() => {\r\n // Mark that we want to submit audio when blob is ready\r\n pendingAudioSubmitRef.current = true\r\n audioRecorder.stopRecording()\r\n }, [audioRecorder])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n audioRecorder.cancelRecording()\r\n setState('idle')\r\n }, [audioRecorder])\r\n\r\n // Submit based on current state\r\n const submit = useCallback(() => {\r\n if (audioRecorder.isRecording) {\r\n stopRecording()\r\n } else if (text.trim()) {\r\n submitText()\r\n }\r\n }, [audioRecorder.isRecording, text, stopRecording, submitText])\r\n\r\n // Reset all state\r\n const reset = useCallback(() => {\r\n setState('idle')\r\n setText('')\r\n setError(null)\r\n setResult(null)\r\n rateLimiter.reset()\r\n audioRecorder.reset()\r\n }, [rateLimiter, audioRecorder])\r\n\r\n // Can submit check\r\n const canSubmit =\r\n rateLimiter.canRequest &&\r\n state !== 'loading' &&\r\n (audioRecorder.isRecording || text.trim().length > 0)\r\n\r\n return {\r\n // State\r\n state,\r\n error,\r\n result,\r\n\r\n // Text\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n\r\n // Audio\r\n isRecording: audioRecorder.isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration: audioRecorder.duration,\r\n maxRecordingDuration: audioConfigMerged.maxDurationMs,\r\n audioLevels: audioRecorder.audioLevels,\r\n\r\n // Rate limiting\r\n cooldownRemaining: rateLimiter.cooldownRemaining,\r\n requestsRemaining: rateLimiter.requestsRemaining,\r\n\r\n // Utils\r\n reset,\r\n }\r\n}\r\n","import React from 'react'\r\nimport { useAiInput } from '../hooks/useAiInput'\r\nimport type { AiInputProps, AiInputRenderProps } from '../types'\r\n\r\n/**\r\n * Format milliseconds to MM:SS display\r\n */\r\nfunction formatDuration(ms: number): string {\r\n const seconds = Math.floor(ms / 1000)\r\n const minutes = Math.floor(seconds / 60)\r\n const remainingSeconds = seconds % 60\r\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`\r\n}\r\n\r\n/**\r\n * Waveform visualization component with smooth animations\r\n */\r\nfunction Waveform({ levels, className = '' }: { levels: number[]; className?: string }) {\r\n // Generate 16 bars if no levels provided\r\n const bars = levels.length > 0 ? levels : Array(16).fill(0.15)\r\n\r\n return (\r\n <div className={`flex items-center justify-center gap-1 h-10 ${className}`}>\r\n {bars.map((level, i) => (\r\n <div\r\n key={i}\r\n className=\"w-1.5 bg-gradient-to-t from-amber-600 to-amber-400 rounded-full transition-all duration-100 ease-out\"\r\n style={{\r\n height: `${Math.max(6, level * 40)}px`,\r\n opacity: 0.6 + level * 0.4,\r\n }}\r\n />\r\n ))}\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Recording pulse indicator\r\n */\r\nfunction RecordingPulse() {\r\n return (\r\n <span className=\"relative flex h-3 w-3 mr-2\">\r\n <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75\"></span>\r\n <span className=\"relative inline-flex rounded-full h-3 w-3 bg-red-500\"></span>\r\n </span>\r\n )\r\n}\r\n\r\n/**\r\n * Microphone icon (Phosphor style)\r\n */\r\nfunction MicIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M128,176a48.05,48.05,0,0,0,48-48V64a48,48,0,0,0-96,0v64A48.05,48.05,0,0,0,128,176ZM96,64a32,32,0,0,1,64,0v64a32,32,0,0,1-64,0Zm40,143.6V232a8,8,0,0,1-16,0V207.6A80.11,80.11,0,0,1,48,128a8,8,0,0,1,16,0,64,64,0,0,0,128,0,8,8,0,0,1,16,0A80.11,80.11,0,0,1,136,207.6Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Arrow up icon for submit\r\n */\r\nfunction ArrowUpIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,117.66a8,8,0,0,1-11.32,0L136,59.31V216a8,8,0,0,1-16,0V59.31L61.66,117.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,205.66,117.66Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Stop icon (filled square)\r\n */\r\nfunction StopIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <rect x=\"64\" y=\"64\" width=\"128\" height=\"128\" rx=\"8\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * X icon for cancel\r\n */\r\nfunction XIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={className} viewBox=\"0 0 256 256\" fill=\"currentColor\">\r\n <path d=\"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Spinner for loading state\r\n */\r\nfunction Spinner({ className = '' }: { className?: string }) {\r\n return (\r\n <svg className={`animate-spin ${className}`} viewBox=\"0 0 24 24\" fill=\"none\">\r\n <circle\r\n className=\"opacity-25\"\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"4\"\r\n />\r\n <path\r\n className=\"opacity-75\"\r\n fill=\"currentColor\"\r\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\r\n />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Default UI for the unified AiInput component\r\n */\r\nfunction DefaultUI({\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n state,\r\n error,\r\n isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration,\r\n audioLevels,\r\n cooldownRemaining,\r\n placeholder = 'Ask anything...',\r\n disabled = false,\r\n}: AiInputRenderProps & {\r\n placeholder?: string\r\n disabled?: boolean\r\n}) {\r\n const isLoading = state === 'loading'\r\n const isRateLimited = state === 'rate-limited'\r\n const hasError = state === 'error'\r\n\r\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\r\n if (e.key === 'Enter' && !e.shiftKey && canSubmit && !isRecording) {\r\n e.preventDefault()\r\n submit()\r\n }\r\n }\r\n\r\n // Auto-resize textarea\r\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\r\n setText(e.target.value)\r\n // Reset height to auto to get the correct scrollHeight\r\n e.target.style.height = 'auto'\r\n // Set height to scrollHeight, with min and max constraints\r\n e.target.style.height = `${Math.min(Math.max(e.target.scrollHeight, 56), 200)}px`\r\n }\r\n\r\n return (\r\n <div className=\"w-full\">\r\n {/* Main container */}\r\n <div\r\n className={`\r\n bg-zinc-900 border rounded-xl\r\n transition-all duration-300 ease-out\r\n ${isRecording\r\n ? 'border-red-500/50 shadow-lg shadow-red-500/10'\r\n : 'border-zinc-800 focus-within:border-amber-500/50 focus-within:shadow-lg focus-within:shadow-amber-500/5'\r\n }\r\n ${disabled ? 'opacity-50' : ''}\r\n `}\r\n >\r\n {/* Text input area - always visible for live transcription */}\r\n <textarea\r\n value={text}\r\n onChange={handleInput}\r\n onKeyDown={handleKeyDown}\r\n placeholder={isRecording ? 'Listening...' : placeholder}\r\n disabled={disabled || isLoading || isRateLimited}\r\n rows={1}\r\n className={`\r\n w-full px-4 pt-4 pb-2\r\n bg-transparent text-zinc-100 placeholder:text-zinc-500\r\n focus:outline-none\r\n disabled:cursor-not-allowed\r\n resize-none\r\n min-h-[56px]\r\n transition-colors duration-200\r\n `}\r\n style={{ height: '56px' }}\r\n />\r\n\r\n {/* Toolbar */}\r\n <div className=\"flex items-center justify-between px-3 pb-3 pt-1\">\r\n {/* Left side - error/status or waveform during recording */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n {/* Cancel recording */}\r\n <button\r\n onClick={cancelRecording}\r\n disabled={disabled}\r\n className=\"p-2 text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 rounded-lg transition-all duration-200 active:scale-95\"\r\n aria-label=\"Cancel recording\"\r\n >\r\n <XIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Recording indicator + Waveform */}\r\n <div className=\"flex items-center\">\r\n <RecordingPulse />\r\n <Waveform levels={audioLevels} />\r\n </div>\r\n\r\n {/* Timer */}\r\n <span className=\"text-sm text-zinc-400 font-mono tabular-nums\">\r\n {formatDuration(recordingDuration)}\r\n </span>\r\n </>\r\n ) : (\r\n <div className=\"text-sm min-h-[28px] flex items-center\">\r\n {hasError && error && (\r\n <span className=\"text-red-400 animate-pulse\">{error.message}</span>\r\n )}\r\n {isRateLimited && (\r\n <span className=\"text-amber-400\">\r\n Wait {formatDuration(cooldownRemaining)}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Right side - action buttons */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n {/* Stop and send */}\r\n <button\r\n onClick={stopRecording}\r\n disabled={disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n bg-red-500 hover:bg-red-400\r\n text-white\r\n transition-all duration-200\r\n hover:scale-105 active:scale-95\r\n disabled:opacity-50 disabled:cursor-not-allowed\r\n shadow-lg shadow-red-500/25\r\n `}\r\n aria-label=\"Stop recording\"\r\n >\r\n <StopIcon className=\"h-5 w-5\" />\r\n </button>\r\n </>\r\n ) : (\r\n <>\r\n {/* Mic button */}\r\n <button\r\n onClick={startRecording}\r\n disabled={disabled || isLoading || isRateLimited}\r\n className={`\r\n p-2 text-zinc-400 \r\n hover:text-amber-400 hover:bg-zinc-800\r\n rounded-lg\r\n transition-all duration-200\r\n hover:scale-105 active:scale-95\r\n disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\r\n `}\r\n aria-label=\"Start recording\"\r\n >\r\n <MicIcon className=\"h-5 w-5\" />\r\n </button>\r\n\r\n {/* Submit button */}\r\n <button\r\n onClick={submit}\r\n disabled={!canSubmit || disabled}\r\n className={`\r\n p-2.5 rounded-full\r\n transition-all duration-200\r\n disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\r\n ${canSubmit\r\n ? 'bg-amber-500 hover:bg-amber-400 text-zinc-900 hover:scale-105 active:scale-95 shadow-lg shadow-amber-500/25'\r\n : 'bg-zinc-800 text-zinc-500'\r\n }\r\n `}\r\n aria-label=\"Send message\"\r\n >\r\n {isLoading ? (\r\n <Spinner className=\"h-5 w-5\" />\r\n ) : (\r\n <ArrowUpIcon className=\"h-5 w-5\" />\r\n )}\r\n </button>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * AiInput Component\r\n * \r\n * A React component for text/audio input with AI API integration.\r\n * Unified design with text input and audio recording in a single component.\r\n * \r\n * @example\r\n * // Basic usage\r\n * <AiInput\r\n * send={async (input) => {\r\n * const response = await fetch('/api/chat', {\r\n * method: 'POST',\r\n * body: JSON.stringify({ message: input }),\r\n * })\r\n * return response.json()\r\n * }}\r\n * onSuccess={(result) => console.log(result)}\r\n * />\r\n * \r\n * @example\r\n * // With separate audio handler and transcription\r\n * <AiInput\r\n * send={sendTextFn}\r\n * sendAudio={sendAudioFn}\r\n * onTranscription={(text) => console.log('Transcribed:', text)}\r\n * />\r\n * \r\n * @example\r\n * // Headless mode with custom UI\r\n * <AiInput send={sendFn}>\r\n * {({ text, setText, submit, state, isRecording, audioLevels }) => (\r\n * <div>\r\n * {isRecording ? (\r\n * <MyWaveform levels={audioLevels} />\r\n * ) : (\r\n * <input value={text} onChange={(e) => setText(e.target.value)} />\r\n * )}\r\n * <button onClick={submit}>Send</button>\r\n * </div>\r\n * )}\r\n * </AiInput>\r\n */\r\nexport function AiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n children,\r\n placeholder,\r\n className,\r\n disabled = false,\r\n}: AiInputProps) {\r\n const inputState = useAiInput({\r\n send,\r\n sendAudio,\r\n rateLimit,\r\n audioConfig,\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n })\r\n\r\n // Headless mode - render prop\r\n if (children) {\r\n return <>{children(inputState)}</>\r\n }\r\n\r\n // Default UI\r\n return (\r\n <div className={`w-full ${className || ''}`}>\r\n <DefaultUI\r\n {...inputState}\r\n placeholder={placeholder}\r\n disabled={disabled}\r\n />\r\n </div>\r\n )\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useRateLimiter.ts","../src/hooks/useAudioRecorder.ts","../src/hooks/useAiInput.ts","../src/components/AiInput.tsx"],"names":["DEFAULT_OPTIONS","useState","useRef","useCallback","useEffect","error"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseRateLimiterOptions, UseRateLimiterReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseRateLimiterOptions = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\n/**\r\n * Hook for soft rate limiting at the UI level.\r\n * Provides UX protection by tracking requests and enforcing cooldowns.\r\n * \r\n * Note: This is not a security measure. Actual rate limiting \r\n * should be handled by the AI provider or backend.\r\n * \r\n * @param options - Rate limiting configuration\r\n * @returns Rate limiter state and controls\r\n */\r\nexport function useRateLimiter(\r\n options: Partial<UseRateLimiterOptions> = {}\r\n): UseRateLimiterReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n // Track request timestamps within the window\r\n const requestTimestamps = useRef<number[]>([])\r\n\r\n // Cooldown state\r\n const [cooldownEnd, setCooldownEnd] = useState<number>(0)\r\n const [cooldownRemaining, setCooldownRemaining] = useState<number>(0)\r\n\r\n // Force re-render for requestsRemaining updates\r\n const [, forceUpdate] = useState({})\r\n\r\n // Cleanup old timestamps and calculate remaining requests\r\n const cleanupAndCount = useCallback(() => {\r\n const now = Date.now()\r\n const windowStart = now - config.windowMs\r\n\r\n // Remove timestamps outside the window\r\n requestTimestamps.current = requestTimestamps.current.filter(\r\n (ts) => ts > windowStart\r\n )\r\n\r\n return config.maxRequests - requestTimestamps.current.length\r\n }, [config.windowMs, config.maxRequests])\r\n\r\n // Update cooldown remaining\r\n useEffect(() => {\r\n if (cooldownEnd <= Date.now()) {\r\n setCooldownRemaining(0)\r\n return\r\n }\r\n\r\n const interval = setInterval(() => {\r\n const remaining = Math.max(0, cooldownEnd - Date.now())\r\n setCooldownRemaining(remaining)\r\n\r\n if (remaining === 0) {\r\n clearInterval(interval)\r\n }\r\n }, 100)\r\n\r\n return () => clearInterval(interval)\r\n }, [cooldownEnd])\r\n\r\n // Check if request is allowed\r\n const canRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Check cooldown\r\n if (now < cooldownEnd) {\r\n return false\r\n }\r\n\r\n // Check request count\r\n return cleanupAndCount() > 0\r\n }, [cooldownEnd, cleanupAndCount])\r\n\r\n // Record a request\r\n const recordRequest = useCallback(() => {\r\n const now = Date.now()\r\n\r\n // Add timestamp\r\n requestTimestamps.current.push(now)\r\n\r\n // Start cooldown\r\n const newCooldownEnd = now + config.cooldownMs\r\n setCooldownEnd(newCooldownEnd)\r\n setCooldownRemaining(config.cooldownMs)\r\n\r\n // Trigger re-render\r\n forceUpdate({})\r\n }, [config.cooldownMs])\r\n\r\n // Reset rate limiter\r\n const reset = useCallback(() => {\r\n requestTimestamps.current = []\r\n setCooldownEnd(0)\r\n setCooldownRemaining(0)\r\n forceUpdate({})\r\n }, [])\r\n\r\n return {\r\n canRequest: canRequest(),\r\n cooldownRemaining,\r\n requestsRemaining: cleanupAndCount(),\r\n recordRequest,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useRef, useEffect } from 'react'\r\nimport type { UseAudioRecorderOptions, UseAudioRecorderReturn } from '../types'\r\n\r\nconst DEFAULT_OPTIONS: UseAudioRecorderOptions = {\r\n maxDurationMs: 60000, // 1 minute\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Get the best supported MIME type for MediaRecorder\r\n */\r\nfunction getSupportedMimeType(preferredTypes: string[]): string | null {\r\n if (typeof MediaRecorder === 'undefined') {\r\n return null\r\n }\r\n\r\n for (const mimeType of preferredTypes) {\r\n if (MediaRecorder.isTypeSupported(mimeType)) {\r\n return mimeType\r\n }\r\n }\r\n\r\n // Fallback to default\r\n return ''\r\n}\r\n\r\n/**\r\n * Hook for audio recording using Web APIs.\r\n * Uses navigator.mediaDevices, MediaRecorder, and Web Audio API for visualization.\r\n * \r\n * @param options - Audio recording configuration\r\n * @returns Audio recorder state and controls\r\n */\r\nexport function useAudioRecorder(\r\n options: Partial<UseAudioRecorderOptions> = {}\r\n): UseAudioRecorderReturn {\r\n const config = { ...DEFAULT_OPTIONS, ...options }\r\n\r\n const [isRecording, setIsRecording] = useState(false)\r\n const [duration, setDuration] = useState(0)\r\n const [audioBlob, setAudioBlob] = useState<Blob | null>(null)\r\n const [error, setError] = useState<Error | null>(null)\r\n const [audioLevels, setAudioLevels] = useState<number[]>([])\r\n\r\n const mediaRecorderRef = useRef<MediaRecorder | null>(null)\r\n const streamRef = useRef<MediaStream | null>(null)\r\n const chunksRef = useRef<Blob[]>([])\r\n const startTimeRef = useRef<number>(0)\r\n const durationIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\r\n const maxDurationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\r\n\r\n // Web Audio API refs for visualization\r\n const audioContextRef = useRef<AudioContext | null>(null)\r\n const analyserRef = useRef<AnalyserNode | null>(null)\r\n const animationFrameRef = useRef<number | null>(null)\r\n\r\n // Check if audio recording is supported\r\n const isSupported = typeof navigator !== 'undefined'\r\n && 'mediaDevices' in navigator\r\n && 'getUserMedia' in navigator.mediaDevices\r\n && typeof MediaRecorder !== 'undefined'\r\n\r\n // Update audio levels from analyser - uses time domain for better voice visualization\r\n const updateAudioLevels = useCallback(() => {\r\n if (!analyserRef.current) return\r\n\r\n const analyser = analyserRef.current\r\n const bufferLength = analyser.fftSize\r\n const dataArray = new Uint8Array(bufferLength)\r\n\r\n // Use time domain data for even distribution across bars\r\n analyser.getByteTimeDomainData(dataArray)\r\n\r\n // Sample 16 bars from the waveform data\r\n const bars = 16\r\n const step = Math.floor(bufferLength / bars)\r\n const levels: number[] = []\r\n\r\n for (let i = 0; i < bars; i++) {\r\n // Get amplitude variation from center (128) for each segment\r\n let maxDeviation = 0\r\n for (let j = 0; j < step; j++) {\r\n const value = dataArray[i * step + j]\r\n const deviation = Math.abs(value - 128)\r\n maxDeviation = Math.max(maxDeviation, deviation)\r\n }\r\n // Normalize to 0-1 range (max deviation is 128)\r\n // Apply some amplification for better visibility\r\n levels.push(Math.min(1, (maxDeviation / 128) * 2.5))\r\n }\r\n\r\n setAudioLevels(levels)\r\n\r\n // Continue animation loop\r\n animationFrameRef.current = requestAnimationFrame(updateAudioLevels)\r\n }, [])\r\n\r\n // Cleanup function\r\n const cleanup = useCallback(() => {\r\n if (durationIntervalRef.current) {\r\n clearInterval(durationIntervalRef.current)\r\n durationIntervalRef.current = null\r\n }\r\n\r\n if (maxDurationTimeoutRef.current) {\r\n clearTimeout(maxDurationTimeoutRef.current)\r\n maxDurationTimeoutRef.current = null\r\n }\r\n\r\n if (animationFrameRef.current) {\r\n cancelAnimationFrame(animationFrameRef.current)\r\n animationFrameRef.current = null\r\n }\r\n\r\n if (audioContextRef.current) {\r\n audioContextRef.current.close()\r\n audioContextRef.current = null\r\n }\r\n\r\n if (streamRef.current) {\r\n streamRef.current.getTracks().forEach((track) => track.stop())\r\n streamRef.current = null\r\n }\r\n\r\n analyserRef.current = null\r\n mediaRecorderRef.current = null\r\n chunksRef.current = []\r\n setAudioLevels([])\r\n }, [])\r\n\r\n // Stop recording\r\n const stopRecording = useCallback(() => {\r\n if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {\r\n mediaRecorderRef.current.stop()\r\n }\r\n setIsRecording(false)\r\n }, [])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!isSupported) {\r\n setError(new Error('Audio recording is not supported in this browser'))\r\n return\r\n }\r\n\r\n // Reset state\r\n setError(null)\r\n setAudioBlob(null)\r\n setDuration(0)\r\n setAudioLevels([])\r\n chunksRef.current = []\r\n\r\n try {\r\n // Get microphone access\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\r\n streamRef.current = stream\r\n\r\n // Set up Web Audio API for visualization\r\n const audioContext = new AudioContext()\r\n audioContextRef.current = audioContext\r\n\r\n const source = audioContext.createMediaStreamSource(stream)\r\n const analyser = audioContext.createAnalyser()\r\n analyser.fftSize = 256\r\n analyser.smoothingTimeConstant = 0.8\r\n source.connect(analyser)\r\n analyserRef.current = analyser\r\n\r\n // Get supported MIME type\r\n const mimeType = getSupportedMimeType(config.mimeTypes)\r\n\r\n // Create MediaRecorder\r\n const mediaRecorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined)\r\n mediaRecorderRef.current = mediaRecorder\r\n\r\n // Handle data available\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n chunksRef.current.push(event.data)\r\n }\r\n }\r\n\r\n // Handle recording stop\r\n mediaRecorder.onstop = () => {\r\n const blob = new Blob(chunksRef.current, {\r\n type: mimeType || 'audio/webm'\r\n })\r\n setAudioBlob(blob)\r\n\r\n // Call callback if provided\r\n if (config.onRecordingComplete) {\r\n config.onRecordingComplete(blob)\r\n }\r\n\r\n cleanup()\r\n }\r\n\r\n // Handle errors\r\n mediaRecorder.onerror = () => {\r\n setError(new Error('Recording error occurred'))\r\n setIsRecording(false)\r\n cleanup()\r\n }\r\n\r\n // Start recording\r\n mediaRecorder.start(100) // Collect data every 100ms\r\n startTimeRef.current = Date.now()\r\n setIsRecording(true)\r\n\r\n // Start audio level visualization\r\n updateAudioLevels()\r\n\r\n // Update duration every 100ms\r\n durationIntervalRef.current = setInterval(() => {\r\n setDuration(Date.now() - startTimeRef.current)\r\n }, 100)\r\n\r\n // Auto-stop at max duration\r\n maxDurationTimeoutRef.current = setTimeout(() => {\r\n stopRecording()\r\n }, config.maxDurationMs)\r\n\r\n } catch (err) {\r\n const errorMessage = err instanceof Error\r\n ? err.message\r\n : 'Failed to access microphone'\r\n setError(new Error(errorMessage))\r\n cleanup()\r\n }\r\n }, [isSupported, config.mimeTypes, config.maxDurationMs, config.onRecordingComplete, cleanup, stopRecording, updateAudioLevels])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n }, [cleanup])\r\n\r\n // Reset hook state\r\n const reset = useCallback(() => {\r\n cleanup()\r\n setIsRecording(false)\r\n setDuration(0)\r\n setAudioBlob(null)\r\n setError(null)\r\n setAudioLevels([])\r\n }, [cleanup])\r\n\r\n // Cleanup on unmount\r\n useEffect(() => {\r\n return () => {\r\n cleanup()\r\n }\r\n }, [cleanup])\r\n\r\n return {\r\n isRecording,\r\n isSupported,\r\n duration,\r\n audioBlob,\r\n audioLevels,\r\n error,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n reset,\r\n }\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from 'react'\r\nimport { useRateLimiter } from './useRateLimiter'\r\nimport { useAudioRecorder } from './useAudioRecorder'\r\nimport type {\r\n UseAiInputOptions,\r\n UseAiInputReturn,\r\n AiInputState,\r\n RateLimitConfig,\r\n AudioConfig,\r\n} from '../types'\r\n\r\nconst DEFAULT_RATE_LIMIT: RateLimitConfig = {\r\n cooldownMs: 1000,\r\n maxRequests: 10,\r\n windowMs: 60000,\r\n}\r\n\r\nconst DEFAULT_AUDIO_CONFIG: AudioConfig = {\r\n maxDurationMs: 60000,\r\n mimeTypes: ['audio/webm', 'audio/mp4', 'audio/ogg', 'audio/wav'],\r\n}\r\n\r\n/**\r\n * Main hook for AI input functionality.\r\n * Combines rate limiting, audio recording, and API communication.\r\n * Unified design - text and audio in single component.\r\n * \r\n * @param options - Configuration options\r\n * @returns Complete state and controls for AI input\r\n */\r\nexport function useAiInput(options: UseAiInputOptions): UseAiInputReturn {\r\n const {\r\n send,\r\n sendAudio,\r\n rateLimit = {},\r\n audioConfig = {},\r\n onSuccess,\r\n onError,\r\n onTranscription,\r\n } = options\r\n\r\n const rateLimitConfig = { ...DEFAULT_RATE_LIMIT, ...rateLimit }\r\n const audioConfigMerged = { ...DEFAULT_AUDIO_CONFIG, ...audioConfig }\r\n\r\n // State\r\n const [state, setState] = useState<AiInputState>('idle')\r\n const [text, setText] = useState('')\r\n const [error, setError] = useState<Error | null>(null)\r\n const [result, setResult] = useState<unknown>(null)\r\n\r\n // Ref to track if we're waiting to submit audio after recording stops\r\n const pendingAudioSubmitRef = useRef(false)\r\n\r\n // Rate limiter\r\n const rateLimiter = useRateLimiter(rateLimitConfig)\r\n\r\n // Audio recorder\r\n const audioRecorder = useAudioRecorder({\r\n ...audioConfigMerged,\r\n })\r\n\r\n // Update state based on rate limiter\r\n useEffect(() => {\r\n if (!rateLimiter.canRequest && state === 'idle') {\r\n setState('rate-limited')\r\n } else if (rateLimiter.canRequest && state === 'rate-limited') {\r\n setState('idle')\r\n }\r\n }, [rateLimiter.canRequest, state])\r\n\r\n // Update state when recording\r\n useEffect(() => {\r\n if (audioRecorder.isRecording && state !== 'recording') {\r\n setState('recording')\r\n }\r\n }, [audioRecorder.isRecording, state])\r\n\r\n // Handle audio recorder errors\r\n useEffect(() => {\r\n if (audioRecorder.error) {\r\n setError(audioRecorder.error)\r\n setState('error')\r\n onError?.(audioRecorder.error)\r\n }\r\n }, [audioRecorder.error, onError])\r\n\r\n // Submit text\r\n const submitText = useCallback(async () => {\r\n if (!text.trim() || !rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n const response = await send(text)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n // Clear text after successful send\r\n setText('')\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [text, rateLimiter, send, onSuccess, onError])\r\n\r\n // Submit audio\r\n const submitAudio = useCallback(async (blob: Blob) => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n\r\n setState('loading')\r\n setError(null)\r\n rateLimiter.recordRequest()\r\n\r\n try {\r\n // Use sendAudio if provided, otherwise use send\r\n const sendFn = sendAudio || send\r\n const response = await sendFn(blob)\r\n setResult(response)\r\n setState('success')\r\n onSuccess?.(response)\r\n\r\n // Handle transcription if callback provided\r\n if (onTranscription && response && typeof response === 'object') {\r\n const res = response as Record<string, unknown>\r\n // Try common transcription response formats\r\n const transcriptionText = res.text || res.transcription || res.transcript\r\n if (typeof transcriptionText === 'string') {\r\n setText(transcriptionText)\r\n onTranscription(transcriptionText)\r\n }\r\n }\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Request failed')\r\n setError(error)\r\n setState('error')\r\n onError?.(error)\r\n }\r\n }, [rateLimiter, send, sendAudio, onSuccess, onError, onTranscription])\r\n\r\n // Handle audio blob ready - submit if we were waiting\r\n useEffect(() => {\r\n if (pendingAudioSubmitRef.current && audioRecorder.audioBlob && !audioRecorder.isRecording) {\r\n pendingAudioSubmitRef.current = false\r\n submitAudio(audioRecorder.audioBlob)\r\n }\r\n }, [audioRecorder.audioBlob, audioRecorder.isRecording, submitAudio])\r\n\r\n // Start recording\r\n const startRecording = useCallback(async () => {\r\n if (!rateLimiter.canRequest) {\r\n return\r\n }\r\n await audioRecorder.startRecording()\r\n }, [rateLimiter.canRequest, audioRecorder])\r\n\r\n // Stop recording and submit\r\n const stopRecording = useCallback(() => {\r\n // Mark that we want to submit audio when blob is ready\r\n pendingAudioSubmitRef.current = true\r\n audioRecorder.stopRecording()\r\n }, [audioRecorder])\r\n\r\n // Cancel recording (discard audio)\r\n const cancelRecording = useCallback(() => {\r\n audioRecorder.cancelRecording()\r\n setState('idle')\r\n }, [audioRecorder])\r\n\r\n // Submit based on current state\r\n const submit = useCallback(() => {\r\n if (audioRecorder.isRecording) {\r\n stopRecording()\r\n } else if (text.trim()) {\r\n submitText()\r\n }\r\n }, [audioRecorder.isRecording, text, stopRecording, submitText])\r\n\r\n // Reset all state\r\n const reset = useCallback(() => {\r\n setState('idle')\r\n setText('')\r\n setError(null)\r\n setResult(null)\r\n rateLimiter.reset()\r\n audioRecorder.reset()\r\n }, [rateLimiter, audioRecorder])\r\n\r\n // Can submit check\r\n const canSubmit =\r\n rateLimiter.canRequest &&\r\n state !== 'loading' &&\r\n (audioRecorder.isRecording || text.trim().length > 0)\r\n\r\n return {\r\n // State\r\n state,\r\n error,\r\n result,\r\n\r\n // Text\r\n text,\r\n setText,\r\n submit,\r\n canSubmit,\r\n\r\n // Audio\r\n isRecording: audioRecorder.isRecording,\r\n startRecording,\r\n stopRecording,\r\n cancelRecording,\r\n recordingDuration: audioRecorder.duration,\r\n maxRecordingDuration: audioConfigMerged.maxDurationMs,\r\n audioLevels: audioRecorder.audioLevels,\r\n\r\n // Rate limiting\r\n cooldownRemaining: rateLimiter.cooldownRemaining,\r\n requestsRemaining: rateLimiter.requestsRemaining,\r\n\r\n // Utils\r\n reset,\r\n }\r\n}\r\n","import React from 'react'\r\nimport { useAiInput } from '../hooks/useAiInput'\r\nimport type { AiInputProps, AiInputRenderProps } from '../types'\r\n\r\n/**\r\n * Format milliseconds to MM:SS display\r\n */\r\nfunction formatDuration(ms: number): string {\r\n const seconds = Math.floor(ms / 1000)\r\n const minutes = Math.floor(seconds / 60)\r\n const remainingSeconds = seconds % 60\r\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`\r\n}\r\n\r\n/**\r\n * Waveform visualization component with smooth animations\r\n */\r\nfunction Waveform({ levels, className = '' }: { levels: number[]; className?: string }) {\r\n 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\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\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 <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\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\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 className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\r\n <path className=\"opacity-75\" fill=\"currentColor\" 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 </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Default UI - uses CSS variables for automatic theme detection\r\n * The CSS variables are defined in styles.css and automatically switch\r\n * based on prefers-color-scheme, .dark class, or data-theme attribute\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 const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\r\n setText(e.target.value)\r\n e.target.style.height = 'auto'\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=\"ai-input w-full\">\r\n {/* Main container - uses CSS variables for theming */}\r\n <div\r\n className={`\r\n ai-input-container\r\n border rounded-xl\r\n transition-all duration-300 ease-out\r\n ${isRecording ? 'ai-input-recording' : ''}\r\n ${disabled ? 'opacity-50' : ''}\r\n `}\r\n >\r\n {/* Text input */}\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=\"ai-input-textarea w-full px-4 pt-4 pb-2 bg-transparent focus:outline-none disabled:cursor-not-allowed resize-none min-h-[56px] transition-colors duration-200\"\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 */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <>\r\n <button\r\n onClick={cancelRecording}\r\n disabled={disabled}\r\n className=\"ai-input-btn-secondary p-2 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 <div className=\"flex items-center\">\r\n <RecordingPulse />\r\n <Waveform levels={audioLevels} />\r\n </div>\r\n <span className=\"ai-input-text-muted text-sm 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=\"ai-input-text-error animate-pulse\">{error.message}</span>\r\n )}\r\n {isRateLimited && (\r\n <span className=\"ai-input-text-warning\">\r\n Wait {formatDuration(cooldownRemaining)}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Right side */}\r\n <div className=\"flex items-center gap-2\">\r\n {isRecording ? (\r\n <button\r\n onClick={stopRecording}\r\n disabled={disabled}\r\n className=\"p-2.5 rounded-full bg-red-500 hover:bg-red-400 text-white transition-all duration-200 hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed shadow-lg shadow-red-500/25\"\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 <button\r\n onClick={startRecording}\r\n disabled={disabled || isLoading || isRateLimited}\r\n className=\"ai-input-btn-secondary p-2 rounded-lg transition-all duration-200 hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\"\r\n aria-label=\"Start recording\"\r\n >\r\n <MicIcon className=\"h-5 w-5\" />\r\n </button>\r\n <button\r\n onClick={submit}\r\n disabled={!canSubmit || disabled}\r\n className={`\r\n p-2.5 rounded-full transition-all duration-200\r\n disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100\r\n ${canSubmit\r\n ? 'ai-input-btn-primary hover:scale-105 active:scale-95 shadow-lg'\r\n : 'ai-input-btn-disabled'\r\n }\r\n `}\r\n aria-label=\"Send message\"\r\n >\r\n {isLoading ? <Spinner className=\"h-5 w-5\" /> : <ArrowUpIcon className=\"h-5 w-5\" />}\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 * Text/audio input with automatic light/dark mode detection.\r\n * \r\n * **Theme Detection (in order of priority):**\r\n * 1. `.dark` class on html/body (Tailwind/Next.js)\r\n * 2. `data-theme=\"dark\"` attribute\r\n * 3. `prefers-color-scheme` system preference\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 if (children) {\r\n return <>{children(inputState)}</>\r\n }\r\n\r\n return (\r\n <div className={`w-full ${className || ''}`}>\r\n <DefaultUI {...inputState} placeholder={placeholder} disabled={disabled} />\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-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}}
|
|
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-600:#d97706;--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{.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}.bg-red-400{background-color:var(--color-red-400)}.bg-red-500{background-color:var(--color-red-500)}.bg-transparent{background-color:#0000}.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-white{color:var(--color-white)}.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-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)}@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-red-400:hover{background-color:var(--color-red-400)}}.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)}}}.ai-input-container{background-color:#fff;border-color:#e4e4e7}.ai-input-container:focus-within{border-color:#f59e0b80;box-shadow:0 10px 15px -3px #f59e0b0d}.ai-input-container.ai-input-recording{border-color:#ef444480;box-shadow:0 10px 15px -3px #ef44441a}.ai-input-textarea{color:#18181b}.ai-input-textarea::placeholder{color:#a1a1aa}.ai-input-text-muted{color:#71717a}.ai-input-text-error{color:#ef4444}.ai-input-text-warning{color:#d97706}.ai-input-btn-secondary{color:#71717a}.ai-input-btn-secondary:hover{color:#d97706;background-color:#f4f4f5}.ai-input-btn-primary{color:#fff;background-color:#f59e0b;box-shadow:0 10px 15px -3px #f59e0b40}.ai-input-btn-primary:hover{background-color:#fbbf24}.ai-input-btn-disabled{color:#a1a1aa;background-color:#f4f4f5}@media (prefers-color-scheme:dark){.ai-input-container{background-color:#18181b;border-color:#27272a}.ai-input-container:focus-within{border-color:#f59e0b80;box-shadow:0 10px 15px -3px #f59e0b0d}.ai-input-container.ai-input-recording{border-color:#ef444480;box-shadow:0 10px 15px -3px #ef44441a}.ai-input-textarea{color:#f4f4f5}.ai-input-textarea::placeholder{color:#71717a}.ai-input-text-muted{color:#a1a1aa}.ai-input-text-error{color:#f87171}.ai-input-text-warning{color:#fbbf24}.ai-input-btn-secondary{color:#a1a1aa}.ai-input-btn-secondary:hover{color:#fbbf24;background-color:#27272a}.ai-input-btn-primary{color:#18181b;background-color:#f59e0b;box-shadow:0 10px 15px -3px #f59e0b40}.ai-input-btn-primary:hover{background-color:#fbbf24}.ai-input-btn-disabled{color:#71717a;background-color:#27272a}}.dark .ai-input-container,[data-theme=dark] .ai-input-container,[data-mode=dark] .ai-input-container{background-color:#18181b;border-color:#27272a}.dark .ai-input-container:focus-within,[data-theme=dark] .ai-input-container:focus-within,[data-mode=dark] .ai-input-container:focus-within{border-color:#f59e0b80;box-shadow:0 10px 15px -3px #f59e0b0d}.dark .ai-input-container.ai-input-recording,[data-theme=dark] .ai-input-container.ai-input-recording,[data-mode=dark] .ai-input-container.ai-input-recording{border-color:#ef444480;box-shadow:0 10px 15px -3px #ef44441a}.dark .ai-input-textarea,[data-theme=dark] .ai-input-textarea,[data-mode=dark] .ai-input-textarea{color:#f4f4f5}.dark .ai-input-textarea::placeholder,[data-theme=dark] .ai-input-textarea::placeholder,[data-mode=dark] .ai-input-textarea::placeholder{color:#71717a}.dark .ai-input-text-muted,[data-theme=dark] .ai-input-text-muted,[data-mode=dark] .ai-input-text-muted{color:#a1a1aa}.dark .ai-input-text-error,[data-theme=dark] .ai-input-text-error,[data-mode=dark] .ai-input-text-error{color:#f87171}.dark .ai-input-text-warning,[data-theme=dark] .ai-input-text-warning,[data-mode=dark] .ai-input-text-warning{color:#fbbf24}.dark .ai-input-btn-secondary,[data-theme=dark] .ai-input-btn-secondary,[data-mode=dark] .ai-input-btn-secondary{color:#a1a1aa}.dark .ai-input-btn-secondary:hover,[data-theme=dark] .ai-input-btn-secondary:hover,[data-mode=dark] .ai-input-btn-secondary:hover{color:#fbbf24;background-color:#27272a}.dark .ai-input-btn-primary,[data-theme=dark] .ai-input-btn-primary,[data-mode=dark] .ai-input-btn-primary{color:#18181b;background-color:#f59e0b;box-shadow:0 10px 15px -3px #f59e0b40}.dark .ai-input-btn-primary:hover,[data-theme=dark] .ai-input-btn-primary:hover,[data-mode=dark] .ai-input-btn-primary:hover{background-color:#fbbf24}.dark .ai-input-btn-disabled,[data-theme=dark] .ai-input-btn-disabled,[data-mode=dark] .ai-input-btn-disabled{color:#71717a;background-color:#27272a}.light .ai-input-container,[data-theme=light] .ai-input-container,[data-mode=light] .ai-input-container{background-color:#fff;border-color:#e4e4e7}.light .ai-input-textarea,[data-theme=light] .ai-input-textarea,[data-mode=light] .ai-input-textarea{color:#18181b}.light .ai-input-textarea::placeholder,[data-theme=light] .ai-input-textarea::placeholder,[data-mode=light] .ai-input-textarea::placeholder{color:#a1a1aa}.light .ai-input-text-muted,[data-theme=light] .ai-input-text-muted,[data-mode=light] .ai-input-text-muted,.light .ai-input-btn-secondary,[data-theme=light] .ai-input-btn-secondary,[data-mode=light] .ai-input-btn-secondary{color:#71717a}.light .ai-input-btn-secondary:hover,[data-theme=light] .ai-input-btn-secondary:hover,[data-mode=light] .ai-input-btn-secondary:hover{color:#d97706;background-color:#f4f4f5}.light .ai-input-btn-disabled,[data-theme=light] .ai-input-btn-disabled,[data-mode=light] .ai-input-btn-disabled{color:#a1a1aa;background-color:#f4f4f5}@keyframes wave{0%,to{transform:scaleY(.5)}50%{transform:scaleY(1)}}@keyframes pulse{50%{opacity:.5}}@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)}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-input-react",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.4",
|
|
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",
|