- carinha.html assina /eventos (expressao, app_config, conversa, reacao, recarregar) e volta ao polling de /fila sozinho se o SSE cair; foto dos ajustes a cada (re)conexao; token (?t=) vai em todo fetch ao servidor - APK 0.8-token: token nas preferencias e no menu de toque longo, enviado na pagina, no HTTP do Java e ao llama-server (--api-key); trocar servidor por Intent exige o token salvo; camera/microfone so para o servidor configurado ou a copia local - cerebro/conversa mandam a chave ao llama-server do S10 - servidor: evento SSE 'recarregar'; remove /camera/frame e /app/connect; enderecos antigos redirecionam para a secao do painel novo - remove dashboard, painel, conversa, emojis, treinamento, braco, servos, api, scripts e CSS antigos, audio-robot.js e servir-local.bat - logica pura do painel em web/src/lib/nucleo.ts com testes node --test (substitui test_movimentos.js e test_rastreamento.js) - testes de navegador portados para o painel novo (rtc, rosto, rastreamento) - README, docs/PAINEL.md e docs/SEGURANCA.md reescritos; versao 0.8.0; carinha-pc.service le /etc/carinha.env Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
5.4 KiB
JavaScript
55 lines
5.4 KiB
JavaScript
(function(){
|
|
const icons={nenhum:'',estrela:'✦',coracao:'♥',musica:'♫',cafe:'☕',ideia:'💡'};
|
|
const badge=document.createElement('div');badge.id='reacaoSimbolo';badge.style.cssText='position:fixed;left:5%;top:6%;z-index:15;color:#5cd6ff;font:clamp(30px,7vw,64px) sans-serif;pointer-events:none;white-space:pre-wrap';document.body.append(badge);
|
|
let timer,last=null,fetching=false,animation;let particles=[];
|
|
function clearVisual(){window.carinhaOlhosIcone=null;window.carinhaOlhosDesenho=null;if(animation)animation.cancel();for(const a of particles)a.cancel();particles=[];badge.replaceChildren();}
|
|
window.carinhaConstruirSVG=function(drawing){
|
|
if(!drawing||!Array.isArray(drawing.formas)||drawing.formas.length>16)return null;
|
|
const svg=document.createElementNS('http://www.w3.org/2000/svg','svg');svg.setAttribute('viewBox','0 0 100 100');svg.setAttribute('class','figurinhaDinamica');
|
|
for(const f of drawing.formas){
|
|
if(typeof f.caminho!=='string'||f.caminho.length>600||!/^([MLCQZmlcqz0-9.,\s+-])+$/.test(f.caminho)||!/^#[0-9a-f]{6}$/i.test(f.cor))return null;
|
|
const path=document.createElementNS(svg.namespaceURI,'path');path.setAttribute('d',f.caminho);path.setAttribute('fill',f.cor);svg.append(path);
|
|
}
|
|
return svg;
|
|
};
|
|
function visual(recipe){
|
|
if(recipe.emoji){
|
|
const e=recipe.emoji;if(e.local==='olhos'){window.carinhaOlhosIcone=e.caractere;return;}
|
|
badge.style.cssText='position:fixed;right:5%;top:8%;z-index:15;font:clamp(42px,10vw,90px) sans-serif;pointer-events:none';badge.textContent=e.caractere;
|
|
animation=badge.animate([{transform:'scale(.92)'},{transform:'scale(1.06)'},{transform:'scale(.92)'}],{duration:1400,iterations:Infinity});return;
|
|
}
|
|
if(recipe.figurinha){
|
|
const svg=window.carinhaConstruirSVG(recipe.figurinha);if(!svg)return;
|
|
if(recipe.figurinha.local==='olhos'){window.carinhaOlhosDesenho=recipe.figurinha;return;}
|
|
badge.style.cssText='position:fixed;right:5%;top:8%;width:24%;height:30%;z-index:15;pointer-events:none';svg.style.cssText='width:100%;height:100%';badge.append(svg);
|
|
animation=svg.animate([{transform:'scale(.92) rotate(-3deg)'},{transform:'scale(1.05) rotate(3deg)'},{transform:'scale(.92) rotate(-3deg)'}],{duration:1500,iterations:Infinity});return;
|
|
}
|
|
if(['ideia','cafe'].includes(recipe.simbolo)){window.carinhaOlhosIcone=icons[recipe.simbolo];return;}
|
|
const scattered=['musica','estrela','coracao'].includes(recipe.simbolo);
|
|
badge.style.cssText=scattered?'position:fixed;inset:0;z-index:15;pointer-events:none;overflow:hidden':'position:fixed;left:5%;top:6%;z-index:15;font:clamp(30px,7vw,64px) sans-serif;pointer-events:none';
|
|
badge.style.color=recipe.passos[0].cor;
|
|
if(!scattered){badge.textContent=icons[recipe.simbolo];animation=badge.animate([{transform:'translateY(0)'},{transform:'translateY(-10px)'},{transform:'translateY(0)'}],{duration:1000,iterations:Infinity});return;}
|
|
for(let i=0;i<16;i++){
|
|
const p=document.createElement('span');p.className='particula';p.textContent=recipe.simbolo==='musica'?(i%2?'♫':'♪'):icons[recipe.simbolo];
|
|
p.style.cssText='position:absolute;top:'+(8+Math.random()*75)+'%;left:'+(5+Math.random()*85)+'%;font-size:'+(20+Math.floor(Math.random()*22))+'px;opacity:0;text-shadow:0 0 12px currentColor';badge.append(p);
|
|
const duration=1200+Math.random()*500;
|
|
particles.push(p.animate([{opacity:0},{opacity:.85,offset:.45},{opacity:0}],{duration,delay:Math.random()*1000,iterations:2,fill:'both'}));
|
|
}
|
|
}
|
|
function render(recipe){
|
|
if(recipe?.limpar){clearTimeout(timer);clearVisual();r.fila=[];r.kf=null;congelado=false;Object.assign(r,{forma:'aberto',forma2:null,boca:'neutra',esc:1,dx:0,dy:0,ab:1,ab2:1});desenhar();return;}
|
|
if(!recipe||!Array.isArray(recipe.passos)||recipe.passos.length<1||recipe.passos.length>6||!(recipe.simbolo in icons))return;
|
|
for(const s of recipe.passos)if(!['aberto','feliz','surpreso','coracao','triste'].includes(s.olhos)||!['neutra','sorriso','aberta','triste'].includes(s.boca)||!/^#[0-9a-f]{6}$/i.test(s.cor)||!Number.isFinite(s.duracao)||s.duracao<.15||s.duracao>2||!Number.isFinite(s.dx)||Math.abs(s.dx)>25||!Number.isFinite(s.dy)||Math.abs(s.dy)>12||!Number.isFinite(s.escala)||s.escala<.8||s.escala>1.3)return;
|
|
clearTimeout(timer);clearVisual();window.eilik.soltar();trata('acordar');MIC.ultimoSom=performance.now()/1000;
|
|
visual(recipe);
|
|
r.fila=recipe.passos.map(s=>[{forma:s.olhos,forma2:null,boca:s.boca,cor:[1,3,5].map(i=>parseInt(s.cor.slice(i,i+2),16)),dx:s.dx,dy:s.dy,esc:s.escala,ab:1,ab2:1},s.duracao]);
|
|
r.fila.push([{forma:'aberto',forma2:null,boca:'neutra',cor:CIANO.slice(),dx:0,dy:0,esc:1,ab:1,ab2:1},.4]);proximo();
|
|
timer=setTimeout(clearVisual,Math.max(recipe.figurinha?6000:0,['musica','estrela','coracao'].includes(recipe.simbolo)?4500:['ideia','cafe'].includes(recipe.simbolo)?2500:0,recipe.passos.reduce((n,s)=>n+s.duracao,0)*1000+400));
|
|
}
|
|
window.eilik.receita=render;
|
|
addEventListener('message',e=>{if(e.origin===location.origin&&e.source===parent&&e.data&&e.data.tipo==='previa-reacao')render(e.data.receita);});
|
|
if(new URLSearchParams(location.search).get('espelho')==='1'||location.protocol==='file:')return;
|
|
async function poll(){if(fetching||window.carinhaAtiva===false||window.carinhaSSE)return;fetching=true;try{const response=await fetch('/treinamento/expressao',{cache:'no-store',signal:AbortSignal.timeout(2000)});if(response.ok){const d=await response.json();if(d&&d.id!==last&&Date.now()/1000-d.horario<60){last=d.id;render(d.receita);}}}catch(e){}finally{fetching=false;}}
|
|
setInterval(poll,1000);
|
|
})();
|