@leia-org/luke-client 0.1.0

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.
Files changed (51) hide show
  1. package/README.md +69 -0
  2. package/dist/Luke.css +430 -0
  3. package/dist/components/AudioControls.d.ts +12 -0
  4. package/dist/components/AudioControls.d.ts.map +1 -0
  5. package/dist/components/AudioControls.js +43 -0
  6. package/dist/components/AudioControls.js.map +1 -0
  7. package/dist/components/ConnectionStatus.d.ts +7 -0
  8. package/dist/components/ConnectionStatus.d.ts.map +1 -0
  9. package/dist/components/ConnectionStatus.js +33 -0
  10. package/dist/components/ConnectionStatus.js.map +1 -0
  11. package/dist/components/LukeProvider.d.ts +5 -0
  12. package/dist/components/LukeProvider.d.ts.map +1 -0
  13. package/dist/components/LukeProvider.js +25 -0
  14. package/dist/components/LukeProvider.js.map +1 -0
  15. package/dist/components/TranscriptionDisplay.d.ts +9 -0
  16. package/dist/components/TranscriptionDisplay.d.ts.map +1 -0
  17. package/dist/components/TranscriptionDisplay.js +22 -0
  18. package/dist/components/TranscriptionDisplay.js.map +1 -0
  19. package/dist/hooks/useLuke.d.ts +3 -0
  20. package/dist/hooks/useLuke.d.ts.map +1 -0
  21. package/dist/hooks/useLuke.js +382 -0
  22. package/dist/hooks/useLuke.js.map +1 -0
  23. package/dist/index.d.ts +11 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +12 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/types.d.ts +89 -0
  28. package/dist/types.d.ts.map +1 -0
  29. package/dist/types.js +4 -0
  30. package/dist/types.js.map +1 -0
  31. package/dist/ui/VoiceClientUI.d.ts +17 -0
  32. package/dist/ui/VoiceClientUI.d.ts.map +1 -0
  33. package/dist/ui/VoiceClientUI.js +84 -0
  34. package/dist/ui/VoiceClientUI.js.map +1 -0
  35. package/dist/ui/components/Icons.d.ts +8 -0
  36. package/dist/ui/components/Icons.d.ts.map +1 -0
  37. package/dist/ui/components/Icons.js +9 -0
  38. package/dist/ui/components/Icons.js.map +1 -0
  39. package/dist/ui/index.d.ts +3 -0
  40. package/dist/ui/index.d.ts.map +1 -0
  41. package/dist/ui/index.js +3 -0
  42. package/dist/ui/index.js.map +1 -0
  43. package/dist/worker/audio-utils.d.ts +5 -0
  44. package/dist/worker/audio-utils.d.ts.map +1 -0
  45. package/dist/worker/audio-utils.js +52 -0
  46. package/dist/worker/audio-utils.js.map +1 -0
  47. package/dist/worker/audio.worker.d.ts +2 -0
  48. package/dist/worker/audio.worker.d.ts.map +1 -0
  49. package/dist/worker/audio.worker.js +60 -0
  50. package/dist/worker/audio.worker.js.map +1 -0
  51. package/package.json +37 -0
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # @leia-org/luke-client
2
+
3
+ React client library for the Luke realtime AI voice communication platform.
4
+
5
+ ## Features
6
+
7
+ - **LukeProvider**: Context provider that manages the WebSocket connection and audio state.
8
+ - **AudioControls**: Ready-made component for microphone and speaker control.
9
+ - **TranscriptionDisplay**: Component to show real-time transcripts from the conversation.
10
+ - **Hooks**: Custom hooks like `useLukeContext` for building custom UI.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pnpm add @leia-org/luke-client
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ Wrap your application (or the part that needs voice features) with `LukeProvider`.
21
+
22
+ ```tsx
23
+ import { LukeProvider, useLukeContext, AudioControls, TranscriptionDisplay } from '@leia-org/luke-client';
24
+
25
+ function App() {
26
+ const token = "your_auth_token_here";
27
+ const serverUrl = "ws://localhost:3001";
28
+
29
+ return (
30
+ <LukeProvider serverUrl={serverUrl} authToken={token}>
31
+ <VoiceChat />
32
+ </LukeProvider>
33
+ );
34
+ }
35
+
36
+ function VoiceChat() {
37
+ const { providers, selectProvider, transcription } = useLukeContext();
38
+
39
+ return (
40
+ <div>
41
+ <h3>Conversation</h3>
42
+ <AudioControls />
43
+ <TranscriptionDisplay messages={transcription} />
44
+ </div>
45
+ );
46
+ }
47
+ ```
48
+
49
+ ## API
50
+
51
+ ### LukeProvider Props
52
+
53
+ - `serverUrl` (string): The WebSocket URL of your Luke server.
54
+ - `authToken` (string): JWT or auth token for connection.
55
+ - `onError` (function): Callback for connection errors.
56
+
57
+ ### useLukeContext
58
+
59
+ Returns an object with:
60
+
61
+ - `isConnected` (boolean)
62
+ - `isRecording` (boolean)
63
+ - `transcription` (array): List of transcription messages.
64
+ - `providers` (array): List of available AI providers from the server.
65
+ - `selectProvider` (function): Function to switch providers.
66
+
67
+ ## License
68
+
69
+ MIT
package/dist/Luke.css ADDED
@@ -0,0 +1,430 @@
1
+ /* Luke Client UI Library Styles - Shadcn/Vercel Inspired Theme */
2
+
3
+ :root {
4
+ /* Shadcn Zinc Platte */
5
+ --luke-white: #ffffff;
6
+ --luke-zinc-50: #fafafa;
7
+ --luke-zinc-100: #f4f4f5;
8
+ --luke-zinc-200: #e4e4e7;
9
+ --luke-zinc-300: #d4d4d8;
10
+ --luke-zinc-400: #a1a1aa;
11
+ --luke-zinc-500: #71717a;
12
+ --luke-zinc-600: #52525b;
13
+ --luke-zinc-700: #3f3f46;
14
+ --luke-zinc-800: #27272a;
15
+ --luke-zinc-900: #18181b;
16
+ --luke-zinc-950: #09090b;
17
+
18
+ --luke-bg-color: var(--luke-white);
19
+ --luke-surface-color: var(--luke-white);
20
+ /* Cards often white with border */
21
+ --luke-surface-secondary: var(--luke-zinc-50);
22
+ --luke-border-color: var(--luke-zinc-200);
23
+
24
+ --luke-text-primary: var(--luke-zinc-950);
25
+ --luke-text-secondary: var(--luke-zinc-500);
26
+
27
+ /* Primary Action (Black in Vercel/Shadcn) */
28
+ --luke-primary-bg: #000000;
29
+ --luke-primary-fg: #ffffff;
30
+
31
+ /* Status */
32
+ --luke-error-color: #ef4444;
33
+ --luke-success-color: #10b981;
34
+ --luke-warning-color: #f59e0b;
35
+
36
+ /* UI Tokens */
37
+ --luke-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
38
+ --luke-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
39
+ --luke-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
40
+ --luke-radius-sm: 0.375rem;
41
+ /* 6px */
42
+ --luke-radius-md: 0.5rem;
43
+ /* 8px */
44
+ --luke-radius-lg: 0.75rem;
45
+ /* 12px */
46
+
47
+ --luke-font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
48
+ --luke-font-size-sm: 0.875rem;
49
+ --luke-font-size-base: 1rem;
50
+ }
51
+
52
+ [data-luke-theme='dark'] {
53
+ --luke-bg-color: var(--luke-zinc-950);
54
+ --luke-surface-color: var(--luke-zinc-950);
55
+ --luke-surface-secondary: var(--luke-zinc-900);
56
+ --luke-border-color: var(--luke-zinc-800);
57
+
58
+ --luke-text-primary: var(--luke-zinc-50);
59
+ --luke-text-secondary: var(--luke-zinc-400);
60
+
61
+ --luke-primary-bg: var(--luke-zinc-50);
62
+ --luke-primary-fg: var(--luke-zinc-900);
63
+ }
64
+
65
+ /* Base Wrapper */
66
+ .luke-wrapper {
67
+ font-family: var(--luke-font-family);
68
+ color: var(--luke-text-primary);
69
+ box-sizing: border-box;
70
+ -webkit-font-smoothing: antialiased;
71
+ -moz-osx-font-smoothing: grayscale;
72
+ }
73
+
74
+ .luke-wrapper * {
75
+ box-sizing: border-box;
76
+ }
77
+
78
+ /* Modes */
79
+ .luke-mode-fullscreen {
80
+ position: fixed;
81
+ inset: 0;
82
+ background-color: var(--luke-bg-color);
83
+ z-index: 9999;
84
+ display: flex;
85
+ }
86
+
87
+ .luke-mode-modal {
88
+ position: fixed;
89
+ z-index: 9999;
90
+ width: 400px;
91
+ max-height: 80vh;
92
+ height: 650px;
93
+ background-color: var(--luke-bg-color);
94
+ border: 1px solid var(--luke-border-color);
95
+ border-radius: var(--luke-radius-lg);
96
+ box-shadow: var(--luke-shadow-lg);
97
+ display: flex;
98
+ flex-direction: column;
99
+ overflow: hidden;
100
+ transition: opacity 0.2s ease, transform 0.2s ease;
101
+ animation: luke-fade-in 0.2s ease-out;
102
+ }
103
+
104
+ @keyframes luke-fade-in {
105
+ from {
106
+ opacity: 0;
107
+ transform: translate(-50%, -48%) scale(0.96);
108
+ }
109
+
110
+ to {
111
+ opacity: 1;
112
+ transform: translate(-50%, -50%) scale(1);
113
+ }
114
+ }
115
+
116
+ /* Position adjustments for animation trigger */
117
+ .luke-mode-modal.luke-pos-bottom-right {
118
+ bottom: 24px;
119
+ right: 24px;
120
+ animation: luke-slide-up-right 0.3s cubic-bezier(0.16, 1, 0.3, 1);
121
+ }
122
+
123
+ .luke-mode-modal.luke-pos-bottom-left {
124
+ bottom: 24px;
125
+ left: 24px;
126
+ animation: luke-slide-up-left 0.3s cubic-bezier(0.16, 1, 0.3, 1);
127
+ }
128
+
129
+ .luke-mode-modal.luke-pos-center {
130
+ top: 50%;
131
+ left: 50%;
132
+ transform: translate(-50%, -50%);
133
+ }
134
+
135
+ @keyframes luke-slide-up-right {
136
+ from {
137
+ opacity: 0;
138
+ transform: translateY(10px);
139
+ }
140
+
141
+ to {
142
+ opacity: 1;
143
+ transform: translateY(0);
144
+ }
145
+ }
146
+
147
+ @keyframes luke-slide-up-left {
148
+ from {
149
+ opacity: 0;
150
+ transform: translateY(10px);
151
+ }
152
+
153
+ to {
154
+ opacity: 1;
155
+ transform: translateY(0);
156
+ }
157
+ }
158
+
159
+
160
+ /* Layout */
161
+ .luke-header {
162
+ height: 60px;
163
+ padding: 0 20px;
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: space-between;
167
+ border-bottom: 1px solid var(--luke-border-color);
168
+ background-color: var(--luke-surface-color);
169
+ }
170
+
171
+ .luke-header-title {
172
+ font-weight: 600;
173
+ font-size: 14px;
174
+ letter-spacing: -0.01em;
175
+ }
176
+
177
+ .luke-body {
178
+ flex: 1;
179
+ display: flex;
180
+ flex-direction: column;
181
+ overflow: hidden;
182
+ background-color: var(--luke-surface-secondary);
183
+ /* Subtle contrast for chat area */
184
+ }
185
+
186
+ /* Chat Area */
187
+ .luke-messages {
188
+ flex: 1;
189
+ overflow-y: auto;
190
+ padding: 20px;
191
+ display: flex;
192
+ flex-direction: column;
193
+ gap: 24px;
194
+ }
195
+
196
+ .luke-message {
197
+ display: flex;
198
+ flex-direction: column;
199
+ max-width: 85%;
200
+ gap: 6px;
201
+ }
202
+
203
+ .luke-message.user {
204
+ align-self: flex-end;
205
+ align-items: flex-end;
206
+ }
207
+
208
+ .luke-message.assistant {
209
+ align-self: flex-start;
210
+ align-items: flex-start;
211
+ }
212
+
213
+ .luke-bubble {
214
+ padding: 12px 16px;
215
+ border-radius: var(--luke-radius-lg);
216
+ font-size: 14px;
217
+ line-height: 1.6;
218
+ position: relative;
219
+ }
220
+
221
+ .luke-message.user .luke-bubble {
222
+ background-color: var(--luke-primary-bg);
223
+ color: var(--luke-primary-fg);
224
+ border-bottom-right-radius: 4px;
225
+ }
226
+
227
+ .luke-message.assistant .luke-bubble {
228
+ background-color: var(--luke-surface-color);
229
+ color: var(--luke-text-primary);
230
+ border: 1px solid var(--luke-border-color);
231
+ border-bottom-left-radius: 4px;
232
+ box-shadow: var(--luke-shadow-sm);
233
+ }
234
+
235
+ .luke-role-label {
236
+ font-size: 12px;
237
+ font-weight: 500;
238
+ color: var(--luke-text-secondary);
239
+ }
240
+
241
+ /* Controls Area */
242
+ .luke-controls {
243
+ padding: 20px;
244
+ border-top: 1px solid var(--luke-border-color);
245
+ background-color: var(--luke-bg-color);
246
+ display: flex;
247
+ flex-direction: column;
248
+ gap: 16px;
249
+ }
250
+
251
+ .luke-mic-area {
252
+ display: flex;
253
+ flex-direction: column;
254
+ align-items: center;
255
+ gap: 12px;
256
+ position: relative;
257
+ min-height: 80px;
258
+ justify-content: center;
259
+ }
260
+
261
+ .luke-mic-wrapper {
262
+ position: relative;
263
+ display: flex;
264
+ align-items: center;
265
+ justify-content: center;
266
+ width: 100px;
267
+ height: 100px;
268
+ }
269
+
270
+ .luke-mic-btn {
271
+ width: 64px;
272
+ height: 64px;
273
+ border-radius: 50%;
274
+ background-color: var(--luke-primary-bg);
275
+ color: var(--luke-primary-fg);
276
+ border: none;
277
+ display: flex;
278
+ align-items: center;
279
+ justify-content: center;
280
+ cursor: pointer;
281
+ transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
282
+ z-index: 10;
283
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
284
+ }
285
+
286
+ .luke-mic-btn:hover {
287
+ transform: scale(1.05);
288
+ opacity: 0.9;
289
+ }
290
+
291
+ .luke-mic-btn.active {
292
+ background-color: var(--luke-error-color);
293
+ color: white;
294
+ animation: luke-breathing 2s infinite ease-in-out;
295
+ }
296
+
297
+ @keyframes luke-breathing {
298
+ 0% {
299
+ transform: scale(1);
300
+ box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4);
301
+ }
302
+
303
+ 50% {
304
+ transform: scale(1.05);
305
+ box-shadow: 0 0 0 10px rgba(239, 68, 68, 0);
306
+ }
307
+
308
+ 100% {
309
+ transform: scale(1);
310
+ box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
311
+ }
312
+ }
313
+
314
+ .luke-status-text {
315
+ font-size: 13px;
316
+ font-weight: 500;
317
+ color: var(--luke-text-secondary);
318
+ letter-spacing: 0.02em;
319
+ }
320
+
321
+ /* Buttons/Icons */
322
+ .luke-btn-icon {
323
+ width: 32px;
324
+ height: 32px;
325
+ display: flex;
326
+ align-items: center;
327
+ justify-content: center;
328
+ border-radius: var(--luke-radius-md);
329
+ border: none;
330
+ background: transparent;
331
+ color: var(--luke-text-secondary);
332
+ cursor: pointer;
333
+ transition: background-color 0.2s, color 0.2s;
334
+ }
335
+
336
+ .luke-btn-icon:hover {
337
+ background-color: var(--luke-zinc-100);
338
+ color: var(--luke-text-primary);
339
+ }
340
+
341
+ /* Connect Button (Custom in component but styled here for consistency if class used) */
342
+ .luke-btn-primary {
343
+ width: 100%;
344
+ padding: 0 16px;
345
+ height: 40px;
346
+ display: inline-flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ border-radius: var(--luke-radius-md);
350
+ font-size: 14px;
351
+ font-weight: 500;
352
+ background-color: var(--luke-primary-bg);
353
+ color: var(--luke-primary-fg);
354
+ border: none;
355
+ cursor: pointer;
356
+ transition: opacity 0.2s;
357
+ }
358
+
359
+ .luke-btn-primary:hover {
360
+ opacity: 0.9;
361
+ }
362
+
363
+ /* Visualizer */
364
+ .luke-visualizer {
365
+ position: absolute;
366
+ top: 50%;
367
+ left: 50%;
368
+ transform: translate(-50%, -50%);
369
+ width: 100px;
370
+ height: 100px;
371
+ border-radius: 50%;
372
+ border: 1px solid var(--luke-border-color);
373
+ opacity: 0.5;
374
+ pointer-events: none;
375
+ }
376
+
377
+ /* Selection Pill */
378
+ .luke-selection-pill {
379
+ position: relative;
380
+ display: inline-flex;
381
+ align-items: center;
382
+ gap: 6px;
383
+ padding: 6px 12px;
384
+ background-color: var(--luke-zinc-100);
385
+ border-radius: 9999px;
386
+ /* Pill shape */
387
+ color: var(--luke-text-primary);
388
+ font-size: 13px;
389
+ font-weight: 500;
390
+ cursor: pointer;
391
+ transition: background-color 0.2s;
392
+ user-select: none;
393
+ max-width: 160px;
394
+ }
395
+
396
+ .luke-selection-pill:hover {
397
+ background-color: var(--luke-zinc-200);
398
+ }
399
+
400
+ .luke-pill-select {
401
+ position: absolute;
402
+ inset: 0;
403
+ width: 100%;
404
+ height: 100%;
405
+ opacity: 0;
406
+ cursor: pointer;
407
+ appearance: none;
408
+ z-index: 10;
409
+ }
410
+
411
+ .luke-pill-icon,
412
+ .luke-pill-chevron {
413
+ display: flex;
414
+ align-items: center;
415
+ color: var(--luke-text-secondary);
416
+ }
417
+
418
+ .luke-pill-label {
419
+ white-space: nowrap;
420
+ overflow: hidden;
421
+ text-overflow: ellipsis;
422
+ }
423
+
424
+ .luke-pills-container {
425
+ display: flex;
426
+ flex-wrap: wrap;
427
+ justify-content: center;
428
+ gap: 8px;
429
+ margin-top: 8px;
430
+ }
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ export interface AudioControlsProps {
3
+ className?: string;
4
+ renderButton?: (props: {
5
+ isRecording: boolean;
6
+ isConnected: boolean;
7
+ onClick: () => void;
8
+ }) => React.ReactNode;
9
+ renderLevel?: (level: number) => React.ReactNode;
10
+ }
11
+ export declare function AudioControls({ className, renderButton, renderLevel, }: AudioControlsProps): React.ReactElement;
12
+ //# sourceMappingURL=AudioControls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioControls.d.ts","sourceRoot":"","sources":["../../src/components/AudioControls.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,kBAAkB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE;QACnB,WAAW,EAAE,OAAO,CAAC;QACrB,WAAW,EAAE,OAAO,CAAC;QACrB,OAAO,EAAE,MAAM,IAAI,CAAC;KACvB,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;CACpD;AAED,wBAAgB,aAAa,CAAC,EAC1B,SAAS,EACT,YAAY,EACZ,WAAW,GACd,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAiEzC"}
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useLukeContext } from './LukeProvider.js';
3
+ export function AudioControls({ className, renderButton, renderLevel, }) {
4
+ const { isRecording, isConnected, startRecording, stopRecording, audioLevel } = useLukeContext();
5
+ const handleClick = () => {
6
+ if (isRecording) {
7
+ stopRecording();
8
+ }
9
+ else {
10
+ startRecording();
11
+ }
12
+ };
13
+ // Default button rendering
14
+ const defaultButton = (_jsx("button", { type: "button", onClick: handleClick, disabled: !isConnected, "aria-label": isRecording ? 'Stop recording' : 'Start recording', style: {
15
+ width: 64,
16
+ height: 64,
17
+ borderRadius: '50%',
18
+ border: 'none',
19
+ backgroundColor: isRecording ? '#ef4444' : '#3b82f6',
20
+ color: 'white',
21
+ cursor: isConnected ? 'pointer' : 'not-allowed',
22
+ opacity: isConnected ? 1 : 0.5,
23
+ transition: 'background-color 0.2s',
24
+ }, children: isRecording ? 'Stop' : 'Mic' }));
25
+ // Default level indicator
26
+ const defaultLevel = (_jsx("div", { style: {
27
+ width: 100,
28
+ height: 8,
29
+ backgroundColor: '#e5e7eb',
30
+ borderRadius: 4,
31
+ overflow: 'hidden',
32
+ marginTop: 8,
33
+ }, children: _jsx("div", { style: {
34
+ width: `${Math.min(100, audioLevel * 100)}%`,
35
+ height: '100%',
36
+ backgroundColor: '#22c55e',
37
+ transition: 'width 0.05s',
38
+ } }) }));
39
+ return (_jsxs("div", { className: className, style: { display: 'flex', flexDirection: 'column', alignItems: 'center' }, children: [renderButton
40
+ ? renderButton({ isRecording, isConnected, onClick: handleClick })
41
+ : defaultButton, renderLevel ? renderLevel(audioLevel) : defaultLevel] }));
42
+ }
43
+ //# sourceMappingURL=AudioControls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioControls.js","sourceRoot":"","sources":["../../src/components/AudioControls.tsx"],"names":[],"mappings":";AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAanD,MAAM,UAAU,aAAa,CAAC,EAC1B,SAAS,EACT,YAAY,EACZ,WAAW,GACM;IACjB,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE,CAAC;IAEjG,MAAM,WAAW,GAAG,GAAG,EAAE;QACrB,IAAI,WAAW,EAAE,CAAC;YACd,aAAa,EAAE,CAAC;QACpB,CAAC;aAAM,CAAC;YACJ,cAAc,EAAE,CAAC;QACrB,CAAC;IACL,CAAC,CAAC;IAEF,2BAA2B;IAC3B,MAAM,aAAa,GAAG,CAClB,iBACI,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,CAAC,WAAW,gBACV,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,iBAAiB,EAC9D,KAAK,EAAE;YACH,KAAK,EAAE,EAAE;YACT,MAAM,EAAE,EAAE;YACV,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,MAAM;YACd,eAAe,EAAE,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;YACpD,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa;YAC/C,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;YAC9B,UAAU,EAAE,uBAAuB;SACtC,YAEA,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GACxB,CACZ,CAAC;IAEF,0BAA0B;IAC1B,MAAM,YAAY,GAAG,CACjB,cACI,KAAK,EAAE;YACH,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,CAAC;YACT,eAAe,EAAE,SAAS;YAC1B,YAAY,EAAE,CAAC;YACf,QAAQ,EAAE,QAAQ;YAClB,SAAS,EAAE,CAAC;SACf,YAED,cACI,KAAK,EAAE;gBACH,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,GAAG,CAAC,GAAG;gBAC5C,MAAM,EAAE,MAAM;gBACd,eAAe,EAAE,SAAS;gBAC1B,UAAU,EAAE,aAAa;aAC5B,GACH,GACA,CACT,CAAC;IAEF,OAAO,CACH,eAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,aAC/F,YAAY;gBACT,CAAC,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;gBAClE,CAAC,CAAC,aAAa,EAClB,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,YAAY,IACnD,CACT,CAAC;AACN,CAAC"}
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ export interface ConnectionStatusProps {
3
+ className?: string;
4
+ showProvider?: boolean;
5
+ }
6
+ export declare function ConnectionStatus({ className, showProvider, }: ConnectionStatusProps): React.ReactElement;
7
+ //# sourceMappingURL=ConnectionStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConnectionStatus.d.ts","sourceRoot":"","sources":["../../src/components/ConnectionStatus.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,qBAAqB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,SAAS,EACT,YAAmB,GACtB,EAAE,qBAAqB,GAAG,KAAK,CAAC,YAAY,CA8D5C"}
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useLukeContext } from './LukeProvider.js';
3
+ export function ConnectionStatus({ className, showProvider = true, }) {
4
+ const { connectionState, selectedProvider, connect, disconnect } = useLukeContext();
5
+ const statusColors = {
6
+ disconnected: '#9ca3af',
7
+ connecting: '#f59e0b',
8
+ connected: '#22c55e',
9
+ error: '#ef4444',
10
+ };
11
+ return (_jsxs("div", { className: className, style: {
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ gap: 12,
15
+ padding: '8px 16px',
16
+ backgroundColor: '#f9fafb',
17
+ borderRadius: 8,
18
+ }, children: [_jsx("div", { style: {
19
+ width: 10,
20
+ height: 10,
21
+ borderRadius: '50%',
22
+ backgroundColor: statusColors[connectionState],
23
+ } }), _jsx("span", { style: { fontSize: 14, color: '#374151' }, children: connectionState.charAt(0).toUpperCase() + connectionState.slice(1) }), showProvider && selectedProvider && (_jsxs("span", { style: { fontSize: 12, color: '#6b7280' }, children: ["(", selectedProvider.name, ")"] })), _jsx("button", { type: "button", onClick: connectionState === 'disconnected' ? connect : disconnect, style: {
24
+ marginLeft: 'auto',
25
+ padding: '4px 12px',
26
+ fontSize: 12,
27
+ border: '1px solid #d1d5db',
28
+ borderRadius: 4,
29
+ backgroundColor: 'white',
30
+ cursor: 'pointer',
31
+ }, children: connectionState === 'disconnected' ? 'Connect' : 'Disconnect' })] }));
32
+ }
33
+ //# sourceMappingURL=ConnectionStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConnectionStatus.js","sourceRoot":"","sources":["../../src/components/ConnectionStatus.tsx"],"names":[],"mappings":";AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAOnD,MAAM,UAAU,gBAAgB,CAAC,EAC7B,SAAS,EACT,YAAY,GAAG,IAAI,GACC;IACpB,MAAM,EAAE,eAAe,EAAE,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE,CAAC;IAEpF,MAAM,YAAY,GAA2B;QACzC,YAAY,EAAE,SAAS;QACvB,UAAU,EAAE,SAAS;QACrB,SAAS,EAAE,SAAS;QACpB,KAAK,EAAE,SAAS;KACnB,CAAC;IAEF,OAAO,CACH,eACI,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE;YACH,OAAO,EAAE,MAAM;YACf,UAAU,EAAE,QAAQ;YACpB,GAAG,EAAE,EAAE;YACP,OAAO,EAAE,UAAU;YACnB,eAAe,EAAE,SAAS;YAC1B,YAAY,EAAE,CAAC;SAClB,aAGD,cACI,KAAK,EAAE;oBACH,KAAK,EAAE,EAAE;oBACT,MAAM,EAAE,EAAE;oBACV,YAAY,EAAE,KAAK;oBACnB,eAAe,EAAE,YAAY,CAAC,eAAe,CAAC;iBACjD,GACH,EAGF,eAAM,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,YAC1C,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,GAChE,EAGN,YAAY,IAAI,gBAAgB,IAAI,CACjC,gBAAM,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,kBACzC,gBAAgB,CAAC,IAAI,SACpB,CACV,EAGD,iBACI,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,eAAe,KAAK,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,EAClE,KAAK,EAAE;oBACH,UAAU,EAAE,MAAM;oBAClB,OAAO,EAAE,UAAU;oBACnB,QAAQ,EAAE,EAAE;oBACZ,MAAM,EAAE,mBAAmB;oBAC3B,YAAY,EAAE,CAAC;oBACf,eAAe,EAAE,OAAO;oBACxB,MAAM,EAAE,SAAS;iBACpB,YAEA,eAAe,KAAK,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,GACzD,IACP,CACT,CAAC;AACN,CAAC"}
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { UseLukeReturn, LukeProviderProps } from '../types.js';
3
+ export declare function LukeProvider({ serverUrl, authToken, autoConnect, children, }: LukeProviderProps): React.ReactElement;
4
+ export declare function useLukeContext(): UseLukeReturn;
5
+ //# sourceMappingURL=LukeProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LukeProvider.d.ts","sourceRoot":"","sources":["../../src/components/LukeProvider.tsx"],"names":[],"mappings":"AAGA,OAAO,KAA6C,MAAM,OAAO,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMpE,wBAAgB,YAAY,CAAC,EACzB,SAAS,EACT,SAAS,EACT,WAAmB,EACnB,QAAQ,GACX,EAAE,iBAAiB,GAAG,KAAK,CAAC,YAAY,CAYxC;AAGD,wBAAgB,cAAc,IAAI,aAAa,CAQ9C"}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // LukeProvider Component
3
+ // React context provider for realtime AI voice communication
4
+ import { createContext, useContext } from 'react';
5
+ import { useLuke } from '../hooks/useLuke.js';
6
+ // Context for Luke state
7
+ const LukeContext = createContext(null);
8
+ // Provider component wraps children with Luke context
9
+ export function LukeProvider({ serverUrl, authToken, autoConnect = false, children, }) {
10
+ const luke = useLuke({
11
+ serverUrl,
12
+ authToken,
13
+ autoConnect,
14
+ });
15
+ return (_jsx(LukeContext.Provider, { value: luke, children: children }));
16
+ }
17
+ // Hook to access Luke context
18
+ export function useLukeContext() {
19
+ const context = useContext(LukeContext);
20
+ if (!context) {
21
+ throw new Error('useLukeContext must be used within a LukeProvider');
22
+ }
23
+ return context;
24
+ }
25
+ //# sourceMappingURL=LukeProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LukeProvider.js","sourceRoot":"","sources":["../../src/components/LukeProvider.tsx"],"names":[],"mappings":";AAAA,yBAAyB;AACzB,6DAA6D;AAE7D,OAAc,EAAE,aAAa,EAAE,UAAU,EAAW,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAG9C,yBAAyB;AACzB,MAAM,WAAW,GAAG,aAAa,CAAuB,IAAI,CAAC,CAAC;AAE9D,sDAAsD;AACtD,MAAM,UAAU,YAAY,CAAC,EACzB,SAAS,EACT,SAAS,EACT,WAAW,GAAG,KAAK,EACnB,QAAQ,GACQ;IAChB,MAAM,IAAI,GAAG,OAAO,CAAC;QACjB,SAAS;QACT,SAAS;QACT,WAAW;KACd,CAAC,CAAC;IAEH,OAAO,CACH,KAAC,WAAW,CAAC,QAAQ,IAAC,KAAK,EAAE,IAAI,YAC5B,QAAQ,GACU,CAC1B,CAAC;AACN,CAAC;AAED,8BAA8B;AAC9B,MAAM,UAAU,cAAc;IAC1B,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IAExC,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,OAAO,CAAC;AACnB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import type { TranscriptionMessage } from '../types.js';
3
+ export interface TranscriptionDisplayProps {
4
+ messages: TranscriptionMessage[];
5
+ className?: string;
6
+ renderMessage?: (message: TranscriptionMessage, index: number) => React.ReactNode;
7
+ }
8
+ export declare function TranscriptionDisplay({ messages, className, renderMessage, }: TranscriptionDisplayProps): React.ReactElement;
9
+ //# sourceMappingURL=TranscriptionDisplay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TranscriptionDisplay.d.ts","sourceRoot":"","sources":["../../src/components/TranscriptionDisplay.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,MAAM,WAAW,yBAAyB;IACtC,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;CACrF;AAED,wBAAgB,oBAAoB,CAAC,EACjC,QAAQ,EACR,SAAS,EACT,aAAa,GAChB,EAAE,yBAAyB,GAAG,KAAK,CAAC,YAAY,CAuChD"}