@fluencypassdevs/cycle 1.6.0 → 1.9.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 (39) hide show
  1. package/bin/mcp.mjs +150 -0
  2. package/dist/chunk-37C2K2NM.js +101 -0
  3. package/dist/chunk-37C2K2NM.js.map +1 -0
  4. package/dist/chunk-I5A3CME4.js +84 -0
  5. package/dist/chunk-I5A3CME4.js.map +1 -0
  6. package/dist/chunk-JRJBKPXW.js +118 -0
  7. package/dist/chunk-JRJBKPXW.js.map +1 -0
  8. package/dist/{chunk-PCLVQPIG.js → chunk-L2DJS6PG.js} +3 -3
  9. package/dist/{chunk-PCLVQPIG.js.map → chunk-L2DJS6PG.js.map} +1 -1
  10. package/dist/chunk-PW464XEP.js +787 -0
  11. package/dist/chunk-PW464XEP.js.map +1 -0
  12. package/dist/chunk-QANROH2K.js +50 -0
  13. package/dist/chunk-QANROH2K.js.map +1 -0
  14. package/dist/chunk-YMWRR7ET.js +503 -0
  15. package/dist/chunk-YMWRR7ET.js.map +1 -0
  16. package/dist/index.d.ts +8 -1
  17. package/dist/index.js +18 -12
  18. package/dist/layout/container.d.ts +24 -0
  19. package/dist/layout/container.js +5 -0
  20. package/dist/layout/container.js.map +1 -0
  21. package/dist/layout/grid.d.ts +26 -0
  22. package/dist/layout/grid.js +5 -0
  23. package/dist/layout/grid.js.map +1 -0
  24. package/dist/layout/stack.d.ts +47 -0
  25. package/dist/layout/stack.js +5 -0
  26. package/dist/layout/stack.js.map +1 -0
  27. package/dist/types-CVdl5nka.d.ts +9 -0
  28. package/dist/ui/chat-message.d.ts +57 -0
  29. package/dist/ui/chat-message.js +11 -0
  30. package/dist/ui/chat-message.js.map +1 -0
  31. package/dist/ui/chat-panel.js +1 -1
  32. package/dist/ui/live-waiting.js +2 -2
  33. package/dist/ui/message-bar.d.ts +69 -0
  34. package/dist/ui/message-bar.js +5 -0
  35. package/dist/ui/message-bar.js.map +1 -0
  36. package/dist/ui/message-rating.d.ts +29 -0
  37. package/dist/ui/message-rating.js +5 -0
  38. package/dist/ui/message-rating.js.map +1 -0
  39. package/package.json +1 -1
