dashboard management ✦ jornn

painel de controle

dashboard management ✦ jornn

[clique para exibir]
offline
Status do pipeline
Anytype Astro
carregando...
Anytype Alegoria
carregando...
Cache API
Cron
a cada 10min
📋 Dados AnyType
🎮 Jogando agora
carregando...
📅 Calendário
carregando...
Analytics Cloudflare
Requests (24h)
últimas 24 horas
Requests (7d)
últimos 7 dias
Requests (30d)
total mensal
Erros (30d)
total de erros no worker
Acessos (páginas e visitantes)
Total de acessos
desde o início do rastreamento
Acessos hoje
páginas visitadas hoje
Visitantes únicos
visitantes diferentes hoje
Países / Regiões
Carregando dados de localização...
KV Usage (CURATOR_KV)
Leituras (reads)
0 / 100.000
Escritas (writes)
0 / 1.000
Deleções (deletes)
0 / 1.000
Listagens (lists)
0 / 1.000
Encurtador de Links

Crie links curtos com URL personalizada e acompanhe acessos. Formato: /{slug} (ex: /discord). /l/{slug} continua funcionando.

Slug Destino Acessos Criado Ações
Editar link
Live Status — detecção de livestream

Detecta automaticamente (cron a cada 10min) quando você está ao vivo em Twitch, Kick ou YouTube e exibe a barra AO VIVO no topo de /gg. Use a simulação abaixo para testar a barra sem estar em live.

verificando…
Canais
Simulação (testar a barra sem estar em live)

A barra mostra "ao vivo agora em:" + botões de TODAS as plataformas ao vivo (cada um na cor da plataforma, abre a live em nova aba). O gradiente da barra usa a cor da plataforma de maior prioridade (Twitch → Kick → YouTube). Sem título/jogo.

Console
admin@jornn — bash
admin@jornn:~$ Bem-vindo ao painel. Digite help para comandos.
$
Ações rápidas
Última ação
Nenhuma ação executada ainda.
⏱ Último cron
carregando...
Publicação de artigos
Novo artigo
/gg/artigos/
Nenhum artigo publicado ainda.
Nenhum artigo publicado ainda.
Nenhum artigo publicado ainda.
Prévia do artigo
Wiki — Documentação do Projeto

Documentos de referência em docs/ — veja também o painel Documentos ao lado.

1. Projetos #

O ecossistema Astroverso tem 4 projetos, cada um com tema visual e rota próprios.

ProjetoRotaTemaDescrição
jornn / violeta Página principal (Hub)
Astrojorn /gg violeta #8b5cf6 Games, streams, setup
Alegoria /alegoria amber #f9a21e Portfólio design e vídeo
astroPcismo /pcismo verde neon #00ffa3 Hardware e venda de produtos
Dashboard /carneiro violeta Admin (esta página)

jornn (Hub) — /

Página principal do ecossistema. Sem navbar — apenas o título com gradiente animado, cards de navegação para os projetos e um terminal de comandos (!carneiro, !clear). Cursor customizado ativo (tema hub). Neon glow oscilante nos cards ao hover.

Astrojorn — /gg

Página de games e streams. Navbar .site-nav com sombra violeta + links para YouTube, Twitch, Kick, Discord. Hero .ticket animado com gradiente + grid parallax + meteoros. Dados do Anytype principal. Sub-páginas: /jogos (catálogo), /setup (hardware), artigos em /gg/artigos/{slug}. Componentes Astro: CuratorCard, GameCards, SetupCards, Commands, Calendar.

Alegoria Criativa — /alegoria

Portfólio de design e vídeo. Navbar .site-nav com sombra amber. Hero com gradiente animado + noise overlay + Three.js shader. Efeito radial reveal nos cards. Cursor customizado ativo (tema alegoria). Dados do Anytype Alegoria. Artigos em /alegoria/artigos/{slug}. Componente: AlegoriaPortfolio.

astroPcismo — /pcismo

