尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Transforme um briefing criativo em peças de campanha validadas e exportáveis — sem deixar um agente reinventar a sua marca. A premissa: um modelo generativo é bom em escolher e redigir, e ruim em ser cobrado por uma especificação. Então ele nunca desenha nada. Ele devolve um Creative Spec — qual template aprovado, qual imagem aprovada, que texto vai em cada slot nomeado — e o motor faz o resto de forma determinística. Uma falha em qualquer estágio interrompe a cadeia. Variações bloqueadas continuam visíveis no estúdio, com suas checagens reprovadas intactas.檢視完整 README ↗
Transforme um briefing criativo em peças de campanha validadas e exportáveis — sem deixar um agente reinventar a sua marca.
A premissa: um modelo generativo é bom em escolher e redigir, e ruim em ser cobrado por uma especificação. Então ele nunca desenha nada. Ele devolve um Creative Spec — qual template aprovado, qual imagem aprovada, que texto vai em cada slot nomeado — e o motor faz o resto de forma determinística.
| Estágio | O que acontece | O que é recusado |
|---|---|---|
| 1. Sistema de marca | Tokens, regras, imagens, tipografia e o logo oficial são registrados — e podem ser editados depois | Chaves desconhecidas, hex malformado, fontes não registradas, arquivos cujos bytes não batem com o tipo declarado |
| 2. Agente criativo | Devolve exatamente três Creative Specs estruturados | Qualquer saída que não sejam três specs sobre templates e imagens já aprovados |
| 3. Regras de pré-voo | 12 checagens sobre métricas reais das fontes | Estouro de texto, cores fora da paleta, violações do logo, quebra de área segura — antes de um glifo ser desenhado |
| 4. Renderização | Slots preenchidos em um DOM interpretado; texto convertido em vetor | Imagens remotas, peças não aprovadas, slots transformados |
| 5. Auditoria pós-render | Saída revalidada contra o template de origem | Qualquer desvio byte a byte em elemento protegido, viewBox alterado, logo substituído |
| 6. Exportação | SVG ou PNG | Tudo que falhar nos estágios 3 ou 5 — rechecado a cada requisição |
Uma falha em qualquer estágio interrompe a cadeia. Variações bloqueadas continuam visíveis no estúdio, com suas checagens reprovadas intactas.
A interface inteira, as mensagens de validação, as justificativas do agente e o conteúdo da campanha demo existem em português e inglês. O padrão é português; o toggle no topo da tela alterna e guarda a escolha em um cookie.
O idioma não é só uma camada de tela. A copy da campanha é renderizada dentro do SVG, então trocar de idioma faz o servidor semear novamente o workspace demo e o agente gerar a copy no idioma escolhido. Campanhas que você mesmo gerou permanecem no idioma em que foram criadas — o arquivo exportado já foi renderizado com aquele texto.
Para que isso funcionasse, o que o servidor produz virou dado em vez de prosa:
{ruleId, params}, e o texto vive nos dicionários;{theme, onTheme};nameKey traduzível (templates criados por você
mantêm o nome que você deu);AppError(chave, params) e só viram frase na
borda HTTP, então parser, renderizador e motor de regras seguem sem saber nada
sobre idioma. Erros de schema usam o locale pt-BR do próprio Zod, resolvido por
requisição — nada de configuração global compartilhada entre pedidos concorrentes.Assim uma conformidade já gravada pode ser lida em qualquer idioma. Um novo idioma é
um arquivo em lib/i18n/ — o tipo Dictionary é derivado do português, então
qualquer chave faltando vira erro de compilação.
Cada marca começa com as duas famílias embutidas e pode registrar as suas, de dois jeitos:
fonts.googleapis.com e fonts.gstatic.com são aceitos, e o
nome passa pelo schema antes de entrar na URL.Os tokens da marca só aceitam famílias que ela realmente tem — familyName no schema
garante que o valor parece um nome, e a rota confere se a família está registrada.
O sanitizador aceita a marcação que Figma e Illustrator realmente exportam —
fill-rule, clip-rule, stroke-miterlimit, version, xml:space, gradientes e
máscaras declarados localmente. Ficam de fora os elementos que trazem execução ou
busca de recursos: <style> (CSS arbitrário pode ter @import de outro domínio),
<use>, <filter> e <script>. Referências url(...) só valem apontando para um id
do próprio documento.
PATCH /api/brands aceita mudanças parciais. O ponto delicado: os templates guardam
o SVG já resolvido, com as cores e os nomes de fonte escritos dentro dele. Mudar um
token sem mais nada deixaria toda a geração reprovada na regra de paleta — então os
templates gerados pela factory são refeitos com os novos tokens. Templates que você
escreveu à mão não são tocados, e a resposta diz quantos ficaram de fora para você
conferir.
O lote inteiro — marca e templates refeitos — é gravado de uma vez, em vez de uma
escrita por registro. No modo local isso também evita um EPERM intermitente no
Windows, onde renomear o arquivo recém-escrito falha se um antivírus ou indexador
ainda o mantém aberto; o rename ainda assim tenta de novo algumas vezes.
Ao refazer, o corpo do texto é dimensionado pelas métricas da fonte escolhida: uma família mais larga que a anterior encolhe até caber na geometria, em vez de reprovar no overflow. O ajuste cobre o pior caso realista (uma manchete no limite de caracteres da marca); uma palavra única gigante continua sendo pega pela regra de overflow, que é a rede de segurança.
A interface é construída com Mantine 9: AppShell para o layout, tema em
lib/theme.ts com as escalas da marca (brand laranja, navy azul-marinho) e a
Manrope, e componentes do core para tudo o mais. O globals.css guarda só o que o
tema não cobre — o destaque serifado dos títulos e os editores de marcação.
Três skills do Mantine para agentes estão instaladas em .agents/skills/ e foram
seguidas onde se aplicam:
useForm com campos aninhados no mesmo
formato do schema do servidor, validação no blur espelhando o que a API exige, e
NumberInput/ColorInput, que por si sós eliminam os estados inválidos que antes
viravam um 400.Combobox que desenha cada
família na própria fonte, carregando-a no navegador pela API FontFace a partir
dos bytes que a marca já guarda.Swatch é construído com factory(), Styles API e
createVarsResolver, então o tema pode redefinir tamanho, raio e classes sem tocar no
componente.Para reinstalar ou atualizar as skills: npx skills add https://github.com/mantinedev/skills --skill <nome>.
app/
page.tsx · campaigns/ Estúdio de campanha — briefing entra, três direções validadas saem
dashboard/ Visão geral: contagens, saúde da conformidade, desempenho por regra
brands/ Sistema de marca: tokens, regras, biblioteca de imagens, cadastro
templates/ Geometria aprovada, contratos de slot, autoria de templates
architecture/ Como o pipeline garante o que garante
api/ workspace · brands · templates · assets · campaigns · export
lib/
i18n/ Dicionários pt/en, cookie de idioma e os formatadores
brand/repository.ts Persistência: Supabase quando configurado, JSON local caso contrário
brand/template-factory.ts Gera os cinco templates padrão a partir dos tokens da marca
brand/demo.ts O workspace Serein semeado, por idioma
agent/provider.ts Interface CreativeAgentProvider + mock determinístico
agent/pipeline.ts Briefing → specs → validação → renderização → auditoria
renderer/parser.ts Higienizador de SVG e leitor do contrato de template/slots
renderer/fonts.ts Carregamento de fontes e medição de texto
renderer/render.ts Preenchimento determinístico de slots e vetorização do texto
validation/engine.ts As 12 regras de marca
lib/theme.ts Tema Mantine: escalas brand (laranja) e navy, Manrope, raios e sombras
components/ui/blocks.tsx Blocos de layout compartilhados (cabeçalho, título de painel, item de regra)
components/ui/swatch/ Componente com factory() e Styles API, conforme a skill mantine-custom-components
components/brand/ Formulário com useForm (mantine-form) e seletor de tipografia com Combobox (mantine-combobox)
schemas/ Objetos estritos do Zod — o único vocabulário aceito
supabase/migrations/ Tabelas, políticas de RLS e o bucket privado de imagens
tests/ Testes unitários (node:test via tsx)
e2e/ Testes de navegador (Playwright)
npm install
npm run dev # http://localhost:3000
Não é preciso configurar nada. Sem credenciais do Supabase, o app usa um workspace
local persistente em .data/, identificado por um cookie httpOnly e semeado com a
marca demo Serein — um logo, três fotografias, cinco templates aprovados e uma
campanha.
supabase/migrations/ — pelo SQL Editor do painel ou com supabase db push..env.local (veja .env.example).npm run supabase:verify.O app nunca usa a service-role key: o RLS é a única fronteira entre inquilinos, e a
migração aplica um conjunto de políticas por dono às nove tabelas e ao bucket privado brand-assets,
que guarda imagens e arquivos de fonte.
A chave primária é sempre composta, (owner_id, id). O workspace demo é semeado
com ids fixos e idênticos para todo mundo, então uma PK só em id faria o segundo
visitante colidir com as linhas do primeiro — exatamente o caso de um portfólio
público. supabase:verify tem um teste de regressão para isso.
Na Vercel o Supabase é obrigatório — o workspace local em JSON precisa de um sistema de arquivos gravável.
O projeto conectado ao repositório constrói a cada push em main. As duas variáveis
precisam existir em Production e Preview como variáveis normais, não "Sensitive":
NEXT_PUBLIC_* é embutida no bundle em build time, e uma variável sensível não fica
disponível nessa fase — o app sobe achando que o Supabase não está configurado.
npx vercel link --project brand
printf '%s' "$URL" | npx vercel env add NEXT_PUBLIC_SUPABASE_URL production
printf '%s' "$KEY" | npx vercel env add NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY production
Depois de mudar uma variável, reconstrua (npx vercel redeploy <url>): trocar o valor
não altera um build já feito.
npm run typecheck # tsc --noEmit
npm test # 71 testes unitários
npm run test:e2e # 24 testes de navegador (requer: npx playwright install chromium)
npm run test:all # os três
npm run supabase:verify # valida a conexão Supabase, se houver .env.local
supabase:verify usa as mesmas credenciais do app — publishable key e usuário
anônimo, sem service-role — e confere as tabelas, o bucket, escrita e leitura reais e,
principalmente, o isolamento entre inquilinos: um segundo usuário anônimo não pode
ler, baixar nem gravar em nome do primeiro. Como o RLS é a única fronteira de
segurança, ele é testado de verdade em vez de presumido. Cria dois usuários anônimos
descartáveis e limpa as linhas que criou.
Os testes de navegador rodam sempre no workspace local: o playwright.config.ts zera
as variáveis do Supabase, e uma asserção falha se a suíte subir conectada — senão cada
teste criaria um usuário anônimo no projeto de verdade.
Os testes unitários cobrem onde errar sai caro: o higienizador de SVG (scripts,
entidades, referências remotas, marcação malformada), o contrato de slots, a medição e
quebra de texto, cada regra do motor de validação, a detecção de adulteração na saída
renderizada, e a recusa do pipeline em renderizar saída malformada do agente.
brand-system.test.ts verifica que as quinze combinações de formato × tema produzem um
template que valida contra a marca que o gerou. i18n.test.ts verifica que os
dicionários têm o mesmo formato, que nenhuma string ficou vazia, que toda regra e toda
chave de erro têm tradução, e que a copy em português cabe nos glifos das fontes
empacotadas — um acento faltando viraria um retângulo vazio no arquivo exportado. Os
testes de exceção afirmam a chave do erro, não a frase, então continuam válidos em
qualquer idioma.
lib/api.ts rejeita escritas cujo Origin não bate com o cabeçalho Host — a defesa
contra CSRF. A comparação é deliberadamente com o Host, e não com
new URL(request.url).origin: o Next deriva aquela origem por conta própria, então
comparar com ela recusava toda escrita quando o app era acessado por um endereço
que ele não resolveu — localhost enquanto npm run dev escuta em 0.0.0.0, ou
qualquer proxy. tests/api.test.ts trava esses casos.
A comparação é de host, não de esquema, porque Host não carrega esquema e derivar o
esquema da requisição recriaria o mesmo problema atrás de um proxy TLS.
O layout lê o cookie de idioma no servidor para evitar um flash de tradução e para
acertar o <html lang>. Em troca, as páginas passaram a ser renderizadas sob demanda
em vez de pré-geradas. Para um app de workspace — que já busca seus dados por fetch —
a troca vale a pena.
O MockCreativeAgent é determinístico para que a demo seja reprodutível e os testes
estáveis. Substitua-o implementando um único método:
export interface CreativeAgentProvider {
generateCreativeSpecs(context: AgentContext): Promise<unknown>;
}
O tipo de retorno é unknown de propósito. O que quer que um modelo produza é
interpretado por creativeSpecSchema e reamarrado aos templates e imagens reais da
marca antes de qualquer renderização — um id de template alucinado ou um campo a mais
reprova na leitura. O AgentContext inclui o locale, então seu provedor sabe em que
idioma escrever. Passe seu provedor como terceiro argumento de generateCampaign.