Biz Next.js Chatbot OpenRouter — Instala un chatbot AI flotante en tu sitio web en un solo paso
Esta skill añade un chatbot AI en forma de widget flotante a tu proyecto Next.js existente. El widget se sitúa en la esquina inferior derecha, es totalmente responsive en los 3 tamaños de pantalla, llama a un LLM a través de OpenRouter con una base de conocimiento basada en FAQ + documentación del curso/producto, renderiza Markdown en las burbujas del asistente (negritas para números/precios, listas con viñetas, enlaces cliqueables, código inline) usando react-markdown + remark-gfm, y extrae y guarda automáticamente leads (nombre + teléfono + correo electrónico) en KV (Upstash Redis / Vercel KV) cuando el usuario proporciona la información completa en el chat — el administrador puede verlos a través del dashboard /admin. Objetivo: solopreneurs / creadores de cursos que necesitan un bot de soporte automático 24/7 + captura pasiva de leads directamente en su landing page de venta de cursos, sin perder clientes que preguntan por chat.
Espíritu: Detecta automáticamente todo lo que puede (versión de Next.js, App/Pages Router, TS/JS, Tailwind/CSS), solo pregunta al usuario las 4 cosas realmente necesarias: (1) qué proyecto si hay varios, (2) FAQ, (3) documentación del producto, (4) tono/nombre de marca. El resto es código → en vivo en minutos.
Cuándo usarlo
El usuario quiere:
- Añadir un chatbot AI de atención al cliente en su landing page de venta de cursos/servicios
- Reducir la carga de responder preguntas repetitivas (precio, ruta, reembolso, público objetivo...)
- Un widget de chat pequeño en la esquina inferior derecha, que no tape el contenido
- Un chatbot que entienda con precisión el contenido del curso/producto (impulsado por FAQ)
- Ya tiene un proyecto Next.js (acaba de ejecutar
/biz-sales-page-layout, o es un proyecto antiguo)
Cuándo NO usarlo
- El proyecto no es Next.js (React puro con Vite, Vue, Svelte, HTML plano) → esta skill es específica para Next.js. Se puede adaptar pero no es óptimo.
- Se necesita un chatbot con RAG (recuperación aumentada) con una base de conocimiento de miles de páginas → se necesita una base de datos vectorial (Pinecone/Supabase pgvector), esta skill usa un system prompt simple adecuado para ≤ 20K tokens de conocimiento.
- Se necesita un chatbot con voz / multimodal (enviar imágenes) → esta skill es solo texto.
- Se necesita guardar el historial de chat por usuario en una base de datos → esta skill es sin estado, sesión efímera, se puede ampliar más adelante.
- El backend no es compatible con Vercel (necesita long polling, WebSocket persistente) → OpenRouter streaming funciona bien en Vercel Edge, pero si se necesita un WS separado, es diferente.
Resultado que recibirá el usuario
Después de que la skill se ejecute:
-
Nuevos archivos en el proyecto Next.js del usuario:
components/Chatbot.tsx(o.jsx) — widget UI (renderiza markdown conreact-markdown+remark-gfm)app/api/chat/route.ts(App Router) Opages/api/chat.ts(Pages Router) — proxy API que llama a OpenRouter + extrae automáticamente leads después del streamlib/chatbot-knowledge.ts(o.js) — base de conocimiento + system prompt (incluye bloque de FORMATO DE RESPUESTA + bloque de RECOPILACIÓN ACTIVA DE LEADS)lib/chat-lead.ts— SOLO se crea si el proyecto tiene KV/Upstash — extractor y guardador, deduplica por teléfono, TTL de 90 días, namespacechat-lead:{phone}- Estilos de Markdown
.chatbot-mdañadidos aapp/globals.css(desdetemplates/chatbot-markdown-styles.css) - Se crea/añade
.env.localconOPENROUTER_API_KEY=(placeholder) .env.example(si no existía) para documentar las variables de entorno- Se monta
<Chatbot />enapp/layout.tsxopages/_app.tsx
-
Paquetes adicionales instalados (a través del gestor de paquetes ya detectado por el lockfile):
react-markdown— renderiza markdown de forma segura (sin necesidad dedangerouslySetInnerHTML)remark-gfm— GitHub Flavored Markdown (tablas, autolink, tachado)
-
Instrucciones claras al final de la sesión:
- 3 pasos que el usuario debe hacer por su cuenta: (a) obtener la clave API de OpenRouter, (b) pegarla en
.env.local, (c)npm run devpara probar - Cómo cambiar el modelo entre Gemini y Sonnet
- Cómo actualizar la base de conocimiento más adelante
- 3 pasos que el usuario debe hacer por su cuenta: (a) obtener la clave API de OpenRouter, (b) pegarla en
Flujo de trabajo
Paso 1 — Detectar el proyecto Next.js
Busca proyectos Next.js candidatos. Ejecuta:
# Busca package.json con "next" en dependencies, profundidad máxima 4
find /Users/tonyhoang/Documents/GitHub -maxdepth 4 -name "package.json" -not -path "*/node_modules/*" 2>/dev/null | xargs grep -l '"next"' 2>/dev/null | head -20
(Si el usuario está en un directorio de trabajo específico, prioriza la búsqueda desde cwd hacia abajo, fallback a ~/Documents/GitHub si no encuentra.)
Ramificación:
- 0 proyectos: Informa al usuario y se detiene. Sugiere: "¿Tiene algún proyecto Next.js? Si no, ¿quiere que cree uno nuevo con
/biz-sales-page-layouty luego volvemos?" - 1 proyecto: Confirma la ruta con el usuario antes de continuar: "Instalaré el chatbot en [ruta]. ¿Está bien?"
- ≥ 2 proyectos: Lista la ruta + el nombre (
namedel package.json) en una lista numerada, pide al usuario que elija: "Encontré N proyectos Next.js. ¿En cuál desea instalar el chatbot?"
Paso 2 — Sondear el contexto del proyecto
Después de que el usuario confirme el proyecto, lee:
-
package.json→ verifica:- TypeScript (¿tiene
typescripten deps?) - Tailwind (¿tiene
tailwindcssen devDeps?) - Gestor de paquetes (lockfile:
pnpm-lock.yaml→ pnpm,yarn.lock→ yarn, por defectonpm) - Versión de Next.js
- TypeScript (¿tiene
-
Estructura de directorios → verifica:
- Si existe el directorio
app/→ App Router - Si existe el directorio
pages/(y no hayapp/) → Pages Router - Si ambos existen → prioriza App Router para archivos nuevos (Next.js está migrando)
- Si hay wrapper
src/→ ajusta la ruta (ej.src/app/,src/components/)
- Si existe el directorio
-
tsconfig.json→ verifica el alias de ruta (@/*→./src/*o./*) -
.env.local,.env.example→ verifica si ya existen -
Disponibilidad de KV/Upstash (para captura automática de leads):
package.jsontiene@upstash/redisO@vercel/kv→ tiene KV → CREA chat-lead.ts + lo conecta a la ruta- Tiene archivo
src/lib/kv.ts(olib/kv.ts) que exportakv→ usa el helper existente (patrón de campaña) - No tiene KV → OMITE la creación de chat-lead.ts, informa al usuario: "El proyecto no tiene KV — la captura automática de leads no está activada. Puedo ejecutar
/biz-setup-sepay-paymentpara instalar KV primero y luego volver a activar la captura de leads, u omitirlo."
Guarda estas observaciones en una tabla pequeña para usarlas en pasos posteriores.
Paso 3 — Recopilar la base de conocimiento del usuario
Haz al usuario 4 preguntas (agrupadas en un solo mensaje para que el usuario responda de una vez, evitando idas y vueltas):
Para que el chatbot responda con precisión, necesito 4 cosas:
1. **FAQ** — lista de preguntas frecuentes + respuestas (precio, ruta, reembolso, público objetivo, duración, soporte post-curso...).
Péguelas directamente aquí, O indique la ruta del archivo (.md / .txt / .docx).
2. **Documentación del producto/curso** — descripción detallada del curso: nombre, contenido de cada módulo, resultados esperados, instructor, bonificaciones, niveles de precio si hay varios.
También puede pegar o indicar la ruta del archivo.
3. **Nombre de la marca + tono** — Nombre de la marca/curso para que el chatbot se presente (ej. "asistente de AI Mastery"). Tono del bot: amigable (por defecto) / formal / casual.
4. **Modelo** — por defecto `google/gemini-3-flash-preview` (rápido, barato, suficientemente bueno para soporte). Si quiere usar `anthropic/claude-sonnet-4.6` (mayor calidad, ~10x más caro) dígamelo.
¿Puede responder todo de una vez?
Cuando el usuario responda:
- Si solo indica la ruta del archivo → léelo con la herramienta Read.
- Si pega el contenido directamente → usa el contenido pegado.
- Si el usuario dice "genera un placeholder por mí" → crea un placeholder FAQ genérico y márcalo con
// TODO: user fill inpara que el usuario sepa que debe actualizarlo. - Si no hay nombre de marca → por defecto "Asistente de IA" y trato de usted.
Paso 4 — Generar los archivos en el proyecto
Lee las plantillas en templates/ de esta skill y adáptalas al proyecto. Las plantillas:
templates/chatbot-knowledge.ts— Módulo que exporta la base de conocimiento + systemPrompt. Inyecta FAQ, información del producto, nombre de marca y tono aquí. Incluye bloque de FORMATO DE RESPUESTA (reglas de markdown) + bloque de RECOPILACIÓN ACTIVA DE LEADS (enseña al LLM a pedir nombre/teléfono/correo cuando hay señal de comprador, agradecer + confirmar cuando el usuario los proporciona, NO decir "guardado en base de datos").templates/Chatbot.tsx— Componente del widget, prioriza Tailwind. Ya importareact-markdown+remark-gfm, renderiza los mensajes del asistente con<ReactMarkdown remarkPlugins={[remarkGfm]}>(solo asistente — los mensajes del usuario siguen siendowhitespace-pre-wrapsimples). Si el proyecto NO tiene Tailwind → usa fallbacktemplates/Chatbot-noTailwind.tsx+templates/chatbot.module.css.templates/chat-route-app.ts— Ruta API para App Router (app/api/chat/route.ts). Incluye el hookpersistLeadIfReadyque se ejecuta fire-and-forget después de que el stream se cierra — no bloquea la respuesta.templates/chat-route-pages.ts— Ruta API para Pages Router (pages/api/chat.ts).templates/chat-lead.ts— SOLO se crea si el proyecto tiene KV/Upstash — extrae regex de teléfono VN + correo electrónico + heurística de nombre (patrones de introducción "me llamo X" / "soy X" / "nombre: X"),saveChatLeaddeduplica por teléfono, TTL de 90 días, namespace de clavechat-lead:{phone}.templates/chatbot-markdown-styles.css— Fragmento CSS para la clase.chatbot-md. DEBE añadirse al final deapp/globals.css(osrc/app/globals.css/styles/globals.css). Cambia el color de acento (#fde68a/#f59e0b) según la paleta de la marca del proyecto antes de añadirlo.templates/env-instructions.md— Fragmento de instrucciones para obtener la clave de OpenRouter, usado en el paso final.
Reglas al adaptar la plantilla:
-
TS vs JS: si el proyecto solo tiene JS, cambia la extensión
.ts/.tsx→.js/.jsxy elimina las anotaciones de tipo (no solo cambies el nombre del archivo). Lee el archivo de ejemplo, analízalo, elimina los tipos y escribe el resultado. -
Alias de ruta: si
tsconfigmapea@/*→./src/*, las importaciones en la plantilla deben usar@/lib/chatbot-knowledgey el archivo debe colocarse ensrc/lib/. Si no hay alias, usa rutas relativas. -
Modelo: reemplaza el placeholder
__MODEL_ID__por el modelo elegido por el usuario (google/gemini-3-flash-previewoanthropic/claude-sonnet-4.6). -
Marca: reemplaza
__BRAND_NAME__,__BRAND_TONE__. -
Conocimiento: construye
systemPrompta partir de FAQ + información del producto en una estructura clara (ver plantilla). Mantén el bloque de FORMATO DE RESPUESTA en el systemPrompt — esto es lo que enseña al LLM a generar markdown adecuado para una burbuja de chat pequeña (negritas en lugar de encabezados, viñetas cortas de una línea). -
Instala paquetes de markdown (OBLIGATORIO — si se omite, el chatbot mostrará
**bold**en texto plano en lugar de en negrita en la burbuja):# detecta lockfile → elige el gestor de paquetes correcto npm install react-markdown remark-gfm # o: pnpm add react-markdown remark-gfm # o: yarn add react-markdown remark-gfm -
Añade los estilos de markdown: lee
templates/chatbot-markdown-styles.css, cambia el color de acento según la paleta de la marca del proyecto (revisaglobals.cssen busca de--color-primaryo equivalente), luego añádelo al final deapp/globals.css(osrc/app/globals.css/styles/globals.css). -
Monta el widget:
- App Router: edita
app/layout.tsx(osrc/app/layout.tsx), añadeimport Chatbot from '@/components/Chatbot'y<Chatbot />justo antes de</body>. Lee el layout.tsx actual antes de editarlo para mantener los metadatos, fuentes, providers. - Pages Router: edita
pages/_app.tsx, envuelve el return:<><Component {...pageProps} /><Chatbot /></>. Lee_appprimero, mantén los providers existentes.
- App Router: edita
-
Conexión de la captura automática de leads (solo si se detectó KV en el Paso 2):
- Copia
templates/chat-lead.tsenlib/chat-lead.ts(osrc/lib/chat-lead.ts). - Si el proyecto AÚN NO tiene
lib/kv.ts→ créalo:import { Redis } from "@upstash/redis"; export const kv = Redis.fromEnv(); - El manejador de ruta importa:
import { extractLeadFromMessages, saveChatLead } from "@/lib/chat-lead". - En el bucle del stream: rastrea
assistantText(acumula delta), cuando el stream se cierre → llama apersistLeadIfReady(history, assistantText)(fire-and-forget, NO uses await en la ruta de respuesta). - System prompt: asegúrate de que el bloque de RECOPILACIÓN ACTIVA DE LEADS esté en
chatbot-knowledge.ts(la plantilla ya lo incluye). - Confirma con el usuario: "He activado la captura automática de leads — cuando un cliente pegue nombre/teléfono/correo en el chat, el sistema lo guarda automáticamente en KV namespace
chat-lead:{teléfono}. Puede ver los leads en/admin(si ya instaló/biz-admin-leads-dashboard)."
- Copia
Paso 5 — Configurar .env.local + .gitignore
-
Verifica si
.env.localya existe:- No: créalo con:
# Clave API de OpenRouter — Obténla en https://openrouter.ai/keys OPENROUTER_API_KEY= # Opcional: URL del sitio para que OpenRouter rastree el referrer (ayuda con el rate limit) NEXT_PUBLIC_SITE_URL=http://localhost:3000 - Sí: añade las 2 variables anteriores si no están, sin sobrescribir las existentes.
- No: créalo con:
-
Actualiza
.env.example(crea uno nuevo si no existe):OPENROUTER_API_KEY=sk-or-v1-xxxxx NEXT_PUBLIC_SITE_URL=https://your-domain.com -
Verifica si
.gitignoreya tiene.env.local. Si no → añade.env.localy.env*.local.
Paso 6 — Informe al usuario
Imprime 1 mensaje de resumen claro con 4 partes:
✅ Chatbot instalado correctamente en [nombre del proyecto]
📁 Archivos creados:
- components/Chatbot.tsx (renderiza markdown con react-markdown + remark-gfm)
- app/api/chat/route.ts
- lib/chatbot-knowledge.ts
- app/globals.css (se añadieron los estilos de markdown .chatbot-md)
- .env.local (placeholder)
📦 Paquetes instalados: react-markdown, remark-gfm
🔑 3 pasos que debe hacer para que el chatbot funcione:
1. Obtén la clave API de OpenRouter:
- Ve a https://openrouter.ai → Regístrate (puedes iniciar sesión con Google)
- Ve a https://openrouter.ai/keys → Crear clave
- Añade crédito (https://openrouter.ai/credits) — Gemini 3 Flash es barato, con $5 tienes para miles de chats
- Copia la clave con formato `sk-or-v1-xxxxx`
2. Pégala en el archivo `.env.local`:
OPENROUTER_API_KEY=sk-or-v1-xxxxx-pégala-aquí
3. Pruébalo:
[npm/pnpm/yarn] run dev
Abre http://localhost:3000 → haz clic en la burbuja de la esquina inferior derecha
🔄 Cambiar entre modelos Gemini ↔ Sonnet:
Abre `app/api/chat/route.ts`, modifica la variable `model:` al principio del archivo.
- `google/gemini-3-flash-preview` (por defecto, $0.075/1M input, rápido)
- `anthropic/claude-sonnet-4.6` (avanzado, ~$3/1M input, mejor calidad)
📚 Actualizar la base de conocimiento más adelante:
Edita `lib/chatbot-knowledge.ts` — añade/modifica FAQ y productInfo, guarda, recarga en caliente.
Diseño responsive Mobile-first
El widget debe cumplir con 3 puntos de ruptura:
| Punto de ruptura | Comportamiento |
|---|---|
| ≤ 640px (móvil) | Al hacer clic en la burbuja → se abre una superposición de pantalla completa (z-index 9999), el encabezado tiene un botón X para cerrar. El input está anclado en la parte inferior con safe-area-inset para que no quede oculto por el teclado. |
| 641–1023px (tablet) | Panel de 380px × 70vh, anclado en la esquina inferior derecha con margen de 16px. |
| ≥ 1024px (escritorio) | Panel de 400px × 600px, anclado en la esquina inferior derecha con margen de 24px. Burbuja redonda de 60px. |
**
…
[truncado para la traducción]