- 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>
57 lines
1.4 KiB
Svelte
57 lines
1.4 KiB
Svelte
<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>
|