Novedades · Changelog

Cada semana, un poco mejor.

Mejoramos GarBotGPT casi a diario. Estas son las novedades más recientes: nuevas herramientas, mejoras de velocidad y arreglos, contadas en claro.

  1. v8.33.5

    🎬 Video Generation — Runway Gen-3 + card embebida

    Feature 5/7 del roadmap. Nueva tool de generación de video usando Runway Gen-3 (text-to-video, 5-10s, 16:9/9:16/1:1). Toggle en el menú del ChatInput debajo de "Generar Imagen". El video se renderiza embebido en una card con descarga + abrir + estados loading/error.

    Nuevo

    • 🎬 Tool video-generation provider-agnostic (Runway/Sora/Veo/Luma)
    • 📦 meta_json.video_generation persistido en el assistant message
    • 🚨 Si falta RUNWAY_API_KEY: status skipped + mensaje en systemPromptAddition
    • 🧪 Typecheck verde

    Mejorado

    • Default: Runway Gen-3 (RUNWAY_API_KEY) con polling 60s + fallback "processing"

    Arreglado

    • 🎨 VideoGenerationCard: header gradient + <video> embebido + descarga + abrir + estados ready/processing/error

    Seguridad

    • 🪪 Toggle "Generar Video" en el menú de tools (sólo si tiene permiso de imageGeneration)

    Nuevo: src/lib/server/chat/tools/video-generation.ts

    • runVideoGenerationTool({ prompt, durationSec, aspectRatio })
    • runRunwayGeneration: POST /v1/text_to_video → poll /v1/tasks/{id}
    • Devuelve { status: ready | processing | error, video_url, thumbnail_url, provider, prompt, duration_s, task_id, error }
    • Sin API key → status: skipped (no bloquea, informa al LLM)

    Nuevo: src/components/chat/VideoGenerationCard.tsx

    • 3 estados: ready (video embebido + downloads), processing (loader), error (rojo + sugerencia)
    • Botón descarga via fetch + Blob + a.click
    • Botón abrir en pestaña nueva

    Integración chat

    • chat-store: videoGeneration boolean + setVideoGeneration
    • src/app/api/chat/route.ts: parámetro video_generation + effectiveVideoGeneration + invocación + persistencia
    • src/components/chat/ChatInput.tsx: toggle "Generar Video" en menú desktop (icono Film)
    • src/components/chat/MessageBubble.tsx: getter videoGenerationMeta + render VideoGenerationCard

    Configuración requerida

    • Env var: RUNWAY_API_KEY (https://app.runwayml.com/account)
    • Pricing aproximado: ~$0.05/segundo de video Gen-3 turbo
    • Sin la env var, la tool devuelve "skipped" + el LLM informa al user
  2. v8.33.4

    📌 Web Capture — Chrome extension + endpoint /api/capture

    Feature 4/7 del roadmap. Extensión de Chrome (Manifest V3) que envía cualquier página web o selección de texto a GarBotGPT con un click derecho. Crea una conversación nueva con el contenido capturado pre-cargado como primer mensaje del usuario.

    Nuevo

    • 📦 Nueva carpeta chrome-extension/ con la extensión completa
    • 🖱️ 3 entradas en context menu: página completa / selección / enlace
    • 🪟 Popup en la toolbar con 3 botones
    • 🔐 Auth vía cookie del navegador (debes estar logueado en ai.garbotgpt.com)
    • 📝 README con install + uso + troubleshooting

    Mejorado

    • Endpoint /api/capture (Next.js route) con fetchPageContent para extraer contenido
    • 🎨 Iconos 16/32/48/128 generados con node-canvas (script reproducible)

    chrome-extension/

    • manifest.json — Manifest V3, host_permissions ai.garbotgpt.com, action popup + icons
    • background.js — service worker con context menus + chrome.action click + badge progress
    • popup.html + popup.js — UI con 3 botones (página/selección/abrir app)
    • generate-icons.cjs — script node-canvas que regenera icons/icon-{16,32,48,128}.png
    • README.md — install / uso / troubleshooting

    src/app/api/capture/route.ts

    • POST endpoint con auth Supabase cookie
    • Body: { url, title?, selection?, full_page? }
    • Si full_page=true, fetcha el HTML via fetchPageContent (cap 6000 chars)
    • Crea conversación con título "📌 <título>" + participant entry
    • Inserta primer message del user con: URL + selección + contenido + meta_json captured_*
    • sources_json incluye chunk con source_type: web para el SourcesPanel
    • Devuelve { conversation_id, url: "/chat/<id>" }
    • maxDuration 25s (fetchPageContent puede tardar)

    Flujo de usuario

    • 1. Instala la extensión (chrome://extensions → cargar descomprimida → chrome-extension/)
    • 2. Estás logueado en ai.garbotgpt.com
    • 3. Click derecho en cualquier web → "Enviar página a GarBotGPT"
    • 4. La extensión llama POST /api/capture con cookie
    • 5. Backend crea conv + 1er mensaje con el contenido + redirige al chat
    • 6. El AI ve el contenido y puede analizarlo/resumir/responder preguntas
  3. v8.33.3

    ✨ Smart Suggestions v2 — sugerencias proactivas con anti-spam

    Feature 3/7 del roadmap. La WelcomeScreen ahora muestra 2-3 cards con sugerencias proactivas generadas por LLM basándose en el historial reciente del usuario (últimos 7 días). Click → crea conversación nueva con el prompt pre-cargado.

    Nuevo

    • 🧠 Backend lee conversaciones recientes (últimos 7 días, hasta 12 convs) y resume con últimos mensajes user/assistant
    • Anti-spam: dismissal persistido en localStorage por hash
    • 😴 Fatigue: 2 dismissals/día → silencio total las próximas 24h
    • 👁️ Skip si está en streaming (no distraer mid-respuesta)
    • 📝 Draft pickup: click envía el prompt al ChatInput vía sessionStorage

    Mejorado

    • LLM gpt-4o-mini genera 2-3 sugerencias variadas (continuar, revisar, crear)
    • 💾 Cache 24h por usuario en memoria → 1 LLM call/día máximo (coste controlado)
    • 🎨 Cards bonitas en grid 2-col con icono sparkle + título + razón

    Nuevo: src/lib/server/proactive-suggestions.ts

    • loadRecentConversations: query convs activas + sus últimos 2 user messages + 1 assistant
    • buildUserPrompt: estructura el contexto temporal con fechas
    • callLlm: gpt-4o-mini con response_format JSON estricto, parsea + valida
    • Cache in-memory Map per-user, TTL 24h

    Nuevo: src/app/api/suggestions/proactive/route.ts

    • GET endpoint, query param ?force=1 para saltar cache (debug)
    • maxDuration 15s (LLM puede tardar)

    Nuevo: src/components/chat/ProactiveSuggestions.tsx

    • Renderiza en WelcomeScreen encima del ChatInput
    • localStorage para dismissals (hash del texto)
    • Fatigue counter (2 dismissals/24h → hide all)
    • Click → crea conv + sessionStorage draft → router push

    Integración con ChatInput existente

    • Draft pickup desde sessionStorage tiene prioridad sobre localStorage
    • Key: garbotgpt:draft:<convId>
    • Se consume y limpia tras leer
  4. v8.33.2

    🎨 Splash screen 2s + fix PWA start_url para iOS

    Pantalla de bienvenida con logo + animaciones suaves en cold-start (una vez por sesión). Plus fix del quirk de iOS donde añadir la PWA desde /auth/login dejaba al user atascado en login — ahora si hay sesión activa, redirige a /chat automáticamente.

    Nuevo

    • 🎬 SplashScreen.tsx — logo + glow pulsante + spinner + fade-out 400ms (2s total)
    • 📦 sessionStorage flag para mostrar solo en cold-start (no en cada navegación)
    • Respeta prefers-reduced-motion (sin animaciones)

    Mejorado

    • 🔧 manifest.json: start_url con ?source=pwa para tracking, id="/", display_override=[standalone, minimal-ui]
    • 🎨 themeColor unificado a #7C3AED (era #8B5CF6, ahora coincide con el manifest)

    Arreglado

    • 🐛 Override: `?splash=1` fuerza el splash para debug
    • 🍏 Fix PWA iOS: si /auth/login se abre con sesión activa → redirect a /chat (o nextPath)

    Nuevo: src/components/ui/SplashScreen.tsx

    • Mesh gradient backdrop con paleta del logo (indigo/magenta/ámbar)
    • Logo 100-120px con scale-up + glow pulsante
    • Spinner sutil debajo del wordmark
    • Fade-out 400ms tras 2000ms
    • sessionStorage key: garbotgpt:splash:shown:v1

    Fix PWA quirk de iOS

    • src/app/auth/login/page.tsx: useEffect que llama supabase.auth.getSession() al mount; si hay session.user, router.replace(nextPath)
    • public/manifest.json: start_url "/?source=pwa", id "/", display_override
    • src/app/layout.tsx: themeColor unificado a #7C3AED

    Por qué pasaba el bug PWA

    • iOS Safari respeta el `start_url` del manifest cuando lanza el PWA desde home screen
    • PERO en algunos casos guarda como bookmark la URL exacta donde se hizo "Añadir a pantalla de inicio"
    • Si añadiste desde /auth/login, el PWA abría en /auth/login aunque hubieras sido logueado
    • Fix: detectar sesión activa en login → redirect inmediato
  5. v8.33.1

    ✨ Custom GPTs / Saved Workflows — plantillas reutilizables

    Feature 2/7 del roadmap. Crea plantillas con nombre + icono + system prompt + modelo + tools por defecto + starter prompts. Aparecen en la sección "Mis GPTs" del sidebar. Activar un GPT crea una conversación nueva con su config aplicada automáticamente (system prompt inyectado, tools forzados, modelo preseleccionado).

    Nuevo

    • 🆕 Columna `conversations.active_gpt_id` (FK con ON DELETE SET NULL)
    • 🔌 API: GET /api/gpts, POST /api/gpts, PATCH /api/gpts/[id], DELETE /api/gpts/[id]
    • 💾 Zustand store `useCustomGptsStore` con CRUD + modal state
    • 📋 Sidebar: sección "Mis GPTs" con icon + nombre, botón "+", edit on hover
    • 🚀 Activar GPT: aplica modelo + tools + crea conv con active_gpt_id
    • 🧠 Chat route: carga active_gpt, inyecta su system_prompt como bloque prioritario, fuerza tools_json marcados, RPC increment_usage
    • 🧪 Typecheck verde

    Mejorado

    • 🎨 Editor modal completo: name, descripción, emoji, color (12 paletas), system prompt, modelo, 9 toggles de tools, hasta 8 starters

    Seguridad

    • 🆕 Nueva tabla `custom_gpts` con RLS per-user + función increment_custom_gpt_usage

    Nueva infrastructure

    • supabase/migrations/077_custom_gpts.sql (tabla + RLS + función + columna en conversations)
    • src/lib/types.ts: CustomGpt + CustomGptToolsConfig + Conversation.active_gpt_id
    • src/app/api/gpts/route.ts + [id]/route.ts
    • src/store/custom-gpts-store.ts
    • src/components/gpts/CustomGptEditorModal.tsx (~400 lineas)
    • src/components/sidebar/CustomGptsList.tsx (~170 lineas)

    Integración con chat existente

    • src/components/sidebar/sleek/SidebarSleek.tsx: sección "Mis GPTs" arriba de Carpetas
    • src/components/chat/ChatShell.tsx: <CustomGptEditorModal /> global
    • src/store/chat-store.ts: createConversation acepta options.activeGptId
    • src/app/api/conversations/route.ts: pasa active_gpt_id al INSERT
    • src/app/api/chat/route.ts: carga active_gpt, aplica system_prompt + tools_json + increment_usage

    Próximos features del roadmap v8.33.x

    • v8.33.2 — Smart Suggestions v2 (proactivas con anti-spam)
    • v8.33.3 — Web Capture (Chrome extension + endpoint /api/capture)
    • v8.33.4 — Video Generation (Runway/Sora/Veo)
    • v8.33.5 — Voice Cloning (ElevenLabs per agent)
    • v8.34.0 — API pública (api.garbotgpt.com)
  6. v8.33.0

    ⚡ Slash commands inline (/) — comandos rápidos en el ChatInput

    Al escribir "/" al inicio del input aparece un menú flotante con comandos rápidos (summarize, explain, improve, translate, code, clear, help). Navegación con ↑↓ + Enter o Tab. Cada comando construye un prompt inteligente y lo envía automaticamente al chat. Feature #1 del roadmap v8.33.x.

    Nuevo

    • 🔍 Filtrado por substring: "/sum" deja solo /summarize
    • ⌨️ Keyboard nav: ↑↓ navegar, Enter o Tab para seleccionar, Esc salir
    • 🎯 Dark mode + accent violet, accesible
    • 📝 /help muestra toast con la lista completa
    • 🚫 Enter normal NO envía "/comando" crudo — el menú lo intercepta
    • 🧪 Typecheck verde

    Mejorado

    • 7 comandos built-in: /summarize, /explain, /improve, /translate, /code, /clear, /help
    • 🎨 Menú flotante con icon + descripción + arg hint

    Nuevo: src/components/chat/SlashCommandMenu.tsx

    • Detecta "/" al inicio del input + filtra por substring del query
    • Cada SlashCommandDef tiene name, trigger, description, icon, argHint, buildPrompt()
    • Acciones especiales (clear, help) gestionadas por el padre vía cmd.special
    • Keyboard navigation con event listener global (capture phase)
    • Position above|below (default above)

    src/components/chat/ChatInput.tsx

    • Textarea ahora envuelto en <div className="relative min-w-0 flex-1"> para que el menú floate encima
    • handleSlashCommandSelect callback que construye QueuedMessageItem y lo envía via handleSend
    • handleKeyDown bloquea Enter cuando el input es un comando incompleto (sin match exacto)
    • /help → toast informativo con todos los comandos
    • /clear → vacía el input

    Próximos pasos del roadmap v8.33.x

    • v8.33.1+ — Saved Workflows / Custom GPTs (templates de prompt + tools + memoria)
    • v8.33.x — Smart Suggestions v2 (proactivas con anti-spam)
    • v8.33.x — Web Capture (Chrome extension + endpoint)
    • v8.33.x — Video Generation (Runway/Sora)
    • v8.33.x — Voice Cloning (ElevenLabs per agent)
    • v8.34.0 — API pública (api.garbotgpt.com)
  7. v8.32.7

    🌐 Migración de dominio: gia.gesem.es → ai.garbotgpt.com

    Actualizadas todas las referencias hardcodeadas al dominio antiguo (gia.gesem.es y gia.fgarola.es) por el nuevo ai.garbotgpt.com. Esto afecta los fallback URLs cuando faltan los env vars de producción: OAuth redirects (Google, Microsoft, Spotify, GitHub, Monday), invite emails de registro, auth callbacks. El comportamiento normal sigue priorizando NEXT_PUBLIC_APP_URL / NEXT_PUBLIC_SITE_URL si están definidos.

    Nuevo

    • 🔗 src/lib/app-base-url.ts: DEFAULT_FALLBACK_ORIGIN actualizado
    • 🔐 src/lib/integration-oauth-state.ts: PRODUCTION_FALLBACK actualizado (afecta TODOS los OAuth callbacks)
    • 📧 src/app/api/admin/users/create/route.ts: invite emails y action_links
    • 📧 src/app/api/admin/registration-requests/route.ts: redirect tras aprobar registro
    • 🎙️ src/app/api/voice/transcribe/route.ts: vocabulary hint actualizado
    • 📝 .env.example: ejemplos actualizados (Spotify, GitHub, Monday redirect URIs)

    ⚠️ Pasos manuales necesarios (no automatizables)

    • 1. Vercel/hosting: actualizar env vars NEXT_PUBLIC_APP_URL y NEXT_PUBLIC_SITE_URL → https://ai.garbotgpt.com
    • 2. Google Cloud Console → OAuth 2.0 Client → Authorized redirect URIs: añadir https://ai.garbotgpt.com/api/integrations/google/callback
    • 3. Microsoft Azure Portal → App registrations → Redirect URIs: añadir https://ai.garbotgpt.com/api/integrations/microsoft/callback
    • 4. Spotify Dashboard → App settings → Redirect URIs: añadir el callback correspondiente
    • 5. GitHub OAuth App → Authorization callback URL: actualizar
    • 6. Monday CRM app → Redirect URI: actualizar
    • 7. Supabase Dashboard → Authentication → URL Configuration: Site URL = https://ai.garbotgpt.com + Additional Redirect URLs incluir el callback
    • Hasta que estos cambios estén hechos en cada consola, los nuevos OAuth flows seguirán fallando aunque el código apunte al dominio correcto.

    Notas

    • Los cookies internos (gia_google_integration_state, etc.) NO se renombran — son identificadores internos, no dominios.
    • Las referencias a tika.fgarola.es se mantienen — es un servicio backend separado (Apache Tika), no la app.
    • src/lib/changelog.ts mantiene las menciones históricas al dominio antiguo (historial inmutable).
  8. v8.32.6

    🎨 Logo v2 super pro + PNG transparente + suite favicon completa

    Rediseño mayor del logo: gradiente iridiscente 5-stop (purple → violet → magenta → coral → ámbar), G refinada con sparkle accent estilo Gemini/Claude, depth real con inner shadow + highlight cenital, y suite completa de assets: 5 variantes de logo + 7 favicons regenerados. Manifest actualizado a marca GarBotGPT (antes seguía con "GIA").

    Nuevo

    • 🌈 Gradiente iridiscente 5 stops: #4F46E5 indigo → #7C3AED violet → #EC4899 magenta → #F97316 coral → #FBBF24 ámbar
    • Sparkle accent (4-point star) dentro del counter de la G — toque AI/intelligence
    • 🌑 Inner shadow real en el bottom del mark (depth 3D, no solo highlight top)
    • 🎯 G refinada: gap 52° (más estrecho), crossbar 62% del radio, rounded caps
    • 🪟 5 variantes de logo:
    • • logo.png (512, fondo blanco)
    • • logo-transparent.png (512, FONDO TRANSPARENTE)
    • • logo-mark.png (512, solo mark)
    • • logo-mark-transparent.png (512, solo mark transparent)
    • • logo-square.png (1024 hi-res)
    • 📱 Suite favicon completa regenerada con el nuevo design:
    • • favicon-16x16.png, favicon-32x32.png, favicon-96x96.png (transparent)
    • • apple-touch-icon.png (180×180, fondo blanco)
    • • android-chrome-192x192.png, android-chrome-512x512.png
    • • favicon.ico (también copiado a src/app/favicon.ico para Next App Router)
    • 📦 manifest.json: nombre "GarBotGPT" (antes "GIA"), theme #7C3AED, iconos correctos
    • 📦 site.webmanifest: actualizado a GarBotGPT

    Nuevos assets generados

    • public/logo.png — full logo, fondo blanco
    • public/logo-transparent.png — full logo, alpha real
    • public/logo-mark.png — mark only
    • public/logo-mark-transparent.png — mark only transparent
    • public/logo-square.png — hi-res 1024
    • public/favicon-16x16.png — transparent
    • public/favicon-32x32.png — transparent
    • public/favicon-96x96.png — transparent (nuevo)
    • public/apple-touch-icon.png — 180×180
    • public/android-chrome-192x192.png
    • public/android-chrome-512x512.png
    • public/favicon.ico — 48×48 PNG renombrado
    • src/app/favicon.ico — copia auto-sincronizada para Next

    Manifests actualizados

    • public/manifest.json: name "GarBotGPT", theme #7C3AED, icons → android-chrome-* (purposes any + maskable)
    • public/site.webmanifest: name "GarBotGPT", theme #7C3AED

    scripts/generate-logo.cjs

    • Refactor completo: 1 script genera los 13 assets
    • Soporte transparent para PNG + variants
    • Auto-copia favicon.ico a src/app/ para Next App Router
    • Tokens de diseño centralizados (palette + gradient + glyph dims)
  9. v8.32.5

    🐛 Fix: Network RAG ya no se auto-activa por menciones del assistant

    Cuando un turno previo (sobre todo Deep Research) mencionaba archivos de red en su respuesta (ej: Manual_Interno_GESEM.txt), el SIGUIENTE turno corto del usuario auto-activaba network_drive_rag aunque el usuario no lo hubiera pedido. La intención de consultar la unidad de red debe venir de lo que el usuario escribió, no de lo que el assistant mencionó en respuestas anteriores.

    Nuevo

    • 🚫 extractRecentNetworkFileReferences ahora solo escanea mensajes del usuario
    • 🗑️ Eliminado el boost x1.5 para mensajes del assistant (era el responsable principal)
    • Si el usuario escribe "consulta el Manual_X.pdf" → network RAG se activa correctamente
    • Si el assistant menciona "Manual_X.pdf" en su respuesta → NO contamina el siguiente turno
    • 🧪 Typecheck verde

    Mejorado

    • 📝 Nota: web_search y deep_research badges siguen siendo "sticky" (persisten entre turnos por diseño). Si no quieres usarlos en un turno, desactivalos manualmente en el menú.

    src/lib/server/chat/helpers/network-rag-helpers.ts

    • extractRecentNetworkFileReferences: guard `if (message.role !== "user") continue`
    • roleBoost eliminado (ya no hay distinción usuario/assistant — solo usuario cuenta)
    • recencyBoost se mantiene como única señal de ranking
  10. v8.32.4

    🎨 Logo nuevo nivel diseñador: app-icon G monogram + wordmark

    Logo completamente rediseñado para GarBotGPT. Reemplaza el wordmark plano "garbotgpt" por una composición premium: app-icon rounded square (estilo iOS, radius 22.5%) con gradiente indigo→magenta→ámbar, "G" custom blanca con stroke geométrico (arc abierto + crossbar) dentro del mark, drop shadow suave para elevación, highlight cenital sutil, y wordmark "GarBotGPT" debajo en Inter Semibold con letter-spacing optical.

    Nuevo

    • 📱 App-icon mark estilo iOS premium (rounded square 22.5% radius)
    • ✍️ G custom dibujada con stroke-geometry: 304° arc + crossbar inner, rounded caps
    • 🌫️ Drop shadow indigo suave (premium feel) + highlight cenital top-down
    • 🎯 Wordmark "GarBotGPT" en Inter Semibold con tracking optical -0.025em
    • 📦 3 variantes generadas: logo.png (512, full), logo-square.png (1024, hi-res), logo-mark.png (512, mark sin wordmark)
    • 🛠️ Script reproducible (scripts/generate-logo.cjs) — edita y regenera con `node scripts/generate-logo.cjs`

    Mejorado

    • 🎨 Gradiente 3-stop sofisticado: #5B47FF indigo → #EC4899 magenta → #FBBF24 ámbar (vs. el flat magenta-naranja del antiguo)

    Composición del logo

    • Mark rounded square con app-icon proportions (iOS standard)
    • Drop shadow: dy=4%, blur=13% del size, color del propio gradient
    • Inner stroke 1.2px rgba(0,0,0,0.08) para definir borde
    • G glyph: stroke 11.5% del mark size, white, rounded caps
    • G opening: 56° gap a la derecha (28° arriba + 28° abajo del eje X)
    • Crossbar inner: 55% del radio, alineado con el borde inferior de la apertura

    Outputs

    • public/logo.png (512×512) — logo completo (mark + wordmark)
    • public/logo-square.png (1024×1024) — versión hi-res para redes sociales
    • public/logo-mark.png (512×512) — solo el mark, sin texto, para favicons/avatares
    • public/logo.svg — fuente vectorial editable
    • scripts/generate-logo.cjs — generador reproducible (node-canvas)
  11. v8.32.3

    🎨 Logo nuevo: "garbotgpt" con la estética del antiguo "gia"

    Reemplazado el logo "gia" antiguo por uno nuevo de "GarBotGPT" manteniendo exactamente la misma estética visual: charcoal lowercase bold + dot icónico con gradiente magenta→naranja. El nombre se distribuye en 2 líneas ("garbot" / "gpt") para preservar la compacidad del square format, y el dot del acento se coloca sobre la "o" central de "garbot" como homenaje al dot del "i" del logo original.

    Nuevo

    • Misma tipografía: Arial Black 900, letter-spacing apretado
    • 🔘 Dot iconico sobre la "o" de "garbot" — posición calculada midiendo el ancho del texto
    • 📄 SVG fuente en public/logo.svg para futuras ediciones manuales
    • 🛠️ scripts/generate-logo.cjs reproducible — regeneración con `node scripts/generate-logo.cjs`

    Mejorado

    • 🎨 Logo PNG 512×512 generado vía script con node-canvas
    • 🎯 Misma paleta: charcoal #2a2a2e + gradiente magenta→coral→naranja

    Archivos nuevos / reemplazados

    • public/logo.png — reemplazado (gia → garbotgpt)
    • public/logo.svg — nuevo (fuente vectorial editable)
    • scripts/generate-logo.cjs — nuevo (script reproducible)
  12. v8.32.2

    ✨ Sources estilo OpenWebUI + 2 bugs (mobile selector + auto-model al editar)

    Reemplazada la lista plana de chips de fuentes al final de cada mensaje del assistant por una card colapsable estilo OpenWebUI: pill con favicons apilados + contador, click para expandir la lista numerada con cada fuente clicable. Plus 2 bugfixes: (1) los modelos Ollama no se mostraban como seleccionados al clicarlos en el selector móvil, y (2) al editar un mensaje, la badge del modelo mostraba el predefinido en lugar del auto-elegido por el auto-router.

    Nuevo

    • 🔗 SourcesPanel: pill colapsable con stack de 3 favicons + "N fuentes" + chevron
    • 📜 Expandido: lista numerada con [1] [2] [3]… cada fuente clicable (web → URL, RAG → file preview, network → download inline)
    • Dark mode soportado, accesible (aria-expanded, keyboard)
    • 🧪 Typecheck verde

    Mejorado

    • 🎨 Inspirado en Citations.svelte de OpenWebUI (favicons overlapping + accent color por tipo de fuente)

    Arreglado

    • 🐛 Fix mobile selector: comparación isSelected extendida a m.id || m.config_id || m.provider_model_id (como ya hacía MobileHeader). Ollama models ahora muestran check al seleccionarlos.
    • 🐛 Fix auto-model al editar: persistir publicModelId (modelo realmente usado tras auto-routing) en lugar del model crudo del body. La badge del mensaje regenerado ahora refleja el modelo auto-elegido.

    Nuevo: src/components/chat/SourcesPanel.tsx

    • Dedupe automático por (kind + key) — RAG dupes del mismo file_id se consolidan
    • Web → favicon de google.s2 + abre en pestaña nueva con noopener
    • RAG → icono FileText emerald + onClick → openFilePreview(file_id)
    • Network → icono HardDrive purple + abre /api/network-files/<id>/download?inline=1
    • Pill con 3 favicons + "+N" badge si > 3 fuentes
    • Lista numerada [1] [2]…, accent color por tipo, line-clamp 2 en título

    src/components/chat/MessageBubble.tsx

    • Eliminada la grid plana de chips inline (web blue, rag emerald, network purple).
    • Sustituida por <SourcesPanel sources={sources} onOpenFile={openFilePreview} />.

    src/components/chat/Header.tsx

    • Línea 636 (mobile bottom sheet): isSelected ahora comprueba m.id || m.config_id || m.provider_model_id (antes solo m.id).

    src/app/api/chat/route.ts

    • Líneas 6681-6687 (regenerate principal): model: model → model: publicModelId.
    • Líneas 5799-5812 (regenerate del network_full_dump): mismo cambio.
    • message_versions inserts también usan publicModelId.
  13. v8.32.1

    🐛 Fix bugs al editar mensaje: PDFs de red fantasma + respuesta solapada

    Dos bugs al editar un mensaje ya enviado: (1) reaparecían los PDFs de unidad de red como fuentes (mismo bug que arreglamos en v8.31.8 pero por otro path no cubierto). (2) Solapamiento — la respuesta vieja del assistant reaparecía sobre el chat mientras la nueva se generaba, especialmente notable con web search por su mayor latencia. El bug afectaba a TODAS las herramientas y al modo sin herramientas — web search solo lo hacía más visible por el tiempo extra.

    Nuevo

    • 👁️ Mensaje solapado: poll de 15s en ChatArea pisaba el stream local con la versión vieja de la DB
    • 🎯 Causa raíz: durante un regenerate, el server actualiza el assistant message AL FINAL del stream, no durante. Cualquier loadMessages mientras streamea trae la versión vieja.
    • 🧪 Typecheck verde

    Mejorado

    • Mismo fix para el fast-sync de 250ms en conversaciones compartidas

    Arreglado

    • 🚫 Network drive: 2 puntos de auto-enable adicionales fixed (line 2342, 2347 de route.ts) que NO respetaban regenerate_message_id

    src/app/api/chat/route.ts

    • Línea 2332: `if (!regenerate_message_id && ...)` añadido al guard que escaneaba conversación histórica buscando refs a PDFs.
    • Línea 2352: `if (!regenerate_message_id && ...)` añadido al forced-enable basado en `effectiveExplicitFileReferences`.

    src/components/chat/ChatArea.tsx

    • Poll de 15s (5s si hay task pendiente): si isStreaming && streamingConversationId === activeConversationId → skip refetch.
    • Fast-sync de 250ms (cuando hay foreign user awaiting assistant): mismo guard.
  14. v8.32.0

    🔬 Deep Research reconstruido — multi-step plan-visible (MVP)

    Paso 2/2 de la reconstrucción de Deep Research. Nueva arquitectura: planner (LLM barato genera 3-5 sub-preguntas) → searcher (Tavily en paralelo) → scraper (fetchPageContent extrae texto) → synthesizer (inyecta fuentes citables [1][2] en el systemPrompt). El plan + fuentes consultadas se renderizan en una card colapsable encima de la respuesta.

    Nuevo

    • 🎯 Planner: gpt-4o-mini con response_format JSON → goal + rationale + 3-5 sub-queries
    • 🔍 Searcher: Tavily paralelo (3-4 resultados por sub-query) + dedupe round-robin
    • 📖 Scraper: fetchPageContent con concurrency 4, timeout 12s/URL, cap 4.5k chars
    • 🧠 Synthesizer: systemPrompt con plan + 6-8 fuentes [1][2]… + instrucciones de citación
    • 📦 Persistencia: meta_json.deep_research = { plan, sources, telemetry } en el assistant message
    • 🟡 Mode "exhaustive" pide 4 sub-queries y 8 URLs (vs 3/6 del modo "standard")
    • 🧪 Typecheck verde, ~600 líneas nuevas

    Mejorado

    • 🎨 UI: DeepResearchCard colapsable (header con goal + chips de stats; abierta muestra rationale + plan + lista de fuentes clicables)

    Nuevos archivos (src/lib/server/chat/tools/deep-research/)

    • types.ts — DeepResearchPlan, DeepResearchSourceLite, DeepResearchEvent, DeepResearchResult
    • planner.ts — generateDeepResearchPlan() + fallback determinista si el LLM falla
    • searcher.ts — searchSubQueries() paralelo + dedupeAndLimit() round-robin
    • scraper.ts — scrapeCandidates() con worker pool de 4 + timeout 12s
    • index.ts — runDeepResearch() orquestador con event emitter

    Nuevo componente UI

    • src/components/chat/DeepResearchCard.tsx — card colapsable amarillo-ámbar
    • Header: icono FlaskConical + goal truncado + chips (sub-preguntas, fuentes, duración)
    • Cuerpo (abierta): rationale italic, lista numerada de sub-queries, grid de fuentes con hostname + título + snippet
    • Footer telemetry: URLs leídas / candidatas / k-chars

    Wire-up en route.ts

    • Import runDeepResearch del nuevo módulo.
    • Ejecutado donde estaba el placeholder de v8.31.9 (antes del DB query tool).
    • systemPrompt += drResult.systemPromptAddition (incluye fuentes citables [1][2]…).
    • meta_json.deep_research persistido si DR completó.
    • recordTool('deep_research', start, status, detail) para telemetría.

    MessageBubble.tsx

    • getDeepResearch() extractor de meta_json.deep_research.
    • useMemo para evitar re-parsing en cada render.
    • <DeepResearchCard> renderizado encima del MusicPlayer y del contenido principal.
  15. v8.31.9

    🧨 Deep Research desmantelado (paso 1 de la reconstrucción)

    Paso 1/2: eliminado todo el pipeline antiguo de Deep Research (planificación con sub-preguntas, cache, evidence matrix, SSE de progreso, componentes de UI). El toggle del menú sigue visible pero ahora actúa como un "Web Search extendido". En v8.32 se reconstruye con arquitectura multi-step plan-visible (planner → sub-queries → scrape → synthesize → citations) que ya quedó decidida.

    Nuevo

    • 🗑️ Eliminados 3 componentes: DeepResearchProgress, DeepResearchInlineViewport, DeepResearchFloatingWindow
    • 🗑️ Eliminado endpoint SSE /api/chat/research-events y lib/chat/research-live-events.ts
    • 🗑️ Eliminado openResearchEventsSource + researchEventsSourceRef de ChatInput.tsx
    • 🎚️ Toggle "Deep Research" sigue visible — al activarlo se comporta como Web Search extendido (sin las directivas DR)
    • 🧪 Typecheck verde

    Mejorado

    • 🗑️ Eliminado el bloque DR de route.ts (~350 líneas: planning, follow-up searches, ranking, cache, evidence matrix)
    • 🗑️ Eliminado deepResearchProgress + setDeepResearchProgress de ui-store

    Archivos eliminados

    • src/lib/chat/research-live-events.ts
    • src/app/api/chat/research-events/route.ts
    • src/components/chat/DeepResearchProgress.tsx
    • src/components/chat/DeepResearchInlineViewport.tsx
    • src/components/chat/DeepResearchFloatingWindow.tsx

    route.ts

    • Bloque DR (lines ~5186-5514) → comment placeholder.
    • publishLiveResearchEvent → no-op stub (web-search.ts y youtube.ts siguen llamándolo sin efectos).
    • reset call al inicio del request eliminado.
    • Imports de research-helpers reducidos a las utilidades aún en uso fuera del DR.

    research-helpers.ts (NO eliminado)

    • Se mantiene porque web-search.ts y youtube.ts lo importan.
    • route.ts conserva imports de rankWebSources, runWithConcurrency, parseRerankScores, buildFocusedResearchQuery, extractYouTubeVideoIds, etc.
    • En v8.32 las funciones DR-only de aquí se podrán retirar.
  16. v8.31.8

    🐛 Fix: PDFs de red fantasma + mensaje desaparece en Web Search

    Dos bugs arreglados: (1) la IA inyectaba PDFs de unidad de red como fuentes en respuestas que NO los pedían (ej: "hazme una imagen de Barcelona" → adjuntaba 01-26-FerranGarola.pdf, 02-25-FerranGarola.pdf). (2) Al enviar mensaje desde Welcome con Búsqueda Web activa, el mensaje del usuario desaparecía durante varios segundos antes de que la IA respondiera.

    Nuevo

    • 🚫 Eliminado el auto-enable global de network_drive_rag (cualquier mensaje con 3+ palabras lo activaba si el user tenía drives sincronizados)
    • 🎯 Ahora network_drive_rag respeta solo el flag explícito del cliente (el client ya hace intent detection via hasNetworkDriveIntent)
    • 👁️ Optimistic user message se preserva al cambiar de activeConversation si pertenece a la nueva conv
    • 🧪 Typecheck verde

    Mejorado

    • ⏱️ Especialmente notable en web search por su latencia mayor — antes se veía un gap visual de 5-15s

    src/app/api/chat/route.ts (líneas ~1717-1741)

    • Eliminado el bloque que auto-activaba effectiveNetworkDriveRag basándose en hasInput + 3+ palabras + drives sincronizados.
    • Reemplazado por un comentario explicativo. El client (ChatInput.handleSend, WelcomeScreen.handleSend) ya hace hasNetworkDriveIntent() y solo manda network_drive_rag=true cuando la frase realmente sugiere consultar la red.

    src/store/chat-store.ts (setActiveConversation)

    • Antes: `messages: []` borraba TODO al activar una nueva conversación.
    • Ahora: preserva los mensajes optimistic_local que pertenecen a la nueva conv (filtrados con isOptimisticLocalMessage).
    • mergeServerMessagesWithOptimistic + dedupeMessagesByContentWindow ya manejan los duplicados cuando el servidor responde con la versión persistida.
  17. v8.31.7

    📱 Music player: versión móvil super-adaptada

    Bloque CSS @media (max-width: 520px) y un fallback (max-width: 360px) que reescriben la card del reproductor para pantallas pequeñas: cover centrada, controles touch-friendly (≥44px), waveform compacto, crossfade oculto, descripción y greeting ocultos, playlist con filas reducidas y scroll corto.

    Nuevo

    • 🎯 Cover centrada con max-width 230px (200px en ≤360px)
    • 👆 Botón play 64px + secundarios 44px — todos los tap targets ≥44px
    • 🎚️ Crossfade oculto en móvil (poco usado, libera espacio)
    • 🚫 Sin parallax 3D en touch — evita ángulos raros con tap-drag
    • 🔍 Botón fullscreen del cover siempre visible (no hay hover)
    • 📜 Playlist max-height 320px (260px en ≤360px), filas 38px
    • 🔇 Descripción y greeting del footer ocultos (recuperan vertical)
    • 〰️ Waveform 44px (vs 56px desktop) con padding tap-area 12px

    Mejorado

    • 🎨 Topbar minimalista: sin source-tag, sin subtitle del brand

    src/components/chat/MusicPlayer.tsx

    • Nuevo bloque @media (max-width: 520px) con ~60 reglas mobile.
    • Bloque adicional @media (max-width: 360px) para ultra-narrow.
    • No requiere cambios de JSX: solo CSS.
  18. v8.31.6

    🎤 +14 canciones Suno de Ferran Garola en el music player

    Añadidas las 14 pistas de la playlist Suno (ad3cf70f-…) de Ferran Garola al DEFAULT_PLAYLIST. Los MP3 se sirven directamente desde cdn1.suno.ai con CORS abierto.

    Nuevo

    • 🎵 Rumba de Verano, Baila Hasta el Sol, Ella No Dice Nada
    • 🎵 La Chica del Club, Silencio de Verano, Qué Nos Está Pasando
    • 🎵 Corazón Traicionero, Fiesta Portuguesa, Costa Dorada
    • 🎵 No pares de Bailar, A ti, Corazón Partido
    • 🎵 Vem Encosta Devagar, Qué Está Pasando
    • 🌐 URLs verificadas (200 OK + Access-Control-Allow-Origin: *)
    • 🖼️ Covers desde cdn2.suno.ai (image_<id>.jpeg)
  19. v8.31.5

    🎶 Music player: arreglado el corte a 7s + waveform mucho mas vivo

    Bug crítico: el reproductor se detenía a los ~7 segundos. Causa: el useEffect de MusicPlayer dependía de `initialPlaylist`, cuya referencia cambia cada vez que el padre (MessageBubble) re-renderiza tras refrescar el mensaje del stream — la cleanup ejecutaba audio.pause() + audio.src = "". Ahora el efecto depende solo de `playerId` (message.id es estable). Además, la waveform ahora baila con todas las barras y múltiples frecuencias superpuestas (rápida + lenta + flicker) para parecerse a audio real.

    Nuevo

    • 🎵 useEffect deps reducidas: [initialPlaylist, crossfadeMs, playerId] → [playerId]
    • 🌊 Waveform: las 56 barras bailan (antes solo 4 cerca del playhead)
    • 🎚️ Amplitud proporcional a la distancia al playhead (max cerca, residual lejos)

    Mejorado

    • 🔊 3 frecuencias mezcladas: rápida (90ms), lenta (230ms), flicker (55ms)
    • Eliminado `transition: height 0.12s` para que la danza sea fluida (RAF la maneja)
    • will-change: height + min-height: 4px (mas dinámico visualmente)

    Arreglado

    • 🛑 Fix: audio ya no se detiene a los ~7s por la cleanup del useEffect

    src/components/chat/MusicPlayer.tsx

    • updateWavefill: combina sin(now/90) + sin(now/230) + sin(now/55) por barra.
    • proximityBoost: factor 0.3-1.0 según distancia al playhead.
    • CSS: removido transition de height; gestionado por RAF directo.
    • min-height 6px → 4px para permitir picos más expresivos.
  20. v8.31.4

    🎵 Reproductor de música ahora aparece como chip activo

    Al activar la herramienta "Reproductor de música" desde el menú del ChatInput, ya aparece su chip rosa en el composer y se cuenta en el badge "N actives". Antes el toggle se marcaba con check pero no se reflejaba ni en la cuenta ni en los chips visibles.

    Nuevo

    • 🎵 musicPlayer añadido al array activeToolsCount
    • 🩷 Chip rosa con icono <Music> y botón X para desactivar
    • 🧹 setMusicPlayer(false) añadido a clearActiveTools

    Mejorado

    • Sin cambios de comportamiento del propio reproductor — solo UI
  21. v8.31.3

    📎 Cite mode siempre activo + toggle eliminado del Header

    El botón de "Citar fuentes" (icono Quote) se ha eliminado del Header. citeMode queda activo por defecto (su default ya era true en el store) — las respuestas con RAG citan siempre las fuentes recuperadas sin que el usuario tenga que activarlo manualmente.

    Nuevo

    • 🗑️ Botón Quote eliminado del Header
    • citeMode default sigue siendo true (siempre cita fuentes)
    • 🧹 Removido citeMode/setCiteMode del destructuring de useChatStore
    • 🧹 Removido Quote del import de lucide-react
  22. v8.31.2

    📚 RAG siempre activo + toggle eliminado del Header

    El sistema RAG ahora está activado por defecto en modo "assisted" y el toggle de RAG (off/assisted/strict) se ha eliminado del Header del chat. El botón de "citar fuentes" sigue visible para alternar citaciones.

    Nuevo

    • 🟢 ragMode default: "off" → "assisted" (siempre activo)
    • 🗑️ Toggle RAG eliminado del Header (chat desktop y mobile)
    • 📝 Botón citeMode ahora siempre visible (antes solo si ragMode !== "off")
    • 🧹 Limpiados imports y destructuring de ragMode/setRagMode en Header

    src/store/chat-store.ts

    • Default ragMode pasa de "off" a "assisted".
    • Conversation loader sigue respetando conv.rag_mode persistido.

    src/components/chat/Header.tsx

    • Eliminado el botón <BookOpen> que ciclaba off → assisted → strict.
    • CiteMode button independiente, siempre visible.
    • Removido ragMode/setRagMode del destructuring de useChatStore.
  23. v8.31.1

    🐛 Fix subida de archivos con acentos/apóstrofes ("Invalid key")

    Supabase Storage rechazaba uploads cuyos nombres contenían acentos (è/é/í/ñ), apóstrofes tipográficas, espacios o paréntesis con error "Invalid key". sanitizeFilename ahora normaliza NFD, elimina marcas combinantes (diacríticos) y fuerza el set [A-Za-z0-9._-], preservando legibilidad.

    Nuevo

    • 🔤 Acentos eliminados (NFD + strip combining marks): Estratègies → Estrategies
    • ✂️ Caracteres no permitidos → "_" (apóstrofes, paréntesis, espacios)
    • 📏 Cap a 180 caracteres con extensión preservada
    • 🧹 Colapso de "_" repetidos y trim de bordes
    • 🔄 Misma función compartida en client (WelcomeScreen, ChatInput indirecto) y server (/api/files/upload, /api/projects/.../replace)
    • 🗑️ Eliminado duplicado de sanitizeFilename en upload/route.ts

    src/lib/file-utils.ts

    • sanitizeFilename ahora separa stem/extension, normaliza NFD, strip /[\u0300-\u036f]/ (diacríticos).
    • Sustituye /[^A-Za-z0-9._-]+/ → "_" en stem y extension por separado.
    • Colapsa "_+" y recorta "._-" de bordes.
    • Cap a 180 chars preservando la extensión en minúsculas.

    src/app/api/files/upload/route.ts

    • Eliminado sanitizeFilename local que solo escapaba <>:"/\|?* y control chars.
    • Importa el shared util de @/lib/file-utils.
  24. v8.31.0

    ✨ Welcome enriquecido + rebrand GIA → GarBotGPT

    WelcomeScreen ahora muestra 7 toggles de herramientas en móvil (web, deep, red, doc, imagen, BD, excel) en lugar de 4, con badge contador de herramientas activas y filtrado por permisos del admin. Rebrand completo: todas las menciones de "GIA" se han renombrado a "GarBotGPT" (124 reemplazos en 52 archivos), preservando los IDs de modelo GIA-5.2 etc.

    Nuevo

    • 📱 Mobile quick actions ampliadas: 4 → 7 toggles (añadidos imagen, BD, excel)
    • 🔢 Badge contador de herramientas activas en la sección quick actions
    • 🏷️ Rebrand GIA → GarBotGPT en 52 archivos (traducciones, auth, chat, mobile, sidebar, settings…)
    • 🧠 Preservados los identificadores de modelo (GIA-5.2, GIA-5.2-mini) vía regex con look-ahead
    • Typecheck verde

    Seguridad

    • 🔒 Acciones se filtran por toolPermissions del admin (antes el estado se cargaba pero no se usaba)

    WelcomeScreen.tsx

    • Importados Database, ImageIcon, Sheet de lucide-react.
    • mobileQuickActions ahora incluye image (rosa), db (violeta), sheet (teal) además de los 4 originales.
    • Filtrado .filter() por toolPermissions[key] para respetar configuración admin.
    • activeToolsCount renderizado como badge verde junto al título "Quick actions".

    Rebrand masivo

    • Script regex \bGIA\b(?![-A-Za-z0-9]) → "GarBotGPT".
    • 124 reemplazos en 52 archivos.
    • src/lib/changelog.ts excluido (historia preservada).
    • Identificadores de modelo (GIA-5.2 etc.) intactos por look-ahead.
  25. v8.23.0

    🎆 Petardos del Barça + modal infinito + banner abajo

    Sistema de cohetes/petardos que suben desde abajo y explotan en lluvia de chispas. El modal de celebración ya NO se cierra solo — espera a que el usuario clique fuera de la tarjeta. Banner residual movido a la esquina inferior derecha. Burst de confeti programado cada 4.5s mientras dure el modal.

    Nuevo

    • 🚀 Cohetes/petardos que salen disparados desde el suelo hacia arriba
    • 💥 Cada cohete explota en 36-52 chispas radiales heredando su color
    • Trail de 12 puntos con fade-out por cada cohete
    • 🌧️ Lanzamiento cada 280-420ms (con 45% de doble lanzamiento)
    • 🚫 Modal SIN auto-cierre — usuario controla cuándo cerrar
    • 🎉 Burst extra programado cada 4.5s para celebración infinita
    • 👇 Banner residual movido de top-right a bottom-right

    Mejorado

    • 🎨 Cabeza brillante del cohete con shadow blur 14px

    Fireworks engine

    • Nuevo tipo Rocket con x/y/vx/vy/color/trail/exploded.
    • createRocket() spawns a posición x random 10%-90%, vy -11 a -16 (sube fuerte).
    • Gravedad 0.18 px/frame² aplicada → decelera la subida hasta vy >= -0.5.
    • Al peak (vy >= -0.5 o y < 18% altura) → explosión: 36-52 partículas radiales con createParticle(originX, originY, intensity 1.3).
    • Color de explosión: 70% del color del cohete, 30% de la paleta general.
    • Trail: array de últimas 12 posiciones con alpha decay 0.85 por frame.
    • Cabeza del cohete: círculo blanco interior + círculo de color exterior + shadowBlur 14 px.

    UX cambios

    • closeModal() ya NO se llama por timer. Solo al click en overlay.
    • Banner residual bajado a bottom: 20px, right: 20px (móvil 12/12).
    • Slide animation invertida: ahora entra desde abajo.
    • Tip del modal actualizado: "Toca fuera de la tarjeta para cerrar".
    • Burst extra cada 4.5s mantiene la fiesta viva.
  26. v8.22.0

    🏆 Celebració Barça · EPIC EDITION (escudo real + audio + cinema)

    Rediseño total de la celebración: escudo oficial FCB descargado de Wikipedia, intento de autoplay de "Viva la Vida" con fallback a botón dorado, 4 fases cinemáticas con flash blanco inicial, spotlights conicos rotando, senyera ondulando en BG, 30 estrellas titilando, halo dorado pulsante alrededor del escudo, título letra-por-letra con rotación 3D, count-up de 4 stats reales (28 Ligas · 5 Champions · 32 Copas · 14 Supercopas), confeti masivo con 4 formas (rect/círculo/estrella/triángulo) + multi-burst, glow rays + shimmer animation.

    Nuevo

    • Escudo OFICIAL FC Barcelona (descargado de Wikipedia Commons)
    • 🎵 Audio "Viva la Vida" con autoplay + fallback button dorado si bloqueado
    • 🎬 4 fases: flash blanco → spotlights → escudo emergiendo → título + stats
    • 🌟 Halo dorado pulsante alrededor del escudo (radial gradient + blur)
    • 🔤 Título "ÉS CAMPIONS!" letra-por-letra con rotación 3D y blur entrance
    • 📊 4 stats reales con count-up animado (28/5/32/14)
    • 🎭 Spotlights conicos rotando (4 capas, sentidos opuestos, mix-blend screen)
    • 🟡 🔴 Senyera (bandera catalana) ondulando en BG con skewY -12°
    • 30 estrellas titilando + shimmer en partículas confeti
    • 🎉 Confeti masivo con 4 formas (rect/círculo/estrella/triángulo)
    • 💥 2 bursts de confeti programados (600ms y 2800ms)
    • ⏱️ Auto-cierre a 9.5s + banner residual pulsante

    Recursos

    • public/celebrations/barca-crest.svg (14KB, oficial Wikipedia)
    • public/celebrations/viva-la-vida.mp3 (placeholder — el usuario debe subir el MP3)

    Componente

    • src/components/celebration/BarcaChampions.tsx (~700 líneas).
    • Phases: hidden → opening (flash) → modal → banner.
    • Audio loop, volume 0.7. Detecta bloqueo de autoplay y muestra botón "Activa la música".
    • Burst confetti triggers via state increment (Reactive useEffect).
    • StatCounter con useCountUp hook (easeOutCubic, 1400ms).
    • 12 @keyframes animations distintos.
  27. v8.21.0

    🏆 Celebració FC Barcelona campió de Liga (temporal)

    Componente temporal que celebra el título de Liga del FC Barcelona. Al cargar la app aparece un modal con confeti, escudo FCB animado y el lema "ÉS CAMPIONS!". Después queda un banner pequeño top-right que persiste durante la sesión. Auto-desactiva el 15 de junio de 2026.

    Nuevo

    • 🎉 Modal full-screen con backdrop blaugrana + confeti continuo (canvas)
    • Escudo FCB dibujado en SVG desde cero (cruz Sant Jordi + senyera + bandas blaugrana + balón)
    • Título "ÉS CAMPIONS!" con gradient blanco→dorado y shine animation
    • 🏵️ Tres estrellas doradas parpadeantes + badge "🏆 Campeones de La Liga 2025-26"
    • 📌 Banner persistente top-right con pulse cada 3s, cierre 24h via localStorage
    • Auto-expiración: desaparece después del 2026-06-15

    Componente

    • `src/components/celebration/BarcaChampions.tsx` (~580 líneas).
    • Canvas-based confetti: 600 partículas máx, paleta blaugrana + dorado (#004D98, #A50044, #EDBB00, blanco), burst inicial desde 5 puntos.
    • Modal con animaciones: crest-spin-in (540°), title-in (scale + blur), star-twinkle, shine cruzando la card cada 3.5s.
    • Banner pill con pulse y borde dorado.
    • Configurable via STARTS_AT / EXPIRES_AT constants.
    • Persistencia: localStorage 'barca-champions-celebration-v1' (seenModal + dismissed timestamp).

    Montaje

    • Importado y renderizado en `src/app/layout.tsx` dentro de ActivityProvider.
    • Se carga en TODAS las páginas de la app.
    • Para apagar: cambiar `EXPIRES_AT` a una fecha pasada en BarcaChampions.tsx.
  28. v8.20.0

    Refactor v8.20 — Helpers de research/web/YouTube extraídos (-829 líneas)

    El módulo más grande del Bloque A: 40+ helpers de search, ranking, deep research, image collection, YouTube, concurrency y rerank parsing se mueven a `src/lib/server/chat/helpers/research-helpers.ts` (~960 líneas). DI eliminada en 2 tools (web-search y youtube).

    Nuevo

    • 🔍 40+ helpers extraídos a research-helpers.ts (~960 líneas)
    • 📊 Source scoring: authority, freshness, coverage, dedupe, rankWebSources
    • 🧠 Deep-research intent parsing: detectIntentHints, sanitizePlan, buildFallbacks
    • 🖼️ Image collection cross-source con scoring lexical + URL + size
    • 🎬 YouTube: metadata, transcript (XML + JSON3), captionTracks parsing
    • 🧪 parseRerankScores con fallback a regex si JSON está roto
    • DI eliminada en web-search.ts y youtube.ts

    Mejorado

    • runWithConcurrency<T,R> shared utility

    Nuevo módulo

    • `src/lib/server/chat/helpers/research-helpers.ts` con 6 tipos públicos + 40 funciones.
    • Tipos: ResearchMode, RankedWebSource, DeepResearchPlan, DeepResearchCacheEntry, DeepResearchImage, DeepResearchImageCandidate.
    • URL/text utils: canonicalizeUrl, tokenize, jaccardSimilarity, uniqueStrings.
    • Source scoring: scoreSourceAuthority/Freshness/Coverage, removeDuplicateSources, rankWebSources.
    • Deep-research: buildFocusedResearchQuery, detectDeepResearchIntentHints, sanitizeDeepResearchPlan, buildGenericDeepResearchFallbackQueries.
    • HTML/Images: extractImageCandidatesFromHtml, fetchHtmlWithTimeout, collectDeepResearchImages.
    • YouTube: extractYouTubeVideoIds, fetchYouTubeMetadata, fetchYouTubeTranscript, decodeHtmlEntities.
    • parseRerankScores: parser robusto para output JSON del LLM rerank.

    Tools afectados

    • tools/web-search.ts: rankWebSources + collectDeepResearchImages inyectados → imports directos.
    • tools/youtube.ts: 4 helpers DI eliminados. YoutubeToolContext queda en 1 campo (antes 5).
    • route.ts: 9829 → 9001 líneas (-828).
  29. v8.19.0

    Refactor v8.19 — Helpers de Gmail/Outlook extraídos (-652 líneas)

    Helpers compartidos por Gmail tool y Microsoft tool salen al módulo `gmail-helpers.ts` (~600 líneas con 14 públicos + 11 privados). DI eliminada en 2 tools.

    Nuevo

    • 📧 30+ helpers de email a gmail-helpers.ts
    • 🔧 Regex EMAIL_ADDRESS_RE y GMAIL_SEND_CONFIRMATION_RE como exports
    • ✍️ Parseo intent + edición + AI draft generation centralizados
    • 🌐 fetchGmailMessages + sendGmailMessage (con buildGmailRawMessage MIME)
    • 🗂️ buildMicrosoftDriveQuery + buildMicrosoftDriveBlock (OneDrive)
    • DI eliminada en gmail.ts (17 → 0 DI) y microsoft.ts (14 → 0 DI)

    Mejorado

    • 🎨 HTML templates: wantsHtmlEmail + buildEmailHtmlTemplate

    Nuevo módulo

    • `src/lib/server/chat/helpers/gmail-helpers.ts` (~600 líneas).
    • Públicos: parseGmailSendIntent, parseGmailSendIntentFromHistory, mergeGmailDraft, applyGmailDraftEditsFromPrompt, generateGmailDraftIfNeeded, computeGmailMissingFields, isConfirmationOnlyMessage, wantsHtmlEmail, buildEmailHtmlTemplate, extractGmailSenderName, buildGmailSearchQuery, buildGmailMessagesGroupedBlock, fetchGmailMessages, sendGmailMessage, buildMicrosoftDriveQuery, buildMicrosoftDriveBlock.
    • Internos: decodeMailtoParam, extractLabeledLineValue, cleanParsedGmailBody, parseGmailDraftFromAssistantText, escapeHtml, normalizeInstructionText, isInstructionalEmailSubject/Body, parseGeneratedDraftJson, stripHtmlToText, buildGmailRawMessage, formatGmailDayHeader, formatGmailHour.

    Tools afectados

    • tools/gmail.ts: GmailToolContext 17 → 6 campos.
    • tools/microsoft.ts: MicrosoftToolContext 14 → 7 campos.
    • route.ts: 10481 → 9829 líneas (-652).
  30. v8.18.0

    Refactor v8.18 — Helpers de Google AdSense extraídos (-343 líneas)

    15+ helpers de parseo y formateo de Google AdSense salen al módulo `adsense-helpers.ts`. DI eliminada en AdSense tool.

    Nuevo

    • 📈 4 tipos públicos + 14 funciones de scoring/formateo a helpers
    • 🔧 parseAdSenseDateRange, parseAdSenseDimensions, parseAdSenseMetrics, wantsAdSenseTodayVsYesterdayComparison
    • DI eliminada en google-adsense.ts (13 → 0 DI)

    Mejorado

    • 🎨 buildAdSenseMetricPayloads, buildAdSenseComparisonPayloads, buildAdSenseRowsTable, buildAdSenseAccountsBlock

    Nuevo módulo

    • `src/lib/server/chat/helpers/adsense-helpers.ts` (~360 líneas).
    • Tipos: AdSenseDateRangeSelection, AdSenseMetricKind, AdSenseMetricPayload, AdSenseComparisonMetricPayload.
    • Internos: adsenseDateParts, ADSENSE_METRIC_LABELS, formatAdSenseHeaderLabel, getAdSenseCells/MetricKind/NumericValue/MetricHeaders/MetricEntries/MetricEntryByName, pickAdSenseMetricRow, formatAdSenseMetricValue/DeltaValue.

    Tools afectados

    • tools/google-adsense.ts: AdSenseToolContext 13 → 3 campos.
    • route.ts: 10824 → 10481 líneas (-343).

1118 versiones publicadas · página 34 de 38. Míralo dentro de la app.