Files
carinha-control-panel/web/src/lib/componentes/Gestos.svelte
T
julioandClaude Opus 5.5 c4b53a8976 feat(web): cria painel Svelte com shell, Ao vivo e Ajustes
- web/ com Vite + Svelte 5 + TypeScript; servidor entrega web/dist em "/"
  (telas antigas seguem em /dashboard, /painel, /braco...)
- design system em tokens CSS, tema escuro e claro, fontes locais
- estado unico: foto de /estado + um EventSource em /eventos, sem polling
- shell com navegacao lateral/inferior, selos de status reais e E-Stop fixo
- command palette (Ctrl+K), gestos favoritos com atalhos 1-9, g+letra navega
- Ao vivo: camera WebRTC e rastreamento portados para TS, rosto, aparencia,
  sensores do celular, IA local e feed de acontecimentos
- Ajustes: campos do celular agrupados com busca, salvamento otimista e
  restaurar padrao (GET /app/padrao); motor da conversa, Gemini e token
- carinha.html ganha modo ?painel=1 dirigido por postMessage (sem polling)
- teste Playwright de ponta a ponta do painel novo

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 19:47:18 -03:00

102 lines
2.6 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
import { Star } from '@lucide/svelte';
import { favoritos, gesto, GESTOS_INFO, gestosDisponiveis } from '../comandos.svelte';
// compacto: só os favoritos com atalho 1–9 (dock). Completo: todos, com estrela para fixar.
let { completo = false }: { completo?: boolean } = $props();
const lista = $derived(completo ? gestosDisponiveis() : favoritos.lista.filter((g) => g in GESTOS_INFO));
</script>
<div class="gestos" class:completo>
{#each lista as g, i (g)}
{@const info = GESTOS_INFO[g]}
<div class="item">
<button class="gesto" onclick={() => gesto(g)} title={completo ? info.nome : `${info.nome} (tecla ${i + 1})`}>
<span class="emoji" aria-hidden="true">{info.emoji}</span>
<span class="nome">{info.nome}</span>
{#if !completo}<kbd>{i + 1}</kbd>{/if}
</button>
{#if completo}
<button
class="estrela"
class:ativa={favoritos.lista.includes(g)}
aria-label={favoritos.lista.includes(g) ? `Tirar ${info.nome} dos favoritos` : `Fixar ${info.nome} nos favoritos`}
aria-pressed={favoritos.lista.includes(g)}
onclick={() => favoritos.alternar(g)}
>
<Star size={14} />
</button>
{/if}
</div>
{/each}
</div>
<style>
.gestos {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
gap: var(--e-2);
}
.item {
position: relative;
}
.gesto {
width: 100%;
display: grid;
justify-items: center;
gap: 2px;
padding: var(--e-3) var(--e-1) var(--e-2);
border: 1px solid var(--borda);
border-radius: var(--raio);
background: var(--superficie-2);
color: var(--texto);
font: inherit;
cursor: pointer;
position: relative;
transition: background var(--transicao), border-color var(--transicao), transform var(--transicao);
}
.gesto:hover {
border-color: var(--destaque);
background: var(--destaque-suave);
}
.gesto:active {
transform: scale(0.97);
}
.emoji {
font-size: 1.6rem;
line-height: 1.2;
}
.nome {
font-size: var(--t-xs);
color: var(--texto-2);
text-align: center;
}
kbd {
position: absolute;
top: 4px;
right: 6px;
font: 500 10px var(--fonte-mono);
color: var(--texto-3);
}
.estrela {
position: absolute;
top: 2px;
right: 2px;
width: 26px;
height: 26px;
display: grid;
place-items: center;
border: 0;
border-radius: 50%;
background: transparent;
color: var(--texto-3);
cursor: pointer;
}
.estrela.ativa {
color: var(--aviso);
}
.estrela.ativa :global(svg) {
fill: currentColor;
}
</style>