liquidcn 0.0.1 → 0.1.0-alpha.6

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 (133) hide show
  1. package/README.md +209 -2
  2. package/dist/esm/client/components/audio-visualizer.js +88 -0
  3. package/dist/esm/client/components/audio-visualizer.js.map +1 -0
  4. package/dist/esm/client/components/chat-view.js +182 -0
  5. package/dist/esm/client/components/chat-view.js.map +1 -0
  6. package/dist/esm/client/components/form-builder.js +416 -0
  7. package/dist/esm/client/components/form-builder.js.map +1 -0
  8. package/dist/esm/client/components/index.js +3 -0
  9. package/dist/esm/client/components/index.js.map +1 -1
  10. package/dist/esm/client/components/ui/dialog.js +3 -3
  11. package/dist/esm/client/components/ui/dialog.js.map +1 -1
  12. package/dist/esm/client/components/ui/index.js +2 -0
  13. package/dist/esm/client/components/ui/index.js.map +1 -1
  14. package/dist/esm/client/components/ui/label.js +9 -0
  15. package/dist/esm/client/components/ui/label.js.map +1 -0
  16. package/dist/esm/client/components/ui/resizable-navbar.js +8 -8
  17. package/dist/esm/client/components/ui/resizable-navbar.js.map +1 -1
  18. package/dist/esm/client/components/ui/select.js +5 -5
  19. package/dist/esm/client/components/ui/select.js.map +1 -1
  20. package/dist/esm/client/components/ui/slider.js +10 -0
  21. package/dist/esm/client/components/ui/slider.js.map +1 -0
  22. package/dist/esm/client/components/ui/switch.js +1 -1
  23. package/dist/esm/client/components/ui/switch.js.map +1 -1
  24. package/dist/esm/client/components/ui/tabs.js +7 -6
  25. package/dist/esm/client/components/ui/tabs.js.map +1 -1
  26. package/dist/esm/client/hooks/index.js +1 -0
  27. package/dist/esm/client/hooks/index.js.map +1 -1
  28. package/dist/esm/client/hooks/use-speech-to-text.js +54 -0
  29. package/dist/esm/client/hooks/use-speech-to-text.js.map +1 -0
  30. package/dist/esm/client/utils/audio-utils.js +83 -0
  31. package/dist/esm/client/utils/audio-utils.js.map +1 -0
  32. package/dist/esm/components/ui/badge.js +6 -6
  33. package/dist/esm/components/ui/badge.js.map +1 -1
  34. package/dist/esm/components/ui/button.js +6 -6
  35. package/dist/esm/components/ui/button.js.map +1 -1
  36. package/dist/esm/components/ui/input.js +2 -2
  37. package/dist/esm/components/ui/input.js.map +1 -1
  38. package/dist/esm/components/ui/textarea.js +2 -2
  39. package/dist/esm/components/ui/textarea.js.map +1 -1
  40. package/dist/esm/index.js +1 -0
  41. package/dist/esm/index.js.map +1 -1
  42. package/dist/esm/speech-to-text/index.js +3 -0
  43. package/dist/esm/speech-to-text/index.js.map +1 -0
  44. package/dist/esm/speech-to-text/server.js +63 -0
  45. package/dist/esm/speech-to-text/server.js.map +1 -0
  46. package/dist/esm/speech-to-text/types.js +2 -0
  47. package/dist/esm/speech-to-text/types.js.map +1 -0
  48. package/dist/styles.css +116 -25
  49. package/dist/types/client/components/audio-visualizer.d.ts +49 -0
  50. package/dist/types/client/components/audio-visualizer.d.ts.map +1 -0
  51. package/dist/types/client/components/chat-view.d.ts +55 -0
  52. package/dist/types/client/components/chat-view.d.ts.map +1 -0
  53. package/dist/types/client/components/form-builder.d.ts +211 -0
  54. package/dist/types/client/components/form-builder.d.ts.map +1 -0
  55. package/dist/types/client/components/index.d.ts +3 -0
  56. package/dist/types/client/components/index.d.ts.map +1 -1
  57. package/dist/types/client/components/ui/dialog.d.ts.map +1 -1
  58. package/dist/types/client/components/ui/index.d.ts +2 -0
  59. package/dist/types/client/components/ui/index.d.ts.map +1 -1
  60. package/dist/types/client/components/ui/label.d.ts +5 -0
  61. package/dist/types/client/components/ui/label.d.ts.map +1 -0
  62. package/dist/types/client/components/ui/resizable-navbar.d.ts.map +1 -1
  63. package/dist/types/client/components/ui/select.d.ts.map +1 -1
  64. package/dist/types/client/components/ui/slider.d.ts +5 -0
  65. package/dist/types/client/components/ui/slider.d.ts.map +1 -0
  66. package/dist/types/client/components/ui/switch.d.ts.map +1 -1
  67. package/dist/types/client/components/ui/tabs.d.ts.map +1 -1
  68. package/dist/types/client/hooks/index.d.ts +1 -0
  69. package/dist/types/client/hooks/index.d.ts.map +1 -1
  70. package/dist/types/client/hooks/use-speech-to-text.d.ts +51 -0
  71. package/dist/types/client/hooks/use-speech-to-text.d.ts.map +1 -0
  72. package/dist/types/client/utils/audio-utils.d.ts +20 -0
  73. package/dist/types/client/utils/audio-utils.d.ts.map +1 -0
  74. package/dist/types/components/ui/badge.d.ts.map +1 -1
  75. package/dist/types/components/ui/input.d.ts.map +1 -1
  76. package/dist/types/components/ui/textarea.d.ts.map +1 -1
  77. package/dist/types/index.d.ts +1 -0
  78. package/dist/types/index.d.ts.map +1 -1
  79. package/dist/types/speech-to-text/index.d.ts +7 -0
  80. package/dist/types/speech-to-text/index.d.ts.map +1 -0
  81. package/dist/types/speech-to-text/server.d.ts +35 -0
  82. package/dist/types/speech-to-text/server.d.ts.map +1 -0
  83. package/dist/types/speech-to-text/types.d.ts +94 -0
  84. package/dist/types/speech-to-text/types.d.ts.map +1 -0
  85. package/package.json +21 -10
  86. package/dist/cjs/client/components/index.js +0 -18
  87. package/dist/cjs/client/components/index.js.map +0 -1
  88. package/dist/cjs/client/components/ui/dialog.js +0 -48
  89. package/dist/cjs/client/components/ui/dialog.js.map +0 -1
  90. package/dist/cjs/client/components/ui/index.js +0 -24
  91. package/dist/cjs/client/components/ui/index.js.map +0 -1
  92. package/dist/cjs/client/components/ui/pretty-date.js +0 -91
  93. package/dist/cjs/client/components/ui/pretty-date.js.map +0 -1
  94. package/dist/cjs/client/components/ui/resizable-navbar.js +0 -126
  95. package/dist/cjs/client/components/ui/resizable-navbar.js.map +0 -1
  96. package/dist/cjs/client/components/ui/select.js +0 -50
  97. package/dist/cjs/client/components/ui/select.js.map +0 -1
  98. package/dist/cjs/client/components/ui/sonner.js +0 -12
  99. package/dist/cjs/client/components/ui/sonner.js.map +0 -1
  100. package/dist/cjs/client/components/ui/switch.js +0 -11
  101. package/dist/cjs/client/components/ui/switch.js.map +0 -1
  102. package/dist/cjs/client/components/ui/tabs.js +0 -53
  103. package/dist/cjs/client/components/ui/tabs.js.map +0 -1
  104. package/dist/cjs/client/hooks/index.js +0 -18
  105. package/dist/cjs/client/hooks/index.js.map +0 -1
  106. package/dist/cjs/client/hooks/use-cookie-with-fallback.js +0 -107
  107. package/dist/cjs/client/hooks/use-cookie-with-fallback.js.map +0 -1
  108. package/dist/cjs/client/index.js +0 -19
  109. package/dist/cjs/client/index.js.map +0 -1
  110. package/dist/cjs/components/index.js +0 -18
  111. package/dist/cjs/components/index.js.map +0 -1
  112. package/dist/cjs/components/ui/alert.js +0 -33
  113. package/dist/cjs/components/ui/alert.js.map +0 -1
  114. package/dist/cjs/components/ui/badge.js +0 -28
  115. package/dist/cjs/components/ui/badge.js.map +0 -1
  116. package/dist/cjs/components/ui/button.js +0 -39
  117. package/dist/cjs/components/ui/button.js.map +0 -1
  118. package/dist/cjs/components/ui/card.js +0 -48
  119. package/dist/cjs/components/ui/card.js.map +0 -1
  120. package/dist/cjs/components/ui/footer.js +0 -9
  121. package/dist/cjs/components/ui/footer.js.map +0 -1
  122. package/dist/cjs/components/ui/index.js +0 -25
  123. package/dist/cjs/components/ui/index.js.map +0 -1
  124. package/dist/cjs/components/ui/input.js +0 -12
  125. package/dist/cjs/components/ui/input.js.map +0 -1
  126. package/dist/cjs/components/ui/pretty-amount.js +0 -167
  127. package/dist/cjs/components/ui/pretty-amount.js.map +0 -1
  128. package/dist/cjs/components/ui/textarea.js +0 -12
  129. package/dist/cjs/components/ui/textarea.js.map +0 -1
  130. package/dist/cjs/index.js +0 -19
  131. package/dist/cjs/index.js.map +0 -1
  132. package/dist/cjs/utils.js +0 -9
  133. package/dist/cjs/utils.js.map +0 -1
