feat(web): migra Conversa e Reacoes para o painel novo

- Conversa: chat empurrado por SSE, indicador de "pensando", gesto da
  resposta com repetir, voz com aviso claro quando o contexto nao e seguro
  e leitura em voz alta so de respostas novas
- Memoria unificada: lembrancas da conversa + rotina/preferencias em abas;
  apagar tudo com confirmacao inline (sem confirm nativo)
- Reacoes: biblioteca com miniaturas estaticas (sem iframe por cartao),
  busca, mostrar no celular, feedback, editar em <dialog>, duplicar,
  ativar e excluir com Desfazer no toast; criar com IA; diario; grade de
  gestos com estrela para favoritos; uma unica previa lateral
- treinamento recarrega por evento SSE ao terminar, sem polling
- corrige icones encolhidos em botoes .pequeno.icone

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
julio
2026-10-05 19:51:45 -03:00
co-authored by Claude Opus 5.5
parent c4b53a8976
commit 947c0a3127
9 changed files with 1137 additions and 9 deletions
+56
View File
@@ -0,0 +1,56 @@
<script lang="ts">
import { X } from '@lucide/svelte';
import type { Snippet } from 'svelte';
// <dialog> nativo: foco preso, Esc e fundo vêm de graça.
let { aberto = $bindable(false), titulo, children }: { aberto: boolean; titulo: string; children: Snippet } = $props();
let dialogo: HTMLDialogElement;
$effect(() => {
if (aberto && !dialogo.open) dialogo.showModal();
else if (!aberto && dialogo.open) dialogo.close();
});
</script>
<dialog
bind:this={dialogo}
onclose={() => (aberto = false)}
onclick={(e) => e.target === dialogo && (aberto = false)}
aria-label={titulo}
>
<div class="conteudo">
<header>
<h2>{titulo}</h2>
<button class="btn fantasma icone pequeno" aria-label="Fechar" onclick={() => (aberto = false)}><X size={18} /></button>
</header>
{#if aberto}{@render children()}{/if}
</div>
</dialog>
<style>
dialog {
width: min(480px, calc(100vw - 2 * var(--e-4)));
padding: 0;
border: 1px solid var(--borda-forte);
border-radius: var(--raio-lg);
background: var(--superficie);
color: var(--texto);
box-shadow: var(--sombra-flutuante);
}
dialog::backdrop {
background: rgb(0 0 0 / 0.5);
}
.conteudo {
display: grid;
gap: var(--e-4);
padding: var(--e-5);
}
header {
display: flex;
justify-content: space-between;
align-items: center;
}
h2 {
font-size: var(--t-lg);
}
</style>