/** 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('/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; } /** `ajuda` = tópico do guia (ⓘ ao lado do título). */ export const GRUPOS: { titulo: string; descricao: string; ajuda: string; campos: Campo[] }[] = [ { titulo: 'Tela e personalidade', ajuda: 'tela', 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: 'iris', rotulo: 'Mostrar íris', ajuda: 'Pupila que acompanha o olhar (Viva, Bloco e Matriz)', tipo: 'toggle' }, { chave: 'subirBoca', rotulo: 'Subir a boca', tipo: 'slider', min: -20, max: 60, passo: 2 }, { chave: 'distSobrancelha', rotulo: 'Distância das sobrancelhas', tipo: 'slider', min: 0, max: 60, passo: 2 }, { 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' }, { chave: 'apagarTela', rotulo: 'Apagar a tela quando dormir', ajuda: 'Tela preta e brilho mínimo; acorda com som repetido, movimento, toque ou PIR', tipo: 'toggle' }, { chave: 'apagarTelaMin', rotulo: 'Dormindo há quanto tempo para apagar', tipo: 'slider', min: 1, max: 120, passo: 1, unidade: ' min' }, ], }, { titulo: 'Sensores e reações', ajuda: 'sensores', 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: 'Sono e madrugada', ajuda: 'sono', descricao: 'O que acorda o rosto enquanto ele dorme.', campos: [ { chave: 'somAcordar', rotulo: 'Sons para acordar', ajuda: 'Quantos barulhos em 1 minuto para ele acordar', tipo: 'slider', min: 1, max: 10, passo: 1 }, { chave: 'noiteSilenciosa', rotulo: 'Madrugada silenciosa', ajuda: 'Movimento, câmera e PIR não acordam; só barulho alto repetido, toque ou conversa', tipo: 'toggle', }, { chave: 'noiteInicio', rotulo: 'Começa às', tipo: 'slider', min: 0, max: 23, passo: 1, unidade: 'h' }, { chave: 'noiteFim', rotulo: 'Termina às', tipo: 'slider', min: 0, max: 23, passo: 1, unidade: 'h' }, ], }, { titulo: 'Vídeo e rastreamento', ajuda: 'camera', 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)', ajuda: 'sensibilidade', 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' }, ], }, ];