@fluencypassdevs/cycle 1.32.0 → 1.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/composites/chat-thread.tsx"],"names":[],"mappings":"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","file":"chunk-OFGJDRRR.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\"\nimport { StatusBanner, useCountdown } from \"@/components/ui/status-banner\"\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: \"Você pode enviar novamente em {s}s\" */\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 /** Texto do banner de offline. Default: \"Você está sem conexão. Confira o Wi-Fi ou os dados móveis.\" */\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/** 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,\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 status (topo) */}\n {offline && (\n <StatusBanner kind=\"offline\" className=\"shrink-0 rounded-none border-x-0 border-t-0\">\n {offlineBannerText}\n </StatusBanner>\n )}\n {isRateLimited && (\n <StatusBanner\n kind=\"rate-limit\"\n until={rateLimitedUntil}\n rateLimitText={rateLimitBannerText}\n className=\"shrink-0 rounded-none border-x-0 border-t-0\"\n />\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
+ {"version":3,"sources":["../src/components/composites/chat-thread.tsx"],"names":[],"mappings":"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","file":"chunk-OZCMOOT7.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\"\nimport { StatusBanner, useCountdown } from \"@/components/ui/status-banner\"\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: \"Você pode enviar novamente em {s}s\" */\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 /** Texto do banner de offline. Default: \"Você está sem conexão. Confira o Wi-Fi ou os dados móveis.\" */\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/** 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,\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 status (topo) */}\n {offline && (\n <StatusBanner kind=\"offline\" className=\"shrink-0 rounded-none border-x-0 border-t-0\">\n {offlineBannerText}\n </StatusBanner>\n )}\n {isRateLimited && (\n <StatusBanner\n kind=\"rate-limit\"\n until={rateLimitedUntil}\n rateLimitText={rateLimitBannerText}\n className=\"shrink-0 rounded-none border-x-0 border-t-0\"\n />\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,5 +1,5 @@
1
1
  import { Spinner } from './chunk-TDBDX5NV.js';
2
- import { Progress } from './chunk-GDPUWRNH.js';
2
+ import { Progress } from './chunk-2MGHBZ7I.js';
3
3
  import { CycleIcon } from './chunk-US5LXJTO.js';
4
4
  import { cn } from './chunk-W4KJCBMW.js';
5
5
  import { __objRest, __spreadValues, __spreadProps } from './chunk-2NMEKWO5.js';
@@ -228,5 +228,5 @@ function FileDropzoneItem(_a) {
228
228
  }
229
229
 
230
230
  export { FileDropzone, FileDropzoneItem, formatFileSize };
231
- //# sourceMappingURL=chunk-WVCNTN4F.js.map
232
- //# sourceMappingURL=chunk-WVCNTN4F.js.map
231
+ //# sourceMappingURL=chunk-R7QITHNQ.js.map
232
+ //# sourceMappingURL=chunk-R7QITHNQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/ui/file-dropzone.tsx"],"names":["_a","_b","FileIcon"],"mappings":"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","file":"chunk-WVCNTN4F.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Upload, X, Check, File as FileIcon, AlertCircle } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { CycleIcon } from \"@/components/icons\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { Progress } from \"@/components/ui/progress\"\n\n/* ─── Types ─── */\n\n/** Motivo de rejeição na validação de SELEÇÃO (upload é problema do consumidor) */\nexport type FileRejectionReason = \"type\" | \"size\" | \"empty\" | \"count\"\n\nexport interface FileDropzoneLabels {\n /** Instrução principal (default: \"Arraste um arquivo ou clique para selecionar\") */\n instruction?: string\n /** Texto de apoio (ex: \"MP3 ou WAV até 20 MB\") — default derivado de accept/maxSize */\n hint?: string\n /** Mensagens de erro por motivo de rejeição */\n errors?: Partial<Record<FileRejectionReason, string>>\n}\n\nexport interface FileDropzoneProps\n extends Omit<React.ComponentProps<\"div\">, \"onDrop\"> {\n /** Tipos aceitos, sintaxe do input nativo (ex: \"audio/*,.mp3\" ou \"image/png,image/jpeg\") */\n accept?: string\n /** Tamanho máximo por arquivo, em bytes */\n maxSize?: number\n /** Permitir múltiplos arquivos */\n multiple?: boolean\n /** Máximo de arquivos por seleção (só com multiple) */\n maxFiles?: number\n disabled?: boolean\n /** Arquivos que passaram na validação de seleção */\n onFilesAccepted: (files: File[]) => void\n /** Arquivo rejeitado + motivo (type | size | empty | count) */\n onFileRejected?: (file: File, reason: FileRejectionReason) => void\n /** Textos customizáveis (defaults PT-BR) */\n labels?: FileDropzoneLabels\n}\n\n/* ─── Helpers ─── */\n\n/** Formata bytes em PT-BR (1,2 MB) */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 B\"\n const units = [\"B\", \"KB\", \"MB\", \"GB\"]\n const i = Math.min(units.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)))\n const value = bytes / 1024 ** i\n return `${value.toLocaleString(\"pt-BR\", { maximumFractionDigits: 1 })} ${units[i]}`\n}\n\n/** Valida file.type/nome contra a sintaxe de `accept` do input nativo */\nfunction matchesAccept(file: File, accept?: string): boolean {\n if (!accept) return true\n return accept.split(\",\").some((raw) => {\n const pattern = raw.trim().toLowerCase()\n if (!pattern) return false\n if (pattern.startsWith(\".\")) return file.name.toLowerCase().endsWith(pattern)\n if (pattern.endsWith(\"/*\")) return file.type.toLowerCase().startsWith(pattern.slice(0, -1))\n return file.type.toLowerCase() === pattern\n })\n}\n\nconst DEFAULT_ERRORS: Record<FileRejectionReason, string> = {\n type: \"Tipo de arquivo não aceito\",\n size: \"Arquivo excede o tamanho máximo\",\n empty: \"Arquivo vazio ou corrompido (0 bytes)\",\n count: \"Quantidade de arquivos excede o limite\",\n}\n\n/* ─── Dropzone ─── */\n\nfunction FileDropzone({\n accept,\n maxSize,\n multiple = false,\n maxFiles,\n disabled = false,\n onFilesAccepted,\n onFileRejected,\n labels,\n className,\n ...props\n}: FileDropzoneProps) {\n const inputRef = React.useRef<HTMLInputElement>(null)\n const [isDragOver, setIsDragOver] = React.useState(false)\n const [lastError, setLastError] = React.useState<string | null>(null)\n const dragDepth = React.useRef(0)\n\n const errorMessages = { ...DEFAULT_ERRORS, ...labels?.errors }\n\n const hint =\n labels?.hint ??\n [accept, maxSize ? `até ${formatFileSize(maxSize)}` : null].filter(Boolean).join(\" · \")\n\n const processFiles = (fileList: FileList | null) => {\n if (!fileList || disabled) return\n let files = Array.from(fileList)\n setLastError(null)\n\n if (!multiple) files = files.slice(0, 1)\n if (multiple && maxFiles && files.length > maxFiles) {\n for (const extra of files.slice(maxFiles)) {\n onFileRejected?.(extra, \"count\")\n }\n setLastError(errorMessages.count)\n files = files.slice(0, maxFiles)\n }\n\n const accepted: File[] = []\n for (const file of files) {\n const reason: FileRejectionReason | null =\n file.size === 0 ? \"empty\"\n : !matchesAccept(file, accept) ? \"type\"\n : maxSize && file.size > maxSize ? \"size\"\n : null\n if (reason) {\n onFileRejected?.(file, reason)\n setLastError(errorMessages[reason])\n } else {\n accepted.push(file)\n }\n }\n if (accepted.length > 0) onFilesAccepted(accepted)\n }\n\n return (\n <div data-slot=\"file-dropzone\" className={cn(\"w-full\", className)} {...props}>\n <button\n type=\"button\"\n disabled={disabled}\n aria-label={labels?.instruction ?? \"Arraste um arquivo ou clique para selecionar\"}\n onClick={() => inputRef.current?.click()}\n onDragEnter={(e) => {\n e.preventDefault()\n dragDepth.current++\n if (!disabled) setIsDragOver(true)\n }}\n onDragOver={(e) => e.preventDefault()}\n onDragLeave={(e) => {\n e.preventDefault()\n dragDepth.current--\n if (dragDepth.current <= 0) {\n dragDepth.current = 0\n setIsDragOver(false)\n }\n }}\n onDrop={(e) => {\n e.preventDefault()\n dragDepth.current = 0\n setIsDragOver(false)\n processFiles(e.dataTransfer.files)\n }}\n data-drag-over={isDragOver || undefined}\n className={cn(\n \"flex w-full cursor-pointer flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed border-neutral-border bg-background px-6 py-10 text-center outline-none transition-colors\",\n \"hover:border-ring hover:bg-accent/50\",\n \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n isDragOver && \"border-ring bg-accent/50\",\n lastError && \"border-destructive\",\n disabled && \"pointer-events-none opacity-50\"\n )}\n >\n <span\n aria-hidden\n className={cn(\n \"flex size-10 items-center justify-center rounded-full bg-accent text-foreground\",\n isDragOver && \"text-primary\"\n )}\n >\n <CycleIcon icon={Upload} size=\"sm\" decorative />\n </span>\n <span className=\"text-sm font-medium text-foreground\">\n {labels?.instruction ?? \"Arraste um arquivo ou clique para selecionar\"}\n </span>\n {hint && <span className=\"text-xs text-muted-foreground\">{hint}</span>}\n </button>\n\n {lastError && (\n <p role=\"alert\" className=\"mt-2 flex items-center gap-1.5 text-sm text-destructive\">\n <CycleIcon icon={AlertCircle} size=\"xs\" decorative />\n {lastError}\n </p>\n )}\n\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n className=\"sr-only\"\n tabIndex={-1}\n onChange={(e) => {\n processFiles(e.target.files)\n e.target.value = \"\" // permite re-selecionar o mesmo arquivo\n }}\n />\n </div>\n )\n}\n\n/* ─── Item da lista de arquivos ─── */\n\nexport type FileDropzoneItemStatus = \"idle\" | \"uploading\" | \"success\" | \"error\"\n\nexport interface FileDropzoneItemProps extends React.ComponentProps<\"div\"> {\n /** Nome do arquivo */\n name: string\n /** Tamanho em bytes (formatado em PT-BR) */\n size?: number\n /** Estado do upload — o upload em si é do consumidor */\n status?: FileDropzoneItemStatus\n /** Progresso 0–100 (exibido quando status=\"uploading\") */\n progress?: number\n /** Mensagem exibida quando status=\"error\" */\n error?: string\n /** Remove o arquivo da lista (também usado para cancelar) */\n onRemove?: () => void\n /** Label do botão remover (default: \"Remover arquivo\") */\n removeLabel?: string\n}\n\nfunction FileDropzoneItem({\n name,\n size,\n status = \"idle\",\n progress,\n error,\n onRemove,\n removeLabel = \"Remover arquivo\",\n className,\n ...props\n}: FileDropzoneItemProps) {\n return (\n <div\n data-slot=\"file-dropzone-item\"\n data-status={status}\n className={cn(\n \"flex items-center gap-3 rounded-lg border border-border bg-card p-3\",\n status === \"error\" && \"border-destructive/50\",\n className\n )}\n {...props}\n >\n <span\n aria-hidden\n className=\"flex size-9 shrink-0 items-center justify-center rounded-md bg-accent text-foreground\"\n >\n <CycleIcon icon={FileIcon} size=\"sm\" decorative />\n </span>\n\n <div className=\"min-w-0 flex-1 space-y-1\">\n <div className=\"flex items-baseline gap-2\">\n <p className=\"truncate text-sm font-medium text-foreground\">{name}</p>\n {size !== undefined && (\n <span className=\"shrink-0 text-xs text-muted-foreground\">{formatFileSize(size)}</span>\n )}\n </div>\n {status === \"uploading\" && (\n <Progress value={progress ?? 0} size=\"xs\" aria-label={`Enviando ${name}`} />\n )}\n {status === \"error\" && error && (\n <p className=\"text-xs text-destructive\">{error}</p>\n )}\n </div>\n\n {status === \"uploading\" && <Spinner size=\"sm\" className=\"shrink-0 text-muted-foreground\" />}\n {status === \"success\" && (\n <span aria-hidden className=\"shrink-0 text-positive\">\n <CycleIcon icon={Check} size=\"sm\" decorative />\n </span>\n )}\n\n {onRemove && (\n <button\n type=\"button\"\n aria-label={removeLabel}\n onClick={onRemove}\n className=\"flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <CycleIcon icon={X} size=\"sm\" decorative />\n </button>\n )}\n </div>\n )\n}\n\nexport { FileDropzone, FileDropzoneItem }\n"]}
1
+ {"version":3,"sources":["../src/components/ui/file-dropzone.tsx"],"names":["_a","_b","FileIcon"],"mappings":"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","file":"chunk-R7QITHNQ.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Upload, X, Check, File as FileIcon, AlertCircle } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { CycleIcon } from \"@/components/icons\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { Progress } from \"@/components/ui/progress\"\n\n/* ─── Types ─── */\n\n/** Motivo de rejeição na validação de SELEÇÃO (upload é problema do consumidor) */\nexport type FileRejectionReason = \"type\" | \"size\" | \"empty\" | \"count\"\n\nexport interface FileDropzoneLabels {\n /** Instrução principal (default: \"Arraste um arquivo ou clique para selecionar\") */\n instruction?: string\n /** Texto de apoio (ex: \"MP3 ou WAV até 20 MB\") — default derivado de accept/maxSize */\n hint?: string\n /** Mensagens de erro por motivo de rejeição */\n errors?: Partial<Record<FileRejectionReason, string>>\n}\n\nexport interface FileDropzoneProps\n extends Omit<React.ComponentProps<\"div\">, \"onDrop\"> {\n /** Tipos aceitos, sintaxe do input nativo (ex: \"audio/*,.mp3\" ou \"image/png,image/jpeg\") */\n accept?: string\n /** Tamanho máximo por arquivo, em bytes */\n maxSize?: number\n /** Permitir múltiplos arquivos */\n multiple?: boolean\n /** Máximo de arquivos por seleção (só com multiple) */\n maxFiles?: number\n disabled?: boolean\n /** Arquivos que passaram na validação de seleção */\n onFilesAccepted: (files: File[]) => void\n /** Arquivo rejeitado + motivo (type | size | empty | count) */\n onFileRejected?: (file: File, reason: FileRejectionReason) => void\n /** Textos customizáveis (defaults PT-BR) */\n labels?: FileDropzoneLabels\n}\n\n/* ─── Helpers ─── */\n\n/** Formata bytes em PT-BR (1,2 MB) */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 B\"\n const units = [\"B\", \"KB\", \"MB\", \"GB\"]\n const i = Math.min(units.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)))\n const value = bytes / 1024 ** i\n return `${value.toLocaleString(\"pt-BR\", { maximumFractionDigits: 1 })} ${units[i]}`\n}\n\n/** Valida file.type/nome contra a sintaxe de `accept` do input nativo */\nfunction matchesAccept(file: File, accept?: string): boolean {\n if (!accept) return true\n return accept.split(\",\").some((raw) => {\n const pattern = raw.trim().toLowerCase()\n if (!pattern) return false\n if (pattern.startsWith(\".\")) return file.name.toLowerCase().endsWith(pattern)\n if (pattern.endsWith(\"/*\")) return file.type.toLowerCase().startsWith(pattern.slice(0, -1))\n return file.type.toLowerCase() === pattern\n })\n}\n\nconst DEFAULT_ERRORS: Record<FileRejectionReason, string> = {\n type: \"Tipo de arquivo não aceito\",\n size: \"Arquivo excede o tamanho máximo\",\n empty: \"Arquivo vazio ou corrompido (0 bytes)\",\n count: \"Quantidade de arquivos excede o limite\",\n}\n\n/* ─── Dropzone ─── */\n\nfunction FileDropzone({\n accept,\n maxSize,\n multiple = false,\n maxFiles,\n disabled = false,\n onFilesAccepted,\n onFileRejected,\n labels,\n className,\n ...props\n}: FileDropzoneProps) {\n const inputRef = React.useRef<HTMLInputElement>(null)\n const [isDragOver, setIsDragOver] = React.useState(false)\n const [lastError, setLastError] = React.useState<string | null>(null)\n const dragDepth = React.useRef(0)\n\n const errorMessages = { ...DEFAULT_ERRORS, ...labels?.errors }\n\n const hint =\n labels?.hint ??\n [accept, maxSize ? `até ${formatFileSize(maxSize)}` : null].filter(Boolean).join(\" · \")\n\n const processFiles = (fileList: FileList | null) => {\n if (!fileList || disabled) return\n let files = Array.from(fileList)\n setLastError(null)\n\n if (!multiple) files = files.slice(0, 1)\n if (multiple && maxFiles && files.length > maxFiles) {\n for (const extra of files.slice(maxFiles)) {\n onFileRejected?.(extra, \"count\")\n }\n setLastError(errorMessages.count)\n files = files.slice(0, maxFiles)\n }\n\n const accepted: File[] = []\n for (const file of files) {\n const reason: FileRejectionReason | null =\n file.size === 0 ? \"empty\"\n : !matchesAccept(file, accept) ? \"type\"\n : maxSize && file.size > maxSize ? \"size\"\n : null\n if (reason) {\n onFileRejected?.(file, reason)\n setLastError(errorMessages[reason])\n } else {\n accepted.push(file)\n }\n }\n if (accepted.length > 0) onFilesAccepted(accepted)\n }\n\n return (\n <div data-slot=\"file-dropzone\" className={cn(\"w-full\", className)} {...props}>\n <button\n type=\"button\"\n disabled={disabled}\n aria-label={labels?.instruction ?? \"Arraste um arquivo ou clique para selecionar\"}\n onClick={() => inputRef.current?.click()}\n onDragEnter={(e) => {\n e.preventDefault()\n dragDepth.current++\n if (!disabled) setIsDragOver(true)\n }}\n onDragOver={(e) => e.preventDefault()}\n onDragLeave={(e) => {\n e.preventDefault()\n dragDepth.current--\n if (dragDepth.current <= 0) {\n dragDepth.current = 0\n setIsDragOver(false)\n }\n }}\n onDrop={(e) => {\n e.preventDefault()\n dragDepth.current = 0\n setIsDragOver(false)\n processFiles(e.dataTransfer.files)\n }}\n data-drag-over={isDragOver || undefined}\n className={cn(\n \"flex w-full cursor-pointer flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed border-neutral-border bg-background px-6 py-10 text-center outline-none transition-colors\",\n \"hover:border-ring hover:bg-accent/50\",\n \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n isDragOver && \"border-ring bg-accent/50\",\n lastError && \"border-destructive\",\n disabled && \"pointer-events-none opacity-50\"\n )}\n >\n <span\n aria-hidden\n className={cn(\n \"flex size-10 items-center justify-center rounded-full bg-accent text-foreground\",\n isDragOver && \"text-primary\"\n )}\n >\n <CycleIcon icon={Upload} size=\"sm\" decorative />\n </span>\n <span className=\"text-sm font-medium text-foreground\">\n {labels?.instruction ?? \"Arraste um arquivo ou clique para selecionar\"}\n </span>\n {hint && <span className=\"text-xs text-muted-foreground\">{hint}</span>}\n </button>\n\n {lastError && (\n <p role=\"alert\" className=\"mt-2 flex items-center gap-1.5 text-sm text-destructive\">\n <CycleIcon icon={AlertCircle} size=\"xs\" decorative />\n {lastError}\n </p>\n )}\n\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n className=\"sr-only\"\n tabIndex={-1}\n onChange={(e) => {\n processFiles(e.target.files)\n e.target.value = \"\" // permite re-selecionar o mesmo arquivo\n }}\n />\n </div>\n )\n}\n\n/* ─── Item da lista de arquivos ─── */\n\nexport type FileDropzoneItemStatus = \"idle\" | \"uploading\" | \"success\" | \"error\"\n\nexport interface FileDropzoneItemProps extends React.ComponentProps<\"div\"> {\n /** Nome do arquivo */\n name: string\n /** Tamanho em bytes (formatado em PT-BR) */\n size?: number\n /** Estado do upload — o upload em si é do consumidor */\n status?: FileDropzoneItemStatus\n /** Progresso 0–100 (exibido quando status=\"uploading\") */\n progress?: number\n /** Mensagem exibida quando status=\"error\" */\n error?: string\n /** Remove o arquivo da lista (também usado para cancelar) */\n onRemove?: () => void\n /** Label do botão remover (default: \"Remover arquivo\") */\n removeLabel?: string\n}\n\nfunction FileDropzoneItem({\n name,\n size,\n status = \"idle\",\n progress,\n error,\n onRemove,\n removeLabel = \"Remover arquivo\",\n className,\n ...props\n}: FileDropzoneItemProps) {\n return (\n <div\n data-slot=\"file-dropzone-item\"\n data-status={status}\n className={cn(\n \"flex items-center gap-3 rounded-lg border border-border bg-card p-3\",\n status === \"error\" && \"border-destructive/50\",\n className\n )}\n {...props}\n >\n <span\n aria-hidden\n className=\"flex size-9 shrink-0 items-center justify-center rounded-md bg-accent text-foreground\"\n >\n <CycleIcon icon={FileIcon} size=\"sm\" decorative />\n </span>\n\n <div className=\"min-w-0 flex-1 space-y-1\">\n <div className=\"flex items-baseline gap-2\">\n <p className=\"truncate text-sm font-medium text-foreground\">{name}</p>\n {size !== undefined && (\n <span className=\"shrink-0 text-xs text-muted-foreground\">{formatFileSize(size)}</span>\n )}\n </div>\n {status === \"uploading\" && (\n <Progress value={progress ?? 0} size=\"xs\" aria-label={`Enviando ${name}`} />\n )}\n {status === \"error\" && error && (\n <p className=\"text-xs text-destructive\">{error}</p>\n )}\n </div>\n\n {status === \"uploading\" && <Spinner size=\"sm\" className=\"shrink-0 text-muted-foreground\" />}\n {status === \"success\" && (\n <span aria-hidden className=\"shrink-0 text-positive\">\n <CycleIcon icon={Check} size=\"sm\" decorative />\n </span>\n )}\n\n {onRemove && (\n <button\n type=\"button\"\n aria-label={removeLabel}\n onClick={onRemove}\n className=\"flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <CycleIcon icon={X} size=\"sm\" decorative />\n </button>\n )}\n </div>\n )\n}\n\nexport { FileDropzone, FileDropzoneItem }\n"]}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
- export { ChatThread } from '../chunk-OFGJDRRR.js';
2
+ export { ChatThread } from '../chunk-OZCMOOT7.js';
3
3
  import '../chunk-MQTQIX4N.js';
