feat: smart scroll — keep position when reading, badge for new messages

- Add pollMensajes() so wire:poll no longer force-scrolls to bottom
- MutationObserver detects new DOM nodes: auto-scrolls only if user
  is already at the bottom (within 80px threshold)
- Floating '↓ N mensaje nuevo' badge appears when new bot messages
  arrive while user is scrolled up; tap badge to jump to bottom
- scroll-chat event (emitted on button clicks/sends) still forces
  scroll since the user just took an action

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro
2026-07-14 13:36:31 +00:00
co-authored by Claude Sonnet 4.6
parent a5bac9cee7
commit 26953c9dd5
3 changed files with 132 additions and 24 deletions
@@ -154,12 +154,8 @@
{{-- ══ PASO 3: Interfaz de chat ══ --}}
@else
<div class="flex-1 flex flex-col min-h-0 bg-[#0b141a]"
x-data="{ uploading: false }"
x-on:scroll-chat.window="$nextTick(() => {
const el = document.getElementById('pub-messages');
if (el) el.scrollTop = el.scrollHeight;
})">
<div class="flex-1 flex flex-col min-h-0 bg-[#0b141a] relative"
x-data="{ uploading: false }">
{{-- Header --}}
<div class="bg-[#202c33] shadow-md flex-shrink-0 safe-top safe-left safe-right">
@@ -196,10 +192,22 @@
</div>
</div>
{{-- Badge: nuevos mensajes mientras se lee el historial --}}
<div id="new-msg-badge" class="hidden absolute left-0 right-0 flex justify-center z-20 pointer-events-none"
style="bottom:80px;">
<button id="new-msg-badge-btn"
class="pointer-events-auto flex items-center gap-2 bg-[#00a884] text-white text-xs font-semibold px-4 py-2 rounded-full shadow-xl active:bg-[#06cf9c] transition">
<svg class="w-3.5 h-3.5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 13.5 12 21m0 0-7.5-7.5M12 21V3" />
</svg>
<span id="new-msg-count">1</span>&nbsp;mensaje nuevo
</button>
</div>
{{-- Mensajes --}}
<div class="flex-1 min-h-0 overflow-y-auto px-3 py-4 space-y-3 ios-scroll"
id="pub-messages"
wire:poll.3000ms="cargarMensajes"
wire:poll.3000ms="pollMensajes"
style="overscroll-behavior-y:contain;-webkit-overflow-scrolling:touch;">
@forelse($mensajes as $msg)