Hardware e venda de produtos. Tema pcismo (preto, branco e verde neon #00ffa3, contornos totalmente quadrados). Navbar .site-nav com breadcrumbs + seções. Grupos de promoções no topo (botões Telegram, WhatsApp, Discord). Hero "ASTROPCISMO" com subtítulo "vivendo o pcismo ao extremo!". Vitrine de produtos com botão de compra. Sub-páginas: /produtos (catálogo), /artigos + /artigos/{slug}. Dados de produtos em src/data/produtos.js (estático). Artigos: KV tipo pcismo + fallback Content Collection.

Dashboard — /carneiro

Painel administrativo (esta página). Rota privada sem links no site. Autenticação: token + pergunta de segurança. Pipeline status, analytics Cloudflare, publicação de artigos, terminal, wiki, documentação.

2. Design System #

Temas gerenciados por variáveis CSS em src/styles/theme.css conforme atributo data-theme no <html>. Astro gera HTML estático com zero frameworks no frontend.

Design Tokens

Cada tema define variáveis CSS no seletor [data-theme="..."]:

--bg: #080b12;        // fundo principal
--surface: #131a2b;    // superfície de cards
--line: #3d4f7a;       // bordas
--text: #eae8f5;       // texto
--accent: #8b5cf6;     // cor de destaque (violeta)
--accent-soft: #a78bfa;
--nav-accent: rgb(139,92,246);
--radius-s: 8px;
--font-display: 'Space Grotesk', ...;
--font-mono: 'JetBrains Mono', ...;

Layouts (Astro)

LayoutUsoCaracterísticas
BaseHtml.astroShell de todosHTML base, cursor customizado, footer, scroll progress
HubLayout.astroPágina /Sem navbar, gradient text, terminal, project cards
AstroLayout.astro/gg/*Nav completa, hero ticket, meteoros, grid parallax
AlegoriaLayout.astro/alegoria/*Nav completa, noise overlay, Three.js, radial reveal
PcismoLayout.astro/pcismo/*Nav completa, preto/branco/verde neon, cantos quadrados, vitrine de produtos
ArtigoLayout.astroArtigosÍndice flutuante h2–h4, hero com data/tags, footer nav

Componentes Astro

ComponenteDescrição
CuratorCardCard de curadoria Steam com dados da API
GameCardsGrid de jogos categorizados por status
SetupCardsCards de hardware/periféricos
CommandsGrid de comandos de chat
CalendarLista de eventos agendados
AlegoriaPortfolioGrid de design + vídeo com fallback
TagPillTag estilizada colorida
HardwareCarouselCarrossel animado de peças em destaque em /gg/setup (loop infinito, modal)
LiveAnnouncementBarBarra "AO VIVO AGORA EM:" com polling adaptativo e efeito ARGB
ProductCardCard de produto do astroPcismo (spec mono, botão de compra com glow verde neon)

Animações

AnimaçãoOndeDescrição
fadeInUpGlobal (.reveal)Opacidade 0→1 + translateY 24px na viewport
gradientJornnHub titleGradiente + hue-rotate infinito no "jornn"
neonOscillateHub cardsBox-shadow neon oscilante ao hover
textShiftAstro heroGradiente + hue-rotate no ticket
meteor-dropAstro heroMeteoros com cauda diagonal
navSlideInSite navSlide dos links ao carregar
carousel-scrollHardwareCarouselLoop infinito direita→esquerda do carrossel de hardware
liveArgbSweepLiveAnnouncementBarSweep arco-íris passando pelo card AO VIVO (mix-blend-mode:screen)
Inner-Reveal inner-revealCards/listas/containers de dadosReveal interno em cascata — os itens DENTRO de um card (tags, labels, linhas, badges) fazem fade + slide escalonado quando o container fica visível. Receita completa em docs/DESIGN-SISTEMA.md (base sempre ativa + .revealed + stagger --r-delay)

Cursor Customizado

Ativo nos temas hub e alegoria. Círculo de 20px com mix-blend-mode: difference que expande para 60px ao hover em elementos interativos. Desativado no tema Astro.

3. Pipeline de Dados #

3 camadas de cache com requisição condicional e proteção contra dados corrompidos. Endpoints SSR em src/pages/api/ (worker/ é referência legada).

Cache

CamadaVelocidadePersistência
Cache APImsEfêmera (edge)
KV~5msPersistente
Anytype~200-500msFonte original

Requisição Condicional

ETag / Last-Modified → If-None-Match / If-Modified-Since. Se Anytype responder 304, pula tudo. SHA-256 hash como fallback defensivo.

Qualidade de Dados

QualidadeCritérioCache TTL
okTudo funcionando10min (SWR 1h)
lowConfidence1 warning10min
degraded2+ warnings30s (SWR 5min)

KV nunca sobrescrito com dados degraded. Só persiste ok ou lowConfidence.

Cron

A cada 10 minutos: fetch condicional Anytype principal + Alegoria + tentativa de curadoria Steam. Se 304, apenas atualiza TTL do Cache API.

Refresh Manual

POST /api/anytype/refresh ou POST /api/anytype/alegoria/refresh com header X-Refresh-Token. Força ignorar ETag/hash, baixa HTML completo, parseia e grava KV + Cache API.

APIs (endpoints SSR em src/pages/api/)

EndpointMétodoFunção
/api/anytypeGETDados do Anytype principal (3 camadas de cache)
/api/anytype/authPOSTAutenticação do painel
/api/anytype/refreshPOSTRefresh forçado do Anytype principal
/api/anytype/alegoriaGETDados do Anytype Alegoria
/api/anytype/alegoria/refreshPOSTRefresh forçado da Alegoria
/api/curatorGETDados da curadoria Steam
/api/curator/refreshPOSTRefresh da curadoria Steam
/api/article/publishPOSTCria/atualiza artigo (dashboard → KV)
/api/article/[type]GETLista metadados de artigos de um tipo
/api/article/[type]/[slug]GET / DELETELê ou exclui um artigo
/api/linksGET / POSTLista ou cria links curtos (encurtador)
/api/links/[slug]PATCH / DELETEAtualiza ou exclui um link curto
/{slug}GETRedireciona 302 para o destino (link curto na raiz) + conta acesso
/l/[slug]GETRedireciona 302 (formato legado — continua funcionando)
/api/carousel/itemsGET / POSTLista (público) ou cria itens do carrossel de hardware (auth)
/api/carousel/items/[id]PATCH / DELETEAtualiza ou exclui um item do carrossel
/api/carousel/reorderPOSTReordena itens do carrossel (lista de ids)
/api/uploadPOSTUpload de imagem (magic bytes, máx 2MB) → KV
/api/image/[name]GETServe imagem enviada (cache imutável 1 ano)
/api/live-status/currentGETStatus atual da live (polling, refresh on-demand >2min, s-maxage=30)
/api/live-status/configGET / PUT / PATCH / DELETEConfiguração de canais/enabled/simulate (auth)
/api/live-status/simulatePOSTSimula ao vivo em uma ou mais plataformas (auth)
/api/live-status/refreshPOSTRe-checa canais e grava status no KV (cron + botão)
/api/analyticsGETMétricas Cloudflare (requer auth + secrets)
/api/cronGETStatus da última execução do cron
/api/settings/cloudflareGET / POSTConfiguração Cloudflare (token mascarado)

4. Sistema de Artigos #

Artigos têm duas fontes resolvidas em cascata na rota SSR: KV (publicados por esta página, com Markdown renderizado via marked) e Content Collections (arquivos .md em src/content/) como fallback.

Resolução na rota [...slug]

  1. KV: CURATOR_KV.get("article:{type}:{slug}") → renderiza com marked
  2. Fallback: getCollection("{type}-artigos")render(post)
  3. Nada encontrado → 404 com página de diagnóstico

Rotas

  • /gg/artigos/{slug}, /alegoria/artigos/{slug} e /pcismo/artigos/{slug}
  • Rotas antigas /artigo/{slug} → redirect 301

Estrutura (fallback Content Collection)

src/content/
├── astro-artigos/       # Artigos do Astrojorn (fallback)
│   ├── meu-primeiro-post.md
│   └── dicas-streaming.md
├── alegoria-artigos/    # Artigos da Alegoria Criativa
│   └── design-identidade-visual.md
└── pcismo-artigos/      # Artigos do astroPcismo (fallback)
    └── guia-de-setup.md

Schema (Zod — src/content.config.js)

title: string (obrigatório)
date: date (obrigatório)
tags: string[] (opcional, padrão [])
description: string (opcional)

Fluxo de Publicação

  1. Pelo dashboard (recomendado): aba Publicação → preencher título, tags e conteúdo em Markdown → "Publicar artigo" → salvo em KV, atualização imediata
  2. Por arquivo (fallback): criar .md em src/content/{tipo}-artigos/ com frontmatter title/date/tags → Astro gera no build

Layout

Cada artigo usa ArtigoLayout.astro: índice flutuante à direita (headings h2–h4, expande no hover, destaca heading ativo), hero com data publicada/atualizada e tags coloridas, footer com navegação. Colunas lado a lado via <div class="row"><div class="col">.

5. Painel Administrativo #

Esta página. Painel protegido em /carneiro para gerenciar o pipeline de dados e documentos do projeto.

Autenticação

  1. Login com usuário + token (X-Refresh-Token)
  2. Pergunta de segurança com resposta hasheada (SHA-256)
  3. 3 tentativas máximas para a pergunta de segurança
  4. Sessão em sessionStorage + localStorage opcional

Abas

AbaFunção
DashboardStatus do pipeline (Anytype, cache, cron) + Analytics Cloudflare
LinksAtalhos para páginas do site, sub-páginas e redes sociais
EncurtadorLinks curtos /{slug} (criar/editar/excluir + acessos, check de disponibilidade ao vivo e slug reservado)
TerminalConsole bash com comandos: status, refresh, whoami, date
Ações rápidasBotões para refresh Anytype/Alegoria, verificar status
PublicaçãoPublicar/editar/excluir artigos em Markdown (KV)
WikiDocumentação resumida do projeto
DocumentosArquivos de referência docs/ completos
AjudaComandos do terminal e info do pipeline

Analytics

Requer secrets CLOUDFLARE_API_TOKEN e CLOUDFLARE_ACCOUNT_ID. Mostra requests 24h/7d/30d, erros, países (via GraphQL, requer CLOUDFLARE_ZONE_ID), e pageviews por KV tracking (total, daily, unique visitors).

6. Diretrizes de Desenvolvimento #

Convenções para manter consistência no código. Documento completo em docs/diretrizes_ia.md.

Stack

Astro v7 Zero frameworks no frontend HTML/CSS/JS puro Cloudflare Workers KV

Convenções

ContextoEstiloExemplo
Arquivos .astroPascalCaseAstroLayout.astro
Variáveis JScamelCasefetchAndCacheAnytype
ConstantesUPPER_SNAKEANYTYPE_URL
CSS classeskebab-case.site-nav
Chaves KVsnake_caseanytype_main
IDs HTMLkebab-casepanel-dashboard

Regras

  • Sempre verificar layouts existentes antes de criar novos — reutilizar BaseHtml.astro
  • Não adicionar dependências npm sem justificativa (hoje: astro, wrangler, @astrojs/cloudflare, marked)
  • Sempre usar escapeHtml() em dados de fonte externa
  • APIs em src/pages/api/ — não editar worker/ (referência legada)
  • Artigos vêm do KV (dashboard) primeiro; Content Collections são fallback
  • Headings h2–h4 suportados no índice flutuante e no renderMarkdown
  • Colunas lado a lado usam <div class="row"> + <div class="col">
  • var em vez de let/const em escopo de função no JS do dashboard
  • Testar com npm run dev para design, npm run wrangler:dev para teste completo
  • Animação de itens internos de card = inner-reveal (receita em docs/DESIGN-SISTEMA.md) — não criar mecanismo novo; container [data-live-fallback] é revelado pelo lift()

7. Arquivos de Referência #

Todos os documentos de referência estão em docs/. Veja o conteúdo completo no painel Documentos.

ArquivoDescrição
docs/README.mdDocumentação principal — arquitetura, stack, comandos, histórico de bugs
docs/PROJETOS.mdCatálogo dos 4 projetos com rotas, cores, páginas internas
docs/DESIGN-SISTEMA.mdDesign system completo: layouts, tokens, componentes, animações (inclui o nome/receita do Inner-Reveal)
docs/ENGENHARIA.mdEngenharia de correção e desenvolvimento: post-mortems de bugs + padrões por projeto
docs/diretrizes_ia.mdDiretrizes de desenvolvimento para IA: stack, nomenclatura, regras
docs/ADMIN.mdDocumentação do painel administrativo /carneiro
docs/ARTIGOS.mdSistema de artigos (KV via dashboard + Content Collections, Markdown)
src/styles/theme.cssTemas via CSS custom properties (data-theme)
src/pages/api/Endpoints SSR — article, analytics, anytype, curator, cron
wrangler.tomlConfig Cloudflare — KV, cron, assets, compatibility date

8. Cards / Componentes #

Cada card ou componente que exibe dados dinâmicos no site, com origem do dado, sistema de consulta e namespace KV usado.

Comandos disponíveis
Terminal
help — mostra esta ajuda
status — verifica status do pipeline
refresh anytype — força refresh do Anytype Astro
refresh alegoria — força refresh da Anytype Alegoria
refresh all — refresh dos dois ao mesmo tempo
clear — limpa o terminal
whoami — mostra o usuário atual
date — data/hora atual

Acesso
URL: /carneiro
Sessão: sessionStorage + verificação de segurança

Pipeline
3 camadas: Cache API (edge) → KV (persistente) → Anytype (fonte)
Requisições condicionais com ETag / SHA-256 hash
Cron: a cada 10 minutos
Dados degradados nunca sobrescrevem o KV
Gerenciador de Assets

Imagens, links e vídeos de todos os projetos.

Buglog — Bugs, Otimizações e Falhas

Índice com filtros de bugs corrigidos, otimizações e falhas identificadas. Fonte da verdade: docs/ENGENHARIA.md e docs/README.md. IDs estáveis {TIPO}-{NNN} (BUG / OPT / FALHA) usados como referência em commits e documentação.

Visualizador de Modelos

Modelos 3D, mockups e renders dos projetos.

Componentes & Animações

Componentes de UI e animações CSS do design system.

Documentos de Referência

Arquivos em docs/ — documentação completa do projeto.

SiteHUB — Astroverso

Hub de projetos do Astroverso, construído com Astro v7.

Stack

  • Astro v7 — framework (output: static, SSR via adapter Cloudflare)
  • @astrojs/cloudflare — adapter (build gera dist/client + dist/server)
  • Cloudflare Workers — API (ES module format)
  • KV — armazenamento persistente (Anytype + analytics + artigos publicados + cron + live status + cache de borda)
  • D1 — banco SQLite relacional (links curtos links + link_clicks, carrossel carousel_items)
  • marked — parser Markdown para artigos publicados via dashboard
  • Zero frameworks no frontend — HTML/CSS/JS puro gerado pelo Astro

Projetos

Projeto Rota Cor Layout
Hub (jornn) / violeta HubLayout
Astrojorn /gg violeta AstroLayout
Alegoria Criativa /alegoria amber AlegoriaLayout
astroPcismo /pcismo verde neon (preto/branco) PcismoLayout
Dashboard /carneiro violeta HTML puro

Comandos

npm run dev          # astro dev (servidor local de design)
npm run build        # validate-config → astro build → post-build patch
npm run preview      # astro preview
npm run wrangler:dev # build + worker local (teste completo)
npm run deploy       # build + wrangler deploy
npm run verify       # testa todas as rotas com Sec-Fetch-Mode: navigate
npm run release      # deploy + verify (para releases confiáveis)

Detalhes completos do pipeline e das 3 barreiras de segurança em CLAUDE.md na raiz.

Arquitetura

SiteHUB/
├── src/
│   ├── layouts/         # Layouts compartilhados (BaseHtml, AstroLayout, etc.)
│   ├── pages/           # Rotas do site
│   │   ├── index.astro  # Hub (/)
│   │   ├── gg/          # Projeto Astrojorn
│   │   │   ├── index.astro
│   │   │   ├── jogos.astro
│   │   │   ├── setup.astro
│   │   │   ├── artigos.astro
│   │   │   ├── artigos/[...slug].astro  # Artigos (KV + fallback Collection)
│   │   │   └── artigo/[...slug].astro   # Redirect 301 → /gg/artigos/{slug}
│   │   ├── alegoria/    # Projeto Alegoria Criativa
│   │   │   ├── index.astro
│   │   │   ├── artigos.astro
│   │   │   ├── artigos/[...slug].astro
│   │   │   └── artigo/[...slug].astro   # Redirect 301 → /alegoria/artigos/{slug}
│   │   ├── pcismo/      # Projeto astroPcismo (hardware + venda de produtos)
│   │   │   ├── index.astro
│   │   │   ├── produtos.astro
│   │   │   ├── artigos.astro
│   │   │   ├── artigos/[...slug].astro
│   │   │   └── artigo/[...slug].astro   # Redirect 301 → /pcismo/artigos/{slug}
│   │   ├── [...slug].astro  # Links curtos na raiz /{slug} (302, D1+KV)
│   │   └── api/         # Endpoints SSR (article, analytics, anytype, curator, cron, links, carousel, live-status)
│   ├── components/      # Componentes Astro reutilizáveis
│   ├── content/         # Content Collections (fallback de artigos em Markdown)
│   ├── styles/          # Temas CSS (theme.css)
│   └── utils/           # Utilitários (tagColor, gameColors, markdown, embedLinks, links)
├── public/              # Assets estáticos (JS, imagens, dashboard)
├── worker/              # Worker original (referência, não usado mais)
├── docs/                # Documentação
├── astro.config.mjs
└── wrangler.toml

Sistemas recentes

Sistema Descrição
Barra "AO VIVO" (LiveAnnouncementBar.astro) Primeiro elemento da categoria "Onde assistir" em /gg quando o streamer está ao vivo (Twitch/Kick/YouTube). Polling adaptativo client-side (30s offline / 15s ao vivo) + refresh on-demand no /api/live-status/current (re-checa canais quando status >2min, coalescido) + cache de borda s-maxage=30. Efeito ARGB (arco-íris) passando pelo card via ::before com mix-blend-mode:screen. Aparece/some sem refresh (live real ou simulação). Detalhes em docs/ADMIN.md e docs/PROJETOS.md
Encurtador de Links na raiz Links curtos em /{slug} (ex: astrojorn.com/discord), com /l/{slug} legado. Catch-all src/pages/[...slug].astro + util src/utils/links.ts (RESERVED_SLUGS, getShortLink D1→KV, registerClick com ctx.waitUntil). Detecção de conflito: slugs reservados (rotas do site) bloqueados sem tocar no banco
Carrossel de Hardware (HardwareCarousel.astro) Carrossel animado de peças em destaque no fim da categoria Hardware de /gg/setup. D1 (carousel_items) + KV, CRUD via /api/carousel/*, upload de imagem via /api/upload + /api/image/[name]. Sempre anima (ignora prefers-reduced-motion — decisão do dono)
Artigo Multi-tipo Publicação via dashboard para astro, alegoria e pcismo — KV article:{type}:{slug} + fallback Content Collection {type}-artigos
Flash-guard (fim do flash de dados antigos) Containers com fallback estático ([data-live-fallback]) ficam opacity:0 no build até o fetch trocar o innerHTML — o MutationObserver no site.js remove o atributo no momento exato da troca. Timeout de segurança de 2.5s revela o fallback se o fetch falhar; <noscript> re-exibe tudo. Elimina o flash de dados velhos por milissegundos no reload (ex: "PC - Principal" antes de "PC - Hardware")
Inner-Reveal (animação dos itens internos dos cards) Padrão universal de animação por nome: os itens DENTRO de um card (tags, labels, linhas, badges) fazem fade + slide escalonado quando o container fica visível — aplicado aos cards de jogos, setup, comandos, curadoria Steam, painéis de jogos e ao card "games jogados em live". Receita completa (base sempre ativa + .revealed + stagger --r-delay) em docs/DESIGN-SISTEMA.md
Cron auto-refresh (scheduled handler) O post-build injeta async function scheduled() em entry.mjs (cron */10 * * * *) que chama os 4 endpoints de refresh internos via handle() com headers Content-Type: application/json + Origin: https://cron — sem isso o middleware cross-site do Cloudflare bloqueia os POSTs com 403. Grava cron:last_run em KV (estado running antes, done + resultados por endpoint depois); GET /api/cron expõe o estado
Curadoria Steam — dados precisos (CuratorCard) Parser centralizado em src/utils/curator.ts: fetch do endpoint AJAX ajaxgetcuratorrecommendations (estrutura 2026, data-ds-appid + slug da URL) com paginação; contagem via total_count (o total de recomendações que a Steam exibe — hoje 47), não mais filter_tagid_num_all (que soma todas as listas/coleções e mostrava 290). Card redesenhado com aura RGB animada (moldura + anel do avatar + botão "siga a curadoria" — aura no botão, texto limpo) e chips de jogos sem link para a Steam. Cache KV 6h/24h + fallback estático
Buglog + Barra de erros copiável (/carneiro) Aba "Buglog" logo abaixo de Wiki com índice filtrável (tipo / status / projeto / busca textual) de bugs corrigidos, otimizações e falhas — IDs estáveis BUG-### / OPT-### / FALHA-### em public/carneiro/buglog.js (window.BUGLOG), espelho de docs/ENGENHARIA.md. A barra _dbgConsole de erros JS no rodapé ganhou botão à direita que copia todos os erros da sessão (timestamp + linha) via navigator.clipboard com fallback execCommand('copy')
Sync docs → Dashboard (scripts/sync-docs.mjs) Pré-build regenera o painel Documentos de /carneiro a partir de docs/*.md: panes renderizadas via marked + abas, entre marcadores SYNC-DOC-START/END e SYNC-DOC-TABS-START/END. Editar um doc local + npm run deploy = painel atualizado. Valida o Buglog (buglog.js via vm, campos obrigatórios + IDs únicos) contra as referências em docs/ (bidirecional) e docs órfãos (todo docs/*.md precisa de entrada no manifest); qualquer drift falha o build com ERR-DOC-{n}. Idempotente (write-if-changed)

Design System

Ver docs/DESIGN-SISTEMA.md.

Histórico de bugs

2026-07-19 — Migração para Astro

  • Projeto migrado de HTML/CSS/JS solto para Astro framework
  • Documentação reorganizada em docs/
  • Worker.js e site.js mantidos sem alterações de lógica

2026-07-20 — Correções pós-migração

  • Cursor customizado movido para BaseHtml com ativação condicional por tema
  • Efeito neon glow nos cards do hub com box-shadow (sem filter/z-index conflict)
  • Gradiente animado no título "jornn" usando hue-rotate
  • Terminal de comandos no hub (!carneiro, !clear)
  • Correção de encoding UTF-8 na pergunta de segurança do dashboard
  • API de autenticação (/api/anytype/auth) criada

2026-07-26 — Rotas de artigo no plural

  • /astro/artigo/ e /alegoria/artigo/ renomeadas para /astro/artigos/ e /alegoria/artigos/
  • Redirects 301 criados nas rotas antigas
  • Publicação de artigos passa a ser via dashboard (KV) com fallback para Content Collections

2026-08-02 — Publicação manual + Markdown

  • Editor de blocos (Notion-like) revertido para textareas simples funcionais
  • marked adicionado para renderização Markdown dos artigos publicados via KV
  • Headings h2–h4 suportados no índice flutuante e no layout de artigo
  • Dashboard: campos escuros, botão "+ Inserir Imagem", modal de edição ampliado

2026-08-02 — Astrojorn mudou de /astro para /gg

  • Pasta src/pages/astro/ renomeada para src/pages/gg/ → rotas /gg, /gg/jogos, /gg/setup, /gg/artigos/*
  • Redirects 301 de /astro/*/gg/* via src/utils/site-config.ts (config central de URLs legadas)
  • URLs antigas compartilhadas continuam funcionando (301)

2026-08-03 — Barra AO VIVO em tempo real (polling + refresh on-demand)

  • LiveAnnouncementBar.astro agora faz polling adaptativo de /api/live-status/current: 30s offline / 15s ao vivo — a barra aparece e some sem refresh
  • /current faz refresh on-demand dos canais quando o status automático está velho (>2min, coalescido pela timestamp checkedAt)
  • Checks de canais paralelizados (Promise.all) em src/utils/live-status.ts
  • Efeito ARGB (arco-íris) no card via ::before + mix-blend-mode:screen (texto branco segue legível)

2026-08-03 — Encurtador de links na raiz /{slug}

  • Catch-all src/pages/[...slug].astro resolve links curtos na raiz; /l/{slug} continua funcionando (legado)
  • Util src/utils/links.ts centraliza RESERVED_SLUGS, getShortLink (D1→KV) e registerClick (ctx.waitUntil)
  • Slugs reservados (rotas do site) bloqueados antes de tocar KV/D1; dashboard mostra disponibilidade ao vivo ao digitar

2026-08-03 — Fix texto "Monitor 1" em /gg/setup

  • stripHtml do parser Anytype passa a decodificar &#34;" (a linha aparecia como ModeloAzorpa M1RC-27&#34; Quad HD)
  • Linha "Modelo" no card vira linha de valor sem rótulo (evita "ModeloAzorpa..." colado)

2026-08-03 — Carrossel de Hardware em /gg/setup

  • HardwareCarousel.astro com loop infinito animado (CSS carousel-scroll), nome animado, modal com imagem/descrição/link
  • D1 (carousel_items, migração 0002_carousel.sql) como fonte da verdade + KV como cache; CRUD via /api/carousel/*
  • Upload de imagem no dashboard via /api/upload (magic bytes, máx 2MB) servida por /api/image/[name]

2026-08-03 — Novo projeto astroPcismo /pcismo

  • Tema próprio pcismo em src/styles/theme.css + PcismoLayout.astro
  • Home com vitrine de hardware/produtos, catálogo /pcismo/produtos, artigos em /pcismo/artigos/{slug}
  • Card no hub + pcismo na lista de slugs reservados; suporte ao tipo de artigo pcismo no dashboard

2026-08-04 — Rebrand astroPcismo → ASTROPCISMO + redesign

  • Paleta mudou de ciano/laranja para preto, branco e verde neon (--accent: #00ffa3); contornos de botões/cards/objetos agora totalmente quadrados (--radius-*: 0px)
  • Marca "PCISMO" → "ASTROPCISMO"; subtítulo "vivendo o pcismo ao extremo!"
  • Grupos de promoções no topo da home (#promos): botões Telegram, WhatsApp e Discord

2026-08-04 — Cron auto-refresh FIX (403 cross-site)

  • O scheduled handler (injetado pelo post-build) chamava os refreshes internos com POST sem Content-Type/Origin → o middleware isForbiddenCrossOriginRequest do Cloudflare respondia 403 em todos (Anytype, Alegoria, curadoria, live-status)
  • Fix: requests com Content-Type: application/json + Origin: https://cron; verificado com todos os 4 endpoints → 200, allOk=true
  • O handler grava cron:last_run em KV (estado running antes dos refreshes, done + resultados por endpoint depois); GET /api/cron expõe o estado no dashboard

2026-08-04 — Flash-guard — fim do flash de dados antigos

  • Containers com fallback estático exibiam dados de build por milissegundos no reload antes do fetch trazer os dados live (ex: "PC - Principal" antes de "PC - Hardware" em /gg/setup)
  • [data-live-fallback]{opacity:0!important} no BaseHtml.astro + MutationObserver no site.js revela no momento exato da troca de innerHTML; timeout de 2.5s revela o fallback se o fetch falhar; <noscript> re-exibe

2026-08-04 — Parser Curadoria Steam FIX (estrutura 2026)

  • Steam mudou a página de curadoria: curator_recommendation_name/All(\d+) não existem mais
  • Novo parser em api/curator/index.ts + refresh.ts: jogos nos carrosséis data-ds-appid="<id>" (nome vem do slug /app/<id>/<Nome>), total em id="filter_tagid_num_all"; GET /api/curator voltou a retornar 290 jogos com cache KV 6h

2026-08-05 — Inner-Reveal universal + fix "elementos internos estáticos" + grids da Alegoria

  • Inner-Reveal universal: itens internos dos cards (jogos, setup, comandos, curadoria) e dos painéis de jogos passaram a animar em cascata (fade + slide escalonado) quando o card fica visível — nome de referência inner-reveal (receita em docs/DESIGN-SISTEMA.md)
  • Card "games jogados em live" (#historyList): labels e game-tags com o mesmo padrão de cascata (stagger por grupo×posição via --r-delay)
  • Bug "elementos internos estáticos" (deploy da557ef4): a cascata interna rodava fora da tela no momento do swap → lift() do flash-guard agora adia a revelação interna quando o container está dentro de um wrapper .reveal:not(.revealed), disparando só quando o wrapper fica visível
  • Bug grids da Alegoria invisíveis (deploy c5e4f47e): #alegoria-design-grid/#alegoria-video-grid ficavam opacity:0 permanente porque initReveal exclui [data-live-fallback] e o lift() não revelava containers .reveallift() agora adiciona .revealed ao próprio container
  • Post-mortems completos e padrões de engenharia em docs/ENGENHARIA.md

2026-08-05 — Buglog (índice de bugs/otimizações/falhas) + barra de erros copiável

  • Nova aba Buglog no dashboard (/carneiro), logo abaixo de Wiki, com filtros por tipo (Bug / Otimização / Falha), status (corrigido / aplicado / identificado), projeto e busca textual
  • IDs estáveis {TIPO}-{NNN} em public/carneiro/buglog.js (window.BUGLOG) — espelho do histórico de docs/ENGENHARIA.md; IDs não derivam da posição no array
  • Barra inferior de erros JS (_dbgConsole) agora tem botão à direita que copia todos os erros da sessão (timestamp + linha) via navigator.clipboard com fallback execCommand('copy')

2026-08-05 — Sync automático docs → Dashboard Documentos

  • Novo scripts/sync-docs.mjs (pré-build, entre validate-config e astro build) regenera o painel Documentos de /carneiro a partir de docs/*.md — editar um doc local + deploy = painel atualizado, sem edição manual
  • Panes viram o markdown renderizado (marked) entre marcadores SYNC-DOC-START:{key} / SYNC-DOC-END:{key}; abas regeneradas entre SYNC-DOC-TABS-START / SYNC-DOC-TABS-END; CSS .md-render para h1/h2/blockquote/a/img/ol
  • Validações que falham o build com ERR-DOC-{n}: Buglog (vm + 11 campos obrigatórios + IDs BUG/OPT/FALHA-### únicos) ↔ referências em docs/ (bidirecional) e docs órfãos (todo *.md precisa de entrada no manifest)
  • Idempotente (write-if-changed): roda em todo build, só grava se algo mudou

2026-08-05 — Curadoria Steam: contagem precisa (47) + redesign com aura RGB

  • Bug de contagem (BUG-005): o card mostrava 290 "jogos analisados" porque o parser lia filter_tagid_num_all (soma itens de todas as listas/coleções) — o total real de recomendações é o total_count do endpoint AJAX ajaxgetcuratorrecommendations (47)
  • Sistema revisado (OPT-005): lógica movida para src/utils/curator.ts compartilhado (paginação + total_count + lista completa de jogos); index.ts e refresh.ts passaram a usar o util — um único fetch estruturado em vez de scrape da página HTML
  • Redesign do card: moldura, anel do avatar e botão com aura RGB animada (conic-gradient + @property); chips de jogos sem link para páginas da Steam; botão "siga a curadoria" com aura no botão e texto branco limpo (o gradiente animado saiu do texto); prefers-reduced-motion para a aura

Projetos — Astroverso

Catálogo dos projetos gerenciados pelo SiteHUB.

Projeto Rota Cor Layout
Hub (jornn) / violeta HubLayout
Astrojorn /gg violeta AstroLayout
Alegoria Criativa /alegoria amber AlegoriaLayout
astroPcismo /pcismo verde neon (preto/branco) PcismoLayout
Dashboard /carneiro violeta HTML puro

Hub (jornn) — /

Página central do Astroverso. Sem navbar — apenas o título com gradiente animado, cards de navegação e terminal de comandos.

  • Layout: HubLayout.astro
  • Cor: violeta (#8b5cf6)
  • Cursor: customizado ativo
  • Páginas: / (index)
  • Comandos: !carneiro (abre dashboard), !clear/!limpar (limpa terminal)

Astrojorn — /gg

Página de livestreams, setup, jogos e artigos.

  • Layout: AstroLayout.astro
  • Cor: violeta (#8b5cf6)
  • Navbar: .site-nav com links para YouTube, Twitch, Kick, Discord
  • Hero: .ticket animado + grid parallax + meteoros
  • Barra "AO VIVO AGORA EM:": primeiro elemento do conteúdo da categoria "Onde assistir / Livestreams" (logo abaixo do título) — aparece quando o streamer está ao vivo (Twitch, Kick ou YouTube), com dot pulsante, rótulo "ao vivo agora em:", botões de TODAS as plataformas ao vivo (cada um na cor da plataforma, abre a live em nova aba) e efeito ARGB (arco-íris) passando pelo card (::before com gradiente rainbow, mix-blend-mode:screen, 4s linear infinite). Sem título/jogo. Polling adaptativo (30s offline / 15s ao vivo): a barra aparece e some sem refresh (live real ou simulação), com o endpoint /current fazendo refresh on-demand quando o status está velho (>2min) e cache de borda s-maxage=30 Elementos centralizados; no mobile o "ao vivo agora em:" fica na linha de cima e os botões abaixo juntos (quebrando em colunas em ordem se não couber). O gradiente da barra usa a cor da plataforma de maior prioridade (sistema PLATFORM_PRIORITY: Twitch → Kick → YouTube). Simulação no dashboard pode ativar todas as plataformas de uma vez (com platforms[]). Detecção automática via cron (a cada 10min) gravando live:status em KV; simulação manual no dashboard (aba "Live Status"). Detalhes no docs/ADMIN.md
  • Páginas internas:
    • /gg — index com lives, calendário, curadoria Steam
    • /gg/jogos — catálogo de jogos da steam
    • /gg/setup — hardware, periféricos, comandos + carrossel animado de peças em destaque no fim da categoria Hardware (gerenciado pela aba "Carrosséis" do dashboard, dados em D1/KV; sempre anima, ignora prefers-reduced-motion — decisão do dono)
    • /gg/artigos — lista de artigos
    • /gg/artigos/[...slug] — artigos (KV via dashboard + fallback Content Collection)
    • /gg/artigo/[...slug] — redirect 301 para /gg/artigos/{slug}
  • URL legada: /astro/* redireciona 301 para /gg/* (config em src/utils/site-config.ts)
  • Artigos: publicados via dashboard (KV) ou src/content/astro-artigos/*.md

Alegoria Criativa — /alegoria

Página do projeto Alegoria Criativa — portfólio de design e vídeo.

  • Layout: AlegoriaLayout.astro
  • Cor: amber (#f9a21e)
  • Navbar: .site-nav com mesma estrutura do AstroLayout
  • Cursor: customizado ativo
  • Efeitos: noise overlay, Three.js shader, radial reveal nos cards
  • Páginas internas:
    • /alegoria — index com portfólio
    • /alegoria/artigos — lista de artigos
    • /alegoria/artigos/[...slug] — artigos (KV via dashboard + fallback Content Collection)
    • /alegoria/artigo/[...slug] — redirect 301 para /alegoria/artigos/{slug}
  • Artigos: publicados via dashboard (KV) ou src/content/alegoria-artigos/*.md

astroPcismo — /pcismo

Página de hardware e venda de produtos. Paleta preto, branco e verde neon com contornos totalmente quadrados (cantos retos em botões, cards e objetos).

  • Layout: PcismoLayout.astro
  • Cor: verde neon #00ffa3 sobre fundo preto/branco (accent-gradient verde → branco)
  • Navbar: .site-nav com breadcrumbs + seções internas
  • Grupos de promoções: primeiro bloco do conteúdo da home (#promos) — "Entre em nossos grupos de promoções" com botões Telegram, WhatsApp e Discord (ícones coloridos por plataforma)
  • Hero: marca "ASTROPCISMO" com subtítulo "vivendo o pcismo ao extremo!"
  • Vitrine de produtos: cards de hardware com spec, preço e botão de compra (sem backend de checkout — vitrine/loja visual)
  • Páginas internas:
    • /pcismo — index com hero, destaques, categorias e teaser de artigos
    • /pcismo/produtos — catálogo completo com filtro por categoria
    • /pcismo/artigos — lista de artigos
    • /pcismo/artigos/[...slug] — artigos (KV via dashboard + fallback Content Collection)
    • /pcismo/artigo/[...slug] — redirect 301 para /pcismo/artigos/{slug}
  • Artigos: publicados via dashboard (tipo pcismo, KV article:pcismo:*) ou src/content/pcismo-artigos/*.md
  • Produtos: dados em src/data/produtos.js (estático — sem integração com loja real)
  • Engenharia: vitrine estática + artigos via KV/Content Collection (ver docs/ENGENHARIA.md)

Dashboard — /carneiro

Painel administrativo privado. Fora do build do Astro — HTML puro em public/carneiro/.

  • Autenticação: token + pergunta de segurança (SHA-256)
  • Funcionalidades: status do pipeline, analytics (Cloudflare + pageviews), KV usage, cron, publicação/edição/exclusão de artigos (tipos astro, alegoria, pcismo), encurtador de links (criar/editar/excluir links /{slug} na raiz com contagem de acessos, /l/{slug} legado), terminal, wiki, documentação
  • Publicação de artigos: manual, em Markdown — campos escuros (título, tags, conteúdo), botão "+ Inserir Imagem" (insere ![alt](url)), modal de edição amplo (900px)
  • Encurtador de links: D1 (LINKS_DB) como fonte da verdade + KV (LINKS_KV) como cache de borda; redirecionamento 302 na raiz /{slug} (com /l/{slug} legado) e contagem de clique em background; CRUD via /api/links; check de disponibilidade ao vivo no dashboard e bloqueio de slugs reservados
  • Carrosséis (aba própria): gerencia o carrossel animado de hardware/periféricos da página /gg/setup — nome animado, imagem clicável com modal (descrição + link) e botão de link; loop infinito com pausa no hover; D1 (carousel_items) como fonte da verdade + KV como cache; CRUD + reordenação via /api/carousel/*; upload de imagem direto no painel (botão ⬆ UploadPOST /api/upload, validação por magic bytes, máx 2MB, servido por /api/image/[name])
  • Rota sem links no site (conhecida apenas pelo admin)
  • Servido como asset estático, APIs roteadas pelo worker

Engenharia

Padrões de desenvolvimento e post-mortems de bugs de todos os projetos (data flow em 3 camadas, flash-guard, sistema de reveal/Inner-Reveal, pipeline de deploy com 3 barreiras, encurtador, carrossel, live status) em docs/ENGENHARIA.md. A animação dos itens internos dos cards é nomeada inner-reveal — receita em docs/DESIGN-SISTEMA.md.

Design System — Astroverso

Biblioteca de layouts e componentes reutilizáveis no Astro.

Filosofia

  • Astro como gerador de HTML estático
  • Zero frameworks no frontend — HTML/CSS/JS puro
  • 1 layout por projeto — cada tema herda do BaseHtml.astro
  • NavSections pattern — layouts recebem navSections prop para links de navegação interna
  • Footer compartilhadoBaseHtml.astro renderiza o rodapé padrão, aceita footerText (prop) e slot footer-extra

Layouts

BaseHtml.astro

Raiz de todos os layouts. Props: title, description, theme, htmlClass, footerText.

  • <head>: viewport, fontes (Space Grotesk + JetBrains Mono), favicon
  • Cursor customizado — círculo com mix-blend-mode: difference, ativado nos temas hub e alegoria (verificado via data-theme), expande em elementos interativos
  • Footer compartilhado — classe .footer-shared, borda usa var(--accent)
  • Slot footer-extra — para elementos adicionais no rodapé (ex: link admin no hub)
  • Scripts globais inline: LINKS, FALLBACK_DATA, smoothScrollTo, initReveal, initNavOverflow, initVideoModal, initImageModal, etc.
  • initNavOverflow() — esconde links excedentes da .nav-right e cria menu "nave ⋯"
  • Scroll progress bar — barra de progresso de leitura no topo da página
  • Flash-guard — containers com fallback estático ([data-live-fallback]) ficam opacity:0!important até o fetch trocar o innerHTML (o MutationObserver do site.js remove o atributo no momento exato); <noscript> re-exibe tudo com opacity:1!important

AstroLayout.astro

Tema violeta (--accent: #8b5cf6). Páginas do Astrojorn.

  • Props: title, description, navSections (array de {id, text})
  • Nav: .site-nav com nav-left (marca) + nav-right (inicio + seções + redes sociais)
  • Redes sociais na nav: YouTube, Twitch, Kick, Discord (ícones SVG, cor da plataforma no hover via --social-hover)
  • Hero: .ticket animado com gradiente + grid parallax (initHeroGrid())
  • Seções: .section com #lives, #curator, etc.

AlegoriaLayout.astro

Tema amber (--accent: #f9a21e). Portfólio Alegoria Criativa.

  • Props: title, description, navSections (array de {id, text})
  • Nav: mesma estrutura do AstroLayout (inicio + seções + redes sociais)
  • Hero: .hero-brand com gradiente animado + Three.js shader (#canvas-container)
  • Modais: vídeo (YouTube embed) e imagem (fullscreen)

PcismoLayout.astro

Tema preto, branco e verde neon (--accent: #00ffa3, --bg: #050505, accent-gradient verde → branco) com contornos totalmente quadrados (--radius-s/m/l: 0px). Páginas do astroPcismo.

  • Props: title, description, navSections (array de {id, text})
  • Nav: .site-nav com breadcrumbs (nav-left) + inicio + seções (nav-right)
  • Grupos de promoções (#promos): primeiro bloco da home — "Entre em nossos grupos de promoções" com botões Telegram, WhatsApp e Discord (.promo-btn, cantos retos, ícone colorido por plataforma)
  • Hero: marca "ASTROPCISMO" (.pc-brand) com subtítulo "vivendo o pcismo ao extremo!" (.pc-tagline)
  • Vitrine: .product-card com spec em mono, preço e botão de compra com glow verde neon
  • Footer: compartilhado do BaseHtml com footerText próprio

HubLayout.astro

Página central (/). Sem navbar — design limpo com foco no conteúdo.

  • Hero: título "jornn" com gradiente animado via hue-rotate (cores violeta → verde → rosa)
  • Projetos: grid de cards .project-card com gradientes e neon glow oscilante no hover
  • Terminal: campo de comandos (!carneiro, !clear) abaixo dos cards
  • Cursor: customizado ativo (tema hub)
  • Footer: usa o compartilhado do BaseHtml, admin link via slot="footer-extra"

ArtigoLayout.astro

Layout de artigo com índice flutuante, hero e footer de navegação.

  • Props: title, description, date, updatedAt, tags, headings, collection, breadcrumbs
  • Índice flutuante: .art-index na lateral direita (desktop, expande no hover), itens h2–h4, destaca o heading ativo via scroll
  • Hero: .art-hero com título gradiente, meta (data publicada/atualizada) e tags coloridas
  • Corpo: .art-body com estilos para h2–h4, blockquote, code/pre, table, img, hr e colunas (<div class="row"><div class="col">)
  • Footer: .art-foot com links "↑ Início" e "Voltar"
  • Footer texto: customizado via footerText (ex: "Astrojorn — Astroverso: todos os direitos reservados.")

NavSections Pattern

Cada layout que usa .site-nav com navegação interna segue este padrão:

  1. Layout declara const { navSections = [] } = Astro.props;
  2. Página passa navSections={[{ id: 'sec-id', text: 'nome' }]}
  3. Nav renderiza inicio + seções + redes sociais
  4. initNavOverflow() esconde links excedentes em janelas estreitas

Redes sociais: 4 ícones fixos (YouTube, Twitch, Kick, Discord) com target="_blank", cor da plataforma no hover via --social-hover CSS custom property.

Footer Pattern

  • BaseHtml.astro renderiza <footer class="footer-shared">
  • footerText prop: string customizada (padrão: "Astrojorn - Astroverso: ...")
  • footer-extra slot: para elementos extras (ex: link admin no hub)
  • CSS: background:#000; border-top:1px solid var(--accent); color:var(--text-muted)
  • Responsivo: empilha em telas < 600px

Tema / Design Tokens

Definidos em src/styles/theme.css por data-theme:

Tema data-theme Cor de destaque Uso
Hub hub Violeta #8b5cf6 Página inicial
Astrojorn astrojorn Violeta #8b5cf6 Páginas do Astrojorn
Alegoria alegoria Amber #f9a21e Portfólio Alegoria
astroPcismo pcismo Verde neon #00ffa3 sobre preto/branco (cantos quadrados) Hardware/produtos

Tokens comuns: --bg, --surface, --surface-2, --line, --text, --text-muted, --accent, --accent-soft, --nav-accent, --radius-s/m/l, --font-display, --font-mono.

Componentes

Componente Descrição
Navbar .site-nav fixa no topo com nav-left + nav-right + overflow
Hero Banner com gradiente animado + efeitos (varia por tema)
Footer .footer-shared no BaseHtml, compartilhado por todos layouts
Floating Index Índice colapsável na lateral direita (artigos, h2–h4)
Scroll Progress Barra de 2px no topo da página (BaseHtml)
Hero de artigo Título gradiente + meta (data/atualização) + tags
CuratorCard Card de curador com dados da API (fallback estático)
AlegoriaPortfolio Grid de design + vídeo com fetch da API
GameCards Grid de jogos categorizados por status
SetupCards Cards de hardware/periféricos
Commands Grid de comandos de chat
HardwareCarousel Carrossel animado de peças em destaque em /gg/setup (loop infinito, nome animado, modal com imagem/descrição/link)
LiveAnnouncementBar Barra "AO VIVO AGORA EM:" em /gg — polling adaptativo (30s/15s), botões de todas as plataformas ao vivo, efeito ARGB
ProductCard Card de produto do astroPcismo (spec mono, preço em verde neon, botão de compra com glow verde, cantos retos)

Animações

Animação Gatilho Descrição
.fadeInUp .reveal.revealed Opacidade 0→1 + translateY (24px)
gradientJornn .hero-brand (hub) Gradiente + hue-rotate infinito no título "jornn"
neonOscillate .project-card:hover Box-shadow neon oscilante nos cards do hub
.borderPulse .hero-text-box Borda violeta→verde alternando
@keyframes textShift .ticket Gradiente + hue-rotate infinito
@keyframes meteor-drop Meteoros Trajetória diagonal com cauda
navSlideIn .site-nav a Slide dos links da nav ao carregar
shimmer .skeleton Placeholder de carregamento
carousel-scroll HardwareCarousel Loop infinito direita→esquerda do carrossel de hardware (translateX(-100/copies%), velocidade ~55px/s)
liveArgbSweep .live-banner::before Sweep arco-íris passando pelo card AO VIVO (translateX(-100%)→60%, 4s linear infinite, mix-blend-mode:screen)
Inner-Reveal Cards/listas/containers de dados Reveal interno em cascata — os itens DENTRO de um card (labels, tags, linhas, badges) fazem fade + slide escalonado quando o container fica visível. Ver seção dedicada abaixo

Inner-Reveal — animação dos itens internos de um card

Nome de referência: inner-reveal (Reveal Interno em Cascata). Use este nome em código, documentação e pedidos para identificar esta animação: os itens internos de um card/container (tags, labels, linhas, badges, filhos de um grid) aparecem em fade + slide escalonado (cascata) quando o container fica visível — exatamente como os game-tags/labels do card "games jogados em live", os painéis "jogando agora / futuras streams / legenda", os cards de jogos, setup, comandos e a curadoria Steam.

Quando usar

Qualquer card, painel, grid ou lista que exiba dados e deva ter entrada animada em cascata. O padrão é universal no SiteHUB — quem cria um card novo com itens internos deve seguir esta receita.

As 3 peças da receita

1. Regra base SEMPRE ativa — os itens internos começam invisíveis (opacity:0 + leve slide) sem depender de .revealed. Se a base só valesse sob .revealed, a transição dispararia 1→1 (sem animação) quando a classe fosse adicionada.

/* Exemplo genérico: itens do card */
.card > * {
  opacity: 0;
  translate: 0 8px;            /* ou transform: translateY(8px) quando não houver hover no item */
  --r-delay: 0s;
  transition: opacity .35s ease var(--r-delay, 0s),
              translate .35s ease var(--r-delay, 0s),
              transform .2s ease, box-shadow .2s ease;
}