4
- import '../chunk-VZ2GYSNK.js';
4
+ import '../chunk-OE4CHHIW.js';
5
5
  import '../chunk-CLL76ZXJ.js';
6
6
  import '../chunk-T2TJG24M.js';
7
7
  import '../chunk-YIOXPV2N.js';
package/dist/index.js CHANGED
@@ -24,16 +24,16 @@ export { Toaster, cycleToast } from './chunk-7ZSICZXA.js';
24
24
  export { Grid, GridItem } from './chunk-325K2R3T.js';
25
25
  export { FileCard, fileCardVariants } from './chunk-ULVUD6RZ.js';
26
26
  export { ChatBubble, chatBubbleVariants } from './chunk-VHNPIT56.js';
27
- export { ChatThread } from './chunk-OFGJDRRR.js';
27
+ export { ChatThread } from './chunk-OZCMOOT7.js';
28
28
  export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger } from './chunk-MQTQIX4N.js';
29
- export { ChatAudio, ChatMessage, chatMessageVariants } from './chunk-VZ2GYSNK.js';
29
+ export { ChatAudio, ChatMessage, chatMessageVariants } from './chunk-OE4CHHIW.js';
30
30
  export { LikeDislike, likeDislikeVariants } from './chunk-CLL76ZXJ.js';
