Clique para abrir em uma nova aba.
Crie links curtos com URL personalizada e acompanhe acessos. Formato: /{slug} (ex: /discord). /l/{slug} continua funcionando.
| Slug | Destino | Acessos | Criado | Ações |
|---|
Gerencia o carrossel animado da página /gg/setup. Loop infinito direita→esquerda, pausa no hover, clique na imagem abre detalhes. Use ⬆ Upload para enviar uma imagem (WebP/JPEG/PNG/GIF/AVIF, máx 2MB) — ela é salva no site e a URL é preenchida sozinha.
| Imagem | Nome | Link | Ordem | Ativo | Ações |
|---|
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.
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.
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.
| Projeto | Rota | Tema | Descriçã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)
| Layout | Uso | Características |
|---|---|---|
BaseHtml.astro | Shell de todos | HTML base, cursor customizado, footer, scroll progress |
HubLayout.astro | Pá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.astro | Artigos | Índice flutuante h2–h4, hero com data/tags, footer nav |
Componentes Astro
| Componente | Descrição |
|---|---|
CuratorCard | Card de curadoria Steam com dados da API |
GameCards | Grid de jogos categorizados por status |
SetupCards | Cards de hardware/periféricos |
Commands | Grid de comandos de chat |
Calendar | Lista de eventos agendados |
AlegoriaPortfolio | Grid de design + vídeo com fallback |
TagPill | Tag estilizada colorida |
HardwareCarousel | Carrossel animado de peças em destaque em /gg/setup (loop infinito, modal) |
LiveAnnouncementBar | Barra "AO VIVO AGORA EM:" com polling adaptativo e efeito ARGB |
ProductCard | Card de produto do astroPcismo (spec mono, botão de compra com glow verde neon) |
Animações
| Animação | Onde | Descrição |
|---|---|---|
fadeInUp | Global (.reveal) | Opacidade 0→1 + translateY 24px na viewport |
gradientJornn | Hub title | Gradiente + hue-rotate infinito no "jornn" |
neonOscillate | Hub cards | Box-shadow neon oscilante ao hover |
textShift | Astro hero | Gradiente + hue-rotate no ticket |
meteor-drop | Astro hero | Meteoros com cauda diagonal |
navSlideIn | Site nav | Slide dos links ao carregar |
carousel-scroll | HardwareCarousel | Loop infinito direita→esquerda do carrossel de hardware |
liveArgbSweep | LiveAnnouncementBar | Sweep arco-íris passando pelo card AO VIVO (mix-blend-mode:screen) |
Inner-Reveal inner-reveal | Cards/listas/containers de dados | Reveal 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
| Camada | Velocidade | Persistência |
|---|---|---|
| Cache API | ms | Efêmera (edge) |
| KV | ~5ms | Persistente |
| Anytype | ~200-500ms | Fonte 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
| Qualidade | Critério | Cache TTL |
|---|---|---|
ok | Tudo funcionando | 10min (SWR 1h) |
lowConfidence | 1 warning | 10min |
degraded | 2+ warnings | 30s (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/)
| Endpoint | Método | Função |
|---|---|---|
/api/anytype | GET | Dados do Anytype principal (3 camadas de cache) |
/api/anytype/auth | POST | Autenticação do painel |
/api/anytype/refresh | POST | Refresh forçado do Anytype principal |
/api/anytype/alegoria | GET | Dados do Anytype Alegoria |
/api/anytype/alegoria/refresh | POST | Refresh forçado da Alegoria |
/api/curator | GET | Dados da curadoria Steam |
/api/curator/refresh | POST | Refresh da curadoria Steam |
/api/article/publish | POST | Cria/atualiza artigo (dashboard → KV) |
/api/article/[type] | GET | Lista metadados de artigos de um tipo |
/api/article/[type]/[slug] | GET / DELETE | Lê ou exclui um artigo |
/api/links | GET / POST | Lista ou cria links curtos (encurtador) |
/api/links/[slug] | PATCH / DELETE | Atualiza ou exclui um link curto |
/{slug} | GET | Redireciona 302 para o destino (link curto na raiz) + conta acesso |
/l/[slug] | GET | Redireciona 302 (formato legado — continua funcionando) |
/api/carousel/items | GET / POST | Lista (público) ou cria itens do carrossel de hardware (auth) |
/api/carousel/items/[id] | PATCH / DELETE | Atualiza ou exclui um item do carrossel |
/api/carousel/reorder | POST | Reordena itens do carrossel (lista de ids) |
/api/upload | POST | Upload de imagem (magic bytes, máx 2MB) → KV |
/api/image/[name] | GET | Serve imagem enviada (cache imutável 1 ano) |
/api/live-status/current | GET | Status atual da live (polling, refresh on-demand >2min, s-maxage=30) |
/api/live-status/config | GET / PUT / PATCH / DELETE | Configuração de canais/enabled/simulate (auth) |
/api/live-status/simulate | POST | Simula ao vivo em uma ou mais plataformas (auth) |
/api/live-status/refresh | POST | Re-checa canais e grava status no KV (cron + botão) |
/api/analytics | GET | Métricas Cloudflare (requer auth + secrets) |
/api/cron | GET | Status da última execução do cron |
/api/settings/cloudflare | GET / POST | Configuraçã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]
- KV:
CURATOR_KV.get("article:{type}:{slug}")→ renderiza commarked - Fallback:
getCollection("{type}-artigos")→render(post) - 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
- Pelo dashboard (recomendado): aba Publicação → preencher título, tags e conteúdo em Markdown → "Publicar artigo" → salvo em KV, atualização imediata
- Por arquivo (fallback): criar
.mdemsrc/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
- Login com usuário + token (X-Refresh-Token)
- Pergunta de segurança com resposta hasheada (SHA-256)
- 3 tentativas máximas para a pergunta de segurança
- Sessão em sessionStorage + localStorage opcional
Abas
| Aba | Função |
|---|---|
| Dashboard | Status do pipeline (Anytype, cache, cron) + Analytics Cloudflare |
| Links | Atalhos para páginas do site, sub-páginas e redes sociais |
| Encurtador | Links curtos /{slug} (criar/editar/excluir + acessos, check de disponibilidade ao vivo e slug reservado) |
| Terminal | Console bash com comandos: status, refresh, whoami, date |
| Ações rápidas | Botões para refresh Anytype/Alegoria, verificar status |
| Publicação | Publicar/editar/excluir artigos em Markdown (KV) |
| Wiki | Documentação resumida do projeto |
| Documentos | Arquivos de referência docs/ completos |
| Ajuda | Comandos 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
| Contexto | Estilo | Exemplo |
|---|---|---|
| Arquivos .astro | PascalCase | AstroLayout.astro |
| Variáveis JS | camelCase | fetchAndCacheAnytype |
| Constantes | UPPER_SNAKE | ANYTYPE_URL |
| CSS classes | kebab-case | .site-nav |
| Chaves KV | snake_case | anytype_main |
| IDs HTML | kebab-case | panel-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 editarworker/(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"> varem vez delet/constem escopo de função no JS do dashboard- Testar com
npm run devpara design,npm run wrangler:devpara 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 pelolift()
7. Arquivos de Referência #
Todos os documentos de referência estão em docs/. Veja o conteúdo completo no painel Documentos.
| Arquivo | Descrição |
|---|---|
docs/README.md | Documentação principal — arquitetura, stack, comandos, histórico de bugs |
docs/PROJETOS.md | Catálogo dos 4 projetos com rotas, cores, páginas internas |
docs/DESIGN-SISTEMA.md | Design system completo: layouts, tokens, componentes, animações (inclui o nome/receita do Inner-Reveal) |
docs/ENGENHARIA.md | Engenharia de correção e desenvolvimento: post-mortems de bugs + padrões por projeto |
docs/diretrizes_ia.md | Diretrizes de desenvolvimento para IA: stack, nomenclatura, regras |
docs/ADMIN.md | Documentação do painel administrativo /carneiro |
docs/ARTIGOS.md | Sistema de artigos (KV via dashboard + Content Collections, Markdown) |
src/styles/theme.css | Temas via CSS custom properties (data-theme) |
src/pages/api/ | Endpoints SSR — article, analytics, anytype, curator, cron |
wrangler.toml | Config 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.
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:
/carneiroSessão:
sessionStorage + verificação de segurançaPipeline
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
Imagens, links e vídeos de todos os projetos.
Í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.
Modelos 3D, mockups e renders dos projetos.
Componentes de UI e animações CSS do design system.
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, carrosselcarousel_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.mdna 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
markedadicionado 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 parasrc/pages/gg/→ rotas/gg,/gg/jogos,/gg/setup,/gg/artigos/* - Redirects 301 de
/astro/*→/gg/*viasrc/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.astroagora faz polling adaptativo de/api/live-status/current: 30s offline / 15s ao vivo — a barra aparece e some sem refresh/currentfaz refresh on-demand dos canais quando o status automático está velho (>2min, coalescido pela timestampcheckedAt)- Checks de canais paralelizados (
Promise.all) emsrc/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].astroresolve links curtos na raiz;/l/{slug}continua funcionando (legado) - Util
src/utils/links.tscentralizaRESERVED_SLUGS,getShortLink(D1→KV) eregisterClick(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
stripHtmldo parser Anytype passa a decodificar"→"(a linha aparecia comoModeloAzorpa M1RC-27" 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.astrocom loop infinito animado (CSScarousel-scroll), nome animado, modal com imagem/descrição/link- D1 (
carousel_items, migração0002_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
pcismoemsrc/styles/theme.css+PcismoLayout.astro - Home com vitrine de hardware/produtos, catálogo
/pcismo/produtos, artigos em/pcismo/artigos/{slug} - Card no hub +
pcismona lista de slugs reservados; suporte ao tipo de artigopcismono 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
scheduledhandler (injetado pelo post-build) chamava os refreshes internos com POST semContent-Type/Origin→ o middlewareisForbiddenCrossOriginRequestdo 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_runem KV (estadorunningantes dos refreshes,done+ resultados por endpoint depois);GET /api/cronexpõ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}noBaseHtml.astro+MutationObservernosite.jsrevela 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éisdata-ds-appid="<id>"(nome vem do slug/app/<id>/<Nome>), total emid="filter_tagid_num_all";GET /api/curatorvoltou 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 emdocs/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-gridficavamopacity:0permanente porqueinitRevealexclui[data-live-fallback]e olift()não revelava containers.reveal→lift()agora adiciona.revealedao 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}empublic/carneiro/buglog.js(window.BUGLOG) — espelho do histórico dedocs/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) vianavigator.clipboardcom fallbackexecCommand('copy')
2026-08-05 — Sync automático docs → Dashboard Documentos
- Novo
scripts/sync-docs.mjs(pré-build, entrevalidate-configeastro build) regenera o painel Documentos de/carneiroa partir dedocs/*.md— editar um doc local + deploy = painel atualizado, sem edição manual - Panes viram o markdown renderizado (
marked) entre marcadoresSYNC-DOC-START:{key}/SYNC-DOC-END:{key}; abas regeneradas entreSYNC-DOC-TABS-START/SYNC-DOC-TABS-END; CSS.md-renderpara h1/h2/blockquote/a/img/ol - Validações que falham o build com
ERR-DOC-{n}: Buglog (vm+ 11 campos obrigatórios + IDsBUG/OPT/FALHA-###únicos) ↔ referências emdocs/(bidirecional) e docs órfãos (todo*.mdprecisa 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 liafilter_tagid_num_all(soma itens de todas as listas/coleções) — o total real de recomendações é ototal_countdo endpoint AJAXajaxgetcuratorrecommendations(47) - Sistema revisado (
OPT-005): lógica movida parasrc/utils/curator.tscompartilhado (paginação +total_count+ lista completa de jogos);index.tserefresh.tspassaram 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-motionpara 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-navcom links para YouTube, Twitch, Kick, Discord - Hero:
.ticketanimado + 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 (
::beforecom 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/currentfazendo refresh on-demand quando o status está velho (>2min) e cache de bordas-maxage=30Elementos 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 (sistemaPLATFORM_PRIORITY: Twitch → Kick → YouTube). Simulação no dashboard pode ativar todas as plataformas de uma vez (complatforms[]). Detecção automática via cron (a cada 10min) gravandolive:statusem KV; simulação manual no dashboard (aba "Live Status"). Detalhes nodocs/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, ignoraprefers-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 emsrc/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-navcom 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
#00ffa3sobre fundo preto/branco (accent-gradient verde → branco) - Navbar:
.site-navcom 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, KVarticle:pcismo:*) ousrc/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
), 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⬆ Upload→POST /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
navSectionsprop para links de navegação interna - Footer compartilhado —
BaseHtml.astrorenderiza o rodapé padrão, aceitafooterText(prop) e slotfooter-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 temashubealegoria(verificado viadata-theme), expande em elementos interativos - Footer compartilhado — classe
.footer-shared, borda usavar(--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-righte 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]) ficamopacity:0!importantaté o fetch trocar o innerHTML (oMutationObserverdosite.jsremove o atributo no momento exato);<noscript>re-exibe tudo comopacity:1!important
AstroLayout.astro
Tema violeta (--accent: #8b5cf6). Páginas do Astrojorn.
- Props:
title,description,navSections(array de{id, text}) - Nav:
.site-navcom 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:
.ticketanimado com gradiente + grid parallax (initHeroGrid()) - Seções:
.sectioncom#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-brandcom 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-navcom 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-cardcom spec em mono, preço e botão de compra com glow verde neon - Footer: compartilhado do BaseHtml com
footerTextpró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-cardcom 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-indexna lateral direita (desktop, expande no hover), itens h2–h4, destaca o heading ativo via scroll - Hero:
.art-herocom título gradiente, meta (data publicada/atualizada) e tags coloridas - Corpo:
.art-bodycom estilos para h2–h4, blockquote, code/pre, table, img, hr e colunas (<div class="row"><div class="col">) - Footer:
.art-footcom 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:
- Layout declara
const { navSections = [] } = Astro.props; - Página passa
navSections={[{ id: 'sec-id', text: 'nome' }]} - Nav renderiza
inicio+ seções + redes sociais 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)
- A base
opacity:0vale SEMPRE, não só sob.revealed— senão não há transição. - Item com hover → animar o slide com a propriedade
translate(propriedade separada), deixandotransformlivre para o hover (ex:.game-tag:hover{transform:translateY(-3px) scale(1.06)}). Se o reveal animassetransform, a regra de reveal (mais específica) engoliria o hover. - O container precisa receber
.revealedno momento em que fica visível. Dois caminhos:.revealnormal →initReveal(IntersectionObserver nosite.js) adiciona.revealedno scroll.- Container
[data-live-fallback]→ o flash-guard (lift()no head doBaseHtml.astro) adiciona.revealedno momento do swap do fetch. Olift()também revela o PRÓPRIO container quando ele é o.reveal(ex: grids da Alegoria) — sem isso ele ficariaopacity:0para sempre.
- Container
[data-live-fallback]DENTRO de um wrapper.reveal(ex:#historyListdentro 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. (Verdocs/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).
- Padrão universal dos cards:
- Mecânica JS:
initReveal/__revealContainernosite.js+ flash-guardlift()no head doBaseHtml.astro.
Como aplicar a um card NOVO (checklist)
- No componente, marque os itens internos que devem animar (filhos diretos do container).
- Adicione no CSS: base sempre ativa +
.revealed+ stagger (--r-delayoutransition-delay). - Garanta que o container recebe
.revealed:- Wrapper
.revealnormal → 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 dolift()já cobre o caso dentro de wrapper).
- Wrapper
- Item com hover → use
translateno slide etransformno hover. - Verifique com CDP (headless Chrome, cache off) que a base está
opacity:0antes do reveal e que os delays progridem em cascata atéopacity:1(verdocs/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
!importantusado apenas em utilitários de override (.nav-social,.nav-hidden)- Variáveis CSS para temas via
data-themeno<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
:rootno início de cada bloco<style> - Nenhum
!important(exceto casos extremos) - Mobile-first: breakpoint 600px
3. Regras para IA
- Sempre verificar layouts existentes antes de criar novos — reutilize
BaseHtml.astro - Não adicionar dependências npm sem justificativa — hoje só existem
astro,wrangler,@astrojs/cloudflareemarked - 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 - Não quebrar o padrão
export default { async fetch... }do worker - Sempre incluir
escapeHtml()em dados de fonte externa - Artigos vêm do KV (dashboard) primeiro, Content Collections (
src/content/) são fallback - Headings h2–h4 são suportados no índice flutuante e no
renderMarkdown— não limitar a h2/h3 ArtigoLayout.astrogerencia índice flutuante, hero e footer — não recriar em páginas- Colunas lado a lado em artigos usam
<div class="row">+<div class="col">(viram flexbox, empilham em mobile) - Testar com
npm run devpara design,npm run wrangler:devpara teste completo - Cursor customizado fica em
BaseHtml.astrocom ativação pordata-theme— não duplicar em layouts filhos - APIs ficam em
src/pages/api/(não editarworker/— é referência legada) - 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-delaydescrito emdocs/DESIGN-SISTEMA.md. Não criar mecanismo novo. Container[data-live-fallback]é revelado pelolift()do flash-guard (head doBaseHtml.astro) — nunca remover o atributo manualmente. Item com hover: slide viatranslate, hover viatransform(senão o reveal engole o hover)
4. Fluxo ao adicionar nova página
- Crie o layout em
src/layouts/(se novo projeto) - Crie a página em
src/pages/usando o layout - Adicione assets estáticos em
public/ - Se precisar de API, crie endpoint em
src/pages/api/ - 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) +localStorageopcional (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/discord→https://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}eGET /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)
- Formato:
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
⬆ Uploadao lado do campo (no formulário e no modal de edição) envia o arquivo direto paraPOST /api/upload(auth) — salva em KV (LINKS_KV, chaveimage:{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 porGET /api/image/[name]com cache imutável (max-age=31536000) - Botões de ordenação ↑↓ no painel chamam
POST /api/carousel/reordercom a lista completa de ids - Fonte da verdade: D1 (
carousel_items, migração0002_carousel.sql) · cache de borda: KV (LINKS_KV, TTL 1h, chavecarousel:items) - Rotas:
GET /api/carousel/items(público, só ativos, caches-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.astroesconde 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)comper = 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 — comdisplay:noneoclientWidthé 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 setaprefers-reduced-motion: reducee 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 desetup.astroinsere 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
scheduledhandler do post-build) chamaPOST /api/live-status/refresh, que verifica os canais uma única vez e gravalive:statusem KV (CURATOR_KV, chavelive:status). A página faz polling deGET /api/live-status/current(cache de bordas-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
/currentre-checa os canais sozinho quando ocheckedAtdo status automático tem mais de 2min (eenablede NÃO está simulando) — só o primeiro poll de cada janela de 2min paga a checagem externa (os checks foram paralelizados viaPromise.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:configem KV — canais (twitch,kick,youtube),enabled(ativa/desativa a checagem automática) esimulate(modo simulação) - Multi-plataforma:
LiveStatus.statusesé uma lista de plataformas ao vivo ordenada por prioridade (constantePLATFORM_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 aceitaplatforms: ["twitch","kick","youtube"]para simular todas ao mesmo tempo (campoplatformlegado continua funcionando) - Detecção por plataforma: Twitch via Helix (se
TWITCH_CLIENT_ID/TWITCH_CLIENT_SECRETno 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úblicakick.com/api/v2/channels/{slug}(livestreamnull = 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/gamepor compatibilidade) - Barra:
LiveAnnouncementBar.astro— barra ú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-headna seção#lives), largura do conteúdo comborder-radius:14pxe 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::beforecom 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çãoliveArgbSweep(translateX(-100%)→translateX(60%),4s linear infinite). O card usaoverflow:hidden+isolation:isolatepara clipar o sweep nas bordas arredondadas e conter o blend.mix-blend-mode:screenmanté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], sistemaPLATFORM_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")
- Arquitetura cron → KV + polling (custo quase fixo): o cron (a cada 10min, via
Cron (refresh automático)
- Gatilho:
[triggers] crons = ["*/10 * * * *"]nowrangler.toml. O post-build injetaasync function scheduled(event, env, context)nodist/server/entry.mjs— sem ela o Cloudflare só executafetch. - 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/refreshePOST /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. SemContent-Typeou com content-type de form, o middlewareisForbiddenCrossOriginRequestdo Cloudflare responde 403 e o cron não atualiza nada - Estado em KV: grava
cron:last_runemCURATOR_KV— estadorunning(timestamp + runId) antes dos refreshes (se o worker for morto no limite de execução, fica a evidência de que disparou), edonecomdurationMs,results[](por endpoint: ok, status, ms) eallOkdepois - 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/refreshetc. 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,alegoriaepcismo— 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 omarkedpreserva 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 houverog:image). Os metadados são buscados no publish (com timeout 6s, paralelo, cache KVlinkmeta:*por 30 dias) e gravados embodyRendered— 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/previewe renderiza com o mesmomarkedde 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;
createdAtpreservado,updatedAtatualizado) 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
409e 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:
- KV (runtime) — publicados/editados pelo dashboard (
/carneiro) via API - 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
no cursor - Ações por artigo: ver, editar (modal 900px com texto completo), excluir
- Datas:
createdAtpreservado na edição;updatedAtatualizado 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 headingsextractHeadings(body)— extrai headings h2–h4 para o índice flutuanterenderMarkdown(body)— converte Markdown → HTML commarkede injetaidnos 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)
- Acesse
/carneiro→ aba Publicação - Escolha o tipo (Astro, Alegoria ou astroPcismo)
- Preencha título, tags e conteúdo em Markdown (use "+ Inserir Imagem" para imagens)
- Clique em Publicar artigo — aparece na lista e em
/gg/artigos,/alegoria/artigosou/pcismo/artigos
Pelo arquivo (fallback)
- Crie um arquivo
.mdemsrc/content/astro-artigos/,alegoria-artigos/oupcismo-artigos/ - Preencha o frontmatter com title, date e tags
- Escreva o conteúdo em Markdown
- 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
- Build (SSR): Componentes Astro são pré-renderizados com dados fallback estáticos (definidos em
FALLBACKno API handler). - Client-side (fetch): Cada página faz
fetch("/api/anytype")e substitui o conteúdo SSR por dados frescos da API. - 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
Título correspondente a regras fixas (
TITLE_RULES):jogando em live atualmente→jogandoAgorafuturas streams→jogarFuturamentegames jogados em live→gamesJogadoscalendário/próximas lives→calendariostatus dos jogos→statusDosJogoscomandos em live→comandosLive
Título em SETUP_TITLES:
pc,monitor,periféricos,áudio,sensibilidade,outros links,musicas,stream settings→setupPerifericos
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.
- Bloco com
>no conteúdo:- Se a última parte for um status de jogo (jogando/jogado/hiato/zerado) →
gamesCategorizados - Senão →
setupPerifericos
- Se a última parte for um status de jogo (jogando/jogado/hiato/zerado) →
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 Astrosetup.astro(fetch) — substituição via JavaScript
Comportamento:
- Se
spec.valuecorresponde a/^https?:\/\/[^\s]+$/→ renderiza<a class="spec-link-value">com ↗ - Se
linkcorresponde 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:
>(space + greater-than + space)>(greater-than sem espaço antes)-(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: " → " foi adicionada à cadeia de decodificação ( , &, <, >, ", ", ', ') — sem isso, um modelo de monitor com polegadas aparecia como Azorpa M1RC-27" 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
- KV Cache → dados frescos (5min TTL)
- AnyType API → dados ao vivo (timeout 15s)
- KV Cache (stale) → dados antigos se AnyType falhar
- 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:
npm run verify—scripts/verify-routes.mjstesta todas as rotas críticas comSec-Fetch-Mode: navigate(header real que browsers enviam em navegação). Regressão de config do wrangler.toml (ex:not_found_handlingvoltar para"404-page") quebra todas as rotas SSR — detectado na hora.- CDP (Chrome DevTools Protocol) headless — scripts temporários
scripts/tmp-*.mjsque:- spawnam Chrome com
--headless=new --remote-debugging-port=<porta>+--user-data-dirisolado; - conectam via WebSocket em
target.webSocketDebuggerUrl; - usam
Runtime.evaluatecomreturnByValue:truepara ler computed styles (getComputedStyle(el).opacity,transitionDelay, etc.) e estados de classe; - burlam cache de edge com
Network.setCacheDisabled+Network.clearBrowserCacheantes 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.
- spawnam Chrome com
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) elift()já adiciona.revealedantes do próximoawait, 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):
- Verificação CDP em produção: mediu-se
#historyList(container live) e o wrapper.games-history.reveal. - Estado antes do scroll:
elRevealed:false,wrapRevealed:false,L1opacity:"0"→ a cascata estava corretamente escondida (opacity 0), então o CSS estava certo. - Após
scrollIntoView+ 620ms: aindawrapRevealed:false→ o wrapper ainda não tinha revelado (oinitReveallibera 1 item por ~120ms no batch-flush; o card demora mais que 620ms). - Conclusão: a cascata rodava fora da tela no momento do swap.
Causa raiz (cadeia):
- O
lift()do flash-guard adicionava.revealedao 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.revealsó fadava depois, quando o IntersectionObserver doinitRevealdisparava 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
MutationObserverobserva 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 ancestralclosest()parte doparentElement, então o próprio container nunca casa.
Verificação (produção, CDP, cache off):
- Antes do scroll:
elRevealed:false, itensopacity:0. - Após scroll + 2s:
wrapRevealed:true,elRevealed:true, delays0.08s/0.16s, itensopacity: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.revealque 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.revealedao 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 oinitRevealnunca o observa → olift()é o único caminho capaz de revelá-lo.
Verificação (produção, CDP):
/alegoria: design grid e video grid agorarevealed/opacity:1./gg/jogos: painéis deferidos (#gamesList,#futureList,#statusLegend) revelam após scroll./gg: history card segue com cascata ok.npm run verify32/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:
.revealnormal (fora de[data-live-fallback]) →initReveal(IO) adiciona.revealedno scroll.[data-live-fallback]sem wrapper.revealpendente →lift()imediato no swap.[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 usatransformno hover — senão a regra de reveal (mais específica) engole o hover. - Novo container
[data-live-fallback]é pego automaticamente pelo flash-guard (MutationObserver nodocumentElement). Nunca removadata-live-fallbackmanualmente fora dolift()— o atributo é a trava que evita o flash de dados antigos. initRevealexclui 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)
scripts/validate-config.mjs— pré-build, validawrangler.toml(assets dir,not_found_handling, bindings KV) → falha comERR-CFG-{n}.scripts/post-build.mjs— pós-build, patch noentry.mjs(removefallbackToAssets+ injetascheduledhandler do cron com headersContent-Type/Origincorretos) e verifica o patch.scripts/verify-routes.mjs— pós-deploy, testa todas as rotas comSec-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 (" não decodificado) (BUG-004) |
— | stripHtml decodifica " → " |
| 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.BUGLOGempublic/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. projetousa chaves curtas:global,gg,alegoria,pcismo,dashboard.status:corrigido,aplicadoouidentificado.- 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 ✓.
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.
- API Token — dash.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 ID — dash.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.