- 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>
108 lines
5.2 KiB
TypeScript
108 lines
5.2 KiB
TypeScript
/** Ajustes do app do celular (/app/config): salvamento otimista e campos agrupados. */
|
|
import { pedir, postar } from './api';
|
|
import { avisos } from './avisos.svelte';
|
|
import { estado } from './estado.svelte';
|
|
import type { AppConfig } from './tipos';
|
|
|
|
/** Atualiza na hora; se o servidor recusar, volta ao valor anterior e avisa. Não trava o formulário. */
|
|
export async function salvarApp(parcial: AppConfig) {
|
|
const antes = estado.appConfig ? { ...estado.appConfig } : null;
|
|
if (estado.appConfig) estado.appConfig = { ...estado.appConfig, ...parcial };
|
|
try {
|
|
const r = await postar<{ config: AppConfig }>('/app/config', parcial);
|
|
estado.appConfig = r.config;
|
|
} catch (e) {
|
|
estado.appConfig = antes;
|
|
avisos.erro(e);
|
|
}
|
|
}
|
|
|
|
export async function restaurarPadrao() {
|
|
const padrao = await pedir<AppConfig>('/app/padrao');
|
|
// A transmissão não é "ajuste": continua como está.
|
|
delete padrao.preview;
|
|
await salvarApp(padrao);
|
|
avisos.ok('Ajustes do celular restaurados para o padrão.');
|
|
}
|
|
|
|
type Opcoes = [string | number, string][];
|
|
export interface Campo {
|
|
chave: string;
|
|
rotulo: string;
|
|
ajuda?: string;
|
|
tipo: 'toggle' | 'slider' | 'escolha';
|
|
min?: number;
|
|
max?: number;
|
|
passo?: number;
|
|
unidade?: string;
|
|
opcoes?: Opcoes;
|
|
}
|
|
|
|
export const GRUPOS: { titulo: string; descricao: string; campos: Campo[] }[] = [
|
|
{
|
|
titulo: 'Tela e personalidade',
|
|
descricao: 'Como o rosto aparece e se comporta sozinho.',
|
|
campos: [
|
|
{ chave: 'auto', rotulo: 'Gestos espontâneos', ajuda: 'Pisca e olha em volta sem comando', tipo: 'toggle' },
|
|
{ chave: 'boca', rotulo: 'Mostrar boca', tipo: 'toggle' },
|
|
{ chave: 'sobrancelhas', rotulo: 'Mostrar sobrancelhas', tipo: 'toggle' },
|
|
{ chave: 'audio', rotulo: 'Sons no celular', ajuda: 'Bipes do próprio robô', tipo: 'toggle' },
|
|
{ chave: 'volume', rotulo: 'Volume dos sons', tipo: 'slider', min: 0, max: 1, passo: 0.05 },
|
|
{ chave: 'brilho', rotulo: 'Brilho normal', tipo: 'slider', min: 0.01, max: 1, passo: 0.01 },
|
|
{ chave: 'economia', rotulo: 'Reduzir brilho em repouso', tipo: 'toggle' },
|
|
{ chave: 'brilhoRepouso', rotulo: 'Brilho em repouso', tipo: 'slider', min: 0.01, max: 1, passo: 0.01 },
|
|
{ chave: 'repousoTela', rotulo: 'Espera para reduzir brilho', tipo: 'slider', min: 5, max: 300, passo: 5, unidade: ' s' },
|
|
],
|
|
},
|
|
{
|
|
titulo: 'Sensores e reações',
|
|
descricao: 'O que o celular percebe e como reage.',
|
|
campos: [
|
|
{ chave: 'sensorMic', rotulo: 'Microfone', ajuda: 'Só o nível de som é usado, nunca o áudio', tipo: 'toggle' },
|
|
{ chave: 'ouvir', rotulo: 'Reagir a sons', tipo: 'toggle' },
|
|
{ chave: 'micSono', rotulo: 'Microfone durante o sono', tipo: 'toggle' },
|
|
{ chave: 'sompausa', rotulo: 'Silêncio para dormir', tipo: 'slider', min: 1, max: 120, passo: 1, unidade: ' s' },
|
|
{ chave: 'sensorCamera', rotulo: 'Câmera como sensor', ajuda: 'Detecta movimento e permite o vídeo', tipo: 'toggle' },
|
|
{ chave: 'reagir', rotulo: 'Reagir a movimento', tipo: 'toggle' },
|
|
{ chave: 'cameraSono', rotulo: 'Câmera durante o sono', tipo: 'toggle' },
|
|
{ chave: 'movolhar', rotulo: 'Quanto os olhos acompanham', tipo: 'slider', min: 0, max: 100, passo: 1, unidade: '%' },
|
|
],
|
|
},
|
|
{
|
|
titulo: 'Vídeo e rastreamento',
|
|
descricao: 'Qualidade da transmissão e rastreamento feito neste PC.',
|
|
campos: [
|
|
{ chave: 'cameraLado', rotulo: 'Câmera', tipo: 'escolha', opcoes: [['user', 'Frontal'], ['environment', 'Traseira']] },
|
|
{
|
|
chave: 'videoResolucao',
|
|
rotulo: 'Resolução',
|
|
tipo: 'escolha',
|
|
opcoes: [['auto', 'Automática'], ['max', 'Máxima'], ['480', '480p'], ['720', '720p'], ['1080', '1080p'], ['2160', '4K'], ['4320', '8K']],
|
|
},
|
|
{
|
|
chave: 'videoFPS',
|
|
rotulo: 'Quadros por segundo',
|
|
tipo: 'escolha',
|
|
opcoes: [[0, 'Automático'], [-1, 'Máximo'], [15, '15'], [24, '24'], [30, '30'], [60, '60'], [120, '120'], [240, '240']],
|
|
},
|
|
{ chave: 'rastrearRosto', rotulo: 'Rastrear rosto', tipo: 'toggle' },
|
|
{ chave: 'rastrearMaos', rotulo: 'Rastrear mãos', tipo: 'toggle' },
|
|
{ chave: 'seguirRastreamento', rotulo: 'Olhos seguem o alvo', tipo: 'toggle' },
|
|
{ chave: 'mostrarRastreamento', rotulo: 'Desenhar rastreamento no vídeo', tipo: 'toggle' },
|
|
],
|
|
},
|
|
{
|
|
titulo: 'Sensibilidade (avançado)',
|
|
descricao: 'Limiares dos sustos e intervalos entre reações.',
|
|
campos: [
|
|
{ chave: 'somlim', rotulo: 'Nível para perceber som', tipo: 'slider', min: 0, max: 1, passo: 0.01 },
|
|
{ chave: 'somaltolim', rotulo: 'Som mínimo para susto', tipo: 'slider', min: 0, max: 1, passo: 0.01 },
|
|
{ chave: 'somganho', rotulo: 'Aumento súbito para susto', tipo: 'slider', min: 0, max: 1, passo: 0.01 },
|
|
{ chave: 'somcooldown', rotulo: 'Intervalo entre sustos por som', tipo: 'slider', min: 0, max: 120, passo: 1, unidade: ' s' },
|
|
{ chave: 'movlim', rotulo: 'Movimento mínimo', tipo: 'slider', min: 0, max: 1, passo: 0.01 },
|
|
{ chave: 'movforte', rotulo: 'Movimento para susto', tipo: 'slider', min: 0, max: 1, passo: 0.01 },
|
|
{ chave: 'movcooldown', rotulo: 'Intervalo entre sustos por movimento', tipo: 'slider', min: 0, max: 120, passo: 1, unidade: ' s' },
|
|
],
|
|
},
|
|
];
|