This commit is contained in:
Jordan Diaz
2026-04-25 10:27:51 +00:00
parent e84a36c83d
commit 6881d64a08
42 changed files with 3207 additions and 3413 deletions

View File

@@ -0,0 +1,212 @@
# Layout Global y Librerías Globales
Este documento explica cómo gestionar los **4 campos globales del proyecto** (`style` CSS global, `javascript` JS global, `header` Twig del header del sitio, `footer` Twig del footer) y las **librerías globales** (CSS/JS/fonts inyectadas en `<head>` o antes de `</body>`). Cubre la regla crítica de NO editar nunca `cms/lib/plugins/builder_saas/layout.json` ni los `.tpl` de `custom-header-twig` / `custom-footer-twig` directamente, las tools `get_layout_field` / `set_layout_field` (única vía válida para editar header/footer/style/javascript) y las tools `list_global_libraries` / `add_global_library` / `remove_global_library` / `set_global_libraries` para gestionar las URLs de librerías. Léelo antes de tocar cualquier cosa relacionada con header, footer, CSS global o librerías externas (jQuery, Vue CDN, Google Fonts, etc.).
## Layout global
Los 4 campos globales del proyecto viven en `cms/lib/plugins/builder_saas/layout.json`:
| Campo | Contenido |
|-------|-----------|
| `style` | CSS global del proyecto (se inyecta en todas las páginas) |
| `javascript` | JS global del proyecto (se inyecta en todas las páginas) |
| `header` | Twig del header del sitio (se renderiza arriba de cada página) |
| `footer` | Twig del footer del sitio (se renderiza al final de cada página) |
Los campos `header` y `footer` son **Twig** — se beneficias de filtros (`| get`, `| translate`, etc.) y atributos (`c-if`, `c-for`).
### REGLA CRÍTICA — Nunca edites estos archivos directamente
**Está prohibido** usar `acai-view`, `acai-line-replace`, `acai-write` ni `acai-delete` sobre:
- `cms/lib/plugins/builder_saas/layout.json`
- `template/estandar/modulos/custom-header-twig/*`
- `template/estandar/modulos/custom-footer-twig/*`
- `template/estandar/modulos/custom-header/*`
- `template/estandar/modulos/custom-footer/*`
Estos ficheros son **artefactos generados** a partir de `layout.json`. Editarlos directamente provoca:
- Desincronización con `layout.json.{header,footer}ModuleCustom.htmlParsed`.
- Sobrescritura de tus cambios cuando el usuario abre el builder visual y guarda.
- Comportamiento inconsistente entre el render público y el builder.
**El backend protege estas rutas** — las tools de archivo devolverán error si intentas tocarlas.
### Workflow correcto
#### Leer
```
get_layout_field({ field: "header" }) // Twig source del header
get_layout_field({ field: "footer" }) // Twig source del footer
get_layout_field({ field: "style" }) // CSS global
get_layout_field({ field: "javascript" }) // JS global
```
#### Escribir
```
set_layout_field({
field: "footer",
content: "<footer class='bg-gray-900 text-white py-10'>…nuevo HTML/Twig…</footer>"
})
```
`set_layout_field` ejecuta una pipeline atómica:
1. Escribe el source en `layout.json.{field}`.
2. Sincroniza `layout.json.{field}ModuleCustom.htmlParsed`.
3. Regenera los `.tpl` de `custom-{field}-twig/`.
4. Compila el Twig a PHP.
Es **destructivo** — sobrescribe el contenido completo. **Pair con `get_layout_field` primero** para leer el actual y modificarlo, no escribirlo desde cero.
### Ejemplos de uso
#### Cambiar el copyright del footer
```
// 1. Leer
get_layout_field({ field: "footer" })
// devuelve: <footer><p>© 2024 Mi Sitio</p>…</footer>
// 2. Modificar localmente y reescribir entero
set_layout_field({
field: "footer",
content: "<footer><p>© 2025 Mi Sitio. Todos los derechos reservados.</p>…</footer>"
})
```
#### Añadir un menú al header
```
// 1. Leer source actual
get_layout_field({ field: "header" })
// 2. Escribir nueva versión con el menú añadido
set_layout_field({
field: "header",
content: "<header>…<nav>{% for item in 'apartados' | get('parentNum=0 AND visible_en_el_menu=1', 'globalOrder ASC') %}<a href='{{ item.enlace }}'>{{ item.name }}</a>{% endfor %}</nav>…</header>"
})
```
#### Añadir CSS global
```
// 1. Leer
get_layout_field({ field: "style" })
// 2. Reescribir con la regla añadida
set_layout_field({
field: "style",
content: ":root { --main-color: #2563eb; }\n.btn-primary { … }\n…"
})
```
## Librerías globales
`layout.json["libraries"]` define una lista de URLs (CSS, JS, fonts) que el CMS inyecta en cada página. Hay **dos secciones**:
| Sección | Posición | Uso típico |
|---------|----------|------------|
| `top` | Dentro de `<head>` | CSS, fonts (Google Fonts), JS crítico (preload) |
| `bottom` | Antes de `</body>` | La mayoría de JS (jQuery, Vue, sliders, etc.) |
### Tools
#### Listar — `list_global_libraries`
```
list_global_libraries()
```
Devuelve:
```json
{
"top": [
{ "num": 1, "url": "https://fonts.googleapis.com/css2?family=Inter" },
{ "num": 2, "url": "/css/extras.css" }
],
"bottom": [
{ "num": 1, "url": "https://unpkg.com/vue@3/dist/vue.global.prod.js" },
{ "num": 2, "url": "/js/main.js" }
],
"layoutExists": true
}
```
Llama a esta tool **antes** de añadir/quitar para no duplicar entradas.
#### Añadir — `add_global_library`
Idempotente: si la URL ya existe en esa sección, devuelve `added: false`.
```
add_global_library({
section: "bottom",
url: "https://unpkg.com/vue@3/dist/vue.global.prod.js"
})
add_global_library({
section: "top",
url: "https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
})
// Rutas relativas al proyecto también valen
add_global_library({
section: "bottom",
url: "/js/custom.js"
})
```
#### Eliminar — `remove_global_library`
Idempotente: si la URL no existe, devuelve `removed: false`.
```
remove_global_library({
section: "bottom",
url: "https://unpkg.com/vue@3/dist/vue.global.prod.js"
})
```
#### Reemplazar entera — `set_global_libraries`
**Destructivo** — sobrescribe la lista completa de la sección. Úsalo solo para reordenar masivamente o reemplazar el conjunto. Para cambios incrementales prefiere `add_global_library` / `remove_global_library`.
```
set_global_libraries({
section: "bottom",
libraries: [
{ url: "/js/jquery.min.js" },
{ url: "/js/main.js" },
{ url: "https://unpkg.com/vue@3/dist/vue.global.prod.js" }
]
})
```
### Convenciones
- Para **librerías populares**, prefiere CDN oficial (`unpkg.com`, `cdn.jsdelivr.net`, `cdnjs.cloudflare.com`).
- Para **assets propios** del proyecto, usa rutas relativas (`/js/main.js`, `/css/extras.css`).
- Si añades una librería que ya tiene equivalente cargada (ej. dos versiones de jQuery), elimina la antigua antes de añadir la nueva.
- El **orden** importa: las dependencias deben ir antes que sus consumidores. `set_global_libraries` permite reordenar.
## Decisión: ¿layout global o módulo?
| Caso | Solución |
|------|----------|
| Header/footer del sitio | Layout global (`set_layout_field` con `header`/`footer`) |
| CSS aplicado a todo el sitio | Layout global (`set_layout_field` con `style`) o librería global |
| JS aplicado a todo el sitio | Layout global (`set_layout_field` con `javascript`) o librería global |
| Componente reutilizable en páginas | Módulo en `template/estandar/modulos/` |
| Detalle de un registro | Sección general `custom-{tableName}` |
| Bloque visual específico | Módulo |
## Reglas críticas
1. **NUNCA edites directamente** `layout.json`, `custom-header-twig/*`, `custom-footer-twig/*`, `custom-header/*`, `custom-footer/*`. Las tools de archivo te devolverán error si lo intentas.
2. Para header/footer/style/javascript globales, **única vía**: `get_layout_field` + `set_layout_field`.
3. `set_layout_field` es **destructivo** — siempre lee primero, modifica, escribe.
4. Para librerías globales: `list_global_libraries``add_global_library` / `remove_global_library`. `set_global_libraries` solo para reordenar/reemplazar masivamente.
5. Inyección automática: `top` va en `<head>`, `bottom` antes de `</body>`. Decide según el tipo de asset.
6. El orden de las librerías importa para dependencias.