@fluencypassdevs/cycle 1.28.0 → 1.29.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.
- package/bin/components.generated.mjs +14 -7
- package/dist/{chunk-JPEQ4BYK.js → chunk-5AH2PNYW.js} +68 -3
- package/dist/chunk-5AH2PNYW.js.map +1 -0
- package/dist/{chunk-CLMO3IRS.js → chunk-MQX4NBKQ.js} +47 -10
- package/dist/chunk-MQX4NBKQ.js.map +1 -0
- package/dist/composites/chat-thread.d.ts +16 -5
- package/dist/composites/chat-thread.js +3 -2
- package/dist/index.js +2 -2
- package/dist/ui/message-bar.d.ts +4 -1
- package/dist/ui/message-bar.js +4 -1
- package/package.json +1 -1
- package/dist/chunk-CLMO3IRS.js.map +0 -1
- package/dist/chunk-JPEQ4BYK.js.map +0 -1
|
@@ -1087,11 +1087,11 @@ export const COMPONENTS = [
|
|
|
1087
1087
|
{
|
|
1088
1088
|
"name": "MessageBar",
|
|
1089
1089
|
"import": "import { MessageBar } from \"@fluencypassdevs/cycle\"",
|
|
1090
|
-
"description": "Input bar para chat com IA.
|
|
1090
|
+
"description": "Input bar para chat com IA. 7 estados: default, audio-only, active, disabled, recording, paused, requesting (mic vira spinner enquanto o consumer aguarda getUserMedia). UI-only: consumer implementa MediaRecorder + HTMLAudioElement via callbacks. Live waveform real durante recording (Web Audio API, rolling window estilo WhatsApp). RecordedWaveform real durante paused com peaks + barras tocadas/nao-tocadas + dot verde + click/drag-to-seek. Press-to-record no mobile (touch-only): long-press inicia, drag-up trava em hands-free, drag-left cancela, release < 1s cancela, release >= 1s envia. Vibracao haptica em lock/cancel. Suporta limite de duracao via maxRecordingDuration (segundos) com warning visual configuravel nos segundos finais (timer destructive + 'Xs restantes' aria-live) e callback onMaxDurationReached ao atingir o limite.",
|
|
1091
1091
|
"props": [
|
|
1092
1092
|
{
|
|
1093
1093
|
"name": "state",
|
|
1094
|
-
"type": "\"default\" | \"audio-only\" | \"active\" | \"disabled\" | \"recording\" | \"paused\"",
|
|
1094
|
+
"type": "\"default\" | \"audio-only\" | \"active\" | \"disabled\" | \"recording\" | \"paused\" | \"requesting\"",
|
|
1095
1095
|
"default": "\"default\""
|
|
1096
1096
|
},
|
|
1097
1097
|
{
|
|
@@ -1217,7 +1217,7 @@ export const COMPONENTS = [
|
|
|
1217
1217
|
{
|
|
1218
1218
|
"name": "ChatThread",
|
|
1219
1219
|
"import": "import { ChatThread, type ChatThreadMessage } from \"@fluencypassdevs/cycle\"",
|
|
1220
|
-
"description": "Composite 'tela de chat pronta' que junta ChatMessage + MessageBar + MessageRating. Gerencia state machine interno do MessageBar (default↔recording↔paused) baseado nos callbacks. Auto-scroll inteligente (preserva posicao se user subiu). Scroll infinito via onLoadMore. Banners configuraveis: offline, rateLimitedUntil (countdown), quotaExhausted (AlertDialog). Retry inline em msgs failed do user (B2) e regenerar resposta da IA (B3). Botao 'Parar resposta' durante thinking e OPT-IN via presenca de onStopResponse — sem callback, MessageBar some com animacao de saida e volta quando IA termina. requestRating per-message renderiza MessageRating inline embaixo da bubble da IA — escolha some imediato, callback onRate(messageId, value, label) permite consumer enviar como msg do user. initialThinking pra conversas que comecam com IA falando. Input controlled internamente. Animacoes fade+slide (200ms enter / 150ms leave). Suporta limite de duracao de gravacao via maxRecordingDuration (segundos) com warning visual configuravel e auto-pause ao atingir; override via onMaxDurationReached.",
|
|
1220
|
+
"description": "Composite 'tela de chat pronta' que junta ChatMessage + MessageBar + MessageRating. Gerencia state machine interno do MessageBar (default↔requesting↔recording↔paused) baseado nos callbacks. onStartRecording/onResumeRecording podem retornar Promise: MessageBar fica em requesting ate resolver; rejeitou → volta pro estado anterior + onRecordingError. Auto-scroll inteligente (preserva posicao se user subiu). Scroll infinito via onLoadMore. Banners configuraveis: offline, rateLimitedUntil (countdown), quotaExhausted (AlertDialog). Retry inline em msgs failed do user (B2) e regenerar resposta da IA (B3). Botao 'Parar resposta' durante thinking e OPT-IN via presenca de onStopResponse — sem callback, MessageBar some com animacao de saida e volta quando IA termina. requestRating per-message renderiza MessageRating inline embaixo da bubble da IA — escolha some imediato, callback onRate(messageId, value, label) permite consumer enviar como msg do user. initialThinking pra conversas que comecam com IA falando. Input controlled internamente. Animacoes fade+slide (200ms enter / 150ms leave). Suporta limite de duracao de gravacao via maxRecordingDuration (segundos) com warning visual configuravel e auto-pause ao atingir; override via onMaxDurationReached.",
|
|
1221
1221
|
"props": [
|
|
1222
1222
|
{
|
|
1223
1223
|
"name": "messages",
|
|
@@ -1256,9 +1256,9 @@ export const COMPONENTS = [
|
|
|
1256
1256
|
},
|
|
1257
1257
|
{
|
|
1258
1258
|
"name": "onStartRecording",
|
|
1259
|
-
"type": "() => void",
|
|
1259
|
+
"type": "() => void | Promise<unknown>",
|
|
1260
1260
|
"default": "-",
|
|
1261
|
-
"note": "consumer chama getUserMedia + cria MediaRecorder"
|
|
1261
|
+
"note": "consumer chama getUserMedia + cria MediaRecorder. Retorne a Promise: MessageBar fica em requesting ate resolver e so entra em recording depois (evita 'gravando' sem microfone)"
|
|
1262
1262
|
},
|
|
1263
1263
|
{
|
|
1264
1264
|
"name": "onPauseRecording",
|
|
@@ -1268,8 +1268,15 @@ export const COMPONENTS = [
|
|
|
1268
1268
|
},
|
|
1269
1269
|
{
|
|
1270
1270
|
"name": "onResumeRecording",
|
|
1271
|
-
"type": "() => void",
|
|
1272
|
-
"default": "-"
|
|
1271
|
+
"type": "() => void | Promise<unknown>",
|
|
1272
|
+
"default": "-",
|
|
1273
|
+
"note": "mesma semantica de Promise do onStartRecording (rejeitou → volta pra paused)"
|
|
1274
|
+
},
|
|
1275
|
+
{
|
|
1276
|
+
"name": "onRecordingError",
|
|
1277
|
+
"type": "(error: unknown) => void",
|
|
1278
|
+
"default": "-",
|
|
1279
|
+
"note": "dispara quando a Promise de start/resume rejeita (ex.: permissao negada). Estado ja restaurado; consumer da o feedback"
|
|
1273
1280
|
},
|
|
1274
1281
|
{
|
|
1275
1282
|
"name": "onCancelRecording",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Spinner } from './chunk-TDBDX5NV.js';
|
|
1
2
|
import { cn } from './chunk-W4KJCBMW.js';
|
|
2
3
|
import { __objRest, __spreadProps, __spreadValues } from './chunk-2NMEKWO5.js';
|
|
3
4
|
import * as React from 'react';
|
|
@@ -582,10 +583,15 @@ function MessageBar(_a) {
|
|
|
582
583
|
const lastBaseStateRef = React.useRef(
|
|
583
584
|
state === "audio-only" ? "audio-only" : "default"
|
|
584
585
|
);
|
|
586
|
+
const requestingFromRef = React.useRef(null);
|
|
585
587
|
React.useEffect(() => {
|
|
586
588
|
const prev = prevStateRef.current;
|
|
589
|
+
if (state === "requesting" && prev !== "requesting") {
|
|
590
|
+
requestingFromRef.current = prev;
|
|
591
|
+
}
|
|
587
592
|
if (state === "recording" && prev !== "paused" && prev !== "recording") {
|
|
588
|
-
|
|
593
|
+
const resuming = prev === "requesting" && requestingFromRef.current === "paused";
|
|
594
|
+
if (!resuming) samplesRef.current = [];
|
|
589
595
|
}
|
|
590
596
|
if (state === "default" || state === "audio-only") {
|
|
591
597
|
lastBaseStateRef.current = state;
|
|
@@ -607,6 +613,16 @@ function MessageBar(_a) {
|
|
|
607
613
|
pressDeltaRef.current = { x: dx, y: dy };
|
|
608
614
|
setPressDelta({ x: dx, y: dy });
|
|
609
615
|
};
|
|
616
|
+
const prevStateForPressRef = React.useRef(state);
|
|
617
|
+
React.useEffect(() => {
|
|
618
|
+
const prev = prevStateForPressRef.current;
|
|
619
|
+
prevStateForPressRef.current = state;
|
|
620
|
+
const backToBase = state === "default" || state === "audio-only" || state === "disabled";
|
|
621
|
+
if (prev === "requesting" && backToBase && isPressedRef.current) {
|
|
622
|
+
setIsPressed(false);
|
|
623
|
+
updateDelta(0, 0);
|
|
624
|
+
}
|
|
625
|
+
}, [state]);
|
|
610
626
|
React.useEffect(() => {
|
|
611
627
|
const onTouchMove = (e) => {
|
|
612
628
|
if (isPressedRef.current) {
|
|
@@ -701,6 +717,55 @@ function MessageBar(_a) {
|
|
|
701
717
|
onSendText == null ? void 0 : onSendText(value);
|
|
702
718
|
}
|
|
703
719
|
};
|
|
720
|
+
if (state === "requesting") {
|
|
721
|
+
const busyMic = /* @__PURE__ */ jsx(
|
|
722
|
+
"button",
|
|
723
|
+
{
|
|
724
|
+
type: "button",
|
|
725
|
+
disabled: true,
|
|
726
|
+
"aria-busy": "true",
|
|
727
|
+
"aria-label": "Pedindo acesso ao microfone",
|
|
728
|
+
className: "shrink-0 inline-flex items-center justify-center size-10 rounded-md bg-primary text-primary-foreground theme-brand opacity-70 cursor-wait",
|
|
729
|
+
children: /* @__PURE__ */ jsx(Spinner, { size: "sm" })
|
|
730
|
+
}
|
|
731
|
+
);
|
|
732
|
+
if (lastBaseStateRef.current === "audio-only") {
|
|
733
|
+
return /* @__PURE__ */ jsx(
|
|
734
|
+
"div",
|
|
735
|
+
__spreadProps(__spreadValues({
|
|
736
|
+
"data-slot": "message-bar",
|
|
737
|
+
"data-state": "requesting",
|
|
738
|
+
className: cn("flex items-center justify-center p-2", className)
|
|
739
|
+
}, props), {
|
|
740
|
+
children: busyMic
|
|
741
|
+
})
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
return /* @__PURE__ */ jsxs(
|
|
745
|
+
"div",
|
|
746
|
+
__spreadProps(__spreadValues({
|
|
747
|
+
"data-slot": "message-bar",
|
|
748
|
+
"data-state": "requesting",
|
|
749
|
+
className: cn("flex items-center gap-2 rounded-2xl bg-muted p-2", className)
|
|
750
|
+
}, props), {
|
|
751
|
+
children: [
|
|
752
|
+
/* @__PURE__ */ jsx(
|
|
753
|
+
"input",
|
|
754
|
+
{
|
|
755
|
+
type: "text",
|
|
756
|
+
value,
|
|
757
|
+
readOnly: true,
|
|
758
|
+
disabled: true,
|
|
759
|
+
placeholder,
|
|
760
|
+
className: "flex-1 min-w-0 bg-transparent px-3 py-2 text-base text-neutral-foreground placeholder:text-neutral-muted-foreground outline-none disabled:cursor-wait disabled:opacity-50",
|
|
761
|
+
"aria-label": "Mensagem"
|
|
762
|
+
}
|
|
763
|
+
),
|
|
764
|
+
busyMic
|
|
765
|
+
]
|
|
766
|
+
})
|
|
767
|
+
);
|
|
768
|
+
}
|
|
704
769
|
if (state === "audio-only") {
|
|
705
770
|
return /* @__PURE__ */ jsx(
|
|
706
771
|
"div",
|
|
@@ -932,5 +997,5 @@ function MessageBar(_a) {
|
|
|
932
997
|
}
|
|
933
998
|
|
|
934
999
|
export { MessageBar };
|
|
935
|
-
//# sourceMappingURL=chunk-
|
|
936
|
-
//# sourceMappingURL=chunk-
|
|
1000
|
+
//# sourceMappingURL=chunk-5AH2PNYW.js.map
|
|
1001
|
+
//# sourceMappingURL=chunk-5AH2PNYW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/message-bar.tsx"],"names":["_a","_b"],"mappings":"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","file":"chunk-5AH2PNYW.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Mic, Send, Trash2, Pause, Play, Lock, ChevronUp, ChevronLeft } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Spinner } from \"@/components/ui/spinner\"\n\n/* ─── Types ───────────────────────────────────────────────────── */\n\n/**\n * Estados visuais do MessageBar:\n * - `default`: input texto + botao mic (estado inicial, sem texto digitado)\n * - `audio-only`: apenas botao mic centralizado (modo so audio)\n * - `active`: input texto com conteudo digitado + botao send\n * - `disabled`: input desabilitado\n * - `recording`: barra de gravacao (timer + waveform live + delete/pause/send)\n * - `paused`: gravacao pausada (timer + waveform + play/delete/mic/send)\n * - `requesting`: aguardando o consumer obter o microfone (getUserMedia pendente) —\n * mic vira spinner desabilitado. O ChatThread entra aqui quando `onStartRecording`\n * / `onResumeRecording` retornam Promise; consumers diretos podem usar tambem.\n */\nexport type MessageBarState =\n | \"default\"\n | \"audio-only\"\n | \"active\"\n | \"disabled\"\n | \"recording\"\n | \"paused\"\n | \"requesting\"\n\nexport interface MessageBarProps extends Omit<React.ComponentProps<\"div\">, \"onChange\"> {\n /** Estado visual do componente (controlado pelo consumer) */\n state?: MessageBarState\n /** Valor do input de texto */\n value?: string\n /** Callback ao digitar */\n onChange?: (value: string) => void\n /** Callback ao enviar texto (Enter ou clique no botao send) */\n onSendText?: (text: string) => void\n /** Inicia gravacao (consumer chama MediaRecorder.start) */\n onStartRecording?: () => void\n /** Pausa gravacao */\n onPauseRecording?: () => void\n /** Retoma gravacao */\n onResumeRecording?: () => void\n /** Cancela/deleta gravacao */\n onCancelRecording?: () => void\n /** Envia o audio gravado */\n onSendAudio?: () => void\n /** Toggle play/pause do audio gravado durante o estado `paused` */\n onTogglePlay?: () => void\n /** Duracao da gravacao em segundos (controlado pelo consumer) */\n recordingDuration?: number\n /** Indica se esta tocando o audio (durante state=paused) */\n isPlaying?: boolean\n /**\n * MediaStream do microfone (durante state=recording). Quando fornecido,\n * o componente analisa em tempo real e renderiza as barras conforme a voz.\n * Sem stream → fallback para waveform decorativo.\n *\n * Como usar:\n * ```ts\n * const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\n * const recorder = new MediaRecorder(stream)\n * // Passa o MESMO stream pro MessageBar:\n * <MessageBar state=\"recording\" recordingStream={stream} ... />\n * ```\n */\n recordingStream?: MediaStream | null\n /**\n * Progresso do playback do audio gravado (0-1), usado durante state=paused.\n * Quando fornecido, o waveform mostra barras tocadas/nao tocadas + dot verde\n * sincronizado. Sem isso, o waveform mostra todas as barras como nao tocadas.\n *\n * Alimente via `requestAnimationFrame` enquanto o audio toca, NAO so via\n * `timeupdate` — esse evento dispara ~4x/s (250ms) e o dot pula em stop motion.\n * Ver exemplo na doc do MessageBar.\n */\n playbackProgress?: number\n /**\n * Callback ao clicar/arrastar no waveform durante state=paused.\n * Recebe o novo progresso (0-1); o consumer deve setar currentTime do audio.\n */\n onSeekPlayback?: (progress: number) => void\n /** Placeholder do input */\n placeholder?: string\n /**\n * Limite de duracao da gravacao em segundos. Quando omitido, sem limite\n * (comportamento default). Quando atingido, o componente dispara\n * `onMaxDurationReached` UMA VEZ — o ChatThread usa esse callback pra auto-pausar;\n * consumers diretos do MessageBar podem usar pra customizar.\n */\n maxRecordingDuration?: number\n /**\n * Quantos segundos antes do limite ativar o warning visual\n * (timer em vermelho + label \"Xs restantes\" abaixo). Default: 10.\n * Ignorado se `maxRecordingDuration` nao for passado.\n */\n warnAtSecondsLeft?: number\n /**\n * Callback disparado uma unica vez quando `recordingDuration` >= `maxRecordingDuration`.\n * Reseta automaticamente quando o componente sai do estado `recording`.\n */\n onMaxDurationReached?: () => void\n /**\n * Label do warning inline. Recebe quantos segundos restam, retorna string.\n * Default pt-BR: `(s) => \\`${s}s restantes\\``.\n */\n secondsLeftLabel?: (secondsLeft: number) => string\n}\n\n/* ─── Helpers ─────────────────────────────────────────────────── */\n\nfunction formatDuration(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\n/** Calcula estado de warning do timer durante gravacao.\n * - `isWarning` vira true quando faltam `warnAt` segundos ou menos.\n * - `secondsLeft` = max(0, max - elapsed).\n * Sem `max` ou valores invalidos, isWarning=false e secondsLeft=Infinity. */\nfunction getWarningState(\n elapsed: number,\n max: number | undefined,\n warnAt: number\n): { isWarning: boolean; secondsLeft: number } {\n if (!max || max <= 0) return { isWarning: false, secondsLeft: Infinity }\n const secondsLeft = Math.max(0, max - elapsed)\n return { isWarning: secondsLeft <= warnAt, secondsLeft }\n}\n\nconst DEFAULT_WARN_AT_SECONDS_LEFT = 10\nconst DEFAULT_SECONDS_LEFT_LABEL = (s: number) => `${s}s restantes`\n\n/* ─── Sub-componentes ─────────────────────────────────────────── */\n\n/** Botao mic vermelho coral (theme-brand) — usado em default, audio-only e durante press.\n * - `variant=\"large\"` aumenta para 64x64 (overlay durante press)\n * - `pressed` aplica scale-110 + opacity 90\n * - Aceita pointer events para suportar press-and-hold (mobile WhatsApp-style) */\nfunction MicButton({\n onClick,\n onPointerDown,\n onPointerMove,\n onPointerUp,\n onPointerCancel,\n size = \"default\",\n variant = \"default\",\n pressed = false,\n ariaLabel = \"Gravar audio\",\n className,\n style,\n}: {\n onClick?: () => void\n onPointerDown?: (e: React.PointerEvent<HTMLButtonElement>) => void\n onPointerMove?: (e: React.PointerEvent<HTMLButtonElement>) => void\n onPointerUp?: (e: React.PointerEvent<HTMLButtonElement>) => void\n onPointerCancel?: (e: React.PointerEvent<HTMLButtonElement>) => void\n size?: \"default\" | \"sm\"\n variant?: \"default\" | \"large\"\n pressed?: boolean\n ariaLabel?: string\n className?: string\n style?: React.CSSProperties\n}) {\n const buttonRef = React.useRef<HTMLButtonElement>(null)\n const hasPressFlow = !!onPointerDown\n\n /* touchstart non-passive — marca o gesto como NAO scroll desde o primeiro evento.\n * Critico pra Android Chrome (Blink): se o touchstart for passive (default no React),\n * o navegador classifica o gesto como \"potencialmente scroll\" e ignora preventDefault\n * posterior. Listener via addEventListener pra poder usar { passive: false }. */\n React.useEffect(() => {\n const btn = buttonRef.current\n if (!btn || !hasPressFlow) return\n const onTouchStart = (e: TouchEvent) => {\n e.preventDefault()\n }\n btn.addEventListener(\"touchstart\", onTouchStart, { passive: false })\n return () => btn.removeEventListener(\"touchstart\", onTouchStart)\n }, [hasPressFlow])\n\n const sizeClass =\n variant === \"large\" ? \"size-16\" : size === \"default\" ? \"size-10\" : \"size-8\"\n const radiusClass = variant === \"large\" ? \"rounded-lg\" : \"rounded-md\"\n return (\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={onClick}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerCancel}\n aria-pressed={pressed || undefined}\n className={cn(\n \"shrink-0 inline-flex items-center justify-center bg-primary text-primary-foreground theme-brand transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n sizeClass,\n radiusClass,\n pressed && \"opacity-90 select-none\",\n // touch-none aplicado sempre que ha press flow (nao so quando ja esta pressed),\n // pra Android Chrome nao classificar o touchstart como scroll\n (hasPressFlow || pressed) && \"touch-none\",\n className\n )}\n style={style}\n aria-label={ariaLabel}\n >\n <Mic className={variant === \"large\" ? \"size-6\" : \"size-5\"} />\n </button>\n )\n}\n\n/** Botao send verde (theme-positive) */\nfunction SendButton({\n onClick,\n disabled,\n ariaLabel = \"Enviar\",\n}: {\n onClick?: () => void\n disabled?: boolean\n ariaLabel?: string\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className=\"shrink-0 inline-flex items-center justify-center size-10 rounded-md bg-primary text-primary-foreground theme-positive transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:opacity-50 disabled:cursor-not-allowed\"\n aria-label={ariaLabel}\n >\n <Send className=\"size-5\" fill=\"currentColor\" strokeWidth={0} />\n </button>\n )\n}\n\n/** Botao icon-only neutral (trash, pause, play, mic durante paused) */\nfunction IconButton({\n onClick,\n icon: Icon,\n ariaLabel,\n filled = false,\n}: {\n onClick?: () => void\n icon: React.ComponentType<{ className?: string; fill?: string; strokeWidth?: number }>\n ariaLabel: string\n filled?: boolean\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"shrink-0 inline-flex items-center justify-center size-10 rounded-md text-neutral-foreground hover:bg-muted/50 transition-colors focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50\"\n aria-label={ariaLabel}\n >\n <Icon\n className=\"size-5\"\n {...(filled ? { fill: \"currentColor\", strokeWidth: 0 } : {})}\n />\n </button>\n )\n}\n\n/** Indicador de gravacao (red dot pulsando) */\nfunction RecordingIndicator() {\n return (\n <span className=\"shrink-0 relative inline-flex items-center justify-center size-3\" aria-label=\"Gravando\">\n <span className=\"absolute inset-0 rounded-full bg-primary theme-brand animate-ping opacity-75\" />\n <span className=\"relative size-2.5 rounded-full bg-primary theme-brand\" />\n </span>\n )\n}\n\n/** Helper: agrega N samples brutos em K barras (pico por chunk). Quando samples.length\n * < barCount, \"estica\" via lookup proporcional (cada barra pega o sample mais proximo). */\nfunction aggregateSamples(samples: number[], barCount: number): number[] {\n if (samples.length === 0 || barCount === 0) return new Array(barCount).fill(0)\n const result = new Array(barCount).fill(0)\n // Poucos samples: estica via lookup proporcional\n if (samples.length < barCount) {\n for (let i = 0; i < barCount; i++) {\n const idx = Math.floor((i / barCount) * samples.length)\n result[i] = samples[idx] ?? 0\n }\n return result\n }\n // Muitos samples: agrega pegando o pico (max) do chunk\n const chunkSize = samples.length / barCount\n for (let i = 0; i < barCount; i++) {\n const start = Math.floor(i * chunkSize)\n const end = Math.floor((i + 1) * chunkSize)\n let max = 0\n for (let j = start; j < end && j < samples.length; j++) {\n if (samples[j] > max) max = samples[j]\n }\n result[i] = max\n }\n return result\n}\n\n/** LiveWaveform — analise em tempo real do microfone via AnalyserNode.\n * Comportamento estilo WhatsApp:\n * - **Preenche toda a largura disponivel** (ResizeObserver detecta width e calcula limite max de barras)\n * - Cada bar = 2px wide, 2px gap (4px stride)\n * - **Comeca VAZIO**: primeiras barras entram pela DIREITA e empurram pra esquerda\n * - Depois que enche: rolling window (mais antiga sai esquerda, nova entra direita)\n * - Visualmente: container alinha barras com `justify-end` */\nfunction LiveWaveform({\n stream,\n samplesRef,\n}: {\n stream?: MediaStream | null\n /** Ref opcional: quando fornecido, acumula TODOS os samples brutos da gravacao (para usar no\n * state=paused via RecordedWaveform). O LiveWaveform mantem seu rolling window separado. */\n samplesRef?: React.MutableRefObject<number[]>\n}) {\n const STRIDE = 4 // 2px barra + 2px gap\n const MIN_BARS = 20\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [bars, setBars] = React.useState<number[]>([])\n const barCountRef = React.useRef<number>(MIN_BARS)\n\n /* ResizeObserver: ajusta o LIMITE de barras (nao o buffer atual, exceto se shrink). */\n React.useEffect(() => {\n const el = containerRef.current\n if (!el) return\n\n const updateBarCount = (width: number) => {\n const count = Math.max(MIN_BARS, Math.floor(width / STRIDE))\n barCountRef.current = count\n // Se o buffer atual passou do novo limite (shrink), trunca mantendo as mais recentes\n setBars((prev) => (prev.length > count ? prev.slice(prev.length - count) : prev))\n }\n\n updateBarCount(el.clientWidth)\n\n const ro = new ResizeObserver((entries) => {\n const width = entries[0]?.contentRect.width ?? 0\n updateBarCount(width)\n })\n ro.observe(el)\n\n return () => ro.disconnect()\n }, [])\n\n /* Audio analysis setup (so depende do stream) */\n React.useEffect(() => {\n if (!stream) {\n setBars([])\n return\n }\n\n const AudioContextClass =\n window.AudioContext ||\n (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext\n const audioContext = new AudioContextClass()\n const source = audioContext.createMediaStreamSource(stream)\n const analyser = audioContext.createAnalyser()\n analyser.fftSize = 256\n source.connect(analyser)\n\n const dataArray = new Uint8Array(analyser.frequencyBinCount)\n let rafId: number | null = null\n let lastUpdate = 0\n let cancelled = false\n\n const tick = (timestamp: number) => {\n if (cancelled) return\n // Throttle ~30fps (33ms entre samples)\n if (timestamp - lastUpdate >= 33) {\n lastUpdate = timestamp\n analyser.getByteTimeDomainData(dataArray)\n\n // RMS — amplitude media percebida\n let sum = 0\n for (let i = 0; i < dataArray.length; i++) {\n const v = (dataArray[i] - 128) / 128\n sum += v * v\n }\n const rms = Math.sqrt(sum / dataArray.length)\n\n // Acumula no ref do parent (para reaproveitar no state=paused via RecordedWaveform)\n if (samplesRef) samplesRef.current.push(rms)\n\n setBars((prev) => {\n // Enquanto nao encheu: append no final (vai aparecer na direita por causa do justify-end)\n if (prev.length < barCountRef.current) return [...prev, rms]\n // Cheio: rolling window (descarta mais antiga, adiciona nova)\n return [...prev.slice(1), rms]\n })\n }\n rafId = requestAnimationFrame(tick)\n }\n\n rafId = requestAnimationFrame(tick)\n\n return () => {\n cancelled = true\n if (rafId !== null) cancelAnimationFrame(rafId)\n void audioContext.close()\n }\n }, [stream, samplesRef])\n\n return (\n <div\n ref={containerRef}\n className=\"flex-1 relative h-8 min-w-0 overflow-hidden\"\n >\n <div className=\"absolute inset-y-0 left-0 right-0 flex items-center justify-end gap-[2px]\">\n {bars.map((amp, i) => {\n // Voz humana tende a ter RMS baixo (~0.05-0.3). Boost x4 para visual.\n const normalized = Math.min(amp * 4, 1)\n const height = Math.max(2, Math.round(normalized * 32))\n return (\n <div\n key={i}\n className=\"w-[2px] shrink-0 rounded-full bg-neutral-foreground\"\n style={{ height: `${height}px` }}\n />\n )\n })}\n </div>\n </div>\n )\n}\n\n/** RecordedWaveform — waveform real do audio ja gravado (usado no state=paused).\n * - Recebe samples brutos acumulados durante o recording (via samplesRef)\n * - Agrega em N barras via ResizeObserver (largura dinamica, igual ChatAudio)\n * - Barras tocadas (i / N <= progress) em `bg-neutral-foreground`, demais em `bg-neutral-ring`\n * - Dot verde sincronizado com o progresso\n * - Click/drag-to-seek via pointer events */\nfunction RecordedWaveform({\n samples,\n progress = 0,\n onSeek,\n}: {\n samples: number[]\n progress?: number\n onSeek?: (progress: number) => void\n}) {\n const STRIDE = 4 // 2px barra + 2px gap\n const MIN_BARS = 20\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [barCount, setBarCount] = React.useState<number>(MIN_BARS)\n\n React.useEffect(() => {\n const el = containerRef.current\n if (!el) return\n const updateBarCount = (width: number) => {\n const count = Math.max(MIN_BARS, Math.floor(width / STRIDE))\n setBarCount(count)\n }\n updateBarCount(el.clientWidth)\n const ro = new ResizeObserver((entries) => {\n updateBarCount(entries[0]?.contentRect.width ?? 0)\n })\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n // Agrega samples acumulados em barCount barras + normaliza para 0-1 (relativo ao pico)\n const bars = React.useMemo(() => {\n const aggregated = aggregateSamples(samples, barCount)\n const maxPeak = Math.max(...aggregated, 0.01)\n return aggregated.map((p) => p / maxPeak)\n }, [samples, barCount])\n\n const isDraggingRef = React.useRef(false)\n\n const seekFromClientX = React.useCallback(\n (clientX: number) => {\n const el = containerRef.current\n if (!el || !onSeek) return\n const rect = el.getBoundingClientRect()\n const x = clientX - rect.left\n const newProgress = Math.max(0, Math.min(x / rect.width, 1))\n onSeek(newProgress)\n },\n [onSeek]\n )\n\n const handlePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {\n if (!onSeek) return\n isDraggingRef.current = true\n e.currentTarget.setPointerCapture(e.pointerId)\n seekFromClientX(e.clientX)\n }\n const handlePointerMove = (e: React.PointerEvent<HTMLDivElement>) => {\n if (!isDraggingRef.current) return\n seekFromClientX(e.clientX)\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 // Largura natural do layout das barras (px), usada para posicionar o dot em pixels\n const barsLayoutWidth = bars.length * STRIDE - 2\n\n return (\n <div\n ref={containerRef}\n className={cn(\n \"flex-1 relative h-8 min-w-0 overflow-hidden select-none\",\n onSeek && \"touch-none cursor-pointer\"\n )}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerUp}\n role={onSeek ? \"slider\" : undefined}\n aria-label={onSeek ? \"Posicao do audio\" : undefined}\n aria-valuemin={onSeek ? 0 : undefined}\n aria-valuemax={onSeek ? 100 : undefined}\n aria-valuenow={onSeek ? Math.round(progress * 100) : undefined}\n >\n <div className=\"absolute inset-y-0 left-0 flex items-center gap-[2px]\">\n {bars.map((amp, i) => {\n const isPlayed = i / bars.length <= progress\n const height = Math.max(2, Math.round(amp * 32))\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 {progress > 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] shadow-md pointer-events-none\"\n style={{ left: `${progress * barsLayoutWidth}px` }}\n aria-hidden=\"true\"\n />\n )}\n </div>\n )\n}\n\n/* ─── Press-to-record constants ───────────────────────────────── */\n\nconst LOCK_THRESHOLD = 120 // px arrastados pra cima → trava em modo hands-free\nconst CANCEL_THRESHOLD_RATIO = 0.65 // % da largura do MessageBar\nconst CANCEL_THRESHOLD_MIN = 140 // floor (telas pequenas — iPhone SE)\nconst CANCEL_THRESHOLD_MAX = 220 // ceiling (telas grandes — nao precisa atravessar a tela toda)\nconst MIN_DURATION = 1000 // ms — release antes disso cancela\n\n/** Calcula o threshold de cancel baseado na largura do container do MessageBar.\n * Sempre proporcional, com clamps min/max pra funcionar bem em telas pequenas (iPhone SE)\n * sem ficar trivialmente facil em telas grandes. */\nfunction computeCancelThreshold(containerWidth: number): number {\n return Math.min(\n CANCEL_THRESHOLD_MAX,\n Math.max(CANCEL_THRESHOLD_MIN, containerWidth * CANCEL_THRESHOLD_RATIO)\n )\n}\n\n/** PressedRecordingOverlay — UI exibida enquanto o usuario mantem o mic pressionado.\n * Inspirado no comportamento de gravacao de audio do WhatsApp.\n *\n * - Pill esquerda: live dot + timer + \"Deslize para cancelar\"\n * - Mic vermelho 64×64 (acompanha o cursor via translate clamped)\n * - Pill flutuante ACIMA do mic com lock + chevron up (indica \"deslize pra travar\")\n *\n * Variants:\n * - `default`: layout horizontal (cancel pill esquerda + mic direita)\n * - `audio-only`: layout horizontal centralizado (cancel pill curta + mic + timer pill) */\nfunction PressedRecordingOverlay({\n variant,\n duration,\n deltaX,\n deltaY,\n lockThreshold,\n cancelThreshold,\n isWarning,\n secondsLeft,\n secondsLeftLabel,\n}: {\n variant: \"default\" | \"audio-only\"\n duration: number\n deltaX: number\n deltaY: number\n lockThreshold: number\n cancelThreshold: number\n isWarning: boolean\n secondsLeft: number\n secondsLeftLabel: (s: number) => string\n}) {\n const aboutToLock = deltaY <= -lockThreshold\n const aboutToCancel = deltaX <= -cancelThreshold\n // Clamp: so move pra cima/esquerda (delta negativo); ignora positivos\n const clampedX = Math.min(0, deltaX)\n const clampedY = Math.min(0, deltaY)\n // Restringe a UM EIXO POR VEZ — sem movimento diagonal.\n // Detecta direcao dominante (maior magnitude) e zera o outro eixo.\n const absX = Math.abs(clampedX)\n const absY = Math.abs(clampedY)\n const micTranslateX = absX >= absY ? clampedX : 0\n const micTranslateY = absY > absX ? clampedY : 0\n\n return (\n <div\n data-slot=\"message-bar\"\n data-state=\"recording-pressed\"\n className={cn(\n \"flex items-center gap-2 p-2 sm:gap-3 sm:p-0 touch-none select-none\",\n variant === \"audio-only\" && \"justify-center\"\n )}\n >\n {/* Layout: cancel-pill + mic (default) | cancel-pill + mic + timer-pill (audio-only) */}\n {variant === \"audio-only\" ? (\n <div className=\"relative flex items-center gap-2\">\n {/* Cancel pill (compact) */}\n <div\n className={cn(\n \"flex items-center gap-1 rounded-full bg-muted h-7 px-3 transition-colors\",\n aboutToCancel && \"bg-destructive/10\"\n )}\n >\n <ChevronLeft className=\"size-3 text-muted-foreground\" />\n <span\n className={cn(\n \"text-xs\",\n aboutToCancel ? \"text-destructive font-medium\" : \"text-muted-foreground\"\n )}\n >\n Cancelar\n </span>\n </div>\n\n {/* Mic + lock pill (anchor) */}\n <div className=\"relative\">\n {/* Lock pill ACIMA do mic */}\n <div\n className={cn(\n \"absolute left-1/2 -translate-x-1/2 bottom-full mb-2 flex flex-col items-center gap-3 rounded-t-full bg-muted h-20 w-8 py-3 transition-transform\",\n aboutToLock && \"scale-125 ring-2 ring-[#0EA460]\"\n )}\n aria-hidden=\"true\"\n >\n <Lock className={cn(\"size-3\", aboutToLock ? \"text-[#0EA460]\" : \"text-muted-foreground\")} />\n <ChevronUp className=\"size-3 text-muted-foreground animate-bounce\" />\n </div>\n\n <MicButton\n variant=\"large\"\n pressed\n ariaLabel=\"Gravando — arraste para cima para travar, para esquerda para cancelar\"\n className={cn(aboutToCancel && \"animate-shake\")}\n style={\n aboutToCancel\n ? undefined\n : { transform: `translate(${micTranslateX}px, ${micTranslateY}px) scale(1.1)` }\n }\n />\n </div>\n\n {/* Timer pill — colore o texto e o RecordingIndicator quando proximo do limite */}\n <div className=\"flex flex-col items-center gap-0.5\">\n <div className=\"flex items-center gap-1 rounded-full bg-muted h-7 px-3\">\n <RecordingIndicator />\n <span\n className={cn(\n \"text-sm tabular-nums\",\n isWarning ? \"text-destructive font-medium\" : \"text-neutral-foreground\"\n )}\n >\n {formatDuration(duration)}\n </span>\n </div>\n {isWarning && (\n <span\n className=\"text-xs text-destructive tabular-nums\"\n aria-live=\"polite\"\n >\n {secondsLeftLabel(secondsLeft)}\n </span>\n )}\n </div>\n </div>\n ) : (\n // Variant default: cancel pill flex-1 + mic\n <>\n <div\n className={cn(\n \"flex-1 flex items-center justify-between gap-2 rounded-xl bg-muted px-4 py-3 h-14\",\n aboutToCancel && \"bg-destructive/10\"\n )}\n >\n <div className=\"flex flex-col items-start gap-0.5\">\n <div className=\"flex items-center gap-2\">\n <RecordingIndicator />\n <span\n className={cn(\n \"text-sm tabular-nums\",\n isWarning ? \"text-destructive font-medium\" : \"text-neutral-foreground\"\n )}\n >\n {formatDuration(duration)}\n </span>\n </div>\n {isWarning && (\n <span\n className=\"text-xs text-destructive tabular-nums\"\n aria-live=\"polite\"\n >\n {secondsLeftLabel(secondsLeft)}\n </span>\n )}\n </div>\n <div\n className={cn(\n \"flex items-center gap-1 text-xs\",\n aboutToCancel ? \"text-destructive font-medium\" : \"text-muted-foreground\"\n )}\n >\n <ChevronLeft className=\"size-3\" />\n <span>Deslize para cancelar</span>\n </div>\n </div>\n\n {/* Mic + lock pill */}\n <div className=\"relative\">\n <div\n className={cn(\n \"absolute left-1/2 -translate-x-1/2 bottom-full mb-2 flex flex-col items-center gap-3 rounded-t-full bg-muted h-20 w-8 py-3 transition-transform\",\n aboutToLock && \"scale-125 ring-2 ring-[#0EA460]\"\n )}\n aria-hidden=\"true\"\n >\n <Lock className={cn(\"size-3\", aboutToLock ? \"text-[#0EA460]\" : \"text-muted-foreground\")} />\n <ChevronUp className=\"size-3 text-muted-foreground animate-bounce\" />\n </div>\n\n <MicButton\n variant=\"large\"\n pressed\n ariaLabel=\"Gravando — arraste para cima para travar, para esquerda para cancelar\"\n className={cn(aboutToCancel && \"animate-shake\")}\n style={\n aboutToCancel\n ? undefined\n : { transform: `translate(${micTranslateX}px, ${micTranslateY}px) scale(1.1)` }\n }\n />\n </div>\n </>\n )}\n </div>\n )\n}\n\n/* ─── Component principal ─────────────────────────────────────── */\n\nfunction MessageBar({\n state = \"default\",\n value = \"\",\n onChange,\n onSendText,\n onStartRecording,\n onPauseRecording,\n onResumeRecording,\n onCancelRecording,\n onSendAudio,\n onTogglePlay,\n recordingDuration = 0,\n isPlaying = false,\n recordingStream,\n playbackProgress = 0,\n onSeekPlayback,\n placeholder = \"Digite sua mensagem...\",\n maxRecordingDuration,\n warnAtSecondsLeft = DEFAULT_WARN_AT_SECONDS_LEFT,\n onMaxDurationReached,\n secondsLeftLabel = DEFAULT_SECONDS_LEFT_LABEL,\n className,\n ...props\n}: MessageBarProps) {\n /* ─── Limite de duracao da gravacao ─── */\n const { isWarning: isDurationWarning, secondsLeft: durationSecondsLeft } =\n getWarningState(recordingDuration, maxRecordingDuration, warnAtSecondsLeft)\n\n // Dispara onMaxDurationReached uma unica vez quando o limite e atingido.\n // Reset ao sair do estado recording (proxima gravacao reativa o disparo).\n const maxReachedRef = React.useRef(false)\n React.useEffect(() => {\n if (!maxRecordingDuration || maxReachedRef.current) return\n if (recordingDuration >= maxRecordingDuration) {\n maxReachedRef.current = true\n onMaxDurationReached?.()\n }\n }, [recordingDuration, maxRecordingDuration, onMaxDurationReached])\n React.useEffect(() => {\n if (state !== \"recording\") maxReachedRef.current = false\n }, [state])\n // Acumula samples brutos do microfone durante recording — reaproveitado em paused via RecordedWaveform\n const samplesRef = React.useRef<number[]>([])\n\n // Reset dos samples quando inicia nova gravacao (transicao para recording a partir de outros estados)\n // Nao zera ao voltar de paused → recording (continua a mesma gravacao)\n const prevStateRef = React.useRef<MessageBarState>(state)\n // Lembra o ultimo base state (default/audio-only) pra escolher variant do overlay pressed\n const lastBaseStateRef = React.useRef<\"default\" | \"audio-only\">(\n state === \"audio-only\" ? \"audio-only\" : \"default\"\n )\n // De onde veio o `requesting` (paused = retomando gravacao existente; senao = gravacao nova)\n const requestingFromRef = React.useRef<MessageBarState | null>(null)\n React.useEffect(() => {\n const prev = prevStateRef.current\n if (state === \"requesting\" && prev !== \"requesting\") {\n requestingFromRef.current = prev\n }\n if (state === \"recording\" && prev !== \"paused\" && prev !== \"recording\") {\n // requesting → recording so zera se a gravacao e nova (nao veio de paused)\n const resuming = prev === \"requesting\" && requestingFromRef.current === \"paused\"\n if (!resuming) samplesRef.current = []\n }\n if (state === \"default\" || state === \"audio-only\") {\n lastBaseStateRef.current = state\n }\n prevStateRef.current = state\n }, [state])\n\n /* ─── Press-to-record (WhatsApp-like, touch-only) ───────────── */\n const [isPressed, setIsPressed] = React.useState(false)\n const [pressDelta, setPressDelta] = React.useState({ x: 0, y: 0 })\n // Cancel threshold dinamico — calculado baseado na largura do container no pointerdown\n const [pressCancelThreshold, setPressCancelThreshold] = React.useState(CANCEL_THRESHOLD_MAX)\n const pressDeltaRef = React.useRef({ x: 0, y: 0 })\n const pressCancelThresholdRef = React.useRef(CANCEL_THRESHOLD_MAX)\n const pressInfoRef = React.useRef({ startX: 0, startY: 0, startTime: 0, locked: false })\n // Ref sincronizada com isPressed — usada por listener permanente de touchmove\n // (instalado no mount, mas so preventDefault quando esta pressionado)\n const isPressedRef = React.useRef(false)\n // Refs pros callbacks — handlers do window leem a versao mais recente sem precisar reinstalar listeners\n const callbacksRef = React.useRef({ onStartRecording, onCancelRecording, onSendAudio })\n React.useEffect(() => {\n callbacksRef.current = { onStartRecording, onCancelRecording, onSendAudio }\n })\n\n const updateDelta = (dx: number, dy: number) => {\n pressDeltaRef.current = { x: dx, y: dy }\n setPressDelta({ x: dx, y: dy })\n }\n\n // Pedido de microfone falhou durante press-to-record (touch): requesting → base state.\n // Solta o press pra que o release nao dispare onSendAudio/onCancelRecording de uma\n // gravacao que nunca existiu.\n const prevStateForPressRef = React.useRef<MessageBarState>(state)\n React.useEffect(() => {\n const prev = prevStateForPressRef.current\n prevStateForPressRef.current = state\n const backToBase = state === \"default\" || state === \"audio-only\" || state === \"disabled\"\n if (prev === \"requesting\" && backToBase && isPressedRef.current) {\n setIsPressed(false)\n updateDelta(0, 0)\n }\n }, [state])\n\n /* Listener PERMANENTE de touchmove com preventDefault — instalado no mount.\n * Por que permanente: se instalar so quando isPressed=true (dentro do useEffect transient),\n * o iOS ja decidiu que o gesto e scroll/swipe ANTES do listener montar.\n * Solucao: listener sempre presente, com guard via isPressedRef.current. */\n React.useEffect(() => {\n const onTouchMove = (e: TouchEvent) => {\n if (isPressedRef.current) {\n e.preventDefault()\n }\n }\n window.addEventListener(\"touchmove\", onTouchMove, { passive: false })\n return () => window.removeEventListener(\"touchmove\", onTouchMove)\n }, [])\n\n const triggerHaptic = (ms = 50) => {\n if (typeof navigator !== \"undefined\" && typeof navigator.vibrate === \"function\") {\n try {\n navigator.vibrate(ms)\n } catch {\n /* ignore — alguns browsers podem rejeitar fora de user gesture */\n }\n }\n }\n\n // Listeners no window (sobrevivem a troca de DOM quando o overlay aparece e o MicButton original desmonta)\n // Sincroniza isPressedRef com isPressed pra o listener permanente de touchmove ler\n React.useEffect(() => {\n isPressedRef.current = isPressed\n }, [isPressed])\n\n // Listeners de pointer (transient): so existem durante o press flow\n React.useEffect(() => {\n if (!isPressed) return\n\n // touch-action: none em html + body bloqueia gestos nativos (backup, ja temos o\n // touchmove permanente com preventDefault)\n const prevHtmlTouchAction = document.documentElement.style.touchAction\n const prevBodyTouchAction = document.body.style.touchAction\n document.documentElement.style.touchAction = \"none\"\n document.body.style.touchAction = \"none\"\n\n const onMove = (e: PointerEvent) => {\n if (pressInfoRef.current.locked) return\n const { startX, startY } = pressInfoRef.current\n const dx = e.clientX - startX\n const dy = e.clientY - startY\n updateDelta(dx, dy)\n // Atinge lock threshold → trava em modo hands-free\n if (dy <= -LOCK_THRESHOLD) {\n pressInfoRef.current.locked = true\n triggerHaptic(50)\n setIsPressed(false)\n updateDelta(0, 0)\n }\n }\n\n const onEnd = () => {\n if (pressInfoRef.current.locked) return\n const { startTime } = pressInfoRef.current\n const elapsed = Date.now() - startTime\n const { x: dx } = pressDeltaRef.current\n\n setIsPressed(false)\n updateDelta(0, 0)\n\n if (dx <= -pressCancelThresholdRef.current || elapsed < MIN_DURATION) {\n triggerHaptic(50)\n callbacksRef.current.onCancelRecording?.()\n return\n }\n callbacksRef.current.onSendAudio?.()\n }\n\n window.addEventListener(\"pointermove\", onMove)\n window.addEventListener(\"pointerup\", onEnd)\n // pointercancel tratado como release (usa delta atual pra decidir cancel/send)\n window.addEventListener(\"pointercancel\", onEnd)\n return () => {\n window.removeEventListener(\"pointermove\", onMove)\n window.removeEventListener(\"pointerup\", onEnd)\n window.removeEventListener(\"pointercancel\", onEnd)\n document.documentElement.style.touchAction = prevHtmlTouchAction\n document.body.style.touchAction = prevBodyTouchAction\n }\n }, [isPressed])\n\n const handlePressStart = (e: React.PointerEvent<HTMLButtonElement>) => {\n // Desktop (mouse/pen): comportamento antigo de click — dispara onStartRecording, sai do flow press\n if (e.pointerType !== \"touch\") {\n onStartRecording?.()\n return\n }\n // Mede a largura do container do MessageBar pra calcular o cancel threshold adaptativo\n const wrapper = e.currentTarget.closest('[data-slot=\"message-bar\"]')\n const containerWidth =\n wrapper instanceof HTMLElement ? wrapper.clientWidth : window.innerWidth\n const threshold = computeCancelThreshold(containerWidth)\n pressCancelThresholdRef.current = threshold\n setPressCancelThreshold(threshold)\n\n pressInfoRef.current = {\n startX: e.clientX,\n startY: e.clientY,\n startTime: Date.now(),\n locked: false,\n }\n updateDelta(0, 0)\n setIsPressed(true)\n onStartRecording?.()\n }\n\n const pressHandlers = {\n onPointerDown: handlePressStart,\n }\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey && value.trim()) {\n e.preventDefault()\n onSendText?.(value)\n }\n }\n\n /* ─── State: requesting (aguardando permissao/stream do microfone) ── */\n if (state === \"requesting\") {\n const busyMic = (\n <button\n type=\"button\"\n disabled\n aria-busy=\"true\"\n aria-label=\"Pedindo acesso ao microfone\"\n className=\"shrink-0 inline-flex items-center justify-center size-10 rounded-md bg-primary text-primary-foreground theme-brand opacity-70 cursor-wait\"\n >\n <Spinner size=\"sm\" />\n </button>\n )\n if (lastBaseStateRef.current === \"audio-only\") {\n return (\n <div\n data-slot=\"message-bar\"\n data-state=\"requesting\"\n className={cn(\"flex items-center justify-center p-2\", className)}\n {...props}\n >\n {busyMic}\n </div>\n )\n }\n return (\n <div\n data-slot=\"message-bar\"\n data-state=\"requesting\"\n className={cn(\"flex items-center gap-2 rounded-2xl bg-muted p-2\", className)}\n {...props}\n >\n <input\n type=\"text\"\n value={value}\n readOnly\n disabled\n placeholder={placeholder}\n className=\"flex-1 min-w-0 bg-transparent px-3 py-2 text-base text-neutral-foreground placeholder:text-neutral-muted-foreground outline-none disabled:cursor-wait disabled:opacity-50\"\n aria-label=\"Mensagem\"\n />\n {busyMic}\n </div>\n )\n }\n\n /* ─── State: audio-only (so botao mic centralizado) ─────────── */\n if (state === \"audio-only\") {\n return (\n <div\n data-slot=\"message-bar\"\n data-state=\"audio-only\"\n className={cn(\"flex items-center justify-center p-2\", className)}\n {...props}\n >\n <MicButton {...pressHandlers} />\n </div>\n )\n }\n\n /* ─── State: recording + pressed (overlay WhatsApp-like) ────── */\n if (state === \"recording\" && isPressed) {\n return (\n <PressedRecordingOverlay\n variant={lastBaseStateRef.current}\n duration={recordingDuration}\n deltaX={pressDelta.x}\n deltaY={pressDelta.y}\n lockThreshold={LOCK_THRESHOLD}\n cancelThreshold={pressCancelThreshold}\n isWarning={isDurationWarning}\n secondsLeft={durationSecondsLeft}\n secondsLeftLabel={secondsLeftLabel}\n />\n )\n }\n\n /* ─── State: recording (hands-free, apos lock ou click no mic) ─ */\n if (state === \"recording\") {\n return (\n <div\n data-slot=\"message-bar\"\n data-state=\"recording\"\n className={cn(\n \"flex items-center gap-2 p-2 sm:gap-3 sm:p-0\",\n className\n )}\n {...props}\n >\n {/* Mobile: 2-row layout */}\n <div className=\"flex flex-col gap-2 w-full sm:contents\">\n {/* Top: trash + recording info + waveform (mobile) / inline (desktop) */}\n <div className=\"flex items-center gap-2 sm:contents\">\n {/* Trash — only inline at desktop, mobile shows in bottom row */}\n <div className=\"hidden sm:contents\">\n <IconButton\n onClick={onCancelRecording}\n icon={Trash2}\n ariaLabel=\"Cancelar gravacao\"\n />\n </div>\n\n {/* Recording bar — bg-muted container.\n Quando proximo do limite, o timer vira destructive + label \"Xs restantes\"\n aparece abaixo (aria-live polite anuncia pra screen readers). */}\n <div className=\"flex items-center gap-3 flex-1 rounded-2xl bg-muted px-4 py-3\">\n <RecordingIndicator />\n <div className=\"flex flex-col items-start gap-0.5 shrink-0\">\n <span\n className={cn(\n \"text-sm tabular-nums\",\n isDurationWarning ? \"text-destructive font-medium\" : \"text-neutral-foreground\"\n )}\n >\n {formatDuration(recordingDuration)}\n </span>\n {isDurationWarning && (\n <span\n className=\"text-xs text-destructive tabular-nums whitespace-nowrap\"\n aria-live=\"polite\"\n >\n {secondsLeftLabel(durationSecondsLeft)}\n </span>\n )}\n </div>\n <LiveWaveform stream={recordingStream} samplesRef={samplesRef} />\n {/* Pause inside the bar — only desktop */}\n <div className=\"hidden sm:contents\">\n <button\n type=\"button\"\n onClick={onPauseRecording}\n className=\"shrink-0 inline-flex items-center justify-center text-neutral-foreground hover:opacity-80 transition-opacity\"\n aria-label=\"Pausar gravacao\"\n >\n <Pause className=\"size-5\" fill=\"currentColor\" strokeWidth={0} />\n </button>\n </div>\n </div>\n\n {/* Send — only inline at desktop */}\n <div className=\"hidden sm:contents\">\n <SendButton onClick={onSendAudio} ariaLabel=\"Enviar audio\" />\n </div>\n </div>\n\n {/* Bottom row — mobile only */}\n <div className=\"flex items-center justify-between sm:hidden\">\n <IconButton\n onClick={onCancelRecording}\n icon={Trash2}\n ariaLabel=\"Cancelar gravacao\"\n />\n <button\n type=\"button\"\n onClick={onPauseRecording}\n className=\"shrink-0 inline-flex items-center justify-center size-10 text-neutral-foreground hover:opacity-80 transition-opacity\"\n aria-label=\"Pausar gravacao\"\n >\n <Pause className=\"size-5\" fill=\"currentColor\" strokeWidth={0} />\n </button>\n <SendButton onClick={onSendAudio} ariaLabel=\"Enviar audio\" />\n </div>\n </div>\n </div>\n )\n }\n\n /* ─── State: paused (com audio gravado) ──────────────────────── */\n if (state === \"paused\") {\n return (\n <div\n data-slot=\"message-bar\"\n data-state=\"paused\"\n className={cn(\n \"flex items-center gap-2 p-2 sm:gap-3 sm:p-0\",\n className\n )}\n {...props}\n >\n <div className=\"flex flex-col gap-2 w-full sm:contents\">\n <div className=\"flex items-center gap-2 sm:contents\">\n <div className=\"hidden sm:contents\">\n <IconButton\n onClick={onCancelRecording}\n icon={Trash2}\n ariaLabel=\"Cancelar gravacao\"\n />\n </div>\n\n <div className=\"flex items-center gap-3 flex-1 rounded-2xl bg-muted px-4 py-3\">\n <button\n type=\"button\"\n onClick={onTogglePlay}\n className=\"shrink-0 inline-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-5\" fill=\"currentColor\" strokeWidth={0} />\n ) : (\n <Play className=\"size-5\" fill=\"currentColor\" strokeWidth={0} />\n )}\n </button>\n <span className=\"shrink-0 text-sm text-neutral-foreground tabular-nums\">\n {formatDuration(recordingDuration)}\n </span>\n <RecordedWaveform\n samples={samplesRef.current}\n progress={playbackProgress}\n onSeek={onSeekPlayback}\n />\n {/* Mic inline — only desktop */}\n <div className=\"hidden sm:contents\">\n <button\n type=\"button\"\n onClick={onResumeRecording}\n className=\"shrink-0 inline-flex items-center justify-center text-neutral-foreground hover:opacity-80 transition-opacity\"\n aria-label=\"Continuar gravacao\"\n >\n <Mic className=\"size-5\" />\n </button>\n </div>\n </div>\n\n <div className=\"hidden sm:contents\">\n <SendButton onClick={onSendAudio} ariaLabel=\"Enviar audio\" />\n </div>\n </div>\n\n {/* Bottom row — mobile only */}\n <div className=\"flex items-center justify-between sm:hidden\">\n <IconButton\n onClick={onCancelRecording}\n icon={Trash2}\n ariaLabel=\"Cancelar gravacao\"\n />\n <button\n type=\"button\"\n onClick={onResumeRecording}\n className=\"shrink-0 inline-flex items-center justify-center size-10 text-neutral-foreground hover:opacity-80 transition-opacity\"\n aria-label=\"Continuar gravacao\"\n >\n <Mic className=\"size-5\" />\n </button>\n <SendButton onClick={onSendAudio} ariaLabel=\"Enviar audio\" />\n </div>\n </div>\n </div>\n )\n }\n\n /* ─── States: default | active | disabled (input + button) ──── */\n const isDisabled = state === \"disabled\"\n const isActive = state === \"active\" || (state === \"default\" && value.trim().length > 0)\n\n return (\n <div\n data-slot=\"message-bar\"\n data-state={state}\n className={cn(\n \"flex items-center gap-2 rounded-2xl bg-muted p-2\",\n className\n )}\n {...props}\n >\n <input\n type=\"text\"\n value={value}\n onChange={(e) => onChange?.(e.target.value)}\n onKeyDown={handleKeyDown}\n disabled={isDisabled}\n placeholder={placeholder}\n className={cn(\n \"flex-1 min-w-0 bg-transparent px-3 py-2 text-base text-neutral-foreground placeholder:text-neutral-muted-foreground outline-none\",\n \"disabled:cursor-not-allowed disabled:opacity-50\"\n )}\n aria-label=\"Mensagem\"\n />\n {isActive ? (\n <SendButton\n onClick={() => value.trim() && onSendText?.(value)}\n disabled={!value.trim()}\n />\n ) : (\n <MicButton {...pressHandlers} />\n )}\n </div>\n )\n}\n\nexport { MessageBar }\n"]}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from './chunk-2KFWBA76.js';
|
|
2
2
|
import { ChatMessage } from './chunk-KSEDDFGG.js';
|
|
3
3
|
import { DEFAULT_MESSAGE_RATING_LABELS, MessageRating } from './chunk-YIOXPV2N.js';
|
|
4
|
-
import { MessageBar } from './chunk-
|
|
4
|
+
import { MessageBar } from './chunk-5AH2PNYW.js';
|
|
5
5
|
import { cn } from './chunk-W4KJCBMW.js';
|
|
6
6
|
import { __objRest, __spreadValues, __spreadProps } from './chunk-2NMEKWO5.js';
|
|
7
7
|
import * as React from 'react';
|
|
8
8
|
import { Square } from 'lucide-react';
|
|
9
9
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
10
10
|
|
|
11
|
+
function isThenable(value) {
|
|
12
|
+
return !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
13
|
+
}
|
|
11
14
|
var SCROLL_BOTTOM_THRESHOLD = 100;
|
|
12
15
|
var LOAD_MORE_THRESHOLD = 200;
|
|
13
16
|
function ChatThreadBanner({
|
|
@@ -112,6 +115,7 @@ function ChatThread(_a) {
|
|
|
112
115
|
onStartRecording,
|
|
113
116
|
onPauseRecording,
|
|
114
117
|
onResumeRecording,
|
|
118
|
+
onRecordingError,
|
|
115
119
|
onCancelRecording,
|
|
116
120
|
onTogglePlay,
|
|
117
121
|
onSeekPlayback,
|
|
@@ -155,6 +159,7 @@ function ChatThread(_a) {
|
|
|
155
159
|
"onStartRecording",
|
|
156
160
|
"onPauseRecording",
|
|
157
161
|
"onResumeRecording",
|
|
162
|
+
"onRecordingError",
|
|
158
163
|
"onCancelRecording",
|
|
159
164
|
"onTogglePlay",
|
|
160
165
|
"onSeekPlayback",
|
|
@@ -271,27 +276,59 @@ function ChatThread(_a) {
|
|
|
271
276
|
);
|
|
272
277
|
const baseMessageBarState = audioOnlyMode ? "audio-only" : "default";
|
|
273
278
|
const [internalRecordingState, setInternalRecordingState] = React.useState("idle");
|
|
279
|
+
const recordingRequestTokenRef = React.useRef(0);
|
|
280
|
+
const isMountedRef = React.useRef(false);
|
|
281
|
+
React.useEffect(() => {
|
|
282
|
+
isMountedRef.current = true;
|
|
283
|
+
return () => {
|
|
284
|
+
isMountedRef.current = false;
|
|
285
|
+
recordingRequestTokenRef.current++;
|
|
286
|
+
};
|
|
287
|
+
}, []);
|
|
274
288
|
React.useEffect(() => {
|
|
275
289
|
if (internalRecordingState === "idle") return;
|
|
276
290
|
}, [audioOnlyMode, internalRecordingState]);
|
|
277
|
-
const messageBarState = isDisabled ? "disabled" : internalRecordingState === "recording" ? "recording" : internalRecordingState === "paused" ? "paused" : baseMessageBarState;
|
|
291
|
+
const messageBarState = isDisabled ? "disabled" : internalRecordingState === "recording" ? "recording" : internalRecordingState === "paused" ? "paused" : internalRecordingState === "requesting" ? "requesting" : baseMessageBarState;
|
|
292
|
+
const runRecordingRequest = React.useCallback(
|
|
293
|
+
(callback, fallbackState) => {
|
|
294
|
+
const result = callback == null ? void 0 : callback();
|
|
295
|
+
if (!isThenable(result)) {
|
|
296
|
+
setInternalRecordingState("recording");
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const token = ++recordingRequestTokenRef.current;
|
|
300
|
+
setInternalRecordingState("requesting");
|
|
301
|
+
result.then(
|
|
302
|
+
() => {
|
|
303
|
+
if (!isMountedRef.current || recordingRequestTokenRef.current !== token) return;
|
|
304
|
+
setInternalRecordingState("recording");
|
|
305
|
+
},
|
|
306
|
+
(error) => {
|
|
307
|
+
if (!isMountedRef.current || recordingRequestTokenRef.current !== token) return;
|
|
308
|
+
setInternalRecordingState(fallbackState);
|
|
309
|
+
onRecordingError == null ? void 0 : onRecordingError(error);
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
},
|
|
313
|
+
[onRecordingError]
|
|
314
|
+
);
|
|
278
315
|
const handleStartRecordingInternal = React.useCallback(() => {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}, [onStartRecording]);
|
|
316
|
+
runRecordingRequest(onStartRecording, "idle");
|
|
317
|
+
}, [onStartRecording, runRecordingRequest]);
|
|
282
318
|
const handlePauseRecordingInternal = React.useCallback(() => {
|
|
283
319
|
setInternalRecordingState("paused");
|
|
284
320
|
onPauseRecording == null ? void 0 : onPauseRecording();
|
|
285
321
|
}, [onPauseRecording]);
|
|
286
322
|
const handleResumeRecordingInternal = React.useCallback(() => {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}, [onResumeRecording]);
|
|
323
|
+
runRecordingRequest(onResumeRecording, "paused");
|
|
324
|
+
}, [onResumeRecording, runRecordingRequest]);
|
|
290
325
|
const handleCancelRecordingInternal = React.useCallback(() => {
|
|
326
|
+
recordingRequestTokenRef.current++;
|
|
291
327
|
setInternalRecordingState("idle");
|
|
292
328
|
onCancelRecording == null ? void 0 : onCancelRecording();
|
|
293
329
|
}, [onCancelRecording]);
|
|
294
330
|
const handleSendAudioInternal = React.useCallback(() => {
|
|
331
|
+
recordingRequestTokenRef.current++;
|
|
295
332
|
setInternalRecordingState("idle");
|
|
296
333
|
onSendAudio == null ? void 0 : onSendAudio();
|
|
297
334
|
}, [onSendAudio]);
|
|
@@ -449,5 +486,5 @@ function ChatThread(_a) {
|
|
|
449
486
|
}
|
|
450
487
|
|
|
451
488
|
export { ChatThread };
|
|
452
|
-
//# sourceMappingURL=chunk-
|
|
453
|
-
//# sourceMappingURL=chunk-
|
|
489
|
+
//# sourceMappingURL=chunk-MQX4NBKQ.js.map
|
|
490
|
+
//# sourceMappingURL=chunk-MQX4NBKQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/composites/chat-thread.tsx"],"names":[],"mappings":";;;;;;;;;;AA8BA,SAAS,WAAW,KAAA,EAA+C;AACjE,EAAA,OACE,CAAC,CAAC,KAAA,KACD,OAAO,KAAA,KAAU,QAAA,IAAY,OAAO,KAAA,KAAU,UAAA,CAAA,IAC/C,OAAQ,KAAA,CAA6B,IAAA,KAAS,UAAA;AAElD;AAiLA,IAAM,uBAAA,GAA0B,GAAA;AAChC,IAAM,mBAAA,GAAsB,GAAA;AAK5B,SAAS,gBAAA,CAAiB;AAAA,EACxB,OAAA;AAAA,EACA;AACF,CAAA,EAGG;AACD,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,QAAA;AAAA,MACL,WAAA,EAAU,QAAA;AAAA,MACV,SAAA,EAAW,EAAA;AAAA,QACT,6DAAA;AAAA,QACA,YAAY,SAAA,IACV,0DAAA;AAAA,QACF,YAAY,YAAA,IACV;AAAA,OACJ;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ;AAGA,SAAS,aAAa,KAAA,EAA0C;AAC9D,EAAA,MAAM,CAAC,KAAK,MAAM,CAAA,GAAU,eAAS,MAAM,IAAA,CAAK,KAAK,CAAA;AAErD,EAAM,gBAAU,MAAM;AACpB,IAAA,IAAI,CAAC,KAAA,IAAS,KAAA,IAAS,IAAA,CAAK,KAAI,EAAG;AACnC,IAAA,MAAM,EAAA,GAAK,YAAY,MAAM,MAAA,CAAO,KAAK,GAAA,EAAK,GAAG,GAAI,CAAA;AACrD,IAAA,OAAO,MAAM,cAAc,EAAE,CAAA;AAAA,EAC/B,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,IAAI,CAAC,OAAO,OAAO,CAAA;AACnB,EAAA,MAAM,cAAc,KAAA,GAAQ,GAAA;AAC5B,EAAA,OAAO,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,IAAA,CAAK,WAAA,GAAc,GAAI,CAAC,CAAA;AAClD;AAGA,SAAS,kBAAA,CAAmB;AAAA,EAC1B,OAAA;AAAA,EACA,KAAA,GAAQ;AACV,CAAA,EAGG;AACD,EAAA,uBACE,IAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,QAAA;AAAA,MACL,OAAA;AAAA,MACA,SAAA,EAAU,oNAAA;AAAA,MACV,YAAA,EAAY,KAAA;AAAA,MAEZ,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,UAAO,SAAA,EAAU,QAAA,EAAS,IAAA,EAAK,cAAA,EAAe,aAAa,CAAA,EAAG,CAAA;AAAA,wBAC/D,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,qBAAA,EAAuB,QAAA,EAAA,KAAA,EAAM;AAAA;AAAA;AAAA,GAC/C;AAEJ;AAGA,SAAS,qBAAA,CAAsB;AAAA,EAC7B,QAAA;AAAA,EACA;AACF,CAAA,EAGG;AACD,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA;AAAA,QACT,sDAAA;AAAA,QACA,kBAAkB,0BAAA,GAA6B;AAAA,OACjD;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ;AAKA,IAAM,iBAAA,GAAoB,GAAA;AAQ1B,SAAS,UAAA,CAAW;AAAA,EAClB,IAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF,CAAA,EAIG;AACD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAU,eAAyB,IAAI,CAAA;AACnE,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAU,eAA4B,OAAO,CAAA;AAEnE,EAAM,gBAAU,MAAM;AACpB,IAAA,IAAI,aAAa,IAAA,EAAM;AACvB,IAAA,QAAA,CAAS,OAAO,CAAA;AAChB,IAAA,MAAM,CAAA,GAAI,WAAW,MAAM;AACzB,MAAA,WAAA,CAAY,IAAI,CAAA;AAChB,MAAA,QAAA,CAAS,OAAO,CAAA;AAAA,IAClB,GAAG,iBAAiB,CAAA;AACpB,IAAA,OAAO,MAAM,aAAa,CAAC,CAAA;AAAA,EAC7B,CAAA,EAAG,CAAC,IAAA,EAAM,QAAQ,CAAC,CAAA;AAEnB,EAAA,uBACE,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAA,EACZ,uBAAa,QAAA,oBACZ,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MAEC,SAAA,EACE,KAAA,KAAU,OAAA,GAAU,0BAAA,GAA6B,0BAAA;AAAA,MAGlD,QAAA,EAAA,QAAA,KAAa,eAAe,UAAA,GAAa;AAAA,KAAA;AAAA,IALrC;AAAA,GAMP,EAEJ,CAAA;AAEJ;AAIA,SAAS,WAAW,EAAA,EA6CA;AA7CA,EAAA,IAAA,EAAA,GAAA,EAAA,EAClB;AAAA,IAAA,QAAA;AAAA,IACA,KAAA,GAAQ,MAAA;AAAA,IACR,eAAA,GAAkB,KAAA;AAAA,IAClB,UAAA;AAAA,IACA,WAAA;AAAA,IACA,cAAA;AAAA,IACA,oBAAA;AAAA,IACA,cAAA;AAAA,IACA,UAAA;AAAA,IACA,gBAAA;AAAA,IACA,gBAAA;AAAA,IACA,iBAAA;AAAA,IACA,gBAAA;AAAA,IACA,iBAAA;AAAA,IACA,YAAA;AAAA,IACA,cAAA;AAAA,IACA,eAAA;AAAA,IACA,iBAAA;AAAA,IACA,SAAA;AAAA,IACA,gBAAA;AAAA,IACA,OAAA,GAAU,KAAA;AAAA,IACV,gBAAA,GAAmB,IAAA;AAAA,IACnB,mBAAA;AAAA,IACA,cAAA,GAAiB,KAAA;AAAA,IACjB,oBAAA;AAAA,IACA,MAAA;AAAA,IACA,YAAA;AAAA,IACA,WAAA;AAAA,IACA,SAAA;AAAA,IACA,aAAA,GAAgB,KAAA;AAAA,IAChB,oBAAA;AAAA,IACA,iBAAA;AAAA,IACA,gBAAA;AAAA,IACA,oBAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAA;AAAA,IACA,gBAAA;AAAA,IACA,iBAAA,GAAoB,qCAAA;AAAA,IACpB,eAAA,GAAkB,kBAAA;AAAA,IAClB,oBAAA,GAAuB,oBAAA;AAAA,IACvB,gBAAA,GAAmB,gBAAA;AAAA,IACnB,kBAAA,GAAqB,mCAAA;AAAA,IACrB;AAAA,GAzYF,GA8VoB,EAAA,EA4Cf,KAAA,GAAA,SAAA,CA5Ce,EAAA,EA4Cf;AAAA,IA3CH,UAAA;AAAA,IACA,OAAA;AAAA,IACA,iBAAA;AAAA,IACA,YAAA;AAAA,IACA,aAAA;AAAA,IACA,gBAAA;AAAA,IACA,sBAAA;AAAA,IACA,gBAAA;AAAA,IACA,YAAA;AAAA,IACA,kBAAA;AAAA,IACA,kBAAA;AAAA,IACA,mBAAA;AAAA,IACA,kBAAA;AAAA,IACA,mBAAA;AAAA,IACA,cAAA;AAAA,IACA,gBAAA;AAAA,IACA,iBAAA;AAAA,IACA,mBAAA;AAAA,IACA,WAAA;AAAA,IACA,kBAAA;AAAA,IACA,SAAA;AAAA,IACA,kBAAA;AAAA,IACA,qBAAA;AAAA,IACA,gBAAA;AAAA,IACA,sBAAA;AAAA,IACA,QAAA;AAAA,IACA,cAAA;AAAA,IACA,aAAA;AAAA,IACA,WAAA;AAAA,IACA,eAAA;AAAA,IACA,sBAAA;AAAA,IACA,mBAAA;AAAA,IACA,kBAAA;AAAA,IACA,sBAAA;AAAA,IACA,YAAA;AAAA,IACA,UAAA;AAAA,IACA,kBAAA;AAAA,IACA,mBAAA;AAAA,IACA,iBAAA;AAAA,IACA,sBAAA;AAAA,IACA,kBAAA;AAAA,IACA,oBAAA;AAAA,IACA;AAAA,GAAA,CAAA;AAIA,EAAA,MAAM,oBAAA,GAAuB,aAAa,gBAAgB,CAAA;AAC1D,EAAA,MAAM,gBAAgB,oBAAA,GAAuB,CAAA;AAC7C,EAAA,MAAM,UAAA,GACJ,KAAA,KAAU,UAAA,IAAe,eAAA,IAAmB,SAAS,MAAA,KAAW,CAAA;AAClE,EAAA,MAAM,UAAA,GAAa,WAAW,aAAA,IAAiB,cAAA;AAG/C,EAAA,MAAM,SAAA,GAAkB,aAAuB,IAAI,CAAA;AACnD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAU,eAAS,IAAI,CAAA;AACvD,EAAA,MAAM,mBAAA,GAA4B,KAAA,CAAA,MAAA,CAAO,QAAA,CAAS,MAAM,CAAA;AACxD,EAAA,MAAM,mBAAA,GAA4B,aAAO,KAAK,CAAA;AAC9C,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAU,eAAS,KAAK,CAAA;AAE9D,EAAA,MAAM,UAAA,GAAmB,aAAO,KAAK,CAAA;AACrC,EAAM,gBAAU,MAAM;AACpB,IAAA,UAAA,CAAW,OAAA,GAAU,IAAA;AAAA,EACvB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,mBAAA,GAA4B,kBAAY,MAAM;AAClD,IAAA,MAAM,KAAK,SAAA,CAAU,OAAA;AACrB,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,MAAM,kBAAA,GAAqB,EAAA,CAAG,YAAA,GAAe,EAAA,CAAG,YAAY,EAAA,CAAG,YAAA;AAC/D,IAAA,MAAM,WAAW,kBAAA,IAAsB,uBAAA;AACvC,IAAA,aAAA,CAAc,QAAQ,CAAA;AAAA,EACxB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,cAAA,GAAuB,KAAA,CAAA,WAAA,CAAY,CAAC,MAAA,GAAS,IAAA,KAAS;AAC1D,IAAA,MAAM,KAAK,SAAA,CAAU,OAAA;AACrB,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,EAAA,CAAG,QAAA,CAAS;AAAA,MACV,KAAK,EAAA,CAAG,YAAA;AAAA,MACR,QAAA,EAAU,SAAS,QAAA,GAAW;AAAA,KAC/B,CAAA;AAAA,EACH,CAAA,EAAG,EAAE,CAAA;AAGL,EAAM,gBAAU,MAAM;AACpB,IAAA,MAAM,OAAO,mBAAA,CAAoB,OAAA;AACjC,IAAA,MAAM,OAAO,QAAA,CAAS,MAAA;AACtB,IAAA,mBAAA,CAAoB,OAAA,GAAU,IAAA;AAE9B,IAAA,IAAI,QAAQ,IAAA,EAAM;AAIlB,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,qBAAA,CAAsB,MAAM,cAAA,CAAe,IAAI,CAAC,CAAA;AAAA,IAClD;AAAA,EAEF,CAAA,EAAG,CAAC,QAAA,CAAS,MAAM,CAAC,CAAA;AAGpB,EAAM,gBAAU,MAAM;AACpB,IAAA,MAAM,KAAK,SAAA,CAAU,OAAA;AACrB,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,MAAM,UAAU,MAAM;AACpB,MAAA,mBAAA,EAAoB;AAEpB,MAAA,IACE,cACA,CAAC,mBAAA,CAAoB,OAAA,IACrB,EAAA,CAAG,aAAa,mBAAA,EAChB;AACA,QAAA,mBAAA,CAAoB,OAAA,GAAU,IAAA;AAC9B,QAAA,gBAAA,CAAiB,IAAI,CAAA;AACrB,QAAA,MAAM,mBAAmB,EAAA,CAAG,YAAA;AAC5B,QAAA,OAAA,CAAQ,OAAA,CAAQ,UAAA,EAAY,CAAA,CACzB,KAAK,MAAM;AAEV,UAAA,qBAAA,CAAsB,MAAM;AAC1B,YAAA,MAAM,kBAAkB,EAAA,CAAG,YAAA;AAC3B,YAAA,EAAA,CAAG,YAAY,eAAA,GAAkB,gBAAA;AAAA,UACnC,CAAC,CAAA;AAAA,QACH,CAAC,CAAA,CACA,OAAA,CAAQ,MAAM;AACb,UAAA,mBAAA,CAAoB,OAAA,GAAU,KAAA;AAC9B,UAAA,gBAAA,CAAiB,KAAK,CAAA;AAAA,QACxB,CAAC,CAAA;AAAA,MACL;AAAA,IACF,CAAA;AACA,IAAA,EAAA,CAAG,iBAAiB,QAAA,EAAU,OAAA,EAAS,EAAE,OAAA,EAAS,MAAM,CAAA;AAExD,IAAA,qBAAA,CAAsB,MAAM,cAAA,CAAe,KAAK,CAAC,CAAA;AACjD,IAAA,OAAO,MAAM,EAAA,CAAG,mBAAA,CAAoB,QAAA,EAAU,OAAO,CAAA;AAAA,EAEvD,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAGf,EAAA,MAAM,aAAA,GAAsB,cAAQ,MAAM;AACxC,IAAA,KAAA,IAAS,IAAI,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG,CAAA,IAAK,GAAG,CAAA,EAAA,EAAK;AAC7C,MAAA,IAAI,SAAS,CAAC,CAAA,CAAE,YAAY,IAAA,EAAM,OAAO,SAAS,CAAC,CAAA;AAAA,IACrD;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAGb,EAAA,MAAM,CAAC,iBAAiB,kBAAkB,CAAA,GAAU,eAAsB,sBAAM,IAAI,KAAK,CAAA;AAGzF,EAAA,MAAM,kBAAA,GAAgD,KAAA,CAAA,OAAA;AAAA,IACpD,MAAO,kCAAK,6BAAA,CAAA,EAAkC,YAAA,CAAA;AAAA,IAC9C,CAAC,YAAY;AAAA,GACf;AAEA,EAAA,MAAM,iBAAA,GAA0B,KAAA,CAAA,WAAA;AAAA,IAC9B,CAAC,WAAmB,KAAA,KAA8B;AAGhD,MAAA,kBAAA,CAAmB,CAAC,IAAA,KAAS;AAC3B,QAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,IAAI,CAAA;AACzB,QAAA,IAAA,CAAK,IAAI,SAAS,CAAA;AAClB,QAAA,OAAO,IAAA;AAAA,MACT,CAAC,CAAA;AAED,MAAA,MAAA,IAAA,IAAA,GAAA,MAAA,GAAA,MAAA,CAAS,SAAA,EAAW,KAAA,EAAO,kBAAA,CAAmB,KAAK,CAAA,CAAA;AAAA,IACrD,CAAA;AAAA,IACA,CAAC,QAAQ,kBAAkB;AAAA,GAC7B;AAGA,EAAA,MAAM,mBAAA,GAAuC,gBAAgB,YAAA,GAAe,SAAA;AAC5E,EAAA,MAAM,CAAC,sBAAA,EAAwB,yBAAyB,CAAA,GAAU,eAEhE,MAAM,CAAA;AAER,EAAA,MAAM,wBAAA,GAAiC,aAAO,CAAC,CAAA;AAC/C,EAAA,MAAM,YAAA,GAAqB,aAAO,KAAK,CAAA;AACvC,EAAM,gBAAU,MAAM;AACpB,IAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,IAAA,OAAO,MAAM;AACX,MAAA,YAAA,CAAa,OAAA,GAAU,KAAA;AACvB,MAAA,wBAAA,CAAyB,OAAA,EAAA;AAAA,IAC3B,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAGL,EAAM,gBAAU,MAAM;AACpB,IAAA,IAAI,2BAA2B,MAAA,EAAQ;AAAA,EAEzC,CAAA,EAAG,CAAC,aAAA,EAAe,sBAAsB,CAAC,CAAA;AAG1C,EAAA,MAAM,eAAA,GAAmC,UAAA,GACrC,UAAA,GACA,sBAAA,KAA2B,WAAA,GACzB,WAAA,GACA,sBAAA,KAA2B,QAAA,GACzB,QAAA,GACA,sBAAA,KAA2B,YAAA,GACzB,YAAA,GACA,mBAAA;AAIV,EAAA,MAAM,mBAAA,GAA4B,KAAA,CAAA,WAAA;AAAA,IAChC,CACE,UACA,aAAA,KACG;AACH,MAAA,MAAM,MAAA,GAAS,QAAA,IAAA,IAAA,GAAA,MAAA,GAAA,QAAA,EAAA;AACf,MAAA,IAAI,CAAC,UAAA,CAAW,MAAM,CAAA,EAAG;AACvB,QAAA,yBAAA,CAA0B,WAAW,CAAA;AACrC,QAAA;AAAA,MACF;AACA,MAAA,MAAM,KAAA,GAAQ,EAAE,wBAAA,CAAyB,OAAA;AACzC,MAAA,yBAAA,CAA0B,YAAY,CAAA;AACtC,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,MAAM;AACJ,UAAA,IAAI,CAAC,YAAA,CAAa,OAAA,IAAW,wBAAA,CAAyB,YAAY,KAAA,EAAO;AACzE,UAAA,yBAAA,CAA0B,WAAW,CAAA;AAAA,QACvC,CAAA;AAAA,QACA,CAAC,KAAA,KAAmB;AAClB,UAAA,IAAI,CAAC,YAAA,CAAa,OAAA,IAAW,wBAAA,CAAyB,YAAY,KAAA,EAAO;AACzE,UAAA,yBAAA,CAA0B,aAAa,CAAA;AACvC,UAAA,gBAAA,IAAA,IAAA,GAAA,MAAA,GAAA,gBAAA,CAAmB,KAAA,CAAA;AAAA,QACrB;AAAA,OACF;AAAA,IACF,CAAA;AAAA,IACA,CAAC,gBAAgB;AAAA,GACnB;AAGA,EAAA,MAAM,4BAAA,GAAqC,kBAAY,MAAM;AAC3D,IAAA,mBAAA,CAAoB,kBAAkB,MAAM,CAAA;AAAA,EAC9C,CAAA,EAAG,CAAC,gBAAA,EAAkB,mBAAmB,CAAC,CAAA;AAE1C,EAAA,MAAM,4BAAA,GAAqC,kBAAY,MAAM;AAC3D,IAAA,yBAAA,CAA0B,QAAQ,CAAA;AAClC,IAAA,gBAAA,IAAA,IAAA,GAAA,MAAA,GAAA,gBAAA,EAAA;AAAA,EACF,CAAA,EAAG,CAAC,gBAAgB,CAAC,CAAA;AAErB,EAAA,MAAM,6BAAA,GAAsC,kBAAY,MAAM;AAC5D,IAAA,mBAAA,CAAoB,mBAAmB,QAAQ,CAAA;AAAA,EACjD,CAAA,EAAG,CAAC,iBAAA,EAAmB,mBAAmB,CAAC,CAAA;AAE3C,EAAA,MAAM,6BAAA,GAAsC,kBAAY,MAAM;AAC5D,IAAA,wBAAA,CAAyB,OAAA,EAAA;AACzB,IAAA,yBAAA,CAA0B,MAAM,CAAA;AAChC,IAAA,iBAAA,IAAA,IAAA,GAAA,MAAA,GAAA,iBAAA,EAAA;AAAA,EACF,CAAA,EAAG,CAAC,iBAAiB,CAAC,CAAA;AAEtB,EAAA,MAAM,uBAAA,GAAgC,kBAAY,MAAM;AACtD,IAAA,wBAAA,CAAyB,OAAA,EAAA;AACzB,IAAA,yBAAA,CAA0B,MAAM,CAAA;AAChC,IAAA,WAAA,IAAA,IAAA,GAAA,MAAA,GAAA,WAAA,EAAA;AAAA,EACF,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAIhB,EAAA,MAAM,yBAAA,GAAkC,kBAAY,MAAM;AACxD,IAAA,IAAI,oBAAA,EAAsB;AACxB,MAAA,oBAAA,EAAqB;AAAA,IACvB,CAAA,MAAO;AACL,MAAA,4BAAA,EAA6B;AAAA,IAC/B;AAAA,EACF,CAAA,EAAG,CAAC,oBAAA,EAAsB,4BAA4B,CAAC,CAAA;AAIvD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAU,eAAS,EAAE,CAAA;AACrD,EAAA,MAAM,sBAAA,GAA+B,KAAA,CAAA,WAAA;AAAA,IACnC,CAAC,IAAA,KAAiB;AAChB,MAAA,aAAA,CAAc,EAAE,CAAA;AAChB,MAAA,UAAA,IAAA,IAAA,GAAA,MAAA,GAAA,UAAA,CAAa,IAAA,CAAA;AAAA,IACf,CAAA;AAAA,IACA,CAAC,UAAU;AAAA,GACb;AAGA,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA,aAAA,CAAA,cAAA,CAAA;AAAA,MACC,WAAA,EAAU,aAAA;AAAA,MACV,SAAA,EAAW,EAAA,CAAG,6CAAA,EAA+C,SAAS;AAAA,KAAA,EAClE,KAAA,CAAA,EAHL;AAAA,MAME,QAAA,EAAA;AAAA,QAAA,OAAA,oBACC,IAAA,CAAC,gBAAA,EAAA,EAAiB,OAAA,EAAQ,SAAA,EACxB,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,aAAA,EAAY,MAAA,EAAO,QAAA,EAAA,QAAA,EAAC,CAAA;AAAA,0BAC1B,GAAA,CAAC,UAAM,QAAA,EAAA,iBAAA,EAAkB;AAAA,SAAA,EAC3B,CAAA;AAAA,QAED,aAAA,oBACC,IAAA,CAAC,gBAAA,EAAA,EAAiB,OAAA,EAAQ,YAAA,EACxB,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,aAAA,EAAY,MAAA,EAAO,QAAA,EAAA,QAAA,EAAC,CAAA;AAAA,0BAC1B,GAAA,CAAC,UACE,QAAA,EAAA,mBAAA,GACG,mBAAA,CAAoB,oBAAoB,CAAA,GACxC,CAAA,QAAA,EAAW,oBAAoB,CAAA,+BAAA,CAAA,EACrC;AAAA,SAAA,EACF,CAAA;AAAA,wBAIF,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,SAAA;AAAA,YAEL,SAAA,EAAU,iGAAA;AAAA,YACV,IAAA,EAAK,KAAA;AAAA,YACL,WAAA,EAAU,QAAA;AAAA,YACV,aAAA,EAAY,OAAA;AAAA,YAGX,QAAA,EAAA;AAAA,cAAA,aAAA,oBACC,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,0BAAA,EAA2B,YAAA,EAAW,mCACnD,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,6GAAA,EAA8G,CAAA,EAChI,CAAA;AAAA,cAGD,QAAA,CAAS,GAAA,CAAI,CAAC,GAAA,EAAK,GAAA,KAAQ;AAE1B,gBAAA,MAAM,UAAA,GACJ,UAAA,CAAW,OAAA,IAAW,GAAA,IAAO,oBAAoB,OAAA,GAAU,CAAA;AAE7D,gBAAA,MAAM,YAAA,GAAe,GAAA,CAAI,OAAA,KAAY,MAAA,IAAU,IAAI,MAAA,KAAW,QAAA;AAC9D,gBAAA,MAAM,UAAA,GAAa,GAAA,CAAI,OAAA,KAAY,IAAA,IAAQ,IAAI,MAAA,KAAW,QAAA;AAC1D,gBAAA,MAAM,aAAA,GAAgB,GAAA,CAAI,OAAA,KAAY,MAAA,IAAU,IAAI,MAAA,KAAW,SAAA;AAE/D,gBAAA,uBACE,IAAA,CAAC,qBAAA,EAAA,EAAmC,eAAA,EAAiB,UAAA,EACnD,QAAA,EAAA;AAAA,kCAAA,GAAA;AAAA,oBAAC,WAAA;AAAA,oBAAA;AAAA,sBACC,SAAS,GAAA,CAAI,OAAA;AAAA,sBACb,MAAM,GAAA,CAAI,IAAA;AAAA,sBACV,UAAU,GAAA,CAAI,QAAA;AAAA,sBACd,MAAA,EAAQ,GAAA,CAAI,OAAA,KAAY,MAAA,GAAS,UAAA,GAAa,MAAA;AAAA,sBAC9C,MAAA,EAAQ,GAAA,CAAI,OAAA,KAAY,MAAA,GAAS,QAAA,GAAW,MAAA;AAAA,sBAC5C,KAAA,EAAO,gBAAA;AAAA,sBAEP,SAAS,aAAA,IAAiB,CAAC,GAAA,CAAI,IAAA,IAAQ,CAAC,GAAA,CAAI,QAAA;AAAA,sBAC5C,SAAA,EAAW,EAAA;AAAA,wBACT,aAAA,IAAiB,YAAA;AAAA,wBACjB,YAAA,IAAgB;AAAA;AAClB;AAAA,mBACF;AAAA,kBAEC,YAAA,oBACC,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uBAAA,EACb,QAAA,kBAAA,IAAA;AAAA,oBAAC,QAAA;AAAA,oBAAA;AAAA,sBACC,IAAA,EAAK,QAAA;AAAA,sBACL,OAAA,EAAS,MAAM,cAAA,IAAA,IAAA,GAAA,MAAA,GAAA,cAAA,CAAiB,GAAA,CAAI,EAAA,CAAA;AAAA,sBACpC,SAAA,EAAU,gKAAA;AAAA,sBACX,QAAA,EAAA;AAAA,wBAAA,SAAA;AAAA,wBACI;AAAA;AAAA;AAAA,mBACL,EACF,CAAA;AAAA,kBAGD,UAAA;AAAA;AAAA,kCAGC,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,+CAAA,EACb,QAAA,EAAA;AAAA,oCAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,0BAAA,EACZ,QAAA,EAAA,GAAA,CAAI,aAAa,kBAAA,EACpB,CAAA;AAAA,oCACA,IAAA;AAAA,sBAAC,QAAA;AAAA,sBAAA;AAAA,wBACC,IAAA,EAAK,QAAA;AAAA,wBACL,OAAA,EAAS,oBAAA;AAAA,wBACT,SAAA,EAAU,4JAAA;AAAA,wBACX,QAAA,EAAA;AAAA,0BAAA,SAAA;AAAA,0BACI;AAAA;AAAA;AAAA;AACL,mBAAA,EACF,CAAA;AAAA,kBAOD,GAAA,CAAI,OAAA,KAAY,IAAA,IAAQ,GAAA,CAAI,iBAAiB,CAAC,eAAA,CAAgB,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA,oBACvE,GAAA,CAAC,KAAA,EAAA,EAAI,WAAU,wCAAA,EACb,QAAA,kBAAA,GAAA;AAAA,oBAAC,aAAA;AAAA,oBAAA;AAAA,sBACC,KAAA,EAAO,IAAA;AAAA,sBACP,MAAA,EAAQ,YAAA;AAAA,sBACR,UAAU,CAAC,KAAA,KAAU,iBAAA,CAAkB,GAAA,CAAI,IAAI,KAAK;AAAA;AAAA,mBACtD,EACF;AAAA,iBAAA,EAAA,EAxDwB,IAAI,EA0DhC,CAAA;AAAA,cAEJ,CAAC,CAAA;AAAA,cAGA,UAAA,oBACC,GAAA,CAAC,qBAAA,EAAA,EAAsB,eAAA,EAAe,IAAA,EACpC,QAAA,kBAAA,GAAA,CAAC,WAAA,EAAA,EAAY,OAAA,EAAQ,IAAA,EAAK,OAAA,EAAO,IAAA,EAAC,CAAA,EACpC,CAAA;AAAA,8BAIF,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,SAAA,EAAU,WAAA,EAAU,UAAS,aAAA,EAAY,MAAA,EACrD,QAAA,EAAA,CAAA,aAAA,IAAA,IAAA,GAAA,MAAA,GAAA,aAAA,CAAe,IAAA,KAAQ,EAAA,EAC1B;AAAA;AAAA;AAAA,SACF;AAAA,wBAMA,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,IAAA,EACE,UAAA,GACI,cAAA,GACE,MAAA,GACA,QAAA,GACF,YAAA;AAAA,YAEN,UAAA,kBACE,GAAA;AAAA,cAAC,UAAA;AAAA,cAAA;AAAA,gBACC,KAAA,EAAO,eAAA;AAAA,gBACP,KAAA,EAAO,UAAA;AAAA,gBACP,QAAA,EAAU,aAAA;AAAA,gBACV,UAAA,EAAY,sBAAA;AAAA,gBACZ,WAAA,EAAa,uBAAA;AAAA,gBACb,gBAAA,EAAkB,4BAAA;AAAA,gBAClB,gBAAA,EAAkB,4BAAA;AAAA,gBAClB,iBAAA,EAAmB,6BAAA;AAAA,gBACnB,iBAAA,EAAmB,6BAAA;AAAA,gBACnB,YAAA;AAAA,gBACA,cAAA;AAAA,gBACA,eAAA;AAAA,gBACA,iBAAA;AAAA,gBACA,SAAA;AAAA,gBACA,gBAAA;AAAA,gBACA,WAAA;AAAA,gBACA,oBAAA;AAAA,gBACA,iBAAA;AAAA,gBACA,gBAAA;AAAA,gBACA,oBAAA,EAAsB;AAAA;AAAA,aACxB;AAAA,YAEF,4BACE,GAAA,CAAC,kBAAA,EAAA,EAAmB,OAAA,EAAS,cAAA,EAAgB,OAAO,gBAAA,EAAkB;AAAA;AAAA,SAE1E;AAAA,QAGC,kCACC,GAAA,CAAC,WAAA,EAAA,EAAY,IAAA,EAAI,IAAA,EACf,+BAAC,kBAAA,EAAA,EACC,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,iBAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,gBAAA,EAAA,EACE,QAAA,EAAA,CAAA,oBAAA,IAAA,IAAA,GAAA,MAAA,GAAA,oBAAA,CAAsB,KAAA,KAAS,iBAAA,EAClC,CAAA;AAAA,4BACA,GAAA,CAAC,sBAAA,EAAA,EACE,QAAA,EAAA,CAAA,oBAAA,IAAA,IAAA,GAAA,MAAA,GAAA,oBAAA,CAAsB,WAAA,KACrB,+EAAA,EACJ;AAAA,WAAA,EACF,CAAA;AAAA,+BACC,iBAAA,EAAA,EACE,QAAA,EAAA;AAAA,YAAA,CAAA,oBAAA,IAAA,IAAA,GAAA,MAAA,GAAA,oBAAA,CAAsB,QAAA,yBACpB,iBAAA,EAAA,EAAkB,OAAA,EAAS,qBAAqB,QAAA,EAC9C,QAAA,EAAA,oBAAA,CAAqB,eAAe,UAAA,EACvC,CAAA;AAAA,gCAED,iBAAA,EAAA,EAAkB,OAAA,EAAS,6DAAsB,KAAA,EAC/C,QAAA,EAAA,CAAA,oBAAA,IAAA,IAAA,GAAA,MAAA,GAAA,oBAAA,CAAsB,aAAY,eAAA,EACrC;AAAA,WAAA,EACF;AAAA,SAAA,EACF,CAAA,EACF;AAAA;AAAA,KAAA;AAAA,GAEJ;AAEJ","file":"chunk-MQX4NBKQ.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Square } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ChatMessage } from \"@/components/ui/chat-message\"\nimport {\n MessageBar,\n type MessageBarState,\n} from \"@/components/ui/message-bar\"\nimport {\n MessageRating,\n DEFAULT_MESSAGE_RATING_LABELS,\n type MessageRatingValue,\n type MessageRatingLabels,\n type RatingLabel,\n} from \"@/components/ui/message-rating\"\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n} from \"@/components/ui/alert-dialog\"\n\n/** Duck-typing de Promise/thenable — aceita Promise nativa e qualquer objeto com .then */\nfunction isThenable(value: unknown): value is PromiseLike<unknown> {\n return (\n !!value &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n typeof (value as { then?: unknown }).then === \"function\"\n )\n}\n\n/* ─── Types ───────────────────────────────────────────────────── */\n\nexport type ChatThreadMessageStatus = \"sent\" | \"pending\" | \"failed\"\nexport type ChatThreadPersona = \"ai\" | \"user\" | \"system\"\n\nexport interface ChatThreadMessage {\n id: string\n persona: ChatThreadPersona\n status?: ChatThreadMessageStatus\n text?: string\n audioSrc?: string\n audioPeaks?: number[]\n /** Mensagem de erro pra exibir quando `persona=\"ai\"` e `status=\"failed\"` */\n errorText?: string\n /**\n * Quando true em uma mensagem da IA, renderiza o `MessageRating` (5 emojis) logo\n * abaixo da bubble — alinhado com a IA, parte da mesma \"linha visual\" da mensagem.\n * Apos o usuario responder, o rating some automaticamente (fade-out + slide-down).\n *\n * Usado quando a IA pede avaliacao da atividade: o consumer adiciona a msg da IA\n * com este campo `true` no array `messages` e ouve `onRate` para receber a resposta.\n */\n requestRating?: boolean\n}\n\nexport type ChatThreadState = \"idle\" | \"sending\" | \"thinking\" | \"error\"\n\nexport interface QuotaExhaustedConfig {\n title?: string\n description?: string\n ctaLabel?: string\n cancelLabel?: string\n onCta?: () => void\n onCancel?: () => void\n}\n\nexport interface ChatThreadProps extends Omit<React.ComponentProps<\"div\">, \"onChange\"> {\n /** Lista de mensagens da conversa */\n messages: ChatThreadMessage[]\n /** Estado global da conversa (controlled) */\n state?: ChatThreadState\n /** Quando true e thread vazio, monta direto em thinking (cenario Class — IA fala primeiro) */\n initialThinking?: boolean\n\n /* ─── Callbacks essenciais ─── */\n onSendText?: (text: string) => void\n /** Disparado quando user confirma envio do audio gravado (MessageBar dispara apos release/lock).\n * Consumer gerencia MediaRecorder e tem acesso ao Blob no momento que isso for chamado. */\n onSendAudio?: () => void\n /** Retry de mensagem do usuario que falhou enviar (B2) */\n onRetryMessage?: (messageId: string) => void\n /** Regenerar resposta da IA com erro (B3) */\n onRegenerateResponse?: () => void\n /** Cancelar resposta da IA mid-thinking (F1). Passar este callback **ativa** o botao\n * \"Parar resposta\" no rodape durante o estado `thinking`. Se omitido, o MessageBar\n * some com animacao de saida quando a IA esta pensando e volta com animacao de entrada\n * quando termina — o consumer indica que nao quer expor cancelamento. */\n onStopResponse?: () => void\n /** Carregar mensagens antigas (G1 — scroll infinito) */\n onLoadMore?: () => Promise<void> | void\n\n /* ─── Callbacks do MessageBar (pass-through) ─── */\n /** Dispara quando o user pressiona o mic. Consumer chama getUserMedia + MediaRecorder.\n * Pode retornar uma Promise: enquanto ela estiver pendente o MessageBar fica em\n * `requesting` (mic com spinner) e so entra em `recording` quando resolver. Se\n * rejeitar, volta pro estado anterior e dispara `onRecordingError`. Retorno sincrono\n * (void) mantem o comportamento antigo: entra em `recording` imediatamente.\n * Recomendado: `onStartRecording={() => startRecording()}` retornando a Promise do\n * getUserMedia — evita mostrar \"gravando\" antes do microfone existir. */\n onStartRecording?: () => void | Promise<unknown>\n /** Dispara quando o user pausa a gravacao. Consumer faz recorder.stop() + cria Blob. */\n onPauseRecording?: () => void\n /** Dispara quando o user retoma gravacao apos pause. Mesma semantica de Promise do\n * `onStartRecording` (rejeitou → volta pra `paused`). */\n onResumeRecording?: () => void | Promise<unknown>\n /** Dispara quando a Promise de `onStartRecording`/`onResumeRecording` rejeita (ex.:\n * permissao de microfone negada). O ChatThread ja restaurou o estado do MessageBar;\n * o consumer decide o feedback (toast, alert, banner). */\n onRecordingError?: (error: unknown) => void\n /** Dispara quando o user cancela a gravacao. Consumer cleanup do MediaRecorder. */\n onCancelRecording?: () => void\n /** Toggle play/pause do audio gravado durante o estado `paused` do MessageBar. */\n onTogglePlay?: () => void\n /** Callback de seek no waveform do audio gravado (durante paused). */\n onSeekPlayback?: (progress: number) => void\n\n /* ─── State do MessageBar (pass-through) ─── */\n /** Stream do microfone durante recording — usado pelo MessageBar pra renderizar o live waveform. */\n recordingStream?: MediaStream | null\n /** Duracao da gravacao em segundos (controlado pelo consumer via setInterval). */\n recordingDuration?: number\n /** Indica se o audio gravado esta tocando (durante paused). */\n isPlaying?: boolean\n /** Progresso de playback (0-1) — usado pra sincronizar dot verde no waveform. */\n playbackProgress?: number\n\n /* ─── Erro states (configuravel) ─── */\n /** Mostra banner de offline + disabled MessageBar (C1) */\n offline?: boolean\n /** Timestamp epoch ate quando bloquear envio (C2). null/undefined = sem rate limit */\n rateLimitedUntil?: number | null\n /** Texto do banner de rate limit. Default: \"Aguarde {s}s antes de enviar nova mensagem\" */\n rateLimitBannerText?: (secondsRemaining: number) => string\n /** Modal de cota esgotada (C3) */\n quotaExhausted?: boolean\n quotaExhaustedConfig?: QuotaExhaustedConfig\n\n /* ─── Rating (J) ─── */\n /**\n * Callback disparado quando o usuario responde um rating de uma mensagem com `requestRating=true`.\n * Recebe:\n * - `messageId`: id da mensagem da IA que pediu avaliacao\n * - `value`: numero 1-5\n * - `label`: objeto com emoji/title/subtitle correspondente ao value\n *\n * O consumer tipicamente usa esses dados para adicionar uma nova mensagem do usuario no array\n * `messages` com o texto da escolha (ex: `label.subtitle` em PT-BR). O `MessageRating` some\n * imediatamente apos o clique (sem estado \"selecionado\" visual).\n */\n onRate?: (messageId: string, value: MessageRatingValue, label: RatingLabel) => void\n /**\n * Labels customizadas do `MessageRating` (sobrescreve PT-BR defaults).\n * Passado pro componente E usado no callback `onRate` ao montar o `label`.\n */\n ratingLabels?: Partial<MessageRatingLabels>\n\n /* ─── Configuracoes do MessageBar ─── */\n placeholder?: string\n maxLength?: number\n /** Modo so audio — passa pro MessageBar como state base */\n audioOnlyMode?: boolean\n\n /* ─── Limite de duracao da gravacao de audio ─── */\n /**\n * Limite de duracao da gravacao em segundos (configurado pelo consumer).\n * Quando atingido, o ChatThread auto-pausa a gravacao (vai pro paused state com\n * preview play/cancel/send) — a menos que `onMaxDurationReached` seja passado,\n * caso em que o consumer assume controle. Sem essa prop, sem limite.\n */\n maxRecordingDuration?: number\n /** Quantos segundos antes do limite ativar o warning visual (timer destructive\n * + label \"Xs restantes\"). Default 10. Ignorado sem `maxRecordingDuration`. */\n warnAtSecondsLeft?: number\n /** Label do warning. Default pt-BR: `(s) => \\`${s}s restantes\\``. */\n secondsLeftLabel?: (secondsLeft: number) => string\n /**\n * Callback opcional disparado ao atingir o limite. Se omitido, ChatThread\n * auto-pausa internamente. Se passado, **substitui** o auto-pause — o consumer\n * decide o que fazer (ex: mostrar toast antes de pausar manualmente).\n */\n onMaxDurationReached?: () => void\n\n /* ─── Identidade do usuario ─── */\n /** URL da imagem do avatar do usuario (aparece em todas as msgs do user no desktop). */\n userAvatar?: string\n /** Nome do usuario (usado para iniciais no fallback do avatar e alt da imagem). */\n userName?: string\n /** Classe de tema das bolhas do user (pass-through pro ChatMessage). Default \"theme-positive\". */\n userMessageTheme?: string\n\n /* ─── i18n / texto customizavel ─── */\n /** Default: \"Sem conexao. Tentando reconectar...\" */\n offlineBannerText?: string\n /** Default: \"Tentar novamente\" */\n retryButtonText?: string\n /** Default: \"Regenerar resposta\" */\n regenerateButtonText?: string\n /** Default: \"Parar resposta\" */\n stopResponseText?: string\n /** Default: \"Desculpe, nao consegui responder.\" */\n defaultAiErrorText?: string\n}\n\n/* ─── Constantes ──────────────────────────────────────────────── */\n\nconst SCROLL_BOTTOM_THRESHOLD = 100 // px do bottom pra considerar \"no fundo\"\nconst LOAD_MORE_THRESHOLD = 200 // px do topo pra disparar onLoadMore\n\n/* ─── Sub-componentes internos ────────────────────────────────── */\n\n/** Banner persistente no topo (offline, rate limit, etc.) */\nfunction ChatThreadBanner({\n variant,\n children,\n}: {\n variant: \"offline\" | \"rate-limit\"\n children: React.ReactNode\n}) {\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={cn(\n \"shrink-0 flex items-center gap-2 px-4 py-2 text-sm border-b\",\n variant === \"offline\" &&\n \"bg-destructive/10 text-destructive border-destructive/20\",\n variant === \"rate-limit\" &&\n \"bg-muted text-muted-foreground border-border\"\n )}\n >\n {children}\n </div>\n )\n}\n\n/** Conta segundos restantes ate `until` (timestamp epoch). */\nfunction useCountdown(until: number | null | undefined): number {\n const [now, setNow] = React.useState(() => Date.now())\n\n React.useEffect(() => {\n if (!until || until <= Date.now()) return\n const id = setInterval(() => setNow(Date.now()), 1000)\n return () => clearInterval(id)\n }, [until])\n\n if (!until) return 0\n const remainingMs = until - now\n return Math.max(0, Math.ceil(remainingMs / 1000))\n}\n\n/** Botao \"Parar resposta\" — substitui MessageBar durante thinking (F1) */\nfunction StopResponseButton({\n onClick,\n label = \"Parar resposta\",\n}: {\n onClick?: () => void\n label?: string\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"w-full flex items-center justify-center gap-2 h-12 rounded-2xl bg-muted text-neutral-foreground hover:bg-muted/80 transition-colors focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50\"\n aria-label={label}\n >\n <Square className=\"size-4\" fill=\"currentColor\" strokeWidth={0} />\n <span className=\"text-sm font-medium\">{label}</span>\n </button>\n )\n}\n\n/** Bubble wrapper com animacao de entrada (E1: fade-in + slide-up 8px, 200ms) */\nfunction MessageBubbleAnimated({\n children,\n enableAnimation,\n}: {\n children: React.ReactNode\n enableAnimation: boolean\n}) {\n return (\n <div\n className={cn(\n \"transition-[opacity,transform] duration-200 ease-out\",\n enableAnimation ? \"animate-thread-msg-enter\" : \"\"\n )}\n >\n {children}\n </div>\n )\n}\n\n/* ─── Bottom slot: gerencia animacao sequencial entre MessageBar / StopButton / hidden ─── */\n\ntype BottomSlotKind = \"messagebar\" | \"stop\" | \"hidden\"\nconst LEAVE_DURATION_MS = 150\n\n/**\n * Slot que alterna entre 3 estados com animacao sequencial (sem cross-fade):\n * o conteudo atual anima saida (`thread-msg-leave`, 150ms), troca o conteudo,\n * e o novo anima entrada (`thread-msg-enter`, 200ms). Similar ao\n * AnimatePresence mode=\"wait\" do Framer Motion, mas em CSS puro.\n */\nfunction BottomSlot({\n kind,\n messageBar,\n stopButton,\n}: {\n kind: BottomSlotKind\n messageBar: React.ReactNode\n stopButton: React.ReactNode\n}) {\n const [rendered, setRendered] = React.useState<BottomSlotKind>(kind)\n const [phase, setPhase] = React.useState<\"enter\" | \"leave\">(\"enter\")\n\n React.useEffect(() => {\n if (rendered === kind) return\n setPhase(\"leave\")\n const t = setTimeout(() => {\n setRendered(kind)\n setPhase(\"enter\")\n }, LEAVE_DURATION_MS)\n return () => clearTimeout(t)\n }, [kind, rendered])\n\n return (\n <div className=\"shrink-0 px-4 pb-4 pt-2\">\n {rendered !== \"hidden\" && (\n <div\n key={rendered}\n className={\n phase === \"enter\" ? \"animate-thread-msg-enter\" : \"animate-thread-msg-leave\"\n }\n >\n {rendered === \"messagebar\" ? messageBar : stopButton}\n </div>\n )}\n </div>\n )\n}\n\n/* ─── Component principal ─────────────────────────────────────── */\n\nfunction ChatThread({\n messages,\n state = \"idle\",\n initialThinking = false,\n onSendText,\n onSendAudio,\n onRetryMessage,\n onRegenerateResponse,\n onStopResponse,\n onLoadMore,\n onStartRecording,\n onPauseRecording,\n onResumeRecording,\n onRecordingError,\n onCancelRecording,\n onTogglePlay,\n onSeekPlayback,\n recordingStream,\n recordingDuration,\n isPlaying,\n playbackProgress,\n offline = false,\n rateLimitedUntil = null,\n rateLimitBannerText,\n quotaExhausted = false,\n quotaExhaustedConfig,\n onRate,\n ratingLabels,\n placeholder,\n maxLength,\n audioOnlyMode = false,\n maxRecordingDuration,\n warnAtSecondsLeft,\n secondsLeftLabel,\n onMaxDurationReached,\n userAvatar,\n userName,\n userMessageTheme,\n offlineBannerText = \"Sem conexao. Tentando reconectar...\",\n retryButtonText = \"Tentar novamente\",\n regenerateButtonText = \"Regenerar resposta\",\n stopResponseText = \"Parar resposta\",\n defaultAiErrorText = \"Desculpe, nao consegui responder.\",\n className,\n ...props\n}: ChatThreadProps) {\n /* ─── State derivado ─── */\n const rateLimitSecondsLeft = useCountdown(rateLimitedUntil)\n const isRateLimited = rateLimitSecondsLeft > 0\n const isThinking =\n state === \"thinking\" || (initialThinking && messages.length === 0)\n const isDisabled = offline || isRateLimited || quotaExhausted\n\n /* ─── Refs e scroll inteligente (D1) ─── */\n const scrollRef = React.useRef<HTMLDivElement>(null)\n const [isAtBottom, setIsAtBottom] = React.useState(true)\n const prevMessageCountRef = React.useRef(messages.length)\n const loadMoreInFlightRef = React.useRef(false)\n const [isLoadingMore, setIsLoadingMore] = React.useState(false)\n // Marca de quando o componente montou — usada para suprimir animacao das mensagens iniciais\n const mountedRef = React.useRef(false)\n React.useEffect(() => {\n mountedRef.current = true\n }, [])\n\n const checkScrollPosition = React.useCallback(() => {\n const el = scrollRef.current\n if (!el) return\n const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight\n const atBottom = distanceFromBottom <= SCROLL_BOTTOM_THRESHOLD\n setIsAtBottom(atBottom)\n }, [])\n\n const scrollToBottom = React.useCallback((smooth = true) => {\n const el = scrollRef.current\n if (!el) return\n el.scrollTo({\n top: el.scrollHeight,\n behavior: smooth ? \"smooth\" : \"auto\",\n })\n }, [])\n\n /* ─── Auto-scroll quando chega nova mensagem ─── */\n React.useEffect(() => {\n const prev = prevMessageCountRef.current\n const curr = messages.length\n prevMessageCountRef.current = curr\n\n if (curr <= prev) return\n\n // Esta no fundo → scroll suave pra nova msg. Se o usuario subiu, preserva a\n // posicao (sem botao flutuante — ele sobrepunha o MessageBar).\n if (isAtBottom) {\n requestAnimationFrame(() => scrollToBottom(true))\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [messages.length])\n\n /* ─── Scroll handler (detecta posicao + paginacao) ─── */\n React.useEffect(() => {\n const el = scrollRef.current\n if (!el) return\n const handler = () => {\n checkScrollPosition()\n // Paginacao: se chegou perto do topo e ha onLoadMore, dispara\n if (\n onLoadMore &&\n !loadMoreInFlightRef.current &&\n el.scrollTop <= LOAD_MORE_THRESHOLD\n ) {\n loadMoreInFlightRef.current = true\n setIsLoadingMore(true)\n const prevScrollHeight = el.scrollHeight\n Promise.resolve(onLoadMore())\n .then(() => {\n // Preserva posicao visual apos prepend de mensagens antigas\n requestAnimationFrame(() => {\n const newScrollHeight = el.scrollHeight\n el.scrollTop = newScrollHeight - prevScrollHeight\n })\n })\n .finally(() => {\n loadMoreInFlightRef.current = false\n setIsLoadingMore(false)\n })\n }\n }\n el.addEventListener(\"scroll\", handler, { passive: true })\n // Inicial: garante scroll no fundo\n requestAnimationFrame(() => scrollToBottom(false))\n return () => el.removeEventListener(\"scroll\", handler)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [onLoadMore])\n\n /* ─── Aria-live announce de nova msg da IA (H1) ─── */\n const lastAiMessage = React.useMemo(() => {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].persona === \"ai\") return messages[i]\n }\n return null\n }, [messages])\n\n /* ─── Rating per-message: rastreia IDs cujo rating foi respondido (auto-dismiss imediato) ─── */\n const [ratedMessageIds, setRatedMessageIds] = React.useState<Set<string>>(() => new Set())\n\n // Merge dos labels customizados com defaults PT-BR. Usado pra renderizar E pra montar o callback.\n const mergedRatingLabels: MessageRatingLabels = React.useMemo(\n () => ({ ...DEFAULT_MESSAGE_RATING_LABELS, ...ratingLabels }),\n [ratingLabels]\n )\n\n const handleRateMessage = React.useCallback(\n (messageId: string, value: MessageRatingValue) => {\n // Marca como respondido IMEDIATAMENTE — re-render remove o rating do DOM antes\n // do paint visual de \"selected\", entao o usuario nao ve o estado intermediario.\n setRatedMessageIds((prev) => {\n const next = new Set(prev)\n next.add(messageId)\n return next\n })\n // Dispara callback com label completo (emoji/title/subtitle).\n onRate?.(messageId, value, mergedRatingLabels[value])\n },\n [onRate, mergedRatingLabels]\n )\n\n /* ─── MessageBar state interno (state machine de gravacao) ─── */\n const baseMessageBarState: MessageBarState = audioOnlyMode ? \"audio-only\" : \"default\"\n const [internalRecordingState, setInternalRecordingState] = React.useState<\n \"idle\" | \"requesting\" | \"recording\" | \"paused\"\n >(\"idle\")\n // Token do pedido de microfone em voo — descarta resolucao de pedidos antigos (ex.: cancelou/desmontou no meio)\n const recordingRequestTokenRef = React.useRef(0)\n const isMountedRef = React.useRef(false)\n React.useEffect(() => {\n isMountedRef.current = true\n return () => {\n isMountedRef.current = false\n recordingRequestTokenRef.current++\n }\n }, [])\n\n // Reset pra idle quando muda audioOnlyMode e nao esta gravando\n React.useEffect(() => {\n if (internalRecordingState === \"idle\") return\n // mantem o state se gravacao em andamento\n }, [audioOnlyMode, internalRecordingState])\n\n // State final do MessageBar — combina base, recording state e disabled\n const messageBarState: MessageBarState = isDisabled\n ? \"disabled\"\n : internalRecordingState === \"recording\"\n ? \"recording\"\n : internalRecordingState === \"paused\"\n ? \"paused\"\n : internalRecordingState === \"requesting\"\n ? \"requesting\"\n : baseMessageBarState\n\n /** Roda o callback de start/resume. Se devolver Promise, segura o MessageBar em\n * `requesting` ate resolver; ao rejeitar, volta pro estado anterior e avisa o consumer. */\n const runRecordingRequest = React.useCallback(\n (\n callback: (() => void | Promise<unknown>) | undefined,\n fallbackState: \"idle\" | \"paused\"\n ) => {\n const result = callback?.()\n if (!isThenable(result)) {\n setInternalRecordingState(\"recording\")\n return\n }\n const token = ++recordingRequestTokenRef.current\n setInternalRecordingState(\"requesting\")\n result.then(\n () => {\n if (!isMountedRef.current || recordingRequestTokenRef.current !== token) return\n setInternalRecordingState(\"recording\")\n },\n (error: unknown) => {\n if (!isMountedRef.current || recordingRequestTokenRef.current !== token) return\n setInternalRecordingState(fallbackState)\n onRecordingError?.(error)\n }\n )\n },\n [onRecordingError]\n )\n\n /* Wrappers dos callbacks: atualizam state interno + chamam consumer */\n const handleStartRecordingInternal = React.useCallback(() => {\n runRecordingRequest(onStartRecording, \"idle\")\n }, [onStartRecording, runRecordingRequest])\n\n const handlePauseRecordingInternal = React.useCallback(() => {\n setInternalRecordingState(\"paused\")\n onPauseRecording?.()\n }, [onPauseRecording])\n\n const handleResumeRecordingInternal = React.useCallback(() => {\n runRecordingRequest(onResumeRecording, \"paused\")\n }, [onResumeRecording, runRecordingRequest])\n\n const handleCancelRecordingInternal = React.useCallback(() => {\n recordingRequestTokenRef.current++ // invalida pedido de microfone em voo\n setInternalRecordingState(\"idle\")\n onCancelRecording?.()\n }, [onCancelRecording])\n\n const handleSendAudioInternal = React.useCallback(() => {\n recordingRequestTokenRef.current++\n setInternalRecordingState(\"idle\")\n onSendAudio?.()\n }, [onSendAudio])\n\n /* Auto-pause ao atingir limite de duracao. Se consumer passou onMaxDurationReached,\n delega controle pra ele (sem auto-pause). */\n const handleMaxDurationInternal = React.useCallback(() => {\n if (onMaxDurationReached) {\n onMaxDurationReached()\n } else {\n handlePauseRecordingInternal()\n }\n }, [onMaxDurationReached, handlePauseRecordingInternal])\n\n /* Input controlled — sem isso o MessageBar fica \"engessado\" em value=\"\"\n (o consumer recebe o texto via onSendText, mas nao precisa controlar o input). */\n const [inputValue, setInputValue] = React.useState(\"\")\n const handleSendTextInternal = React.useCallback(\n (text: string) => {\n setInputValue(\"\")\n onSendText?.(text)\n },\n [onSendText]\n )\n\n /* ─── Render ──────────────────────────────────────── */\n return (\n <div\n data-slot=\"chat-thread\"\n className={cn(\"relative flex flex-col h-full bg-background\", className)}\n {...props}\n >\n {/* Banners de erro (topo) */}\n {offline && (\n <ChatThreadBanner variant=\"offline\">\n <span aria-hidden=\"true\">⚠</span>\n <span>{offlineBannerText}</span>\n </ChatThreadBanner>\n )}\n {isRateLimited && (\n <ChatThreadBanner variant=\"rate-limit\">\n <span aria-hidden=\"true\">⏱</span>\n <span>\n {rateLimitBannerText\n ? rateLimitBannerText(rateLimitSecondsLeft)\n : `Aguarde ${rateLimitSecondsLeft}s antes de enviar nova mensagem`}\n </span>\n </ChatThreadBanner>\n )}\n\n {/* Thread scrollavel */}\n <div\n ref={scrollRef}\n // Scrollbar oculta (Firefox + WebKit/Chromium); scroll segue funcionando\n className=\"flex-1 overflow-y-auto px-4 py-4 space-y-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\n role=\"log\"\n aria-live=\"polite\"\n aria-atomic=\"false\"\n >\n {/* Spinner de paginacao no topo (G2) */}\n {isLoadingMore && (\n <div className=\"flex justify-center py-2\" aria-label=\"Carregando mensagens anteriores\">\n <span className=\"inline-block size-5 border-2 border-muted-foreground/30 border-t-muted-foreground rounded-full animate-spin\" />\n </div>\n )}\n\n {messages.map((msg, idx) => {\n // Suprime animacao das mensagens que ja existiam no mount inicial\n const enableAnim =\n mountedRef.current && idx >= prevMessageCountRef.current - 1\n\n const isFailedUser = msg.persona === \"user\" && msg.status === \"failed\"\n const isFailedAi = msg.persona === \"ai\" && msg.status === \"failed\"\n const isPendingUser = msg.persona === \"user\" && msg.status === \"pending\"\n\n return (\n <MessageBubbleAnimated key={msg.id} enableAnimation={enableAnim}>\n <ChatMessage\n persona={msg.persona}\n text={msg.text}\n audioSrc={msg.audioSrc}\n avatar={msg.persona === \"user\" ? userAvatar : undefined}\n author={msg.persona === \"user\" ? userName : undefined}\n theme={userMessageTheme}\n // Indicador de pending: typing dots no lugar do conteudo normal e visual sutil\n loading={isPendingUser && !msg.text && !msg.audioSrc}\n className={cn(\n isPendingUser && \"opacity-80\",\n isFailedUser && \"[&_[data-slot=chat-message-bubble]]:border-2 [&_[data-slot=chat-message-bubble]]:border-destructive\"\n )}\n />\n {/* Retry inline pra msg do user que falhou (B2) */}\n {isFailedUser && (\n <div className=\"mt-2 flex justify-end\">\n <button\n type=\"button\"\n onClick={() => onRetryMessage?.(msg.id)}\n className=\"inline-flex items-center gap-1 text-xs text-destructive hover:underline focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 rounded\"\n >\n ↻ {retryButtonText}\n </button>\n </div>\n )}\n {/* Bubble de erro pra resposta da IA (B3) */}\n {isFailedAi && (\n /* Alinhado com a bubble da IA: offset = badge 32px + gap 12px = 44px (desktop).\n Mobile sem offset porque o badge fica oculto no ChatMessage AI. */\n <div className=\"mt-2 sm:pl-11 flex flex-col items-start gap-2\">\n <div className=\"text-sm text-destructive\">\n {msg.errorText || defaultAiErrorText}\n </div>\n <button\n type=\"button\"\n onClick={onRegenerateResponse}\n className=\"inline-flex items-center gap-1 text-xs text-primary hover:underline focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 rounded\"\n >\n ↻ {regenerateButtonText}\n </button>\n </div>\n )}\n {/* MessageRating inline na mensagem da IA que pede avaliacao (J).\n Alinhado com a bubble da IA (offset 44px desktop, 0 mobile).\n value={null} mantem o componente sem estado \"selecionado\" visual —\n ao clicar, o ChatThread marca como rated imediatamente (re-render remove\n do DOM antes do paint), e o consumer recebe (messageId, value, label). */}\n {msg.persona === \"ai\" && msg.requestRating && !ratedMessageIds.has(msg.id) && (\n <div className=\"mt-3 sm:pl-11 animate-thread-msg-enter\">\n <MessageRating\n value={null}\n labels={ratingLabels}\n onChange={(value) => handleRateMessage(msg.id, value)}\n />\n </div>\n )}\n </MessageBubbleAnimated>\n )\n })}\n\n {/* Typing indicator da IA durante thinking */}\n {isThinking && (\n <MessageBubbleAnimated enableAnimation>\n <ChatMessage persona=\"ai\" loading />\n </MessageBubbleAnimated>\n )}\n\n {/* Aria-live oculto pra anunciar ultima msg da IA */}\n <div className=\"sr-only\" aria-live=\"polite\" aria-atomic=\"true\">\n {lastAiMessage?.text || \"\"}\n </div>\n </div>\n\n {/* Bottom area: MessageBar / StopResponse / hidden.\n O botao \"Parar resposta\" so aparece durante thinking E quando o consumer passa\n `onStopResponse` — quem nao implementa cancelamento ve o MessageBar sumir com\n animacao de saida e voltar com animacao de entrada quando a IA termina. */}\n <BottomSlot\n kind={\n isThinking\n ? onStopResponse\n ? \"stop\"\n : \"hidden\"\n : \"messagebar\"\n }\n messageBar={\n <MessageBar\n state={messageBarState}\n value={inputValue}\n onChange={setInputValue}\n onSendText={handleSendTextInternal}\n onSendAudio={handleSendAudioInternal}\n onStartRecording={handleStartRecordingInternal}\n onPauseRecording={handlePauseRecordingInternal}\n onResumeRecording={handleResumeRecordingInternal}\n onCancelRecording={handleCancelRecordingInternal}\n onTogglePlay={onTogglePlay}\n onSeekPlayback={onSeekPlayback}\n recordingStream={recordingStream}\n recordingDuration={recordingDuration}\n isPlaying={isPlaying}\n playbackProgress={playbackProgress}\n placeholder={placeholder}\n maxRecordingDuration={maxRecordingDuration}\n warnAtSecondsLeft={warnAtSecondsLeft}\n secondsLeftLabel={secondsLeftLabel}\n onMaxDurationReached={handleMaxDurationInternal}\n />\n }\n stopButton={\n <StopResponseButton onClick={onStopResponse} label={stopResponseText} />\n }\n />\n\n {/* Quota exhausted modal (C3) */}\n {quotaExhausted && (\n <AlertDialog open>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {quotaExhaustedConfig?.title || \"Limite atingido\"}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {quotaExhaustedConfig?.description ||\n \"Voce atingiu o limite de mensagens do seu plano. Faca upgrade para continuar.\"}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n {quotaExhaustedConfig?.onCancel && (\n <AlertDialogCancel onClick={quotaExhaustedConfig.onCancel}>\n {quotaExhaustedConfig.cancelLabel || \"Cancelar\"}\n </AlertDialogCancel>\n )}\n <AlertDialogAction onClick={quotaExhaustedConfig?.onCta}>\n {quotaExhaustedConfig?.ctaLabel || \"Fazer upgrade\"}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n )}\n </div>\n )\n}\n\nexport { ChatThread }\n"]}
|
|
@@ -54,12 +54,23 @@ interface ChatThreadProps extends Omit<React.ComponentProps<"div">, "onChange">
|
|
|
54
54
|
onStopResponse?: () => void;
|
|
55
55
|
/** Carregar mensagens antigas (G1 — scroll infinito) */
|
|
56
56
|
onLoadMore?: () => Promise<void> | void;
|
|
57
|
-
/** Dispara quando o user pressiona o mic. Consumer chama getUserMedia + MediaRecorder.
|
|
58
|
-
|
|
57
|
+
/** Dispara quando o user pressiona o mic. Consumer chama getUserMedia + MediaRecorder.
|
|
58
|
+
* Pode retornar uma Promise: enquanto ela estiver pendente o MessageBar fica em
|
|
59
|
+
* `requesting` (mic com spinner) e so entra em `recording` quando resolver. Se
|
|
60
|
+
* rejeitar, volta pro estado anterior e dispara `onRecordingError`. Retorno sincrono
|
|
61
|
+
* (void) mantem o comportamento antigo: entra em `recording` imediatamente.
|
|
62
|
+
* Recomendado: `onStartRecording={() => startRecording()}` retornando a Promise do
|
|
63
|
+
* getUserMedia — evita mostrar "gravando" antes do microfone existir. */
|
|
64
|
+
onStartRecording?: () => void | Promise<unknown>;
|
|
59
65
|
/** Dispara quando o user pausa a gravacao. Consumer faz recorder.stop() + cria Blob. */
|
|
60
66
|
onPauseRecording?: () => void;
|
|
61
|
-
/** Dispara quando o user retoma gravacao apos pause.
|
|
62
|
-
|
|
67
|
+
/** Dispara quando o user retoma gravacao apos pause. Mesma semantica de Promise do
|
|
68
|
+
* `onStartRecording` (rejeitou → volta pra `paused`). */
|
|
69
|
+
onResumeRecording?: () => void | Promise<unknown>;
|
|
70
|
+
/** Dispara quando a Promise de `onStartRecording`/`onResumeRecording` rejeita (ex.:
|
|
71
|
+
* permissao de microfone negada). O ChatThread ja restaurou o estado do MessageBar;
|
|
72
|
+
* o consumer decide o feedback (toast, alert, banner). */
|
|
73
|
+
onRecordingError?: (error: unknown) => void;
|
|
63
74
|
/** Dispara quando o user cancela a gravacao. Consumer cleanup do MediaRecorder. */
|
|
64
75
|
onCancelRecording?: () => void;
|
|
65
76
|
/** Toggle play/pause do audio gravado durante o estado `paused` do MessageBar. */
|
|
@@ -139,6 +150,6 @@ interface ChatThreadProps extends Omit<React.ComponentProps<"div">, "onChange">
|
|
|
139
150
|
/** Default: "Desculpe, nao consegui responder." */
|
|
140
151
|
defaultAiErrorText?: string;
|
|
141
152
|
}
|
|
142
|
-
declare function ChatThread({ messages, state, initialThinking, onSendText, onSendAudio, onRetryMessage, onRegenerateResponse, onStopResponse, onLoadMore, onStartRecording, onPauseRecording, onResumeRecording, onCancelRecording, onTogglePlay, onSeekPlayback, recordingStream, recordingDuration, isPlaying, playbackProgress, offline, rateLimitedUntil, rateLimitBannerText, quotaExhausted, quotaExhaustedConfig, onRate, ratingLabels, placeholder, maxLength, audioOnlyMode, maxRecordingDuration, warnAtSecondsLeft, secondsLeftLabel, onMaxDurationReached, userAvatar, userName, userMessageTheme, offlineBannerText, retryButtonText, regenerateButtonText, stopResponseText, defaultAiErrorText, className, ...props }: ChatThreadProps): react_jsx_runtime.JSX.Element;
|
|
153
|
+
declare function ChatThread({ messages, state, initialThinking, onSendText, onSendAudio, onRetryMessage, onRegenerateResponse, onStopResponse, onLoadMore, onStartRecording, onPauseRecording, onResumeRecording, onRecordingError, onCancelRecording, onTogglePlay, onSeekPlayback, recordingStream, recordingDuration, isPlaying, playbackProgress, offline, rateLimitedUntil, rateLimitBannerText, quotaExhausted, quotaExhaustedConfig, onRate, ratingLabels, placeholder, maxLength, audioOnlyMode, maxRecordingDuration, warnAtSecondsLeft, secondsLeftLabel, onMaxDurationReached, userAvatar, userName, userMessageTheme, offlineBannerText, retryButtonText, regenerateButtonText, stopResponseText, defaultAiErrorText, className, ...props }: ChatThreadProps): react_jsx_runtime.JSX.Element;
|
|
143
154
|
|
|
144
155
|
export { ChatThread, type ChatThreadMessage, type ChatThreadMessageStatus, type ChatThreadPersona, type ChatThreadProps, type ChatThreadState, type QuotaExhaustedConfig };
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
export { ChatThread } from '../chunk-
|
|
2
|
+
export { ChatThread } from '../chunk-MQX4NBKQ.js';
|
|
3
3
|
import '../chunk-2KFWBA76.js';
|
|
4
4
|
import '../chunk-KSEDDFGG.js';
|
|
5
5
|
import '../chunk-RPU3FY62.js';
|
|
6
6
|
import '../chunk-YIOXPV2N.js';
|
|
7
|
-
import '../chunk-
|
|
7
|
+
import '../chunk-5AH2PNYW.js';
|
|
8
8
|
import '../chunk-T2TJG24M.js';
|
|
9
|
+
import '../chunk-TDBDX5NV.js';
|
|
9
10
|
import '../chunk-N2F3ZG2S.js';
|
|
10
11
|
import '../chunk-KLX547KZ.js';
|
|
11
12
|
import '../chunk-US5LXJTO.js';
|
package/dist/index.js
CHANGED
|
@@ -18,12 +18,12 @@ export { VideoPlayer } from './chunk-QXZRH3HU.js';
|
|
|
18
18
|
export { playerLocales } from './chunk-EFT7BHN7.js';
|
|
19
19
|
export { Toaster, cycleToast } from './chunk-7ZSICZXA.js';
|
|
20
20
|
export { Alert, AlertAction, AlertClose, AlertDescription, AlertTitle, alertVariants } from './chunk-PS3MV5G7.js';
|
|
21
|
-
export { ChatThread } from './chunk-
|
|
21
|
+
export { ChatThread } from './chunk-MQX4NBKQ.js';
|
|
22
22
|
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger } from './chunk-2KFWBA76.js';
|
|
23
23
|
export { ChatAudio, ChatMessage, chatMessageVariants } from './chunk-KSEDDFGG.js';
|
|
24
24
|
export { LikeDislike, likeDislikeVariants } from './chunk-RPU3FY62.js';
|
|
25
25
|
export { MessageRating } from './chunk-YIOXPV2N.js';
|
|
26
|
-
export { MessageBar } from './chunk-
|
|
26
|
+
export { MessageBar } from './chunk-5AH2PNYW.js';
|
|
27
27
|
export { SelectorBar } from './chunk-SKBY6OZL.js';
|
|
28
28
|
export { ChatPanel } from './chunk-OLYW5LVG.js';
|
|
29
29
|
import './chunk-7FJ7LRSV.js';
|
package/dist/ui/message-bar.d.ts
CHANGED
|
@@ -9,8 +9,11 @@ import * as React from 'react';
|
|
|
9
9
|
* - `disabled`: input desabilitado
|
|
10
10
|
* - `recording`: barra de gravacao (timer + waveform live + delete/pause/send)
|
|
11
11
|
* - `paused`: gravacao pausada (timer + waveform + play/delete/mic/send)
|
|
12
|
+
* - `requesting`: aguardando o consumer obter o microfone (getUserMedia pendente) —
|
|
13
|
+
* mic vira spinner desabilitado. O ChatThread entra aqui quando `onStartRecording`
|
|
14
|
+
* / `onResumeRecording` retornam Promise; consumers diretos podem usar tambem.
|
|
12
15
|
*/
|
|
13
|
-
type MessageBarState = "default" | "audio-only" | "active" | "disabled" | "recording" | "paused";
|
|
16
|
+
type MessageBarState = "default" | "audio-only" | "active" | "disabled" | "recording" | "paused" | "requesting";
|
|
14
17
|
interface MessageBarProps extends Omit<React.ComponentProps<"div">, "onChange"> {
|
|
15
18
|
/** Estado visual do componente (controlado pelo consumer) */
|
|
16
19
|
state?: MessageBarState;
|
package/dist/ui/message-bar.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
export { MessageBar } from '../chunk-
|
|
2
|
+
export { MessageBar } from '../chunk-5AH2PNYW.js';
|
|
3
|
+
import '../chunk-TDBDX5NV.js';
|
|
4
|
+
import '../chunk-KLX547KZ.js';
|
|
5
|
+
import '../chunk-US5LXJTO.js';
|
|
3
6
|
import '../chunk-LYTC7UMN.js';
|
|
4
7
|
import '../chunk-W4KJCBMW.js';
|
|
5
8
|
import '../chunk-2NMEKWO5.js';
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/composites/chat-thread.tsx"],"names":[],"mappings":"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","file":"chunk-CLMO3IRS.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Square } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ChatMessage } from \"@/components/ui/chat-message\"\nimport {\n MessageBar,\n type MessageBarState,\n} from \"@/components/ui/message-bar\"\nimport {\n MessageRating,\n DEFAULT_MESSAGE_RATING_LABELS,\n type MessageRatingValue,\n type MessageRatingLabels,\n type RatingLabel,\n} from \"@/components/ui/message-rating\"\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n} from \"@/components/ui/alert-dialog\"\n\n/* ─── Types ───────────────────────────────────────────────────── */\n\nexport type ChatThreadMessageStatus = \"sent\" | \"pending\" | \"failed\"\nexport type ChatThreadPersona = \"ai\" | \"user\" | \"system\"\n\nexport interface ChatThreadMessage {\n id: string\n persona: ChatThreadPersona\n status?: ChatThreadMessageStatus\n text?: string\n audioSrc?: string\n audioPeaks?: number[]\n /** Mensagem de erro pra exibir quando `persona=\"ai\"` e `status=\"failed\"` */\n errorText?: string\n /**\n * Quando true em uma mensagem da IA, renderiza o `MessageRating` (5 emojis) logo\n * abaixo da bubble — alinhado com a IA, parte da mesma \"linha visual\" da mensagem.\n * Apos o usuario responder, o rating some automaticamente (fade-out + slide-down).\n *\n * Usado quando a IA pede avaliacao da atividade: o consumer adiciona a msg da IA\n * com este campo `true` no array `messages` e ouve `onRate` para receber a resposta.\n */\n requestRating?: boolean\n}\n\nexport type ChatThreadState = \"idle\" | \"sending\" | \"thinking\" | \"error\"\n\nexport interface QuotaExhaustedConfig {\n title?: string\n description?: string\n ctaLabel?: string\n cancelLabel?: string\n onCta?: () => void\n onCancel?: () => void\n}\n\nexport interface ChatThreadProps extends Omit<React.ComponentProps<\"div\">, \"onChange\"> {\n /** Lista de mensagens da conversa */\n messages: ChatThreadMessage[]\n /** Estado global da conversa (controlled) */\n state?: ChatThreadState\n /** Quando true e thread vazio, monta direto em thinking (cenario Class — IA fala primeiro) */\n initialThinking?: boolean\n\n /* ─── Callbacks essenciais ─── */\n onSendText?: (text: string) => void\n /** Disparado quando user confirma envio do audio gravado (MessageBar dispara apos release/lock).\n * Consumer gerencia MediaRecorder e tem acesso ao Blob no momento que isso for chamado. */\n onSendAudio?: () => void\n /** Retry de mensagem do usuario que falhou enviar (B2) */\n onRetryMessage?: (messageId: string) => void\n /** Regenerar resposta da IA com erro (B3) */\n onRegenerateResponse?: () => void\n /** Cancelar resposta da IA mid-thinking (F1). Passar este callback **ativa** o botao\n * \"Parar resposta\" no rodape durante o estado `thinking`. Se omitido, o MessageBar\n * some com animacao de saida quando a IA esta pensando e volta com animacao de entrada\n * quando termina — o consumer indica que nao quer expor cancelamento. */\n onStopResponse?: () => void\n /** Carregar mensagens antigas (G1 — scroll infinito) */\n onLoadMore?: () => Promise<void> | void\n\n /* ─── Callbacks do MessageBar (pass-through) ─── */\n /** Dispara quando o user pressiona o mic. Consumer chama getUserMedia + MediaRecorder. */\n onStartRecording?: () => void\n /** Dispara quando o user pausa a gravacao. Consumer faz recorder.stop() + cria Blob. */\n onPauseRecording?: () => void\n /** Dispara quando o user retoma gravacao apos pause. */\n onResumeRecording?: () => void\n /** Dispara quando o user cancela a gravacao. Consumer cleanup do MediaRecorder. */\n onCancelRecording?: () => void\n /** Toggle play/pause do audio gravado durante o estado `paused` do MessageBar. */\n onTogglePlay?: () => void\n /** Callback de seek no waveform do audio gravado (durante paused). */\n onSeekPlayback?: (progress: number) => void\n\n /* ─── State do MessageBar (pass-through) ─── */\n /** Stream do microfone durante recording — usado pelo MessageBar pra renderizar o live waveform. */\n recordingStream?: MediaStream | null\n /** Duracao da gravacao em segundos (controlado pelo consumer via setInterval). */\n recordingDuration?: number\n /** Indica se o audio gravado esta tocando (durante paused). */\n isPlaying?: boolean\n /** Progresso de playback (0-1) — usado pra sincronizar dot verde no waveform. */\n playbackProgress?: number\n\n /* ─── Erro states (configuravel) ─── */\n /** Mostra banner de offline + disabled MessageBar (C1) */\n offline?: boolean\n /** Timestamp epoch ate quando bloquear envio (C2). null/undefined = sem rate limit */\n rateLimitedUntil?: number | null\n /** Texto do banner de rate limit. Default: \"Aguarde {s}s antes de enviar nova mensagem\" */\n rateLimitBannerText?: (secondsRemaining: number) => string\n /** Modal de cota esgotada (C3) */\n quotaExhausted?: boolean\n quotaExhaustedConfig?: QuotaExhaustedConfig\n\n /* ─── Rating (J) ─── */\n /**\n * Callback disparado quando o usuario responde um rating de uma mensagem com `requestRating=true`.\n * Recebe:\n * - `messageId`: id da mensagem da IA que pediu avaliacao\n * - `value`: numero 1-5\n * - `label`: objeto com emoji/title/subtitle correspondente ao value\n *\n * O consumer tipicamente usa esses dados para adicionar uma nova mensagem do usuario no array\n * `messages` com o texto da escolha (ex: `label.subtitle` em PT-BR). O `MessageRating` some\n * imediatamente apos o clique (sem estado \"selecionado\" visual).\n */\n onRate?: (messageId: string, value: MessageRatingValue, label: RatingLabel) => void\n /**\n * Labels customizadas do `MessageRating` (sobrescreve PT-BR defaults).\n * Passado pro componente E usado no callback `onRate` ao montar o `label`.\n */\n ratingLabels?: Partial<MessageRatingLabels>\n\n /* ─── Configuracoes do MessageBar ─── */\n placeholder?: string\n maxLength?: number\n /** Modo so audio — passa pro MessageBar como state base */\n audioOnlyMode?: boolean\n\n /* ─── Limite de duracao da gravacao de audio ─── */\n /**\n * Limite de duracao da gravacao em segundos (configurado pelo consumer).\n * Quando atingido, o ChatThread auto-pausa a gravacao (vai pro paused state com\n * preview play/cancel/send) — a menos que `onMaxDurationReached` seja passado,\n * caso em que o consumer assume controle. Sem essa prop, sem limite.\n */\n maxRecordingDuration?: number\n /** Quantos segundos antes do limite ativar o warning visual (timer destructive\n * + label \"Xs restantes\"). Default 10. Ignorado sem `maxRecordingDuration`. */\n warnAtSecondsLeft?: number\n /** Label do warning. Default pt-BR: `(s) => \\`${s}s restantes\\``. */\n secondsLeftLabel?: (secondsLeft: number) => string\n /**\n * Callback opcional disparado ao atingir o limite. Se omitido, ChatThread\n * auto-pausa internamente. Se passado, **substitui** o auto-pause — o consumer\n * decide o que fazer (ex: mostrar toast antes de pausar manualmente).\n */\n onMaxDurationReached?: () => void\n\n /* ─── Identidade do usuario ─── */\n /** URL da imagem do avatar do usuario (aparece em todas as msgs do user no desktop). */\n userAvatar?: string\n /** Nome do usuario (usado para iniciais no fallback do avatar e alt da imagem). */\n userName?: string\n /** Classe de tema das bolhas do user (pass-through pro ChatMessage). Default \"theme-positive\". */\n userMessageTheme?: string\n\n /* ─── i18n / texto customizavel ─── */\n /** Default: \"Sem conexao. Tentando reconectar...\" */\n offlineBannerText?: string\n /** Default: \"Tentar novamente\" */\n retryButtonText?: string\n /** Default: \"Regenerar resposta\" */\n regenerateButtonText?: string\n /** Default: \"Parar resposta\" */\n stopResponseText?: string\n /** Default: \"Desculpe, nao consegui responder.\" */\n defaultAiErrorText?: string\n}\n\n/* ─── Constantes ──────────────────────────────────────────────── */\n\nconst SCROLL_BOTTOM_THRESHOLD = 100 // px do bottom pra considerar \"no fundo\"\nconst LOAD_MORE_THRESHOLD = 200 // px do topo pra disparar onLoadMore\n\n/* ─── Sub-componentes internos ────────────────────────────────── */\n\n/** Banner persistente no topo (offline, rate limit, etc.) */\nfunction ChatThreadBanner({\n variant,\n children,\n}: {\n variant: \"offline\" | \"rate-limit\"\n children: React.ReactNode\n}) {\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={cn(\n \"shrink-0 flex items-center gap-2 px-4 py-2 text-sm border-b\",\n variant === \"offline\" &&\n \"bg-destructive/10 text-destructive border-destructive/20\",\n variant === \"rate-limit\" &&\n \"bg-muted text-muted-foreground border-border\"\n )}\n >\n {children}\n </div>\n )\n}\n\n/** Conta segundos restantes ate `until` (timestamp epoch). */\nfunction useCountdown(until: number | null | undefined): number {\n const [now, setNow] = React.useState(() => Date.now())\n\n React.useEffect(() => {\n if (!until || until <= Date.now()) return\n const id = setInterval(() => setNow(Date.now()), 1000)\n return () => clearInterval(id)\n }, [until])\n\n if (!until) return 0\n const remainingMs = until - now\n return Math.max(0, Math.ceil(remainingMs / 1000))\n}\n\n/** Botao \"Parar resposta\" — substitui MessageBar durante thinking (F1) */\nfunction StopResponseButton({\n onClick,\n label = \"Parar resposta\",\n}: {\n onClick?: () => void\n label?: string\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"w-full flex items-center justify-center gap-2 h-12 rounded-2xl bg-muted text-neutral-foreground hover:bg-muted/80 transition-colors focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50\"\n aria-label={label}\n >\n <Square className=\"size-4\" fill=\"currentColor\" strokeWidth={0} />\n <span className=\"text-sm font-medium\">{label}</span>\n </button>\n )\n}\n\n/** Bubble wrapper com animacao de entrada (E1: fade-in + slide-up 8px, 200ms) */\nfunction MessageBubbleAnimated({\n children,\n enableAnimation,\n}: {\n children: React.ReactNode\n enableAnimation: boolean\n}) {\n return (\n <div\n className={cn(\n \"transition-[opacity,transform] duration-200 ease-out\",\n enableAnimation ? \"animate-thread-msg-enter\" : \"\"\n )}\n >\n {children}\n </div>\n )\n}\n\n/* ─── Bottom slot: gerencia animacao sequencial entre MessageBar / StopButton / hidden ─── */\n\ntype BottomSlotKind = \"messagebar\" | \"stop\" | \"hidden\"\nconst LEAVE_DURATION_MS = 150\n\n/**\n * Slot que alterna entre 3 estados com animacao sequencial (sem cross-fade):\n * o conteudo atual anima saida (`thread-msg-leave`, 150ms), troca o conteudo,\n * e o novo anima entrada (`thread-msg-enter`, 200ms). Similar ao\n * AnimatePresence mode=\"wait\" do Framer Motion, mas em CSS puro.\n */\nfunction BottomSlot({\n kind,\n messageBar,\n stopButton,\n}: {\n kind: BottomSlotKind\n messageBar: React.ReactNode\n stopButton: React.ReactNode\n}) {\n const [rendered, setRendered] = React.useState<BottomSlotKind>(kind)\n const [phase, setPhase] = React.useState<\"enter\" | \"leave\">(\"enter\")\n\n React.useEffect(() => {\n if (rendered === kind) return\n setPhase(\"leave\")\n const t = setTimeout(() => {\n setRendered(kind)\n setPhase(\"enter\")\n }, LEAVE_DURATION_MS)\n return () => clearTimeout(t)\n }, [kind, rendered])\n\n return (\n <div className=\"shrink-0 px-4 pb-4 pt-2\">\n {rendered !== \"hidden\" && (\n <div\n key={rendered}\n className={\n phase === \"enter\" ? \"animate-thread-msg-enter\" : \"animate-thread-msg-leave\"\n }\n >\n {rendered === \"messagebar\" ? messageBar : stopButton}\n </div>\n )}\n </div>\n )\n}\n\n/* ─── Component principal ─────────────────────────────────────── */\n\nfunction ChatThread({\n messages,\n state = \"idle\",\n initialThinking = false,\n onSendText,\n onSendAudio,\n onRetryMessage,\n onRegenerateResponse,\n onStopResponse,\n onLoadMore,\n onStartRecording,\n onPauseRecording,\n onResumeRecording,\n onCancelRecording,\n onTogglePlay,\n onSeekPlayback,\n recordingStream,\n recordingDuration,\n isPlaying,\n playbackProgress,\n offline = false,\n rateLimitedUntil = null,\n rateLimitBannerText,\n quotaExhausted = false,\n quotaExhaustedConfig,\n onRate,\n ratingLabels,\n placeholder,\n maxLength,\n audioOnlyMode = false,\n maxRecordingDuration,\n warnAtSecondsLeft,\n secondsLeftLabel,\n onMaxDurationReached,\n userAvatar,\n userName,\n userMessageTheme,\n offlineBannerText = \"Sem conexao. Tentando reconectar...\",\n retryButtonText = \"Tentar novamente\",\n regenerateButtonText = \"Regenerar resposta\",\n stopResponseText = \"Parar resposta\",\n defaultAiErrorText = \"Desculpe, nao consegui responder.\",\n className,\n ...props\n}: ChatThreadProps) {\n /* ─── State derivado ─── */\n const rateLimitSecondsLeft = useCountdown(rateLimitedUntil)\n const isRateLimited = rateLimitSecondsLeft > 0\n const isThinking =\n state === \"thinking\" || (initialThinking && messages.length === 0)\n const isDisabled = offline || isRateLimited || quotaExhausted\n\n /* ─── Refs e scroll inteligente (D1) ─── */\n const scrollRef = React.useRef<HTMLDivElement>(null)\n const [isAtBottom, setIsAtBottom] = React.useState(true)\n const prevMessageCountRef = React.useRef(messages.length)\n const loadMoreInFlightRef = React.useRef(false)\n const [isLoadingMore, setIsLoadingMore] = React.useState(false)\n // Marca de quando o componente montou — usada para suprimir animacao das mensagens iniciais\n const mountedRef = React.useRef(false)\n React.useEffect(() => {\n mountedRef.current = true\n }, [])\n\n const checkScrollPosition = React.useCallback(() => {\n const el = scrollRef.current\n if (!el) return\n const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight\n const atBottom = distanceFromBottom <= SCROLL_BOTTOM_THRESHOLD\n setIsAtBottom(atBottom)\n }, [])\n\n const scrollToBottom = React.useCallback((smooth = true) => {\n const el = scrollRef.current\n if (!el) return\n el.scrollTo({\n top: el.scrollHeight,\n behavior: smooth ? \"smooth\" : \"auto\",\n })\n }, [])\n\n /* ─── Auto-scroll quando chega nova mensagem ─── */\n React.useEffect(() => {\n const prev = prevMessageCountRef.current\n const curr = messages.length\n prevMessageCountRef.current = curr\n\n if (curr <= prev) return\n\n // Esta no fundo → scroll suave pra nova msg. Se o usuario subiu, preserva a\n // posicao (sem botao flutuante — ele sobrepunha o MessageBar).\n if (isAtBottom) {\n requestAnimationFrame(() => scrollToBottom(true))\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [messages.length])\n\n /* ─── Scroll handler (detecta posicao + paginacao) ─── */\n React.useEffect(() => {\n const el = scrollRef.current\n if (!el) return\n const handler = () => {\n checkScrollPosition()\n // Paginacao: se chegou perto do topo e ha onLoadMore, dispara\n if (\n onLoadMore &&\n !loadMoreInFlightRef.current &&\n el.scrollTop <= LOAD_MORE_THRESHOLD\n ) {\n loadMoreInFlightRef.current = true\n setIsLoadingMore(true)\n const prevScrollHeight = el.scrollHeight\n Promise.resolve(onLoadMore())\n .then(() => {\n // Preserva posicao visual apos prepend de mensagens antigas\n requestAnimationFrame(() => {\n const newScrollHeight = el.scrollHeight\n el.scrollTop = newScrollHeight - prevScrollHeight\n })\n })\n .finally(() => {\n loadMoreInFlightRef.current = false\n setIsLoadingMore(false)\n })\n }\n }\n el.addEventListener(\"scroll\", handler, { passive: true })\n // Inicial: garante scroll no fundo\n requestAnimationFrame(() => scrollToBottom(false))\n return () => el.removeEventListener(\"scroll\", handler)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [onLoadMore])\n\n /* ─── Aria-live announce de nova msg da IA (H1) ─── */\n const lastAiMessage = React.useMemo(() => {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].persona === \"ai\") return messages[i]\n }\n return null\n }, [messages])\n\n /* ─── Rating per-message: rastreia IDs cujo rating foi respondido (auto-dismiss imediato) ─── */\n const [ratedMessageIds, setRatedMessageIds] = React.useState<Set<string>>(() => new Set())\n\n // Merge dos labels customizados com defaults PT-BR. Usado pra renderizar E pra montar o callback.\n const mergedRatingLabels: MessageRatingLabels = React.useMemo(\n () => ({ ...DEFAULT_MESSAGE_RATING_LABELS, ...ratingLabels }),\n [ratingLabels]\n )\n\n const handleRateMessage = React.useCallback(\n (messageId: string, value: MessageRatingValue) => {\n // Marca como respondido IMEDIATAMENTE — re-render remove o rating do DOM antes\n // do paint visual de \"selected\", entao o usuario nao ve o estado intermediario.\n setRatedMessageIds((prev) => {\n const next = new Set(prev)\n next.add(messageId)\n return next\n })\n // Dispara callback com label completo (emoji/title/subtitle).\n onRate?.(messageId, value, mergedRatingLabels[value])\n },\n [onRate, mergedRatingLabels]\n )\n\n /* ─── MessageBar state interno (state machine de gravacao) ─── */\n const baseMessageBarState: MessageBarState = audioOnlyMode ? \"audio-only\" : \"default\"\n const [internalRecordingState, setInternalRecordingState] = React.useState<\n \"idle\" | \"recording\" | \"paused\"\n >(\"idle\")\n\n // Reset pra idle quando muda audioOnlyMode e nao esta gravando\n React.useEffect(() => {\n if (internalRecordingState === \"idle\") return\n // mantem o state se gravacao em andamento\n }, [audioOnlyMode, internalRecordingState])\n\n // State final do MessageBar — combina base, recording state e disabled\n const messageBarState: MessageBarState = isDisabled\n ? \"disabled\"\n : internalRecordingState === \"recording\"\n ? \"recording\"\n : internalRecordingState === \"paused\"\n ? \"paused\"\n : baseMessageBarState\n\n /* Wrappers dos callbacks: atualizam state interno + chamam consumer */\n const handleStartRecordingInternal = React.useCallback(() => {\n setInternalRecordingState(\"recording\")\n onStartRecording?.()\n }, [onStartRecording])\n\n const handlePauseRecordingInternal = React.useCallback(() => {\n setInternalRecordingState(\"paused\")\n onPauseRecording?.()\n }, [onPauseRecording])\n\n const handleResumeRecordingInternal = React.useCallback(() => {\n setInternalRecordingState(\"recording\")\n onResumeRecording?.()\n }, [onResumeRecording])\n\n const handleCancelRecordingInternal = React.useCallback(() => {\n setInternalRecordingState(\"idle\")\n onCancelRecording?.()\n }, [onCancelRecording])\n\n const handleSendAudioInternal = React.useCallback(() => {\n setInternalRecordingState(\"idle\")\n onSendAudio?.()\n }, [onSendAudio])\n\n /* Auto-pause ao atingir limite de duracao. Se consumer passou onMaxDurationReached,\n delega controle pra ele (sem auto-pause). */\n const handleMaxDurationInternal = React.useCallback(() => {\n if (onMaxDurationReached) {\n onMaxDurationReached()\n } else {\n handlePauseRecordingInternal()\n }\n }, [onMaxDurationReached, handlePauseRecordingInternal])\n\n /* Input controlled — sem isso o MessageBar fica \"engessado\" em value=\"\"\n (o consumer recebe o texto via onSendText, mas nao precisa controlar o input). */\n const [inputValue, setInputValue] = React.useState(\"\")\n const handleSendTextInternal = React.useCallback(\n (text: string) => {\n setInputValue(\"\")\n onSendText?.(text)\n },\n [onSendText]\n )\n\n /* ─── Render ──────────────────────────────────────── */\n return (\n <div\n data-slot=\"chat-thread\"\n className={cn(\"relative flex flex-col h-full bg-background\", className)}\n {...props}\n >\n {/* Banners de erro (topo) */}\n {offline && (\n <ChatThreadBanner variant=\"offline\">\n <span aria-hidden=\"true\">⚠</span>\n <span>{offlineBannerText}</span>\n </ChatThreadBanner>\n )}\n {isRateLimited && (\n <ChatThreadBanner variant=\"rate-limit\">\n <span aria-hidden=\"true\">⏱</span>\n <span>\n {rateLimitBannerText\n ? rateLimitBannerText(rateLimitSecondsLeft)\n : `Aguarde ${rateLimitSecondsLeft}s antes de enviar nova mensagem`}\n </span>\n </ChatThreadBanner>\n )}\n\n {/* Thread scrollavel */}\n <div\n ref={scrollRef}\n // Scrollbar oculta (Firefox + WebKit/Chromium); scroll segue funcionando\n className=\"flex-1 overflow-y-auto px-4 py-4 space-y-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\n role=\"log\"\n aria-live=\"polite\"\n aria-atomic=\"false\"\n >\n {/* Spinner de paginacao no topo (G2) */}\n {isLoadingMore && (\n <div className=\"flex justify-center py-2\" aria-label=\"Carregando mensagens anteriores\">\n <span className=\"inline-block size-5 border-2 border-muted-foreground/30 border-t-muted-foreground rounded-full animate-spin\" />\n </div>\n )}\n\n {messages.map((msg, idx) => {\n // Suprime animacao das mensagens que ja existiam no mount inicial\n const enableAnim =\n mountedRef.current && idx >= prevMessageCountRef.current - 1\n\n const isFailedUser = msg.persona === \"user\" && msg.status === \"failed\"\n const isFailedAi = msg.persona === \"ai\" && msg.status === \"failed\"\n const isPendingUser = msg.persona === \"user\" && msg.status === \"pending\"\n\n return (\n <MessageBubbleAnimated key={msg.id} enableAnimation={enableAnim}>\n <ChatMessage\n persona={msg.persona}\n text={msg.text}\n audioSrc={msg.audioSrc}\n avatar={msg.persona === \"user\" ? userAvatar : undefined}\n author={msg.persona === \"user\" ? userName : undefined}\n theme={userMessageTheme}\n // Indicador de pending: typing dots no lugar do conteudo normal e visual sutil\n loading={isPendingUser && !msg.text && !msg.audioSrc}\n className={cn(\n isPendingUser && \"opacity-80\",\n isFailedUser && \"[&_[data-slot=chat-message-bubble]]:border-2 [&_[data-slot=chat-message-bubble]]:border-destructive\"\n )}\n />\n {/* Retry inline pra msg do user que falhou (B2) */}\n {isFailedUser && (\n <div className=\"mt-2 flex justify-end\">\n <button\n type=\"button\"\n onClick={() => onRetryMessage?.(msg.id)}\n className=\"inline-flex items-center gap-1 text-xs text-destructive hover:underline focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 rounded\"\n >\n ↻ {retryButtonText}\n </button>\n </div>\n )}\n {/* Bubble de erro pra resposta da IA (B3) */}\n {isFailedAi && (\n /* Alinhado com a bubble da IA: offset = badge 32px + gap 12px = 44px (desktop).\n Mobile sem offset porque o badge fica oculto no ChatMessage AI. */\n <div className=\"mt-2 sm:pl-11 flex flex-col items-start gap-2\">\n <div className=\"text-sm text-destructive\">\n {msg.errorText || defaultAiErrorText}\n </div>\n <button\n type=\"button\"\n onClick={onRegenerateResponse}\n className=\"inline-flex items-center gap-1 text-xs text-primary hover:underline focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 rounded\"\n >\n ↻ {regenerateButtonText}\n </button>\n </div>\n )}\n {/* MessageRating inline na mensagem da IA que pede avaliacao (J).\n Alinhado com a bubble da IA (offset 44px desktop, 0 mobile).\n value={null} mantem o componente sem estado \"selecionado\" visual —\n ao clicar, o ChatThread marca como rated imediatamente (re-render remove\n do DOM antes do paint), e o consumer recebe (messageId, value, label). */}\n {msg.persona === \"ai\" && msg.requestRating && !ratedMessageIds.has(msg.id) && (\n <div className=\"mt-3 sm:pl-11 animate-thread-msg-enter\">\n <MessageRating\n value={null}\n labels={ratingLabels}\n onChange={(value) => handleRateMessage(msg.id, value)}\n />\n </div>\n )}\n </MessageBubbleAnimated>\n )\n })}\n\n {/* Typing indicator da IA durante thinking */}\n {isThinking && (\n <MessageBubbleAnimated enableAnimation>\n <ChatMessage persona=\"ai\" loading />\n </MessageBubbleAnimated>\n )}\n\n {/* Aria-live oculto pra anunciar ultima msg da IA */}\n <div className=\"sr-only\" aria-live=\"polite\" aria-atomic=\"true\">\n {lastAiMessage?.text || \"\"}\n </div>\n </div>\n\n {/* Bottom area: MessageBar / StopResponse / hidden.\n O botao \"Parar resposta\" so aparece durante thinking E quando o consumer passa\n `onStopResponse` — quem nao implementa cancelamento ve o MessageBar sumir com\n animacao de saida e voltar com animacao de entrada quando a IA termina. */}\n <BottomSlot\n kind={\n isThinking\n ? onStopResponse\n ? \"stop\"\n : \"hidden\"\n : \"messagebar\"\n }\n messageBar={\n <MessageBar\n state={messageBarState}\n value={inputValue}\n onChange={setInputValue}\n onSendText={handleSendTextInternal}\n onSendAudio={handleSendAudioInternal}\n onStartRecording={handleStartRecordingInternal}\n onPauseRecording={handlePauseRecordingInternal}\n onResumeRecording={handleResumeRecordingInternal}\n onCancelRecording={handleCancelRecordingInternal}\n onTogglePlay={onTogglePlay}\n onSeekPlayback={onSeekPlayback}\n recordingStream={recordingStream}\n recordingDuration={recordingDuration}\n isPlaying={isPlaying}\n playbackProgress={playbackProgress}\n placeholder={placeholder}\n maxRecordingDuration={maxRecordingDuration}\n warnAtSecondsLeft={warnAtSecondsLeft}\n secondsLeftLabel={secondsLeftLabel}\n onMaxDurationReached={handleMaxDurationInternal}\n />\n }\n stopButton={\n <StopResponseButton onClick={onStopResponse} label={stopResponseText} />\n }\n />\n\n {/* Quota exhausted modal (C3) */}\n {quotaExhausted && (\n <AlertDialog open>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {quotaExhaustedConfig?.title || \"Limite atingido\"}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {quotaExhaustedConfig?.description ||\n \"Voce atingiu o limite de mensagens do seu plano. Faca upgrade para continuar.\"}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n {quotaExhaustedConfig?.onCancel && (\n <AlertDialogCancel onClick={quotaExhaustedConfig.onCancel}>\n {quotaExhaustedConfig.cancelLabel || \"Cancelar\"}\n </AlertDialogCancel>\n )}\n <AlertDialogAction onClick={quotaExhaustedConfig?.onCta}>\n {quotaExhaustedConfig?.ctaLabel || \"Fazer upgrade\"}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n )}\n </div>\n )\n}\n\nexport { ChatThread }\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ui/message-bar.tsx"],"names":["_a","_b"],"mappings":"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","file":"chunk-JPEQ4BYK.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Mic, Send, Trash2, Pause, Play, Lock, ChevronUp, ChevronLeft } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* ─── Types ───────────────────────────────────────────────────── */\n\n/**\n * Estados visuais do MessageBar:\n * - `default`: input texto + botao mic (estado inicial, sem texto digitado)\n * - `audio-only`: apenas botao mic centralizado (modo so audio)\n * - `active`: input texto com conteudo digitado + botao send\n * - `disabled`: input desabilitado\n * - `recording`: barra de gravacao (timer + waveform live + delete/pause/send)\n * - `paused`: gravacao pausada (timer + waveform + play/delete/mic/send)\n */\nexport type MessageBarState =\n | \"default\"\n | \"audio-only\"\n | \"active\"\n | \"disabled\"\n | \"recording\"\n | \"paused\"\n\nexport interface MessageBarProps extends Omit<React.ComponentProps<\"div\">, \"onChange\"> {\n /** Estado visual do componente (controlado pelo consumer) */\n state?: MessageBarState\n /** Valor do input de texto */\n value?: string\n /** Callback ao digitar */\n onChange?: (value: string) => void\n /** Callback ao enviar texto (Enter ou clique no botao send) */\n onSendText?: (text: string) => void\n /** Inicia gravacao (consumer chama MediaRecorder.start) */\n onStartRecording?: () => void\n /** Pausa gravacao */\n onPauseRecording?: () => void\n /** Retoma gravacao */\n onResumeRecording?: () => void\n /** Cancela/deleta gravacao */\n onCancelRecording?: () => void\n /** Envia o audio gravado */\n onSendAudio?: () => void\n /** Toggle play/pause do audio gravado durante o estado `paused` */\n onTogglePlay?: () => void\n /** Duracao da gravacao em segundos (controlado pelo consumer) */\n recordingDuration?: number\n /** Indica se esta tocando o audio (durante state=paused) */\n isPlaying?: boolean\n /**\n * MediaStream do microfone (durante state=recording). Quando fornecido,\n * o componente analisa em tempo real e renderiza as barras conforme a voz.\n * Sem stream → fallback para waveform decorativo.\n *\n * Como usar:\n * ```ts\n * const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\n * const recorder = new MediaRecorder(stream)\n * // Passa o MESMO stream pro MessageBar:\n * <MessageBar state=\"recording\" recordingStream={stream} ... />\n * ```\n */\n recordingStream?: MediaStream | null\n /**\n * Progresso do playback do audio gravado (0-1), usado durante state=paused.\n * Quando fornecido, o waveform mostra barras tocadas/nao tocadas + dot verde\n * sincronizado. Sem isso, o waveform mostra todas as barras como nao tocadas.\n *\n * Alimente via `requestAnimationFrame` enquanto o audio toca, NAO so via\n * `timeupdate` — esse evento dispara ~4x/s (250ms) e o dot pula em stop motion.\n * Ver exemplo na doc do MessageBar.\n */\n playbackProgress?: number\n /**\n * Callback ao clicar/arrastar no waveform durante state=paused.\n * Recebe o novo progresso (0-1); o consumer deve setar currentTime do audio.\n */\n onSeekPlayback?: (progress: number) => void\n /** Placeholder do input */\n placeholder?: string\n /**\n * Limite de duracao da gravacao em segundos. Quando omitido, sem limite\n * (comportamento default). Quando atingido, o componente dispara\n * `onMaxDurationReached` UMA VEZ — o ChatThread usa esse callback pra auto-pausar;\n * consumers diretos do MessageBar podem usar pra customizar.\n */\n maxRecordingDuration?: number\n /**\n * Quantos segundos antes do limite ativar o warning visual\n * (timer em vermelho + label \"Xs restantes\" abaixo). Default: 10.\n * Ignorado se `maxRecordingDuration` nao for passado.\n */\n warnAtSecondsLeft?: number\n /**\n * Callback disparado uma unica vez quando `recordingDuration` >= `maxRecordingDuration`.\n * Reseta automaticamente quando o componente sai do estado `recording`.\n */\n onMaxDurationReached?: () => void\n /**\n * Label do warning inline. Recebe quantos segundos restam, retorna string.\n * Default pt-BR: `(s) => \\`${s}s restantes\\``.\n */\n secondsLeftLabel?: (secondsLeft: number) => string\n}\n\n/* ─── Helpers ─────────────────────────────────────────────────── */\n\nfunction formatDuration(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\n/** Calcula estado de warning do timer durante gravacao.\n * - `isWarning` vira true quando faltam `warnAt` segundos ou menos.\n * - `secondsLeft` = max(0, max - elapsed).\n * Sem `max` ou valores invalidos, isWarning=false e secondsLeft=Infinity. */\nfunction getWarningState(\n elapsed: number,\n max: number | undefined,\n warnAt: number\n): { isWarning: boolean; secondsLeft: number } {\n if (!max || max <= 0) return { isWarning: false, secondsLeft: Infinity }\n const secondsLeft = Math.max(0, max - elapsed)\n return { isWarning: secondsLeft <= warnAt, secondsLeft }\n}\n\nconst DEFAULT_WARN_AT_SECONDS_LEFT = 10\nconst DEFAULT_SECONDS_LEFT_LABEL = (s: number) => `${s}s restantes`\n\n/* ─── Sub-componentes ─────────────────────────────────────────── */\n\n/** Botao mic vermelho coral (theme-brand) — usado em default, audio-only e durante press.\n * - `variant=\"large\"` aumenta para 64x64 (overlay durante press)\n * - `pressed` aplica scale-110 + opacity 90\n * - Aceita pointer events para suportar press-and-hold (mobile WhatsApp-style) */\nfunction MicButton({\n onClick,\n onPointerDown,\n onPointerMove,\n onPointerUp,\n onPointerCancel,\n size = \"default\",\n variant = \"default\",\n pressed = false,\n ariaLabel = \"Gravar audio\",\n className,\n style,\n}: {\n onClick?: () => void\n onPointerDown?: (e: React.PointerEvent<HTMLButtonElement>) => void\n onPointerMove?: (e: React.PointerEvent<HTMLButtonElement>) => void\n onPointerUp?: (e: React.PointerEvent<HTMLButtonElement>) => void\n onPointerCancel?: (e: React.PointerEvent<HTMLButtonElement>) => void\n size?: \"default\" | \"sm\"\n variant?: \"default\" | \"large\"\n pressed?: boolean\n ariaLabel?: string\n className?: string\n style?: React.CSSProperties\n}) {\n const buttonRef = React.useRef<HTMLButtonElement>(null)\n const hasPressFlow = !!onPointerDown\n\n /* touchstart non-passive — marca o gesto como NAO scroll desde o primeiro evento.\n * Critico pra Android Chrome (Blink): se o touchstart for passive (default no React),\n * o navegador classifica o gesto como \"potencialmente scroll\" e ignora preventDefault\n * posterior. Listener via addEventListener pra poder usar { passive: false }. */\n React.useEffect(() => {\n const btn = buttonRef.current\n if (!btn || !hasPressFlow) return\n const onTouchStart = (e: TouchEvent) => {\n e.preventDefault()\n }\n btn.addEventListener(\"touchstart\", onTouchStart, { passive: false })\n return () => btn.removeEventListener(\"touchstart\", onTouchStart)\n }, [hasPressFlow])\n\n const sizeClass =\n variant === \"large\" ? \"size-16\" : size === \"default\" ? \"size-10\" : \"size-8\"\n const radiusClass = variant === \"large\" ? \"rounded-lg\" : \"rounded-md\"\n return (\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={onClick}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerCancel}\n aria-pressed={pressed || undefined}\n className={cn(\n \"shrink-0 inline-flex items-center justify-center bg-primary text-primary-foreground theme-brand transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n sizeClass,\n radiusClass,\n pressed && \"opacity-90 select-none\",\n // touch-none aplicado sempre que ha press flow (nao so quando ja esta pressed),\n // pra Android Chrome nao classificar o touchstart como scroll\n (hasPressFlow || pressed) && \"touch-none\",\n className\n )}\n style={style}\n aria-label={ariaLabel}\n >\n <Mic className={variant === \"large\" ? \"size-6\" : \"size-5\"} />\n </button>\n )\n}\n\n/** Botao send verde (theme-positive) */\nfunction SendButton({\n onClick,\n disabled,\n ariaLabel = \"Enviar\",\n}: {\n onClick?: () => void\n disabled?: boolean\n ariaLabel?: string\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className=\"shrink-0 inline-flex items-center justify-center size-10 rounded-md bg-primary text-primary-foreground theme-positive transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:opacity-50 disabled:cursor-not-allowed\"\n aria-label={ariaLabel}\n >\n <Send className=\"size-5\" fill=\"currentColor\" strokeWidth={0} />\n </button>\n )\n}\n\n/** Botao icon-only neutral (trash, pause, play, mic durante paused) */\nfunction IconButton({\n onClick,\n icon: Icon,\n ariaLabel,\n filled = false,\n}: {\n onClick?: () => void\n icon: React.ComponentType<{ className?: string; fill?: string; strokeWidth?: number }>\n ariaLabel: string\n filled?: boolean\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"shrink-0 inline-flex items-center justify-center size-10 rounded-md text-neutral-foreground hover:bg-muted/50 transition-colors focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50\"\n aria-label={ariaLabel}\n >\n <Icon\n className=\"size-5\"\n {...(filled ? { fill: \"currentColor\", strokeWidth: 0 } : {})}\n />\n </button>\n )\n}\n\n/** Indicador de gravacao (red dot pulsando) */\nfunction RecordingIndicator() {\n return (\n <span className=\"shrink-0 relative inline-flex items-center justify-center size-3\" aria-label=\"Gravando\">\n <span className=\"absolute inset-0 rounded-full bg-primary theme-brand animate-ping opacity-75\" />\n <span className=\"relative size-2.5 rounded-full bg-primary theme-brand\" />\n </span>\n )\n}\n\n/** Helper: agrega N samples brutos em K barras (pico por chunk). Quando samples.length\n * < barCount, \"estica\" via lookup proporcional (cada barra pega o sample mais proximo). */\nfunction aggregateSamples(samples: number[], barCount: number): number[] {\n if (samples.length === 0 || barCount === 0) return new Array(barCount).fill(0)\n const result = new Array(barCount).fill(0)\n // Poucos samples: estica via lookup proporcional\n if (samples.length < barCount) {\n for (let i = 0; i < barCount; i++) {\n const idx = Math.floor((i / barCount) * samples.length)\n result[i] = samples[idx] ?? 0\n }\n return result\n }\n // Muitos samples: agrega pegando o pico (max) do chunk\n const chunkSize = samples.length / barCount\n for (let i = 0; i < barCount; i++) {\n const start = Math.floor(i * chunkSize)\n const end = Math.floor((i + 1) * chunkSize)\n let max = 0\n for (let j = start; j < end && j < samples.length; j++) {\n if (samples[j] > max) max = samples[j]\n }\n result[i] = max\n }\n return result\n}\n\n/** LiveWaveform — analise em tempo real do microfone via AnalyserNode.\n * Comportamento estilo WhatsApp:\n * - **Preenche toda a largura disponivel** (ResizeObserver detecta width e calcula limite max de barras)\n * - Cada bar = 2px wide, 2px gap (4px stride)\n * - **Comeca VAZIO**: primeiras barras entram pela DIREITA e empurram pra esquerda\n * - Depois que enche: rolling window (mais antiga sai esquerda, nova entra direita)\n * - Visualmente: container alinha barras com `justify-end` */\nfunction LiveWaveform({\n stream,\n samplesRef,\n}: {\n stream?: MediaStream | null\n /** Ref opcional: quando fornecido, acumula TODOS os samples brutos da gravacao (para usar no\n * state=paused via RecordedWaveform). O LiveWaveform mantem seu rolling window separado. */\n samplesRef?: React.MutableRefObject<number[]>\n}) {\n const STRIDE = 4 // 2px barra + 2px gap\n const MIN_BARS = 20\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [bars, setBars] = React.useState<number[]>([])\n const barCountRef = React.useRef<number>(MIN_BARS)\n\n /* ResizeObserver: ajusta o LIMITE de barras (nao o buffer atual, exceto se shrink). */\n React.useEffect(() => {\n const el = containerRef.current\n if (!el) return\n\n const updateBarCount = (width: number) => {\n const count = Math.max(MIN_BARS, Math.floor(width / STRIDE))\n barCountRef.current = count\n // Se o buffer atual passou do novo limite (shrink), trunca mantendo as mais recentes\n setBars((prev) => (prev.length > count ? prev.slice(prev.length - count) : prev))\n }\n\n updateBarCount(el.clientWidth)\n\n const ro = new ResizeObserver((entries) => {\n const width = entries[0]?.contentRect.width ?? 0\n updateBarCount(width)\n })\n ro.observe(el)\n\n return () => ro.disconnect()\n }, [])\n\n /* Audio analysis setup (so depende do stream) */\n React.useEffect(() => {\n if (!stream) {\n setBars([])\n return\n }\n\n const AudioContextClass =\n window.AudioContext ||\n (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext\n const audioContext = new AudioContextClass()\n const source = audioContext.createMediaStreamSource(stream)\n const analyser = audioContext.createAnalyser()\n analyser.fftSize = 256\n source.connect(analyser)\n\n const dataArray = new Uint8Array(analyser.frequencyBinCount)\n let rafId: number | null = null\n let lastUpdate = 0\n let cancelled = false\n\n const tick = (timestamp: number) => {\n if (cancelled) return\n // Throttle ~30fps (33ms entre samples)\n if (timestamp - lastUpdate >= 33) {\n lastUpdate = timestamp\n analyser.getByteTimeDomainData(dataArray)\n\n // RMS — amplitude media percebida\n let sum = 0\n for (let i = 0; i < dataArray.length; i++) {\n const v = (dataArray[i] - 128) / 128\n sum += v * v\n }\n const rms = Math.sqrt(sum / dataArray.length)\n\n // Acumula no ref do parent (para reaproveitar no state=paused via RecordedWaveform)\n if (samplesRef) samplesRef.current.push(rms)\n\n setBars((prev) => {\n // Enquanto nao encheu: append no final (vai aparecer na direita por causa do justify-end)\n if (prev.length < barCountRef.current) return [...prev, rms]\n // Cheio: rolling window (descarta mais antiga, adiciona nova)\n return [...prev.slice(1), rms]\n })\n }\n rafId = requestAnimationFrame(tick)\n }\n\n rafId = requestAnimationFrame(tick)\n\n return () => {\n cancelled = true\n if (rafId !== null) cancelAnimationFrame(rafId)\n void audioContext.close()\n }\n }, [stream, samplesRef])\n\n return (\n <div\n ref={containerRef}\n className=\"flex-1 relative h-8 min-w-0 overflow-hidden\"\n >\n <div className=\"absolute inset-y-0 left-0 right-0 flex items-center justify-end gap-[2px]\">\n {bars.map((amp, i) => {\n // Voz humana tende a ter RMS baixo (~0.05-0.3). Boost x4 para visual.\n const normalized = Math.min(amp * 4, 1)\n const height = Math.max(2, Math.round(normalized * 32))\n return (\n <div\n key={i}\n className=\"w-[2px] shrink-0 rounded-full bg-neutral-foreground\"\n style={{ height: `${height}px` }}\n />\n )\n })}\n </div>\n </div>\n )\n}\n\n/** RecordedWaveform — waveform real do audio ja gravado (usado no state=paused).\n * - Recebe samples brutos acumulados durante o recording (via samplesRef)\n * - Agrega em N barras via ResizeObserver (largura dinamica, igual ChatAudio)\n * - Barras tocadas (i / N <= progress) em `bg-neutral-foreground`, demais em `bg-neutral-ring`\n * - Dot verde sincronizado com o progresso\n * - Click/drag-to-seek via pointer events */\nfunction RecordedWaveform({\n samples,\n progress = 0,\n onSeek,\n}: {\n samples: number[]\n progress?: number\n onSeek?: (progress: number) => void\n}) {\n const STRIDE = 4 // 2px barra + 2px gap\n const MIN_BARS = 20\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [barCount, setBarCount] = React.useState<number>(MIN_BARS)\n\n React.useEffect(() => {\n const el = containerRef.current\n if (!el) return\n const updateBarCount = (width: number) => {\n const count = Math.max(MIN_BARS, Math.floor(width / STRIDE))\n setBarCount(count)\n }\n updateBarCount(el.clientWidth)\n const ro = new ResizeObserver((entries) => {\n updateBarCount(entries[0]?.contentRect.width ?? 0)\n })\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n // Agrega samples acumulados em barCount barras + normaliza para 0-1 (relativo ao pico)\n const bars = React.useMemo(() => {\n const aggregated = aggregateSamples(samples, barCount)\n const maxPeak = Math.max(...aggregated, 0.01)\n return aggregated.map((p) => p / maxPeak)\n }, [samples, barCount])\n\n const isDraggingRef = React.useRef(false)\n\n const seekFromClientX = React.useCallback(\n (clientX: number) => {\n const el = containerRef.current\n if (!el || !onSeek) return\n const rect = el.getBoundingClientRect()\n const x = clientX - rect.left\n const newProgress = Math.max(0, Math.min(x / rect.width, 1))\n onSeek(newProgress)\n },\n [onSeek]\n )\n\n const handlePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {\n if (!onSeek) return\n isDraggingRef.current = true\n e.currentTarget.setPointerCapture(e.pointerId)\n seekFromClientX(e.clientX)\n }\n const handlePointerMove = (e: React.PointerEvent<HTMLDivElement>) => {\n if (!isDraggingRef.current) return\n seekFromClientX(e.clientX)\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 // Largura natural do layout das barras (px), usada para posicionar o dot em pixels\n const barsLayoutWidth = bars.length * STRIDE - 2\n\n return (\n <div\n ref={containerRef}\n className={cn(\n \"flex-1 relative h-8 min-w-0 overflow-hidden select-none\",\n onSeek && \"touch-none cursor-pointer\"\n )}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerUp}\n role={onSeek ? \"slider\" : undefined}\n aria-label={onSeek ? \"Posicao do audio\" : undefined}\n aria-valuemin={onSeek ? 0 : undefined}\n aria-valuemax={onSeek ? 100 : undefined}\n aria-valuenow={onSeek ? Math.round(progress * 100) : undefined}\n >\n <div className=\"absolute inset-y-0 left-0 flex items-center gap-[2px]\">\n {bars.map((amp, i) => {\n const isPlayed = i / bars.length <= progress\n const height = Math.max(2, Math.round(amp * 32))\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 {progress > 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] shadow-md pointer-events-none\"\n style={{ left: `${progress * barsLayoutWidth}px` }}\n aria-hidden=\"true\"\n />\n )}\n </div>\n )\n}\n\n/* ─── Press-to-record constants ───────────────────────────────── */\n\nconst LOCK_THRESHOLD = 120 // px arrastados pra cima → trava em modo hands-free\nconst CANCEL_THRESHOLD_RATIO = 0.65 // % da largura do MessageBar\nconst CANCEL_THRESHOLD_MIN = 140 // floor (telas pequenas — iPhone SE)\nconst CANCEL_THRESHOLD_MAX = 220 // ceiling (telas grandes — nao precisa atravessar a tela toda)\nconst MIN_DURATION = 1000 // ms — release antes disso cancela\n\n/** Calcula o threshold de cancel baseado na largura do container do MessageBar.\n * Sempre proporcional, com clamps min/max pra funcionar bem em telas pequenas (iPhone SE)\n * sem ficar trivialmente facil em telas grandes. */\nfunction computeCancelThreshold(containerWidth: number): number {\n return Math.min(\n CANCEL_THRESHOLD_MAX,\n Math.max(CANCEL_THRESHOLD_MIN, containerWidth * CANCEL_THRESHOLD_RATIO)\n )\n}\n\n/** PressedRecordingOverlay — UI exibida enquanto o usuario mantem o mic pressionado.\n * Inspirado no comportamento de gravacao de audio do WhatsApp.\n *\n * - Pill esquerda: live dot + timer + \"Deslize para cancelar\"\n * - Mic vermelho 64×64 (acompanha o cursor via translate clamped)\n * - Pill flutuante ACIMA do mic com lock + chevron up (indica \"deslize pra travar\")\n *\n * Variants:\n * - `default`: layout horizontal (cancel pill esquerda + mic direita)\n * - `audio-only`: layout horizontal centralizado (cancel pill curta + mic + timer pill) */\nfunction PressedRecordingOverlay({\n variant,\n duration,\n deltaX,\n deltaY,\n lockThreshold,\n cancelThreshold,\n isWarning,\n secondsLeft,\n secondsLeftLabel,\n}: {\n variant: \"default\" | \"audio-only\"\n duration: number\n deltaX: number\n deltaY: number\n lockThreshold: number\n cancelThreshold: number\n isWarning: boolean\n secondsLeft: number\n secondsLeftLabel: (s: number) => string\n}) {\n const aboutToLock = deltaY <= -lockThreshold\n const aboutToCancel = deltaX <= -cancelThreshold\n // Clamp: so move pra cima/esquerda (delta negativo); ignora positivos\n const clampedX = Math.min(0, deltaX)\n const clampedY = Math.min(0, deltaY)\n // Restringe a UM EIXO POR VEZ — sem movimento diagonal.\n // Detecta direcao dominante (maior magnitude) e zera o outro eixo.\n const absX = Math.abs(clampedX)\n const absY = Math.abs(clampedY)\n const micTranslateX = absX >= absY ? clampedX : 0\n const micTranslateY = absY > absX ? clampedY : 0\n\n return (\n <div\n data-slot=\"message-bar\"\n data-state=\"recording-pressed\"\n className={cn(\n \"flex items-center gap-2 p-2 sm:gap-3 sm:p-0 touch-none select-none\",\n variant === \"audio-only\" && \"justify-center\"\n )}\n >\n {/* Layout: cancel-pill + mic (default) | cancel-pill + mic + timer-pill (audio-only) */}\n {variant === \"audio-only\" ? (\n <div className=\"relative flex items-center gap-2\">\n {/* Cancel pill (compact) */}\n <div\n className={cn(\n \"flex items-center gap-1 rounded-full bg-muted h-7 px-3 transition-colors\",\n aboutToCancel && \"bg-destructive/10\"\n )}\n >\n <ChevronLeft className=\"size-3 text-muted-foreground\" />\n <span\n className={cn(\n \"text-xs\",\n aboutToCancel ? \"text-destructive font-medium\" : \"text-muted-foreground\"\n )}\n >\n Cancelar\n </span>\n </div>\n\n {/* Mic + lock pill (anchor) */}\n <div className=\"relative\">\n {/* Lock pill ACIMA do mic */}\n <div\n className={cn(\n \"absolute left-1/2 -translate-x-1/2 bottom-full mb-2 flex flex-col items-center gap-3 rounded-t-full bg-muted h-20 w-8 py-3 transition-transform\",\n aboutToLock && \"scale-125 ring-2 ring-[#0EA460]\"\n )}\n aria-hidden=\"true\"\n >\n <Lock className={cn(\"size-3\", aboutToLock ? \"text-[#0EA460]\" : \"text-muted-foreground\")} />\n <ChevronUp className=\"size-3 text-muted-foreground animate-bounce\" />\n </div>\n\n <MicButton\n variant=\"large\"\n pressed\n ariaLabel=\"Gravando — arraste para cima para travar, para esquerda para cancelar\"\n className={cn(aboutToCancel && \"animate-shake\")}\n style={\n aboutToCancel\n ? undefined\n : { transform: `translate(${micTranslateX}px, ${micTranslateY}px) scale(1.1)` }\n }\n />\n </div>\n\n {/* Timer pill — colore o texto e o RecordingIndicator quando proximo do limite */}\n <div className=\"flex flex-col items-center gap-0.5\">\n <div className=\"flex items-center gap-1 rounded-full bg-muted h-7 px-3\">\n <RecordingIndicator />\n <span\n className={cn(\n \"text-sm tabular-nums\",\n isWarning ? \"text-destructive font-medium\" : \"text-neutral-foreground\"\n )}\n >\n {formatDuration(duration)}\n </span>\n </div>\n {isWarning && (\n <span\n className=\"text-xs text-destructive tabular-nums\"\n aria-live=\"polite\"\n >\n {secondsLeftLabel(secondsLeft)}\n </span>\n )}\n </div>\n </div>\n ) : (\n // Variant default: cancel pill flex-1 + mic\n <>\n <div\n className={cn(\n \"flex-1 flex items-center justify-between gap-2 rounded-xl bg-muted px-4 py-3 h-14\",\n aboutToCancel && \"bg-destructive/10\"\n )}\n >\n <div className=\"flex flex-col items-start gap-0.5\">\n <div className=\"flex items-center gap-2\">\n <RecordingIndicator />\n <span\n className={cn(\n \"text-sm tabular-nums\",\n isWarning ? \"text-destructive font-medium\" : \"text-neutral-foreground\"\n )}\n >\n {formatDuration(duration)}\n </span>\n </div>\n {isWarning && (\n <span\n className=\"text-xs text-destructive tabular-nums\"\n aria-live=\"polite\"\n >\n {secondsLeftLabel(secondsLeft)}\n </span>\n )}\n </div>\n <div\n className={cn(\n \"flex items-center gap-1 text-xs\",\n aboutToCancel ? \"text-destructive font-medium\" : \"text-muted-foreground\"\n )}\n >\n <ChevronLeft className=\"size-3\" />\n <span>Deslize para cancelar</span>\n </div>\n </div>\n\n {/* Mic + lock pill */}\n <div className=\"relative\">\n <div\n className={cn(\n \"absolute left-1/2 -translate-x-1/2 bottom-full mb-2 flex flex-col items-center gap-3 rounded-t-full bg-muted h-20 w-8 py-3 transition-transform\",\n aboutToLock && \"scale-125 ring-2 ring-[#0EA460]\"\n )}\n aria-hidden=\"true\"\n >\n <Lock className={cn(\"size-3\", aboutToLock ? \"text-[#0EA460]\" : \"text-muted-foreground\")} />\n <ChevronUp className=\"size-3 text-muted-foreground animate-bounce\" />\n </div>\n\n <MicButton\n variant=\"large\"\n pressed\n ariaLabel=\"Gravando — arraste para cima para travar, para esquerda para cancelar\"\n className={cn(aboutToCancel && \"animate-shake\")}\n style={\n aboutToCancel\n ? undefined\n : { transform: `translate(${micTranslateX}px, ${micTranslateY}px) scale(1.1)` }\n }\n />\n </div>\n </>\n )}\n </div>\n )\n}\n\n/* ─── Component principal ─────────────────────────────────────── */\n\nfunction MessageBar({\n state = \"default\",\n value = \"\",\n onChange,\n onSendText,\n onStartRecording,\n onPauseRecording,\n onResumeRecording,\n onCancelRecording,\n onSendAudio,\n onTogglePlay,\n recordingDuration = 0,\n isPlaying = false,\n recordingStream,\n playbackProgress = 0,\n onSeekPlayback,\n placeholder = \"Digite sua mensagem...\",\n maxRecordingDuration,\n warnAtSecondsLeft = DEFAULT_WARN_AT_SECONDS_LEFT,\n onMaxDurationReached,\n secondsLeftLabel = DEFAULT_SECONDS_LEFT_LABEL,\n className,\n ...props\n}: MessageBarProps) {\n /* ─── Limite de duracao da gravacao ─── */\n const { isWarning: isDurationWarning, secondsLeft: durationSecondsLeft } =\n getWarningState(recordingDuration, maxRecordingDuration, warnAtSecondsLeft)\n\n // Dispara onMaxDurationReached uma unica vez quando o limite e atingido.\n // Reset ao sair do estado recording (proxima gravacao reativa o disparo).\n const maxReachedRef = React.useRef(false)\n React.useEffect(() => {\n if (!maxRecordingDuration || maxReachedRef.current) return\n if (recordingDuration >= maxRecordingDuration) {\n maxReachedRef.current = true\n onMaxDurationReached?.()\n }\n }, [recordingDuration, maxRecordingDuration, onMaxDurationReached])\n React.useEffect(() => {\n if (state !== \"recording\") maxReachedRef.current = false\n }, [state])\n // Acumula samples brutos do microfone durante recording — reaproveitado em paused via RecordedWaveform\n const samplesRef = React.useRef<number[]>([])\n\n // Reset dos samples quando inicia nova gravacao (transicao para recording a partir de outros estados)\n // Nao zera ao voltar de paused → recording (continua a mesma gravacao)\n const prevStateRef = React.useRef<MessageBarState>(state)\n // Lembra o ultimo base state (default/audio-only) pra escolher variant do overlay pressed\n const lastBaseStateRef = React.useRef<\"default\" | \"audio-only\">(\n state === \"audio-only\" ? \"audio-only\" : \"default\"\n )\n React.useEffect(() => {\n const prev = prevStateRef.current\n if (state === \"recording\" && prev !== \"paused\" && prev !== \"recording\") {\n samplesRef.current = []\n }\n if (state === \"default\" || state === \"audio-only\") {\n lastBaseStateRef.current = state\n }\n prevStateRef.current = state\n }, [state])\n\n /* ─── Press-to-record (WhatsApp-like, touch-only) ───────────── */\n const [isPressed, setIsPressed] = React.useState(false)\n const [pressDelta, setPressDelta] = React.useState({ x: 0, y: 0 })\n // Cancel threshold dinamico — calculado baseado na largura do container no pointerdown\n const [pressCancelThreshold, setPressCancelThreshold] = React.useState(CANCEL_THRESHOLD_MAX)\n const pressDeltaRef = React.useRef({ x: 0, y: 0 })\n const pressCancelThresholdRef = React.useRef(CANCEL_THRESHOLD_MAX)\n const pressInfoRef = React.useRef({ startX: 0, startY: 0, startTime: 0, locked: false })\n // Ref sincronizada com isPressed — usada por listener permanente de touchmove\n // (instalado no mount, mas so preventDefault quando esta pressionado)\n const isPressedRef = React.useRef(false)\n // Refs pros callbacks — handlers do window leem a versao mais recente sem precisar reinstalar listeners\n const callbacksRef = React.useRef({ onStartRecording, onCancelRecording, onSendAudio })\n React.useEffect(() => {\n callbacksRef.current = { onStartRecording, onCancelRecording, onSendAudio }\n })\n\n const updateDelta = (dx: number, dy: number) => {\n pressDeltaRef.current = { x: dx, y: dy }\n setPressDelta({ x: dx, y: dy })\n }\n\n /* Listener PERMANENTE de touchmove com preventDefault — instalado no mount.\n * Por que permanente: se instalar so quando isPressed=true (dentro do useEffect transient),\n * o iOS ja decidiu que o gesto e scroll/swipe ANTES do listener montar.\n * Solucao: listener sempre presente, com guard via isPressedRef.current. */\n React.useEffect(() => {\n const onTouchMove = (e: TouchEvent) => {\n if (isPressedRef.current) {\n e.preventDefault()\n }\n }\n window.addEventListener(\"touchmove\", onTouchMove, { passive: false })\n return () => window.removeEventListener(\"touchmove\", onTouchMove)\n }, [])\n\n const triggerHaptic = (ms = 50) => {\n if (typeof navigator !== \"undefined\" && typeof navigator.vibrate === \"function\") {\n try {\n navigator.vibrate(ms)\n } catch {\n /* ignore — alguns browsers podem rejeitar fora de user gesture */\n }\n }\n }\n\n // Listeners no window (sobrevivem a troca de DOM quando o overlay aparece e o MicButton original desmonta)\n // Sincroniza isPressedRef com isPressed pra o listener permanente de touchmove ler\n React.useEffect(() => {\n isPressedRef.current = isPressed\n }, [isPressed])\n\n // Listeners de pointer (transient): so existem durante o press flow\n React.useEffect(() => {\n if (!isPressed) return\n\n // touch-action: none em html + body bloqueia gestos nativos (backup, ja temos o\n // touchmove permanente com preventDefault)\n const prevHtmlTouchAction = document.documentElement.style.touchAction\n const prevBodyTouchAction = document.body.style.touchAction\n document.documentElement.style.touchAction = \"none\"\n document.body.style.touchAction = \"none\"\n\n const onMove = (e: PointerEvent) => {\n if (pressInfoRef.current.locked) return\n const { startX, startY } = pressInfoRef.current\n const dx = e.clientX - startX\n const dy = e.clientY - startY\n updateDelta(dx, dy)\n // Atinge lock threshold → trava em modo hands-free\n if (dy <= -LOCK_THRESHOLD) {\n pressInfoRef.current.locked = true\n triggerHaptic(50)\n setIsPressed(false)\n updateDelta(0, 0)\n }\n }\n\n const onEnd = () => {\n if (pressInfoRef.current.locked) return\n const { startTime } = pressInfoRef.current\n const elapsed = Date.now() - startTime\n const { x: dx } = pressDeltaRef.current\n\n setIsPressed(false)\n updateDelta(0, 0)\n\n if (dx <= -pressCancelThresholdRef.current || elapsed < MIN_DURATION) {\n triggerHaptic(50)\n callbacksRef.current.onCancelRecording?.()\n return\n }\n callbacksRef.current.onSendAudio?.()\n }\n\n window.addEventListener(\"pointermove\", onMove)\n window.addEventListener(\"pointerup\", onEnd)\n // pointercancel tratado como release (usa delta atual pra decidir cancel/send)\n window.addEventListener(\"pointercancel\", onEnd)\n return () => {\n window.removeEventListener(\"pointermove\", onMove)\n window.removeEventListener(\"pointerup\", onEnd)\n window.removeEventListener(\"pointercancel\", onEnd)\n document.documentElement.style.touchAction = prevHtmlTouchAction\n document.body.style.touchAction = prevBodyTouchAction\n }\n }, [isPressed])\n\n const handlePressStart = (e: React.PointerEvent<HTMLButtonElement>) => {\n // Desktop (mouse/pen): comportamento antigo de click — dispara onStartRecording, sai do flow press\n if (e.pointerType !== \"touch\") {\n onStartRecording?.()\n return\n }\n // Mede a largura do container do MessageBar pra calcular o cancel threshold adaptativo\n const wrapper = e.currentTarget.closest('[data-slot=\"message-bar\"]')\n const containerWidth =\n wrapper instanceof HTMLElement ? wrapper.clientWidth : window.innerWidth\n const threshold = computeCancelThreshold(containerWidth)\n pressCancelThresholdRef.current = threshold\n setPressCancelThreshold(threshold)\n\n pressInfoRef.current = {\n startX: e.clientX,\n startY: e.clientY,\n startTime: Date.now(),\n locked: false,\n }\n updateDelta(0, 0)\n setIsPressed(true)\n onStartRecording?.()\n }\n\n const pressHandlers = {\n onPointerDown: handlePressStart,\n }\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey && value.trim()) {\n e.preventDefault()\n onSendText?.(value)\n }\n }\n\n /* ─── State: audio-only (so botao mic centralizado) ─────────── */\n if (state === \"audio-only\") {\n return (\n <div\n data-slot=\"message-bar\"\n data-state=\"audio-only\"\n className={cn(\"flex items-center justify-center p-2\", className)}\n {...props}\n >\n <MicButton {...pressHandlers} />\n </div>\n )\n }\n\n /* ─── State: recording + pressed (overlay WhatsApp-like) ────── */\n if (state === \"recording\" && isPressed) {\n return (\n <PressedRecordingOverlay\n variant={lastBaseStateRef.current}\n duration={recordingDuration}\n deltaX={pressDelta.x}\n deltaY={pressDelta.y}\n lockThreshold={LOCK_THRESHOLD}\n cancelThreshold={pressCancelThreshold}\n isWarning={isDurationWarning}\n secondsLeft={durationSecondsLeft}\n secondsLeftLabel={secondsLeftLabel}\n />\n )\n }\n\n /* ─── State: recording (hands-free, apos lock ou click no mic) ─ */\n if (state === \"recording\") {\n return (\n <div\n data-slot=\"message-bar\"\n data-state=\"recording\"\n className={cn(\n \"flex items-center gap-2 p-2 sm:gap-3 sm:p-0\",\n className\n )}\n {...props}\n >\n {/* Mobile: 2-row layout */}\n <div className=\"flex flex-col gap-2 w-full sm:contents\">\n {/* Top: trash + recording info + waveform (mobile) / inline (desktop) */}\n <div className=\"flex items-center gap-2 sm:contents\">\n {/* Trash — only inline at desktop, mobile shows in bottom row */}\n <div className=\"hidden sm:contents\">\n <IconButton\n onClick={onCancelRecording}\n icon={Trash2}\n ariaLabel=\"Cancelar gravacao\"\n />\n </div>\n\n {/* Recording bar — bg-muted container.\n Quando proximo do limite, o timer vira destructive + label \"Xs restantes\"\n aparece abaixo (aria-live polite anuncia pra screen readers). */}\n <div className=\"flex items-center gap-3 flex-1 rounded-2xl bg-muted px-4 py-3\">\n <RecordingIndicator />\n <div className=\"flex flex-col items-start gap-0.5 shrink-0\">\n <span\n className={cn(\n \"text-sm tabular-nums\",\n isDurationWarning ? \"text-destructive font-medium\" : \"text-neutral-foreground\"\n )}\n >\n {formatDuration(recordingDuration)}\n </span>\n {isDurationWarning && (\n <span\n className=\"text-xs text-destructive tabular-nums whitespace-nowrap\"\n aria-live=\"polite\"\n >\n {secondsLeftLabel(durationSecondsLeft)}\n </span>\n )}\n </div>\n <LiveWaveform stream={recordingStream} samplesRef={samplesRef} />\n {/* Pause inside the bar — only desktop */}\n <div className=\"hidden sm:contents\">\n <button\n type=\"button\"\n onClick={onPauseRecording}\n className=\"shrink-0 inline-flex items-center justify-center text-neutral-foreground hover:opacity-80 transition-opacity\"\n aria-label=\"Pausar gravacao\"\n >\n <Pause className=\"size-5\" fill=\"currentColor\" strokeWidth={0} />\n </button>\n </div>\n </div>\n\n {/* Send — only inline at desktop */}\n <div className=\"hidden sm:contents\">\n <SendButton onClick={onSendAudio} ariaLabel=\"Enviar audio\" />\n </div>\n </div>\n\n {/* Bottom row — mobile only */}\n <div className=\"flex items-center justify-between sm:hidden\">\n <IconButton\n onClick={onCancelRecording}\n icon={Trash2}\n ariaLabel=\"Cancelar gravacao\"\n />\n <button\n type=\"button\"\n onClick={onPauseRecording}\n className=\"shrink-0 inline-flex items-center justify-center size-10 text-neutral-foreground hover:opacity-80 transition-opacity\"\n aria-label=\"Pausar gravacao\"\n >\n <Pause className=\"size-5\" fill=\"currentColor\" strokeWidth={0} />\n </button>\n <SendButton onClick={onSendAudio} ariaLabel=\"Enviar audio\" />\n </div>\n </div>\n </div>\n )\n }\n\n /* ─── State: paused (com audio gravado) ──────────────────────── */\n if (state === \"paused\") {\n return (\n <div\n data-slot=\"message-bar\"\n data-state=\"paused\"\n className={cn(\n \"flex items-center gap-2 p-2 sm:gap-3 sm:p-0\",\n className\n )}\n {...props}\n >\n <div className=\"flex flex-col gap-2 w-full sm:contents\">\n <div className=\"flex items-center gap-2 sm:contents\">\n <div className=\"hidden sm:contents\">\n <IconButton\n onClick={onCancelRecording}\n icon={Trash2}\n ariaLabel=\"Cancelar gravacao\"\n />\n </div>\n\n <div className=\"flex items-center gap-3 flex-1 rounded-2xl bg-muted px-4 py-3\">\n <button\n type=\"button\"\n onClick={onTogglePlay}\n className=\"shrink-0 inline-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-5\" fill=\"currentColor\" strokeWidth={0} />\n ) : (\n <Play className=\"size-5\" fill=\"currentColor\" strokeWidth={0} />\n )}\n </button>\n <span className=\"shrink-0 text-sm text-neutral-foreground tabular-nums\">\n {formatDuration(recordingDuration)}\n </span>\n <RecordedWaveform\n samples={samplesRef.current}\n progress={playbackProgress}\n onSeek={onSeekPlayback}\n />\n {/* Mic inline — only desktop */}\n <div className=\"hidden sm:contents\">\n <button\n type=\"button\"\n onClick={onResumeRecording}\n className=\"shrink-0 inline-flex items-center justify-center text-neutral-foreground hover:opacity-80 transition-opacity\"\n aria-label=\"Continuar gravacao\"\n >\n <Mic className=\"size-5\" />\n </button>\n </div>\n </div>\n\n <div className=\"hidden sm:contents\">\n <SendButton onClick={onSendAudio} ariaLabel=\"Enviar audio\" />\n </div>\n </div>\n\n {/* Bottom row — mobile only */}\n <div className=\"flex items-center justify-between sm:hidden\">\n <IconButton\n onClick={onCancelRecording}\n icon={Trash2}\n ariaLabel=\"Cancelar gravacao\"\n />\n <button\n type=\"button\"\n onClick={onResumeRecording}\n className=\"shrink-0 inline-flex items-center justify-center size-10 text-neutral-foreground hover:opacity-80 transition-opacity\"\n aria-label=\"Continuar gravacao\"\n >\n <Mic className=\"size-5\" />\n </button>\n <SendButton onClick={onSendAudio} ariaLabel=\"Enviar audio\" />\n </div>\n </div>\n </div>\n )\n }\n\n /* ─── States: default | active | disabled (input + button) ──── */\n const isDisabled = state === \"disabled\"\n const isActive = state === \"active\" || (state === \"default\" && value.trim().length > 0)\n\n return (\n <div\n data-slot=\"message-bar\"\n data-state={state}\n className={cn(\n \"flex items-center gap-2 rounded-2xl bg-muted p-2\",\n className\n )}\n {...props}\n >\n <input\n type=\"text\"\n value={value}\n onChange={(e) => onChange?.(e.target.value)}\n onKeyDown={handleKeyDown}\n disabled={isDisabled}\n placeholder={placeholder}\n className={cn(\n \"flex-1 min-w-0 bg-transparent px-3 py-2 text-base text-neutral-foreground placeholder:text-neutral-muted-foreground outline-none\",\n \"disabled:cursor-not-allowed disabled:opacity-50\"\n )}\n aria-label=\"Mensagem\"\n />\n {isActive ? (\n <SendButton\n onClick={() => value.trim() && onSendText?.(value)}\n disabled={!value.trim()}\n />\n ) : (\n <MicButton {...pressHandlers} />\n )}\n </div>\n )\n}\n\nexport { MessageBar }\n"]}
|