@@ -0,0 +1,503 @@
1
+ import { LikeDislike } from './chunk-F2XA2Z75.js';
2
+ import { Ai } from './chunk-JPEDYOV7.js';
3
+ import { Avatar, AvatarImage, AvatarFallback } from './chunk-MSLQRGSP.js';
4
+ import { cn } from './chunk-TYCPXAXF.js';
5
+ import { __objRest, __spreadProps, __spreadValues } from './chunk-YINJ5YZ5.js';
6
+ import * as React from 'react';
7
+ import { cva } from 'class-variance-authority';
8
+ import { Pause, Play } from 'lucide-react';
9
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
+
11
+ var chatMessageVariants = cva("flex w-full text-sm", {
12
+ variants: {
13
+ persona: {
14
+ ai: "justify-start",
15
+ user: "justify-end",
16
+ system: "justify-center"
17
+ }
18
+ },
19
+ defaultVariants: {
20
+ persona: "ai"
21
+ }
22
+ });
23
+ function getInitials(name) {
24
+ if (!name) return "";
25
+ return name.split(" ").slice(0, 2).map((w) => w[0]).join("").toUpperCase();
26
+ }
27
+ function TypingDots() {
28
+ return /* @__PURE__ */ jsxs(
29
+ "span",
30
+ {
31
+ className: "inline-flex items-center gap-1",
32
+ "aria-label": "Digitando...",
33
+ role: "status",
34
+ children: [
35
+ /* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-current opacity-40 animate-bounce [animation-delay:-0.3s]" }),
36
+ /* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-current opacity-40 animate-bounce [animation-delay:-0.15s]" }),
37
+ /* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-current opacity-40 animate-bounce" })
38
+ ]
39
+ }
40
+ );
41
+ }
42
+ var STATIC_FALLBACK_PEAKS = [
43
+ 2,
44
+ 2,
45
+ 2,
46
+ 2,
47
+ 8,
48
+ 24,
49
+ 18,
50
+ 24,
51
+ 8,
52
+ 18,
53
+ 24,
54
+ 24,
55
+ 18,
56
+ 24,
57
+ 18,
58
+ 18,
59
+ 24,
60
+ 24,
61
+ 32,
62
+ 24,
63
+ 24,
64
+ 32,
65
+ 24,
66
+ 32,
67
+ 32,
68
+ 24,
69
+ 18,
70
+ 24,
71
+ 24,
72
+ 18,
73
+ 24,
74
+ 18,
75
+ 18,
76
+ 24,
77
+ 24,
78
+ 24,
79
+ 24,
80
+ 24,
81
+ 32,
82
+ 24,
83
+ 32,
84
+ 18,
85
+ 24,
86
+ 18,
87
+ 24,
88
+ 18,
89
+ 24,
90
+ 18,
91
+ 18,
92
+ 24,
93
+ 24,
94
+ 24,
95
+ 24,
96
+ 24,
97
+ 32,
98
+ 24,
99
+ 32,
100
+ 18,
101
+ 24,
102
+ 18,
103
+ 24,
104
+ 8,
105
+ 18,
106
+ 24,
107
+ 8,
108
+ 4,
109
+ 2,
110
+ 2,
111
+ 2,
112
+ 2,
113
+ 4,
114
+ 8,
115
+ 24,
116
+ 24,
117
+ 18,
118
+ 24,
119
+ 18,
120
+ 18,
121
+ 24,
122
+ 24,
123
+ 32,
124
+ 24,
125
+ 24,
126
+ 32,
127
+ 24,
128
+ 32,
129
+ 32,
130
+ 24,
131
+ 18,
132
+ 24,
133
+ 24,
134
+ 18,
135
+ 24,
136
+ 8,
137
+ 18,
138
+ 24,
139
+ 8,
140
+ 2,
141
+ 2,
142
+ 4
143
+ ].map((h) => h / 32);
144
+ var MIN_BAR_HEIGHT = 2;
145
+ var MAX_BAR_HEIGHT = 32;
146
+ var BAR_WIDTH = 2;
147
+ var BAR_GAP = 2;
148
+ var SPEED_OPTIONS = [1, 1.25, 1.5, 2, 0.75];
149
+ var peaksCache = /* @__PURE__ */ new Map();
150
+ async function calculatePeaks(audioUrl, barCount) {
151
+ const response = await fetch(audioUrl);
152
+ const arrayBuffer = await response.arrayBuffer();
153
+ const AudioContextClass = window.AudioContext || window.webkitAudioContext;
154
+ const audioContext = new AudioContextClass();
155
+ const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
156
+ const channelData = audioBuffer.getChannelData(0);
157
+ const samplesPerBar = Math.floor(channelData.length / barCount);
158
+ const peaks = [];
159
+ for (let i = 0; i < barCount; i++) {
160
+ const start = i * samplesPerBar;
161
+ const end = Math.min(start + samplesPerBar, channelData.length);
162
+ let max = 0;
163
+ for (let j = start; j < end; j++) {
164
+ const absSample = Math.abs(channelData[j]);
165
+ if (absSample > max) max = absSample;
166
+ }
167
+ peaks.push(max);
168
+ }
169
+ const maxPeak = Math.max(...peaks, 0.01);
170
+ void audioContext.close();
171
+ return peaks.map((p) => p / maxPeak);
172
+ }
173
+ async function getPeaks(url, barCount) {
174
+ const cacheKey = `${url}|${barCount}`;
175
+ const cached = peaksCache.get(cacheKey);
176
+ if (cached) return cached;
177
+ const peaks = await calculatePeaks(url, barCount);
178
+ peaksCache.set(cacheKey, peaks);
179
+ return peaks;
180
+ }
181
+ function formatAudioTime(seconds) {
182
+ if (!Number.isFinite(seconds) || seconds < 0) return "00:00";
183
+ const m = Math.floor(seconds / 60);
184
+ const s = Math.floor(seconds % 60);
185
+ return `${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
186
+ }
187
+ function ChatAudio(_a) {
188
+ var _b = _a, {
189
+ src,
190
+ variant = "neutral",
191
+ peaks: providedPeaks,
192
+ barCount = 60,
193
+ className
194
+ } = _b, props = __objRest(_b, [
195
+ "src",
196
+ "variant",
197
+ "peaks",
198
+ "barCount",
199
+ "className"
200
+ ]);
201
+ var _a2;
202
+ const audioRef = React.useRef(null);
203
+ const [isPlaying, setIsPlaying] = React.useState(false);
204
+ const [currentTime, setCurrentTime] = React.useState(0);
205
+ const [duration, setDuration] = React.useState(0);
206
+ const [speed, setSpeed] = React.useState(1);
207
+ const [computedPeaks, setComputedPeaks] = React.useState(null);
208
+ React.useEffect(() => {
209
+ if (providedPeaks) return;
210
+ let cancelled = false;
211
+ getPeaks(src, barCount).then((p) => {
212
+ if (!cancelled) setComputedPeaks(p);
213
+ }).catch((err) => {
214
+ if (process.env.NODE_ENV !== "production") {
215
+ console.warn(
216
+ `[ChatAudio] Failed to analyze waveform for "${src}". Falling back to static waveform. Reason: ${err instanceof Error ? err.message : String(err)}. Audio source likely missing CORS headers (Access-Control-Allow-Origin).`
217
+ );
218
+ }
219
+ });
220
+ return () => {
221
+ cancelled = true;
222
+ };
223
+ }, [src, providedPeaks, barCount]);
224
+ const fallbackPeaks = React.useMemo(() => {
225
+ return Array.from(
226
+ { length: barCount },
227
+ (_, i) => STATIC_FALLBACK_PEAKS[i % STATIC_FALLBACK_PEAKS.length]
228
+ );
229
+ }, [barCount]);
230
+ const peaks = (_a2 = providedPeaks != null ? providedPeaks : computedPeaks) != null ? _a2 : fallbackPeaks;
231
+ const setDurationSafe = React.useCallback((value) => {
232
+ if (Number.isFinite(value) && value > 0) {
233
+ setDuration((prev) => prev !== value ? value : prev);
234
+ }
235
+ }, []);
236
+ React.useEffect(() => {
237
+ const audio = audioRef.current;
238
+ if (!audio) return;
239
+ const handleDuration = () => setDurationSafe(audio.duration);
240
+ handleDuration();
241
+ audio.addEventListener("loadedmetadata", handleDuration);
242
+ audio.addEventListener("durationchange", handleDuration);
243
+ audio.addEventListener("canplay", handleDuration);
244
+ return () => {
245
+ audio.removeEventListener("loadedmetadata", handleDuration);
246
+ audio.removeEventListener("durationchange", handleDuration);
247
+ audio.removeEventListener("canplay", handleDuration);
248
+ };
249
+ }, [src, setDurationSafe]);
250
+ const togglePlay = () => {
251
+ const audio = audioRef.current;
252
+ if (!audio) return;
253
+ if (audio.paused) {
254
+ audio.play().catch(() => {
255
+ });
256
+ } else {
257
+ audio.pause();
258
+ }
259
+ };
260
+ const cycleSpeed = () => {
261
+ var _a3;
262
+ const idx = SPEED_OPTIONS.indexOf(speed);
263
+ const next = (_a3 = SPEED_OPTIONS[(idx + 1) % SPEED_OPTIONS.length]) != null ? _a3 : 1;
264
+ setSpeed(next);
265
+ if (audioRef.current) audioRef.current.playbackRate = next;
266
+ };
267
+ const waveformRef = React.useRef(null);
268
+ const isDraggingRef = React.useRef(false);
269
+ const seekToClientX = React.useCallback(
270
+ (clientX) => {
271
+ const audio = audioRef.current;
272
+ const waveform2 = waveformRef.current;
273
+ if (!audio || !waveform2 || !duration) return;
274
+ const rect = waveform2.getBoundingClientRect();
275
+ const x = clientX - rect.left;
276
+ const newProgress = Math.max(
277
+ 0,
278
+ Math.min(x / (peaks.length * (BAR_WIDTH + BAR_GAP) - BAR_GAP), 1)
279
+ );
280
+ audio.currentTime = newProgress * duration;
281
+ },
282
+ [duration, peaks.length]
283
+ );
284
+ const handlePointerDown = (e) => {
285
+ if (!duration) return;
286
+ isDraggingRef.current = true;
287
+ e.currentTarget.setPointerCapture(e.pointerId);
288
+ seekToClientX(e.clientX);
289
+ };
290
+ const handlePointerMove = (e) => {
291
+ if (!isDraggingRef.current) return;
292
+ seekToClientX(e.clientX);
293
+ };
294
+ const handlePointerUp = (e) => {
295
+ if (!isDraggingRef.current) return;
296
+ isDraggingRef.current = false;
297
+ e.currentTarget.releasePointerCapture(e.pointerId);
298
+ };
299
+ const progress = duration > 0 ? currentTime / duration : 0;
300
+ const displayTime = formatAudioTime(currentTime);
301
+ const barsLayoutWidth = peaks.length * (BAR_WIDTH + BAR_GAP) - BAR_GAP;
302
+ const waveform = /* @__PURE__ */ jsxs(
303
+ "div",
304
+ {
305
+ ref: waveformRef,
306
+ className: "flex-1 relative h-8 min-w-0 overflow-hidden touch-none cursor-pointer select-none",
307
+ onPointerDown: handlePointerDown,
308
+ onPointerMove: handlePointerMove,
309
+ onPointerUp: handlePointerUp,
310
+ onPointerCancel: handlePointerUp,
311
+ role: "slider",
312
+ "aria-label": "Posicao do audio",
313
+ "aria-valuemin": 0,
314
+ "aria-valuemax": Math.round(duration),
315
+ "aria-valuenow": Math.round(currentTime),
316
+ children: [
317
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 flex items-center gap-[2px]", children: peaks.map((peak, i) => {
318
+ const isPlayed = i / peaks.length <= progress;
319
+ const height = Math.max(
320
+ MIN_BAR_HEIGHT,
321
+ Math.round(peak * MAX_BAR_HEIGHT)
322
+ );
323
+ return /* @__PURE__ */ jsx(
324
+ "div",
325
+ {
326
+ className: cn(
327
+ "w-[2px] shrink-0 rounded-full",
328
+ isPlayed ? "bg-neutral-foreground" : "bg-neutral-ring"
329
+ ),
330
+ style: { height: `${height}px` }
331
+ },
332
+ i
333
+ );
334
+ }) }),
335
+ duration > 0 && /* @__PURE__ */ jsx(
336
+ "div",
337
+ {
338
+ className: "absolute top-1/2 z-10 -translate-y-1/2 -translate-x-1/2 size-3 rounded-full bg-[#098A5E] pointer-events-none shadow-md",
339
+ style: { left: `${progress * barsLayoutWidth}px` },
340
+ "aria-hidden": "true"
341
+ }
342
+ )
343
+ ]
344
+ }
345
+ );
346
+ const timeDisplay = /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm text-neutral-ring tabular-nums", children: displayTime });
347
+ const speedBadge = /* @__PURE__ */ jsx(
348
+ "button",
349
+ {
350
+ type: "button",
351
+ onClick: cycleSpeed,
352
+ className: cn(
353
+ "shrink-0 h-[26px] min-w-16 rounded-full text-sm font-medium px-2 transition-opacity hover:opacity-80",
354
+ variant === "brand" ? "bg-muted text-muted-foreground" : "bg-background text-neutral-muted-foreground"
355
+ ),
356
+ "aria-label": `Velocidade ${speed}x. Clique para alternar.`,
357
+ children: speed === 1 ? "1x" : `${speed}x`
358
+ }
359
+ );
360
+ return /* @__PURE__ */ jsxs(
361
+ "div",
362
+ __spreadProps(__spreadValues({
363
+ "data-slot": "chat-audio",
364
+ className: cn(
365
+ "rounded-2xl w-full max-w-md flex flex-col gap-2 p-3 sm:flex-row sm:items-center sm:gap-2 sm:pl-5 sm:pr-3 sm:py-3",
366
+ variant === "brand" ? "bg-accent" : "bg-muted",
367
+ className
368
+ )
369
+ }, props), {
370
+ children: [
371
+ /* @__PURE__ */ jsx(
372
+ "audio",
373
+ {
374
+ ref: audioRef,
375
+ src,
376
+ preload: "metadata",
377
+ onPlay: () => setIsPlaying(true),
378
+ onPause: () => setIsPlaying(false),
379
+ onTimeUpdate: (e) => {
380
+ setCurrentTime(e.currentTarget.currentTime);
381
+ setDurationSafe(e.currentTarget.duration);
382
+ },
383
+ onEnded: () => setIsPlaying(false)
384
+ }
385
+ ),
386
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: [
387
+ /* @__PURE__ */ jsx(
388
+ "button",
389
+ {
390
+ type: "button",
391
+ onClick: togglePlay,
392
+ className: "shrink-0 flex items-center justify-center text-neutral-foreground hover:opacity-80 transition-opacity",
393
+ "aria-label": isPlaying ? "Pausar" : "Reproduzir",
394
+ children: isPlaying ? /* @__PURE__ */ jsx(Pause, { className: "size-4", fill: "currentColor", strokeWidth: 0 }) : /* @__PURE__ */ jsx(Play, { className: "size-4", fill: "currentColor", strokeWidth: 0 })
395
+ }
396
+ ),
397
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:contents", children: timeDisplay }),
398
+ waveform,
399
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:contents", children: speedBadge })
400
+ ] }),
401
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between sm:hidden", children: [
402
+ timeDisplay,
403
+ speedBadge
404
+ ] })
405
+ ]
406
+ })
407
+ );
408
+ }
409
+ function ChatMessage(_a) {
410
+ var _b = _a, {
411
+ persona,
412
+ text,
413
+ audioSrc,
414
+ avatar,
415
+ author,
416
+ showFeedback = false,
417
+ feedbackValue,
418
+ onFeedbackChange,
419
+ loading = false,
420
+ className
421
+ } = _b, props = __objRest(_b, [
422
+ "persona",
423
+ "text",
424
+ "audioSrc",
425
+ "avatar",
426
+ "author",
427
+ "showFeedback",
428
+ "feedbackValue",
429
+ "onFeedbackChange",
430
+ "loading",
431
+ "className"
432
+ ]);
433
+ if (persona === "system") {
434
+ return /* @__PURE__ */ jsx(
435
+ "div",
436
+ __spreadProps(__spreadValues({
437
+ "data-slot": "chat-message",
438
+ "data-persona": "system",
439
+ className: cn(chatMessageVariants({ persona }), className)
440
+ }, props), {
441
+ children: /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground italic", children: text })
442
+ })
443
+ );
444
+ }
445
+ if (persona === "ai") {
446
+ return /* @__PURE__ */ jsx(
447
+ "div",
448
+ __spreadProps(__spreadValues({
449
+ "data-slot": "chat-message",
450
+ "data-persona": "ai",
451
+ className: cn(chatMessageVariants({ persona }), className)
452
+ }, props), {
453
+ children: /* @__PURE__ */ jsxs("div", { className: "flex gap-3 max-w-[85%] sm:max-w-[75%] w-full", children: [
454
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:flex shrink-0 size-8 rounded-md bg-primary items-center justify-center theme-brand", children: /* @__PURE__ */ jsx("span", { className: "text-primary-foreground", children: /* @__PURE__ */ jsx(Ai, { size: "sm", decorative: true }) }) }),
455
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 min-w-0 flex-1", children: loading ? (
456
+ /* Loading: dots dentro de bubble cinza (speech bubble shape — top-left reto) */
457
+ /* @__PURE__ */ jsx("div", { className: "self-start inline-flex items-center rounded-2xl sm:rounded-tl-none bg-muted p-5 text-muted-foreground", children: /* @__PURE__ */ jsx(TypingDots, {}) })
458
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
459
+ text && /* Text bubble: bg-muted sempre, speech bubble shape (tl-none) apenas no desktop */
460
+ /* @__PURE__ */ jsx("div", { className: "rounded-2xl bg-muted p-5 sm:rounded-tl-none", children: /* @__PURE__ */ jsx("p", { className: "text-base leading-6 text-neutral-foreground break-words", children: text }) }),
461
+ audioSrc && /* @__PURE__ */ jsx(ChatAudio, { src: audioSrc, variant: "neutral" }),
462
+ showFeedback && /* @__PURE__ */ jsx(
463
+ LikeDislike,
464
+ {
465
+ size: "xs",
466
+ value: feedbackValue,
467
+ onValueChange: onFeedbackChange
468
+ }
469
+ )
470
+ ] }) })
471
+ ] })
472
+ })
473
+ );
474
+ }
475
+ return /* @__PURE__ */ jsx(
476
+ "div",
477
+ __spreadProps(__spreadValues({
478
+ "data-slot": "chat-message",
479
+ "data-persona": "user",
480
+ className: cn(chatMessageVariants({ persona }), className)
481
+ }, props), {
482
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 max-w-[85%] sm:max-w-[75%] w-full justify-end", children: [
483
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 min-w-0 items-end theme-brand flex-1", children: loading ? (
484
+ /* Loading: dots dentro de bubble pink (speech bubble shape — top-right reto) */
485
+ /* @__PURE__ */ jsx("div", { className: "inline-flex items-center rounded-2xl sm:rounded-tr-none bg-accent p-5 text-neutral-muted-foreground", children: /* @__PURE__ */ jsx(TypingDots, {}) })
486
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
487
+ text && /* Text bubble: speech bubble shape (top-right reto) no desktop.
488
+ text-neutral-foreground = preto neutro fixo, nao muda com theme-brand */
489
+ /* @__PURE__ */ jsx("div", { className: "rounded-2xl sm:rounded-tr-none bg-accent p-5", children: /* @__PURE__ */ jsx("p", { className: "text-base leading-6 text-neutral-foreground break-words", children: text }) }),
490
+ audioSrc && /* @__PURE__ */ jsx(ChatAudio, { src: audioSrc, variant: "brand" })
491
+ ] }) }),
492
+ /* @__PURE__ */ jsxs(Avatar, { size: "lg", className: "hidden sm:flex shrink-0 rounded-lg", children: [
493
+ avatar && /* @__PURE__ */ jsx(AvatarImage, { src: avatar, alt: author != null ? author : "User", className: "rounded-lg" }),
494
+ /* @__PURE__ */ jsx(AvatarFallback, { className: "rounded-lg bg-muted text-muted-foreground text-sm", children: getInitials(author) })
495
+ ] })
496
+ ] })
497
+ })
498
+ );
499
+ }
500
+
501
+ export { ChatAudio, ChatMessage, chatMessageVariants };
502
+ //# sourceMappingURL=chunk-YMWRR7ET.js.map
503
+ //# sourceMappingURL=chunk-YMWRR7ET.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/ui/chat-message.tsx"],"names":["_a","waveform"],"mappings":";;;;;;;;;;AAaA,IAAM,mBAAA,GAAsB,IAAI,qBAAA,EAAuB;AAAA,EACrD,QAAA,EAAU;AAAA,IACR,OAAA,EAAS;AAAA,MACP,EAAA,EAAI,eAAA;AAAA,MACJ,IAAA,EAAM,aAAA;AAAA,MACN,MAAA,EAAQ;AAAA;AACV,GACF;AAAA,EACA,eAAA,EAAiB;AAAA,IACf,OAAA,EAAS;AAAA;AAEb,CAAC;AA6BD,SAAS,YAAY,IAAA,EAAuB;AAC1C,EAAA,IAAI,CAAC,MAAM,OAAO,EAAA;AAClB,EAAA,OAAO,KACJ,KAAA,CAAM,GAAG,EACT,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,CACV,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,CAAC,CAAC,EACf,IAAA,CAAK,EAAE,EACP,WAAA,EAAY;AACjB;AAIA,SAAS,UAAA,GAAa;AACpB,EAAA,uBACE,IAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,gCAAA;AAAA,MACV,YAAA,EAAW,cAAA;AAAA,MACX,IAAA,EAAK,QAAA;AAAA,MAEL,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,oFAAA,EAAqF,CAAA;AAAA,wBACrG,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,qFAAA,EAAsF,CAAA;AAAA,wBACtG,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,4DAAA,EAA6D;AAAA;AAAA;AAAA,GAC/E;AAEJ;AAKA,IAAM,qBAAA,GAAwB;AAAA,EAC5B,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,CAAA;AAAA,EAAG,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EACtE,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAC5E,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAC5E,EAAA;AAAA,EAAI,CAAA;AAAA,EAAG,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EACnE,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,CAAA;AAAA,EAAG,EAAA;AAAA,EAAI,EAAA;AAAA,EAAI,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG,CAAA;AAAA,EAAG;AAC1E,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,GAAI,EAAE,CAAA;AAEnB,IAAM,cAAA,GAAiB,CAAA;AACvB,IAAM,cAAA,GAAiB,EAAA;AACvB,IAAM,SAAA,GAAY,CAAA;AAClB,IAAM,OAAA,GAAU,CAAA;AAChB,IAAM,gBAAgB,CAAC,CAAA,EAAG,IAAA,EAAM,GAAA,EAAK,GAAG,IAAI,CAAA;AAG5C,IAAM,UAAA,uBAAiB,GAAA,EAAsB;AAO7C,eAAe,cAAA,CAAe,UAAkB,QAAA,EAAqC;AACnF,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,QAAQ,CAAA;AACrC,EAAA,MAAM,WAAA,GAAc,MAAM,QAAA,CAAS,WAAA,EAAY;AAE/C,EAAA,MAAM,iBAAA,GACJ,MAAA,CAAO,YAAA,IACN,MAAA,CAAkE,kBAAA;AACrE,EAAA,MAAM,YAAA,GAAe,IAAI,iBAAA,EAAkB;AAC3C,EAAA,MAAM,WAAA,GAAc,MAAM,YAAA,CAAa,eAAA,CAAgB,WAAW,CAAA;AAElE,EAAA,MAAM,WAAA,GAAc,WAAA,CAAY,cAAA,CAAe,CAAC,CAAA;AAChD,EAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,SAAS,QAAQ,CAAA;AAC9D,EAAA,MAAM,QAAkB,EAAC;AAEzB,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,QAAA,EAAU,CAAA,EAAA,EAAK;AACjC,IAAA,MAAM,QAAQ,CAAA,GAAI,aAAA;AAClB,IAAA,MAAM,MAAM,IAAA,CAAK,GAAA,CAAI,KAAA,GAAQ,aAAA,EAAe,YAAY,MAAM,CAAA;AAC9D,IAAA,IAAI,GAAA,GAAM,CAAA;AACV,IAAA,KAAA,IAAS,CAAA,GAAI,KAAA,EAAO,CAAA,GAAI,GAAA,EAAK,CAAA,EAAA,EAAK;AAChC,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,WAAA,CAAY,CAAC,CAAC,CAAA;AACzC,MAAA,IAAI,SAAA,GAAY,KAAK,GAAA,GAAM,SAAA;AAAA,IAC7B;AACA,IAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AAAA,EAChB;AAGA,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,GAAG,OAAO,IAAI,CAAA;AAGvC,EAAA,KAAK,aAAa,KAAA,EAAM;AAExB,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,IAAI,OAAO,CAAA;AACrC;AAGA,eAAe,QAAA,CAAS,KAAa,QAAA,EAAqC;AACxE,EAAA,MAAM,QAAA,GAAW,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA;AACnC,EAAA,MAAM,MAAA,GAAS,UAAA,CAAW,GAAA,CAAI,QAAQ,CAAA;AACtC,EAAA,IAAI,QAAQ,OAAO,MAAA;AACnB,EAAA,MAAM,KAAA,GAAQ,MAAM,cAAA,CAAe,GAAA,EAAK,QAAQ,CAAA;AAChD,EAAA,UAAA,CAAW,GAAA,CAAI,UAAU,KAAK,CAAA;AAC9B,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,gBAAgB,OAAA,EAAyB;AAChD,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA,IAAK,OAAA,GAAU,GAAG,OAAO,OAAA;AACrD,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,OAAA,GAAU,EAAE,CAAA;AACjC,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,OAAA,GAAU,EAAE,CAAA;AACjC,EAAA,OAAO,CAAA,EAAG,CAAA,CAAE,QAAA,EAAS,CAAE,SAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA,EAAI,EAAE,QAAA,EAAS,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA;AAC1E;AA0BA,SAAS,UAAU,EAAA,EAOA;AAPA,EAAA,IAAA,EAAA,GAAA,EAAA,EACjB;AAAA,IAAA,GAAA;AAAA,IACA,OAAA,GAAU,SAAA;AAAA,IACV,KAAA,EAAO,aAAA;AAAA,IACP,QAAA,GAAW,EAAA;AAAA,IACX;AAAA,GAxLF,GAmLmB,EAAA,EAMd,KAAA,GAAA,SAAA,CANc,EAAA,EAMd;AAAA,IALH,KAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GAAA,CAAA;AAxLF,EAAA,IAAAA,GAAAA;AA2LE,EAAA,MAAM,QAAA,GAAiB,aAAyB,IAAI,CAAA;AACpD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAU,eAAS,KAAK,CAAA;AACtD,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAU,eAAS,CAAC,CAAA;AACtD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAU,eAAS,CAAC,CAAA;AAChD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAU,eAAS,CAAC,CAAA;AAC1C,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAU,eAA0B,IAAI,CAAA;AAG9E,EAAM,gBAAU,MAAM;AACpB,IAAA,IAAI,aAAA,EAAe;AACnB,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,QAAA,CAAS,GAAA,EAAK,QAAQ,CAAA,CACnB,IAAA,CAAK,CAAC,CAAA,KAAM;AACX,MAAA,IAAI,CAAC,SAAA,EAAW,gBAAA,CAAiB,CAAC,CAAA;AAAA,IACpC,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AAId,MAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,YAAA,EAAc;AACzC,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,CAAA,4CAAA,EAA+C,GAAG,CAAA,4CAAA,EAErC,GAAA,YAAe,QAAQ,GAAA,CAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAC,CAAA,yEAAA;AAAA,SAE/D;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AACH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,GAAA,EAAK,aAAA,EAAe,QAAQ,CAAC,CAAA;AAGjC,EAAA,MAAM,aAAA,GAAsB,cAAQ,MAAM;AACxC,IAAA,OAAO,KAAA,CAAM,IAAA;AAAA,MACX,EAAE,QAAQ,QAAA,EAAS;AAAA,MACnB,CAAC,CAAA,EAAG,CAAA,KAAM,qBAAA,CAAsB,CAAA,GAAI,sBAAsB,MAAM;AAAA,KAClE;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAGb,EAAA,MAAM,KAAA,GAAA,CAAQA,GAAAA,GAAA,aAAA,IAAA,IAAA,GAAA,aAAA,GAAiB,aAAA,KAAjB,OAAAA,GAAAA,GAAkC,aAAA;AAGhD,EAAA,MAAM,eAAA,GAAwB,KAAA,CAAA,WAAA,CAAY,CAAC,KAAA,KAAkB;AAC3D,IAAA,IAAI,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,QAAQ,CAAA,EAAG;AACvC,MAAA,WAAA,CAAY,CAAC,IAAA,KAAU,IAAA,KAAS,KAAA,GAAQ,QAAQ,IAAK,CAAA;AAAA,IACvD;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAIL,EAAM,gBAAU,MAAM;AACpB,IAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AACvB,IAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,IAAA,MAAM,cAAA,GAAiB,MAAM,eAAA,CAAgB,KAAA,CAAM,QAAQ,CAAA;AAC3D,IAAA,cAAA,EAAe;AAEf,IAAA,KAAA,CAAM,gBAAA,CAAiB,kBAAkB,cAAc,CAAA;AACvD,IAAA,KAAA,CAAM,gBAAA,CAAiB,kBAAkB,cAAc,CAAA;AACvD,IAAA,KAAA,CAAM,gBAAA,CAAiB,WAAW,cAAc,CAAA;AAChD,IAAA,OAAO,MAAM;AACX,MAAA,KAAA,CAAM,mBAAA,CAAoB,kBAAkB,cAAc,CAAA;AAC1D,MAAA,KAAA,CAAM,mBAAA,CAAoB,kBAAkB,cAAc,CAAA;AAC1D,MAAA,KAAA,CAAM,mBAAA,CAAoB,WAAW,cAAc,CAAA;AAAA,IACrD,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,GAAA,EAAK,eAAe,CAAC,CAAA;AAEzB,EAAA,MAAM,aAAa,MAAM;AACvB,IAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AACvB,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,MAAA,KAAA,CAAM,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM;AAAA,MAAC,CAAC,CAAA;AAAA,IAC7B,CAAA,MAAO;AACL,MAAA,KAAA,CAAM,KAAA,EAAM;AAAA,IACd;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,aAAa,MAAM;AA3Q3B,IAAA,IAAAA,GAAAA;AA4QI,IAAA,MAAM,GAAA,GAAM,aAAA,CAAc,OAAA,CAAQ,KAAK,CAAA;AACvC,IAAA,MAAM,IAAA,GAAA,CAAOA,MAAA,aAAA,CAAA,CAAe,GAAA,GAAM,KAAK,aAAA,CAAc,MAAM,CAAA,KAA9C,IAAA,GAAAA,GAAAA,GAAmD,CAAA;AAChE,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI,QAAA,CAAS,OAAA,EAAS,QAAA,CAAS,OAAA,CAAQ,YAAA,GAAe,IAAA;AAAA,EACxD,CAAA;AAIA,EAAA,MAAM,WAAA,GAAoB,aAAuB,IAAI,CAAA;AACrD,EAAA,MAAM,aAAA,GAAsB,aAAO,KAAK,CAAA;AAGxC,EAAA,MAAM,aAAA,GAAsB,KAAA,CAAA,WAAA;AAAA,IAC1B,CAAC,OAAA,KAAoB;AACnB,MAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AACvB,MAAA,MAAMC,YAAW,WAAA,CAAY,OAAA;AAC7B,MAAA,IAAI,CAAC,KAAA,IAAS,CAACA,SAAAA,IAAY,CAAC,QAAA,EAAU;AACtC,MAAA,MAAM,IAAA,GAAOA,UAAS,qBAAA,EAAsB;AAC5C,MAAA,MAAM,CAAA,GAAI,UAAU,IAAA,CAAK,IAAA;AACzB,MAAA,MAAM,cAAc,IAAA,CAAK,GAAA;AAAA,QACvB,CAAA;AAAA,QACA,IAAA,CAAK,IAAI,CAAA,IAAK,KAAA,CAAM,UAAU,SAAA,GAAY,OAAA,CAAA,GAAW,UAAU,CAAC;AAAA,OAClE;AACA,MAAA,KAAA,CAAM,cAAc,WAAA,GAAc,QAAA;AAAA,IACpC,CAAA;AAAA,IACA,CAAC,QAAA,EAAU,KAAA,CAAM,MAAM;AAAA,GACzB;AAEA,EAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,KAA0C;AACnE,IAAA,IAAI,CAAC,QAAA,EAAU;AACf,IAAA,aAAA,CAAc,OAAA,GAAU,IAAA;AACxB,IAAA,CAAA,CAAE,aAAA,CAAc,iBAAA,CAAkB,CAAA,CAAE,SAAS,CAAA;AAC7C,IAAA,aAAA,CAAc,EAAE,OAAO,CAAA;AAAA,EACzB,CAAA;AAEA,EAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,KAA0C;AACnE,IAAA,IAAI,CAAC,cAAc,OAAA,EAAS;AAC5B,IAAA,aAAA,CAAc,EAAE,OAAO,CAAA;AAAA,EACzB,CAAA;AAEA,EAAA,MAAM,eAAA,GAAkB,CAAC,CAAA,KAA0C;AACjE,IAAA,IAAI,CAAC,cAAc,OAAA,EAAS;AAC5B,IAAA,aAAA,CAAc,OAAA,GAAU,KAAA;AACxB,IAAA,CAAA,CAAE,aAAA,CAAc,qBAAA,CAAsB,CAAA,CAAE,SAAS,CAAA;AAAA,EACnD,CAAA;AAEA,EAAA,MAAM,QAAA,GAAW,QAAA,GAAW,CAAA,GAAI,WAAA,GAAc,QAAA,GAAW,CAAA;AACzD,EAAA,MAAM,WAAA,GAAc,gBAAgB,WAAW,CAAA;AAO/C,EAAA,MAAM,eAAA,GAAkB,KAAA,CAAM,MAAA,IAAU,SAAA,GAAY,OAAA,CAAA,GAAW,OAAA;AAM/D,EAAA,MAAM,QAAA,mBACJ,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,WAAA;AAAA,MACL,SAAA,EAAU,mFAAA;AAAA,MACV,aAAA,EAAe,iBAAA;AAAA,MACf,aAAA,EAAe,iBAAA;AAAA,MACf,WAAA,EAAa,eAAA;AAAA,MACb,eAAA,EAAiB,eAAA;AAAA,MACjB,IAAA,EAAK,QAAA;AAAA,MACL,YAAA,EAAW,kBAAA;AAAA,MACX,eAAA,EAAe,CAAA;AAAA,MACf,eAAA,EAAe,IAAA,CAAK,KAAA,CAAM,QAAQ,CAAA;AAAA,MAClC,eAAA,EAAe,IAAA,CAAK,KAAA,CAAM,WAAW,CAAA;AAAA,MAErC,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,SAAI,SAAA,EAAU,uDAAA,EACZ,gBAAM,GAAA,CAAI,CAAC,MAAM,CAAA,KAAM;AACtB,UAAA,MAAM,QAAA,GAAW,CAAA,GAAI,KAAA,CAAM,MAAA,IAAU,QAAA;AACrC,UAAA,MAAM,SAAS,IAAA,CAAK,GAAA;AAAA,YAClB,cAAA;AAAA,YACA,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,cAAc;AAAA,WAClC;AACA,UAAA,uBACE,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cAEC,SAAA,EAAW,EAAA;AAAA,gBACT,+BAAA;AAAA,gBACA,WAAW,uBAAA,GAA0B;AAAA,eACvC;AAAA,cACA,KAAA,EAAO,EAAE,MAAA,EAAQ,CAAA,EAAG,MAAM,CAAA,EAAA,CAAA;AAAK,aAAA;AAAA,YAL1B;AAAA,WAMP;AAAA,QAEJ,CAAC,CAAA,EACH,CAAA;AAAA,QAGC,WAAW,CAAA,oBACV,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,wHAAA;AAAA,YACV,OAAO,EAAE,IAAA,EAAM,CAAA,EAAG,QAAA,GAAW,eAAe,CAAA,EAAA,CAAA,EAAK;AAAA,YACjD,aAAA,EAAY;AAAA;AAAA;AACd;AAAA;AAAA,GAEJ;AAIF,EAAA,MAAM,WAAA,mBACJ,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mDACb,QAAA,EAAA,WAAA,EACH,CAAA;AAIF,EAAA,MAAM,UAAA,mBACJ,GAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,QAAA;AAAA,MACL,OAAA,EAAS,UAAA;AAAA,MACT,SAAA,EAAW,EAAA;AAAA,QACT,sGAAA;AAAA,QACA,OAAA,KAAY,UACR,gCAAA,GACA;AAAA,OACN;AAAA,MACA,YAAA,EAAY,cAAc,KAAK,CAAA,wBAAA,CAAA;AAAA,MAE9B,QAAA,EAAA,KAAA,KAAU,CAAA,GAAI,IAAA,GAAO,CAAA,EAAG,KAAK,CAAA,CAAA;AAAA;AAAA,GAChC;AAGF,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA,aAAA,CAAA,cAAA,CAAA;AAAA,MACC,WAAA,EAAU,YAAA;AAAA,MACV,SAAA,EAAW,EAAA;AAAA,QACT,kHAAA;AAAA,QACA,OAAA,KAAY,UAAU,WAAA,GAAc,UAAA;AAAA,QACpC;AAAA;AACF,KAAA,EACI,KAAA,CAAA,EAPL;AAAA,MASC,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,QAAA;AAAA,YACL,GAAA;AAAA,YACA,OAAA,EAAQ,UAAA;AAAA,YACR,MAAA,EAAQ,MAAM,YAAA,CAAa,IAAI,CAAA;AAAA,YAC/B,OAAA,EAAS,MAAM,YAAA,CAAa,KAAK,CAAA;AAAA,YACjC,YAAA,EAAc,CAAC,CAAA,KAAM;AACnB,cAAA,cAAA,CAAe,CAAA,CAAE,cAAc,WAAW,CAAA;AAE1C,cAAA,eAAA,CAAgB,CAAA,CAAE,cAAc,QAAQ,CAAA;AAAA,YAC1C,CAAA;AAAA,YACA,OAAA,EAAS,MAAM,YAAA,CAAa,KAAK;AAAA;AAAA,SACnC;AAAA,wBAGA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wCAAA,EAEb,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,OAAA,EAAS,UAAA;AAAA,cACT,SAAA,EAAU,uGAAA;AAAA,cACV,YAAA,EAAY,YAAY,QAAA,GAAW,YAAA;AAAA,cAElC,sCACC,GAAA,CAAC,KAAA,EAAA,EAAM,SAAA,EAAU,QAAA,EAAS,MAAK,cAAA,EAAe,WAAA,EAAa,CAAA,EAAG,CAAA,uBAE7D,IAAA,EAAA,EAAK,SAAA,EAAU,UAAS,IAAA,EAAK,cAAA,EAAe,aAAa,CAAA,EAAG;AAAA;AAAA,WAEjE;AAAA,0BAGA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oBAAA,EAAsB,QAAA,EAAA,WAAA,EAAY,CAAA;AAAA,UAEhD,QAAA;AAAA,0BAGD,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oBAAA,EAAsB,QAAA,EAAA,UAAA,EAAW;AAAA,SAAA,EAClD,CAAA;AAAA,wBAGA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6CAAA,EACZ,QAAA,EAAA;AAAA,UAAA,WAAA;AAAA,UACA;AAAA,SAAA,EACH;AAAA;AAAA,KAAA;AAAA,GACF;AAEJ;AAMA,SAAS,YAAY,EAAA,EAYA;AAZA,EAAA,IAAA,EAAA,GAAA,EAAA,EACnB;AAAA,IAAA,OAAA;AAAA,IACA,IAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,YAAA,GAAe,KAAA;AAAA,IACf,aAAA;AAAA,IACA,gBAAA;AAAA,IACA,OAAA,GAAU,KAAA;AAAA,IACV;AAAA,GArdF,GA2cqB,EAAA,EAWhB,KAAA,GAAA,SAAA,CAXgB,EAAA,EAWhB;AAAA,IAVH,SAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,cAAA;AAAA,IACA,eAAA;AAAA,IACA,kBAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GAAA,CAAA;AAIA,EAAA,IAAI,YAAY,QAAA,EAAU;AACxB,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA,aAAA,CAAA,cAAA,CAAA;AAAA,QACC,WAAA,EAAU,cAAA;AAAA,QACV,cAAA,EAAa,QAAA;AAAA,QACb,WAAW,EAAA,CAAG,mBAAA,CAAoB,EAAE,OAAA,EAAS,GAAG,SAAS;AAAA,OAAA,EACrD,KAAA,CAAA,EAJL;AAAA,QAMC,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,sCAAA,EAAwC,QAAA,EAAA,IAAA,EAAK;AAAA,OAAA;AAAA,KAC/D;AAAA,EAEJ;AAGA,EAAA,IAAI,YAAY,IAAA,EAAM;AACpB,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA,aAAA,CAAA,cAAA,CAAA;AAAA,QACC,WAAA,EAAU,cAAA;AAAA,QACV,cAAA,EAAa,IAAA;AAAA,QACb,WAAW,EAAA,CAAG,mBAAA,CAAoB,EAAE,OAAA,EAAS,GAAG,SAAS;AAAA,OAAA,EACrD,KAAA,CAAA,EAJL;AAAA,QAMC,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,8CAAA,EAEb,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,8FAAA,EACb,QAAA,kBAAA,GAAA,CAAC,UAAK,SAAA,EAAU,yBAAA,EACd,QAAA,kBAAA,GAAA,CAAC,EAAA,EAAA,EAAG,IAAA,EAAK,IAAA,EAAK,UAAA,EAAU,IAAA,EAAC,GAC3B,CAAA,EACF,CAAA;AAAA,0BACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oCAAA,EACZ,QAAA,EAAA,OAAA;AAAA;AAAA,gCAEE,KAAA,EAAA,EAAI,SAAA,EAAU,uGAAA,EACb,QAAA,kBAAA,GAAA,CAAC,cAAW,CAAA,EACd;AAAA,8BAEA,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,YAAA,IAAA;AAAA,4BAEC,GAAA,CAAC,SAAI,SAAA,EAAU,6CAAA,EACb,8BAAC,GAAA,EAAA,EAAE,SAAA,EAAU,yDAAA,EAA2D,QAAA,EAAA,IAAA,EAAK,CAAA,EAC/E,CAAA;AAAA,YAED,4BAAY,GAAA,CAAC,SAAA,EAAA,EAAU,GAAA,EAAK,QAAA,EAAU,SAAQ,SAAA,EAAU,CAAA;AAAA,YACxD,YAAA,oBACC,GAAA;AAAA,cAAC,WAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,IAAA;AAAA,gBACL,KAAA,EAAO,aAAA;AAAA,gBACP,aAAA,EAAe;AAAA;AAAA;AACjB,WAAA,EAEJ,CAAA,EAEJ;AAAA,SAAA,EACF;AAAA,OAAA;AAAA,KACF;AAAA,EAEJ;AAGA,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA,aAAA,CAAA,cAAA,CAAA;AAAA,MACC,WAAA,EAAU,cAAA;AAAA,MACV,cAAA,EAAa,MAAA;AAAA,MACb,WAAW,EAAA,CAAG,mBAAA,CAAoB,EAAE,OAAA,EAAS,GAAG,SAAS;AAAA,KAAA,EACrD,KAAA,CAAA,EAJL;AAAA,MAMC,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,sEAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,WAAU,0DAAA,EACZ,QAAA,EAAA,OAAA;AAAA;AAAA,8BAEE,KAAA,EAAA,EAAI,SAAA,EAAU,qGAAA,EACb,QAAA,kBAAA,GAAA,CAAC,cAAW,CAAA,EACd;AAAA,4BAEA,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,UAAA,IAAA;AAAA;AAAA,0BAGC,GAAA,CAAC,SAAI,SAAA,EAAU,8CAAA,EACb,8BAAC,GAAA,EAAA,EAAE,SAAA,EAAU,yDAAA,EAA2D,QAAA,EAAA,IAAA,EAAK,CAAA,EAC/E,CAAA;AAAA,UAED,4BAAY,GAAA,CAAC,SAAA,EAAA,EAAU,GAAA,EAAK,QAAA,EAAU,SAAQ,OAAA,EAAQ;AAAA,SAAA,EACzD,CAAA,EAEJ,CAAA;AAAA,wBAEA,IAAA,CAAC,MAAA,EAAA,EAAO,IAAA,EAAK,IAAA,EAAK,WAAU,oCAAA,EACzB,QAAA,EAAA;AAAA,UAAA,MAAA,oBACC,GAAA,CAAC,eAAY,GAAA,EAAK,MAAA,EAAQ,KAAK,MAAA,IAAA,IAAA,GAAA,MAAA,GAAU,MAAA,EAAQ,WAAU,YAAA,EAAa,CAAA;AAAA,8BAEzE,cAAA,EAAA,EAAe,SAAA,EAAU,mDAAA,EACvB,QAAA,EAAA,WAAA,CAAY,MAAM,CAAA,EACrB;AAAA,SAAA,EACF;AAAA,OAAA,EACF;AAAA,KAAA;AAAA,GACF;AAEJ","file":"chunk-YMWRR7ET.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Play, Pause } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Avatar, AvatarImage, AvatarFallback } from \"@/components/ui/avatar\"\nimport { LikeDislike, type LikeDislikeValue } from \"@/components/ui/like-dislike\"\nimport { Ai } from \"@/components/icons\"\n\n/* ─── Container variants ──────────────────────────────────────── */\n\nconst chatMessageVariants = cva(\"flex w-full text-sm\", {\n variants: {\n persona: {\n ai: \"justify-start\",\n user: \"justify-end\",\n system: \"justify-center\",\n },\n },\n defaultVariants: {\n persona: \"ai\",\n },\n})\n\n/* ─── Types ───────────────────────────────────────────────────── */\n\nexport interface ChatMessageProps\n extends Omit<React.ComponentProps<\"div\">, \"content\">,\n VariantProps<typeof chatMessageVariants> {\n /** Quem enviou: AI (assistente), user (aluno), system (mensagem do sistema) */\n persona: \"ai\" | \"user\" | \"system\"\n /** Texto da mensagem (opcional se houver apenas audio) */\n text?: string\n /** URL do audio — renderiza AudioPlayer dentro do bubble */\n audioSrc?: string\n /** Avatar do user (so usado em persona=\"user\") */\n avatar?: string\n /** Nome do user para fallback do avatar */\n author?: string\n /** Mostrar like/dislike inline (so faz sentido em persona=\"ai\") */\n showFeedback?: boolean\n /** Valor controlado do feedback */\n feedbackValue?: LikeDislikeValue\n /** Callback quando feedback muda */\n onFeedbackChange?: (value: LikeDislikeValue) => void\n /** Estado de loading: mostra typing indicator (\"...\") */\n loading?: boolean\n}\n\n/* ─── Helpers ─────────────────────────────────────────────────── */\n\nfunction getInitials(name?: string): string {\n if (!name) return \"\"\n return name\n .split(\" \")\n .slice(0, 2)\n .map((w) => w[0])\n .join(\"\")\n .toUpperCase()\n}\n\n/* ─── Typing indicator ────────────────────────────────────────── */\n\nfunction TypingDots() {\n return (\n <span\n className=\"inline-flex items-center gap-1\"\n aria-label=\"Digitando...\"\n role=\"status\"\n >\n <span className=\"size-1.5 rounded-full bg-current opacity-40 animate-bounce [animation-delay:-0.3s]\" />\n <span className=\"size-1.5 rounded-full bg-current opacity-40 animate-bounce [animation-delay:-0.15s]\" />\n <span className=\"size-1.5 rounded-full bg-current opacity-40 animate-bounce\" />\n </span>\n )\n}\n\n/* ─── ChatAudio (compact inline audio player com waveform real) ── */\n\n/** Fallback estatico (valores 0-1) — usado enquanto peaks reais carregam ou quando audio nao pode ser analisado (CORS/decode error) */\nconst STATIC_FALLBACK_PEAKS = [\n 2, 2, 2, 2, 8, 24, 18, 24, 8, 18, 24, 24, 18, 24, 18, 18, 24, 24, 32, 24,\n 24, 32, 24, 32, 32, 24, 18, 24, 24, 18, 24, 18, 18, 24, 24, 24, 24, 24, 32, 24,\n 32, 18, 24, 18, 24, 18, 24, 18, 18, 24, 24, 24, 24, 24, 32, 24, 32, 18, 24, 18,\n 24, 8, 18, 24, 8, 4, 2, 2, 2, 2, 4, 8, 24, 24, 18, 24, 18, 18, 24, 24,\n 32, 24, 24, 32, 24, 32, 32, 24, 18, 24, 24, 18, 24, 8, 18, 24, 8, 2, 2, 4,\n].map((h) => h / 32)\n\nconst MIN_BAR_HEIGHT = 2 // px (silencio)\nconst MAX_BAR_HEIGHT = 32 // px (pico maximo)\nconst BAR_WIDTH = 2 // px (largura fixa de cada barra)\nconst BAR_GAP = 2 // px (espaco entre barras)\nconst SPEED_OPTIONS = [1, 1.25, 1.5, 2, 0.75]\n\n/** Cache modular para evitar re-computar peaks do mesmo audio em multiplas instancias */\nconst peaksCache = new Map<string, number[]>()\n\n/**\n * Calcula picos (amplitude maxima) de um audio dividido em N buckets.\n * Usa Web Audio API (decodeAudioData) — sem dependencias externas.\n * Retorna array de valores 0-1 (normalizado pelo pico maximo).\n */\nasync function calculatePeaks(audioUrl: string, barCount: number): Promise<number[]> {\n const response = await fetch(audioUrl)\n const arrayBuffer = await response.arrayBuffer()\n\n const AudioContextClass =\n window.AudioContext ||\n (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext\n const audioContext = new AudioContextClass()\n const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)\n\n const channelData = audioBuffer.getChannelData(0) // mono ou canal esquerdo\n const samplesPerBar = Math.floor(channelData.length / barCount)\n const peaks: number[] = []\n\n for (let i = 0; i < barCount; i++) {\n const start = i * samplesPerBar\n const end = Math.min(start + samplesPerBar, channelData.length)\n let max = 0\n for (let j = start; j < end; j++) {\n const absSample = Math.abs(channelData[j])\n if (absSample > max) max = absSample\n }\n peaks.push(max)\n }\n\n // Normaliza para 0-1 relativo ao pico maximo do clip\n const maxPeak = Math.max(...peaks, 0.01)\n\n // Fecha o AudioContext (libera recursos)\n void audioContext.close()\n\n return peaks.map((p) => p / maxPeak)\n}\n\n/** Wrapper com cache */\nasync function getPeaks(url: string, barCount: number): Promise<number[]> {\n const cacheKey = `${url}|${barCount}`\n const cached = peaksCache.get(cacheKey)\n if (cached) return cached\n const peaks = await calculatePeaks(url, barCount)\n peaksCache.set(cacheKey, peaks)\n return peaks\n}\n\nfunction formatAudioTime(seconds: number): string {\n if (!Number.isFinite(seconds) || seconds < 0) return \"00:00\"\n const m = Math.floor(seconds / 60)\n const s = Math.floor(seconds % 60)\n return `${m.toString().padStart(2, \"0\")}:${s.toString().padStart(2, \"0\")}`\n}\n\nexport interface ChatAudioProps extends React.ComponentProps<\"div\"> {\n /** URL do audio */\n src: string\n /**\n * Variante visual:\n * - `neutral`: container `bg-muted` (cinza claro) — usado em AI messages\n * - `brand`: container `bg-accent` (rosa pastel) — usado em User messages\n * (requer wrapper `.theme-brand` no componente pai)\n */\n variant?: \"neutral\" | \"brand\"\n /**\n * Array de picos pre-calculados (valores 0-1). Quando fornecido, o componente\n * usa direto sem analisar o audio. Util para SSR, performance ou audios sem CORS.\n * Se nao fornecido, o componente analisa o audio automaticamente via Web Audio API.\n */\n peaks?: number[]\n /**\n * Numero de barras no waveform (default: 60).\n * Ignorado se `peaks` for fornecido (usa o length do array).\n * Reduzido de 100 → 60 para barras nao ficarem finas demais em containers menores.\n */\n barCount?: number\n}\n\nfunction ChatAudio({\n src,\n variant = \"neutral\",\n peaks: providedPeaks,\n barCount = 60,\n className,\n ...props\n}: ChatAudioProps) {\n const audioRef = React.useRef<HTMLAudioElement>(null)\n const [isPlaying, setIsPlaying] = React.useState(false)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [speed, setSpeed] = React.useState(1)\n const [computedPeaks, setComputedPeaks] = React.useState<number[] | null>(null)\n\n /** Auto-computa peaks se nao foram fornecidos */\n React.useEffect(() => {\n if (providedPeaks) return // consumer ja forneceu, skip analise\n let cancelled = false\n getPeaks(src, barCount)\n .then((p) => {\n if (!cancelled) setComputedPeaks(p)\n })\n .catch((err) => {\n /* fallback silencioso: mantem null, usa fallback estatico.\n Causa comum: CORS (audio source nao tem Access-Control-Allow-Origin).\n Log apenas em dev para nao poluir prod. */\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[ChatAudio] Failed to analyze waveform for \"${src}\". ` +\n `Falling back to static waveform. ` +\n `Reason: ${err instanceof Error ? err.message : String(err)}. ` +\n `Audio source likely missing CORS headers (Access-Control-Allow-Origin).`\n )\n }\n })\n return () => {\n cancelled = true\n }\n }, [src, providedPeaks, barCount])\n\n /** Fallback estatico ajustado para o barCount atual (evita troca visual brusca quando peaks reais carregam) */\n const fallbackPeaks = React.useMemo(() => {\n return Array.from(\n { length: barCount },\n (_, i) => STATIC_FALLBACK_PEAKS[i % STATIC_FALLBACK_PEAKS.length]\n )\n }, [barCount])\n\n /** Resolve peaks: provided > computed > fallback (ajustado por barCount) */\n const peaks = providedPeaks ?? computedPeaks ?? fallbackPeaks\n\n /** Seta duracao apenas se valor for finito e positivo (evita Infinity/NaN) */\n const setDurationSafe = React.useCallback((value: number) => {\n if (Number.isFinite(value) && value > 0) {\n setDuration((prev) => (prev !== value ? value : prev))\n }\n }, [])\n\n /** Subscribe a multiplos eventos para garantir captura da duracao\n * (alguns audio sources nao disparam loadedmetadata, so durationchange/canplay) */\n React.useEffect(() => {\n const audio = audioRef.current\n if (!audio) return\n\n const handleDuration = () => setDurationSafe(audio.duration)\n handleDuration() // checa se ja esta carregado\n\n audio.addEventListener(\"loadedmetadata\", handleDuration)\n audio.addEventListener(\"durationchange\", handleDuration)\n audio.addEventListener(\"canplay\", handleDuration)\n return () => {\n audio.removeEventListener(\"loadedmetadata\", handleDuration)\n audio.removeEventListener(\"durationchange\", handleDuration)\n audio.removeEventListener(\"canplay\", handleDuration)\n }\n }, [src, setDurationSafe])\n\n const togglePlay = () => {\n const audio = audioRef.current\n if (!audio) return\n if (audio.paused) {\n audio.play().catch(() => {})\n } else {\n audio.pause()\n }\n }\n\n const cycleSpeed = () => {\n const idx = SPEED_OPTIONS.indexOf(speed)\n const next = SPEED_OPTIONS[(idx + 1) % SPEED_OPTIONS.length] ?? 1\n setSpeed(next)\n if (audioRef.current) audioRef.current.playbackRate = next\n }\n\n /* ─── Seek/drag via pointer no waveform ───────────────────── */\n\n const waveformRef = React.useRef<HTMLDivElement>(null)\n const isDraggingRef = React.useRef(false)\n\n /** Converte clientX (event) em time do audio, baseado na largura do layout de barras */\n const seekToClientX = React.useCallback(\n (clientX: number) => {\n const audio = audioRef.current\n const waveform = waveformRef.current\n if (!audio || !waveform || !duration) return\n const rect = waveform.getBoundingClientRect()\n const x = clientX - rect.left\n const newProgress = Math.max(\n 0,\n Math.min(x / (peaks.length * (BAR_WIDTH + BAR_GAP) - BAR_GAP), 1)\n )\n audio.currentTime = newProgress * duration\n },\n [duration, peaks.length]\n )\n\n const handlePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {\n if (!duration) return\n isDraggingRef.current = true\n e.currentTarget.setPointerCapture(e.pointerId)\n seekToClientX(e.clientX)\n }\n\n const handlePointerMove = (e: React.PointerEvent<HTMLDivElement>) => {\n if (!isDraggingRef.current) return\n seekToClientX(e.clientX)\n }\n\n const handlePointerUp = (e: React.PointerEvent<HTMLDivElement>) => {\n if (!isDraggingRef.current) return\n isDraggingRef.current = false\n e.currentTarget.releasePointerCapture(e.pointerId)\n }\n\n const progress = duration > 0 ? currentTime / duration : 0\n const displayTime = formatAudioTime(currentTime)\n\n /**\n * Largura natural do layout de barras (em px):\n * - N barras de BAR_WIDTH + (N-1) gaps de BAR_GAP\n * Usado para posicionar a bolinha e calcular seek por click/drag.\n */\n const barsLayoutWidth = peaks.length * (BAR_WIDTH + BAR_GAP) - BAR_GAP\n\n /* Waveform JSX — reusado entre layouts mobile (top) e desktop (inline).\n Barras com largura FIXA de 2px (matches Figma). Layout pode overflowar\n em containers narrow; overflow-hidden clipa a direita.\n Pointer events permitem clicar/arrastar para seek. */\n const waveform = (\n <div\n ref={waveformRef}\n className=\"flex-1 relative h-8 min-w-0 overflow-hidden touch-none cursor-pointer select-none\"\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerUp}\n role=\"slider\"\n aria-label=\"Posicao do audio\"\n aria-valuemin={0}\n aria-valuemax={Math.round(duration)}\n aria-valuenow={Math.round(currentTime)}\n >\n <div className=\"absolute inset-y-0 left-0 flex items-center gap-[2px]\">\n {peaks.map((peak, i) => {\n const isPlayed = i / peaks.length <= progress\n const height = Math.max(\n MIN_BAR_HEIGHT,\n Math.round(peak * MAX_BAR_HEIGHT)\n )\n return (\n <div\n key={i}\n className={cn(\n \"w-[2px] shrink-0 rounded-full\",\n isPlayed ? \"bg-neutral-foreground\" : \"bg-neutral-ring\"\n )}\n style={{ height: `${height}px` }}\n />\n )\n })}\n </div>\n {/* Bolinha verde — posicionada em PIXELS reais relativos ao layout das barras\n (matches exatamente a transicao played/unplayed). */}\n {duration > 0 && (\n <div\n className=\"absolute top-1/2 z-10 -translate-y-1/2 -translate-x-1/2 size-3 rounded-full bg-[#098A5E] pointer-events-none shadow-md\"\n style={{ left: `${progress * barsLayoutWidth}px` }}\n aria-hidden=\"true\"\n />\n )}\n </div>\n )\n\n /* Time display — reusado entre layouts */\n const timeDisplay = (\n <span className=\"shrink-0 text-sm text-neutral-ring tabular-nums\">\n {displayTime}\n </span>\n )\n\n /* Speed badge — reusado entre layouts */\n const speedBadge = (\n <button\n type=\"button\"\n onClick={cycleSpeed}\n className={cn(\n \"shrink-0 h-[26px] min-w-16 rounded-full text-sm font-medium px-2 transition-opacity hover:opacity-80\",\n variant === \"brand\"\n ? \"bg-muted text-muted-foreground\"\n : \"bg-background text-neutral-muted-foreground\"\n )}\n aria-label={`Velocidade ${speed}x. Clique para alternar.`}\n >\n {speed === 1 ? \"1x\" : `${speed}x`}\n </button>\n )\n\n return (\n <div\n data-slot=\"chat-audio\"\n className={cn(\n \"rounded-2xl w-full max-w-md flex flex-col gap-2 p-3 sm:flex-row sm:items-center sm:gap-2 sm:pl-5 sm:pr-3 sm:py-3\",\n variant === \"brand\" ? \"bg-accent\" : \"bg-muted\",\n className\n )}\n {...props}\n >\n <audio\n ref={audioRef}\n src={src}\n preload=\"metadata\"\n onPlay={() => setIsPlaying(true)}\n onPause={() => setIsPlaying(false)}\n onTimeUpdate={(e) => {\n setCurrentTime(e.currentTarget.currentTime)\n /* Fallback: alguns sources so expoem duration durante o playback */\n setDurationSafe(e.currentTarget.duration)\n }}\n onEnded={() => setIsPlaying(false)}\n />\n\n {/* Mobile top row + Desktop inline row */}\n <div className=\"flex items-center gap-2 flex-1 min-w-0\">\n {/* Play/Pause — icones FILLED */}\n <button\n type=\"button\"\n onClick={togglePlay}\n className=\"shrink-0 flex items-center justify-center text-neutral-foreground hover:opacity-80 transition-opacity\"\n aria-label={isPlaying ? \"Pausar\" : \"Reproduzir\"}\n >\n {isPlaying ? (\n <Pause className=\"size-4\" fill=\"currentColor\" strokeWidth={0} />\n ) : (\n <Play className=\"size-4\" fill=\"currentColor\" strokeWidth={0} />\n )}\n </button>\n\n {/* Time — apenas desktop (no mobile aparece embaixo) */}\n <div className=\"hidden sm:contents\">{timeDisplay}</div>\n\n {waveform}\n\n {/* Speed — apenas desktop (no mobile aparece embaixo) */}\n <div className=\"hidden sm:contents\">{speedBadge}</div>\n </div>\n\n {/* Mobile bottom row: time + speed badge */}\n <div className=\"flex items-center justify-between sm:hidden\">\n {timeDisplay}\n {speedBadge}\n </div>\n </div>\n )\n}\n\nexport { ChatAudio }\n\n/* ─── Component ───────────────────────────────────────────────── */\n\nfunction ChatMessage({\n persona,\n text,\n audioSrc,\n avatar,\n author,\n showFeedback = false,\n feedbackValue,\n onFeedbackChange,\n loading = false,\n className,\n ...props\n}: ChatMessageProps) {\n /* System: centered italic */\n if (persona === \"system\") {\n return (\n <div\n data-slot=\"chat-message\"\n data-persona=\"system\"\n className={cn(chatMessageVariants({ persona }), className)}\n {...props}\n >\n <span className=\"text-xs text-muted-foreground italic\">{text}</span>\n </div>\n )\n }\n\n /* AI: badge quadrado (esquerda, apenas desktop) + conteudo empilhado */\n if (persona === \"ai\") {\n return (\n <div\n data-slot=\"chat-message\"\n data-persona=\"ai\"\n className={cn(chatMessageVariants({ persona }), className)}\n {...props}\n >\n <div className=\"flex gap-3 max-w-[85%] sm:max-w-[75%] w-full\">\n {/* AI badge: oculto no mobile, visivel no desktop */}\n <div className=\"hidden sm:flex shrink-0 size-8 rounded-md bg-primary items-center justify-center theme-brand\">\n <span className=\"text-primary-foreground\">\n <Ai size=\"sm\" decorative />\n </span>\n </div>\n <div className=\"flex flex-col gap-2 min-w-0 flex-1\">\n {loading ? (\n /* Loading: dots dentro de bubble cinza (speech bubble shape — top-left reto) */\n <div className=\"self-start inline-flex items-center rounded-2xl sm:rounded-tl-none bg-muted p-5 text-muted-foreground\">\n <TypingDots />\n </div>\n ) : (\n <>\n {text && (\n /* Text bubble: bg-muted sempre, speech bubble shape (tl-none) apenas no desktop */\n <div className=\"rounded-2xl bg-muted p-5 sm:rounded-tl-none\">\n <p className=\"text-base leading-6 text-neutral-foreground break-words\">{text}</p>\n </div>\n )}\n {audioSrc && <ChatAudio src={audioSrc} variant=\"neutral\" />}\n {showFeedback && (\n <LikeDislike\n size=\"xs\"\n value={feedbackValue}\n onValueChange={onFeedbackChange}\n />\n )}\n </>\n )}\n </div>\n </div>\n </div>\n )\n }\n\n /* User: bubble (direita) + avatar (apenas desktop, alinhado ao topo) */\n return (\n <div\n data-slot=\"chat-message\"\n data-persona=\"user\"\n className={cn(chatMessageVariants({ persona }), className)}\n {...props}\n >\n <div className=\"flex items-start gap-3 max-w-[85%] sm:max-w-[75%] w-full justify-end\">\n <div className=\"flex flex-col gap-2 min-w-0 items-end theme-brand flex-1\">\n {loading ? (\n /* Loading: dots dentro de bubble pink (speech bubble shape — top-right reto) */\n <div className=\"inline-flex items-center rounded-2xl sm:rounded-tr-none bg-accent p-5 text-neutral-muted-foreground\">\n <TypingDots />\n </div>\n ) : (\n <>\n {text && (\n /* Text bubble: speech bubble shape (top-right reto) no desktop.\n text-neutral-foreground = preto neutro fixo, nao muda com theme-brand */\n <div className=\"rounded-2xl sm:rounded-tr-none bg-accent p-5\">\n <p className=\"text-base leading-6 text-neutral-foreground break-words\">{text}</p>\n </div>\n )}\n {audioSrc && <ChatAudio src={audioSrc} variant=\"brand\" />}\n </>\n )}\n </div>\n {/* Avatar: 40x40 rounded-lg, oculto no mobile, alinhado ao topo */}\n <Avatar size=\"lg\" className=\"hidden sm:flex shrink-0 rounded-lg\">\n {avatar && (\n <AvatarImage src={avatar} alt={author ?? \"User\"} className=\"rounded-lg\" />\n )}\n <AvatarFallback className=\"rounded-lg bg-muted text-muted-foreground text-sm\">\n {getInitials(author)}\n </AvatarFallback>\n </Avatar>\n </div>\n </div>\n )\n}\n\nexport { ChatMessage, chatMessageVariants }\n"]}
package/dist/index.d.ts CHANGED
@@ -21,10 +21,17 @@ export { Progress, ProgressProps, indicatorVariants, progressVariants } from './
21
21
  export { ProgressStage, ProgressStageProps } from './ui/progress-stage.js';
22
22
  export { ProgressDot, ProgressDotProps, dotVariants, progressDotVariants } from './ui/progress-dot.js';
23
23
  export { CircularProgress, CircularProgressProps, circularProgressVariants } from './ui/circular-progress.js';
24
+ export { Container, ContainerProps, containerVariants } from './layout/container.js';
25
+ export { Stack, StackProps } from './layout/stack.js';
26
+ export { Grid, GridItem, GridItemProps, GridProps } from './layout/grid.js';
27
+ export { C as ColCount, G as GapToken } from './types-CVdl5nka.js';
24
28
  export { FileCard, FileCardProps, fileCardVariants } from './ui/file-card.js';
25
29
  export { Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage } from './ui/avatar.js';
26
30
  export { ChatBubble, ChatBubbleProps, chatBubbleVariants } from './ui/chat-bubble.js';
27
- export { ChatMessage, ChatPanel, ChatPanelProps } from './ui/chat-panel.js';
31
+ export { ChatAudio, ChatAudioProps, ChatMessage, ChatMessageProps, chatMessageVariants } from './ui/chat-message.js';
32
+ export { MessageRating, MessageRatingLabels, MessageRatingProps, MessageRatingValue, RatingLabel } from './ui/message-rating.js';
33
+ export { MessageBar, MessageBarProps, MessageBarState } from './ui/message-bar.js';
34
+ export { ChatPanel, ChatMessage as ChatPanelMessage, ChatPanelProps } from './ui/chat-panel.js';
28
35
  export { LikeDislike, LikeDislikeProps, LikeDislikeValue, likeDislikeVariants } from './ui/like-dislike.js';
29
36
  export { LiveWaiting, LiveWaitingProps } from './ui/live-waiting.js';
30
37
  export { AudioPlayer, AudioPlayerLabels, AudioPlayerProps, AudioPlayerRef } from './ui/audio-player.js';
package/dist/index.js CHANGED
@@ -1,34 +1,40 @@
1
- export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger } from './chunk-YJ4U7ISM.js';
2
- export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from './chunk-NOMLQHZS.js';
3
1
  export { Editor } from './chunk-D4UH2HJQ.js';
4
2
  export { ClassLogo, GroupTalkLogo, PrivateTalkLogo, ProductLogo } from './chunk-66CU7J2I.js';
5
3
  export { FluencypassIcon, FluencypassLogo } from './chunk-5LZHXNBV.js';
6
- export { Popover, PopoverAnchor, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from './chunk-EF6FQT4Y.js';
7
- export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './chunk-IGMII4BK.js';
8
4
  export { ResizableHandle, ResizablePanel, ResizablePanelGroup } from './chunk-PY2BIZNB.js';
9
5
  export { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from './chunk-F2Q3E2ZM.js';
10
6
  export { Skeleton } from './chunk-2EKU7RP4.js';
11
7
  export { Spinner } from './chunk-5XNYJECW.js';
12
8
  export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from './chunk-5NKTYXWQ.js';
13
9
  export { Fab, fabVariants } from './chunk-7NFHHOAE.js';
14
- export { LiveWaiting } from './chunk-PCLVQPIG.js';
15
- export { AudioPlayer } from './chunk-DCNZ2DRR.js';
10
+ export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger } from './chunk-YJ4U7ISM.js';
11
+ export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from './chunk-NOMLQHZS.js';
16
12
  export { VideoPlayer } from './chunk-V3CBOIGK.js';
17
- export { playerLocales } from './chunk-C6BQAAHX.js';
18
13
  export { Toaster, cycleToast } from './chunk-ELZCZ6ZH.js';
19
14
  export { Alert, AlertAction, AlertClose, AlertDescription, AlertTitle, alertVariants } from './chunk-7ZXAU2CD.js';
20
15
  export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger } from './chunk-3EFI7PYC.js';
21
16
  export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from './chunk-SZUWVHP4.js';
22
- export { ProgressStage } from './chunk-NYJMA2T7.js';
23
- export { ProgressDot, dotVariants, progressDotVariants } from './chunk-ZTANTDKS.js';
24
- export { CircularProgress, circularProgressVariants } from './chunk-A3Z5GGAI.js';
25
- export { FileCard, fileCardVariants } from './chunk-JMNMW54C.js';
17
+ export { Popover, PopoverAnchor, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from './chunk-EF6FQT4Y.js';
18
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './chunk-IGMII4BK.js';
19
+ export { ChatAudio, ChatMessage, chatMessageVariants } from './chunk-YMWRR7ET.js';
20
+ export { MessageRating } from './chunk-37C2K2NM.js';
21
+ export { MessageBar } from './chunk-PW464XEP.js';
26
22
  export { ChatPanel } from './chunk-L32AMI4K.js';
27
23
  export { ChatBubble, chatBubbleVariants } from './chunk-HZJRM5EK.js';
28
- export { Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage } from './chunk-MSLQRGSP.js';
29
24
  export { LikeDislike, likeDislikeVariants } from './chunk-F2XA2Z75.js';
25
+ export { LiveWaiting } from './chunk-L2DJS6PG.js';
26
+ export { AudioPlayer } from './chunk-DCNZ2DRR.js';
27
+ export { playerLocales } from './chunk-C6BQAAHX.js';
30
28
  export { Achievement, Ai, Answer, Badge as BadgeIcon, Certificate, Chat as ChatIcon, Checkpoint, Completion, Conversation, Course, Deadline, Dialogue, Dictionary, Diploma, DotLive, Exercise, Feedback, Flashcard, Fluency, Goal, Grammar, GroupClass, Highlight, Language, Lesson, Listening, Live, MemoryCard, Milestone, Module, Presentation, PrivateClass, Progress as ProgressIcon, Question, Quiz, Ray, Reading, Recap, RecordedClass, Recurring, Repetition, Required, Schedule, Sentence, Streak, Task, Translate, Unit, Vocabulary, Whiteboard } from './chunk-JPEDYOV7.js';
31
29
  export { CycleIcon, ICON_SIZES } from './chunk-V7M2NHUO.js';
30
+ export { ProgressStage } from './chunk-NYJMA2T7.js';
31
+ export { ProgressDot, dotVariants, progressDotVariants } from './chunk-ZTANTDKS.js';
32
+ export { CircularProgress, circularProgressVariants } from './chunk-A3Z5GGAI.js';
33
+ export { Container, containerVariants } from './chunk-QANROH2K.js';
34
+ export { Stack } from './chunk-I5A3CME4.js';
35
+ export { Grid, GridItem } from './chunk-JRJBKPXW.js';
36
+ export { FileCard, fileCardVariants } from './chunk-JMNMW54C.js';
37
+ export { Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage } from './chunk-MSLQRGSP.js';
32
38
  export { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from './chunk-QZVQPUVT.js';
33
39
  export { ScrollArea, ScrollBar } from './chunk-3LXU5C35.js';
34
40
  export { Checkbox, checkboxVariants } from './chunk-5BNTQSCS.js';