From 9e09df40e213f80775353aa2e46db3064284b373 Mon Sep 17 00:00:00 2001 From: julio Date: Tue, 6 Oct 2026 17:39:14 -0300 Subject: [PATCH] =?UTF-8?q?feat(vida):=20ritmo=20tamb=C3=A9m=20define=20a?= =?UTF-8?q?=20sensibilidade=20dos=20sensores?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O servidor manda "ritmo calmo|normal|agitado" ao celular junto com o humor, e o rosto ajusta limiares e quantos estímulos precisa: - calmo: limiares 50% mais altos, 3 sustos seguidos para reagir, o dobro de sons e 4 movimentos para acordar, 3 disparos do PIR em 2 min; - normal: reage a barulho alto e movimento forte, 2 movimentos ou os sons configurados para acordar; - agitado: limiares 40% mais baixos, reage a qualquer som ou movimento (com reações variadas) e acorda no primeiro estímulo. A madrugada silenciosa continua valendo por cima. Com o modo vida desligado o celular volta ao "normal" (só os Ajustes). Co-Authored-By: Claude Opus 5.5 --- android/app/assets/carinha.html | 73 ++++++++++++++++------- carinha.html | 73 ++++++++++++++++------- kiosk-server.py | 2 +- tests/test_vida.py | 16 +++++ vida.py | 20 ++++++- web/src/lib/componentes/VidaCartao.svelte | 9 +-- 6 files changed, 141 insertions(+), 52 deletions(-) diff --git a/android/app/assets/carinha.html b/android/app/assets/carinha.html index 46b7c39..9b3c07e 100644 --- a/android/app/assets/carinha.html +++ b/android/app/assets/carinha.html @@ -592,6 +592,8 @@ function trata(ordem) { } } else if (ordem.indexOf("humor ") === 0) { HUMOR = ordem.slice(6).trim(); + } else if (ordem.indexOf("ritmo ") === 0) { + PERFIL = PERFIS[ordem.slice(6).trim()] || PERFIS.normal; } else if (ordem === "dormir" || ordem === "sono") { r.dormindo = true; } else if (ordem === "acordar") { @@ -743,24 +745,45 @@ const MIC = { /* Acordar com som: precisa de N barulhos em 1 minuto (ajuste "Sons para acordar"). Na madrugada silenciosa só barulho ALTO conta, e movimento/câmera não acordam (alguém se mexendo na cama). */ -const DESPERTAR = { sons: 3, janela: 60, noite: true, ini: 23, fim: 7, eventos: [], ultimo: -Infinity, estava: false }; +const DESPERTAR = { sons: 3, noite: true, ini: 23, fim: 7 }; function madrugada() { if (!DESPERTAR.noite || DESPERTAR.ini === DESPERTAR.fim) return false; const h = new Date().getHours(); return DESPERTAR.ini < DESPERTAR.fim ? h >= DESPERTAR.ini && h < DESPERTAR.fim : h >= DESPERTAR.ini || h < DESPERTAR.fim; } -/* Conta o INÍCIO de cada barulho (o mesmo barulho comprido vale um) e diz se já dá para acordar. */ -function barulhoAcorda(ouviu, agora) { - const D = DESPERTAR; - if (ouviu && !D.estava && agora - D.ultimo >= 1.5) { - D.ultimo = agora; - D.eventos = D.eventos.filter((t) => agora - t < D.janela).concat(agora); - console.log("ouvido: barulho " + D.eventos.length + "/" + D.sons + (madrugada() ? " (madrugada)" : "")); +/* Perfil de sensibilidade = ritmo do modo vida (o servidor manda "ritmo calmo|normal|agitado"). + Calmo precisa de vários estímulos para reagir e limiares mais altos; agitado reage a qualquer + som ou movimento. Multiplica os limiares dos Ajustes, sem mudar os Ajustes. */ +const PERFIS = { + calmo: { lim: 1.5, reagir: 3, acordarSom: 2, acordarMov: 4, espera: 2, qualquer: false }, + normal: { lim: 1.0, reagir: 1, acordarSom: 1, acordarMov: 2, espera: 1, qualquer: false }, + agitado: { lim: 0.6, reagir: 1, acordarSom: 0.34, acordarMov: 1, espera: 0.5, qualquer: true }, +}; +let PERFIL = PERFIS.normal; +const limiar = (x) => Math.min(0.95, x * PERFIL.lim); +/* Conta o INÍCIO de cada estímulo (o mesmo som/movimento comprido vale um) numa janela de tempo + e diz quando chegou a `precisa`. */ +function novoContador(janela) { return { janela, eventos: [], ultimo: -Infinity, estava: false, visto: -Infinity }; } +const CONTA = { somAcordar: novoContador(60), movAcordar: novoContador(30), somReagir: novoContador(20), movReagir: novoContador(20) }; +function contar(c, ativo, agora, precisa, nome) { + // contador parado um tempo (ex.: o de "acordar" enquanto estava acordado): começa do zero + if (agora - c.visto > 2) c.estava = false; + c.visto = agora; + if (ativo && !c.estava && agora - c.ultimo >= 1.0) { + c.ultimo = agora; + c.eventos = c.eventos.filter((t) => agora - t < c.janela).concat(agora); + if (precisa > 1) console.log(nome + " " + c.eventos.length + "/" + precisa + (madrugada() ? " (madrugada)" : "")); } - D.estava = ouviu; - if (D.eventos.length >= D.sons && agora - D.eventos[D.eventos.length - 1] < 1) { D.eventos = []; return true; } + c.estava = ativo; + if (c.eventos.length >= precisa && agora - c.eventos[c.eventos.length - 1] < 1) { c.eventos = []; return true; } return false; } +/* Reação acordado: agitado varia; os outros se assustam. */ +function reacaoEstimulo() { + if (!PERFIL.qualquer) return "surpreso"; + const l = ["surpreso", "feliz", "alegre", "piscadinha", "surpreso"]; + return l[Math.floor(Math.random() * l.length)]; +} function som(nivel) { const n = Math.max(0, Math.min(1, Number(nivel) || 0)); @@ -776,8 +799,8 @@ function som(nivel) { if (MIC.piso === null) MIC.piso = n; else MIC.piso += (n - MIC.piso) * (n < MIC.piso ? 0.05 : 0.0015); - const temSom = n >= MIC.limiarSom; // ruido audivel (absoluto) - const alto = n >= MIC.limiarAlto && (n - MIC.piso) >= MIC.ganho; + const temSom = n >= limiar(MIC.limiarSom); // ruido audivel (absoluto) + const alto = n >= limiar(MIC.limiarAlto) && (n - MIC.piso) >= limiar(MIC.ganho); if (temSom) MIC.ultimoSom = agora; // registro periodico (aparece no logcat do app: prova que o ouvido chegou) @@ -787,16 +810,18 @@ function som(nivel) { } if (r.dormindo) { - if (barulhoAcorda(madrugada() ? alto : temSom, agora)) { // acordou: barulho repetido + const precisa = Math.max(1, Math.round(DESPERTAR.sons * PERFIL.acordarSom)); + if (contar(CONTA.somAcordar, madrugada() ? alto : temSom, agora, precisa, "ouvido: barulho")) { // acordou r.dormindo = false; animar("surpreso"); MIC.proxSurpresa = agora + MIC.cooldown; console.log("ouvido: acordou nivel=" + n.toFixed(2)); relatarSom("acordou", n); } - } else if (alto && !MIC.estavaAlto && agora >= MIC.proxSurpresa) { - MIC.proxSurpresa = agora + MIC.cooldown; // susto: barulho alto e repentino - animar("surpreso"); + } else if (contar(CONTA.somReagir, PERFIL.qualquer ? temSom : alto, agora, PERFIL.reagir, "ouvido: estimulo") && + agora >= MIC.proxSurpresa) { + MIC.proxSurpresa = agora + MIC.cooldown * PERFIL.espera; // susto: barulho alto e repentino + animar(reacaoEstimulo()); console.log("ouvido: surpreso nivel=" + n.toFixed(2)); relatarSom("surpreso", n); } @@ -880,7 +905,7 @@ function rastrear(t) { MOV.olharY+=((t.y-.5)*2*MOV.olharMax*.6-MOV.olharY)*.65; if(t.atividade===true) { MOV.ultimoMov=agora; - if(r.dormindo && !madrugada()) { r.dormindo=false;animar('surpreso'); } + if(r.dormindo && !madrugada() && PERFIL !== PERFIS.calmo) { r.dormindo=false;animar('surpreso'); } } } function movimento(vel, cx, cy) { @@ -894,7 +919,7 @@ function movimento(vel, cx, cy) { if (!COMPORTAMENTO.reagir) { MOV.olharX = MOV.olharY = 0; return; } if (TRACK.alvo && agora-TRACK.visto<.35) return; - const mexeu = v >= MOV.limiar; + const mexeu = v >= limiar(MOV.limiar); if (mexeu) MOV.ultimoMov = agora; // olhar: desliza na direcao de quem se mexeu; sem movimento, volta ao centro @@ -909,19 +934,21 @@ function movimento(vel, cx, cy) { MOV.cx.toFixed(2) + "," + MOV.cy.toFixed(2)); } - const forte = v >= MOV.limiarForte; + const forte = v >= limiar(MOV.limiarForte); if (r.dormindo) { - if (mexeu && !madrugada()) { // acordou: algo se mexeu na frente (de dia) + // acordou: algo se mexeu na frente (de dia; calmo precisa de vários movimentos) + if (!madrugada() && contar(CONTA.movAcordar, mexeu, agora, PERFIL.acordarMov, "presenca: movimento")) { r.dormindo = false; animar("surpreso"); MOV.proxSurpresa = agora + MOV.cooldown; console.log("presenca: acordou nivel=" + v.toFixed(2)); relatarMov("acordou", v); } - } else if (forte && !MOV.estavaForte && agora >= MOV.proxSurpresa) { + } else if (contar(CONTA.movReagir, PERFIL.qualquer ? mexeu : forte, agora, PERFIL.reagir, "presenca: estimulo") && + agora >= MOV.proxSurpresa) { // susto so no INICIO do movimento forte: se continuar se mexendo, nao repete - MOV.proxSurpresa = agora + MOV.cooldown; - animar("surpreso"); + MOV.proxSurpresa = agora + MOV.cooldown * PERFIL.espera; + animar(reacaoEstimulo()); console.log("presenca: surpreso nivel=" + v.toFixed(2)); relatarMov("surpreso", v); } diff --git a/carinha.html b/carinha.html index 46b7c39..9b3c07e 100644 --- a/carinha.html +++ b/carinha.html @@ -592,6 +592,8 @@ function trata(ordem) { } } else if (ordem.indexOf("humor ") === 0) { HUMOR = ordem.slice(6).trim(); + } else if (ordem.indexOf("ritmo ") === 0) { + PERFIL = PERFIS[ordem.slice(6).trim()] || PERFIS.normal; } else if (ordem === "dormir" || ordem === "sono") { r.dormindo = true; } else if (ordem === "acordar") { @@ -743,24 +745,45 @@ const MIC = { /* Acordar com som: precisa de N barulhos em 1 minuto (ajuste "Sons para acordar"). Na madrugada silenciosa só barulho ALTO conta, e movimento/câmera não acordam (alguém se mexendo na cama). */ -const DESPERTAR = { sons: 3, janela: 60, noite: true, ini: 23, fim: 7, eventos: [], ultimo: -Infinity, estava: false }; +const DESPERTAR = { sons: 3, noite: true, ini: 23, fim: 7 }; function madrugada() { if (!DESPERTAR.noite || DESPERTAR.ini === DESPERTAR.fim) return false; const h = new Date().getHours(); return DESPERTAR.ini < DESPERTAR.fim ? h >= DESPERTAR.ini && h < DESPERTAR.fim : h >= DESPERTAR.ini || h < DESPERTAR.fim; } -/* Conta o INÍCIO de cada barulho (o mesmo barulho comprido vale um) e diz se já dá para acordar. */ -function barulhoAcorda(ouviu, agora) { - const D = DESPERTAR; - if (ouviu && !D.estava && agora - D.ultimo >= 1.5) { - D.ultimo = agora; - D.eventos = D.eventos.filter((t) => agora - t < D.janela).concat(agora); - console.log("ouvido: barulho " + D.eventos.length + "/" + D.sons + (madrugada() ? " (madrugada)" : "")); +/* Perfil de sensibilidade = ritmo do modo vida (o servidor manda "ritmo calmo|normal|agitado"). + Calmo precisa de vários estímulos para reagir e limiares mais altos; agitado reage a qualquer + som ou movimento. Multiplica os limiares dos Ajustes, sem mudar os Ajustes. */ +const PERFIS = { + calmo: { lim: 1.5, reagir: 3, acordarSom: 2, acordarMov: 4, espera: 2, qualquer: false }, + normal: { lim: 1.0, reagir: 1, acordarSom: 1, acordarMov: 2, espera: 1, qualquer: false }, + agitado: { lim: 0.6, reagir: 1, acordarSom: 0.34, acordarMov: 1, espera: 0.5, qualquer: true }, +}; +let PERFIL = PERFIS.normal; +const limiar = (x) => Math.min(0.95, x * PERFIL.lim); +/* Conta o INÍCIO de cada estímulo (o mesmo som/movimento comprido vale um) numa janela de tempo + e diz quando chegou a `precisa`. */ +function novoContador(janela) { return { janela, eventos: [], ultimo: -Infinity, estava: false, visto: -Infinity }; } +const CONTA = { somAcordar: novoContador(60), movAcordar: novoContador(30), somReagir: novoContador(20), movReagir: novoContador(20) }; +function contar(c, ativo, agora, precisa, nome) { + // contador parado um tempo (ex.: o de "acordar" enquanto estava acordado): começa do zero + if (agora - c.visto > 2) c.estava = false; + c.visto = agora; + if (ativo && !c.estava && agora - c.ultimo >= 1.0) { + c.ultimo = agora; + c.eventos = c.eventos.filter((t) => agora - t < c.janela).concat(agora); + if (precisa > 1) console.log(nome + " " + c.eventos.length + "/" + precisa + (madrugada() ? " (madrugada)" : "")); } - D.estava = ouviu; - if (D.eventos.length >= D.sons && agora - D.eventos[D.eventos.length - 1] < 1) { D.eventos = []; return true; } + c.estava = ativo; + if (c.eventos.length >= precisa && agora - c.eventos[c.eventos.length - 1] < 1) { c.eventos = []; return true; } return false; } +/* Reação acordado: agitado varia; os outros se assustam. */ +function reacaoEstimulo() { + if (!PERFIL.qualquer) return "surpreso"; + const l = ["surpreso", "feliz", "alegre", "piscadinha", "surpreso"]; + return l[Math.floor(Math.random() * l.length)]; +} function som(nivel) { const n = Math.max(0, Math.min(1, Number(nivel) || 0)); @@ -776,8 +799,8 @@ function som(nivel) { if (MIC.piso === null) MIC.piso = n; else MIC.piso += (n - MIC.piso) * (n < MIC.piso ? 0.05 : 0.0015); - const temSom = n >= MIC.limiarSom; // ruido audivel (absoluto) - const alto = n >= MIC.limiarAlto && (n - MIC.piso) >= MIC.ganho; + const temSom = n >= limiar(MIC.limiarSom); // ruido audivel (absoluto) + const alto = n >= limiar(MIC.limiarAlto) && (n - MIC.piso) >= limiar(MIC.ganho); if (temSom) MIC.ultimoSom = agora; // registro periodico (aparece no logcat do app: prova que o ouvido chegou) @@ -787,16 +810,18 @@ function som(nivel) { } if (r.dormindo) { - if (barulhoAcorda(madrugada() ? alto : temSom, agora)) { // acordou: barulho repetido + const precisa = Math.max(1, Math.round(DESPERTAR.sons * PERFIL.acordarSom)); + if (contar(CONTA.somAcordar, madrugada() ? alto : temSom, agora, precisa, "ouvido: barulho")) { // acordou r.dormindo = false; animar("surpreso"); MIC.proxSurpresa = agora + MIC.cooldown; console.log("ouvido: acordou nivel=" + n.toFixed(2)); relatarSom("acordou", n); } - } else if (alto && !MIC.estavaAlto && agora >= MIC.proxSurpresa) { - MIC.proxSurpresa = agora + MIC.cooldown; // susto: barulho alto e repentino - animar("surpreso"); + } else if (contar(CONTA.somReagir, PERFIL.qualquer ? temSom : alto, agora, PERFIL.reagir, "ouvido: estimulo") && + agora >= MIC.proxSurpresa) { + MIC.proxSurpresa = agora + MIC.cooldown * PERFIL.espera; // susto: barulho alto e repentino + animar(reacaoEstimulo()); console.log("ouvido: surpreso nivel=" + n.toFixed(2)); relatarSom("surpreso", n); } @@ -880,7 +905,7 @@ function rastrear(t) { MOV.olharY+=((t.y-.5)*2*MOV.olharMax*.6-MOV.olharY)*.65; if(t.atividade===true) { MOV.ultimoMov=agora; - if(r.dormindo && !madrugada()) { r.dormindo=false;animar('surpreso'); } + if(r.dormindo && !madrugada() && PERFIL !== PERFIS.calmo) { r.dormindo=false;animar('surpreso'); } } } function movimento(vel, cx, cy) { @@ -894,7 +919,7 @@ function movimento(vel, cx, cy) { if (!COMPORTAMENTO.reagir) { MOV.olharX = MOV.olharY = 0; return; } if (TRACK.alvo && agora-TRACK.visto<.35) return; - const mexeu = v >= MOV.limiar; + const mexeu = v >= limiar(MOV.limiar); if (mexeu) MOV.ultimoMov = agora; // olhar: desliza na direcao de quem se mexeu; sem movimento, volta ao centro @@ -909,19 +934,21 @@ function movimento(vel, cx, cy) { MOV.cx.toFixed(2) + "," + MOV.cy.toFixed(2)); } - const forte = v >= MOV.limiarForte; + const forte = v >= limiar(MOV.limiarForte); if (r.dormindo) { - if (mexeu && !madrugada()) { // acordou: algo se mexeu na frente (de dia) + // acordou: algo se mexeu na frente (de dia; calmo precisa de vários movimentos) + if (!madrugada() && contar(CONTA.movAcordar, mexeu, agora, PERFIL.acordarMov, "presenca: movimento")) { r.dormindo = false; animar("surpreso"); MOV.proxSurpresa = agora + MOV.cooldown; console.log("presenca: acordou nivel=" + v.toFixed(2)); relatarMov("acordou", v); } - } else if (forte && !MOV.estavaForte && agora >= MOV.proxSurpresa) { + } else if (contar(CONTA.movReagir, PERFIL.qualquer ? mexeu : forte, agora, PERFIL.reagir, "presenca: estimulo") && + agora >= MOV.proxSurpresa) { // susto so no INICIO do movimento forte: se continuar se mexendo, nao repete - MOV.proxSurpresa = agora + MOV.cooldown; - animar("surpreso"); + MOV.proxSurpresa = agora + MOV.cooldown * PERFIL.espera; + animar(reacaoEstimulo()); console.log("presenca: surpreso nivel=" + v.toFixed(2)); relatarMov("surpreso", v); } diff --git a/kiosk-server.py b/kiosk-server.py index e4717f3..5e142aa 100644 --- a/kiosk-server.py +++ b/kiosk-server.py @@ -516,7 +516,7 @@ def main(): if tipo == "pir" and not madrugada(servidor.controle.config): with servidor.controle.lock: dormindo = bool(servidor.controle.estado.get("dormindo")) - if dormindo: + if dormindo and vida.pir_acorda(): enfileirar("acordar") def camera(ligada): diff --git a/tests/test_vida.py b/tests/test_vida.py index a75517f..a1b6f97 100644 --- a/tests/test_vida.py +++ b/tests/test_vida.py @@ -80,6 +80,22 @@ class VidaTest(unittest.TestCase): self.assertNotEqual(self.v.agir(noite + 180), 'dormir') self.assertEqual(self.v.agir(noite + 180 + 11 * 60), 'dormir') + def test_ritmo_vira_sensibilidade(self): + self.v.configurar({'ritmo': 'calmo'}) + self.assertIn('ritmo calmo', self.ordens) + self.assertFalse(self.v.pir_acorda(DIA)) # calmo: 3 disparos do PIR em 2 min + self.assertFalse(self.v.pir_acorda(DIA + 30)) + self.assertTrue(self.v.pir_acorda(DIA + 60)) + self.assertFalse(self.v.pir_acorda(DIA + 400)) # os antigos expiraram + self.v.configurar({'ritmo': 'agitado'}) + self.assertTrue(self.v.pir_acorda(DIA + 500)) # agitado: um basta + self.ordens.clear() + self.v.tick(DIA + 505) + self.assertIn('ritmo agitado', self.ordens) # reenviado junto com o humor + self.v.configurar({'ligado': False}) + self.assertIn('ritmo normal', self.ordens) + self.assertTrue(self.v.pir_acorda(DIA + 600)) # desligado: comportamento de sempre + def test_horario_da_madrugada(self): from controle_pc import madrugada h = lambda hora: time.mktime((2026, 10, 6, hora, 10, 0, 0, 0, -1)) diff --git a/vida.py b/vida.py index 1f43443..d83c001 100644 --- a/vida.py +++ b/vida.py @@ -23,6 +23,9 @@ RECUPERA_SONO = 2.5 # energia por minuto dormindo (25 -> 100 em ~30 min SONO_LENTO = 0.25 RITMOS = {'calmo': (60, 150), 'normal': (25, 75), 'agitado': (12, 35)} # segundos entre atos +# O ritmo também é a sensibilidade: o celular recebe "ritmo x" e ajusta limiares e quantos estímulos +# precisa para reagir/acordar. Aqui, quantos disparos do PIR em 2 min acordam o rosto. +PIR_ACORDAR = {'calmo': 3, 'normal': 1, 'agitado': 1} PADRAO = dict(ligado=True, criar_emojis=True, max_emojis_dia=3, ritmo='normal', noite=True) NOITE = (23, 7) # horário padrão da madrugada (o servidor usa o dos ajustes do celular) @@ -103,6 +106,7 @@ class Vida: self.pode_gemini = lambda: bool(os.environ.get('GEMINI_API_KEY')) self.madrugada = self.noite_padrao # o main troca pelo horário dos ajustes self.interagiu_em = 0.0 + self.pir_eventos = [] agora = time.time() self.config = dict(PADRAO) @@ -160,8 +164,11 @@ class Vida: self.anotar('vida', 'Modo autônomo ligado' if limpo['ligado'] else 'Modo autônomo desligado') if not limpo['ligado']: self.enfileirar('humor normal') # o celular volta aos gestos soltos de sempre - self.humor_enviado = (None, 0.0) + self.enfileirar('ritmo normal') # e à sensibilidade dos Ajustes self.proximo_ato = time.time() + 5 + if self.config['ligado'] and 'ritmo' in limpo: + self.enfileirar('ritmo ' + self.config['ritmo']) + self.humor_enviado = (None, 0.0) self.salvar() self.avisar() return self.status() @@ -276,6 +283,16 @@ class Vida: if motivo: self.anotar('sono', motivo, agora) + def pir_acorda(self, agora=None): + """Disparo do PIR com o rosto dormindo: acorda? No ritmo calmo precisa de vários em 2 min.""" + agora = agora or time.time() + self.pir_eventos = [t for t in self.pir_eventos if agora - t < 120] + [agora] + precisa = PIR_ACORDAR[self.config['ritmo']] if self.config['ligado'] else 1 + if len(self.pir_eventos) >= precisa: + self.pir_eventos = [] + return True + return False + @staticmethod def noite_padrao(agora): hora = time.localtime(agora).tm_hour @@ -388,6 +405,7 @@ class Vida: # o celular escolhe os gestos soltos pelo humor; reenvia de tempos em tempos (pode ter reiniciado) if humor != self.humor_enviado[0] or agora - self.humor_enviado[1] > 60: self.enfileirar('humor ' + humor) + self.enfileirar('ritmo ' + self.config['ritmo']) self.humor_enviado = (humor, agora) if agora >= self.proximo_ato: ato = self.agir(agora) diff --git a/web/src/lib/componentes/VidaCartao.svelte b/web/src/lib/componentes/VidaCartao.svelte index b44ff50..25036f4 100644 --- a/web/src/lib/componentes/VidaCartao.svelte +++ b/web/src/lib/componentes/VidaCartao.svelte @@ -10,10 +10,11 @@ const v = $derived(estado.vida); let diarioAberto = $state(false); + // O ritmo também é a sensibilidade dos sensores no celular. const RITMOS = [ - { valor: 'calmo', nome: 'Calmo' }, - { valor: 'normal', nome: 'Normal' }, - { valor: 'agitado', nome: 'Agitado' }, + { valor: 'calmo', nome: 'Calmo', descricao: 'Age pouco; só reage e acorda com vários sons ou movimentos seguidos' }, + { valor: 'normal', nome: 'Normal', descricao: 'Reage a barulho alto e movimento forte' }, + { valor: 'agitado', nome: 'Agitado', descricao: 'Age bastante e reage a qualquer som ou movimento' }, ] as const; const MOTIVOS: Record = { @@ -96,7 +97,7 @@ configurarVida({ criar_emojis: l })} /> {/snippet} - + r.valor === v.config.ritmo)?.descricao ?? ''}> {#snippet acessorio()} {#each RITMOS as r (r.valor)}