ai-input-react 1.0.0-beta.1

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/README.md ADDED
@@ -0,0 +1,294 @@
1
+ # ai-input-react
2
+
3
+ [![npm version](https://img.shields.io/npm/v/ai-input-react.svg)](https://www.npmjs.com/package/ai-input-react)
4
+ [![license](https://img.shields.io/npm/l/ai-input-react.svg)](https://github.com/Soptik1290/ai-input/blob/main/LICENSE)
5
+ [![npm downloads](https://img.shields.io/npm/dm/ai-input-react.svg)](https://www.npmjs.com/package/ai-input-react)
6
+
7
+ **A React input component with AI text/voice support.** Unified text and audio input with real-time waveform visualization, designed for AI-powered applications.
8
+
9
+ ## Why Use This?
10
+
11
+ - 🎤 **Unified Input** – Text and audio in a single component
12
+ - 🌊 **Real-time Waveform** – Audio visualization during recording
13
+ - 🎨 **Zero Config Styling** – Prepacked CSS, no Tailwind needed
14
+ - 🔌 **Headless Mode** – Full control with render props
15
+ - ⚡ **Framework Agnostic** – Next.js, Vite, Laravel, etc.
16
+
17
+ ---
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ # npm
23
+ npm install ai-input-react
24
+
25
+ # yarn
26
+ yarn add ai-input-react
27
+
28
+ # pnpm
29
+ pnpm add ai-input-react
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Quick Start
35
+
36
+ ```tsx
37
+ import { AiInput } from 'ai-input-react'
38
+ import 'ai-input-react/styles.css'
39
+
40
+ function App() {
41
+ return (
42
+ <AiInput
43
+ send={async (input) => {
44
+ const res = await fetch('/api/chat', {
45
+ method: 'POST',
46
+ body: JSON.stringify({ message: input }),
47
+ })
48
+ return res.json()
49
+ }}
50
+ onSuccess={(result) => console.log('Response:', result)}
51
+ />
52
+ )
53
+ }
54
+ ```
55
+
56
+ That's it! The component includes text input with a microphone button for audio recording.
57
+
58
+ ---
59
+
60
+ ## Framework Examples
61
+
62
+ <details>
63
+ <summary><strong>Next.js (App Router)</strong></summary>
64
+
65
+ ```tsx
66
+ // app/page.tsx
67
+ 'use client'
68
+
69
+ import { AiInput } from 'ai-input-react'
70
+ import 'ai-input-react/styles.css'
71
+
72
+ export default function Home() {
73
+ return (
74
+ <AiInput
75
+ send={async (input) => {
76
+ const res = await fetch('/api/chat', {
77
+ method: 'POST',
78
+ body: JSON.stringify({ message: input }),
79
+ })
80
+ return res.json()
81
+ }}
82
+ />
83
+ )
84
+ }
85
+ ```
86
+ </details>
87
+
88
+ <details>
89
+ <summary><strong>Laravel + Inertia</strong></summary>
90
+
91
+ ```tsx
92
+ // resources/js/Pages/Chat.tsx
93
+ import { AiInput } from 'ai-input-react'
94
+ import 'ai-input-react/styles.css'
95
+
96
+ export default function Chat({ csrfToken }: { csrfToken: string }) {
97
+ return (
98
+ <AiInput
99
+ send={async (input) => {
100
+ const res = await fetch('/api/chat', {
101
+ method: 'POST',
102
+ headers: {
103
+ 'X-CSRF-TOKEN': csrfToken,
104
+ 'Content-Type': 'application/json',
105
+ },
106
+ body: JSON.stringify({ message: input }),
107
+ })
108
+ return res.json()
109
+ }}
110
+ />
111
+ )
112
+ }
113
+ ```
114
+ </details>
115
+
116
+ <details>
117
+ <summary><strong>Vite</strong></summary>
118
+
119
+ ```tsx
120
+ // src/App.tsx
121
+ import { AiInput } from 'ai-input-react'
122
+ import 'ai-input-react/styles.css'
123
+
124
+ export default function App() {
125
+ return (
126
+ <AiInput
127
+ send={async (input) => {
128
+ const res = await fetch('/api/chat', {
129
+ method: 'POST',
130
+ body: JSON.stringify({ message: input }),
131
+ })
132
+ return res.json()
133
+ }}
134
+ />
135
+ )
136
+ }
137
+ ```
138
+ </details>
139
+
140
+ ---
141
+
142
+ ## GPT + Whisper Example
143
+
144
+ ```tsx
145
+ <AiInput
146
+ placeholder="Ask anything..."
147
+ send={async (input) => {
148
+ // Text → GPT
149
+ const response = await fetch('https://api.openai.com/v1/chat/completions', {
150
+ method: 'POST',
151
+ headers: {
152
+ 'Authorization': `Bearer ${token}`,
153
+ 'Content-Type': 'application/json',
154
+ },
155
+ body: JSON.stringify({
156
+ model: 'gpt-4',
157
+ messages: [{ role: 'user', content: input as string }],
158
+ }),
159
+ })
160
+ return response.json()
161
+ }}
162
+ sendAudio={async (blob) => {
163
+ // Audio → Whisper
164
+ const formData = new FormData()
165
+ formData.append('file', blob, 'audio.webm')
166
+ formData.append('model', 'whisper-1')
167
+
168
+ const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
169
+ method: 'POST',
170
+ headers: { 'Authorization': `Bearer ${token}` },
171
+ body: formData,
172
+ })
173
+ return response.json()
174
+ }}
175
+ onTranscription={(text) => console.log('Transcribed:', text)}
176
+ />
177
+ ```
178
+
179
+ ---
180
+
181
+ ## API Reference
182
+
183
+ ### Props
184
+
185
+ | Prop | Type | Required | Description |
186
+ |------|------|:--------:|-------------|
187
+ | `send` | `(input: string \| Blob) => Promise<any>` | ✅ | Transport function for sending input |
188
+ | `sendAudio` | `(blob: Blob) => Promise<any>` | | Separate transport for audio (uses `send` if not provided) |
189
+ | `placeholder` | `string` | | Input placeholder text |
190
+ | `disabled` | `boolean` | | Disable the input |
191
+ | `className` | `string` | | Additional CSS classes |
192
+ | `rateLimit` | `{ cooldownMs, maxRequests, windowMs }` | | Rate limiting configuration |
193
+ | `audioConfig` | `{ maxDurationMs, mimeTypes }` | | Audio recording settings |
194
+ | `onSuccess` | `(result: any) => void` | | Called on successful response |
195
+ | `onError` | `(error: Error) => void` | | Called on error |
196
+ | `onTranscription` | `(text: string) => void` | | Called when audio is transcribed |
197
+ | `children` | `(props: RenderProps) => ReactNode` | | Render prop for headless usage |
198
+
199
+ ### Render Props (Headless Mode)
200
+
201
+ ```tsx
202
+ <AiInput send={sendFn}>
203
+ {(props) => (
204
+ // Full control over UI
205
+ )}
206
+ </AiInput>
207
+ ```
208
+
209
+ | Prop | Type | Description |
210
+ |------|------|-------------|
211
+ | `text` | `string` | Current text value |
212
+ | `setText` | `(value: string) => void` | Update text |
213
+ | `submit` | `() => void` | Submit current input |
214
+ | `canSubmit` | `boolean` | Whether submit is allowed |
215
+ | `state` | `'idle' \| 'loading' \| 'success' \| 'error' \| 'recording'` | Current state |
216
+ | `isRecording` | `boolean` | Audio recording active |
217
+ | `startRecording` | `() => Promise<void>` | Start recording |
218
+ | `stopRecording` | `() => void` | Stop and send recording |
219
+ | `cancelRecording` | `() => void` | Discard recording |
220
+ | `audioLevels` | `number[]` | Waveform data (0-1) |
221
+ | `recordingDuration` | `number` | Recording time in ms |
222
+ | `error` | `Error \| null` | Current error |
223
+ | `reset` | `() => void` | Reset to idle state |
224
+
225
+ ---
226
+
227
+ ## Styling
228
+
229
+ ### Prepacked CSS (Recommended)
230
+
231
+ ```tsx
232
+ import 'ai-input-react/styles.css'
233
+ ```
234
+
235
+ Includes dark theme with zinc/amber colors, waveform visualization, and smooth animations.
236
+
237
+ ### Custom Styling (Tailwind)
238
+
239
+ If using Tailwind, don't import the CSS file. The component uses Tailwind utility classes that will be processed by your build.
240
+
241
+ ---
242
+
243
+ ## Security
244
+
245
+ > ⚠️ **Never store API keys in frontend code!**
246
+
247
+ Use short-lived tokens from your backend:
248
+
249
+ ```tsx
250
+ // ❌ Dangerous
251
+ const API_KEY = 'sk-...'
252
+
253
+ // ✅ Safe
254
+ const token = await getTokenFromBackend()
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Browser Support
260
+
261
+ | Browser | Version |
262
+ |---------|---------|
263
+ | Chrome | 49+ |
264
+ | Firefox | 36+ |
265
+ | Safari | 14.1+ |
266
+ | Edge | 79+ |
267
+
268
+ Audio recording requires HTTPS (or localhost) and microphone permission.
269
+
270
+ ---
271
+
272
+ ## Contributing
273
+
274
+ Contributions are welcome! Please feel free to submit a Pull Request.
275
+
276
+ 1. Fork the repository
277
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
278
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
279
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
280
+ 5. Open a Pull Request
281
+
282
+ ---
283
+
284
+ ## Issues & Support
285
+
286
+ - 🐛 **Bug Reports**: [Open an issue](https://github.com/Soptik1290/ai-input/issues/new)
287
+ - 💡 **Feature Requests**: [Open an issue](https://github.com/Soptik1290/ai-input/issues/new)
288
+ - 💬 **Questions**: [GitHub Discussions](https://github.com/Soptik1290/ai-input/discussions)
289
+
290
+ ---
291
+
292
+ ## License
293
+
294
+ [MIT](LICENSE) © 2024
@@ -0,0 +1,255 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * Current state of the AiInput component
6
+ */
7
+ type AiInputState = 'idle' | 'loading' | 'success' | 'error' | 'rate-limited' | 'recording';
8
+ /**
9
+ * Rate limiting configuration for UI protection
10
+ * Note: This is soft rate limiting for UX only.
11
+ * Actual rate limiting should be handled by the AI provider.
12
+ */
13
+ interface RateLimitConfig {
14
+ /** Cooldown between requests in milliseconds */
15
+ cooldownMs: number;
16
+ /** Maximum number of requests allowed in the time window */
17
+ maxRequests: number;
18
+ /** Time window in milliseconds for counting requests */
19
+ windowMs: number;
20
+ }
21
+ /**
22
+ * Audio recording configuration
23
+ */
24
+ interface AudioConfig {
25
+ /** Maximum recording duration in milliseconds */
26
+ maxDurationMs: number;
27
+ /**
28
+ * Allowed MIME types for recording
29
+ * @example ['audio/webm', 'audio/mp4', 'audio/ogg']
30
+ */
31
+ mimeTypes: string[];
32
+ }
33
+ /**
34
+ * Transport function for sending input to AI API.
35
+ * Must be provided by the host application.
36
+ *
37
+ * @param input - Text string or audio Blob to send
38
+ * @returns Promise resolving to the API response
39
+ */
40
+ type SendFunction = (input: string | Blob) => Promise<unknown>;
41
+ /**
42
+ * Props passed to render function for headless usage
43
+ */
44
+ interface AiInputRenderProps {
45
+ /** Current component state */
46
+ state: AiInputState;
47
+ /** Error if state is 'error' */
48
+ error: Error | null;
49
+ /** Result from last successful request */
50
+ result: unknown;
51
+ /** Current text value (controlled) */
52
+ text: string;
53
+ /** Update text value */
54
+ setText: (value: string) => void;
55
+ /** Submit current text or audio */
56
+ submit: () => void;
57
+ /** Whether submit is currently allowed */
58
+ canSubmit: boolean;
59
+ /** Whether currently recording */
60
+ isRecording: boolean;
61
+ /** Start audio recording */
62
+ startRecording: () => Promise<void>;
63
+ /** Stop audio recording and send */
64
+ stopRecording: () => void;
65
+ /** Cancel audio recording (discard) */
66
+ cancelRecording: () => void;
67
+ /** Current recording duration in milliseconds */
68
+ recordingDuration: number;
69
+ /** Maximum recording duration in milliseconds */
70
+ maxRecordingDuration: number;
71
+ /** Audio levels for waveform visualization (0-1 normalized, 12 bars) */
72
+ audioLevels: number[];
73
+ /** Remaining cooldown time in milliseconds */
74
+ cooldownRemaining: number;
75
+ /** Remaining requests in current window */
76
+ requestsRemaining: number;
77
+ /** Reset component to idle state */
78
+ reset: () => void;
79
+ }
80
+ /**
81
+ * Props for the AiInput component
82
+ */
83
+ interface AiInputProps {
84
+ /**
85
+ * Transport function for sending input to AI API.
86
+ * Must be provided by the host application.
87
+ */
88
+ send: SendFunction;
89
+ /**
90
+ * Transport function specifically for audio (optional).
91
+ * If provided, audio will be sent via this function.
92
+ * If not provided, audio will be sent via `send`.
93
+ */
94
+ sendAudio?: SendFunction;
95
+ /** Rate limiting configuration (optional) */
96
+ rateLimit?: Partial<RateLimitConfig>;
97
+ /** Audio configuration (optional) */
98
+ audioConfig?: Partial<AudioConfig>;
99
+ /** Callback when request succeeds */
100
+ onSuccess?: (result: unknown) => void;
101
+ /** Callback when request fails */
102
+ onError?: (error: Error) => void;
103
+ /**
104
+ * Callback when audio transcription is received.
105
+ * Use this to set the text in the input after transcription.
106
+ */
107
+ onTranscription?: (text: string) => void;
108
+ /**
109
+ * Render function for headless usage.
110
+ * When provided, default UI is not rendered.
111
+ */
112
+ children?: (props: AiInputRenderProps) => ReactNode;
113
+ /** Placeholder text for input */
114
+ placeholder?: string;
115
+ /** Additional CSS classes for the container */
116
+ className?: string;
117
+ /** Whether the input is disabled */
118
+ disabled?: boolean;
119
+ }
120
+ /**
121
+ * Options for useRateLimiter hook
122
+ */
123
+ interface UseRateLimiterOptions {
124
+ cooldownMs: number;
125
+ maxRequests: number;
126
+ windowMs: number;
127
+ }
128
+ /**
129
+ * Return type for useRateLimiter hook
130
+ */
131
+ interface UseRateLimiterReturn {
132
+ canRequest: boolean;
133
+ cooldownRemaining: number;
134
+ requestsRemaining: number;
135
+ recordRequest: () => void;
136
+ reset: () => void;
137
+ }
138
+ /**
139
+ * Options for useAudioRecorder hook
140
+ */
141
+ interface UseAudioRecorderOptions {
142
+ maxDurationMs: number;
143
+ mimeTypes: string[];
144
+ onRecordingComplete?: (blob: Blob) => void;
145
+ }
146
+ /**
147
+ * Return type for useAudioRecorder hook
148
+ */
149
+ interface UseAudioRecorderReturn {
150
+ isRecording: boolean;
151
+ isSupported: boolean;
152
+ duration: number;
153
+ audioBlob: Blob | null;
154
+ audioLevels: number[];
155
+ error: Error | null;
156
+ startRecording: () => Promise<void>;
157
+ stopRecording: () => void;
158
+ cancelRecording: () => void;
159
+ reset: () => void;
160
+ }
161
+ /**
162
+ * Options for useAiInput hook
163
+ */
164
+ interface UseAiInputOptions {
165
+ send: SendFunction;
166
+ sendAudio?: SendFunction;
167
+ rateLimit?: Partial<RateLimitConfig>;
168
+ audioConfig?: Partial<AudioConfig>;
169
+ onSuccess?: (result: unknown) => void;
170
+ onError?: (error: Error) => void;
171
+ onTranscription?: (text: string) => void;
172
+ }
173
+ /**
174
+ * Return type for useAiInput hook
175
+ */
176
+ type UseAiInputReturn = AiInputRenderProps;
177
+ /** @deprecated Use AiInputProps without mode */
178
+ type AiInputMode = 'text' | 'audio';
179
+
180
+ /**
181
+ * AiInput Component
182
+ *
183
+ * A React component for text/audio input with AI API integration.
184
+ * Unified design with text input and audio recording in a single component.
185
+ *
186
+ * @example
187
+ * // Basic usage
188
+ * <AiInput
189
+ * send={async (input) => {
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>
221
+ */
222
+ declare function AiInput({ send, sendAudio, rateLimit, audioConfig, onSuccess, onError, onTranscription, children, placeholder, className, disabled, }: AiInputProps): react_jsx_runtime.JSX.Element;
223
+
224
+ /**
225
+ * Main hook for AI input functionality.
226
+ * Combines rate limiting, audio recording, and API communication.
227
+ * Unified design - text and audio in single component.
228
+ *
229
+ * @param options - Configuration options
230
+ * @returns Complete state and controls for AI input
231
+ */
232
+ declare function useAiInput(options: UseAiInputOptions): UseAiInputReturn;
233
+
234
+ /**
235
+ * Hook for audio recording using Web APIs.
236
+ * Uses navigator.mediaDevices, MediaRecorder, and Web Audio API for visualization.
237
+ *
238
+ * @param options - Audio recording configuration
239
+ * @returns Audio recorder state and controls
240
+ */
241
+ declare function useAudioRecorder(options?: Partial<UseAudioRecorderOptions>): UseAudioRecorderReturn;
242
+
243
+ /**
244
+ * Hook for soft rate limiting at the UI level.
245
+ * Provides UX protection by tracking requests and enforcing cooldowns.
246
+ *
247
+ * Note: This is not a security measure. Actual rate limiting
248
+ * should be handled by the AI provider or backend.
249
+ *
250
+ * @param options - Rate limiting configuration
251
+ * @returns Rate limiter state and controls
252
+ */
253
+ declare function useRateLimiter(options?: Partial<UseRateLimiterOptions>): UseRateLimiterReturn;
254
+
255
+ export { AiInput, type AiInputMode, type AiInputProps, type AiInputRenderProps, type AiInputState, type AudioConfig, type RateLimitConfig, type SendFunction, type UseAiInputOptions, type UseAiInputReturn, type UseAudioRecorderOptions, type UseAudioRecorderReturn, type UseRateLimiterOptions, type UseRateLimiterReturn, useAiInput, useAudioRecorder, useRateLimiter };