Arquitectura completa. Arbol de archivos, flujo de datos en build, dependencias entre modulos, CSS y CI/CD.
Tabla de Contenidos 21 secciones
Orden de lectura recomendado: doc-readme → doc-guia → doc-arquitectura → doc-operacion → doc-auditoria
Revisión: 2026-07-08 · Build: 192 páginas · Componentes Astro: 18 · Archivos fuente: 75+ (incluyendo hermes/)
markeup/
├── src/
│ ├── generar_articulo.py ← Script Python: genera posts automáticos desde URLs con Ollama
│ ├── telegram_bot.py ← Bot de Telegram para interactuar con el vault
│ ├── components/
│ │ ├── blog/
│ │ │ ├── Pagination.astro ← Navegación entre páginas con DaisyUI join
│ │ │ ├── PostCard.astro ← Tarjeta clickable con ResponsiveImage + lazy loading
│ │ │ └── PostHero.astro ← Hero con imagen responsiva + fetchpriority="high"
│ │ ├── common/
│ │ │ ├── BackgroundImage.astro ← Fondo con gradiente mask (top + bottom simultaneous)
│ │ │ ├── Breadcrumb.astro ← Breadcrumb navegable + JSON-LD Schema.org
│ │ │ ├── Button.astro ← Botón reutilizable DaisyUI
│ │ │ ├── CategoryHeader.astro ← Cabecera con título, contador y opción back
│ │ │ ├── DocCard.astro ← Tarjeta para documentación (icono + label + título)
│ │ │ ├── FeatureCard.astro ← Tarjeta con icono para sección About
│ │ │ ├── Footer.astro ← Footer con enlaces (RSS, GitHub, año)
│ │ │ ├── Header.astro ← Menú responsive con drawer móvil + selector de temas
│ │ │ ├── Icon.astro ← SVG icon inline (calendar, user, search, etc.)
│ │ │ ├── ResponsiveImage.astro ← <picture> + srcset WebP + aspect-ratio + fetchpriority
│ │ │ ├── StyledBadge.astro ← Badge primary/secondary unificado
│ │ │ ├── TableOfContents.astro ← TOC colapsable con DaisyUI
│ │ │ └── Ventajas.astro ← Banner rotativo de frases
│ │ └── graph/
│ │ ├── ForceGraph2D.astro ← Grafo 2D con D3.js force simulation
│ │ └── ForceGraph3D.astro ← Grafo 3D con Three.js + 3d-force-graph
│ ├── content/
│ │ ├── posts/ ← 63 posts en 5 categorías (Biodanza, Documentación, Literatura, Opinión, Tecnología)
│ │ │ ├── Biodanza/ ← 34 posts (bienestar, energía, filosofía oriental)
│ │ │ ├── Documentación/ ← 5 docs del sistema
│ │ │ ├── Literatura/ ← 2 posts (Pessoa)
│ │ │ ├── Opinión/ ← 1 post
│ │ │ ├── Tecnología/ ← 21 posts (dev, IA, herramientas)
│ │ │ ├── img/ ← Imágenes fuente (34+ archivos) — procesadas a public/img/
│ │ │ └── hermes/ ← 🆕 Sistema de gestión IA del vault (12 archivos)
│ │ │ ├── SOUL.md ← Personalidad y reglas de Hermes Agent
│ │ │ ├── memory/ ← vault-index.md, conventions.md, backlog.md
│ │ │ ├── skills/ ← crear-post, ampliar-post, generar-wikilinks, auditar-vault, documentar
│ │ │ ├── templates/ ← post-template.md (frontmatter YAML 21 campos)
│ │ │ ├── rules/ ← frontmatter-spec, wikilinks-spec, style-guide
│ │ │ └── logs/sessions/ ← Trazabilidad de cada interacción
│ │ └── content.config.ts ← Define colección "posts": Zod schema + loader glob
│ ├── layouts/
│ │ └── BaseLayout.astro ← Layout único: HTML shell, Header, Breadcrumb, Footer, OG tags, Lenis
│ ├── lib/
│ │ ├── posts.ts ← getAllPosts, getPostsByCategory, getAllCategories, paginate, cleanSlug, getAdjacentPosts
│ │ ├── tags.ts ← getAllTags, getPostsByTag
│ │ ├── graph.ts ← generateGraphData: nodos (post/category/tag) + enlaces
│ │ ├── images.ts ← buildSrcSet, formatSrcSet, DEFAULT_SIZES, CARD_SIZES
│ │ ├── smooth-scroll.js ← Lenis init/destroy + anchor interception + ViewTransitions re-init
│ │ ├── remark-wikilink.js ← Plugin Remark: [[wikilink]] → <a href="/posts/slug">
│ │ └── posts.test.ts ← 6 tests unitarios (Vitest)
│ ├── pages/
│ │ ├── index.astro ← / (home con hero, stats, categorías, últimos posts, CTA)
│ │ ├── 404.astro ← /404
│ │ ├── posts/index.astro ← /posts (listado completo)
│ │ ├── posts/[...slug].astro ← /posts/:slug (post individual)
│ │ ├── category/[category]/index.astro ← /category/:cat
│ │ ├── category/[category]/page/[page].astro ← /category/:cat/page/:n
│ │ ├── categories/index.astro ← /categories (grid de categorías)
│ │ ├── tags/index.astro ← /tags (nube de tags)
│ │ ├── tags/[tag].astro ← /tags/:tag
│ │ ├── graph.astro ← /graph (grafo 3D)
│ │ ├── graph-2d.astro ← /graph-2d (grafo 2D)
│ │ ├── search.astro ← /search (Pagefind)
│ │ ├── feed.xml.ts ← /feed.xml (RSS)
│ │ ├── animated-sections.astro ← /animated-sections (demo)
│ │ ├── test-tipografia.astro ← /test-tipografia (test)
│ │ └── admin/ ← /admin, /admin/login
│ └── styles/
│ ├── global.css ← Barrel: @import base + components + utilities
│ ├── base.css ← @tailwind, @font-face (Roboto Flex + Material Symbols), @layer base
│ ├── components.css ← @layer components: glass-card, post-card, post-hero, etc.
│ └── utilities.css ← @layer utilities: text-responsive, container-responsive
├── scripts/
│ ├── process-images.js ← Crop 1920×1080 + variantes 640w/1024w/1280w WebP
│ ├── optimize-images.js ← Conversión batch a WebP
│ ├── sync-images.js ← Sincronización bidireccional src/content/img ↔ public/img
│ ├── compress-audio.js ← Compresión ffmpeg (límite 10MB Cloudflare)
│ ├── export-blog.js ← Exportación offline por categoría
│ └── dev-search.js ← Pagefind index para desarrollo local
├── public/
│ ├── img/ ← 136 imágenes WebP (34 × 4 tamaños)
│ ├── fonts/ ← Roboto Flex (woff2-variations) + Material Symbols
│ └── pagefind/ ← Índice de búsqueda (generado en build)
├── astro.config.mjs ← site, aliases, plugins, chunkSizeWarningLimit
├── tailwind.config.cjs ← DaisyUI 6 temas, typography
├── tsconfig.json ← strict + path aliases
├── vitest.config.ts ← Configuración de tests
└── .github/workflows/ ← deploy.yml, lighthouse.yml
Scripts del build:
npm run images→process-images.js: genera master 1920×1080 + variantes 640w/1024w/1280w enpublic/img/npm run build:astro→astro build:content.config.tscarga y valida todos los.mdcon Zodremark-wikilink.jstransforma[[slug]]→<a href="/posts/slug">- Páginas Astro iteran sobre colecciones (
getCollection("posts")) y generan HTML - Se generan 192 páginas en
dist/
npm run build:search→pagefind --site dist: indexa 192 páginas, 5933 palabras
content.config.ts (Zod schema)
↓
posts.ts ← tags.ts ← graph.ts
↓ ↓ ↓
pages/*.astro (16+ rutas)
↓
components/*.astro (18 componentes)
↓
BaseLayout.astro (HTML shell, OG, Breadcrumb, Lenis)
Regla: lib/ nunca importa de pages/ ni components/. La dependencia es unidireccional hacia arriba.
| Componente | Props clave | Loading | Estados |
|---|---|---|---|
| BaseLayout | title, description, ogType, ogImage, ogImageAlt, preloadImage, breadcrumbTitle | — | OG tags, preload link, Breadcrumb, slot |
| PostHero | post: Post | eager + fetchpriority=high | Con/sin imagen, título overlay, categorías, tags, metadatos |
| PostCard | post: Post | lazy + CARD_SIZES | Con/sin imagen, descripción truncada, overlay categoría, hover scale |
| ResponsiveImage | src, alt, loading, sizes, fetchpriority | configurable | Local: <picture> + srcset · Externo: <img> simple |
| Breadcrumb | pathname, title? | — | Ruta raíz (1 item), intermedia (2+ items), con título personalizado |
| Pagination | currentPage, totalPages, basePath | — | Botones prev/next, página activa, deshabilitado en extremos |
| Header | categories | — | Desktop: horizontal · Móvil: drawer con acordeones |
| TableOfContents | headings, showTOC | — | Colapsable, scroll-to-heading con Lenis |
| ForceGraph3D | data: string (JSON) | client:visible | Carga asíncrona Three.js, interactivo drag/zoom |
| ForceGraph2D | — | client:visible | Carga asíncrona D3.js, nodos dragables, zoom |
Principio: Máximo DaisyUI + Tailwind inline, mínimo CSS manual.
@layer base ← base.css: @font-face, headings, anchor links
@layer components ← components.css: .glass-card, .post-card, .post-hero__image, .category-header
@layer utilities ← utilities.css: .text-responsive-*, .container-responsive
Overrides DaisyUI (fuera de @layer para máxima prioridad): botones, badges, headings, iconos, sombras de cards. Ver components.css para el código completo.
Carga condicional: animated-sections.css se importa solo desde animated-sections.astro.
Roboto Flex: Fuente variable cargada localmente (sin CDN) con 6 subsets + font-display: swap. Ejes tipográficos animados en hover: YTUC, YTCF, YTDE, slnt (500ms ease).
- site:
https://mybrain-limpio.pages.dev - trailingSlash:
never - integrations: TailwindCSS
- vite.aliases:
@→ src,@layouts,@components,@lib,@styles - chunkSizeWarningLimit: 1000 kB
- Remark:
remarkWikilink(con validSlugs precargados del filesystem) - Rehype:
rehypeSlug+rehypeAutolinkHeadings
- plugins: DaisyUI, @tailwindcss/typography
- themes: light, cupcake, corporate, dark, dim, sunset
- darkTheme: dark
- Extiende
astro/tsconfigs/strict - Path aliases:
@/*,@layouts/*,@components/*,@lib/*,@styles/*
on: push → branches: [main]
steps:
- checkout
- node 20, npm ci
- npm run build
- Cloudflare Pages deploy → mybrain-limpio
on: push + pull_request → branches: [main]
steps:
- build
- Lighthouse CI con umbrales: performance ≥ 0.8, a11y ≥ 0.9, best-practices ≥ 0.9, seo ≥ 0.9
| Comando | Función |
|---|---|
npm run dev | Servidor desarrollo localhost:4321 |
npm run build | Build completo: images + astro + pagefind |
npm run build:astro | Solo build Astro → dist/ |
npm run build:search | Solo índice Pagefind |
npm run preview | Previsualizar build producción |
npm test | Vitest (6 tests) |
npm run images | Procesar imágenes (crop + variantes) |
npm run images:watch | Modo vigilancia de imágenes |
npm run compress-audio | Comprimir audios con ffmpeg |
npm run export | Exportar blog por categoría |
Script Python externo al build que automatiza la creación de posts a partir de URLs. No forma parte del pipeline Astro, sino que se ejecuta manualmente cuando se quiere generar contenido rápidamente desde una fuente externa.
URL de entrada
│
▼
┌─────────────────────────────────┐
│ 1. extraer_texto(url) │
│ requests → trafilatura/BS4 │
│ Extrae texto limpio del HTML │
└──────────────┬──────────────────┘
│ contenido (string)
▼
┌─────────────────────────────────┐
│ 2. detectar_categoria(contenido)│
│ Cuenta palabras clave por │
│ categoría. Asigna la de mayor │
│ puntuación. Default: "Documentación" │
└──────────────┬──────────────────┘
│ categoria (string)
▼
┌─────────────────────────────────┐
│ 3. generar_tags(contenido) │
│ Extrae palabras frecuentes, │
│ filtra stopwords, normaliza a │
│ 1-2 palabras con guiones. │
│ Mínimo 3, máximo 8 tags. │
└──────────────┬──────────────────┘
│ tags (list)
▼
┌─────────────────────────────────┐
│ 4. construir_prompt() │
│ Arma prompt detallado con: │
│ - Frontmatter YAML completo │
│ - Estructura del artículo │
│ - Formato Markdown avanzado │
│ - Contenido fuente (15K chars) │
└──────────────┬──────────────────┘
│ prompt (string)
▼
┌─────────────────────────────────┐
│ 5. Ollama API │
│ POST localhost:11434/api/generate│
│ Modelo: qwen2.5-coder:7b-64k │
│ num_ctx: 65536, temp: 0.7 │
│ Timeout: 600s (10 min) │
└──────────────┬──────────────────┘
│ artículo Markdown
▼
┌─────────────────────────────────┐
│ 6. guardar_articulo() │
│ Extrae bloque ```markdown │
│ Guarda en tmp/ → mueve a │
│ src/content/posts/<Categoría>/ │
└─────────────────────────────────┘
El script construye un prompt que especifica al modelo:
- Frontmatter YAML obligatorio con todos los campos del schema Zod del blog:
title,slug,description,pubDate,updatedDate,categories,tags(formato lista con guiones),image,imageAlt,canonicalUrl,aliases,noIndex,showTOC,hideCoverImage,allowComments,share - Estructura fija: introducción (4-5 párrafos), desarrollo con 4-6 secciones (H2, H3, H4, H5), conclusión (3-4 párrafos), referencias (mínimo 5 fuentes)
- Formato Markdown avanzado: listas, bloques de nota (
> [!NOTE],> [!TIP]), citas, negritas, cursivas - Restricción: no inventar información, basarse solo en el contenido proporcionado
- Longitud: 3000 a 5000 palabras
El script mantiene un diccionario de palabras clave por cada categoría del blog:
| Categoría | Palabras clave |
|---|---|
| Biodanza | biodanza, danza, movimiento, expresión, cuerpo, emoción, ritmo, música |
| Documentación | documentación, documentar, guía, manual, tutorial, cómo, paso a paso, referencia, api |
| Tecnología | tecnología, software, hardware, programación, código, ia, inteligencia artificial, machine learning, deep learning, redes, computación, algoritmo, datos, seguridad, cloud, devops |
| Opinión | opinión, reflexión, ensayo, análisis, crítica, perspectiva, personal, experiencia |
| Literatura | literatura, poesía, novela, cuento, ensayo literario, autor, obra, lectura, escritura |
El algoritmo cuenta las apariciones de cada keyword en el texto extraído. La categoría con más coincidencias gana. Si hay empate en 0 (sin coincidencias), se asigna “Documentación” por defecto.
El proceso de generación de etiquetas sigue estos pasos:
- Tokeniza el contenido en palabras de 3+ letras (incluye acentos y ñ)
- Filtra stopwords comunes en español (~40 palabras: “que”, “para”, “como”, “por”, etc.)
- Cuenta frecuencias y selecciona las 10 más frecuentes
- Inserta la primera palabra del slug del título como tag principal
- Normaliza cada tag: une pares de palabras con guiones, elimina caracteres no alfanuméricos
- Asegura mínimo 3 tags (añade “general” si faltan) y máximo 8
| Dependencia | Propósito | Instalación |
|---|---|---|
| Ollama | Servidor local de modelos LLM | brew install ollama |
qwen2.5-coder:7b-64k | Modelo de lenguaje (7B params, 64K contexto) | ollama pull qwen2.5-coder:7b-64k |
requests | Descarga HTTP de la URL y llamada a API de Ollama | uv pip install requests |
trafilatura | Extracción de texto principal de HTML (opcional, fallback a BeautifulSoup) | uv pip install trafilatura |
beautifulsoup4 | Parseo HTML como fallback si trafilatura falla | uv pip install beautifulsoup4 |
El script genera un archivo .md con frontmatter completo directamente en la carpeta de categoría correspondiente (src/content/posts/<Categoría>/[slug].md). Una vez generado:
- Syncthing lo sincroniza a Obsidian (si se ejecutó en el Mac de build)
- El post aparece en
npm run buildcomo cualquier otro contenido - Si
draft: false, se publica en el siguiente deploy - Las imágenes referenciadas en el frontmatter (
image: '/img/[slug].webp') deben añadirse manualmente después
| Documento | Contenido |
|---|---|
| doc-readme | Visión general y stack |
| doc-guia | Instalación y configuración |
| doc-operacion | Comandos y troubleshooting |
| doc-auditoria | Auditoría y deuda técnica |
markeup/
├── src/
│ ├── components/
│ ├── content/
│ ├── layouts/
│ ├── lib/
│ ├── markeup_tools/
│ ├── pages/
│ ├── styles/
│ └── types/
├── scripts/
├── public/
└── package.json
📂 Árbol completo (generado 2026-07-09 20:33)
├── content.d.ts
├── types.d.ts
├── lighthouserc.json
├── deploy.yml
├── lighthouse.yml
├── .gitignore
├── app.json
├── appearance.json
├── community-plugins.json
├── core-plugins.json
├── graph.json
├── data.json
├── embeddings.json
├── main.js
├── manifest.json
├── styles.css
├── data.json
├── main.js
├── manifest.json
├── styles.css
├── workspace.json
├── settings.json
├── Modelfile
├── astro.config.mjs
├── cline-instructions.md
├── commit-local.sh
├── deploy.sh
├── 2026-07-09.md
├── package.json
├── _headers
├── Presuncion.mp3
├── euritmia-steiner.mp3
├── glandula-pineal.mp3
├── registros-akashicos.mp3
├── teoria-polivagal.mp3
├── teosofia.mp3
├── material-symbols-outlined.woff2
├── roboto-flex-cyrillic-ext.woff2
├── roboto-flex-cyrillic.woff2
├── roboto-flex-greek.woff2
├── roboto-flex-latin-ext.woff2
├── roboto-flex-vietnamese.woff2
├── roboto-flex.woff2
├── Neumorfismo.webp
├── Telegram-Ollama-Obsidian-Astro.webp
├── agni-yoga.webp
├── agua.webp
├── ancestros.webp
├── antroposofia-steiner-euritmia.webp
├── apps-chatgpt.webp
├── aura.webp
├── biodanza-danza-vida.webp
├── caretas-mascaras.webp
├── chackras.webp
├── chi.webp
├── cloudflare.webp
├── corrupcion.webp
├── docker.webp
├── documentacion.webp
├── el-ego.webp
├── el-libro-del-desasosiego-anatomia-del-tedio-existencial.webp
├── euritmia-steiner.webp
├── gestualidad-facial.webp
├── glandula-pineal.webp
├── grafo.webp
├── graphify.webp
├── grounding.webp
├── hermes-agent.webp
├── home.webp
├── inteligencia.webp
├── la-ansiedad.webp
├── la-mentira.webp
├── la_respiracion.webp
├── ladanzadelencuentro.webp
├── literatura.webp
├── material-design.webp
├── mesmerismo.webp
├── mirada.webp
├── motionsites.webp
├── musica-esferas.webp
├── npm-node.webp
├── nvidia-nim.webp
├── obsidian.webp
├── orgon.webp
├── pitagoras.webp
├── placeholder.webp
├── posts.webp
├── psicomotricidad-relacional.webp
├── roerich.webp
├── silva-mind-control.webp
├── sonidos-guturales.webp
├── syncthing.webp
├── tags.webp
├── tailwind.webp
├── tecnologiablog.webp
├── teoria-polivagal.webp
├── teosofia-desarrollo-personal.webp
├── trascendencia.webp
├── vault-cms-cover.webp
├── vitalidad.webp
| Desarrollo | |
|---|---|
npm run dev | Servidor desarrollo localhost:4321 |
| Build | |
|---|---|
npm run build | Build completo: images + astro + pagefind |
npm run build:astro | Solo build Astro → dist/ |
npm run build:search | Solo índice Pagefind |
| Imágenes | |
|---|---|
npm run images | Procesar imágenes (crop + WebP) |
npm run images:watch | Modo vigilancia de imágenes |
npm run sync:images | Sincronizar src/img ↔ public/img |
| Audio | |
|---|---|
npm run compress-audio | Comprimir audios con ffmpeg |
npm run compress-audio:watch | Modo vigilancia de audios |
npm run compress-audio:force | Recomprimir todos los audios |
| Tools | |
|---|---|
npm run tools | 🧠 Menú interactivo MarkeUP Command Center v6 |
npm run tools:crear | Crear post (atajo directo) |
npm run tools:auditar | Auditar vault (atajo directo) |
npm run tools:stats | Dashboard stats (atajo directo) |
npm run tools:hermes | Iniciar Hermes Agent (atajo directo) |
npm run tools:ollama | Estado Ollama en JSON |
npm run tools:telegram | Iniciar bot Telegram |
| Testing | |
|---|---|
npm run test | Ejecutar tests (Vitest) |
npm run test:watch | Tests en modo watch |
| Deploy | |
|---|---|
npm run preview | Previsualizar build producción |
npm run export | Exportar blog por categoría |
npm run auto-commit | Auto-sync Android: images → commit → push |
npm run dev:search | Índice Pagefind para desarrollo |