gq-chatbot-widget
v1.5.0
Published
Web Component de chat creado con React, TypeScript y Tailwind.
Maintainers
Readme
GQ Chatbot Widget
Web Component de chat premium, creado con React + TypeScript + Framer Motion, diseñado para integrarse en cualquier sitio con una sola etiqueta HTML.
Qué incluye
- Launcher animado con estados
cerrado,peekychat abierto. - Chat interactivo con animaciones por mensaje, timestamps y autoscroll.
- Modal de términos y condiciones antes de abrir la conversación.
- Botón opcional de WhatsApp con plantilla de mensaje y placeholders.
- Personalización visual avanzada por atributos (colores de panel, burbujas, launcher, input y más).
- Borde con gradiente animado en launcher y panel (activable, con dos colores configurables).
- Scrollbar personalizado en la lista de mensajes (Chrome, Safari, Edge y Firefox), con colores configurables.
- Sin Tailwind preflight global (no resetea
*,html,bodydel sitio host). - Shadow DOM opcional (
use-shadow/shadow) para inyectar estilos solo dentro del widget (recomendado en Next.js App Router sin iframe). - SSR-safe: el registro del custom element no hace nada sin
window/customElements. Entry opcionalgq-chatbot-widget/clientpara registrar solo en cliente.
Instalación
npm install gq-chatbot-widgetUso rápido
Con bundler (React, Vite, Next, etc.)
import 'gq-chatbot-widget'<gq-chatbot-widget
agent-id="agent_0701kp9thvhvevbt2p9tastrz2q7"
agent-name="Pipi"
theme-color="#2563eb"
></gq-chatbot-widget>Con CDN
<script src="https://unpkg.com/[email protected]/dist/chat-widget.umd.js"></script>
<gq-chatbot-widget
agent-id="agent_0701kp9thvhvevbt2p9tastrz2q7"
agent-name="Pipi"
theme-color="#2563eb"
></gq-chatbot-widget>Next.js (App Router) sin iframe
Problema que resuelve
En versiones anteriores el CSS del bundle podía afectar el sitio (p. ej. preflight de Tailwind). Desde v1.5.0:
- El CSS del paquete no incluye preflight (
@tailwind baseeliminado): no se resetean*,htmlnibodydel documento. - Con
use-shadow="true"oshadow="open"(oshadow="closed"), el widget usa Shadow DOM: todo el CSS compilado va en un<style>dentro delshadowRoot, no endocument.head, así no “pisa” estilos del layout. - El módulo principal es seguro en SSR: si Next evalúa el import en el servidor,
registerGqChatbotWidget()no ejecutacustomElements.definesinwindow.
Recomendado: etiqueta con Shadow DOM
// app/layout.tsx (Server Component está bien: solo HTML estático)
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="es">
<body>
{children}
<script
src="https://unpkg.com/[email protected]/dist/chat-widget.umd.js"
defer
/>
<gq-chatbot-widget
use-shadow="true"
agent-id="tu-agent-id"
agent-name="Pipi"
theme-color="#0379D5"
/>
</body>
</html>
)
}Equivalente semántico: shadow="open" o shadow="closed". El atributo vacío shadow en HTML5 suele leerse como cadena vacía y el widget lo trata como modo open.
Importante: definí use-shadow / shadow antes del primer render del nodo (p. ej. en el HTML inicial). No se soporta pasar de light DOM a shadow después de conectar el elemento.
Opción: solo cliente + registro explícito
Si preferís importar desde un Client Component o useEffect:
'use client'
import { useEffect } from 'react'
export function GqChatbotLoader() {
useEffect(() => {
void import('gq-chatbot-widget/client').then(({ registerGqChatbotWidget }) => {
registerGqChatbotWidget()
})
}, [])
return null
}Luego colocá <gq-chatbot-widget use-shadow="true" agent-id="..." /> en el árbol. El entry gq-chatbot-widget/client solo exporta registerGqChatbotWidget (no registra al importar), ideal para evitar side effects en el servidor.
Import único (auto-registro en cliente)
'use client'
import 'gq-chatbot-widget'En Server Components, no hagas import 'gq-chatbot-widget' en el módulo del servidor: usá la etiqueta + <script> como arriba, o un client boundary con import('gq-chatbot-widget').
Autocompletado en el editor (HTML)
VS Code y Cursor pueden sugerir atributos del custom element usando html.customData.
En este repositorio
Ya está configurado en .vscode/settings.json apuntando a .vscode/gq-chatbot-widget.html-data.json.
Si alguien instala el paquete por npm
El archivo publicado vive en:
node_modules/gq-chatbot-widget/editor/gq-chatbot-widget.html-data.json
En el proyecto consumidor, agrega en .vscode/settings.json (o en tu settings.json de usuario):
{
"html.customData": [
"./node_modules/gq-chatbot-widget/editor/gq-chatbot-widget.html-data.json"
]
}Reiniciá el editor o recargá la ventana si no aparece de inmediato.
En proyectos React / TypeScript (JSX)
El paquete publica tipos en dist/gq-chatbot-widget.d.ts y declara "types" + "exports.types" en package.json.
Eso significa que, en un proyecto consumidor con npm install gq-chatbot-widget, al escribir <gq-chatbot-widget /> en .tsx el editor debería sugerir atributos sin copiar un global.d.ts manual, siempre que el proyecto tenga @types/react instalado (porque los tipos referencian react).
Si no ves sugerencias de inmediato: recargá la ventana del editor o verificá que tu tsconfig incluya los archivos donde usás el widget.
API de atributos (qué edita cada uno)
Identidad y comportamiento base
agent-id(requerido): identificador interno del agente (persistencia por agente, plantillas, etc.).agent-name: nombre visible del asistente en bienvenida, avatar y placeholders ({name}).theme-color: color de acento principal (botón enviar, focos, puntos y elementos destacados).use-shadow:true→ Shadow DOM conattachShadow({ mode: 'open' })y CSS solo dentro del shadow (recomendado en Next sin iframe).shadow:open|closed; también activa Shadow DOM (vacío =open). Alternativa ause-shadow="true".
Webhook (chat remoto por HTTP POST)
chat-webhook-url: URL del webhook que recibe JSON{ "userMessage", "sessionId" }. Si está vacío, el chat usa respuesta simulada.chat-api-key: valor del headerx-api-keyen el POST (sensible en HTML público; en producción preferí un proxy en tu backend).session-id: opcional; si se omite, el front genera un UUID y lo guarda ensessionStorageporagent-id.
whatsapp-enabled:true/false; habilita u oculta el botón de WhatsApp en el launcher.whatsapp-phone: número internacional sin+ni espacios (ej:5491122334455).whatsapp-template: plantilla del mensaje para WhatsApp.- Placeholders soportados:
{name},{topic},{agentId}. - Se aplica
encodeURIComponentautomáticamente. - Si
whatsapp-phonees inválido, el botón queda deshabilitado y se muestra warning en consola.
- Placeholders soportados:
Panel y área de conversación
panel-background: fondo global del contenedor del chat.header-background: fondo de la barra superior del chat.messages-background: fondo del área donde se renderizan los mensajes.messages-scrollbar-thumb-color: color del “thumb” del scrollbar vertical de la lista de mensajes (cualquier color CSS). Compatible con Chrome, Edge (motor Chromium), Safari (WebKit) vía::-webkit-scrollbar, y Firefox víascrollbar-color/scrollbar-width: thin.messages-scrollbar-track-color: color de la pista del scrollbar (opcional; por defecto un gris muy suave).assistant-bubble-background: color de burbuja del bot.assistant-bubble-text-color: color de texto dentro de burbujas del bot.user-bubble-background: color de burbuja del usuario.user-bubble-text-color: color de texto dentro de burbujas del usuario.
Input de mensaje
input-wrapper-background: fondo del borde exterior del input.input-background: fondo interno del campo de texto.input-text-color: color del texto escrito por el usuario.input-placeholder-color: color del placeholder del input.input-border-color: color del borde del campo de escritura.
Launcher (botón/barra inferior)
launcher-background: fondo del launcher.launcher-border-color: color del borde del launcher.launcher-text-color: color de texto e íconos del launcher.launcher-secondary-background: fondo de la pastilla¿Necesitas ayuda?.launcher-cta-background: fondo del botón CTA principal.launcher-cta-text-color: color de texto del CTA principal.
Borde con gradiente (launcher + panel abierto)
El anillo animado recorre el perímetro del launcher (círculo y barra “peek”) y del panel de chat cuando está abierto.
gradient-border-enabled:true(por defecto) ofalse. Desactiva por completo el efecto (sin animación ni anillo de 2px).gradient-border-from: primer color del gradiente (cualquier color CSS válido, p. ej.#0084d1).gradient-border-to: segundo color (p. ej.#0047bb).
Si el visitante tiene preferencia de movimiento reducido (prefers-reduced-motion), el borde animado no se muestra aunque gradient-border-enabled sea true (se usa el mismo chrome sólido que con el borde desactivado).
Ejemplo:
<gq-chatbot-widget
agent-id="mi-agente"
gradient-border-enabled="true"
gradient-border-from="#0084d1"
gradient-border-to="#0047bb"
></gq-chatbot-widget>Scrollbar de la lista de mensajes
El área con scroll usa la clase .gq-messages-scroll: barra fina (7px en WebKit), thumb redondeado con margen visual (background-clip: padding-box) y estados :hover / :active discretos.
Si no pasás atributos, el thumb toma un azul semitransparente acorde al tema por defecto del widget.
Ejemplo completo
<gq-chatbot-widget
agent-id="agent_0701kp9thvhvevbt2p9tastrz2q7"
agent-name="Pipi"
theme-color="#0ea5e9"
whatsapp-enabled="true"
whatsapp-phone="5491122334455"
whatsapp-template="Hola! Soy {name}. Necesito ayuda con {topic}. ID: {agentId}"
panel-background="#0f1115"
header-background="#151922"
messages-background="#151922"
messages-scrollbar-thumb-color="rgba(14, 165, 233, 0.45)"
messages-scrollbar-track-color="rgba(255, 255, 255, 0.06)"
assistant-bubble-background="#232a38"
assistant-bubble-text-color="#e6edf8"
user-bubble-background="#0ea5e9"
user-bubble-text-color="#ffffff"
input-wrapper-background="#20283a"
input-background="#0d121c"
input-text-color="#d6deeb"
input-placeholder-color="#8b97ad"
input-border-color="#2f3a52"
launcher-background="rgba(10,12,18,0.92)"
launcher-border-color="rgba(148,163,184,0.25)"
launcher-text-color="#e6edf8"
launcher-secondary-background="rgba(148,163,184,0.14)"
launcher-cta-background="#0ea5e9"
launcher-cta-text-color="#ffffff"
gradient-border-enabled="true"
gradient-border-from="#0084d1"
gradient-border-to="#0047bb"
></gq-chatbot-widget>Desarrollo local
npm install
npm run devWebhook y API key desde .env.local (recomendado en este repo)
Para no hardcodear URL ni clave en index.html, usá variables solo en desarrollo (Vite las expone al cliente si empiezan con VITE_):
- Copiá
.env.examplea.env.localen la raíz del proyecto (ese archivo está cubierto por*.localen.gitignorey no debería subirse a git). - Completá al menos:
VITE_CHAT_WEBHOOK_URL: la URL de tu webhook o una ruta relativa del mismo origen que use el proxy de Vite (ver abajo).VITE_CHAT_API_KEY: misma clave que enviarías como headerx-api-keyal webhook.
- Reiniciá
npm run devcada vez que cambies.env.local.
Proxy en desarrollo vs URL absoluta
En npm run dev el front suele servirse desde localhost con un puerto fijo. Si el widget hace fetch a otro origen (otro dominio o puerto), el navegador aplica CORS; el servidor del webhook tiene que responder con cabeceras Access-Control-Allow-Origin acordes, o aparecerán errores tipo “blocked by CORS policy”.
Dos enfoques habituales:
- Proxy en Vite: en
.env.localusás una ruta relativa del mismo origen que la app de desarrollo (por ejemplo/api/mi-webhook); envite.config.tsconfigurás el proxy para reenviar esa ruta al host y path reales del webhook. El navegador solo habla con el servidor de Vite, así evitás CORS en local. - URL absoluta del webhook: podés poner en
VITE_CHAT_WEBHOOK_URLla URL completa del endpoint, siempre que ese servidor permita explícitamente el origen de tu entorno de desarrollo.
La ruta del proxy, el host y el path del webhook los definís vos según tu stack; usá vite.config.ts y .env.example como referencia y adaptalos a tu despliegue. Podés anotar la URL real en comentarios dentro de .env.local para no perder el contexto.
Prioridad: si el elemento <gq-chatbot-widget> tiene los atributos chat-webhook-url o chat-api-key, esos valores pisan lo definido en .env.local (útil para pruebas puntuales).
En producción (sitio embebido sin Vite), las variables VITE_* no existen: configurá chat-webhook-url y chat-api-key en el HTML, o mejor, un endpoint propio que haga de proxy y oculte la clave.
Build
npm run buildArchivos generados en dist/:
chat-widget.umd.jschat-widget.es.js
Publicación en npm
npm login --auth-type=web
npm publish --access public