Initial commit

This commit is contained in:
marciobever 2026-05-04 11:27:14 +00:00
commit 74a538bc66
21 changed files with 7382 additions and 0 deletions

24
.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
# jetbrains setting folder
.idea/

1
.npmrc Normal file
View file

@ -0,0 +1 @@
legacy-peer-deps=true

4
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

11
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

43
README.md Normal file
View file

@ -0,0 +1,43 @@
# Astro Starter Kit: Minimal
```sh
npm create astro@latest -- --template minimal
```
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── public/
├── src/
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

10
astro.config.mjs Normal file
View file

@ -0,0 +1,10 @@
// @ts-check
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import tailwind from '@astrojs/tailwind';
// https://astro.build/config
export default defineConfig({
integrations: [react(), tailwind()]
});

6948
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

32
package.json Normal file
View file

@ -0,0 +1,32 @@
{
"name": "autoblog-template-astro-base",
"type": "module",
"version": "0.0.1",
"engines": {
"node": ">=22.12.0"
},
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/react": "^5.0.4",
"@astrojs/tailwind": "^6.0.2",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"astro": "^4.16.14",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"tailwindcss": "^3.4.19",
"lucide-react": "^0.473.0",
"clsx": "^2.1.1",
"tailwind-merge": "^2.6.0",
"motion": "^12.0.0",
"framer-motion": "^12.0.0"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.19"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

9
public/favicon.svg Normal file
View file

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 749 B

View file

@ -0,0 +1,7 @@
{
"siteName": "Astro Template Base",
"primaryColor": "#00f0ff",
"backgroundColor": "#0a0a0a",
"fontFamily": "Outfit",
"layout": "default"
}

View file

@ -0,0 +1,26 @@
---
title: "A Revolução do GitOps na Criação de Sites"
date: 2026-04-30
excerpt: "Descubra como arquiteturas descentralizadas baseadas em GitOps e Astro estão mudando o jogo da web estática."
author: "Autoblog AI"
---
# Bem-vindo ao seu novo Blog Astro
Este é um artigo gerado para testes do nosso motor **GitOps**.
Na arquitetura real, a Inteligência Artificial fará commits diários de arquivos Markdown exatamente como este.
## Por que Astro?
Astro é um framework web construído para velocidade. Ele retira 100% do JavaScript desnecessário da página, entregando HTML estático puro.
Isso resulta em notas 100/100 no Google PageSpeed Insights, o que é fundamental para ranqueamento (SEO).
### Imagens injetadas pela IA
A IA do Gemini vai injetar links absolutos do Supabase Storage diretamente no meio do texto, e eles renderizarão perfeitamente:
![Imagem de Exemplo](https://images.unsplash.com/photo-1498050108023-c5249f4df085?auto=format&fit=crop&q=80&w=1200)
## Componentes React no Astro
Nós mantemos toda a beleza dos componentes React atuais (`PopularTheme`, `CorporateTheme`), mas usamos o Astro para convertê-los em arquivos super rápidos no momento da compilação pelo Coolify.

18
src/content/config.ts Normal file
View file

@ -0,0 +1,18 @@
import { defineCollection, z } from 'astro:content';
const blogCollection = defineCollection({
type: 'content',
// Opcionalmente podemos validar frontmatter, mas como nossa IA injeta as vezes markdown puro
// com as imagens inline, a validação de schema pode ser flexível.
schema: z.object({
title: z.string().optional(),
date: z.date().optional(),
excerpt: z.string().optional(),
author: z.string().optional(),
image: z.string().optional()
}).catchall(z.any()), // Aceita qualquer frontmatter adicional gerado pela IA
});
export const collections = {
'blog': blogCollection,
};

51
src/layouts/Layout.astro Normal file
View file

@ -0,0 +1,51 @@
---
import config from '../config/site-config.json';
import '../styles/global.css';
interface Props {
title: string;
}
const { title } = Astro.props;
// Helper para converter Hex para HSL/RGB se necessário,
// mas para o nosso uso as CSS variables aceitam Hex perfeitamente na raiz.
---
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="description" content="Astro description" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
{/* Google Fonts baseadas na configuração */}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@200..900&family=Fira+Code:wght@300..700&family=Inter:wght@100..900&family=Lora:wght@400..700&family=Merriweather:wght@300..900&family=Outfit:wght@100..900&family=Playfair+Display:wght@400..900&family=Poppins:wght@100..900&family=Space+Grotesk:wght@300..700&family=Space+Mono:wght@400;700&family=Work+Sans:wght@100..900&display=swap" rel="stylesheet">
<title>{title} | {config.siteName}</title>
</head>
<body>
<slot />
</body>
</html>
<style is:global define:vars={{
primaryColor: config.primaryColor,
backgroundColor: config.backgroundColor,
fontFamily: `"${config.fontFamily}", sans-serif`
}}>
:root {
--primary: var(--primaryColor);
--background: var(--backgroundColor);
--font-family: var(--fontFamily);
}
html {
font-family: var(--font-family);
background: var(--background);
color: white;
}
</style>

57
src/lib/i18n.ts Normal file
View file

@ -0,0 +1,57 @@
import { useState, useEffect } from 'react';
export type Language = 'pt' | 'en' | 'es';
const translations: Record<Language, any> = {
pt: {
nav: { home: 'Início', articles: 'Artigos', contact: 'Contato', language: 'Idioma' },
footer: { rights: 'Todos os direitos reservados', builtWith: 'Construído com' }
},
en: {
nav: { home: 'Home', articles: 'Articles', contact: 'Contact', language: 'Language' },
footer: { rights: 'All rights reserved', builtWith: 'Built with' }
},
es: {
nav: { home: 'Inicio', articles: 'Artículos', contact: 'Contacto', language: 'Idioma' },
footer: { rights: 'Todos los derechos reservados', builtWith: 'Construido con' }
}
};
export function useLanguage() {
const [lang, setLangState] = useState<Language>('pt');
useEffect(() => {
if (typeof window !== 'undefined') {
const saved = localStorage.getItem('lang');
if (saved && translations[saved as Language]) {
setLangState(saved as Language);
}
}
}, []);
useEffect(() => {
const handleLangChange = (e: Event) => {
const detail = (e as CustomEvent).detail;
if (detail && translations[detail as Language]) {
setLangState(detail);
}
};
window.addEventListener('lang-change', handleLangChange);
return () => window.removeEventListener('lang-change', handleLangChange);
}, []);
const setLang = (l: Language) => {
if (typeof window !== 'undefined') {
localStorage.setItem('lang', l);
}
setLangState(l);
window.dispatchEvent(new CustomEvent('lang-change', { detail: l }));
};
return {
lang,
setLanguage: setLang,
setLang,
t: translations[lang] || translations.pt
};
}

6
src/lib/utils.ts Normal file
View file

@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

37
src/pages/[slug].astro Normal file
View file

@ -0,0 +1,37 @@
---
import { getCollection } from 'astro:content';
import Layout from '../layouts/Layout.astro';
import config from '../config/site-config.json';
// GetStaticPaths is required for dynamic routes in Astro SSG
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map(post => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<Layout title={post.data.title || post.slug}>
<article class="max-w-3xl mx-auto px-6 py-16">
<a href="/" class="text-primary font-mono text-sm hover:underline mb-8 inline-block">&larr; Voltar para Home</a>
<header class="mb-12">
<h1 class="text-4xl md:text-5xl font-extrabold mb-6 leading-tight">{post.data.title || post.slug}</h1>
<div class="flex items-center gap-4 text-gray-400 font-mono text-sm border-b border-[rgba(255,255,255,0.1)] pb-8">
{post.data.date && <time>{new Date(post.data.date).toLocaleDateString('pt-BR')}</time>}
{post.data.author && <span>Por {post.data.author}</span>}
<span class="bg-primary/20 text-primary px-3 py-1 rounded-full text-xs font-bold uppercase tracking-widest">{config.siteName}</span>
</div>
</header>
{/* Prose is the Tailwind plugin for styling raw Markdown HTML */}
<div class="prose prose-invert prose-lg prose-a:text-primary hover:prose-a:text-primary/80 prose-img:rounded-xl prose-img:shadow-2xl prose-headings:font-bold max-w-none">
<Content />
</div>
</article>
</Layout>

34
src/pages/index.astro Normal file
View file

@ -0,0 +1,34 @@
---
import Layout from '../layouts/Layout.astro';
import config from '../config/site-config.json';
import { getCollection } from 'astro:content';
const posts = await getCollection('blog');
---
<Layout title="Home">
<main class="max-w-4xl mx-auto px-6 py-12">
<header class="mb-16 text-center">
<h1 class="text-5xl font-bold mb-4 tracking-tight">{config.siteName}</h1>
<p class="text-gray-400 text-lg">Bem-vindo ao nosso blog oficial. Conteúdo exclusivo gerado via GitOps.</p>
</header>
<section class="grid gap-8 md:grid-cols-2">
{posts.map((post) => (
<article class="border border-[rgba(255,255,255,0.1)] bg-[rgba(255,255,255,0.02)] p-6 rounded-lg hover:border-primary transition-colors duration-300">
<h2 class="text-2xl font-bold mb-3">
<a href={`/${post.slug}`} class="hover:text-primary transition-colors">
{post.data.title || post.slug}
</a>
</h2>
<p class="text-gray-400 mb-4 line-clamp-3">
{post.data.excerpt || 'Leia mais sobre este assunto clicando no link abaixo.'}
</p>
<a href={`/${post.slug}`} class="text-primary font-bold uppercase tracking-wider text-sm hover:underline">
Ler Artigo Completo &rarr;
</a>
</article>
))}
</section>
</main>
</Layout>

28
src/styles/global.css Normal file
View file

@ -0,0 +1,28 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
@apply bg-background text-white antialiased;
}
}
/* Custom Scrollbar for a premium feel */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--primary);
}

22
tailwind.config.mjs Normal file
View file

@ -0,0 +1,22 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
'../src/templates/**/*.{js,jsx,ts,tsx}', // Temporary: to read classes from autoblog-2 components during dev
],
theme: {
extend: {
colors: {
primary: 'var(--primary)',
background: 'var(--background)',
},
fontFamily: {
sans: ['var(--font-family)', 'sans-serif'],
mono: ['Space Mono', 'monospace'],
}
},
},
plugins: [
require('@tailwindcss/typography'),
],
}

14
tsconfig.json Normal file
View file

@ -0,0 +1,14 @@
{
"extends": "astro/tsconfigs/strict",
"include": [
".astro/types.d.ts",
"**/*"
],
"exclude": [
"dist"
],
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react"
}
}