{"version":3,"sources":["../src/components/Chatbot/Chatbot.tsx","../src/components/Chatbot/ChatbotFab.tsx","../src/components/Chatbot/ChatbotPanel.tsx","../src/components/Chatbot/ChatbotMessageList.tsx","../src/components/Chatbot/ChatbotMessage.tsx","../src/components/Chatbot/ChatbotContentRenderer.tsx","../src/components/Chatbot/ChatbotTypingIndicator.tsx","../src/components/Chatbot/ChatbotInput.tsx","../src/components/Chatbot/ChatbotConversationList.tsx"],"sourcesContent":["import { useRef } from 'react';\nimport ChatbotFab from './ChatbotFab';\nimport ChatbotPanel from './ChatbotPanel';\nimport ChatbotMessageList from './ChatbotMessageList';\nimport ChatbotInput from './ChatbotInput';\nimport ChatbotConversationList from './ChatbotConversationList';\nimport { createUseChatbot } from '../../hooks/useChatbot';\nimport type {\n  ChatbotApiClient,\n  ChatbotCurrentContext,\n  ChatbotUser,\n} from '../../types/chatbot';\n\n/**\n * Props for the root Chatbot component\n */\nexport interface ChatbotProps {\n  /** API client that wraps REST calls — produced by the host app */\n  apiClient: ChatbotApiClient;\n  /** Authenticated user (used only for UI decoration today) */\n  user: ChatbotUser;\n  /** Optional context sent with every outgoing message */\n  currentContext?: ChatbotCurrentContext;\n  /** Extra tailwind classes on the FAB */\n  fabClassName?: string;\n  /** Extra tailwind classes on the panel */\n  panelClassName?: string;\n}\n\n/**\n * Analytica student chatbot — floating assistant available in the\n * authenticated area. Composes the Fab + Panel and owns no state of its\n * own: the `useChatbot` hook (bound to the provided `apiClient`) keeps\n * everything in one place.\n *\n * Host apps should render this inside the layout so the FAB is reachable\n * from every page. `currentContext` is a hint (activityId, questionId,\n * lessonId) produced by the host to enrich the AI prompt — it flows\n * untouched to the backend.\n */\nexport default function Chatbot({\n  apiClient,\n  user,\n  currentContext,\n  fabClassName,\n  panelClassName,\n}: Readonly<ChatbotProps>) {\n  // Keep the latest apiClient in a ref — any `apiClient` prop swap (e.g.,\n  // after re-auth with a new token-bound client) is seen by the hook on\n  // its next call without tearing down internal state.\n  const apiClientRef = useRef(apiClient);\n  apiClientRef.current = apiClient;\n\n  // Bind the factory exactly once, passing a getter so the hook resolves\n  // `apiClientRef.current` fresh on every call instead of snapshotting\n  // whatever client we saw on first mount.\n  const boundHookRef = useRef<ReturnType<typeof createUseChatbot> | null>(null);\n  boundHookRef.current ??= createUseChatbot(() => apiClientRef.current);\n  const useBoundChatbot = boundHookRef.current;\n  const {\n    isOpen,\n    isSending,\n    isLoadingHistory,\n    isLoadingMessages,\n    errorMessage,\n    conversations,\n    activeConversationId,\n    messages,\n    togglePanel,\n    closePanel,\n    selectConversation,\n    startNewConversation,\n    sendMessage,\n    deleteConversation,\n  } = useBoundChatbot();\n\n  const firstName = user.name?.trim().split(/\\s+/)[0];\n  const emptyHint = firstName\n    ? `Olá, ${firstName}! Como posso ajudar nos seus estudos?`\n    : 'Comece a conversa enviando uma pergunta.';\n\n  const handleSend = (text: string): void => {\n    sendMessage(text, currentContext).catch(() => undefined);\n  };\n\n  return (\n    <>\n      {!isOpen && (\n        <ChatbotFab\n          onClick={togglePanel}\n          isOpen={isOpen}\n          className={fabClassName}\n        />\n      )}\n      <ChatbotPanel\n        isOpen={isOpen}\n        onClose={closePanel}\n        onStartNew={startNewConversation}\n        className={panelClassName}\n        errorMessage={errorMessage}\n        historySlot={\n          <ChatbotConversationList\n            conversations={conversations}\n            activeConversationId={activeConversationId}\n            isLoading={isLoadingHistory}\n            onSelect={selectConversation}\n            onDelete={deleteConversation}\n            onStartNew={startNewConversation}\n          />\n        }\n        messagesSlot={\n          <ChatbotMessageList\n            messages={messages}\n            isSending={isSending}\n            isLoading={isLoadingMessages}\n            emptyHint={emptyHint}\n          />\n        }\n        inputSlot={<ChatbotInput onSend={handleSend} disabled={isSending} />}\n      />\n    </>\n  );\n}\n","import { ChatCircleDotsIcon } from '@phosphor-icons/react';\nimport Button from '../Button/Button';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Props for the floating action button that opens the chatbot panel\n */\nexport interface ChatbotFabProps {\n  /** Click handler — opens or toggles the panel */\n  onClick: () => void;\n  /** Whether the panel is currently open (drives aria + styling) */\n  isOpen?: boolean;\n  /** Optional unread count badge */\n  unreadCount?: number;\n  /** Extra tailwind classes */\n  className?: string;\n}\n\n/**\n * Circular floating action button anchored bottom-right of the viewport.\n * Uses `fixed` so it sits on top of page scroll.\n */\nexport default function ChatbotFab({\n  onClick,\n  isOpen = false,\n  unreadCount = 0,\n  className,\n}: Readonly<ChatbotFabProps>) {\n  const noun = unreadCount === 1 ? 'mensagem não lida' : 'mensagens não lidas';\n  let badgeLabel: string;\n  if (unreadCount > 9) {\n    badgeLabel = 'Mais de 9 mensagens não lidas';\n  } else {\n    badgeLabel = `${unreadCount} ${noun}`;\n  }\n\n  const baseAriaLabel = isOpen\n    ? 'Fechar assistente'\n    : 'Abrir assistente de estudos';\n  const ariaLabel =\n    unreadCount > 0 ? `${baseAriaLabel} (${badgeLabel})` : baseAriaLabel;\n\n  return (\n    <Button\n      variant=\"raw\"\n      onClick={onClick}\n      aria-label={ariaLabel}\n      aria-expanded={isOpen}\n      className={cn(\n        'fixed bottom-6 right-6 z-40',\n        'flex h-14 w-14 items-center justify-center rounded-full',\n        'bg-primary-500 text-white shadow-lg',\n        'transition-all hover:bg-primary-600 active:scale-95',\n        'focus:outline-none focus:ring-2 focus:ring-primary-300',\n        className\n      )}\n    >\n      <ChatCircleDotsIcon size={26} weight=\"fill\" />\n      {unreadCount > 0 && (\n        <Text\n          as=\"span\"\n          size=\"xs\"\n          weight=\"bold\"\n          color=\"text-white\"\n          data-testid=\"chatbot-fab-badge\"\n          aria-live=\"polite\"\n          aria-label={badgeLabel}\n          className=\"absolute -top-1 -right-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-error-500 px-1\"\n        >\n          {unreadCount > 9 ? '9+' : unreadCount}\n        </Text>\n      )}\n    </Button>\n  );\n}\n","import { useEffect, useRef, useState, type ReactNode } from 'react';\nimport {\n  CaretUpIcon,\n  ListIcon,\n  MinusIcon,\n  PlusIcon,\n  RobotIcon,\n  XIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport IconButton from '../IconButton/IconButton';\nimport { cn } from '../../utils/utils';\n\nconst HEADER_ICON_BUTTON_CLASSES =\n  '!text-white hover:!bg-white/15 hover:!text-white focus-visible:!ring-white/60';\n\n/**\n * Props for the chatbot panel shell\n */\nexport interface ChatbotPanelProps {\n  isOpen: boolean;\n  onClose: () => void;\n  onStartNew: () => void;\n  /** Conversation list passed as a node so the panel stays layout-only */\n  historySlot: ReactNode;\n  /** Messages list passed as a node (allows easy swap during tests) */\n  messagesSlot: ReactNode;\n  /** Input area slot (auto-resize textarea + send button) */\n  inputSlot: ReactNode;\n  /** Optional error banner (string shown above the input) */\n  errorMessage?: string | null;\n  className?: string;\n}\n\n/**\n * Fixed drawer-like panel anchored to the bottom-right of the screen.\n * Splits into optional history sidebar + main conversation area. The\n * panel is purely presentational — state lives in the `useChatbot` hook.\n */\nexport default function ChatbotPanel({\n  isOpen,\n  onClose,\n  onStartNew,\n  historySlot,\n  messagesSlot,\n  inputSlot,\n  errorMessage,\n  className,\n}: Readonly<ChatbotPanelProps>) {\n  const [showHistory, setShowHistory] = useState(false);\n  const [isMinimized, setIsMinimized] = useState(false);\n  const closeButtonRef = useRef<HTMLButtonElement>(null);\n  const previouslyFocusedRef = useRef<Element | null>(null);\n  const previousErrorRef = useRef<string | null | undefined>(errorMessage);\n\n  // Reset the minimized state every time the panel is reopened so the\n  // user always lands on the full conversation view.\n  useEffect(() => {\n    if (isOpen) {\n      setIsMinimized(false);\n    }\n  }, [isOpen]);\n\n  // Auto-expand on new errors so the role=\"alert\" banner is mounted and\n  // announced by screen readers. Without this, a failed send while\n  // minimized would be silent — the body (including the alert node) is\n  // unmounted in the minimized state. Compare against the previous value\n  // (not just truthy/falsy) so a distinct truthy error arriving after the\n  // user minimized also re-expands and re-announces.\n  useEffect(() => {\n    const previous = previousErrorRef.current;\n    if (errorMessage && errorMessage !== previous) {\n      setIsMinimized(false);\n    }\n    previousErrorRef.current = errorMessage;\n  }, [errorMessage]);\n\n  // Escape-key dismissal + focus management: when the panel opens we\n  // remember the previously focused element, move focus to the close\n  // button, and listen for Escape to close. On close, focus is restored.\n  useEffect(() => {\n    if (!isOpen) return;\n    previouslyFocusedRef.current = document.activeElement;\n    closeButtonRef.current?.focus();\n\n    const handleKeyDown = (event: KeyboardEvent) => {\n      if (event.key === 'Escape') {\n        event.stopPropagation();\n        onClose();\n      }\n    };\n    globalThis.addEventListener('keydown', handleKeyDown);\n\n    return () => {\n      globalThis.removeEventListener('keydown', handleKeyDown);\n      const previous = previouslyFocusedRef.current as HTMLElement | null;\n      if (previous && typeof previous.focus === 'function') {\n        previous.focus();\n      }\n    };\n  }, [isOpen, onClose]);\n\n  if (!isOpen) return null;\n\n  return (\n    <dialog\n      open\n      aria-label=\"Assistente de estudos\"\n      // Height uses an inline style instead of a Tailwind arbitrary\n      // class so host apps don't need to add an `@source` directive\n      // for Tailwind v4 to pick up an otherwise uncommon utility.\n      style={\n        isMinimized ? undefined : { height: 'min(720px, calc(100vh - 3rem))' }\n      }\n      className={cn(\n        // `<dialog open>` comes with `margin: auto` by default; `m-0`\n        // resets that so the `fixed` positioning below pins the panel\n        // to the bottom-right as intended. The panel sits at the same\n        // bottom-right corner as the FAB (the FAB is hidden while the\n        // panel is open to avoid stacking).\n        'fixed bottom-6 right-6 z-40 m-0',\n        // Responsive sizing: fills small viewports without overflowing,\n        // yet caps at the intended desktop footprint on larger screens.\n        // The panel widens when the history sidebar is visible so the\n        // main chat column keeps enough horizontal room for messages.\n        'flex w-[calc(100vw-3rem)] flex-col overflow-hidden rounded-2xl p-0',\n        showHistory\n          ? 'max-w-[620px] sm:max-w-[640px]'\n          : 'max-w-[400px] sm:max-w-[420px]',\n        'border border-background-200 bg-background shadow-2xl',\n        className\n      )}\n    >\n      <div className=\"flex items-center justify-between gap-2 bg-primary-500 px-4 py-3 text-white\">\n        <div className=\"flex min-w-0 items-center gap-3\">\n          <div\n            aria-hidden=\"true\"\n            className=\"flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-white/15 text-white\"\n          >\n            <RobotIcon size={20} weight=\"fill\" />\n          </div>\n          <div className=\"flex min-w-0 flex-col\">\n            <Text\n              size=\"md\"\n              weight=\"semibold\"\n              className=\"truncate leading-tight text-white\"\n            >\n              Assistente de estudos\n            </Text>\n            <Text size=\"2xs\" className=\"truncate leading-tight text-white/80\">\n              Tire dúvidas enquanto estuda\n            </Text>\n          </div>\n        </div>\n        <div className=\"flex items-center gap-1\">\n          {!isMinimized && (\n            <>\n              <IconButton\n                size=\"sm\"\n                aria-label={\n                  showHistory ? 'Ocultar histórico' : 'Mostrar histórico'\n                }\n                onClick={() => setShowHistory((v) => !v)}\n                icon={<ListIcon size={18} />}\n                className={HEADER_ICON_BUTTON_CLASSES}\n              />\n              <IconButton\n                size=\"sm\"\n                aria-label=\"Nova conversa\"\n                onClick={onStartNew}\n                icon={<PlusIcon size={18} />}\n                className={HEADER_ICON_BUTTON_CLASSES}\n              />\n            </>\n          )}\n          <IconButton\n            size=\"sm\"\n            aria-label={\n              isMinimized ? 'Expandir assistente' : 'Minimizar assistente'\n            }\n            aria-expanded={!isMinimized}\n            onClick={() => setIsMinimized((v) => !v)}\n            icon={\n              isMinimized ? <CaretUpIcon size={18} /> : <MinusIcon size={18} />\n            }\n            className={HEADER_ICON_BUTTON_CLASSES}\n          />\n          <IconButton\n            ref={closeButtonRef}\n            size=\"sm\"\n            aria-label=\"Fechar assistente\"\n            onClick={onClose}\n            icon={<XIcon size={18} />}\n            className={HEADER_ICON_BUTTON_CLASSES}\n          />\n        </div>\n      </div>\n\n      {!isMinimized && (\n        <div className=\"flex flex-1 overflow-hidden border-t border-background-200\">\n          {showHistory && (\n            <div className=\"w-44 shrink-0 border-r border-background-200\">\n              {historySlot}\n            </div>\n          )}\n          <div className=\"flex flex-1 flex-col\">\n            {messagesSlot}\n            {errorMessage && (\n              <div\n                role=\"alert\"\n                aria-live=\"assertive\"\n                className=\"border-t border-error-200 bg-error-50 px-3 py-2\"\n              >\n                <Text size=\"xs\" className=\"text-error-700\">\n                  {errorMessage}\n                </Text>\n              </div>\n            )}\n            {inputSlot}\n          </div>\n        </div>\n      )}\n    </dialog>\n  );\n}\n","import { useEffect, useRef } from 'react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport ChatbotMessage from './ChatbotMessage';\nimport ChatbotTypingIndicator from './ChatbotTypingIndicator';\nimport type { ChatbotMessage as ChatbotMessageType } from '../../types/chatbot';\n\n/**\n * Props for the scrollable list of messages\n */\nexport interface ChatbotMessageListProps {\n  messages: ChatbotMessageType[];\n  isSending?: boolean;\n  isLoading?: boolean;\n  emptyHint?: string;\n  className?: string;\n}\n\n/**\n * Vertical list of messages with auto-scroll to bottom on new items.\n */\nexport default function ChatbotMessageList({\n  messages,\n  isSending = false,\n  isLoading = false,\n  emptyHint = 'Comece a conversa enviando uma pergunta.',\n  className,\n}: Readonly<ChatbotMessageListProps>) {\n  const bottomRef = useRef<HTMLDivElement>(null);\n  const didMountRef = useRef(false);\n\n  useEffect(() => {\n    // Skip the scroll on initial render — otherwise scrollIntoView could\n    // yank the page viewport before the panel is visible. Only auto-scroll\n    // on subsequent updates (new message, typing indicator changes).\n    if (!didMountRef.current) {\n      didMountRef.current = true;\n      return;\n    }\n    if (bottomRef.current) {\n      bottomRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' });\n    }\n  }, [messages.length, isSending]);\n\n  const showEmpty = !isLoading && messages.length === 0 && !isSending;\n\n  return (\n    <div\n      role=\"log\"\n      aria-live=\"polite\"\n      aria-relevant=\"additions\"\n      className={cn(\n        'flex-1 overflow-y-auto p-4 space-y-3',\n        'bg-background-50',\n        className\n      )}\n    >\n      {showEmpty && (\n        <div className=\"flex h-full items-center justify-center\">\n          <Text size=\"sm\" className=\"text-center text-text-400\">\n            {emptyHint}\n          </Text>\n        </div>\n      )}\n      {messages.map((msg) => (\n        <ChatbotMessage key={msg.id} message={msg} />\n      ))}\n      {isSending && (\n        <div className=\"flex justify-start\">\n          <ChatbotTypingIndicator />\n        </div>\n      )}\n      <div ref={bottomRef} />\n    </div>\n  );\n}\n","import { RobotIcon, UserIcon } from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport ChatbotContentRenderer from './ChatbotContentRenderer';\nimport {\n  CHATBOT_MESSAGE_ROLES,\n  type ChatbotMessage as ChatbotMessageType,\n} from '../../types/chatbot';\n\n/**\n * Props for a single message bubble\n */\nexport interface ChatbotMessageProps {\n  message: ChatbotMessageType;\n  className?: string;\n  /** Optional classes forwarded to the assistant markdown renderer. */\n  contentClassName?: string;\n}\n\n/**\n * Format a timestamp using pt-BR locale (hour and minute only)\n */\nfunction formatTimestamp(value: Date | string): string {\n  const date = value instanceof Date ? value : new Date(value);\n  if (Number.isNaN(date.getTime())) return '';\n  return date.toLocaleTimeString('pt-BR', {\n    hour: '2-digit',\n    minute: '2-digit',\n  });\n}\n\n/**\n * Individual chat bubble. User messages are aligned to the right with a\n * primary background; assistant messages sit on the left in a neutral\n * background and pass through markdown / LaTeX renderers.\n */\nexport default function ChatbotMessage({\n  message,\n  className,\n  contentClassName,\n}: Readonly<ChatbotMessageProps>) {\n  const isUser = message.role === CHATBOT_MESSAGE_ROLES.USER;\n  return (\n    <div\n      className={cn(\n        'flex w-full gap-2',\n        isUser ? 'justify-end' : 'justify-start',\n        className\n      )}\n    >\n      {!isUser && (\n        <div\n          role=\"img\"\n          aria-label=\"Mensagem do assistente\"\n          className=\"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-primary-100 text-primary-600\"\n        >\n          <RobotIcon size={18} weight=\"fill\" aria-hidden=\"true\" />\n        </div>\n      )}\n      <div\n        className={cn(\n          'max-w-[80%] px-4 py-2',\n          isUser\n            ? 'rounded-2xl rounded-br-md bg-background-200 text-text-900'\n            : 'rounded-2xl rounded-bl-md bg-background-100 text-text-900'\n        )}\n      >\n        {isUser ? (\n          <Text size=\"sm\" className=\"whitespace-pre-wrap break-words\">\n            {message.content}\n          </Text>\n        ) : (\n          <ChatbotContentRenderer\n            content={message.content}\n            className={contentClassName}\n          />\n        )}\n        <Text size=\"2xs\" className=\"mt-1 block text-right text-text-700\">\n          {formatTimestamp(message.createdAt)}\n        </Text>\n      </div>\n      {isUser && (\n        <div\n          role=\"img\"\n          aria-label=\"Sua mensagem\"\n          className=\"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-primary-500 text-white\"\n        >\n          <UserIcon size={18} weight=\"fill\" aria-hidden=\"true\" />\n        </div>\n      )}\n    </div>\n  );\n}\n","import ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport HtmlMathRenderer from '../HtmlMathRenderer/HtmlMathRenderer';\nimport { containsMath } from '../HtmlMathRenderer/utils';\nimport { cn } from '../../utils/utils';\n\n/**\n * Props for the assistant content renderer\n */\nexport interface ChatbotContentRendererProps {\n  content: string;\n  className?: string;\n}\n\n/**\n * Unambiguous math detection: only treats content as math when there is a\n * `$$...$$` display block, an explicit LaTeX block\n * (`\\begin{...}...\\end{...}`), or an inline `$...$` whose inner content\n * contains an unmistakably mathematical character (backslash / caret /\n * underscore / curly brace). This avoids false positives on prose that\n * contains currency like \"R$ 20\" or \"por $20 e o livro $ 5\".\n *\n * Instead of `matchAll` with a lazy regex (which would greedily consume\n * adjacent `$`s left-to-right and could miss a valid formula following a\n * currency span on the same line, e.g. \"Preço R$ 20 e a fórmula $x^2+1$\"),\n * we collect every `$` index and probe all pairs — detection is O(N²) in\n * the worst case but N here is the count of `$` on a single line, so in\n * practice it remains linear on realistic input.\n */\nconst MATH_CHAR_PATTERN = /[\\\\^_{}]/;\n\n/**\n * Collect every unescaped `$` index in the content.\n */\nfunction collectDollarIndices(content: string): number[] {\n  const result: number[] = [];\n  for (let i = 0; i < content.length; i++) {\n    if (content[i] === '$' && content[i - 1] !== '\\\\') {\n      result.push(i);\n    }\n  }\n  return result;\n}\n\n/**\n * Return true if any `$...$` pair spanning a single line contains a\n * math-only character.\n */\nfunction hasInlineMathSpan(content: string, indices: number[]): boolean {\n  for (let i = 0; i < indices.length; i++) {\n    for (let j = i + 1; j < indices.length; j++) {\n      const inner = content.slice(indices[i] + 1, indices[j]);\n      if (inner.includes('\\n')) break;\n      if (MATH_CHAR_PATTERN.test(inner)) return true;\n    }\n  }\n  return false;\n}\n\nfunction looksLikeMath(content: string): boolean {\n  if (!containsMath(content)) return false;\n  if (/\\$\\$[\\s\\S]+?\\$\\$/.test(content)) return true;\n  if (/\\\\begin\\{[^}]+\\}[\\s\\S]+?\\\\end\\{[^}]+\\}/.test(content)) return true;\n  return hasInlineMathSpan(content, collectDollarIndices(content));\n}\n\n/**\n * Renders a markdown string safely. When the content contains inline LaTeX\n * ($...$ or $$...$$) we fallback to `HtmlMathRenderer`, which already\n * supports math. For plain markdown we use `react-markdown` with GFM\n * (tables, task lists, strikethrough) and no raw HTML — escape by default.\n */\nexport default function ChatbotContentRenderer({\n  content,\n  className,\n}: Readonly<ChatbotContentRendererProps>) {\n  if (looksLikeMath(content)) {\n    return (\n      <HtmlMathRenderer\n        content={content}\n        className={cn('whitespace-pre-wrap break-words', className)}\n      />\n    );\n  }\n\n  return (\n    <div\n      className={cn(\n        'prose prose-sm max-w-none break-words',\n        // tighten prose defaults so the bubble feels compact\n        '[&>*:first-child]:mt-0 [&>*:last-child]:mb-0',\n        className\n      )}\n    >\n      {/*\n        SECURITY: do NOT add `rehype-raw` or any raw-HTML plugin to\n        `rehypePlugins` here. Chatbot content is untrusted (LLM output) and\n        must be escaped by react-markdown's default sanitization. Enabling\n        raw HTML would reintroduce an XSS vector — any change requires an\n        explicit security review and a dedicated sanitization pipeline.\n      */}\n      <ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>\n    </div>\n  );\n}\n","import Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Props for the typing indicator shown while the assistant is generating\n * a reply.\n */\nexport interface ChatbotTypingIndicatorProps {\n  className?: string;\n}\n\n/**\n * Three animated dots signalling \"assistant is typing\".\n * Implemented in pure Tailwind — no additional dependencies.\n */\nexport default function ChatbotTypingIndicator({\n  className,\n}: Readonly<ChatbotTypingIndicatorProps>) {\n  return (\n    <output\n      aria-label=\"Assistente digitando\"\n      className={cn(\n        'inline-flex items-center gap-1 rounded-2xl rounded-bl-md bg-background-100 px-4 py-3',\n        className\n      )}\n    >\n      <Text\n        as=\"span\"\n        className=\"h-2 w-2 animate-bounce rounded-full bg-text-400 [animation-delay:-0.3s]\"\n      />\n      <Text\n        as=\"span\"\n        className=\"h-2 w-2 animate-bounce rounded-full bg-text-400 [animation-delay:-0.15s]\"\n      />\n      <Text\n        as=\"span\"\n        className=\"h-2 w-2 animate-bounce rounded-full bg-text-400\"\n      />\n    </output>\n  );\n}\n","import { useCallback, useState, type KeyboardEvent } from 'react';\nimport { PaperPlaneTiltIcon } from '@phosphor-icons/react';\nimport Button from '../Button/Button';\nimport TextArea from '../TextArea/TextArea';\nimport { cn } from '../../utils/utils';\n\nconst MIN_HEIGHT = 40;\nconst MAX_HEIGHT = 120;\n\n/**\n * Props for the chatbot text input\n */\nexport interface ChatbotInputProps {\n  onSend: (text: string) => void;\n  disabled?: boolean;\n  placeholder?: string;\n  className?: string;\n}\n\n/**\n * Auto-resizing textarea + send button. Enter sends, Shift+Enter creates a\n * new line, as is standard for chat UIs.\n *\n * Uses the library's `<TextArea>` component (design-system compliant)\n * with its built-in `autoResize` — no manual DOM height manipulation.\n */\nexport default function ChatbotInput({\n  onSend,\n  disabled = false,\n  placeholder = 'Pergunte ao assistente...',\n  className,\n}: Readonly<ChatbotInputProps>) {\n  const [value, setValue] = useState('');\n\n  const submit = useCallback(() => {\n    const trimmed = value.trim();\n    if (!trimmed || disabled) return;\n    onSend(trimmed);\n    setValue('');\n  }, [disabled, onSend, value]);\n\n  const handleKeyDown = useCallback(\n    (e: KeyboardEvent<HTMLTextAreaElement>) => {\n      // Ignore Enter while an IME composition is in progress (e.g. typing\n      // Japanese/Chinese), otherwise confirming the composition would\n      // submit the message prematurely.\n      if (e.nativeEvent.isComposing) return;\n      if (e.key === 'Enter' && !e.shiftKey) {\n        e.preventDefault();\n        submit();\n      }\n    },\n    [submit]\n  );\n\n  const isSendDisabled = disabled || value.trim().length === 0;\n\n  return (\n    <div\n      className={cn(\n        'flex items-center gap-2 border-t border-background-200 bg-background p-3',\n        className\n      )}\n    >\n      {/* `TextArea` wraps its <textarea> in a flex column, so `flex-1`\n          needs to live on this outer wrapper — passing it via\n          `className` would only affect the inner element. */}\n      <div className=\"flex-1 min-w-0\">\n        <TextArea\n          value={value}\n          onChange={(e) => setValue(e.target.value)}\n          onKeyDown={handleKeyDown}\n          placeholder={placeholder}\n          disabled={disabled}\n          autoResize\n          minHeight={MIN_HEIGHT}\n          aria-label=\"Mensagem para o assistente\"\n          className=\"w-full\"\n          style={{ maxHeight: MAX_HEIGHT, overflowY: 'auto' }}\n        />\n      </div>\n      {/* Use the library's `<Button>` with `variant=\"raw\"` (same pattern\n          as `ChatbotFab`) so the send action goes through the shared\n          component. `raw` keeps custom sizing/shape without inheriting\n          the default solid/outline/link classes. */}\n      <Button\n        variant=\"raw\"\n        type=\"button\"\n        onClick={submit}\n        disabled={isSendDisabled}\n        aria-label=\"Enviar mensagem\"\n        className={cn(\n          'flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full',\n          'bg-primary-500 text-white transition-colors',\n          'hover:bg-primary-600',\n          'disabled:bg-background-200 disabled:text-text-500 disabled:cursor-not-allowed',\n          'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-300'\n        )}\n      >\n        <PaperPlaneTiltIcon size={20} weight=\"fill\" />\n      </Button>\n    </div>\n  );\n}\n","import { TrashIcon, PlusIcon } from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport IconButton from '../IconButton/IconButton';\nimport EmptyState from '../EmptyState/EmptyState';\nimport { SkeletonText } from '../Skeleton/Skeleton';\nimport { cn } from '../../utils/utils';\nimport type { ChatbotConversation } from '../../types/chatbot';\n\n/**\n * Props for the list of past chatbot conversations\n */\nexport interface ChatbotConversationListProps {\n  conversations: ChatbotConversation[];\n  activeConversationId: string | null;\n  isLoading?: boolean;\n  onSelect: (id: string) => void;\n  onDelete: (id: string) => void;\n  onStartNew: () => void;\n  className?: string;\n}\n\n/**\n * Format the conversation timestamp in pt-BR using the short day/month\n * form (e.g. \"17/04\"), which is enough to disambiguate conversations in\n * the sidebar without overflowing the row.\n */\nfunction formatDate(value: Date | string): string {\n  const d = value instanceof Date ? value : new Date(value);\n  if (Number.isNaN(d.getTime())) return '';\n  return d.toLocaleDateString('pt-BR', {\n    day: '2-digit',\n    month: '2-digit',\n  });\n}\n\n/**\n * Sidebar-like drawer listing the student's past conversations. Clicking\n * a row activates it; the trash icon calls `onDelete` — the actual\n * confirmation flow (modal, toast-undo, etc.) is delegated to the\n * consumer so behaviour can be customized per host app.\n */\nexport default function ChatbotConversationList({\n  conversations,\n  activeConversationId,\n  isLoading = false,\n  onSelect,\n  onDelete,\n  onStartNew,\n  className,\n}: Readonly<ChatbotConversationListProps>) {\n  return (\n    <div\n      className={cn(\n        'flex h-full flex-col border-r border-background-200 bg-background',\n        className\n      )}\n    >\n      <div className=\"p-3\">\n        <Button\n          variant=\"solid\"\n          action=\"primary\"\n          size=\"small\"\n          onClick={onStartNew}\n          iconLeft={<PlusIcon size={14} />}\n          className=\"w-full\"\n        >\n          Nova conversa\n        </Button>\n      </div>\n\n      <div className=\"flex-1 overflow-y-auto px-2 pb-2\">{renderBody()}</div>\n    </div>\n  );\n\n  function renderBody() {\n    if (isLoading) {\n      return (\n        <div className=\"space-y-2 px-2\">\n          <SkeletonText />\n          <SkeletonText />\n          <SkeletonText />\n        </div>\n      );\n    }\n\n    if (conversations.length === 0) {\n      return (\n        <div className=\"mt-4\">\n          <EmptyState\n            title=\"Sem conversas ainda\"\n            description=\"Comece sua primeira conversa com o assistente.\"\n          />\n        </div>\n      );\n    }\n\n    return (\n      <ul className=\"space-y-1\">\n        {conversations.map((c) => {\n          const isActive = c.id === activeConversationId;\n          return (\n            <li key={c.id}>\n              <div\n                className={cn(\n                  'group flex items-start gap-2 rounded-md px-2 py-2 transition-colors',\n                  isActive ? 'bg-primary-50' : 'hover:bg-background-100'\n                )}\n              >\n                <Button\n                  variant=\"raw\"\n                  onClick={() => onSelect(c.id)}\n                  className=\"flex-1 text-left\"\n                >\n                  <Text\n                    size=\"sm\"\n                    weight=\"semibold\"\n                    className=\"truncate text-text-900\"\n                  >\n                    {c.title || 'Conversa sem título'}\n                  </Text>\n                  <Text size=\"2xs\" className=\"text-text-400\">\n                    {formatDate(c.lastMessageAt)}\n                  </Text>\n                </Button>\n                <IconButton\n                  size=\"sm\"\n                  aria-label={`Excluir conversa ${c.title || ''}`.trim()}\n                  onClick={() => onDelete(c.id)}\n                  icon={<TrashIcon size={14} />}\n                />\n              </div>\n            </li>\n          );\n        })}\n      </ul>\n    );\n  }\n}\n"],"mappings":"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","names":["useRef","jsx","jsxs","useEffect","useRef","RobotIcon","jsx","jsx","jsxs","RobotIcon","jsx","jsxs","jsx","jsxs","useRef","useEffect","useState","jsx","jsxs","useState","PlusIcon","jsx","jsxs","PlusIcon","Fragment","jsx","jsxs","useRef"]}