2. Regra de reveal via .revealed — quando o container ganha .revealed, os itens entram:

.card.revealed > * { opacity: 1; translate: 0 0; }

3. Stagger via --r-delay (ou transition-delay) — o delay do stagger é aplicado apenas às propriedades de ENTRADA (opacity/translate/transform do reveal), nunca ao hover:

.card.revealed > *:nth-child(1) { --r-delay: .08s; }
.card.revealed > *:nth-child(2) { --r-delay: .16s; }
.card.revealed > *:nth-child(3) { --r-delay: .24s; }
.card.revealed > *:nth-child(n+9) { --r-delay: .72s; }  /* catch-all p/ listas longas */

Alternativa usada em alguns cards: transition-delay fixo nas regras nth-child (ex: .game-card.revealed>*:nth-child(1){transition-delay:.05s}) — mesmo efeito.

Regras de ouro (armadilhas comuns)

  1. A base opacity:0 vale SEMPRE, não só sob .revealed — senão não há transição.
  2. Item com hover → animar o slide com a propriedade translate (propriedade separada), deixando transform livre para o hover (ex: .game-tag:hover{transform:translateY(-3px) scale(1.06)}). Se o reveal animasse transform, a regra de reveal (mais específica) engoliria o hover.
  3. O container precisa receber .revealed no momento em que fica visível. Dois caminhos:
    • .reveal normalinitReveal (IntersectionObserver no site.js) adiciona .revealed no scroll.
    • Container [data-live-fallback] → o flash-guard (lift() no head do BaseHtml.astro) adiciona .revealed no momento do swap do fetch. O lift() também revela o PRÓPRIO container quando ele é o .reveal (ex: grids da Alegoria) — sem isso ele ficaria opacity:0 para sempre.
  4. Container [data-live-fallback] DENTRO de um wrapper .reveal (ex: #historyList dentro de .games-history.reveal, painéis em /gg/jogos): a revelação interna é ADIADA até o wrapper ganhar .revealed (MutationObserver no wrapper). Se não adiar, a cascata roda fora da tela no momento do swap e o usuário vê o card fadear mas os itens já estáticos. (Ver docs/ENGENHARIA.md → post-mortem "Elementos internos estáticos".)

Onde está implementado

  • CSS global no BaseHtml.astro (bloco <style is:global>):
    • Padrão universal dos cards: .game-card>*, .spec-card>*, .cmd-item>*, .curator-card>*.
    • Painéis: #gamesList, #futureList, #statusLegend (stagger --r-delay).
    • Card "games jogados em live": #historyList (labels + game-tags, stagger grupo×posição).
  • Mecânica JS: initReveal/__revealContainer no site.js + flash-guard lift() no head do BaseHtml.astro.

Como aplicar a um card NOVO (checklist)

  1. No componente, marque os itens internos que devem animar (filhos diretos do container).
  2. Adicione no CSS: base sempre ativa + .revealed + stagger (--r-delay ou transition-delay).
  3. Garanta que o container recebe .revealed:
    • Wrapper .reveal normal → ok (IO cuida).
    • [data-live-fallback] → ok (lift() cuida) — certifique-se de que o container NÃO está em posição que dispare a cascata fora da tela (o deferral do lift() já cobre o caso dentro de wrapper).
  4. Item com hover → use translate no slide e transform no hover.
  5. Verifique com CDP (headless Chrome, cache off) que a base está opacity:0 antes do reveal e que os delays progridem em cascata até opacity:1 (ver docs/ENGENHARIA.md).

Convenções CSS

  • Classes em kebab-case (.site-nav, .article-card)
  • IDs em kebab-case (#scroll-progress, #articleIndex)
  • Mobile-first: breakpoint 600px para empilhamento
  • !important usado apenas em utilitários de override (.nav-social, .nav-hidden)
  • Variáveis CSS para temas via data-theme no <html>

Diretrizes de Desenvolvimento — SiteHUB

Convenções, regras e decisões arquiteturais para desenvolvimento assistido por IA.

1. Stack

  • Astro v7 (output: static + SSR via adapter) — geração de HTML
  • @astrojs/cloudflare — adapter de build
  • Cloudflare Workers — API (ES module format)
  • KV — armazenamento persistente (artigos, analytics, cron)
  • marked — parser Markdown para artigos publicados via dashboard
  • Zero frameworks no frontend — HTML/CSS/JS puro
  • NPM — dependências: astro, wrangler (dev), @astrojs/cloudflare, marked

2. Convenções de código

Contexto Estilo Exemplo
Arquivos .astro PascalCase AstroLayout.astro, BaseHtml.astro
Variáveis/funções JS camelCase artigoPageHtml, fetchAndCacheAnytype
Constantes UPPER_SNAKE ANYTYPE_URL, CACHE_TTL
CSS classes kebab-case .site-nav, .article-card
IDs HTML kebab-case #scroll-progress, #articleIndex
Chaves KV snake_case anytype_main, article_astro:index

JS

  • Funções function name(){} em vez de arrow functions para modulares
  • Arrow functions apenas em callbacks inline
  • === sempre, nunca ==

CSS

  • Variáveis CSS em :root no início de cada bloco <style>
  • Nenhum !important (exceto casos extremos)
  • Mobile-first: breakpoint 600px

3. Regras para IA

  1. Sempre verificar layouts existentes antes de criar novos — reutilize BaseHtml.astro
  2. Não adicionar dependências npm sem justificativa — hoje só existem astro, wrangler, @astrojs/cloudflare e marked
  3. 1 tema por projeto em src/styles/theme.css (data-theme): hub, astrojorn, alegoria, pcismo (artigos usam o tema pai + classe .artigo). Novos projetos adicionam tema + layout próprio
  4. Não quebrar o padrão export default { async fetch... } do worker
  5. Sempre incluir escapeHtml() em dados de fonte externa
  6. Artigos vêm do KV (dashboard) primeiro, Content Collections (src/content/) são fallback
  7. Headings h2–h4 são suportados no índice flutuante e no renderMarkdown — não limitar a h2/h3
  8. ArtigoLayout.astro gerencia índice flutuante, hero e footer — não recriar em páginas
  9. Colunas lado a lado em artigos usam <div class="row"> + <div class="col"> (viram flexbox, empilham em mobile)
  10. Testar com npm run dev para design, npm run wrangler:dev para teste completo
  11. Cursor customizado fica em BaseHtml.astro com ativação por data-theme — não duplicar em layouts filhos
  12. APIs ficam em src/pages/api/ (não editar worker/ — é referência legada)
  13. Animação de itens internos de card = inner-reveal (Reveal Interno em Cascata). Ao criar/alterar um card com itens internos (tags, labels, linhas, badges), use o padrão base sempre ativa (opacity:0) + .revealed + stagger --r-delay/transition-delay descrito em docs/DESIGN-SISTEMA.md. Não criar mecanismo novo. Container [data-live-fallback] é revelado pelo lift() do flash-guard (head do BaseHtml.astro) — nunca remover o atributo manualmente. Item com hover: slide via translate, hover via transform (senão o reveal engole o hover)

4. Fluxo ao adicionar nova página

  1. Crie o layout em src/layouts/ (se novo projeto)
  2. Crie a página em src/pages/ usando o layout
  3. Adicione assets estáticos em public/
  4. Se precisar de API, crie endpoint em src/pages/api/
  5. Documente em docs/

Painel Administrativo — /carneiro

Dashboard privado para gerenciamento de conteúdo do Astroverso.

Acesso

  • Rota: /carneiro
  • Autenticação: Token de refresh + pergunta de segurança (SHA-256)
  • Sessão: sessionStorage (some ao fechar a aba) + localStorage opcional (lembrar)
  • Pergunta de segurança: Resposta hasheada, 3 tentativas máximas

Funcionalidades

Dashboard

  • Status do pipeline (Anytype Astro, Anytype Alegoria, cache, cron)
  • Analytics Cloudflare (requests 24h/7d/30d, erros, total de acessos, visitantes únicos)
  • Pageviews por KV tracking (total, diário, único por IP+UA hash)
  • Mapa de países/regiões via GraphQL (requer CLOUDFLARE_ZONE_ID)

Links

  • Atalhos para todas as páginas do site e sub-páginas (Hub, Astrojorn /gg, Alegoria, Painel, jogos, setup, artigos)
  • Links para redes sociais (YouTube, Twitch, Kick, Instagram, X/Twitter, TikTok)
  • Links para comunidade (Discord, Steam, StreamElements)

Encurtador

  • Encurtador de Links (aba própria) — cria, edita, exclui links curtos com URL personalizada e contagem de acessos
    • Formato: /{slug} na raiz do domínio (ex: astrojorn.com/discordhttps://discord.gg/...). /l/{slug} continua funcionando (legado)
    • Detecção de conflito automática: slugs reservados (rotas do site, diretórios estáticos e rotas de sistema) são bloqueados sem consulta ao banco; o dashboard mostra ao vivo "disponível ✓" / "indisponível — é uma rota do site" / "já em uso por outro link" enquanto você digita
    • Redirecionamento 302 com contagem de clique em background (ctx.waitUntil)
    • Fonte da verdade: D1 (links + link_clicks) · cache de borda: KV (LINKS_KV, TTL 1h)
    • Validação: slug minúsculo (letras/números/hífens, máx. 64), URL http(s) apenas, slugs reservados bloqueados, slug duplicado → 409
    • Lista reservada (em src/utils/links.ts): páginas (astro, alegoria, carneiro, hub, gg), estáticos (assets, _astro, images, js, scripts, favicon, robots, sitemap, 404, manifest…), sistema (api, l, links) e abuso (admin, login, dashboard, www, cdn, status, mail)
    • Rotas: GET /{slug} e GET /l/{slug} (redirect), GET/POST /api/links, PATCH/DELETE /api/links/:slug (auth privado)
    • DELETE/PATCH exigem header Content-Type: application/json (runtime do Workers bloqueia state-changing sem ele)

Carrosséis

  • Carrossel de Hardware/Periféricos (aba própria) — gerencia o carrossel animado da página /gg/setup
    • Loop infinito direita→esquerda, pausa no hover, nome animado, imagem clicável (abre modal com imagem grande + descrição + link) e botão de link abaixo
    • Cada item: name (nome animado), image (URL http(s) ou caminho /...), description (texto do modal), link + link_label (botão abaixo da imagem), active (visível no site), position (ordem)
    • Upload de imagem: botão ⬆ Upload ao lado do campo (no formulário e no modal de edição) envia o arquivo direto para POST /api/upload (auth) — salva em KV (LINKS_KV, chave image:{nome}) e preenche o campo com /api/image/{nome}. Formatos aceitos por detecção de magic bytes: WebP, JPEG, PNG, GIF, AVIF. Máx 2MB. Servido por GET /api/image/[name] com cache imutável (max-age=31536000)
    • Botões de ordenação ↑↓ no painel chamam POST /api/carousel/reorder com a lista completa de ids
    • Fonte da verdade: D1 (carousel_items, migração 0002_carousel.sql) · cache de borda: KV (LINKS_KV, TTL 1h, chave carousel:items)
    • Rotas: GET /api/carousel/items (público, só ativos, cache s-maxage=60), POST /api/carousel/items (auth), PATCH/DELETE /api/carousel/items/:id (auth), POST /api/carousel/reorder (auth), POST /api/upload (auth), GET /api/image/[name] (público)
    • Página consome via fetch client-side (a página /gg/setup é estática): HardwareCarousel.astro esconde o carrossel se não houver itens ativos
    • Implementação do loop: CSS animation carousel-scroll (to { transform: translateX(-100/copies%) }). copies = max(2, ceil((vw+260)/per)+1) com per = nº itens × 260 — cópias suficientes para preencher o viewport. Velocidade constante ~55px/s: duration = max(6, per/55)s. O wrapper é exibido (display='') antes de medir o viewport — com display:none o clientWidth é 0 e o cálculo de cópias quebra (track fica mais estreito que a tela)
    • Sempre anima (ignora prefers-reduced-motion): decisão do dono do site — a regra @media (prefers-reduced-motion:reduce) foi removida do componente para o carrossel animar mesmo com "Efeitos de animação" desligado no Windows (que seta prefers-reduced-motion: reduce e matava a animação). Se quiser respeitar acessibilidade de novo, re-adicionar o bloco no componente
    • Posição na página: fica no fim da categoria Hardware (PC & Periféricos) de /gg/setup. O JS de setup.astro insere as seções dinâmicas (ex: "Outros") depois de #hardwareCarousel (carouselWrap.nextSibling) — se a inserção voltar a ser após #setupContainer, o carrossel cai dentro da seção "Extras/Outros"

Live Status

  • Detecção automática de livestream (aba própria) — mostra quando o streamer está ao vivo em Twitch, Kick ou YouTube e exibe a barra full-width "AO VIVO" como primeiro elemento da categoria "Onde assistir" em /gg
    • Arquitetura cron → KV + polling (custo quase fixo): o cron (a cada 10min, via scheduled handler do post-build) chama POST /api/live-status/refresh, que verifica os canais uma única vez e grava live:status em KV (CURATOR_KV, chave live:status). A página faz polling de GET /api/live-status/current (cache de borda s-maxage=30, sem stale-while-revalidate): 30s offline / 15s ao vivo — a barra aparece e some sem refresh, tanto em live real quanto em simulação
    • Refresh on-demand (coalescido): o endpoint /current re-checa os canais sozinho quando o checkedAt do status automático tem mais de 2min (e enabled e NÃO está simulando) — só o primeiro poll de cada janela de 2min paga a checagem externa (os checks foram paralelizados via Promise.all); os demais apenas leem o KV. Assim a live é detectada em ~1 poll (~30s), sem depender do cron de 10min (que vira fallback)
    • Configuração: chaves live:config em KV — canais (twitch, kick, youtube), enabled (ativa/desativa a checagem automática) e simulate (modo simulação)
    • Multi-plataforma: LiveStatus.statuses é uma lista de plataformas ao vivo ordenada por prioridade (constante PLATFORM_PRIORITY = ['twitch','kick','youtube']; platform é a primária = statuses[0]). A barra exibe botões de TODAS as plataformas ao vivo; a primária define apenas o gradiente da barra (cor). A simulação aceita platforms: ["twitch","kick","youtube"] para simular todas ao mesmo tempo (campo platform legado continua funcionando)
    • Detecção por plataforma: Twitch via Helix (se TWITCH_CLIENT_ID/TWITCH_CLIENT_SECRET no env, com título/jogo) com fallback para o endpoint USher HLS (m3u8 → 200 = ao vivo, 404 = offline, sem API key); Kick via API pública kick.com/api/v2/channels/{slug} (livestream null = offline); YouTube via scrape da página {handle}/live (marcador "isLiveNow":true + título, sem API key)
    • Simulação: aba Live Status → checkboxes de plataformas (Twitch/Kick/YouTube) com botão "todas" + "▶ Simular AO VIVO" / "Parar simulação" — sobrepõe o status automático para testar a barra sem estar em live. Campos título/jogo removidos do painel (a barra não os exibe; a API ainda aceita title/game por compatibilidade)
    • Barra: LiveAnnouncementBar.astrobarra única "AO VIVO AGORA EM:" que fica abaixo do título "Onde assistir / Livestreams - Astrojorn" como primeiro elemento do conteúdo da categoria (inserida após o .section-head na seção #lives), largura do conteúdo com border-radius:14px e cantos arredondados, polling adaptativo (POLL_LIVE=15s / POLL_OFF=30s) — a barra aparece/some sem refresh; render re-executa no lugar, gradiente com cor da plataforma primária (Twitch #9146FF, Kick #53FC18, YouTube #FF0000), dot pulsante e rótulo "ao vivo agora em:". Efeito ARGB (arco-íris) passando pelo card: um ::before com gradiente horizontal (vermelho→amarelo→verde→ciano→azul→magenta, alpha .55, bordas transparentes), width:180%, mix-blend-mode:screen, pointer-events:none (conteúdo continua clicável) e animação liveArgbSweep (translateX(-100%)translateX(60%), 4s linear infinite). O card usa overflow:hidden + isolation:isolate para clipar o sweep nas bordas arredondadas e conter o blend. mix-blend-mode:screen mantém o texto branco sempre branco (legível) mesmo com o sweep sobre o conteúdo. Sem título/jogo — exibe botões de TODAS as plataformas ao vivo (.live-banner-pf, um link por plataforma, cada um na cor da plataforma via --pf-color, com dot + nome, target=_blank rel=noopener, abre a live em nova aba). O gradiente da barra usa a cor da primária (statuses[0], sistema PLATFORM_PRIORITY). Elementos centralizados (justify-content:center). Mobile ≤720px: "ao vivo agora em:" na linha de cima e os botões abaixo juntos na mesma linha centralizados (quebrando em colunas em ordem se não couber — .live-banner-platforms{width:100%}). Responsiva (botões/label menores ≤720px/≤480px)
    • Rotas: GET /api/live-status/current (público, Cache-Control: s-maxage=30, refresh on-demand se status velho >2min), GET/PUT/PATCH/DELETE /api/live-status/config (auth), POST /api/live-status/simulate (auth, body {active, platforms[], title, game}platform único legado também aceito), POST /api/live-status/refresh (auth, usado pelo cron e pelo botão "↻ Verificar agora")

Cron (refresh automático)

  • Gatilho: [triggers] crons = ["*/10 * * * *"] no wrangler.toml. O post-build injeta async function scheduled(event, env, context) no dist/server/entry.mjs — sem ela o Cloudflare só executa fetch.
  • O que executa: chama internamente os 4 endpoints de refresh via handle() em paralelo (Promise.all): POST /api/anytype/refresh, POST /api/anytype/alegoria/refresh, POST /api/curator/refresh e POST /api/live-status/refresh
  • Headers obrigatórios (fix do 403): cada request interno envia Content-Type: application/json + Origin: https://cron + X-Refresh-Token/X-Refresh-Username. Sem Content-Type ou com content-type de form, o middleware isForbiddenCrossOriginRequest do Cloudflare responde 403 e o cron não atualiza nada
  • Estado em KV: grava cron:last_run em CURATOR_KV — estado running (timestamp + runId) antes dos refreshes (se o worker for morto no limite de execução, fica a evidência de que disparou), e done com durationMs, results[] (por endpoint: ok, status, ms) e allOk depois
  • Monitoramento: GET /api/cron (público) retorna { available, timestamp, state, runId, durationMs, results, allOk } ou { available:false } se nunca executou; o dashboard lê esse endpoint no status do pipeline
  • Refresh manual: continua via POST /api/curator/refresh, POST /api/anytype/refresh etc. com o token de refresh (curl ou terminal do painel)

Terminal

  • Console bash com comandos interativos
  • Comandos: help, status, refresh anytype, refresh alegoria, refresh all, clear, whoami, date
  • Atalho: Ctrl+` ou Ctrl+L para abrir o terminal

Ações Rápidas

  • Refresh manual dos dados do Anytype (Astro e Alegoria)
  • Verificação de status
  • Limpeza de cache

Publicação

  • Publicação manual de artigos em Markdown (sem integração Anytype/Notion)
  • Suporte aos tipos: astro, alegoria e pcismo — a tab ativa define o tipo do artigo
  • Editor único compartilhado pelos 3 tipos; o rascunho de cada tipo é preservado ao trocar de tab
  • Campos: título, resumo/descrição (aparece na lista e no meta description), URL editável (slug — auto-derivada do título, mas pode ser alterada manualmente), tags (vírgula) e conteúdo Markdown
  • Barra de ferramentas: negrito, itálico, tachado, código inline, H2/H3, citação, lista, link, imagem por upload (do computador, máx 2MB, via /api/upload) ou por URL, cor do texto, marca-texto e tamanho da fonte — cores/tamanho geram <span style="..."> que o marked preserva no artigo
  • Vídeo embed (): insere /embedyt <link do YouTube> (ou cole o link na linha seguinte). Na página vira um <iframe> 16:9 reproduzível (YouTube nocookie, loading="lazy") + botão "Assistir no YouTube ↗" abaixo. Formato aceitos: youtu.be/ID, youtube.com/watch?v=, /shorts/, /embed/, /live/. Link não-YouTube → card de fallback clicável
  • Bookmark (🔖): insere /bookmark <url> (ou o link na linha seguinte). Na página vira um card estilo Notion: favicon + domínio, título, descrição (og:) e thumbnail (se houver og:image). Os metadados são buscados no publish (com timeout 6s, paralelo, cache KV linkmeta:* por 30 dias) e gravados em bodyRendered — a página renderiza de forma síncrona. O Preview busca ao vivo (mesmo cache). Se a URL falhar/sem metadados, o card cai para domínio + link clicável. Conteúdo externo é escapado (sem risco de HTML injection)
  • Preview: botão "👁 Prever" chama POST /api/article/preview e renderiza com o mesmo marked de produção (paridade exata, inclusive cores e imagens)
  • Listagem de artigos com busca, filtro por tag, visualização lista/grade e resumo no card
  • Ações por artigo: visualizar, editar (abre o editor com o artigo preenchido; createdAt preservado, updatedAt atualizado) e excluir
  • URL editável / renomear: ao mudar o slug durante a edição, o artigo antigo é removido do KV (o slug antigo passa a 404) e o novo passa a renderizar
  • Colisão de URL: publicar com um slug já existente responde 409 e não sobrescreve o artigo existente
  • Artigos persistidos em KV (article:{type}:{slug}) e servidos em /gg/artigos/{slug}, /alegoria/artigos/{slug} e /pcismo/artigos/{slug}

Wiki

  • Documentação resumida do projeto com 7 seções
  • Projetos, Design System, Pipeline, Artigos, Admin, Diretrizes, Arquivos

Documentos

  • Visualização completa de todos os arquivos da pasta docs/
  • Tabs para cada documento: README, Projetos, Design System, Diretrizes IA, Admin, Artigos

Segurança

  • Rota sem links no site (conhecida apenas pelo admin)
  • robots: noindex, nofollow
  • Token via header X-Refresh-Token
  • Verificação de segurança com resposta hasheada (SHA-256)
  • 3 tentativas máximas para a pergunta de segurança
  • Variáveis de ambiente para secrets (CURATOR_REFRESH_TOKEN, CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_ZONE_ID)

Arquivo

O dashboard é servido como HTML puro de public/carneiro/index.html — fora do build do Astro. O worker.js faz a proteção de autenticação antes de servir as APIs.

Sistema de Artigos — Astroverso

Artigos podem vir de duas fontes, resolvidas em cascata na rota SSR:

  1. KV (runtime) — publicados/editados pelo dashboard (/carneiro) via API
  2. Content Collections (build) — arquivos Markdown em src/content/

Rotas

Rota Comportamento
/gg/artigos Lista de artigos do Astrojorn
/gg/artigos/[...slug] Artigo (KV → fallback Content Collection → 404 diagnóstico)
/gg/artigo/[...slug] Redirect 301/gg/artigos/{slug}
/astro/artigos/* Redirect 301/gg/artigos/* (legado)
/alegoria/artigos Lista de artigos da Alegoria
/alegoria/artigos/[...slug] Artigo (KV → fallback Content Collection → 404 diagnóstico)
/alegoria/artigo/[...slug] Redirect 301/alegoria/artigos/{slug}
/pcismo/artigos Lista de artigos do astroPcismo
/pcismo/artigos/[...slug] Artigo (KV → fallback Content Collection → 404 diagnóstico)
/pcismo/artigo/[...slug] Redirect 301/pcismo/artigos/{slug}

Resolução na rota [...slug]

Step 1 — KV: env.CURATOR_KV.get(`article:{type}:{slug}`)   ← publicado via dashboard
Step 2 — Content Collection: getCollection('{type}-artigos').find(id === slug)
Step 3 — 404 com página de diagnóstico (dados do KV, coleção, etc.)

Se o artigo vier do KV, o body é renderizado com marked; se vier da coleção, usa render(post) do Astro.

Publicação via Dashboard (KV)

No painel /carneiro, aba Publicação de artigos (tipos astro, alegoria e pcismo):

  • Campos: título, tags (separadas por vírgula), conteúdo em Markdown
  • "+" Inserir Imagem: abre prompts de URL e alt text e insere ![alt](url) no cursor
  • Ações por artigo: ver, editar (modal 900px com texto completo), excluir
  • Datas: createdAt preservado na edição; updatedAt atualizado a cada save
  • Publicar não requer slug manual — gerado automaticamente do título

API de artigos

Endpoint Descrição
POST /api/article/publish Cria/atualiza artigo ({ slug?, title, tags, type, body, createdAt? })
GET /api/article/[type] Lista metadados (title, tags, createdAt, updatedAt) de um tipo
GET /api/article/[type]/[slug] Retorna o artigo completo
DELETE /api/article/[type]/[slug] Exclui artigo

Publicação via Content Collection (fallback)

Estrutura

src/content/
├── astro-artigos/       # Artigos do Astrojorn (fallback)
│   ├── exemplo-01.md
│   └── ...
├── alegoria-artigos/    # Artigos da Alegoria (fallback)
│   ├── exemplo-01.md
│   └── ...
└── pcismo-artigos/      # Artigos do astroPcismo (fallback)
    ├── exemplo-01.md
    └── ...

Schema definido em src/content.config.js (loader glob + zod).

Frontmatter

---
title: "Título do Artigo"
date: 2026-07-19
tags: ["dev", "design"]
description: "Descrição curta para SEO."
---

Conteúdo do artigo em Markdown.

## Seção

Parágrafo com **formatação** normal.

### Subseção

#### Sub-subseção
Campo Tipo Obrigatório
title string sim
date date sim
tags string[] não (padrão [])
description string não

Renderização Markdown

  • src/utils/markdown.ts — utilitário compartilhado:
    • slugify(text) — gera slugs para headings
    • extractHeadings(body) — extrai headings h2–h4 para o índice flutuante
    • renderMarkdown(body) — converte Markdown → HTML com marked e injeta id nos headings
  • Headings h2, h3 e h4 são suportados no índice flutuante e estilos de artigo
  • Depêndencia: marked (pure ESM, funciona em Cloudflare Workers)

Layout de artigo

Cada página de artigo usa:

  • ArtigoLayout.astro — índice flutuante (h2–h4), hero com data/atualização e tags, corpo estilizado, footer de navegação
  • Props: title, description, date, updatedAt, tags, headings, collection, breadcrumbs
  • Colunas lado a lado: blocos <div class="row"><div class="col">…</div></div> viram flexbox (empilham em mobile)

Como publicar um novo artigo

Pelo dashboard (recomendado)

  1. Acesse /carneiro → aba Publicação
  2. Escolha o tipo (Astro, Alegoria ou astroPcismo)
  3. Preencha título, tags e conteúdo em Markdown (use "+ Inserir Imagem" para imagens)
  4. Clique em Publicar artigo — aparece na lista e em /gg/artigos, /alegoria/artigos ou /pcismo/artigos

Pelo arquivo (fallback)

  1. Crie um arquivo .md em src/content/astro-artigos/, alegoria-artigos/ ou pcismo-artigos/
  2. Preencha o frontmatter com title, date e tags
  3. Escreva o conteúdo em Markdown
  4. O Astro gera a página no build

Diferencial do sistema antigo

Antes Depois
Criar HTML inteiro do zero Publicar em Markdown pelo dashboard, sem deploy
Só Content Collections (precisa rebuild) KV runtime → atualização imediata
Índice manual Gerado automaticamente dos headings (h2–h4)
Tags manuais Via campo de tags no dashboard ou frontmatter

AnyType Integration System

Visão Geral

Sistema automatizado que busca conteúdo de uma coleção AnyType (similar ao Notion) e distribui para páginas, artigos e componentes do site. Substitui o fallback estático no build por dados dinâmicos servidos via API, com cache em KV e fallback em cascata.

Arquitetura

AnyType (coleção remota)
    │
    ▼  GET (com timeout de 15s)
/api/anytype/index.ts  (Cloudflare Workers)
    │
    ├── KV Cache (anytype_cache) → TTL 5min + stale-while-revalidate 1h
    │
    ├── Parser de blocos [prop]
    │       ├── Classificação por título
    │       ├── parseSimpleList() → listas
    │       ├── parseCalendar() → datas de lives
    │       ├── parseCommands() → comandos !cmd
    │       ├── parseGameCategoryBlock() → jogos categorizados
    │       └── parseSetupBlock() → hardware/periféricos/músicas
    │
    └── Resposta JSON → Consumida por páginas via fetch()

Fluxo de Dados

  1. Build (SSR): Componentes Astro são pré-renderizados com dados fallback estáticos (definidos em FALLBACK no API handler).
  2. Client-side (fetch): Cada página faz fetch("/api/anytype") e substitui o conteúdo SSR por dados frescos da API.
  3. Cache: A API verifica KV cache primeiro (5min TTL). Se falhar, busca AnyType direto. Se ambos falharem, retorna fallback estático.

Estrutura da API

Endpoint: GET /api/anytype

Resposta JSON com os seguintes campos:

Campo Tipo Descrição
jogandoAgora string[] Jogos sendo jogados em live atualmente
jogarFuturamente string[] Jogos planejados para futuras streams
calendario string[] Eventos agendados no formato "d Mon, YYYY HHh > Nome"
gamesJogados string[] Categorias e jogos já jogados em live
gamesCategorizados object[] Jogos categorizados com status (jogando/jogado/na fila)
comandosLive string[] Comandos do chat no formato "!cmd > descrição"
setupPerifericos object[] Itens de setup com { titulo, valor, categoria }
statusDosJogos string[] Lista de status possíveis para jogos

Sistema de Blocos [prop]

O AnyType exporta HTML com blocos delimitados por [prop] e [prop end]. O parser (parseGames() em api/anytype/index.ts) extrai e classifica cada bloco:

Regras de Classificação

  1. Título correspondente a regras fixas (TITLE_RULES):

    • jogando em live atualmentejogandoAgora
    • futuras streamsjogarFuturamente
    • games jogados em livegamesJogados
    • calendário / próximas livescalendario
    • status dos jogosstatusDosJogos
    • comandos em livecomandosLive
  2. Título em SETUP_TITLES: pc, monitor, periféricos, áudio, sensibilidade, outros links, musicas, stream settingssetupPerifericos

Seções ## h2 no setup

O parser aceita headings ## (h2) do Anytype para agrupar blocos de setup em seções. Um bloco [prop] de setup que esteja abaixo de um ## Nome ganha o campo secao: Nome:

## Outros
[prop] plataformas
Instagram > https://instagram.com/astrojorn
[prop end]

Na página /gg/setup, o script groupSetupSections() separa itens de Hardware (categoria pc, monitor, periféricos, áudio…) de itens de seções (ex: "Outros"). As seções dinâmicas são renderizadas em grids próprias depois do carrossel de hardware (#hardwareCarousel), para que o carrossel continue no fim da categoria Hardware.

  1. Bloco com > no conteúdo:
    • Se a última parte for um status de jogo (jogando/jogado/hiato/zerado) → gamesCategorizados
    • Senão → setupPerifericos

Formato dos Dados no AnyType

[prop] jogando em live atualmente
Minecraft
Hytale
Don't Starve Together
[prop end]

[prop] musicas
Spotify > https://open.spotify.com/playlist/...
YouTube Music > https://music.youtube.com/playlist/...
[prop end]

[prop] comandos em live
!redes > Todas as redes sociais
!setup > Link da página de setup
!discord > Servidor da comunidade
[prop end]

Sistema de Links (SetupCards)

Detecção Automática de URLs

Quando um valor de item de setup é uma URL (https://...), o sistema automaticamente renderiza um botão clicável (pill verde) em vez de texto simples.

Onde funciona:

  • SetupCards.astro (SSR) — componente Astro
  • setup.astro (fetch) — substituição via JavaScript

Comportamento:

  • Se spec.value corresponde a /^https?:\/\/[^\s]+$/ → renderiza <a class="spec-link-value"> com ↗
  • Se link corresponde a mesma regex (no cat.links) → renderiza como botão
  • O texto do botão é o spec.key, e o href é a URL detectada
  • Links em cat.links (categorias sem specs) também são detectados

CSS (.spec-link-value)

.spec-link-value {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 12px; border-radius: 99px;
  font-family: var(--font-mono); font-size: .78rem; font-weight: 600;
  color: var(--green); text-decoration: none;
  background: color-mix(in srgb, var(--green) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--green) 25%, transparent);
  justify-content: space-between;
}
.spec-link-value:hover {
  transform: translateY(-2px);
  background: color-mix(in srgb, var(--green) 22%, transparent);
  border-color: var(--green);
}

Sistema de Comandos (Commands)

Parsing de Legendas

Comandos podem incluir legenda após separadores. O sistema tenta, em ordem:

  1. > (space + greater-than + space)
  2. > (greater-than sem espaço antes)
  3. - (space + hífen + space)

Exemplos:

  • "!setup > mostra o setup" → cmd=!setup, desc=mostra o setup
  • "!redes>Todas as redes" → cmd=!redes, desc=Todas as redes
  • "!jogos - Lista de jogos" → cmd=!jogos, desc=Lista de jogos

Onde implementado:

  • Commands.astro — componente SSR (linhas 11-23)
  • setup.astro — fetch script (linhas 192-196)

Decodificação de HTML entities

stripHtml decodifica as entities numéricas e nomeadas comuns. Correção recente: &#34;" foi adicionada à cadeia de decodificação (&nbsp;, &amp;, &lt;, &gt;, &quot;, &#34;, &#39;, &#x27;) — sem isso, um modelo de monitor com polegadas aparecia como Azorpa M1RC-27&#34; Quad HD.

No render de /gg/setup, o campo chave "Modelo" é exibido como linha de valor apenas (sem rótulo), evitando que "Modelo" e o valor apareçam colados como ModeloAzorpa....

Páginas que Consomem a API

Página Componentes Dados Usados
/gg GamesList, Calendar, List, GamesHistory, CuratorCard jogandoAgora, calendario, jogarFuturamente, gamesJogados
/gg/jogos GameCards, GamesList, List, StatusLegend jogandoAgora, jogarFuturamente, gamesCategorizados
/gg/setup SetupCards, Commands setupPerifericos, comandosLive

Fallback em Cascata

  1. KV Cache → dados frescos (5min TTL)
  2. AnyType API → dados ao vivo (timeout 15s)
  3. KV Cache (stale) → dados antigos se AnyType falhar
  4. FALLBACK estático → dados hardcoded no api/anytype/index.ts

Arquivos do Sistema

Arquivo Função
src/pages/api/anytype/index.ts API endpoint + parser + cache
src/components/SetupCards.astro Renderiza cards de setup com detecção de links
src/components/Commands.astro Renderiza comandos com legendas
src/pages/gg/setup.astro Página de setup com fetch + URL detection
src/pages/gg/jogos.astro Página de jogos com fetch
src/pages/gg/index.astro Página inicial com calendário e jogos
src/utils/gameColors.js Cores e temas para categorias
wrangler.toml Config KV + vars do Cloudflare

Engenharia — Bugs, Correções e Padrões de Desenvolvimento

Registro de engenharia de correção (como bugs foram diagnosticados e consertados) e de engenharia de desenvolvimento (os padrões arquiteturais usados em todos os projetos do SiteHUB). O objetivo é que qualquer dev/IA consiga, no futuro, entender o porquê das decisões e aplicar os mesmos padrões sem repetir os erros.

Referências: docs/DESIGN-SISTEMA.md (nome e receita do Inner-Reveal), docs/README.md (visão geral).


1. Metodologia de verificação

Todo fix é verificado em 2 níveis:

  1. npm run verifyscripts/verify-routes.mjs testa todas as rotas críticas com Sec-Fetch-Mode: navigate (header real que browsers enviam em navegação). Regressão de config do wrangler.toml (ex: not_found_handling voltar para "404-page") quebra todas as rotas SSR — detectado na hora.
  2. CDP (Chrome DevTools Protocol) headless — scripts temporários scripts/tmp-*.mjs que:
    • spawnam Chrome com --headless=new --remote-debugging-port=<porta> + --user-data-dir isolado;
    • conectam via WebSocket em target.webSocketDebuggerUrl;
    • usam Runtime.evaluate com returnByValue:true para ler computed styles (getComputedStyle(el).opacity, transitionDelay, etc.) e estados de classe;
    • burlam cache de edge com Network.setCacheDisabled + Network.clearBrowserCache antes de navegar (sem isso a produção pode servir CSS/HTML antigo e o teste acusa falso negativo);
    • servem para testar o comportamento real da página em produção, fora de astro dev.

Armadilha conhecida: para medir o estado BASE de um container [data-live-fallback] (opacity 0 antes do reveal), a leitura precisa acontecer no mesmo tick síncrono da injeção do markup — o flash-guard reage via MutationObserver (microtask) e lift() já adiciona .revealed antes do próximo await, mascarando o estado inicial.


2. Post-mortem — "Elementos internos do card estáticos" (2026-08-05)

Sintoma (relato do usuário): o card "games jogados em live" em /gg fazia o fade-in do card, mas os elementos internos (labels e game-tags) apareciam já prontos/estáticos — sem a animação em cascata que os outros cards tinham.

Engenharia da investigação (como o diagnóstico foi feito):

  1. Verificação CDP em produção: mediu-se #historyList (container live) e o wrapper .games-history.reveal.
  2. Estado antes do scroll: elRevealed:false, wrapRevealed:false, L1opacity:"0" → a cascata estava corretamente escondida (opacity 0), então o CSS estava certo.
  3. Após scrollIntoView + 620ms: ainda wrapRevealed:false → o wrapper ainda não tinha revelado (o initReveal libera 1 item por ~120ms no batch-flush; o card demora mais que 620ms).
  4. Conclusão: a cascata rodava fora da tela no momento do swap.

Causa raiz (cadeia):

  • O lift() do flash-guard adicionava .revealed ao container [data-live-fallback] no momento do swap (page load), quando o card ainda estava abaixo da dobra.
  • A cascata interna (--r-delay) rodava naquele instante, invisível.
  • O wrapper .games-history.reveal só fadava depois, quando o IntersectionObserver do initReveal disparava no scroll.
  • Resultado para o usuário: card fadeia, itens já estáticos.

Engenharia da correção (deploy da557ef4):

  • lift() passou a detectar container [data-live-fallback] que está DENTRO de um wrapper .reveal:not(.revealed) via ancestral estrito: el.parentElement.closest('.reveal:not(.revealed)').
  • Nesse caso a revelação interna é adiada: um MutationObserver observa a classe do wrapper e só então aplica .revealed + __revealContainer (fallback de 60s por segurança).
  • Containers que são o próprio .reveal (ex: grids da Alegoria) seguem o caminho imediato — o ancestral closest() parte do parentElement, então o próprio container nunca casa.

Verificação (produção, CDP, cache off):

  • Antes do scroll: elRevealed:false, itens opacity:0.
  • Após scroll + 2s: wrapRevealed:true, elRevealed:true, delays 0.08s/0.16s, itens opacity:1.
  • A checagem anterior (620ms) era cedo demais — sempre esperar o batch-flush completo do initReveal.

Lição: ao animar itens internos de um container carregado via fetch, o reveal precisa estar sincronizado com o momento em que o container fica visível, não com o momento do swap.


3. Post-mortem — Grids da Alegoria invisíveis em produção (2026-08-05)

Sintoma (descoberto durante investigação do bug acima): CDP em produção mostrou #alegoria-design-grid com gridRevealed:false e opacity:"0" permanente, enquanto os cards filhos já estavam .revealed/opacity:1 → o grid inteiro (seção Design Gráfico) ficava invisível.

Causa raiz:

  • initReveal (site.js) pula qualquer .reveal que esteja dentro de [data-live-fallback] (el.closest('[data-live-fallback]')) — e isso inclui o próprio container.
  • O lift() tinha a condição !el.classList.contains('reveal') para adicionar .revealed, assumindo que o IntersectionObserver revelaria containers .reveal. Mas o IO nunca os observa (porque são [data-live-fallback]).
  • Resultado: o grid nunca ganhava .revealed; os cards filhos eram observados pelo __revealContainer (descendentes), por isso apareciam — dentro de um pai invisível.

Engenharia da correção (deploy c5e4f47e):

  • lift() agora adiciona .revealed ao próprio container em ambas as vias (imediata e deferida), independente da classe .reveal — a condição virou só !el.classList.contains('revealed').
  • Justificativa: todo elemento passado ao lift() é um [data-live-fallback], e o initReveal nunca o observa → o lift() é o único caminho capaz de revelá-lo.

Verificação (produção, CDP):

  • /alegoria: design grid e video grid agora revealed/opacity:1.
  • /gg/jogos: painéis deferidos (#gamesList, #futureList, #statusLegend) revelam após scroll.
  • /gg: history card segue com cascata ok. npm run verify 32/32.

Lição: um container que é .reveal e [data-live-fallback] não é revelado nem pelo IO (excluído) nem pelo lift (excluído na condição antiga). Quem revela esse tipo de elemento é o lift(), e ele deve revelar também o próprio container.


4. O Sistema de Reveal (engenharia de desenvolvimento)

Três peças coordenam todas as animações de entrada do site:

Peça Arquivo Papel
initReveal src/scripts/site.js IntersectionObserver que adiciona .revealed a .reveal fora de [data-live-fallback], com batch-flush (debounce 80ms + gap 120ms/item). Também expõe __revealContainer e observa [data-auto-reveal]
Flash-guard lift() head do BaseHtml.astro (script is:inline) Esconde [data-live-fallback] (via CSS opacity:0!important) até o fetch trocar o innerHTML; remove o atributo, adiciona .revealed ao container e chama __revealContainer — com deferral se o container estiver dentro de wrapper .reveal
CSS global BaseHtml.astro (<style is:global>) Base SEMPRE ativa (opacity:0) + reveal via .revealed + stagger --r-delay/transition-delay

Modelo de 3 caminhos de revelação:

  1. .reveal normal (fora de [data-live-fallback]) → initReveal (IO) adiciona .revealed no scroll.
  2. [data-live-fallback] sem wrapper .reveal pendentelift() imediato no swap.
  3. [data-live-fallback] dentro de wrapper .reveal:not(.revealed)lift() adia até o wrapper revelar (caminho 1), para a cascata não rodar fora da tela.

Regras de manutenção do sistema:

  • A regra base de inner-reveal (opacity:0) vale sempre, não só sob .revealed — senão não há transição.
  • Stagger (--r-delay / transition-delay) afeta apenas as propriedades de entrada, nunca o hover.
  • Item com hover anima o slide com translate (propriedade separada) e usa transform no hover — senão a regra de reveal (mais específica) engole o hover.
  • Novo container [data-live-fallback] é pego automaticamente pelo flash-guard (MutationObserver no documentElement). Nunca remova data-live-fallback manualmente fora do lift() — o atributo é a trava que evita o flash de dados antigos.
  • initReveal exclui tudo dentro de [data-live-fallback] de propósito: revelar antes do swap faria os itens "poparem" todos de uma vez quando o guard levantasse.
  • As 3 cópias de site.js (src/scripts/, public/scripts/, public/js/) precisam ficar em sync.

5. Padrões de engenharia por projeto

Data Flow — 3 camadas de cache (todos os projetos)

Cache API (edge) → KV (persistente) → Anytype (fonte original)

  • Requisição condicional com ETag/Last-Modified; SHA-256 hash como fallback defensivo.
  • KV nunca é sobrescrito com dados degraded (só ok/lowConfidence).
  • Fallback estático inline renderizado no build + substituído pelo fetch via flash-guard.
  • Refresh manual via POST /api/anytype/refresh (auth); cron */10 * * * * como fallback.

Flash-guard (todos os projetos)

Containers com fallback estático ([data-live-fallback]) ficam opacity:0!important até o fetch trocar o innerHTML — o script no head remove o atributo no momento exato do swap. Timeout 2.5s revela o fallback se o fetch falhar; <noscript> re-exibe tudo.

Pipeline de deploy — 3 barreiras (todos os projetos)

  1. scripts/validate-config.mjs — pré-build, valida wrangler.toml (assets dir, not_found_handling, bindings KV) → falha com ERR-CFG-{n}.
  2. scripts/post-build.mjs — pós-build, patch no entry.mjs (remove fallbackToAssets + injeta scheduled handler do cron com headers Content-Type/Origin corretos) e verifica o patch.
  3. scripts/verify-routes.mjs — pós-deploy, testa todas as rotas com Sec-Fetch-Mode: navigate. Pipeline recomendado: npm run release (deploy + verify).

Hub (/)

HubLayout.astro, sem navbar. Título com gradiente animado (hue-rotate), cards de projeto com neon glow, terminal (!carneiro, !clear), cursor customizado.

Astrojorn (/gg)

AstroLayout.astro (tema violeta). Dados do Anytype principal. Sub-páginas: /jogos (catálogo), /setup (hardware + carrossel), /gg/artigos/*. Barra "AO VIVO" (LiveAnnouncementBar) com polling adaptativo. Curadoria Steam (CuratorCard). Parser da curadoria atualizado para a estrutura 2026 (data-ds-appid + slug da URL).

Alegoria Criativa (/alegoria)

AlegoriaLayout.astro (tema amber). Portfólio com grids #alegoria-design-grid / #alegoria-video-grid que têm .reveal no próprio container + data-live-fallback — revelados pelo lift() (ver seção 3). Three.js shader, modais de vídeo/imagem, cursor customizado.

astroPcismo (/pcismo)

PcismoLayout.astro (preto/branco/verde neon, cantos quadrados). Produtos são estáticos (src/data/produtos.js) — sem backend de venda. Artigos tipo pcismo (KV + Content Collection).

Dashboard (/carneiro)

HTML puro em public/carneiro/ — fora do build do Astro. Autenticação token + pergunta de segurança (SHA-256). Abas: Dashboard, Links, Encurtador, Carrosséis, Live Status, Publicação, Wiki, Documentos, Ajuda. Wiki estática no próprio HTML (mantida em sync com docs/); painel "Documentos" é um resumo estático de cada doc.

Encurtador de Links

D1 (LINKS_DB) como fonte da verdade + KV (LINKS_KV, TTL 1h) como cache de borda. Catch-all src/pages/[...slug].astro resolve na raiz /{slug} (com /l/{slug} legado). src/utils/links.ts centraliza RESERVED_SLUGS, getShortLink e registerClick (via ctx.waitUntil). Slugs reservados bloqueados sem tocar KV/D1; dashboard mostra disponibilidade ao vivo.

Carrossel de Hardware

D1 (carousel_items) + KV. Loop infinito via CSS translateX(-100/copies%); sempre anima (ignora prefers-reduced-motion — decisão do dono). Medir o viewport antes do wrap ficar display:none (clientWidth=0 quebra o cálculo de cópias).

Live Status

Arquitetura cron → KV + polling: o cron (scheduled handler) verifica os canais uma vez a cada 10min e grava live:status; a página polla GET /api/live-status/current (30s offline / 15s ao vivo), com refresh on-demand coalescido quando o status tem >2min. Multi-plataforma com prioridade (PLATFORM_PRIORITY); efeito ARGB via ::before + mix-blend-mode:screen.


6. Histórico curto de correções de engenharia

Data Bug Deploy Correção
2026-08-05 Contagem de jogos analisados da curadoria errada — 290 em vez de 47 (BUG-005) total_count do endpoint AJAX ajaxgetcuratorrecommendations (era filter_tagid_num_all, que soma itens de todas as listas/coleções)
2026-08-05 Curadoria Steam: fetch via endpoint AJAX + redesign do card com aura RGB (OPT-005) src/utils/curator.ts compartilhado (paginação + total_count); card regenerado: chips sem link e botão "siga a curadoria" com aura RGB no botão
2026-08-05 Aura RGB do card curador não animava em alguns browsers — @property/anima de <angle> sem interpolação e bloco prefers-reduced-motion ocultando a aura (BUG-006) Aura ARGB em sweep (mesmo efeito da barra "AO VIVO AGORA"): ::before com width:180% + linear-gradient arco-íris + mix-blend-mode:screen + animação translateX(-100%→60%) no anel do card, na borda do avatar e na face do botão "seguir"; bloco reduce removido (banner AO VIVO também não tem guard); chips no padrão .game-tag com GAME_COLORS; título "Astroverso"
2026-08-05 Elementos internos do card "games jogados em live" estáticos (BUG-001) da557ef4 lift() adia revelação interna até o wrapper .reveal revelar
2026-08-05 Regressão: revelação interna rodava fora da tela no swap (FALHA-004) da557ef4 Mesma correção do BUG-001 — lift adia a cascata até o wrapper ficar visível
2026-08-05 Grids da Alegoria invisíveis (opacity 0 permanente) (BUG-002) c5e4f47e lift() adiciona .revealed ao próprio container .reveal
2026-08-05 Verificação de animações via CDP headless + cache desabilitado (OPT-003) Chrome headless + Runtime.evaluate + Network.setCacheDisabled/clearBrowserCache
2026-08-04 Cron disparava mas nada atualizava (403 cross-site) (FALHA-001) Headers Content-Type: application/json + Origin: https://cron no scheduled handler
2026-08-04 Flash de dados antigos no reload / race do fetch (FALHA-002) Flash-guard no head (MutationObserver em documentElement) + initReveal pula [data-live-fallback]
2026-08-04 Flash-guard: MutationObserver no head + timeout de segurança 2.5s (OPT-004) Head script observa documentElement; revela no swap + fallback 2.5s
2026-08-04 Parser curadoria Steam quebrado (estrutura 2026) (BUG-003) Novo parser data-ds-appid + slug da URL + total filter_tagid_num_all
2026-08-03 Texto "Monitor 1" colado (&#34; não decodificado) (BUG-004) stripHtml decodifica &#34;"
2026-08-03 Checks de canais paralelizados no live-status (OPT-001) Promise.all em src/utils/live-status.ts
2026-08-03 Polling adaptativo + refresh on-demand na barra AO VIVO (OPT-002) Poll 30s/15s + refresh coalescido + cache s-maxage=30
2026-08-03 Carrossel de hardware sempre anima (ignora reduced-motion) (FALHA-003) Identificado — mantido por decisão do dono

O histórico completo com causa/correção/tags vive na aba Buglog do dashboard (window.BUGLOG em public/carneiro/buglog.js) — ver seção 7.

7. Buglog — índice de bugs, otimizações e falhas

O dashboard tem a aba Buglog (logo abaixo de Wiki) com um índice filtrável de bugs corrigidos, otimizações aplicadas e falhas identificadas. Fonte da verdade: public/carneiro/buglog.js (window.BUGLOG), que espelha o histórico deste documento e de docs/README.md.

Convenção de IDs estáveis {TIPO}-{NNN}:

Prefixo Significado
BUG-### Bug identificado e corrigido
OPT-### Otimização / melhoria aplicada
FALHA-### Falha / incidente identificado

Regras:

  • O ID não deriva da posição no array — ao adicionar uma entrada, use o próximo número do tipo.
  • Campos obrigatórios: id, tipo, status, projeto, data, titulo, causa, correcao, deploy, onde, tags.
  • projeto usa chaves curtas: global, gg, alegoria, pcismo, dashboard.
  • status: corrigido, aplicado ou identificado.
  • Referencie o ID em commits, PRs e na seção 6 para rastrear a correção até o registro.
  • O painel filtra por tipo, status, projeto e busca textual (título, causa, correção, tag, ID, deploy).

Barra de erros do dashboard (_dbgConsole): quando um erro de JS ocorre, uma barra fixa no rodapé mostra a mensagem; o botão à direita copia todos os erros registrados na sessão (com timestamp e linha) via navigator.clipboard com fallback execCommand('copy'), com feedback .

Cloudflare — Configuração da API

Configure as credenciais da API Cloudflare para ativar o Analytics e outras integrações. Os dados ficam salvos no KV e são usados pelo endpoint /api/analytics.

Onde encontrar cada item:
  • API Tokendash.cloudflare.com/profile/api-tokens
    Crie um token com permissão Workers Scripts:Edit e Analytics:Read. Copie o token gerado (só aparece uma vez).
  • Account IDdash.cloudflare.com → visão geral → Account ID no painel direito. Ou em Workers & Pages → visão geral.
  • Zone ID (opcional) — dash.cloudflare.com → clique no domínio → visão geral → Zone ID no painel direito.
    Necessário apenas para dados de países/regiões nos analytics.