npm package: @meerkat-coding/chat-widget
Description: Biblioteca modular de widgets de chat para React com arquitetura escalável
Author: Meerkat Coding
Latest version: 2.0.19

# 🤖 Chat Widget Library

Uma biblioteca React **modular**, **escalável** e **totalmente personalizável** para criar widgets de chat com suporte a Markdown, temas customizáveis e incorporação standalone.

[![npm version](https://img.shields.io/npm/v/@meerkat-coding/chat-widget.svg)](https://www.npmjs.com/package/@meerkat-coding/chat-widget)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## ✨ Features

- 🎨 **Sistema de Tema Completo** - 50+ propriedades customizáveis organizadas em categorias
- 📱 **Responsivo** - Adapta automaticamente a mobile, tablet e desktop com viewport units
- 🪟 **Window Mode** - Botão flutuante minimizado com animações suaves e ícone customizável
- ✏️ **Suporte a Markdown** - Renderização rica com react-markdown + GitHub Flavored Markdown
- 📦 **Embed Code** - Incorpore em qualquer site com apenas 2 linhas de código
- 🔄 **Polling Inteligente** - Atualização automática e adaptativa baseada no estado
- 🔒 **Bloqueio de Mensagens** - Controle de fluxo baseado no backend
- 🎯 **Modular** - Use componentes e hooks individuais ou o widget completo
- 💪 **TypeScript** - Totalmente tipado com interfaces claras
- 🎨 **Totalmente Customizável** - Estilos, comportamento e callbacks

---

## 📦 Instalação

### Via NPM (React Projects)

```bash
npm install @meerkat-coding/chat-widget
```

### Via Standalone Script (Qualquer Site)

```html
<!-- Configuração -->
<script>
  window.ChatWidgetConfig = {
    loadMessagesUrl: "https://seu-servidor.com/api/messages",
    sendMessageUrl: "https://seu-servidor.com/api/send",

    // Internacionalização (obrigatório)
    i18n: {
      pt: {
        title: "Chat de Suporte",
        subtitle: "Estamos aqui para ajudar!",
        footer: "",
        getStarted: "Iniciar Chat",
        inputPlaceholder: "Digite sua mensagem...",
        initialMessages: ["Olá! Como posso ajudar?"],
      },
    },
    defaultLanguage: "pt",

    // Recursos
    enableMarkdown: true,
    enablePolling: true,
    refetchInterval: 3000,

    // Opcional: Personalização de tema
    theme: {
      colors: {
        headerBackground: "#667eea",
        messageUserBackground: "#764ba2",
      },
    },

    // Opcional: Callbacks
    onMessageSent: function (message) {
      console.log("Mensagem enviada:", message);
    },
    onError: function (error) {
      console.error("Erro no chat:", error);
    },
  };
</script>

<!-- Script do Widget via CDN -->
<!-- Opção 1: unpkg (recomendado) -->
<script src="https://unpkg.com/@meerkat-coding/chat-widget@latest/dist/chat-widget.min.js"></script>

<!-- Opção 2: jsDelivr -->
<!-- <script src="https://cdn.jsdelivr.net/npm/@meerkat-coding/chat-widget@latest/dist/chat-widget.min.js"></script> -->
```

> **💡 Dica sobre CDN:**
>
> - **unpkg** e **jsDelivr** servem automaticamente packages do npm
> - Use `@2.0.3` para versão específica ou `@latest` para sempre pegar a mais recente
> - O cache do CDN garante carregamento rápido globalmente
> - Sem necessidade de hospedar o arquivo no seu próprio servidor!

---

## 🚀 Uso Rápido

### Opção 1: Widget Completo (React)

```tsx
import { ChatWidget } from "@meerkat-coding/chat-widget";

function App() {
  return (
    <ChatWidget
      loadMessagesUrl="https://api.exemplo.com/messages"
      sendMessageUrl="https://api.exemplo.com/send"
      i18n={{
        pt: {
          title: "Atendimento",
          subtitle: "Estamos aqui para ajudar!",
          footer: "",
          getStarted: "Iniciar Chat",
          inputPlaceholder: "Digite sua mensagem...",
          initialMessages: ["Olá! Como posso ajudar?"],
        },
      }}
      defaultLanguage="pt"
      enableMarkdown={true}
      theme={{
        colors: {
          headerBackground: "#667eea",
          messageUserBackground: "#764ba2",
        },
      }}
    />
  );
}
```

### Opção 2: Componentes Individuais

```tsx
import {
  MessageList,
  MessageInput,
  useSessionId,
  useMessages,
  useSendMessage,
} from "@meerkat-coding/chat-widget";

function CustomChatWidget() {
  const { sessionId } = useSessionId();

  const { messages, isLoading, isBlocked } = useMessages({
    loadMessagesUrl: "https://api.exemplo.com/messages",
    sessionId,
    enablePolling: true,
  });

  const { sendMessage, isSending } = useSendMessage({
    sendMessageUrl: "https://api.exemplo.com/send",
    sessionId,
  });

  return (
    <div className="my-chat">
      <MessageList
        messages={messages}
        isLoading={isLoading}
        enableMarkdown={true}
      />
      <MessageInput
        onSend={sendMessage}
        isDisabled={isBlocked}
        isSending={isSending}
      />
    </div>
  );
}
```

---

## 🎨 Sistema de Tema

### Estrutura do Tema

O tema é organizado em 4 categorias principais:

```typescript
interface ChatWidgetTheme {
  general?: {
    title?: string;
    subtitle?: string;
    placeholder?: string;
    initialMessage?: string;
    fontFamily?: string;
    widgetIcon?: React.ReactNode; // Ícone do botão flutuante (mode: window)
    iconSize?: string; // Tamanho do ícone do header (ex: "24px", padrão: "24px")
    iconPadding?: string; // Padding do ícone do header (ex: "4px", padrão: "0")
  };

  colors?: {
    // Header (4 propriedades)
    headerBackground?: string;
    headerText?: string;
    headerSubtitleText?: string;
    headerIconColor?: string;

    // Mensagens (6 propriedades)
    messageUserBackground?: string;
    messageUserText?: string;
    messageBotBackground?: string;
    messageBotText?: string;
    messageTimestampColor?: string;

    // Input
    inputBackground?: string;
    inputText?: string;
    inputBorder?: string;
    inputPlaceholder?: string;
    sendButtonBackground?: string;
    sendButtonText?: string;

    // Toggle Button (mode: window)
    toggleBackground?: string; // Cor de fundo do botão flutuante
    toggleHover?: string; // Cor no hover
    toggleIconStroke?: string; // Cor do ícone SVG

    // Outros
    lightBackground?: string;
    disabledText?: string;
  };

  layout?: {
    windowWidth?: string;
    windowHeight?: string;
    borderRadius?: string;
    spacing?: string;
    headerHeight?: string;

    // Toggle Button (mode: window)
    toggleSize?: string; // Tamanho do botão (ex: "60px")
    toggleBorderRadius?: string; // Arredondamento (ex: "50%" para círculo, "12px" para quadrado)

    messageMaxWidth?: string;
    messageBorderRadius?: string;
    transitionDuration?: string; // Duração das animações (ex: "0.3s")
  };

  typography?: {
    titleFontSize?: string;
    subtitleFontSize?: string;
    messageFontSize?: string;
    messageLineHeight?: string;
    subtitleLineHeight?: string;
  };
}
```

### Exemplo de Uso

```tsx
<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  // Tema customizado
  theme={{
    general: {
      title: "Suporte Premium",
      subtitle: "Online 24/7",
      fontFamily: "Inter, sans-serif",
    },
    colors: {
      headerBackground: "#6366f1",
      messageUserBackground: "#8b5cf6",
      messageBotBackground: "#f3f4f6",
    },
    layout: {
      windowWidth: "450px",
      windowHeight: "650px",
      borderRadius: "1rem",
    },
  }}
  // Sobrescritas específicas via customStyles (opcional)
  customStyles={{
    header: {
      background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
    },
  }}
/>
```

### Hierarquia de Estilos

```
defaultTheme (base)
    ↓
  theme (suas customizações)
    ↓
customStyles (sobrescritas específicas)
    ↓
Estilos finais renderizados
```

### Responsividade

O widget é **totalmente responsivo** e se adapta automaticamente a diferentes tamanhos de tela:

**Dimensões Responsivas:**
- Largura: `min(420px, calc(100vw - 40px))` - Adapta a telas pequenas com margem de 20px
- Altura: `min(600px, calc(100vh - 100px))` - Acompanha altura da viewport com margem de 50px topo/base
- Mensagens: `min(75%, 280px)` - Largura máxima garante legibilidade em mobile

**Safe Areas (Notch/Rounded Corners):**
- Botão toggle e widget respeitam `env(safe-area-inset-*)` em iOS/Android modernos
- Posicionamento automático evita sobreposição com elementos do sistema

**Mobile-First:**
```tsx
// Em mobile (< 380px viewport):
// - Widget ocupa 100vw - 40px (deixa margem)
// - Altura se ajusta a 100vh - 100px

// Em desktop (> 420px viewport):
// - Widget usa tamanho fixo 420x600px
// - Comportamento padrão mantido
```

**Customização de Dimensões:**
```tsx
<ChatWidget
  theme={{
    layout: {
      // Sobrescrever dimensões responsivas
      windowWidth: "min(500px, 95vw)",  // Widget maior
      windowHeight: "min(700px, 85vh)",  // Mais alto
    },
  }}
/>
```

**Dica para Mobile:**
Se o widget parecer muito grande em mobile, ajuste as margens:
```tsx
theme={{
  layout: {
    windowWidth: "min(420px, calc(100vw - 60px))",  // Margem 30px
    windowHeight: "min(600px, calc(100vh - 120px))", // Margem 60px
  },
}}
```

---

## ✏️ Suporte a Markdown

O widget suporta **GitHub Flavored Markdown** completo:

### Recursos Suportados

- **Texto**: `**negrito**`, `*itálico*`, `~~riscado~~`
- **Links**: `[texto](url)`
- **Código**: `` `inline` `` ou blocos com ` ``` `
- **Listas**: ordenadas (`1.`) e não-ordenadas (`-`)
- **Citações**: `> texto`
- **Tabelas**: Sintaxe GFM
- **Quebras de linha**: `\n` (quebra simples) ou `\n\n` (novo parágrafo)
  - ✨ **Suporte a line breaks**: Plugin `remark-breaks` ativado - `\n` simples quebra linha automaticamente

### Exemplo

```tsx
<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  enableMarkdown={true}
  initialMessage={
    "Olá! 👋 Eu suporto **Markdown**!\n" +
    "Esta é uma nova linha.\n\n" +
    "Experimente:\n" +
    "- `código inline`\n" +
    "- **negrito** e *itálico*\n" +
    "- [links clicáveis](https://example.com)"
  }
/>
```

**⚠️ Importante sobre quebras de linha:**

Em JSX, use `{...}` com concatenação de strings JavaScript:

✅ **Correto:**

```tsx
initialMessage={
  "Linha 1\n" +
  "Linha 2\n\n" +
  "Parágrafo 2"
}
```

❌ **Incorreto:**

```tsx
initialMessage = "Linha 1\nLinha 2"; // \n pode não ser interpretado corretamente
```

**Resultado renderizado:**

```
Olá! 👋 Eu suporto Markdown!
Esta é uma nova linha.

Experimente:
- código inline
- negrito e itálico
- links clicáveis
```

---

## 🔗 Embed Code - Incorporação em Qualquer Site

### Instalação Rápida

**1. Copie e cole este código no final do `<body>` do seu HTML:**

```html
<!-- Configuração do Widget -->
<script>
  window.ChatWidgetConfig = {
    // Obrigatório
    loadMessagesUrl: "https://api.exemplo.com/api/messages",
    sendMessageUrl: "https://api.exemplo.com/api/send",

    // Internacionalização (obrigatório)
    i18n: {
      pt: {
        title: "Chat de Suporte",
        subtitle: "Estamos aqui para ajudar!",
        footer: "",
        getStarted: "Iniciar Chat",
        inputPlaceholder: "Digite sua mensagem...",
        initialMessages: ["Olá! Como posso ajudar?"],
      },
      en: {
        title: "Support Chat",
        subtitle: "We are here to help!",
        footer: "",
        getStarted: "Start Chat",
        inputPlaceholder: "Type your message...",
        initialMessages: ["Hello! How can I help you?"],
      },
    },
    defaultLanguage: "pt",

    // Recursos
    enableMarkdown: true,
    enablePolling: true,
    refetchInterval: 3000,

    // Welcome Screen & Mode (opcional)
    showWelcomeScreen: false,
    mode: "window",

    // Session ID (opcional - útil para integração com autenticação)
    sessionId: "user-session-123", // ou obtenha de cookies/localStorage

    // Carregar sessão anterior (opcional - padrão: false)
    loadPreviousSession: false, // true = mantém sessão entre reloads, false = sempre nova sessão

    // Tema (opcional)
    theme: {
      colors: {
        headerBackground: "#667eea",
        messageUserBackground: "#764ba2",
      },
    },

    // Callbacks (opcional)
    onMessageSent: function (message) {
      console.log("Mensagem enviada:", message);
    },
    onError: function (error) {
      console.error("Erro:", error);
    },
  };
</script>

<!-- Script do Widget via CDN -->
<!-- Opção 1: unpkg (recomendado) -->
<script src="https://unpkg.com/@meerkat-coding/chat-widget@latest/dist/chat-widget.min.js"></script>

<!-- Opção 2: jsDelivr -->
<!-- <script src="https://cdn.jsdelivr.net/npm/@meerkat-coding/chat-widget@latest/dist/chat-widget.min.js"></script> -->
```

**2. Pronto!** O widget aparecerá automaticamente no canto inferior direito.

### Build do Standalone

Para gerar os arquivos de embed:

```bash
npm run build        # Gera dist/chat-widget.js e chat-widget.min.js
```

**Tamanho do Bundle:**

- `chat-widget.min.js`: ~80KB (~25KB gzip)
- Inclui React, React-DOM e todas as dependências

### Gerador de Embed Code

O projeto inclui um gerador visual de código em `http://localhost:5173`. Role a página para baixo para ver a seção **"🔗 Embed Code Generator"** onde você pode:

- Configurar todas as propriedades visualmente
- Ver preview do código em tempo real
- Copiar código pronto com um clique

### Exemplo Completo

Veja `example/public/embed-demo.html` para uma demonstração funcional do widget incorporado em HTML puro.

---

## ⚙️ Propriedades Completas

### URLs (Obrigatórias)

```tsx
{
  loadMessagesUrl: string; // GET endpoint para carregar mensagens
  sendMessageUrl: string; // POST endpoint para enviar mensagens
}
```

### Internacionalização (Obrigatória)

```tsx
{
  i18n: I18nConfig;                 // Configuração de idiomas (obrigatório)
  defaultLanguage?: string;         // Idioma padrão (padrão: "en")
}

interface I18nConfig {
  [language: string]: I18nTranslations;
}

interface I18nTranslations {
  title: string;                    // Título do header
  subtitle: string;                 // Subtítulo do header
  footer: string;                   // Texto do footer
  getStarted: string;               // Texto do botão "Get Started" (welcome screen)
  inputPlaceholder: string;         // Placeholder do input
  initialMessages?: string[];       // Mensagens iniciais do bot (array)
}
```

### Interface & Comportamento

```tsx
{
  // UI
  showWelcomeScreen?: boolean;      // Mostrar tela de boas-vindas (padrão: false)
  mode?: 'window' | 'fullscreen';   // Modo de exibição (padrão: 'window')
                                    // - window: botão flutuante minimizado, clique para expandir
                                    // - fullscreen: chat sempre aberto
  icon?: React.ReactNode | string;  // Ícone customizado (ReactNode, URL, SVG, emoji ou base64)

  // Dados
  initialMessages?: Message[];      // Mensagens pré-carregadas (além das do i18n)

  // Comportamento
  enableMarkdown?: boolean;         // Ativa Markdown (padrão: true)
  showTimestamps?: boolean;         // Exibir timestamps nas mensagens (padrão: true)
  enablePolling?: boolean;          // Ativa polling automático (padrão: true)
  refetchInterval?: number;         // Intervalo de polling em ms (padrão: 3000)
  authHeader?: AuthConfig;          // Configuração de autenticação
  sessionId?: string;               // Session ID fixo (prioridade sobre generateSessionId)
  generateSessionId?: () => string; // Gerador de session ID customizado
  loadPreviousSession?: boolean;    // Carregar sessão anterior (padrão: false)
}
```

### Tema e Estilos

```tsx
{
  theme?: ChatWidgetTheme;          // Tema estruturado (50+ propriedades)
  customStyles?: ChatWidgetStyles;  // Sobrescritas CSS específicas
}
```

### Callbacks

```tsx
{
  onMessageSent?: (message: Message) => void;
  onMessagesLoaded?: (messages: Message[]) => void;
  onError?: (error: Error) => void;
  onBlockStateChange?: (isBlocked: boolean) => void;
}
```

---

## 📡 Formato das APIs

### GET /api/messages

**Headers:**

```
X-Session-ID: uuid-da-sessao
Authorization: Bearer token (opcional)
```

**Response:**

```json
{
  "messages": [
    {
      "id": "msg-1",
      "message": "Olá!",
      "sender": "customer",
      "timestamp": "2025-01-01T12:00:00Z",
      "type": "text"
    },
    {
      "id": "msg-2",
      "message": "Oi! Como posso ajudar?",
      "sender": "system",
      "timestamp": "2025-01-01T12:00:01Z",
      "type": "text"
    }
  ],
  "blockNewMessages": false
}
```

### POST /api/send

**Headers:**

```
X-Session-ID: uuid-da-sessao
Authorization: Bearer token (opcional)
Content-Type: application/json
```

**Body:**

```json
{
  "message": "Preciso de ajuda",
  "sessionId": "uuid-da-sessao",
  "timestamp": "2025-01-01T12:00:02Z",
  "idempotencyKey": "msg-uuid-da-sessao-1234567890"
}
```

**Response:**

```json
{
  "success": true,
  "message": {
    "id": "msg-uuid-da-sessao-1234567890",
    "message": "Preciso de ajuda",
    "sender": "customer",
    "timestamp": "2025-01-01T12:00:02Z",
    "type": "text"
  },
  "blockNewMessages": true
}
```

### Controle de Bloqueio

O campo `blockNewMessages` controla o estado do input:

- `true`: Input desabilitado (bot processando)
- `false`: Input habilitado

### Idempotency Keys (Recomendado)

O widget automaticamente gera e envia **idempotency keys** para prevenir mensagens duplicadas em cenários de race condition.

**Como funciona:**

1. Quando o usuário envia uma mensagem, o widget gera um ID único: `msg-{sessionId}-{timestamp}`
2. Este ID é enviado no campo `idempotencyKey` do payload POST `/api/send`
3. **O backend deve usar este `idempotencyKey` como `id` da mensagem**
4. Se uma mensagem com este ID já existe, retorna a existente (evita duplicata)

**Implementação no Backend:**

```javascript
app.post('/api/send', (req, res) => {
  const { message, sessionId, idempotencyKey } = req.body;

  // Verifica se já existe mensagem com este ID (previne duplicata)
  const existingMessage = findMessageById(sessionId, idempotencyKey);

  if (existingMessage) {
    // Retorna mensagem existente (evita duplicata)
    return res.json({
      success: true,
      message: existingMessage,
      blockNewMessages: false
    });
  }

  // Salva nova mensagem usando idempotencyKey como ID
  const newMessage = saveMessage({
    id: idempotencyKey,  // ← Usa idempotency key como ID
    message,
    sessionId,
    timestamp: new Date().toISOString(),
    sender: 'customer',
    type: 'text'
  });

  res.json({
    success: true,
    message: newMessage,
    blockNewMessages: false
  });
});
```

**Benefícios:**

- ✅ Previne mensagens duplicadas em race conditions
- ✅ Permite retries seguros (mesmo ID = não duplica)
- ✅ Matching preciso entre mensagens temporárias e reais (compara IDs)
- ✅ Modelo de dados limpo (1 campo ao invés de 2)
- ✅ Padrão da indústria usado por Stripe, Twilio, AWS, etc.

**Backward Compatibility:**

O campo `idempotencyKey` é **opcional** no payload. Se o backend não implementar idempotency, o widget continuará funcionando normalmente (mas sem a proteção contra duplicatas).

---

## 🪝 Hooks Personalizados

### useSessionId()

Gerencia sessionID com localStorage.

```tsx
const {
  sessionId,      // UUID da sessão
  resetSession,   // Gera novo sessionID
  clearSession    // Remove da localStorage
} = useSessionId(customGenerator?);
```

### useMessages()

Carrega e faz polling de mensagens.

```tsx
const {
  messages, // Array de mensagens
  isLoading, // Estado de loading
  isBlocked, // Estado de bloqueio
  error, // Erro (se houver)
  loadMessages, // Função para recarregar
  addMessage, // Adiciona mensagem local
  updateMessage, // Atualiza mensagem
  removeMessage, // Remove mensagem
  setBlockState, // Define estado de bloqueio
  startPolling, // Inicia polling
  stopPolling, // Para polling
} = useMessages({
  loadMessagesUrl,
  sessionId,
  authConfig,
  refetchInterval,
  enablePolling,
  onMessagesLoaded,
  onError,
  onBlockStateChange,
});
```

### useSendMessage()

Envia mensagens para o backend.

```tsx
const {
  sendMessage, // Função de envio
  isSending, // Estado de envio
  error, // Erro (se houver)
  clearError, // Limpa erro
} = useSendMessage({
  sendMessageUrl,
  sessionId,
  authConfig,
  onSuccess,
  onError,
});
```

### useTheme()

Processa tema e gera estilos CSS.

```tsx
const {
  theme,      // Tema processado (merged)
  styles      // Estilos CSS prontos
} = useTheme(theme?, customStyles?);
```

---

## 🎯 Componentes

### ChatWidget

Componente principal all-in-one.

```tsx
<ChatWidget {...props} />
```

### Header

Header com título, subtítulo e ícone.

```tsx
<Header
  title="Chat"
  subtitle="Online"
  icon={<Icon />}  // Ou URL, emoji, SVG ou base64
  containerStyle={...}
  titleStyle={...}
  subtitleStyle={...}
  iconStyle={...}
/>
```

### MessageList

Lista de mensagens com scroll automático.

```tsx
<MessageList
  messages={messages}
  isLoading={isLoading}
  enableMarkdown={true}
  customStyles={...}
/>
```

### MessageContent

Renderiza conteúdo com Markdown.

```tsx
<MessageContent
  content="**Olá**!"
  enableMarkdown={true}
  isUser={false}
  customStyles={...}
/>
```

### MessageInput

Input com validação e estado.

```tsx
<MessageInput
  onSend={handleSend}
  isDisabled={isBlocked}
  isSending={isSending}
  placeholder="Digite..."
  customStyles={...}
/>
```

---

## 📚 Exemplos

### Exemplo 1: Básico

```tsx
<ChatWidget
  loadMessagesUrl="https://api.exemplo.com/messages"
  sendMessageUrl="https://api.exemplo.com/send"
  i18n={{
    en: {
      title: "Support Chat",
      subtitle: "We're online!",
      footer: "",
      getStarted: "Start Chat",
      inputPlaceholder: "Type your message...",
    },
  }}
/>
```

### Exemplo 2: Com Autenticação

```tsx
<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  i18n={{
    en: {
      title: "Support Chat",
      subtitle: "We're online!",
      footer: "",
      getStarted: "Start Chat",
      inputPlaceholder: "Type your message...",
    },
  }}
  authHeader={{
    headerName: "Authorization",
    headerValue: "Bearer seu-token-jwt",
  }}
/>
```

### Exemplo 3: Session ID Personalizado

**Opção A: Session ID Fixo (recomendado quando você já tem o ID)**

```tsx
const userSessionId = "user-123-abc-xyz";

<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  i18n={{
    en: {
      title: "Support Chat",
      subtitle: "We're online!",
      footer: "",
      getStarted: "Start Chat",
      inputPlaceholder: "Type your message...",
    },
  }}
  sessionId={userSessionId}
/>;
```

**Opção B: Função Geradora (quando precisa gerar dinamicamente)**

```tsx
<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  i18n={{
    en: {
      title: "Support Chat",
      subtitle: "We're online!",
      footer: "",
      getStarted: "Start Chat",
      inputPlaceholder: "Type your message...",
    },
  }}
  generateSessionId={() => `user-${userId}-${Date.now()}`}
/>
```

**Hierarquia de Prioridade:**

1. `sessionId` (prop direta) - se fornecido, usa ele
2. `generateSessionId()` (função) - se fornecida, chama ela
3. Auto-gerado (padrão) - gera UUID e persiste no localStorage

**Comportamento do `loadPreviousSession`:**

- `false` (padrão): Sempre cria uma nova sessão ao carregar o widget
- `true`: Mantém a sessão entre reloads usando localStorage
- Útil quando `sessionId` fixo não é fornecido

### Exemplo 4: Tema Customizado

```tsx
<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  i18n={{
    pt: {
      title: "Suporte Premium",
      subtitle: "Atendimento VIP 24/7",
      footer: "",
      getStarted: "Iniciar",
      inputPlaceholder: "Digite...",
    },
  }}
  theme={{
    general: {
      fontFamily: "Inter, sans-serif",
    },
    colors: {
      headerBackground: "#6366f1",
      messageUserBackground: "#8b5cf6",
    },
    layout: {
      windowWidth: "500px",
      borderRadius: "16px",
    },
  }}
/>
```

### Exemplo 5: Ícones Customizados

```tsx
<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  i18n={{
    pt: {
      title: "Suporte",
      subtitle: "Estamos aqui!",
      footer: "",
      getStarted: "Iniciar",
      inputPlaceholder: "Digite...",
    },
  }}
  // React Component
  icon={<MyCustomIcon />}
  // OU URL de imagem
  icon="https://exemplo.com/chat-icon.png"
  // OU Emoji
  icon="💬"
  // OU SVG inline
  icon={
    <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
      <path d="M20 2H4C2.9 2 2 2.9 2 4V22L6 18H20C21.1 18 22 17.1 22 16V4C22 2.9 21.1 2 20 2Z" />
    </svg>
  }
  // OU Base64
  icon="data:image/png;base64,iVBORw0KG..."
/>
```

**Formatos suportados:**

- ✅ **ReactNode**: `<Component />` ou `<svg>...</svg>`
- ✅ **URL**: `"https://..."` ou `"//cdn.example.com/icon.png"`
- ✅ **Emoji/Texto**: `"💬"`, `"Chat"`, etc.
- ✅ **Base64**: `"data:image/png;base64,..."`

**Controlando o tamanho e padding do ícone:**

```tsx
<ChatWidget
  icon="💬"
  theme={{
    general: {
      iconSize: "32px", // Tamanho padrão: "24px"
      iconPadding: "4px", // Padding padrão: "0"
    },
  }}
/>
```

### Exemplo 6: Com Callbacks

```tsx
<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  i18n={{
    en: {
      title: "Support",
      subtitle: "We're here!",
      footer: "",
      getStarted: "Start",
      inputPlaceholder: "Type...",
    },
  }}
  onMessageSent={(msg) => {
    console.log("Enviada:", msg);
    analytics.track("message_sent");
  }}
  onMessagesLoaded={(msgs) => {
    console.log("Carregadas:", msgs.length);
  }}
  onError={(error) => {
    Sentry.captureException(error);
  }}
  onBlockStateChange={(blocked) => {
    console.log("Bloqueado:", blocked);
  }}
/>
```

### Exemplo 7: Multi-idioma com Initial Messages

```tsx
<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  i18n={{
    pt: {
      title: "Chat de Suporte",
      subtitle: "Online agora!",
      footer: "",
      getStarted: "Iniciar",
      inputPlaceholder: "Digite sua mensagem...",
      initialMessages: [
        "Olá! 👋",
        "Experimente enviar:\n- **negrito**\n- *itálico*\n- `código`\n- [links](https://exemplo.com)",
      ],
    },
    en: {
      title: "Support Chat",
      subtitle: "Online now!",
      footer: "",
      getStarted: "Start",
      inputPlaceholder: "Type your message...",
      initialMessages: [
        "Hello! 👋",
        "Try sending:\n- **bold**\n- *italic*\n- `code`\n- [links](https://example.com)",
      ],
    },
  }}
  defaultLanguage="pt"
  enableMarkdown={true}
/>
```

### Exemplo 8: Dark Mode

```tsx
<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  i18n={{
    en: {
      title: "Support",
      subtitle: "24/7 Available",
      footer: "",
      getStarted: "Start",
      inputPlaceholder: "Type...",
    },
  }}
  theme={{
    colors: {
      headerBackground: "#1f2937",
      headerText: "#f9fafb",
      lightBackground: "#111827",
      darkBackground: "#030712",
      messageUserBackground: "#3b82f6",
      messageBotBackground: "#374151",
      messageBotText: "#e5e7eb",
      inputBackground: "#1f2937",
      inputText: "#f9fafb",
      inputBorder: "#4b5563",
    },
  }}
/>
```

### Exemplo 9: Welcome Screen + Fullscreen Mode

```tsx
<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  i18n={{
    pt: {
      title: "Bem-vindo!",
      subtitle: "Clique para iniciar o chat",
      footer: "",
      getStarted: "Começar Conversa",
      inputPlaceholder: "Digite...",
      initialMessages: ["Olá! Estou aqui para ajudar."],
    },
  }}
  showWelcomeScreen={true}
  mode="fullscreen"
/>
```

### Exemplo 10: Window Mode com Botão Customizado

```tsx
<ChatWidget
  loadMessagesUrl="..."
  sendMessageUrl="..."
  i18n={{
    pt: {
      title: "Suporte",
      subtitle: "Estamos online",
      footer: "",
      getStarted: "Iniciar",
      inputPlaceholder: "Digite sua mensagem...",
      initialMessages: ["Olá! Como posso ajudar?"],
    },
  }}
  mode="window"
  theme={{
    general: {
      // Ícone customizado do botão flutuante
      widgetIcon: (
        <svg
          width="24"
          height="24"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
        >
          <path d="M12 2L2 7l10 5 10-5-10-5z" />
          <path d="M2 17l10 5 10-5M2 12l10 5 10-5" />
        </svg>
      ),
    },
    colors: {
      toggleBackground: "#10b981", // Verde
      toggleHover: "#059669", // Verde escuro no hover
      toggleIconStroke: "#ffffff", // Ícone branco
    },
    layout: {
      toggleSize: "64px", // Botão maior
      toggleBorderRadius: "16px", // Quadrado arredondado (use "50%" para círculo)
      transitionDuration: "0.3s", // Animações suaves
    },
  }}
/>
```

**Customizações disponíveis para o botão flutuante:**

- `toggleBackground`: Cor de fundo
- `toggleHover`: Cor no hover (com efeito de escala automático)
- `toggleIconStroke`: Cor do ícone SVG
- `toggleSize`: Tamanho do botão
- `toggleBorderRadius`: `"50%"` para círculo, `"12px"` para quadrado arredondado
- `widgetIcon`: ReactNode customizado para o ícone

**Animações automáticas:**

- Hover no botão: Escala 1.1x + sombra aumentada
- Abrir: Widget cresce do botão (scale 0 → 1) com efeito bounce
- Fechar: Widget diminui em direção ao botão
- Transform origin: Canto inferior direito (onde está o botão)

### Exemplo 11: Layout Totalmente Customizado

```tsx
function CustomLayout() {
  const { sessionId, resetSession } = useSessionId();
  const { messages, isLoading, isBlocked, loadMessages } = useMessages({...});
  const { sendMessage, isSending } = useSendMessage({...});

  return (
    <div className="custom-layout">
      <header>
        <h1>Suporte</h1>
        <button onClick={resetSession}>Nova Conversa</button>
        <button onClick={loadMessages}>Recarregar</button>
      </header>

      <aside>
        <p>Session: {sessionId}</p>
        <p>Mensagens: {messages.length}</p>
        <p>Status: {isBlocked ? '🔒' : '✅'}</p>
      </aside>

      <main>
        <MessageList messages={messages} isLoading={isLoading} enableMarkdown />
      </main>

      <footer>
        <MessageInput
          onSend={sendMessage}
          isDisabled={isBlocked}
          isSending={isSending}
        />
      </footer>
    </div>
  );
}
```

---

## 🏗️ Arquitetura

### Estrutura de Diretórios

```
src/
├── components/
│   └── ChatWidget/
│       ├── ChatWidget.tsx         # Componente principal
│       ├── MessageList.tsx        # Lista de mensagens
│       ├── MessageInput.tsx       # Input de envio
│       ├── MessageContent.tsx     # Renderização de Markdown
│       ├── Header.tsx             # Header com título/subtitle
│       └── types.ts               # Tipos dos componentes
│
├── hooks/
│   ├── useSessionId.ts            # Gerenciamento de sessão
│   ├── useMessages.ts             # Carregamento de mensagens
│   ├── useSendMessage.ts          # Envio de mensagens
│   └── useTheme.ts                # Processamento de tema
│
├── utils/
│   ├── sessionStorage.ts          # Utilitários de sessão
│   └── api.ts                     # Cliente HTTP
│
├── types/
│   └── index.ts                   # Tipos globais
│
├── themes/
│   └── defaultTheme.ts            # Tema padrão
│
├── embed.tsx                      # Entry point standalone
└── index.ts                       # Entry point NPM
```

### Princípios de Design

1. **Single Responsibility**: Cada módulo tem uma única responsabilidade
2. **Composição**: Componentes pequenos e focados que podem ser compostos
3. **Separação de Responsabilidades**: Componentes (UI) + Hooks (lógica) + Utils (helpers)
4. **Open/Closed**: Aberto para extensão, fechado para modificação
5. **Dependency Injection**: Props como injeção de dependências

### Fluxo de Dados

```
ChatWidget (Container)
    ↓
useSessionId → Cria/recupera sessionID
    ↓
useMessages → Carrega mensagens + polling
    ↓
useSendMessage → Envia mensagens
    ↓
MessageList + MessageInput (Presentational)
```

---

## 🧪 TypeScript

Totalmente tipado! Importe os tipos:

```tsx
import type {
  // Componentes
  ChatWidgetProps,
  ChatWidgetTheme,
  ChatWidgetStyles,

  // Data
  Message,

  // API
  AuthConfig,
  LoadMessagesResponse,
  SendMessageResponse,

  // Hooks
  UseMessagesOptions,
  UseSendMessageOptions,
} from "@meerkat-coding/chat-widget";
```

---

## 🛠️ Desenvolvimento

### Setup

```bash
# Instalar dependências
npm install

# Build da biblioteca
npm run build        # Build completo (NPM + standalone)
npm run build:lib    # Apenas NPM
npm run build:embed  # Apenas standalone

# Desenvolvimento
npm run dev          # Inicia servidor mock + exemplo
npm run dev:server   # Apenas servidor mock
npm run dev:example  # Apenas app de exemplo
```

### Estrutura de Build

- `dist/index.js` - CommonJS (NPM)
- `dist/index.esm.js` - ES Modules (NPM)
- `dist/chat-widget.js` - IIFE standalone com sourcemaps
- `dist/chat-widget.min.js` - IIFE standalone minificado

### Ambiente de Exemplo

O projeto inclui um ambiente completo de desenvolvimento em `example/`:

- **App de demonstração** (Vite + React)
- **Servidor mock** (Express) em `http://localhost:3001`
- **Gerador de Embed Code** - Interface visual para gerar código
- **Console de eventos** - Debugging em tempo real

Acesse: `http://localhost:5173`

---

## 📝 Changelog

### v2.0.13 - Bug Fix: URL Icon Sizing

**Correções:**

- 🐛 **Fix: Ícones por URL agora respeitam `iconSize`** - Removidos width/height 100% que faziam imagem ocupar todo o container
- ✅ Ícones de URL agora aplicam corretamente o tamanho e padding definidos no theme
- 📚 Fix aplicado em `iconHelper.tsx` para renderização de imagens

### v2.0.12 - Icon Padding Control

**Melhorias:**

- ✨ **Nova propriedade `iconPadding` no tema** - Controle o padding dos ícones do header
  - Adicione `general.iconPadding` ao theme (ex: `"4px"`, `"0.5rem"`)
  - Valor padrão: `"0"`
  - Adiciona espaçamento interno ao redor do ícone
- 📚 Documentação atualizada com exemplo de uso

### v2.0.11 - Icon Size & Padding Control

**Melhorias:**

- ✨ **Nova propriedade `iconSize` no tema** - Controle o tamanho dos ícones do header via tema
  - Adicione `general.iconSize` ao theme (ex: `"32px"`, `"1.5em"`)
  - Valor padrão: `"24px"`
  - Aplica width, height e fontSize automaticamente
- ✨ **Nova propriedade `iconPadding` no tema** - Controle o padding dos ícones do header
  - Adicione `general.iconPadding` ao theme (ex: `"4px"`, `"0.5rem"`)
  - Valor padrão: `"0"`
  - Adiciona espaçamento interno ao redor do ícone
- 📚 Documentação atualizada com exemplos de uso

### v2.0.10 - Enhanced Icon Support

**Novas Features:**

- ✨ **Suporte expandido para ícones** - Agora aceita múltiplos formatos além de ReactNode
  - **React Component**: `<MyIcon />` ou `<svg>...</svg>`
  - **URL de imagem**: `"https://exemplo.com/icon.png"`
  - **Emoji/Texto**: `"💬"`, `"Chat"`, etc.
  - **Base64**: `"data:image/png;base64,..."`
- ✨ **Detecção automática de formato** - Identifica automaticamente o tipo de ícone
- ✨ **Ícone padrão** - WidgetToggle exibe chat bubble SVG quando ícone não é fornecido

**Melhorias:**

- 🎯 Nova utility `renderIcon()` para renderização inteligente de ícones
- 📚 Documentação expandida com exemplos de todos os formatos suportados
- 🔄 Totalmente backward compatible - código existente continua funcionando

### v2.0.9 - Critical Fix: Next.js 15 Infinite Loop Resolution

**Correções:**

- 🐛 **CRITICAL: Solução definitiva para infinite loop no Next.js 15** - Eliminados TODOS os casos de "Maximum update depth exceeded"
  - **useI18n estabilizado**: JSON.stringify para comparação profunda do objeto i18n, evitando recriação quando passado inline
  - **useMessages otimizado**: Removido `isBlocked` das dependências de `loadMessages`, usando functional setState para evitar leitura de estado
  - **Polling sem dependências circulares**: `loadMessagesRef` usado no polling para quebrar ciclo de dependências
  - **initialMessages estável**: JSON.stringify no ChatWidget para evitar re-renders quando array é equivalente
  - **Exemplo Next.js aprimorado**: Constantes `i18n` e `theme` movidas para fora do componente, todos os callbacks memoizados com `useCallback`
- ⚡ **Performance**: Prevenção de re-renders desnecessários em aplicações Next.js

**Padrões defensivos aplicados:**

- Refs para callbacks que atualizam dependências
- Functional setState para evitar dependências circulares
- JSON.stringify para comparação profunda de objetos complexos
- Constantes externas para configurações estáticas

### v2.0.8 - Critical Fix: Complete Re-render Loop Solution

**Correções:**

- 🐛 **CRITICAL: Solução completa para loop infinito** - Resolvidos TODOS os problemas de "Maximum update depth exceeded" no Next.js
  - **authConfig estabilizado**: Usa refs em ambos `useMessages` e `useSendMessage` para evitar recriação quando passado inline
  - **Polling otimizado**: Removidas dependências circulares `startPolling`/`stopPolling` do useEffect
  - **onSuccess memoizado**: Callback estável no ChatWidget evita recriações desnecessárias
  - **theme e customStyles**: Comparação via JSON.stringify para evitar re-processamento quando objetos são equivalentes
- ⚡ **Performance**: Drasticamente reduzido número de re-renders, especialmente em Next.js

### v2.0.7 - Critical Bug Fix

**Correções:**

- 🐛 **CRITICAL: Fix infinite re-render loop** - Estabilizados callbacks em `useMessages` e `useSendMessage` usando refs para evitar "Maximum update depth exceeded" no Next.js e outros frameworks React
  - Callbacks `onMessagesLoaded`, `onError`, `onBlockStateChange`, `onMessageSent`, `onSuccess` agora são gerenciados via refs
  - Removidas dependências problemáticas dos `useCallback`s internos

### v2.0.6 - Bug Fix

**Correções:**

- 🐛 **Fix: Estilos inline inválidos** - Removidos seletores CSS (`& > *:first-child`, `& > *:last-child`) que causavam erros no Next.js e outros frameworks React

### v2.0.5 - Window Mode & UI Enhancements

**Novas Features:**

- 🪟 **Window Mode** - Botão flutuante minimizado que expande o chat ao clicar
  - Animações suaves de scale com easing customizado
  - Ícone customizável via tema (`widgetIcon`)
  - Totalmente responsivo com transições CSS
- 🎨 **Botão de Enviar Redesenhado** - Ícone de enviar (paper plane) ao invés de texto
  - Formato circular mais moderno
  - Customizável via tema (`buttonBackground`, `buttonText`)
- 🕒 **Controle de Timestamps** - Nova prop `showTimestamps` para mostrar/ocultar horários das mensagens
- ✨ **Botão de Fechar** - Header com botão X para fechar o chat (mode: window)

**Melhorias:**

- 🎯 Centralização perfeita de ícones em botões circulares
- 🎨 Tema expandido com mais opções de customização para botões
- 📐 Layout mais limpo e profissional

### v2.0.1 - Melhorias

**Novas Features:**

- ✨ Prop `sessionId` - Passe session ID fixo diretamente como prop
- ✨ Plugin `remark-breaks` - Quebras de linha com `\n` simples agora funcionam

**Melhorias:**

- 📚 Documentação expandida com exemplos de session ID
- 🎯 Hierarquia clara de prioridade: sessionId > generateSessionId > auto-gerado

### v2.0.0 - Arquitetura Modular

**Novas Features:**

- ✨ Sistema de tema completo (50+ propriedades)
- ✨ Suporte a Markdown (react-markdown + GFM)
- ✨ Embed code standalone
- ✨ Polling adaptativo inteligente
- ✨ Novos componentes: Header, MessageContent
- ✨ Novo hook: useTheme
- ✨ Props: subtitle, initialMessage, icon, theme, enableMarkdown

**Melhorias:**

- ⚡ Desbloqueio imediato (500ms ao invés de 3s)
- ⚡ Performance otimizada
- 🎨 Sistema de estilos mais flexível
- 🐛 Fix: Scroll contido no container
- 🐛 Fix: isMountedRef causando mensagens não aparecerem
- 🐛 Fix: Bloqueio/desbloqueio imediato

**Breaking Changes:**

- Estrutura de estilos expandida (backward compatible)

---

## 📄 Licença

MIT

---

## 🤝 Contribuindo

Contribuições são bem-vindas! Por favor:

1. Fork o projeto
2. Crie uma branch para sua feature (`git checkout -b feature/MinhaFeature`)
3. Commit suas mudanças (`git commit -m 'Adiciona MinhaFeature'`)
4. Push para a branch (`git push origin feature/MinhaFeature`)
5. Abra um Pull Request

---

**Desenvolvido com ❤️**