package/README.md CHANGED
@@ -17,8 +17,10 @@ A collection of reusable, accessible React UI components built with TypeScript,
17
17
  **liquidcn** is a comprehensive component library featuring:
18
18
 
19
19
  - **UI Components**: Button, Card, Alert, Badge, Input, Textarea, Footer, PrettyAmount
20
- - **Client Components**: Dialog, Select, Switch, Tabs, Sonner (Toast), PrettyDate, ResizableNavbar
21
- - **Hooks**: Custom React hooks including `useCookieWithFallback`
20
+ - **Client Components**: Dialog, Select, Switch, Tabs, Sonner (Toast), PrettyDate, ResizableNavbar, Slider, AudioVisualizer
21
+ - **Form Components**: FormBuilder (schema-driven forms with AI mode), ChatView (AI chat interface with voice input)
22
+ - **Hooks**: Custom React hooks including `useCookieWithFallback`, `useSpeechToText`
23
+ - **Server Utilities**: `createSpeechToTextHandler()` for voice-to-text API routes
22
24
  - **Utilities**: `cn()` utility for className merging using clsx and tailwind-merge
23
25
 
24
26
  Bun + Npm + Typescript + Standard Version + Flat Config Linting + Husky + Commit / Release Pipeline
@@ -159,6 +161,211 @@ export function MyFooter() {
159
161
  }
