Files
carinha-control-panel/web/src/lib/paleta.svelte.ts
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

49 lines
1.8 KiB
TypeScript

/** Registro da command palette (Ctrl+K): cada parte do painel contribui com itens. */
import { cor, CORES, gesto, GESTOS_INFO, gestosDisponiveis, pararTudo, rearmar, recarregarCelular, rosto } from './comandos.svelte';
import { estado } from './estado.svelte';
import { nav, ROTAS } from './rotas.svelte';
export interface ItemPaleta {
id: string;
grupo: string;
titulo: string;
dica?: string;
fazer: () => unknown;
}
type Fonte = () => ItemPaleta[];
const fontes = new Set<Fonte>();
export function registrarNaPaleta(fonte: Fonte): () => void {
fontes.add(fonte);
return () => fontes.delete(fonte);
}
class Paleta {
aberta = $state(false);
abrir = () => (this.aberta = true);
fechar = () => (this.aberta = false);
}
export const paleta = new Paleta();
export function itensDaPaleta(): ItemPaleta[] {
return [...fontes].flatMap((f) => f());
}
// Itens base: navegação, ações gerais, gestos, rostos e cores.
registrarNaPaleta(() => [
...ROTAS.map((r) => ({ id: 'ir-' + r.id, grupo: 'Ir para', titulo: r.titulo, fazer: () => nav.ir(r.id) })),
estado.estop.ativo
? { id: 'rearmar', grupo: 'Ações', titulo: 'Rearmar motores', fazer: rearmar }
: { id: 'estop', grupo: 'Ações', titulo: 'Parar tudo (E-Stop)', dica: 'Shift+Espaço', fazer: pararTudo },
{ id: 'recarregar', grupo: 'Ações', titulo: 'Recarregar tela do celular', fazer: recarregarCelular },
...gestosDisponiveis().map((g) => ({
id: 'gesto-' + g,
grupo: 'Gestos',
titulo: `${GESTOS_INFO[g].emoji} ${GESTOS_INFO[g].nome}`,
fazer: () => gesto(g),
})),
...estado.rostos.map((r) => ({ id: 'rosto-' + r, grupo: 'Rosto', titulo: 'Rosto ' + r, fazer: () => rosto(r) })),
...CORES.map((c) => ({ id: 'cor-' + c.nome, grupo: 'Cor dos olhos', titulo: c.nome, fazer: () => cor(c.rgb) })),
]);