feat(vida): jeito de gente e console com o porquê de cada decisão
Console (console.py + tela Console no painel):
- cada parte registra o que fez e o motivo: modo vida (com necessidades
e humor), sentidos, celular (ordens e o que o rosto fez), IA local,
aprendizado e ideias do dia; últimas 400 linhas, ao vivo pelo SSE;
- tela com o "agora" (humor, necessidades, o que está fazendo, próxima
decisão, última decisão, jeito), filtros, detalhes, pausa e busca.
Jeito de gente:
- saudade: a festa cresce com o tempo fora (aceno, "Oi! Você voltou!",
"Senti sua falta!"); bom dia lembrando de ontem;
- pedidos com motivo ("Brinca comigo?", "Me faz um carinho?", mostrar a
figurinha nova), no máximo 1 a cada 20 min;
- larga atividade no meio quando fica chato; personalidade (curiosidade,
brincadeira, timidez) que pesa nos gestos e amadurece com o convívio;
- no rosto: acordar grogue, sonhar com algo do dia (balão no motor),
hesitar antes de gestos, errar na atividade e ficar sem graça, olhar
nos olhos e desviar, sorrir de volta (FaceLandmarker no rastreador do
painel) e reagir à luz (sensor de luz no app 0.10.0);
- ordem "dizer" com legenda e voz de robô.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+4
-1
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Bell, Bot, Command, Hand, MessageCircle, Moon, Settings, Smile, Sun, Video } from '@lucide/svelte';
|
||||
import { Bell, Bot, Command, Hand, MessageCircle, Moon, Settings, Smile, Sun, Terminal, Video } from '@lucide/svelte';
|
||||
import { onMount, type Component } from 'svelte';
|
||||
import Avisos from './lib/componentes/Avisos.svelte';
|
||||
import EStop from './lib/componentes/EStop.svelte';
|
||||
@@ -21,6 +21,7 @@
|
||||
import Guia from './telas/Guia.svelte';
|
||||
import Criar from './telas/Criar.svelte';
|
||||
import DiaADia from './telas/DiaADia.svelte';
|
||||
import Console from './telas/Console.svelte';
|
||||
import Ajuda from './lib/componentes/Ajuda.svelte';
|
||||
import { TOPICO_DA_TELA } from './lib/guia';
|
||||
|
||||
@@ -35,10 +36,12 @@
|
||||
guia: Guia,
|
||||
criar: Criar,
|
||||
'dia-a-dia': DiaADia,
|
||||
console: Console,
|
||||
};
|
||||
const ICONES: Record<string, Component<any>> = {
|
||||
'ao-vivo': Video,
|
||||
eventos: Bell,
|
||||
console: Terminal,
|
||||
conversa: MessageCircle,
|
||||
reacoes: Smile,
|
||||
movimento: Hand,
|
||||
|
||||
@@ -188,7 +188,7 @@ function abrirEventos() {
|
||||
if (estado.conexao !== 'token') estado.conexao = 'caiu';
|
||||
};
|
||||
for (const tipo of ['expressao', 'telemetria', 'app_config', 'aparencia', 'motores', 'estop', 'conversa', 'ia',
|
||||
'reacao', 'treinamento', 'historico', 'pir', 'vida', 'aprendizado', 'criacao', 'inspiracao']) {
|
||||
'reacao', 'treinamento', 'historico', 'pir', 'vida', 'aprendizado', 'criacao', 'inspiracao', 'console']) {
|
||||
fonte.addEventListener(tipo, (e) => {
|
||||
const dados = JSON.parse((e as MessageEvent).data);
|
||||
tratadores[tipo]?.(dados);
|
||||
|
||||
+33
-2
@@ -229,6 +229,37 @@ export const TOPICOS: Record<string, TopicoGuia> = {
|
||||
{ emoji: '⭐', nome: 'Favoritos', texto: 'A estrela põe expressões e atividades criadas na grade de favoritos do Ao vivo (e nas teclas 1 a 9).' },
|
||||
],
|
||||
},
|
||||
console: {
|
||||
titulo: 'Console',
|
||||
emoji: '🖥️',
|
||||
onde: 'Console (menu lateral)',
|
||||
resumo:
|
||||
'Tudo o que ele faz, ao vivo, com o porquê. Em cima, o agora: humor, necessidades, o que está fazendo, quando decide de novo e o jeito dele. Embaixo, cada decisão com o motivo (ex.: "Ficou exausto e foi dormir, porque energia 12: abaixo de 20"). Os filtros escolhem de onde vêm as linhas; "Detalhes" mostra também o que se repete muito (humor reenviado, disparo solto do sensor). Pausar segura a tela para ler.',
|
||||
itens: [
|
||||
{ emoji: '🫀', nome: 'Modo vida', texto: 'As decisões dele: dormir, atividades, gestos, pedidos, reencontros.' },
|
||||
{ emoji: '👀', nome: 'Sentidos', texto: 'Carinho, toque, conversa, presença, som e movimento, com o efeito nas necessidades.' },
|
||||
{ emoji: '📱', nome: 'Celular', texto: 'O que foi mandado ao rosto e o que o rosto fez (dormiu, acordou, começou atividade).' },
|
||||
{ emoji: '🧠', nome: 'IA local / Aprendizado / Ideias', texto: 'Escolhas da IA do S10, decisões tiradas da rotina e as ideias do dia.' },
|
||||
],
|
||||
},
|
||||
jeito: {
|
||||
titulo: 'Jeito de gente',
|
||||
emoji: '🧒',
|
||||
onde: 'Modo vida e rosto',
|
||||
resumo: 'O que faz ele parecer alguém, e não um programa.',
|
||||
itens: [
|
||||
{ emoji: '🤗', nome: 'Saudade', texto: 'A festa depende do tempo fora: 10 min, um aceno; 1 h, "Oi! Você voltou!"; 4 h ou mais, "Senti sua falta!".' },
|
||||
{ emoji: '☀️', nome: 'Bom dia com lembrança', texto: 'No primeiro encontro do dia lembra de ontem: "ontem a gente ficou acordado até as 2h, hein!".' },
|
||||
{ emoji: '🥱', nome: 'Acordar grogue', texto: 'Depois de dormir um tempo, acorda com os olhos pesados, lento, e boceja antes de ficar esperto.' },
|
||||
{ emoji: '💭', nome: 'Sonhar', texto: 'Dormindo, às vezes aparece bem fraco um balão com algo do dia (a bola, as estrelas) e ele sorri.' },
|
||||
{ emoji: '🙈', nome: 'Imperfeição', texto: 'Hesita antes de alguns gestos, erra no meio da atividade e fica sem graça, e às vezes larga uma atividade porque ficou chato.' },
|
||||
{ emoji: '🙋', nome: 'Pedidos', texto: 'Com alguém perto, pede o que falta ("Brinca comigo?", "Me faz um carinho?") ou mostra a figurinha que desenhou. No máximo um pedido a cada 20 min.' },
|
||||
{ emoji: '👁️', nome: 'Contato visual', texto: 'Com o rastreamento de rosto, olha nos olhos alguns segundos, desvia um instante e volta.' },
|
||||
{ emoji: '😊', nome: 'Sorriso de volta', texto: 'Com o Ao vivo aberto e o rastreamento de rosto ligado, se você sorri ele sorri (ou ri) de volta.' },
|
||||
{ emoji: '💡', nome: 'Luz', texto: 'Apagaram a luz: boceja e fica com sono. Acenderam de repente: aperta os olhos.' },
|
||||
{ emoji: '🧬', nome: 'Personalidade', texto: 'Curioso, brincalhão e tímido em medidas próprias. Muda o jeito de tudo e amadurece: carinho deixa menos tímido, brincar deixa mais brincalhão.' },
|
||||
],
|
||||
},
|
||||
ideias: {
|
||||
titulo: 'Ideias do dia',
|
||||
emoji: '💡',
|
||||
@@ -371,13 +402,13 @@ export const TOPICOS: Record<string, TopicoGuia> = {
|
||||
|
||||
/** Ordem do tutorial (página Guia). */
|
||||
export const ORDEM_GUIA = [
|
||||
'inicio', 'vida', 'aprendizado', 'cuidados', 'humores', 'ritmo', 'sono', 'celular', 'camera', 'gestos', 'atividades', 'criar', 'ideias', 'aparencia',
|
||||
'inicio', 'vida', 'aprendizado', 'cuidados', 'humores', 'ritmo', 'sono', 'celular', 'camera', 'gestos', 'atividades', 'jeito', 'console', 'criar', 'ideias', 'aparencia',
|
||||
'personalidade', 'acontecimentos', 'ligacoes', 'eventos', 'presenca', 'tela', 'sensores', 'sensibilidade',
|
||||
'reacoes', 'conversa', 'movimento', 'atalhos',
|
||||
];
|
||||
|
||||
/** Tópico de cada tela, para o ⓘ do topo. */
|
||||
export const TOPICO_DA_TELA: Record<string, string> = {
|
||||
'ao-vivo': 'inicio', eventos: 'eventos', conversa: 'conversa', reacoes: 'reacoes',
|
||||
'ao-vivo': 'inicio', console: 'console', eventos: 'eventos', conversa: 'conversa', reacoes: 'reacoes',
|
||||
movimento: 'movimento', ajustes: 'tela', rosto: 'aparencia', guia: 'inicio', criar: 'criar', 'dia-a-dia': 'aprendizado',
|
||||
};
|
||||
|
||||
@@ -18,6 +18,8 @@ export interface AlvoRastreio {
|
||||
x: number;
|
||||
y: number;
|
||||
atividade?: boolean;
|
||||
/** 0 a 1: o quanto a pessoa está sorrindo (só rosto; o celular sorri de volta) */
|
||||
sorriso?: number;
|
||||
}
|
||||
|
||||
const valido = (p: Ponto | undefined) => !!p && Number.isFinite(p.x) && Number.isFinite(p.y);
|
||||
|
||||
@@ -11,6 +11,7 @@ interface Resultado {
|
||||
hands: Ponto[][];
|
||||
ms: number;
|
||||
timestamp: number;
|
||||
sorriso?: number | null;
|
||||
}
|
||||
|
||||
const LIGACOES = [[0, 1], [1, 2], [2, 3], [3, 4], [0, 5], [5, 6], [6, 7], [7, 8], [5, 9], [9, 10], [10, 11], [11, 12],
|
||||
@@ -146,6 +147,7 @@ export class Rastreamento {
|
||||
const anterior = this.alvo;
|
||||
const alvo = escolherAlvo(r.faces, r.hands, anterior);
|
||||
if (alvo) alvo.atividade = !!anterior && alvo.tipo === anterior.tipo && Math.hypot(alvo.x - anterior.x, alvo.y - anterior.y) > 0.008;
|
||||
if (alvo && alvo.tipo === 'rosto' && typeof r.sorriso === 'number') alvo.sorriso = Math.round(r.sorriso * 100) / 100;
|
||||
this.alvo = alvo;
|
||||
this.alvoEm = r.timestamp;
|
||||
this.intervalo = Math.max(50, Math.min(200, r.ms * 1.2));
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/** Rotas por hash (#/conversa): o servidor só precisa entregar index.html em "/". */
|
||||
|
||||
export type Rota = 'ao-vivo' | 'eventos' | 'conversa' | 'reacoes' | 'movimento' | 'ajustes' | 'rosto' | 'guia' | 'criar' | 'dia-a-dia';
|
||||
export type Rota = 'ao-vivo' | 'eventos' | 'conversa' | 'reacoes' | 'movimento' | 'ajustes' | 'rosto' | 'guia' | 'criar' | 'dia-a-dia' | 'console';
|
||||
|
||||
export const ROTAS: { id: Rota; titulo: string; atalho: string }[] = [
|
||||
{ id: 'ao-vivo', titulo: 'Ao vivo', atalho: 'g v' },
|
||||
{ id: 'console', titulo: 'Console', atalho: 'g k' },
|
||||
{ id: 'eventos', titulo: 'Eventos', atalho: 'g e' },
|
||||
{ id: 'conversa', titulo: 'Conversa', atalho: 'g c' },
|
||||
{ id: 'reacoes', titulo: 'Reações', atalho: 'g r' },
|
||||
|
||||
@@ -104,7 +104,15 @@ export interface Vida {
|
||||
sono_profundo?: boolean;
|
||||
/** '' quando pode inventar figurinha agora; senão, o motivo. */
|
||||
pode_criar: string;
|
||||
diario: { horario: number; tipo: 'acao' | 'sono' | 'emoji' | 'cuidado' | 'vida' | 'atividade' | 'aprendizado' | 'aprendi'; texto: string }[];
|
||||
diario: { horario: number; tipo: 'acao' | 'sono' | 'emoji' | 'cuidado' | 'vida' | 'atividade' | 'aprendizado' | 'aprendi' | 'social' | 'pedido' | 'ideia'; texto: string }[];
|
||||
/** Traços de 0 a 1 (curiosidade, brincadeira, timidez): mudam o jeito e amadurecem com o convívio. */
|
||||
personalidade?: Record<string, number>;
|
||||
/** Motivo da última decisão do modo vida. */
|
||||
porque?: string;
|
||||
/** A última decisão de fato (o que fez e por quê). */
|
||||
ultima?: { horario: number; texto: string; porque: string } | null;
|
||||
/** Segundos até a próxima decisão. */
|
||||
proximo_ato_em?: number;
|
||||
}
|
||||
|
||||
/** O que ele aprendeu com o dia a dia (aprendizado.py). */
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
<script lang="ts">
|
||||
import { Pause, Play, Search, Trash2 } from '@lucide/svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { pedir } from '../lib/api';
|
||||
import { avisos } from '../lib/avisos.svelte';
|
||||
import { estado, ouvir } from '../lib/estado.svelte';
|
||||
|
||||
// Console: tudo o que ele está fazendo e por quê, ao vivo. Em cima o "agora"; embaixo cada decisão
|
||||
// com o motivo (modo vida, sentidos, celular, IA local, aprendizado, ideias do dia).
|
||||
type Origem = 'vida' | 'sentidos' | 'celular' | 'ia' | 'aprendizado' | 'ideias' | 'sistema';
|
||||
interface Linha { id: number; horario: number; origem: Origem; texto: string; motivo: string; nivel: 'info' | 'detalhe' }
|
||||
|
||||
const ORIGENS: { id: Origem; nome: string }[] = [
|
||||
{ id: 'vida', nome: 'Modo vida' }, { id: 'sentidos', nome: 'Sentidos' }, { id: 'celular', nome: 'Celular' },
|
||||
{ id: 'ia', nome: 'IA local' }, { id: 'aprendizado', nome: 'Aprendizado' }, { id: 'ideias', nome: 'Ideias' },
|
||||
{ id: 'sistema', nome: 'Sistema' },
|
||||
];
|
||||
const MAX = 600;
|
||||
|
||||
let linhas = $state<Linha[]>([]);
|
||||
let pausado = $state(false);
|
||||
let guardadas = $state<Linha[]>([]); // chegaram com o console pausado
|
||||
let detalhes = $state(false);
|
||||
let busca = $state('');
|
||||
let ligadas = $state<Record<Origem, boolean>>({ vida: true, sentidos: true, celular: true, ia: true, aprendizado: true, ideias: true, sistema: true });
|
||||
let vidaEm = $state(Date.now());
|
||||
|
||||
onMount(() => {
|
||||
pedir<{ linhas: Linha[] }>('/console').then((d) => (linhas = d.linhas.slice(-MAX).reverse())).catch((e) => avisos.erro(e));
|
||||
return ouvir('console', (l: Linha) => {
|
||||
if (pausado) guardadas = [l, ...guardadas].slice(0, MAX);
|
||||
else linhas = [l, ...linhas].slice(0, MAX);
|
||||
});
|
||||
});
|
||||
$effect(() => {
|
||||
estado.vida; // a contagem do próximo ato parte de quando o status chegou
|
||||
vidaEm = Date.now();
|
||||
});
|
||||
|
||||
function alternarPausa() {
|
||||
pausado = !pausado;
|
||||
if (!pausado && guardadas.length) {
|
||||
linhas = [...guardadas, ...linhas].slice(0, MAX);
|
||||
guardadas = [];
|
||||
}
|
||||
}
|
||||
const visiveis = $derived.by(() => {
|
||||
const q = busca.trim().toLowerCase();
|
||||
return linhas.filter((l) => ligadas[l.origem] && (detalhes || l.nivel !== 'detalhe') &&
|
||||
(!q || (l.texto + ' ' + l.motivo).toLowerCase().includes(q)));
|
||||
});
|
||||
const hora = (s: number) => new Date(s * 1000).toLocaleTimeString('pt-BR');
|
||||
|
||||
// ---------------------------------------------------------------- agora
|
||||
const v = $derived(estado.vida);
|
||||
const cel = $derived(estado.app);
|
||||
const proximo = $derived(v?.proximo_ato_em != null ? Math.max(0, Math.round(v.proximo_ato_em - (estado.agora - vidaEm) / 1000)) : null);
|
||||
const fazendo = $derived.by(() => {
|
||||
if (!cel?.online) return 'celular desconectado';
|
||||
const e = cel.estado ?? {};
|
||||
if (e.atividade) return 'fazendo: ' + e.atividade;
|
||||
if (e.dormindo) return v?.sono_profundo ? 'dormindo (sono pesado)' : 'dormindo';
|
||||
return 'acordado, de bobeira';
|
||||
});
|
||||
const noite = $derived(estado.appConfig ? `${estado.appConfig.noiteInicio ?? 23}h às ${estado.appConfig.noiteFim ?? 7}h` : '');
|
||||
const NEC: { id: 'energia' | 'alegria' | 'social' | 'diversao'; nome: string }[] = [
|
||||
{ id: 'energia', nome: 'Energia' }, { id: 'alegria', nome: 'Alegria' }, { id: 'social', nome: 'Social' }, { id: 'diversao', nome: 'Diversão' },
|
||||
];
|
||||
const TRACO: Record<string, string> = { curiosidade: 'Curioso', brincadeira: 'Brincalhão', timidez: 'Tímido' };
|
||||
</script>
|
||||
|
||||
<div class="console">
|
||||
<section class="agora" aria-label="O que ele está fazendo agora">
|
||||
<div class="quem">
|
||||
<span class="emoji" aria-hidden="true">{v?.emoji ?? '…'}</span>
|
||||
<div>
|
||||
<strong>{v?.nome_humor ?? 'Carregando'}</strong>
|
||||
<small>{fazendo}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="necessidades">
|
||||
{#each NEC as n (n.id)}
|
||||
{@const valor = v?.necessidades[n.id] ?? 0}
|
||||
<div class="nec">
|
||||
<span>{n.nome}</span>
|
||||
<span class="barra" aria-hidden="true"><span style:width="{valor}%" class:baixa={valor < 35}></span></span>
|
||||
<span class="mono">{valor}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<dl class="fatos">
|
||||
<div><dt>Próxima decisão</dt><dd class="mono">{!v?.config.ligado ? 'modo vida desligado' : proximo === null ? '—' : `em ${proximo} s`}</dd></div>
|
||||
<div><dt>Última decisão</dt><dd>{#if v?.ultima}{v.ultima.texto}<small class="porque">{hora(v.ultima.horario)}{v.ultima.porque ? ' · ' + v.ultima.porque : ''}</small>{:else}—{/if}</dd></div>
|
||||
<div><dt>Presença</dt><dd>{estado.pir?.ativo ? 'sensor vendo alguém' : estado.pir?.presenca?.estado === 'ausente' ? 'ninguém por perto' : 'ninguém agora'}</dd></div>
|
||||
<div><dt>Madrugada</dt><dd>{noite}</dd></div>
|
||||
{#if v?.personalidade}
|
||||
<div><dt>Jeito</dt><dd>{Object.entries(v.personalidade).map(([k, x]) => `${TRACO[k] ?? k} ${Math.round(x * 100)}%`).join(' · ')}</dd></div>
|
||||
{/if}
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section class="terminal" aria-label="Registro ao vivo">
|
||||
<div class="barra-ferramentas">
|
||||
<div class="filtros" role="group" aria-label="Mostrar">
|
||||
{#each ORIGENS as o (o.id)}
|
||||
<button class="chip o-{o.id}" aria-pressed={ligadas[o.id]} onclick={() => (ligadas[o.id] = !ligadas[o.id])}>{o.nome}</button>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="acoes">
|
||||
<label class="busca"><Search size={14} /><input type="search" placeholder="Procurar" bind:value={busca} aria-label="Procurar no console" /></label>
|
||||
<label class="detalhes"><input type="checkbox" bind:checked={detalhes} /> Detalhes</label>
|
||||
<button class="btn pequeno" onclick={alternarPausa} aria-pressed={pausado}>
|
||||
{#if pausado}<Play size={14} /> Continuar{guardadas.length ? ` (${guardadas.length})` : ''}{:else}<Pause size={14} /> Pausar{/if}
|
||||
</button>
|
||||
<button class="btn pequeno fantasma" onclick={() => { linhas = []; guardadas = []; }} aria-label="Limpar a tela"><Trash2 size={14} /></button>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="linhas" aria-live="polite">
|
||||
{#each visiveis as l (l.id)}
|
||||
<li class:detalhe={l.nivel === 'detalhe'}>
|
||||
<time class="mono">{hora(l.horario)}</time>
|
||||
<span class="tag o-{l.origem}">{ORIGENS.find((o) => o.id === l.origem)?.nome ?? l.origem}</span>
|
||||
<span class="texto">{l.texto}{#if l.motivo}<span class="motivo">porque {l.motivo}</span>{/if}</span>
|
||||
</li>
|
||||
{:else}
|
||||
<li class="vazio">Nada por aqui ainda. Cada decisão dele aparece aqui na hora, com o motivo.</li>
|
||||
{/each}
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.console {
|
||||
display: grid;
|
||||
gap: var(--e-4);
|
||||
}
|
||||
.agora {
|
||||
display: grid;
|
||||
gap: var(--e-4);
|
||||
padding: var(--e-4);
|
||||
border: 1px solid var(--borda);
|
||||
border-radius: var(--raio-lg);
|
||||
background: var(--superficie);
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.agora {
|
||||
grid-template-columns: minmax(180px, 0.8fr) minmax(220px, 1fr) minmax(0, 2fr);
|
||||
align-items: start;
|
||||
}
|
||||
}
|
||||
.quem {
|
||||
display: flex;
|
||||
gap: var(--e-3);
|
||||
align-items: center;
|
||||
}
|
||||
.quem .emoji {
|
||||
font-size: 2.4rem;
|
||||
line-height: 1;
|
||||
}
|
||||
.quem strong {
|
||||
display: block;
|
||||
font-size: var(--t-lg);
|
||||
}
|
||||
.quem small {
|
||||
color: var(--texto-2);
|
||||
}
|
||||
.necessidades {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
.nec {
|
||||
display: grid;
|
||||
grid-template-columns: 64px 1fr 28px;
|
||||
gap: var(--e-2);
|
||||
align-items: center;
|
||||
font-size: var(--t-xs);
|
||||
color: var(--texto-2);
|
||||
}
|
||||
.barra {
|
||||
height: 6px;
|
||||
border-radius: var(--raio-pill);
|
||||
background: var(--superficie-3);
|
||||
overflow: hidden;
|
||||
}
|
||||
.barra span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background: var(--destaque);
|
||||
transition: width var(--transicao);
|
||||
}
|
||||
.barra span.baixa {
|
||||
background: var(--aviso);
|
||||
}
|
||||
.nec .mono {
|
||||
text-align: right;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.fatos {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: var(--e-2) var(--e-4);
|
||||
margin: 0;
|
||||
}
|
||||
.fatos div {
|
||||
min-width: 0;
|
||||
}
|
||||
.fatos dt {
|
||||
font-size: var(--t-xs);
|
||||
color: var(--texto-3);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.fatos dd {
|
||||
margin: 2px 0 0;
|
||||
font-size: var(--t-sm);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.porque {
|
||||
display: block;
|
||||
color: var(--texto-3);
|
||||
font-size: var(--t-xs);
|
||||
}
|
||||
.terminal {
|
||||
border: 1px solid var(--borda);
|
||||
border-radius: var(--raio-lg);
|
||||
background: var(--fundo-2);
|
||||
overflow: hidden;
|
||||
}
|
||||
.barra-ferramentas {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--e-2) var(--e-3);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--e-3);
|
||||
border-bottom: 1px solid var(--borda);
|
||||
background: var(--superficie);
|
||||
}
|
||||
.filtros, .acoes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
.chip {
|
||||
min-height: 28px;
|
||||
padding: 0 var(--e-3);
|
||||
border: 1px solid var(--borda);
|
||||
border-radius: var(--raio-pill);
|
||||
background: transparent;
|
||||
color: var(--texto-3);
|
||||
font: 500 var(--t-xs) var(--fonte);
|
||||
cursor: pointer;
|
||||
}
|
||||
.chip[aria-pressed='true'] {
|
||||
color: var(--texto);
|
||||
border-color: var(--cor);
|
||||
background: color-mix(in srgb, var(--cor) 14%, transparent);
|
||||
}
|
||||
.busca {
|
||||
display: inline-flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
padding: 0 var(--e-2);
|
||||
border: 1px solid var(--borda);
|
||||
border-radius: var(--raio-sm);
|
||||
color: var(--texto-3);
|
||||
}
|
||||
.busca input {
|
||||
width: 140px;
|
||||
min-height: 28px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--texto);
|
||||
font: inherit;
|
||||
font-size: var(--t-xs);
|
||||
outline: none;
|
||||
}
|
||||
.detalhes {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
font-size: var(--t-xs);
|
||||
color: var(--texto-2);
|
||||
}
|
||||
.linhas {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: var(--e-2) 0;
|
||||
max-height: min(70vh, 900px);
|
||||
overflow-y: auto;
|
||||
font-size: var(--t-sm);
|
||||
}
|
||||
.linhas li {
|
||||
display: grid;
|
||||
grid-template-columns: 72px 104px minmax(0, 1fr);
|
||||
gap: var(--e-3);
|
||||
align-items: baseline;
|
||||
padding: 5px var(--e-4);
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--borda) 45%, transparent);
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.linhas li {
|
||||
grid-template-columns: 64px minmax(0, 1fr);
|
||||
}
|
||||
.linhas li .texto {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
.linhas li.detalhe {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.linhas li.vazio {
|
||||
display: block;
|
||||
color: var(--texto-3);
|
||||
}
|
||||
time {
|
||||
color: var(--texto-3);
|
||||
font-size: var(--t-xs);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.tag {
|
||||
justify-self: start;
|
||||
padding: 1px 8px;
|
||||
border-radius: var(--raio-pill);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: color-mix(in srgb, var(--cor) 70%, var(--texto));
|
||||
background: color-mix(in srgb, var(--cor) 16%, transparent);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.texto {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.motivo {
|
||||
display: block;
|
||||
margin-top: 1px;
|
||||
font-size: var(--t-xs);
|
||||
color: var(--texto-3);
|
||||
}
|
||||
.o-vida { --cor: var(--destaque); }
|
||||
.o-sentidos { --cor: var(--ok); }
|
||||
.o-celular { --cor: #7aa2ff; }
|
||||
.o-ia { --cor: #c084fc; }
|
||||
.o-aprendizado { --cor: #fb923c; }
|
||||
.o-ideias { --cor: var(--aviso); }
|
||||
.o-sistema { --cor: var(--texto-3); }
|
||||
</style>
|
||||
Reference in New Issue
Block a user