160
162
  ```
161
163
 
164
+ ### FormBuilder Component
165
+
166
+ The `FormBuilder` component renders schema-driven forms with optional AI-powered form filling. Works with `useSchemaForm` from `tanstack-effect`.
167
+
168
+ #### Basic Usage
169
+
170
+ ```tsx
171
+ import { useSchemaForm } from 'tanstack-effect/client'
172
+ import { FormBuilder, FormValidationAlert, isFormValid } from 'liquidcn/client'
173
+ import { Button } from 'liquidcn'
174
+ import { Schema } from 'effect'
175
+
176
+ const ProjectSchema = Schema.Struct({
177
+ projectName: Schema.String.pipe(Schema.annotations({ description: 'Name of the project' })),
178
+ projectType: Schema.Literal('web', 'mobile', 'desktop').pipe(
179
+ Schema.annotations({ description: 'Type of project' })
180
+ ),
181
+ teamSize: Schema.Number.pipe(Schema.annotations({ description: 'Number of team members' })),
182
+ })
183
+
184
+ function ProjectForm() {
185
+ const form = useSchemaForm({
186
+ schema: ProjectSchema,
187
+ })
188
+
189
+ return (
190
+ <div>
191
+ <FormBuilder form={form} variant="default" />
192
+ <Button onClick={() => console.log(form.data)}>Submit</Button>
193
+ <FormValidationAlert form={form} />
194
+ </div>
195
+ )
196
+ }
197
+ ```
198
+
199
+ #### With AI Mode
200
+
201
+ Enable AI-powered form filling by adding the `ai` option to `useSchemaForm` and `enableAIMode` to `FormBuilder`:
202
+
203
+ ```tsx
204
+ import { useSchemaForm } from 'tanstack-effect/client'
205
+ import { FormBuilder, FormValidationAlert } from 'liquidcn/client'
206
+ import { Button } from 'liquidcn'
207
+ import { Schema } from 'effect'
208
+
209
+ const ProjectSchema = Schema.Struct({
210
+ projectName: Schema.String.pipe(Schema.annotations({ description: 'Name of the project' })),
211
+ projectType: Schema.Literal('web', 'mobile', 'desktop').pipe(
212
+ Schema.annotations({ description: 'Type of project' })
213
+ ),
214
+ teamSize: Schema.Number.pipe(Schema.annotations({ description: 'Number of team members' })),
215
+ })
216
+
217
+ function ProjectForm() {
218
+ const form = useSchemaForm({
219
+ schema: ProjectSchema,
220
+ // Enable AI form filling
221
+ ai: {
222
+ endpoint: '/api/ai-form-fill',
223
+ },
224
+ })
225
+
226
+ return (
227
+ <div>
228
+ <FormBuilder
229
+ form={form}
230
+ variant="wizard"
231
+ enableAIMode
232
+ aiPlaceholder="Describe your project..."
233
+ aiChatMinHeight="300px"
234
+ />
235
+ <Button onClick={() => console.log(form.data)}>Submit</Button>
236
+ <FormValidationAlert form={form} />
237
+ </div>
238
+ )
239
+ }
240
+ ```
241
+
242
+ With AI mode enabled, the FormBuilder shows:
243
+
244
+ - **AI/Edit toggle buttons** - Switch between chat and manual editing
245
+ - **Chat interface** - Full conversation with AI including message history
246
+ - **Clarification prompts** - AI asks for missing required fields
247
+ - **Summary** - Shows what fields were filled (e.g. "Filled 3 fields: projectName, projectType, teamSize")
248
+
249
+ #### FormBuilder Props
250
+
251
+ | Prop | Type | Default | Description |
252
+ | ----------------- | ------------------------------------ | ----------- | ------------------------------- |
253
+ | `form` | `UseSchemaFormReturn` | required | Form state from `useSchemaForm` |
254
+ | `variant` | `'default' \| 'compact' \| 'wizard'` | `'default'` | Display variant |
255
+ | `enableAIMode` | `boolean` | `false` | Show AI/Edit mode toggle |
256
+ | `initialMode` | `'ai' \| 'edit'` | `'ai'` | Initial mode when AI is enabled |
257
+ | `aiPlaceholder` | `string` | - | Placeholder for AI chat input |
258
+ | `aiChatMinHeight` | `string` | `'300px'` | Minimum height for chat view |
259
+ | `pinnedFields` | `string[]` | `[]` | Fields to show at top level |
260
+ | `hiddenFields` | `string[]` | `[]` | Fields to hide from form |
261
+
262
+ ### ChatView Component
263
+
264
+ The `ChatView` component provides a standalone chat UI for AI interactions with optional voice input:
265
+
266
+ ```tsx
267
+ import { ChatView } from 'liquidcn/client'
268
+
269
+ function AIChat() {
270
+ const [messages, setMessages] = useState([])
271
+
272
+ return (
273
+ <ChatView
274
+ messages={messages}
275
+ status="idle"
276
+ onSend={(msg) => console.log('Send:', msg)}
277
+ placeholder="Ask me anything..."
278
+ enableVoice={true} // Enable when OPENAI_API_KEY is configured on server
279
+ />
280
+ )
281
+ }
282
+ ```
283
+
284
+ #### ChatView Props
285
+
286
+ | Prop | Type | Default | Description |
287
+ | --------------- | ---------- | --------------------- | ------------------------------------------------ |
288
+ | `messages` | `array` | required | Messages in the conversation |
289
+ | `status` | `string` | required | AI status: 'idle', 'filling', 'clarifying', etc. |
290
+ | `onSend` | `function` | required | Callback when user sends a message |
291
+ | `placeholder` | `string` | - | Placeholder text for input |
292
+ | `enableVoice` | `boolean` | `false` | Enable voice input (requires server API key) |
293
+ | `voiceEndpoint` | `string` | `/api/speech-to-text` | Custom endpoint for speech-to-text API |
294
+
295
+ ### Speech-to-Text (Voice Input)
296
+
297
+ LiquidCN provides speech-to-text capabilities for voice input in chat interfaces.
298
+
299
+ #### Server Setup
300
+
301
+ First, install the optional AI SDK dependencies:
302
+
303
+ ```bash
304
+ bun add ai @ai-sdk/openai
305
+ ```
306
+
307
+ Create a speech-to-text API route using the handler:
308
+
309
+ ```typescript
310
+ // app/api/speech-to-text/route.ts
311
+ import { createSpeechToTextHandler } from 'liquidcn'
312
+ import { auth } from '@/auth'
313
+
314
+ const handler = createSpeechToTextHandler({
315
+ authenticate: async () => {
316
+ const session = await auth()
317
+ return !!session?.user
318
+ },
319
+ defaultModel: 'gpt-4o-transcribe', // optional
320
+ })
321
+
322
+ export const POST = handler
323
+ ```
324
+
325
+ Set the `OPENAI_API_KEY` environment variable on your server.
326
+
327
+ #### Client Usage
328
+
329
+ The `useSpeechToText` hook can be used standalone:
330
+
331
+ ```tsx
332
+ import { useSpeechToText } from 'liquidcn/client'
333
+
334
+ function VoiceInput() {
335
+ const { transcribe, isLoading, data, error } = useSpeechToText({
336
+ onSuccess: (result) => console.log('Transcribed:', result.text),
337
+ onError: (err) => console.error('Error:', err.error),
338
+ })
339
+
340
+ const handleRecord = async (audioFile: File) => {
341
+ await transcribe({ audio: audioFile })
342
+ }
343
+
344
+ return (
345
+ <div>
346
+ {isLoading && <p>Transcribing...</p>}
347
+ {data && <p>Result: {data.text}</p>}
348
+ </div>
349
+ )
350
+ }
351
+ ```
352
+
353
+ #### AudioVisualizer Component
354
+
355
+ Display audio waveform during recording:
356
+
357
+ ```tsx
358
+ import { AudioVisualizer } from 'liquidcn/client'
359
+
360
+ ;<AudioVisualizer
361
+ isRecording={isRecording}
362
+ mediaRecorder={mediaRecorder}
363
+ width={280}
364
+ height={60}
365
+ barColor="#a78bfa"
366
+ />
367
+ ```
368
+
162
369
  ## Developing
163
370
 
164
371
  Install Dependencies:
@@ -0,0 +1,88 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ export function AudioVisualizer({ isRecording, mediaRecorder, width = 280, height = 60, barColor = '#a78bfa', barWidth = 3, gap = 2, }) {
5
+ const canvasRef = React.useRef(null);
6
+ const animationRef = React.useRef(null);
7
+ const analyserRef = React.useRef(null);
8
+ const audioContextRef = React.useRef(null);
9
+ const barCount = Math.floor(width / (barWidth + gap));
10
+ const drawIdle = React.useCallback(() => {
11
+ const canvas = canvasRef.current;
12
+ if (!canvas)
13
+ return;
14
+ const ctx = canvas.getContext('2d');
15
+ if (!ctx)
16
+ return;
17
+ ctx.clearRect(0, 0, width, height);
18
+ for (let i = 0; i < barCount; i++) {
19
+ const barHeight = Math.max(4, 4 + Math.sin(i * 0.3) * 3 + Math.sin(i * 0.7) * 2);
20
+ const x = i * (barWidth + gap);
21
+ const y = (height - barHeight) / 2;
22
+ ctx.fillStyle = 'rgba(167, 139, 250, 0.2)';
23
+ ctx.beginPath();
24
+ ctx.roundRect(x, y, barWidth, barHeight, barWidth / 2);
25
+ ctx.fill();
26
+ }
27
+ }, [width, height, barWidth, gap, barCount]);
28
+ React.useEffect(() => {
29
+ if (!isRecording || !mediaRecorder || mediaRecorder.state === 'inactive') {
30
+ drawIdle();
31
+ return;
32
+ }
33
+ const audioContext = new AudioContext();
34
+ audioContextRef.current = audioContext;
35
+ const analyser = audioContext.createAnalyser();
36
+ analyser.fftSize = 256;
37
+ analyserRef.current = analyser;
38
+ const stream = mediaRecorder.stream;
39
+ const source = audioContext.createMediaStreamSource(stream);
40
+ source.connect(analyser);
41
+ const bufferLength = analyser.frequencyBinCount;
42
+ const dataArray = new Uint8Array(bufferLength);
43
+ const canvas = canvasRef.current;
44
+ if (!canvas)
45
+ return;
46
+ const ctx = canvas.getContext('2d');
47
+ if (!ctx)
48
+ return;
49
+ const draw = () => {
50
+ if (!analyserRef.current)
51
+ return;
52
+ animationRef.current = requestAnimationFrame(draw);
53
+ analyserRef.current.getByteFrequencyData(dataArray);
54
+ ctx.clearRect(0, 0, width, height);
55
+ const step = Math.floor(bufferLength / barCount);
56
+ for (let i = 0; i < barCount; i++) {
57
+ const dataIndex = i * step;
58
+ const value = dataArray[dataIndex] || 0;
59
+ const currentBarHeight = Math.max(4, (value / 255) * height * 0.9);
60
+ const x = i * (barWidth + gap);
61
+ const y = (height - currentBarHeight) / 2;
62
+ ctx.fillStyle = barColor;
63
+ ctx.beginPath();
64
+ ctx.roundRect(x, y, barWidth, currentBarHeight, barWidth / 2);
65
+ ctx.fill();
66
+ }
67
+ };
68
+ draw();
69
+ return () => {
70
+ if (animationRef.current) {
71
+ cancelAnimationFrame(animationRef.current);
72
+ animationRef.current = null;
73
+ }
74
+ analyserRef.current = null;
75
+ if (audioContextRef.current) {
76
+ audioContextRef.current.close();
77
+ audioContextRef.current = null;
78
+ }
79
+ };
80
+ }, [isRecording, mediaRecorder, width, height, barColor, barWidth, gap, barCount, drawIdle]);
81
+ React.useEffect(() => {
82
+ if (!isRecording) {
83
+ drawIdle();
84
+ }
85
+ }, [isRecording, drawIdle]);
86
+ return _jsx("canvas", { ref: canvasRef, width: width, height: height, style: { width, height } });
87
+ }
88
+ //# sourceMappingURL=audio-visualizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-visualizer.js","sourceRoot":"","sources":["../../../../src/client/components/audio-visualizer.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,182 @@
1
+ "use client";
2
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Info, Loader2, Mic, MicOff, Send, Sparkles } from "lucide-react";
4
+ import * as React from "react";
5
+ import { Button } from "../../components/ui/button.js";
6
+ import { Textarea } from "../../components/ui/textarea.js";
7
+ import { cn } from "../../utils.js";
8
+ import { useSpeechToText } from "../hooks/use-speech-to-text.js";
9
+ import { convertToWav, getExtensionForMimeType, getSupportedMimeType } from "../utils/audio-utils.js";
10
+ import { AudioVisualizer } from "./audio-visualizer.js";
11
+ function renderMarkdown(text) {
12
+ const lines = text.split("\n");
13
+ return lines.map((line, lineIndex) => {
14
+ const bulletMatch = line.match(/^(\s*)(•|-)\s*(.*)$/);
15
+ let content;
16
+ if (bulletMatch) {
17
+ const [, indent, , bulletContent] = bulletMatch;
18
+ content = (_jsxs("span", { children: [indent, "\u2022 ", renderInlineMarkdown(bulletContent)] }));
19
+ }
20
+ else {
21
+ content = renderInlineMarkdown(line);
22
+ }
23
+ return (_jsxs(React.Fragment, { children: [lineIndex > 0 && _jsx("br", {}), content] }, lineIndex));
24
+ });
25
+ }
26
+ function renderInlineMarkdown(text) {
27
+ const parts = [];
28
+ let remaining = text;
29
+ let keyCounter = 0;
30
+ while (remaining.length > 0) {
31
+ const boldMatch = remaining.match(/\*\*([^*]+)\*\*/);
32
+ const italicMatch = remaining.match(/(?<!\*)\*([^*]+)\*(?!\*)/);
33
+ let firstMatch = null;
34
+ if (boldMatch && italicMatch) {
35
+ if ((boldMatch.index ?? Infinity) <= (italicMatch.index ?? Infinity)) {
36
+ firstMatch = { match: boldMatch, type: "bold" };
37
+ }
38
+ else {
39
+ firstMatch = { match: italicMatch, type: "italic" };
40
+ }
41
+ }
42
+ else if (boldMatch) {
43
+ firstMatch = { match: boldMatch, type: "bold" };
44
+ }
45
+ else if (italicMatch) {
46
+ firstMatch = { match: italicMatch, type: "italic" };
47
+ }
48
+ if (firstMatch && firstMatch.match.index !== undefined) {
49
+ if (firstMatch.match.index > 0) {
50
+ parts.push(remaining.slice(0, firstMatch.match.index));
51
+ }
52
+ if (firstMatch.type === "bold") {
53
+ parts.push(_jsx("strong", { className: "font-semibold", children: firstMatch.match[1] }, keyCounter++));
54
+ }
55
+ else {
56
+ parts.push(_jsx("em", { className: "italic", children: firstMatch.match[1] }, keyCounter++));
57
+ }
58
+ remaining = remaining.slice(firstMatch.match.index + firstMatch.match[0].length);
59
+ }
60
+ else {
61
+ parts.push(remaining);
62
+ break;
63
+ }
64
+ }
65
+ return parts.length === 1 ? parts[0] : _jsx(_Fragment, { children: parts });
66
+ }
67
+ function MessageBubble({ message, isUser }) {
68
+ return (_jsx("div", { className: cn("flex w-full", isUser ? "justify-end" : "justify-start"), children: _jsxs("div", { className: cn("max-w-[85%] rounded-2xl px-4 py-2.5 text-sm", isUser
69
+ ? "bg-primary text-primary-foreground rounded-br-md"
70
+ : "bg-muted text-muted-foreground rounded-bl-md"), children: [_jsx("div", { className: "whitespace-pre-wrap wrap-break-word", children: renderMarkdown(message.content) }), message.timestamp && (_jsx("span", { className: "mt-1 block text-[10px] opacity-60", children: new Date(message.timestamp).toLocaleTimeString([], {
71
+ hour: "2-digit",
72
+ minute: "2-digit",
73
+ }) }))] }) }));
74
+ }
75
+ export function ChatView({ messages, status, onSend, className, placeholder = "Describe what you want to fill in...", enableVoice = false, voiceEndpoint = "/api/speech-to-text", maxHeight, }) {
76
+ const [input, setInput] = React.useState("");
77
+ const messagesContainerRef = React.useRef(null);
78
+ const textareaRef = React.useRef(null);
79
+ const [isRecording, setIsRecording] = React.useState(false);
80
+ const [isProcessingAudio, setIsProcessingAudio] = React.useState(false);
81
+ const [mediaRecorder, setMediaRecorder] = React.useState(null);
82
+ const audioChunksRef = React.useRef([]);
83
+ const { transcribe, isLoading: isTranscribing } = useSpeechToText({
84
+ endpoint: voiceEndpoint,
85
+ onSuccess: (data) => {
86
+ if (data.text.trim()) {
87
+ setInput((prev) => (prev ? `${prev} ${data.text}` : data.text));
88
+ textareaRef.current?.focus();
89
+ }
90
+ },
91
+ });
92
+ React.useEffect(() => {
93
+ if (messagesContainerRef.current) {
94
+ messagesContainerRef.current.scrollTo({
95
+ top: messagesContainerRef.current.scrollHeight,
96
+ behavior: "smooth",
97
+ });
98
+ }
99
+ }, [messages]);
100
+ React.useEffect(() => {
101
+ if (messagesContainerRef.current) {
102
+ messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
103
+ }
104
+ }, []);
105
+ const startRecording = React.useCallback(async () => {
106
+ try {
107
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
108
+ const mimeType = getSupportedMimeType();
109
+ const recorder = mimeType
110
+ ? new MediaRecorder(stream, { mimeType })
111
+ : new MediaRecorder(stream);
112
+ audioChunksRef.current = [];
113
+ recorder.ondataavailable = (event) => {
114
+ if (event.data.size > 0) {
115
+ audioChunksRef.current.push(event.data);
116
+ }
117
+ };
118
+ recorder.onstop = async () => {
119
+ stream.getTracks().forEach((track) => track.stop());
120
+ setMediaRecorder(null);
121
+ if (audioChunksRef.current.length > 0) {
122
+ setIsProcessingAudio(true);
123
+ try {
124
+ const actualMimeType = recorder.mimeType || mimeType || "audio/webm";
125
+ const audioBlob = new Blob(audioChunksRef.current, { type: actualMimeType });
126
+ let audioFile;
127
+ try {
128
+ const wavBlob = await convertToWav(audioBlob);
129
+ audioFile = new File([wavBlob], "recording.wav", { type: "audio/wav" });
130
+ }
131
+ catch (conversionError) {
132
+ console.warn("WAV conversion failed, using original format:", conversionError);
133
+ const extension = getExtensionForMimeType(actualMimeType);
134
+ audioFile = new File([audioBlob], `recording.${extension}`, { type: actualMimeType });
135
+ }
136
+ await transcribe({ audio: audioFile });
137
+ }
138
+ finally {
139
+ setIsProcessingAudio(false);
140
+ }
141
+ }
142
+ };
143
+ recorder.start();
144
+ setMediaRecorder(recorder);
145
+ setIsRecording(true);
146
+ }
147
+ catch (error) {
148
+ console.error("Failed to start recording:", error);
149
+ }
150
+ }, [transcribe]);
151
+ const stopRecording = React.useCallback(() => {
152
+ if (mediaRecorder && mediaRecorder.state !== "inactive") {
153
+ mediaRecorder.stop();
154
+ }
155
+ setIsRecording(false);
156
+ }, [mediaRecorder]);
157
+ const toggleRecording = React.useCallback(() => {
158
+ if (isRecording) {
159
+ stopRecording();
160
+ }
161
+ else {
162
+ startRecording();
163
+ }
164
+ }, [isRecording, startRecording, stopRecording]);
165
+ const handleSend = () => {
166
+ if (input.trim() && status !== "filling") {
167
+ onSend(input.trim());
168
+ setInput("");
169
+ textareaRef.current?.focus();
170
+ }
171
+ };
172
+ const handleKeyDown = (e) => {
173
+ if (e.key === "Enter" && !e.shiftKey) {
174
+ e.preventDefault();
175
+ handleSend();
176
+ }
177
+ };
178
+ const isLoading = status === "filling";
179
+ const hasMessages = messages.length > 0;
180
+ const isVoiceProcessing = isRecording || isProcessingAudio || isTranscribing;
181
+ return (_jsxs("div", { className: cn("flex flex-col", className), children: [_jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto p-4 space-y-3", style: maxHeight ? { maxHeight, overflowY: "auto" } : undefined, children: [!hasMessages && (_jsxs("div", { className: "flex flex-col items-center justify-center h-full text-center text-muted-foreground", children: [_jsx(Sparkles, { className: "h-8 w-8 mb-3 text-primary/50" }), _jsx("p", { className: "text-sm font-medium", children: "AI Form Assistant" }), _jsx("p", { className: "text-xs mt-1", children: "Describe what you want to fill and I'll help you complete the form." })] })), messages.map((message, index) => (_jsx(MessageBubble, { message: message, isUser: message.role === "user" }, index))), isLoading && (_jsx("div", { className: "flex w-full justify-start", children: _jsx("div", { className: "rounded-2xl rounded-bl-md bg-muted px-4 py-3", children: _jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }) }))] }), hasMessages && !isLoading && (_jsxs("div", { className: "flex items-center justify-center gap-2 px-4 py-2 border-t bg-muted/30 text-muted-foreground", children: [_jsx(Info, { className: "h-3.5 w-3.5 shrink-0" }), _jsxs("span", { className: "text-xs", children: ["Switch to ", _jsx("strong", { children: "Edit" }), " tab to review, then ", _jsx("strong", { children: "Save" }), " to apply changes"] })] })), enableVoice && isRecording && (_jsxs("div", { className: "flex items-center justify-center gap-3 px-4 py-2 border-t bg-muted/50", children: [_jsx(AudioVisualizer, { isRecording: isRecording, mediaRecorder: mediaRecorder, width: 200, height: 40, barColor: "hsl(var(--primary))" }), _jsx("span", { className: "text-xs text-muted-foreground", children: "Recording..." })] })), enableVoice && (isProcessingAudio || isTranscribing) && !isRecording && (_jsxs("div", { className: "flex items-center justify-center gap-2 px-4 py-2 border-t bg-muted/50", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin text-primary" }), _jsx("span", { className: "text-xs text-muted-foreground", children: isProcessingAudio ? "Processing audio..." : "Transcribing..." })] })), _jsx("div", { className: "border-t p-4", children: _jsxs("div", { className: "flex items-center gap-2", children: [enableVoice && (_jsx(Button, { onClick: toggleRecording, disabled: isLoading || isTranscribing, size: "icon", variant: isRecording ? "destructive" : "outline", className: "shrink-0", title: isRecording ? "Stop recording" : "Start voice input", children: isRecording ? _jsx(MicOff, { className: "h-4 w-4" }) : _jsx(Mic, { className: "h-4 w-4" }) })), _jsx(Textarea, { ref: textareaRef, value: input, onChange: (e) => setInput(e.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, disabled: isLoading || isVoiceProcessing, className: "min-h-11 max-h-32 resize-none" }), _jsx(Button, { onClick: handleSend, disabled: !input.trim() || isLoading || isVoiceProcessing, size: "icon", className: "shrink-0", children: isLoading ? (_jsx(Loader2, { className: "h-4 w-4 animate-spin" })) : (_jsx(Send, { className: "h-4 w-4" })) })] }) })] }));
182
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-view.js","sourceRoot":"","sources":["../../../../src/client/components/chat-view.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACzE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAA;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AACvD,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAClG,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAiDpD,SAAS,cAAc,CAAC,IAAY;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAE9B,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE;QAEnC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAA;QAErD,IAAI,OAAwB,CAAA;QAE5B,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,CAAC,EAAE,MAAM,EAAE,AAAD,EAAG,aAAa,CAAC,GAAG,WAAW,CAAA;YAC/C,OAAO,GAAG,CACR,2BACG,MAAM,aAAI,oBAAoB,CAAC,aAAa,CAAC,IACzC,CACR,CAAA;QACH,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAA;QACtC,CAAC;QAED,OAAO,CACL,MAAC,KAAK,CAAC,QAAQ,eACZ,SAAS,GAAG,CAAC,IAAI,cAAM,EACvB,OAAO,KAFW,SAAS,CAGb,CAClB,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAKD,SAAS,oBAAoB,CAAC,IAAY;IACxC,MAAM,KAAK,GAAsB,EAAE,CAAA;IACnC,IAAI,SAAS,GAAG,IAAI,CAAA;IACpB,IAAI,UAAU,GAAG,CAAC,CAAA;IAGlB,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAE5B,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;QAEpD,MAAM,WAAW,GAAG,SAAS,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAA;QAE/D,IAAI,UAAU,GAAgE,IAAI,CAAA;QAElF,IAAI,SAAS,IAAI,WAAW,EAAE,CAAC;YAE7B,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,IAAI,QAAQ,CAAC,EAAE,CAAC;gBACrE,UAAU,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;YACjD,CAAC;iBAAM,CAAC;gBACN,UAAU,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;YACrD,CAAC;QACH,CAAC;aAAM,IAAI,SAAS,EAAE,CAAC;YACrB,UAAU,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;QACjD,CAAC;aAAM,IAAI,WAAW,EAAE,CAAC;YACvB,UAAU,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;QACrD,CAAC;QAED,IAAI,UAAU,IAAI,UAAU,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAEvD,IAAI,UAAU,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;gBAC/B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;YACxD,CAAC;YAGD,IAAI,UAAU,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC/B,KAAK,CAAC,IAAI,CACR,iBAA2B,SAAS,EAAC,eAAe,YACjD,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,IADT,UAAU,EAAE,CAEhB,CACV,CAAA;YACH,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CACR,aAAuB,SAAS,EAAC,QAAQ,YACtC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,IADb,UAAU,EAAE,CAEhB,CACN,CAAA;YACH,CAAC;YAED,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAClF,CAAC;aAAM,CAAC;YAEN,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACrB,MAAK;QACP,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,4BAAG,KAAK,GAAI,CAAA;AACrD,CAAC;AAKD,SAAS,aAAa,CAAC,EAAE,OAAO,EAAE,MAAM,EAA+C;IACrF,OAAO,CACL,cAAK,SAAS,EAAE,EAAE,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,CAAC,YACzE,eACE,SAAS,EAAE,EAAE,CACX,6CAA6C,EAC7C,MAAM;gBACJ,CAAC,CAAC,kDAAkD;gBACpD,CAAC,CAAC,8CAA8C,CACnD,aAED,cAAK,SAAS,EAAC,qCAAqC,YAAE,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,GAAO,EAC3F,OAAO,CAAC,SAAS,IAAI,CACpB,eAAM,SAAS,EAAC,mCAAmC,YAChD,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,kBAAkB,CAAC,EAAE,EAAE;wBAClD,IAAI,EAAE,SAAS;wBACf,MAAM,EAAE,SAAS;qBAClB,CAAC,GACG,CACR,IACG,GACF,CACP,CAAA;AACH,CAAC;AAYD,MAAM,UAAU,QAAQ,CAAC,EACvB,QAAQ,EACR,MAAM,EACN,MAAM,EACN,SAAS,EACT,WAAW,GAAG,sCAAsC,EACpD,WAAW,GAAG,KAAK,EACnB,aAAa,GAAG,qBAAqB,EACrC,SAAS,GACK;IACd,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IAC5C,MAAM,oBAAoB,GAAG,KAAK,CAAC,MAAM,CAAiB,IAAI,CAAC,CAAA;IAC/D,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAsB,IAAI,CAAC,CAAA;IAG3D,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3D,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IACvE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAuB,IAAI,CAAC,CAAA;IACpF,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAS,EAAE,CAAC,CAAA;IAG/C,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,eAAe,CAAC;QAChE,QAAQ,EAAE,aAAa;QACvB,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;YAClB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;gBAErB,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;gBAC/D,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,CAAA;YAC9B,CAAC;QACH,CAAC;KACF,CAAC,CAAA;IAGF,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,oBAAoB,CAAC,OAAO,EAAE,CAAC;YACjC,oBAAoB,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACpC,GAAG,EAAE,oBAAoB,CAAC,OAAO,CAAC,YAAY;gBAC9C,QAAQ,EAAE,QAAQ;aACnB,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;IAGd,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,oBAAoB,CAAC,OAAO,EAAE,CAAC;YACjC,oBAAoB,CAAC,OAAO,CAAC,SAAS,GAAG,oBAAoB,CAAC,OAAO,CAAC,YAAY,CAAA;QACpF,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAGN,MAAM,cAAc,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE;QAClD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YAGzE,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAA;YACvC,MAAM,QAAQ,GAAG,QAAQ;gBACvB,CAAC,CAAC,IAAI,aAAa,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC;gBACzC,CAAC,CAAC,IAAI,aAAa,CAAC,MAAM,CAAC,CAAA;YAE7B,cAAc,CAAC,OAAO,GAAG,EAAE,CAAA;YAE3B,QAAQ,CAAC,eAAe,GAAG,CAAC,KAAK,EAAE,EAAE;gBACnC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;oBACxB,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBACzC,CAAC;YACH,CAAC,CAAA;YAED,QAAQ,CAAC,MAAM,GAAG,KAAK,IAAI,EAAE;gBAE3B,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;gBAGnD,gBAAgB,CAAC,IAAI,CAAC,CAAA;gBAGtB,IAAI,cAAc,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACtC,oBAAoB,CAAC,IAAI,CAAC,CAAA;oBAC1B,IAAI,CAAC;wBACH,MAAM,cAAc,GAAG,QAAQ,CAAC,QAAQ,IAAI,QAAQ,IAAI,YAAY,CAAA;wBACpE,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAA;wBAI5E,IAAI,SAAe,CAAA;wBACnB,IAAI,CAAC;4BACH,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,CAAA;4BAC7C,SAAS,GAAG,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;wBACzE,CAAC;wBAAC,OAAO,eAAe,EAAE,CAAC;4BAEzB,OAAO,CAAC,IAAI,CAAC,+CAA+C,EAAE,eAAe,CAAC,CAAA;4BAC9E,MAAM,SAAS,GAAG,uBAAuB,CAAC,cAAc,CAAC,CAAA;4BACzD,SAAS,GAAG,IAAI,IAAI,CAAC,CAAC,SAAS,CAAC,EAAE,aAAa,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAA;wBACvF,CAAC;wBAGD,MAAM,UAAU,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;oBACxC,CAAC;4BAAS,CAAC;wBACT,oBAAoB,CAAC,KAAK,CAAC,CAAA;oBAC7B,CAAC;gBACH,CAAC;YACH,CAAC,CAAA;YAED,QAAQ,CAAC,KAAK,EAAE,CAAA;YAChB,gBAAgB,CAAC,QAAQ,CAAC,CAAA;YAC1B,cAAc,CAAC,IAAI,CAAC,CAAA;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAA;QACpD,CAAC;IACH,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;IAGhB,MAAM,aAAa,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QAC3C,IAAI,aAAa,IAAI,aAAa,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YACxD,aAAa,CAAC,IAAI,EAAE,CAAA;QACtB,CAAC;QACD,cAAc,CAAC,KAAK,CAAC,CAAA;IAGvB,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAA;IAGnB,MAAM,eAAe,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QAC7C,IAAI,WAAW,EAAE,CAAC;YAChB,aAAa,EAAE,CAAA;QACjB,CAAC;aAAM,CAAC;YACN,cAAc,EAAE,CAAA;QAClB,CAAC;IACH,CAAC,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,aAAa,CAAC,CAAC,CAAA;IAEhD,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,IAAI,KAAK,CAAC,IAAI,EAAE,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;YACpB,QAAQ,CAAC,EAAE,CAAC,CAAA;YACZ,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,CAAA;QAC9B,CAAC;IACH,CAAC,CAAA;IAED,MAAM,aAAa,GAAG,CAAC,CAAsB,EAAE,EAAE;QAC/C,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;YACrC,CAAC,CAAC,cAAc,EAAE,CAAA;YAClB,UAAU,EAAE,CAAA;QACd,CAAC;IACH,CAAC,CAAA;IAED,MAAM,SAAS,GAAG,MAAM,KAAK,SAAS,CAAA;IACtC,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;IACvC,MAAM,iBAAiB,GAAG,WAAW,IAAI,iBAAiB,IAAI,cAAc,CAAA;IAE5E,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,eAAe,EAAE,SAAS,CAAC,aAE5C,eACE,GAAG,EAAE,oBAAoB,EACzB,SAAS,EAAC,sCAAsC,EAChD,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,aAE9D,CAAC,WAAW,IAAI,CACf,eAAK,SAAS,EAAC,oFAAoF,aACjG,KAAC,QAAQ,IAAC,SAAS,EAAC,8BAA8B,GAAG,EACrD,YAAG,SAAS,EAAC,qBAAqB,kCAAsB,EACxD,YAAG,SAAS,EAAC,cAAc,oFAEvB,IACA,CACP,EAEA,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAChC,KAAC,aAAa,IAAa,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,KAAK,MAAM,IAAxD,KAAK,CAAuD,CACjF,CAAC,EAGD,SAAS,IAAI,CACZ,cAAK,SAAS,EAAC,2BAA2B,YACxC,cAAK,SAAS,EAAC,8CAA8C,YAC3D,KAAC,OAAO,IAAC,SAAS,EAAC,4CAA4C,GAAG,GAC9D,GACF,CACP,IACG,EAGL,WAAW,IAAI,CAAC,SAAS,IAAI,CAC5B,eAAK,SAAS,EAAC,6FAA6F,aAC1G,KAAC,IAAI,IAAC,SAAS,EAAC,sBAAsB,GAAG,EACzC,gBAAM,SAAS,EAAC,SAAS,2BACb,oCAAqB,2BAAqB,oCAAqB,yBAEpE,IACH,CACP,EAGA,WAAW,IAAI,WAAW,IAAI,CAC7B,eAAK,SAAS,EAAC,uEAAuE,aACpF,KAAC,eAAe,IACd,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,EAAE,EACV,QAAQ,EAAC,qBAAqB,GAC9B,EACF,eAAM,SAAS,EAAC,+BAA+B,6BAAoB,IAC/D,CACP,EAGA,WAAW,IAAI,CAAC,iBAAiB,IAAI,cAAc,CAAC,IAAI,CAAC,WAAW,IAAI,CACvE,eAAK,SAAS,EAAC,uEAAuE,aACpF,KAAC,OAAO,IAAC,SAAS,EAAC,mCAAmC,GAAG,EACzD,eAAM,SAAS,EAAC,+BAA+B,YAC5C,iBAAiB,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,iBAAiB,GACzD,IACH,CACP,EAGD,cAAK,SAAS,EAAC,cAAc,YAC3B,eAAK,SAAS,EAAC,yBAAyB,aAErC,WAAW,IAAI,CACd,KAAC,MAAM,IACL,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,SAAS,IAAI,cAAc,EACrC,IAAI,EAAC,MAAM,EACX,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,EAChD,SAAS,EAAC,UAAU,EACpB,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,mBAAmB,YAE1D,WAAW,CAAC,CAAC,CAAC,KAAC,MAAM,IAAC,SAAS,EAAC,SAAS,GAAG,CAAC,CAAC,CAAC,KAAC,GAAG,IAAC,SAAS,EAAC,SAAS,GAAG,GACpE,CACV,EAED,KAAC,QAAQ,IACP,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACzC,SAAS,EAAE,aAAa,EACxB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,SAAS,IAAI,iBAAiB,EACxC,SAAS,EAAC,+BAA+B,GACzC,EACF,KAAC,MAAM,IACL,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,SAAS,IAAI,iBAAiB,EACzD,IAAI,EAAC,MAAM,EACX,SAAS,EAAC,UAAU,YAEnB,SAAS,CAAC,CAAC,CAAC,CACX,KAAC,OAAO,IAAC,SAAS,EAAC,sBAAsB,GAAG,CAC7C,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,IAAC,SAAS,EAAC,SAAS,GAAG,CAC7B,GACM,IACL,GACF,IACF,CACP,CAAA;AACH,CAAC"}