31
31
  export { Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage } from './chunk-T2TJG24M.js';
32
32
  export { MessageRating } from './chunk-YIOXPV2N.js';
33
33
  export { MessageBar } from './chunk-5AH2PNYW.js';
34
34
  export { ProgressStage } from './chunk-OM4W6UQG.js';
35
35
  export { ProgressDot, dotVariants, progressDotVariants } from './chunk-E7MMJTJY.js';
36
- export { CircularProgress, circularProgressVariants } from './chunk-KNJTTOAD.js';
36
+ export { CircularProgress, circularProgressVariants } from './chunk-2NVKK7UB.js';
37
37
  export { ERROR_STATE_PRESETS, ErrorState, ErrorStateActions, ErrorStateDescription, ErrorStateMedia, ErrorStateMeta, ErrorStateTitle } from './chunk-J3QIX7EZ.js';
38
38
  export { StatusBanner, useCountdown } from './chunk-VMUNB4RA.js';
39
39
  export { Container, containerVariants } from './chunk-JRQLKRNY.js';
@@ -54,9 +54,9 @@ export { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, Pagi
54
54
  export { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from './chunk-T5U4Q37E.js';
55
55
  export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from './chunk-NMIDTIUE.js';
56
56
  export { Stepper } from './chunk-ONEVPGJY.js';
57
- export { FileDropzone, FileDropzoneItem, formatFileSize } from './chunk-WVCNTN4F.js';
57
+ export { FileDropzone, FileDropzoneItem, formatFileSize } from './chunk-R7QITHNQ.js';
58
58
  export { Spinner } from './chunk-TDBDX5NV.js';
59
- export { Progress, indicatorVariants, progressVariants } from './chunk-GDPUWRNH.js';
59
+ export { Progress, indicatorVariants, progressVariants } from './chunk-2MGHBZ7I.js';
60
60
  import './chunk-N2F3ZG2S.js';
61
61
  export { Achievement, Ai, Answer, Badge as BadgeIcon, Certificate, Chat as ChatIcon, Checkpoint, Completion, Conversation, Course, Deadline, Dialogue, Dictionary, Diploma, DotLive, Exercise, Feedback, Flashcard, Fluency, Goal, Grammar, GroupClass, Highlight, Language, Lesson, Listening, Live, MemoryCard, Milestone, Module, Presentation, PrivateClass, Progress as ProgressIcon, Question, Quiz, Ray, Reading, Recap, RecordedClass, Recurring, Repetition, Required, Schedule, Sentence, Streak, Task, Translate, Unit, Vocabulary, Whiteboard } from './chunk-KLX547KZ.js';
62
62
  export { CycleIcon, ICON_SIZES } from './chunk-US5LXJTO.js';
package/dist/lib/utils.js CHANGED
@@ -1,4 +1,3 @@
1
- "use client";
2
1
  import '../chunk-LYTC7UMN.js';
3
2
  export { cn } from '../chunk-W4KJCBMW.js';
4
3
  import '../chunk-2NMEKWO5.js';
@@ -333,16 +333,16 @@
333
333
 
334
334
  .theme-brand {
335
335
  --background: #FFE8E8;
336
- --foreground: #D45558;
336
+ --foreground: #C83336;
337
337
  --card: #FFE8E8;
338
- --card-foreground: #D45558;
338
+ --card-foreground: #C83336;
339
339
  --popover: #FFE8E8;
340
- --popover-foreground: #D45558;
340
+ --popover-foreground: #C83336;
341
341
  --muted: #FFD0D1;
342
342
  --muted-foreground: #8C2D2F;
343
343
  --accent: #FFE8E8;
344
344
  --accent-foreground: #6B1E20;
345
- --primary: #D45558;
345
+ --primary: #D04649;
346
346
  --primary-foreground: #FFFFFF;
347
347
  --secondary: #FFE8E8;
348
348
  --secondary-foreground: #0a0a0a;
@@ -373,16 +373,16 @@
373
373
 
374
374
  .theme-private {
375
375
  --background: #FFEDE0;
376
- --foreground: #E56530;
376
+ --foreground: #BC4818;
377
377
  --card: #FFEDE0;
378
- --card-foreground: #E56530;
378
+ --card-foreground: #BC4818;
379
379
  --popover: #FFEDE0;
380
- --popover-foreground: #E56530;
380
+ --popover-foreground: #BC4818;
381
381
  --muted: #FFD9C2;
382
382
  --muted-foreground: #993A16;
383
383
  --accent: #FFEDE0;
384
384
  --accent-foreground: #73270E;
385
- --primary: #E56530;
385
+ --primary: #CB4E1A;
386
386
  --primary-foreground: #FFFFFF;
387
387
  --secondary: #FFEDE0;
388
388
  --secondary-foreground: #0a0a0a;
@@ -393,11 +393,11 @@
393
393
 
394
394
  .theme-group {
395
395
  --background: #E0FFE9;
396
- --foreground: #00C234;
396
+ --foreground: #008423;
397
397
  --card: #E0FFE9;
398
- --card-foreground: #00C234;
398
+ --card-foreground: #008423;
399
399
  --popover: #E0FFE9;
400
- --popover-foreground: #00C234;
400
+ --popover-foreground: #008423;
401
401
  --muted: #B8FFD0;
402
402
  --muted-foreground: #00731E;
403
403
  --accent: #E0FFE9;
@@ -433,16 +433,16 @@
433
433
 
434
434
  .theme-positive {
435
435
  --background: #E0FAF0;
436
- --foreground: #098A5E;
436
+ --foreground: #088058;
437
437
  --card: #E0FAF0;
438
- --card-foreground: #098A5E;
438
+ --card-foreground: #088058;
439
439
  --popover: #E0FAF0;
440
- --popover-foreground: #098A5E;
440
+ --popover-foreground: #088058;
441
441
  --muted: #B8F2D9;
442
442
  --muted-foreground: #05523A;
443
443
  --accent: #E0FAF0;
444
444
  --accent-foreground: #033526;
445
- --primary: #098A5E;
445
+ --primary: #09875C;
446
446
  --primary-foreground: #FFFFFF;
447
447
  --secondary: #E0FAF0;
448
448
  --secondary-foreground: #0a0a0a;
@@ -453,16 +453,16 @@
453
453
 
454
454
  .theme-warning {
455
455
  --background: #FFF5D9;
456
- --foreground: #D48E00;
456
+ --foreground: #996600;
457
457
  --card: #FFF5D9;
458
- --card-foreground: #D48E00;
458
+ --card-foreground: #996600;
459
459
  --popover: #FFF5D9;
460
- --popover-foreground: #D48E00;
460
+ --popover-foreground: #996600;
461
461
  --muted: #FEEAB0;
462
462
  --muted-foreground: #7D5400;
463
463
  --accent: #FFF5D9;
464
464
  --accent-foreground: #523800;
465
- --primary: #D48E00;
465
+ --primary: #A16C00;
466
466
  --primary-foreground: #FFFFFF;
467
467
  --secondary: #FFF5D9;
468
468
  --secondary-foreground: #0a0a0a;
@@ -505,7 +505,7 @@
505
505
  --muted-foreground: #FFB0B2;
506
506
  --accent: rgba(237, 106, 109, 0.3);
507
507
  --accent-foreground: #FFE8E8;
508
- --primary: #ED6A6D;
508
+ --primary: #E4272B;
509
509
  --primary-foreground: #FFFFFF;
510
510
  --secondary: rgba(237, 106, 109, 0.1);
511
511
  --secondary-foreground: #ffffff;
@@ -526,7 +526,7 @@
526
526
  --muted-foreground: #99BBFF;
527
527
  --accent: rgba(46, 116, 255, 0.3);
528
528
  --accent-foreground: #E0EBFF;
529
- --primary: #2E74FF;
529
+ --primary: #226CFF;
530
530
  --primary-foreground: #FFFFFF;
531
531
  --secondary: rgba(46, 116, 255, 0.1);
532
532
  --secondary-foreground: #ffffff;
@@ -547,7 +547,7 @@
547
547
  --muted-foreground: #FFC09A;
548
548
  --accent: rgba(255, 128, 65, 0.3);
549
549
  --accent-foreground: #FFEDE0;
550
- --primary: #FF8041;
550
+ --primary: #D34600;
551
551
  --primary-foreground: #FFFFFF;
552
552
  --secondary: rgba(255, 128, 65, 0.1);
553
553
  --secondary-foreground: #ffffff;
@@ -610,7 +610,7 @@
610
610
  --muted-foreground: #80E8BC;
611
611
  --accent: rgba(11, 163, 112, 0.3);
612
612
  --accent-foreground: #E0FAF0;
613
- --primary: #0BA370;
613
+ --primary: #09875D;
614
614
  --primary-foreground: #FFFFFF;
615
615
  --secondary: rgba(11, 163, 112, 0.1);
616
616
  --secondary-foreground: #ffffff;
@@ -632,7 +632,7 @@
632
632
  --accent: rgba(245, 168, 0, 0.3);
633
633
  --accent-foreground: #FFF5D9;
634
634
  --primary: #F5A800;
635
- --primary-foreground: #FFFFFF;
635
+ --primary-foreground: #0a0a0a;
636
636
  --secondary: rgba(245, 168, 0, 0.1);
637
637
  --secondary-foreground: #ffffff;
638
638
  --border: #F5A800;
@@ -40,8 +40,25 @@ interface ChatMessageProps extends Omit<React.ComponentProps<"div">, "content">,
40
40
  theme?: string;
41
41
  }
42
42
  interface ChatAudioProps extends React.ComponentProps<"div"> {
43
- /** URL do audio */
44
- src: string;
43
+ /**
44
+ * URL do audio. Opcional: sem `src` o componente vira so a casca visual e o
45
+ * consumidor dirige o estado (`playing`/`onPlayingChange`, `currentTime`/`duration`)
46
+ * — ex.: TTS via speechSynthesis, que nao gera arquivo.
47
+ */
48
+ src?: string;
49
+ /**
50
+ * Controla play/pause de fora. Definido = modo controlado: o botao so chama
51
+ * `onPlayingChange` e nao muda sozinho. Com `src`, o <audio> segue a prop.
52
+ */
53
+ playing?: boolean;
54
+ /** Chamado quando o usuario pede play/pause (e, com `src`, quando o <audio> muda de estado). */
55
+ onPlayingChange?: (playing: boolean) => void;
56
+ /** Tempo atual em segundos, fornecido pelo consumidor. Sobrescreve o do <audio>. */
57
+ currentTime?: number;
58
+ /** Duracao em segundos, fornecida pelo consumidor. Sobrescreve a do <audio>. */
59
+ duration?: number;
60
+ /** Chamado ao trocar a velocidade no badge (ex.: repassar ao `rate` do speechSynthesis). */
61
+ onSpeedChange?: (speed: number) => void;
45
62
  /**
46
63
  * Variante visual:
47
64
  * - `neutral`: container `bg-muted` (cinza claro) — usado em AI messages
@@ -63,7 +80,7 @@ interface ChatAudioProps extends React.ComponentProps<"div"> {
63
80
  */
64
81
  barCount?: number;
65
82
  }
66
- declare function ChatAudio({ src, variant, peaks: providedPeaks, barCount, className, ...props }: ChatAudioProps): react_jsx_runtime.JSX.Element;
83
+ declare function ChatAudio({ src, variant, peaks: providedPeaks, barCount, playing: playingProp, onPlayingChange, currentTime: currentTimeProp, duration: durationProp, onSpeedChange, className, ...props }: ChatAudioProps): react_jsx_runtime.JSX.Element;
67
84
 
68
85
  declare function ChatMessage({ persona, text, audioSrc, avatar, author, showFeedback, feedbackValue, onFeedbackChange, loading, theme, className, ...props }: ChatMessageProps): react_jsx_runtime.JSX.Element;
69
86
 
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- export { ChatAudio, ChatMessage, chatMessageVariants } from '../chunk-VZ2GYSNK.js';
2
+ export { ChatAudio, ChatMessage, chatMessageVariants } from '../chunk-OE4CHHIW.js';
3
3
  import '../chunk-CLL76ZXJ.js';
4
4
  import '../chunk-T2TJG24M.js';
5
5
  import '../chunk-N2F3ZG2S.js';
@@ -15,8 +15,12 @@ interface CircularProgressProps extends Omit<React.ComponentProps<"div">, "child
15
15
  autoTheme?: boolean;
16
16
  /** Mostrar label central com a porcentagem (default true) */
17
17
  showLabel?: boolean;
18
- /** Label customizado — sobrescreve `${value}%` */
19
- label?: string;
18
+ /**
19
+ * Label customizado — sobrescreve `${value}%`. Aceita ReactNode para tipografar o
20
+ * centro (ex.: numero grande + unidade menor). A fonte padrao do size continua
21
+ * aplicada no wrapper; elementos internos podem sobrescrever.
22
+ */
23
+ label?: React.ReactNode;
20
24
  }
21
25
  declare function CircularProgress({ value, size, theme, autoTheme, showLabel, label, className, ...props }: CircularProgressProps): react_jsx_runtime.JSX.Element;
22
26
 
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- export { CircularProgress, circularProgressVariants } from '../chunk-KNJTTOAD.js';
2
+ export { CircularProgress, circularProgressVariants } from '../chunk-2NVKK7UB.js';
3
3
  import '../chunk-LYTC7UMN.js';
4
4
  import '../chunk-W4KJCBMW.js';
5
5
  import '../chunk-2NMEKWO5.js';
@@ -1,7 +1,7 @@
1
1
  "use client";
2
- export { FileDropzone, FileDropzoneItem, formatFileSize } from '../chunk-WVCNTN4F.js';
2
+ export { FileDropzone, FileDropzoneItem, formatFileSize } from '../chunk-R7QITHNQ.js';
3
3
  import '../chunk-TDBDX5NV.js';
4
- import '../chunk-GDPUWRNH.js';
4
+ import '../chunk-2MGHBZ7I.js';
5
5
  import '../chunk-N2F3ZG2S.js';
6
6
  import '../chunk-KLX547KZ.js';
7
7
  import '../chunk-US5LXJTO.js';