Nano Banana Pro
Agent skill for nano-banana-pro
Este documento foi criado especificamente para agentes de IA (GitHub Copilot, Claude, ChatGPT, etc.) que precisam trabalhar neste projeto.
Sign in to like and favorite skills
Este documento foi criado especificamente para agentes de IA (GitHub Copilot, Claude, ChatGPT, etc.) que precisam trabalhar neste projeto.
Tipo: Template para aplicações SaaS
Objetivo: Prototipagem rápida de micro-SaaS com autenticação e banco de dados
Idioma: Português (pt-BR) - Todo código e documentação
Status: Produção-ready com CI/CD configurado
Leia SEMPRE antes de fazer mudanças:
.github/copilot-instructions.md - Instruções específicas do Copilot.github/PROJECT_CONTEXT.md - Contexto completo do projeto.github/ARCHITECTURE.md - Arquitetura detalhadaQUICKSTART.md - Guia rápido de inícioREADME.md - Documentação principal{ "framework": "TanStack Start v1.132.0", "runtime": "Node.js >= 20.19.0", "language": "TypeScript 5.7.2 (strict mode)", "styling": "Tailwind CSS v4.0.6", "database": "PostgreSQL 14+ com Drizzle ORM", "auth": "Better Auth v1.3.28", "testing": "Vitest 3.0.5 + Testing Library", "build": "Vite 7.1.7" }
⚠️ ATENÇÃO: Estas versões são críticas:
micro-sass/ ├── src/ │ ├── components/ │ │ ├── ui/ # Componentes base (Button, Input, Card) │ │ ├── auth/ # LoginForm, SignupForm │ │ └── layout/ # Layout, Header │ ├── routes/ # File-based routing (TanStack Router) │ │ ├── __root.tsx # Layout raiz │ │ ├── index.tsx # Página inicial (/) │ │ ├── login.tsx # /login │ │ ├── signup.tsx # /signup │ │ ├── dashboard.tsx # /dashboard (protegida) │ │ └── api/ │ │ └── auth/$.ts # API de autenticação │ ├── db/ │ │ ├── schema.ts # Schema Drizzle (users, sessions, etc) │ │ └── index.ts # Database client │ ├── lib/ │ │ ├── auth-client.ts # Cliente de autenticação │ │ └── utils.ts # Utilitários (cn, etc) │ ├── test/ │ │ └── setup.ts # Setup de testes │ ├── auth.ts # Configuração Better Auth │ ├── router.tsx # Router config │ └── styles.css # Estilos globais ├── .github/ │ ├── workflows/ # CI/CD (ci.yml, update-dependencies.yml) │ └── *.md # Documentação de contexto ├── docker-compose.yml # PostgreSQL + PgAdmin ├── drizzle.config.ts # Config Drizzle ORM ├── vite.config.ts # Config Vite + Vitest └── package.json # Dependências
✅ SEMPRE use alias
:~/
import { Button } from '~/components/ui/Button' import { cn } from '~/lib/utils' import { auth } from '~/auth'
❌ NUNCA use caminhos relativos profundos:
import { Button } from '../../../components/ui/Button' // ❌ ERRADO
✅ SEMPRE use named exports:
export function MyComponent() {} // ✅ CORRETO
❌ NUNCA use default exports:
export default function MyComponent() {} // ❌ ERRADO
✅ SEMPRE defina tipos explícitos:
type User = { id: string name: string email: string } function getUser(id: string): User { // ... }
❌ NUNCA use
sem justificativa:any
const data: any = {} // ❌ ERRADO
⚠️ IMPORTANTE: Tailwind v4 tem sintaxe diferente da v3!
✅ Correto (v4):
<div className="bg-linear-to-r from-blue-500 to-purple-500" /> <div className="bg-linear-to-br from-zinc-800 to-black" />
❌ Errado (v3):
<div className="bg-gradient-to-r from-blue-500 to-purple-500" /> // ❌
Mudanças principais:
bg-gradient-to-* → bg-linear-to-*bg-gradient-radial → bg-radial✅ Padrão funcional com TypeScript:
type ButtonProps = { children: React.ReactNode variant?: 'default' | 'secondary' onClick?: () => void } export function Button({ children, variant = 'default', onClick }: ButtonProps) { return ( <button onClick={onClick} className={cn('base-styles', variants[variant])}> {children} </button> ) }
npm run dev # Inicia dev server (porta 3000) npm run build # Build produção npm run serve # Preview build npm test # Roda testes (Vitest)
npm run db:push # Sync schema com DB npm run db:studio # Drizzle Studio (visual DB) npm run db:generate # Gera migrations npm run db:migrate # Executa migrations
npm run docker:up # Inicia PostgreSQL + PgAdmin npm run docker:down # Para containers npm run docker:logs # Ver logs npm run docker:reset # Reset completo + db:push
npx tsc --noEmit # Type check npm run build # Valida build
O TanStack Router usa file-based routing:
// src/routes/dashboard.tsx import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/dashboard')({ component: DashboardPage, }) function DashboardPage() { return <div>Dashboard</div> }
// src/routes/api/endpoint.ts import { createFileRoute } from '@tanstack/react-router' import { json } from '@tanstack/react-start' export const Route = createFileRoute('/api/endpoint')({ server: { handlers: { GET: async ({ request }) => { return json({ data: 'response' }) }, POST: async ({ request }) => { const body = await request.json() return json({ success: true }) }, }, }, })
export const Route = createFileRoute('/dashboard')({ beforeLoad: async ({ context }) => { const session = await context.auth.getSession() if (!session) { throw redirect({ to: '/login' }) } }, component: DashboardPage, })
// src/db/schema.ts import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core' export const users = pgTable('users', { id: uuid('id').primaryKey().defaultRandom(), name: text('name').notNull(), email: text('email').notNull().unique(), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), })
import { db } from '~/db' import { users } from '~/db/schema' import { eq } from 'drizzle-orm' // Select const allUsers = await db.select().from(users) const user = await db.select().from(users).where(eq(users.id, userId)) // Insert await db.insert(users).values({ name: 'João', email: '[email protected]' }) // Update await db.update(users).set({ name: 'João Silva' }).where(eq(users.id, userId)) // Delete await db.delete(users).where(eq(users.id, userId))
import { useSession, signIn, signOut } from '~/lib/auth-client' function MyComponent() { const { data: session } = useSession() if (!session) { return <button onClick={() => signIn.email({ email, password })}>Login</button> } return ( <div> <p>Olá, {session.user.name}</p> <button onClick={() => signOut()}>Sair</button> </div> ) }
import { auth } from '~/auth' export const Route = createFileRoute('/api/protected')({ server: { handlers: { GET: async ({ request }) => { const session = await auth.api.getSession({ headers: request.headers }) if (!session) { return new Response('Unauthorized', { status: 401 }) } return json({ user: session.user }) }, }, }, })
SEMPRE verifique se o componente já existe antes de criar um novo!
import { Button } from '~/components/ui/Button' <Button variant="default">Clique aqui</Button> <Button variant="secondary">Secundário</Button> <Button variant="outline">Outline</Button> <Button variant="ghost">Ghost</Button> <Button variant="danger">Perigo</Button> <Button isLoading>Carregando...</Button>
import { Input } from '~/components/ui/Input' <Input label="Email" type="email" placeholder="[email protected]" required error="Email inválido" />
import { Card, CardHeader, CardTitle, CardContent } from '~/components/ui/Card' <Card> <CardHeader> <CardTitle>Título</CardTitle> </CardHeader> <CardContent> Conteúdo do card </CardContent> </Card>
Template:
// src/components/ui/NovoComponente.tsx import { cn } from '~/lib/utils' type NovoComponenteProps = { children: React.ReactNode className?: string } export function NovoComponente({ children, className }: NovoComponenteProps) { return ( <div className={cn('base-styles', className)}> {children} </div> ) }
src/test/setup.ts// src/components/ui/NovoComponente.test.tsx import { describe, it, expect } from 'vitest' import { render, screen } from '@testing-library/react' import { NovoComponente } from './NovoComponente' describe('NovoComponente', () => { it('deve renderizar corretamente', () => { render(<NovoComponente>Teste</NovoComponente>) expect(screen.getByText('Teste')).toBeDefined() }) it('deve aplicar className customizado', () => { render(<NovoComponente className="custom">Teste</NovoComponente>) const element = screen.getByText('Teste') expect(element.className).toContain('custom') }) })
npm test # Roda todos os testes npm test -- --watch # Watch mode npm test Button # Roda testes específicos
❓ Pergunte-se:
package.json)# Verificar CVEs antes de instalar npm audit
🔍 Verifique:
src/components/ui/?✅ Checklist:
# 1. Type check npx tsc --noEmit # 2. Testes npm test # 3. Build npm run build # 4. Lint (se aplicável) # npm run lint
Use Conventional Commits:
feat: adiciona componente Modal fix: corrige erro de validação no LoginForm docs: atualiza README com novos comandos test: adiciona testes para Button component chore: atualiza dependências refactor: reorganiza estrutura de pastas style: corrige formatação do código perf: melhora performance da query de usuários
Sempre inclua:
Causa: Alias
~ não configuradovite.config.ts com vite-tsconfig-paths
Causa: Environment do Vitest não configurado
Solução: Já configurado em
vite.config.ts:
test: { environment: 'jsdom', setupFiles: './src/test/setup.ts', }
Causa: Usando sintaxe v3 em projeto v4
Solução:
// ❌ Errado (v3) <div className="bg-gradient-to-r" /> // ✅ Correto (v4) <div className="bg-linear-to-r" />
Causa: Node.js desatualizado
Solução:
# Verificar versão node --version # Deve ser >= 20.19.0 # Instale via nvm: nvm install 20 nvm use 20
Causa: Docker não está rodando ou variáveis de ambiente erradas
Solução:
# 1. Verificar se Docker está rodando docker ps # 2. Iniciar containers npm run docker:up # 3. Verificar .env # DATABASE_URL=postgresql://microsaas_user:microsaas_password@localhost:5432/microsaas # 4. Sync schema npm run db:push
Causa: Import errado do defineConfig
Solução:
// ❌ Errado import { defineConfig } from 'vite' // ✅ Correto (quando tem test config) import { defineConfig } from 'vitest/config'
O projeto tem 2 workflows automáticos:
CI (ci.yml) - Roda em push/PR:
Update Dependencies (update-dependencies.yml) - Semanal:
Se workflows falharem com erro de permissão:
README.md - Visão geral completaQUICKSTART.md - Início rápido (< 5 min)DOCKER.md - Guia do Docker.github/PROJECT_CONTEXT.md - Contexto completo.github/ARCHITECTURE.md - Decisões arquiteturais.github/WORKFLOWS.md - Guia dos workflows.github/PROMPT_EXAMPLES.md - Exemplos de promptsAo trabalhar neste projeto:
.github/copilot-instructions.md primeiro❌ Sugerir mudança de stack tecnológica
❌ Adicionar dependências sem necessidade
❌ Usar caminhos relativos em vez de alias
❌ Criar componentes duplicados
❌ Ignorar padrões de código estabelecidos
❌ Usar sintaxe Tailwind v3
Antes de qualquer mudança, confirme:
.github/copilot-instructions.md.github/PROJECT_CONTEXT.md~/ para importsbg-linear-to-*)npx tsc --noEmit (sem erros)npm test (todos passando)npm run build (build OK)Última Atualização: Novembro 2025
Versão do Template: 1.0.0
Mantido por: @faelribeiro22
💡 Dica Final: Quando em dúvida, consulte os arquivos de contexto na pasta
.github/. Eles contêm informações detalhadas sobre arquitetura, decisões técnicas e exemplos práticos.