This commit is contained in:
Jordan Diaz
2026-05-08 21:31:28 +00:00
parent 0dabba5442
commit 44cb956f95
37 changed files with 2120 additions and 251 deletions

View File

@@ -1,3 +1,10 @@
---
title: "Campos editables del builder"
tags: [builder, twig, html, modules]
load_priority: 80
load_when: [always]
summary: "Atributos data-field-* (textfield, headfield, link, upload, list, multiv2, checkbox), c-if/c-for/c-class, c-form, componentes built-in del builder Acai."
---
# Builder Fields — Campos editables del index-base.tpl
Este documento define los campos editables que el usuario rellena desde el panel del builder de Acai. Cubre el atributo `data-field-type` con todos sus tipos (`textfield`, `headfield`, `textbox`, `wysiwyg`, `link`, `upload`, `uploadMulti`, `list`, `multiv2`, `checkbox`, `colorpicker`), la regla `data-field-label` → nombre de variable, los atributos Acai (`c-if`, `c-else`, `c-for`, `c-class`, `c-hidden`, `c-required`), el tag `<set>`, la inclusión de módulos, los formularios `c-form` y los componentes built-in. Léelo antes de crear o modificar cualquier `index-base.tpl`.

View File

@@ -1,3 +1,10 @@
---
title: "Filtros Twig personalizados de Acai"
tags: [twig, filters, frontend]
load_priority: 70
load_when: [always]
summary: "Filtros Twig: get, hook, queryDB, translate, imagec, módulo, set; concatenación con ~; if con ==; reglas de uso vs c-if del builder."
---
# Twig — Filtros personalizados de Acai
Este documento describe los filtros Twig propios de Acai (`get`, `queryDB`, `hook`, `module`, `imagec`, `translate`) y los filtros estándar más usados (`raw`, `truncate`, `json_decode`, `split`, `filter`). Acai usa **filtros con pipe `|`**, nunca funciones. Léelo antes de escribir cualquier expresión Twig dentro de `index-base.tpl` o de una sección general. Cubre también la concatenación con `~`, los ternarios, el operador `default` y la diferencia entre `c-if` (=) y `{% if %}` (==).

View File

@@ -1,3 +1,10 @@
---
title: "Módulos y Secciones Generales"
tags: [modules, sections, structure, twig, html]
load_priority: 75
load_when: [always]
summary: "Módulos (carpetas en template/estandar/modulos), index-base.tpl, secciones generales (custom-{tableName}/), thisrecord, gestión de uploads de un registro."
---
# Módulos y Secciones Generales
Este documento explica el sistema modular de Acai: la diferencia entre **módulos** (componentes visuales reutilizables que el usuario coloca en páginas Builder) y **secciones generales** (plantillas ligadas a una tabla que se renderizan automáticamente al acceder al `enlace` de un registro). Cubre la estructura de archivos de un módulo, las reglas obligatorias sobre `index-base.tpl`, las variables globales (`section_id`, `interno`, `server.HTTP_HOST`, `loop`), la convención `custom-{tableName}` para detalles de registro, la inclusión de un módulo dentro de otro y el uso de `thisrecord` en secciones generales. Léelo antes de crear, mover o editar cualquier carpeta dentro de `template/estandar/modulos/`.

View File

@@ -1,3 +1,10 @@
---
title: "Páginas y Registros"
tags: [pages, apartados, records, structure]
load_priority: 70
load_when: [always]
summary: "Páginas Builder vs Standard, controlador, enlace, registros con num/PK, builder_custom, workflows de creación y edición."
---
# Páginas y Registros
Este documento explica cómo Acai modela las páginas del sitio: toda fila con campo `enlace` es una página, y según el campo `controlador` puede ser **Builder** (modular, contenido por módulos) o **Standard** (contenido directo en los campos del registro). Cubre los tipos de tabla por `menuType` (`category`, `multi`, `single`, `separador`), las particularidades de la tabla `apartados`, los campos de visibilidad (`visible_en_el_menu` vs `visible`), las reglas inviolables sobre `enlace` y `controlador`, y el patrón canónico para implementar el detalle de un registro vía sección general `custom-{tableName}`. Léelo antes de crear, modificar o eliminar cualquier registro de tabla con `enlace`.

View File

@@ -1,3 +1,10 @@
---
title: "Schema management — tablas y campos"
tags: [tables, schema, fields, db]
load_priority: 80
load_when: [always]
summary: "create_table/update_table_metadata/delete_table/reorder_tables, create_field/update_field/delete_field/reorder_fields, regenerate_enlaces, tipos de campo."
---
# Tablas y Campos
Este documento explica cómo gestionar tablas y campos en Acai usando las tools del MCP. Cubre: cómo se almacena el schema (`cms/data/schema/{tabla}.ini.php`), los `menuType` (`multi`, `single`, `category`, `separador`), el flag `enlace` para tablas públicas, todos los tipos de campo (`textfield`, `textbox`, `wysiwyg`, `codigo`, `date`, `list`, `checkbox`, `upload`, `multitext`, `separator`), los props comunes (`isRequired`, `defaultValue`, `optionsType`, etc.), la diferencia entre operaciones reversibles e irreversibles (`dropData`, `dropColumn`, rename), y el flujo correcto para crear una funcionalidad nueva. Léelo antes de usar cualquier tool del grupo `tables/`.

View File

@@ -1,3 +1,10 @@
---
title: "Hooks PHP y CmsApi"
tags: [php, hooks, cmsapi, backend]
load_priority: 70
load_when: [always]
summary: "Hooks globales (hooks/hooks.X.php) y de módulo (hook.php), CmsApi::get/insert/update/delete con uploads/relations/translates, set_hook_middleware, auto-registro en layout.json."
---
# Hooks y CmsApi (server-side)
Este documento describe cómo crear y consumir hooks PHP en Acai (lógica server-side) y cómo usar `CmsApi` (alias de `CocoDB`) para acceder a la base de datos. Cubre las dos ubicaciones válidas para un hook (global en `hooks/hooks.<id>.php` o propio de módulo en `template/estandar/modulos/<id>/hook.php`), las cuatro formas de invocarlo (filtro Twig, etiqueta `<hook>`, JS `CmsApi.hook`, `c-form`), las reglas obligatorias (devolver array, no `echo`, no `exit`), la API completa de `CmsApi::get/insert/update/delete` con sus opciones (`uploads`, `relations`, `translates`, `groupBy`, `aggregates`), y la tool `set_hook_middleware` para que un hook global se ejecute automáticamente antes de renderizar páginas. Léelo antes de crear cualquier `.php` de hook.

View File

@@ -1,3 +1,10 @@
---
title: "CSS y JavaScript — Convenciones"
tags: [css, js, frontend, conventions]
load_priority: 65
load_when: [always]
summary: "Tailwind primary, BEM scoped, data-* para pasar valores dinámicos a script.js (que es estático), CmsApi.hook desde JS, native components, Vue 3 builder."
---
# CSS y JavaScript — Convenciones del Módulo
Este documento define cómo escribir CSS, JavaScript y, cuando hace falta, Vue 3 dentro de un módulo Acai. Cubre la regla "Tailwind first" + BEM para CSS custom, las clases utilitarias propias de Acai (`transition3s`, `click-a-child`, `line-clamp2`, `lazyload`, `bg-main-color`, etc.), las CSS variables del tema (`--main-color`), el patrón obligatorio de **scoping** vía la clase raíz del módulo, la regla dura de que `script.js` y `style.css` son **archivos estáticos** (sin Twig dentro), cómo pasar valores dinámicos desde `index-base.tpl` a JS vía `data-*`, cuándo usar Vue 3 y cómo integrarlo evitando conflicto de delimiters con Twig, y los componentes nativos del builder (Carousel `c-tns-wrapper`, Lightbox, Breadcrumb, AOS, Lazy loading). Léelo antes de escribir cualquier `style.css` o `script.js`.

View File

@@ -1,3 +1,10 @@
---
title: "Layout global y librerías"
tags: [layout, header, footer, libraries]
load_priority: 60
load_when: [always]
summary: "header/footer/javascript/style en layout.json via get_layout_field/set_layout_field, librerías globales (CDN, npm), modos top/bottom, librerías AMP."
---
# 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.).

View File

@@ -1,3 +1,10 @@
---
title: "Referencia maestra de tools MCP"
tags: [tools, reference, workflows]
load_priority: 50
load_when: [ranked]
summary: "Inventario completo de tools por categoría (archivos, módulos, registros, tablas, layout, libs, hooks, media, navegación, proyecto, git, auth, docs) y workflows canónicos."
---
# MCP Tools — Referencia Completa
Este documento es el **inventario canónico** de todas las tools MCP disponibles para el agente Acai. Está agrupado por categoría (archivos, módulos, registros, tablas, layout, librerías, hooks, media, navegación, proyecto, git, autenticación, docs) y describe para cada tool su propósito, parámetros clave, qué devuelve y cuándo usarla. Incluye además los **workflows canónicos** para las operaciones más comunes (crear módulo, editar módulo, crear funcionalidad nueva con tabla + detalle, gestionar imágenes de un módulo, editar header/footer, configurar middleware de hook). Léelo antes de cualquier tarea para elegir la secuencia correcta de tools.
@@ -110,6 +117,21 @@ Ver `06-hooks-and-cmsapi.md` para uso. Crear/editar el `.php` del hook se hace c
|------|--------|
| `navigate_browser` | Navega el browser preview del usuario a un `enlace` (e.g. `/servicios/`) |
### Inspección de páginas (Playwright headless)
Tools del MCP `playwright`. El browser headless es del agente — el usuario NO ve lo que pasa aquí. Para que el USER vea algo, usa `navigate_browser`.
| Tool | Acción | Cuándo usarla |
|------|--------|---------------|
| `browser_navigate` | Carga una URL en el headless browser interno | Antes de cualquier otra `browser_*` |
| `browser_snapshot` | Devuelve el accessibility tree YAML (texto estructurado) | **Tool primaria de inspección**. La usas para leer la página. Ves DOM, roles, valores de inputs, enlaces, jerarquía |
| `browser_click`, `browser_fill_form`, `browser_press_key`, `browser_select_option` | Interacciones con elementos | Solo cuando necesitas simular interacción del usuario para reproducir un bug o validar un flow |
| `browser_console_messages` | Logs del console del browser | Para detectar errores JS |
| `browser_network_requests` | Lista de requests | Para detectar 404, fallos de fetch |
| `browser_take_screenshot` | Captura PNG | **Evítala**. El modelo NO procesa imágenes; el screenshot se descarta. Solo úsala si el usuario explícitamente pide "haz un screenshot para que lo vea yo" |
**Regla**: para auditar/inspeccionar/depurar UI, `browser_navigate``browser_snapshot`. NUNCA `browser_take_screenshot` esperando "ver" la página — el modelo es text-only y la imagen se pierde.
### Proyecto
| Tool | Acción |

View File

@@ -1,3 +1,10 @@
---
title: "Patrones de producción"
tags: [patterns, snippets, examples]
load_priority: 55
load_when: [always]
summary: "Snippets reales: header con menú, FAQ acordeón, zigzag de servicios, formularios con c-form, listados con filtros, structured data, breadcrumbs."
---
# Patrones de Producción
Este documento recoge patrones reales usados en módulos y secciones generales de proyectos Acai en producción. Cada patrón incluye el HTML/Twig listo para reutilizar y notas sobre cuándo aplicarlo. Cubre: cabecera de sección con colores configurables, layout zigzag (imagen + texto alternado), acordeón FAQ, formulario de contacto completo con `c-form`, compartir en redes sociales, sección general de detalle de producto, galería con carousel modo `gallery`. Léelo cuando vayas a crear un módulo y quieras evitar reinventar patrones que ya tienen una versión canónica testeada en producción.

110
docs/11a-decision-table.md Normal file
View File

@@ -0,0 +1,110 @@
---
title: "Tabla de decisión — qué tool usar para qué"
tags: [reference, decision, planner, tools]
load_priority: 90
load_when: [cheatsheet, planner_only]
summary: "Tabla decisional 'intención del usuario → tool MCP'. Es la guía rápida para enrutar peticiones sin leer toda la doc."
---
# Tabla de decisión — qué tool usar
Tabla decisional para mapear la intención del usuario a la herramienta correcta. Si tu petición encaja con varias filas, la primera que matchee es la canónica.
## Módulos (componentes visuales)
| Intención | Tool / workflow |
|---|---|
| Crear módulo nuevo | `acai-write` `index-base.tpl` (compila auto) → `add_module_to_record``set_module_config_vars` |
| Editar template de un módulo | `acai-view``acai-line-replace` (compila auto) |
| Ver datos actuales de un módulo en una página | `get_module_config_vars({ tableName, recordNum, sectionId })` |
| Cambiar valores de un módulo | `set_module_config_vars` |
| Reordenar módulos en una página | `reorder_module({ tableName, recordNum, sectionId, fromPosition, toPosition })` |
| Ocultar/mostrar un módulo | `toggle_module_visibility({ sectionId, visible })` |
| Eliminar instancia de módulo de una página | `remove_module_from_record({ sectionId })` |
| Borrar definición de módulo (carpeta entera) | `delete_module({ moduleId, inUse })` — destructivo, confirma con user |
| Comprobar dónde se usa un módulo | `check_module_usage({ moduleId })` |
| Preview de un módulo con datos de prueba | `check_module({ moduleId, vars })` |
| Datos de ejemplo persistentes para preview | `set_module_example_data({ moduleId, data })` |
## Registros / contenido
| Intención | Tool / workflow |
|---|---|
| Listar registros de una tabla | `list_table_records({ tableName, limit, where, orderBy, fields })` |
| Leer un registro concreto | `get_record({ tableName, recordNum })` |
| Crear/actualizar un registro | `create_or_update_record({ tableName, recordNum?, fields })` |
| Borrar registros | `delete_table_records({ tableName, where })` (destructivo, confirma) |
| Listar módulos en una página Builder | `list_page_modules({ tableName, recordNum })` |
## Imágenes y uploads
| Intención | Tool / workflow |
|---|---|
| Generar imagen con IA | `generate_image({ prompt, size? })` → recibe `uploadUrl`/`fullUrl` |
| Añadir imagen NUEVA a un campo upload | `upload_record_image({ tableName, recordNum, fieldName, imageUrl })` |
| Listar imágenes existentes de un campo upload | `list_record_uploads({ tableName, recordNum, fieldName })` → uploadId por imagen |
| Reemplazar imagen existente | `list_record_uploads``replace_record_image({ uploadId, imageUrl })` |
| Borrar una imagen | `list_record_uploads``delete_record_upload({ uploadId })` |
| Reordenar galería | `list_record_uploads``reorder_record_uploads({ uploadIds: [...] })` |
| Subir imagen a `/images/` (assets globales del template) | `upload_image_to_assets({ imageUrl, fileName })` |
## Tablas y campos (schema)
| Intención | Tool / workflow |
|---|---|
| Ver schema de una tabla | `get_table_schema({ tableName, minimal? })` |
| Listar todas las tablas del proyecto | `list_tables` |
| Crear tabla | `create_table({ tableName, displayName, menuType, enlace?, seoMetas? })` |
| Actualizar metadata de tabla | `update_table_metadata({ tableName, ... })` |
| Borrar tabla | `delete_table({ tableName, dropTable: true|false })` (destructivo) |
| Reordenar tablas en el menú admin | `reorder_tables({ order: [...] })` |
| Crear campo | `create_field({ tableName, fieldName, type, label, ... })` |
| Modificar campo (renombrar, cambiar tipo) | `update_field({ tableName, fieldName, newFieldName?, type?, ... })` |
| Borrar campo | `delete_field({ tableName, fieldName, dropColumn })` (destructivo) |
| Reordenar campos | `reorder_fields({ tableName, order: [...] })` |
| Regenerar enlaces (URLs) | `regenerate_enlaces({ tableName, generateAlias? })` (destructivo si no aliases) |
## Layout y librerías globales
| Intención | Tool / workflow |
|---|---|
| Ver header/footer/javascript/style/lo del layout | `get_layout_field({ field })` |
| Modificar header/footer/scripts globales | `set_layout_field({ field, value })` (destructivo, confirma) |
| Listar librerías cargadas | `list_global_libraries` |
| Añadir librería (CDN, npm) | `add_global_library({ url, position: "top"|"bottom" })` |
| Quitar librería | `remove_global_library({ url })` |
| Reescribir todo el array de librerías | `set_global_libraries({ libraries: [...] })` (destructivo) |
## Hooks PHP
| Intención | Tool / workflow |
|---|---|
| Crear hook global | `acai-write hooks/hooks.X.php` (auto-registra en layout.json) |
| Crear hook de módulo | `acai-write template/estandar/modulos/X/hook.php` |
| Borrar hook global | `acai-delete hooks/hooks.X.php` (auto-quita de layout.json) |
| Renombrar hook global | `acai-rename` o `acai-write` con nuevo nombre + `acai-delete` del viejo |
| Hook que se ejecuta antes de cada página | `set_hook_middleware({ hookEndPoint, middleWare: ["allurls"] })` |
| Hook que se ejecuta solo en una página | `set_hook_middleware({ hookEndPoint, middleWare: ["cms_apartados-87"] })` |
| Ver middleware de un hook | `get_hook_middleware({ hookEndPoint })` |
## Archivos y filesystem
| Intención | Tool / workflow |
|---|---|
| Buscar archivos por glob | `acai-glob({ pattern })` |
| Buscar texto en archivos | `acai-grep({ query, path?, type? })` |
| Leer un archivo | `acai-view({ file_path, start_line?, end_line? })` |
| Crear/sobrescribir un archivo | `acai-write({ file_path, content })` |
| Reemplazar líneas en un archivo | `acai-line-replace({ file_path, oldText, newText })` |
| Borrar un archivo | `acai-delete({ file_path })` (destructivo) |
## Proyecto y debugging
| Intención | Tool / workflow |
|---|---|
| URL del sitio (preview en desarrollo) | `get_web_url` (añade `?pruebas=1` siempre) |
| Navegar al browser preview del usuario | `navigate_browser({ enlace })` |
| Token JWT expirado (errores 403) | `refresh_acai_token` |
| Volver a una versión anterior | `list_git_log``recover_git({ id })` o `recover_previous_git` (destructivos, confirma) |
| Guardar estilos del proyecto en doc | `save_project_styles` |
| Necesito un doc no cargado | `read_doc({ name: "..." })` |
| Listado de docs disponibles | `list_docs` |

View File

@@ -1,6 +1,13 @@
# Quick Reference — Cheat sheet
---
title: "Reglas inmutables y cheat-sheet de tipos"
tags: [reference, rules, cheat]
load_priority: 90
load_when: [cheatsheet]
summary: "Reglas no negociables (cms_, num, _num, upload arrays, c-if/{% if %}), tipos de builder field, atributos Acai, filtros Twig, formato de datos para insert/update, errores comunes."
---
# Reglas inmutables y cheat-sheet
Este documento es un **resumen ejecutable** de las reglas críticas, los tipos de campo, los filtros Twig, los formatos de datos para insert/update y las variables globales. Es la **fuente única de verdad** para resolver dudas rápidas sin tener que abrir los docs largos. Léelo antes de cualquier operación cuando quieras refrescar las reglas; el resto de docs (`01``10`) profundizan en cada tema.
Resumen ejecutable de reglas críticas, tipos de campo, filtros y formatos de datos. Si tienes duda rápida, consulta esto antes de los docs largos.
## Reglas inmutables
@@ -80,7 +87,7 @@ Este documento es un **resumen ejecutable** de las reglas críticas, los tipos d
| `multitext` | String JSON | `"[{\"item\":\"valor\"}]"` |
| `upload` | NO enviar — usar `upload_record_image` después |
## Variables globales
## Variables globales en Twig
| Variable | Descripción |
|----------|-------------|
@@ -91,33 +98,6 @@ Este documento es un **resumen ejecutable** de las reglas críticas, los tipos d
| `loop.index is odd` / `is even` | Layouts alternados |
| `thisrecord` | Registro actual (solo en secciones generales) |
## Decisión rápida — qué tool usar
| Intención | Tool / workflow |
|-----------|-----------------|
| Crear módulo nuevo | `acai-write` `index-base.tpl``add_module_to_record``set_module_config_vars` |
| Editar template de módulo | `acai-view``acai-line-replace` |
| Ver datos de un módulo en una página | `get_module_config_vars` |
| Cambiar valores de un módulo | `set_module_config_vars` |
| Subir imagen a un módulo | Usa `uploadFields` de `set_module_config_vars``upload_record_image` (`tableName: "builder_custom"`) |
| Reemplazar imagen existente de un registro | `list_record_uploads``replace_record_image({ uploadId, imageUrl })` |
| Borrar imagen de un registro | `list_record_uploads``delete_record_upload({ uploadId })` |
| Reordenar galería de un registro | `list_record_uploads``reorder_record_uploads({ uploadIds: [...] })` |
| Crear tabla nueva | `create_table` (pregunta `enlace`/`seoMetas` antes) → `create_field` |
| Crear detalle de registro | Sección general en `template/estandar/modulos/custom-{tableName}/` |
| Editar header / footer | `get_layout_field``set_layout_field` (NUNCA edites los `.tpl` directamente) |
| Añadir librería global | `list_global_libraries``add_global_library` (`top` o `bottom`) |
| Hook que se ejecuta antes de cada página | `acai-write` el `.php``set_hook_middleware({ middleWare: ["allurls"] })` |
| Generar imagen IA | `generate_image``upload_record_image` con `uploadUrl`/`fullUrl` |
| Buscar archivos | `acai-glob` |
| Buscar texto en archivos | `acai-grep` |
| URL del proyecto | `get_web_url` (añade `?pruebas=1`) |
| Navegar el preview del usuario | `navigate_browser` |
| Token JWT expirado (403) | `refresh_acai_token` |
| Volver a una versión anterior del proyecto | `list_git_log``recover_git({ id })` (o `recover_previous_git` para el commit anterior) — pide confirmación al usuario |
| Necesito un doc no cargado | `read_doc({ name: "..." })` |
| Listado de docs | `list_docs()` |
## Errores comunes a evitar
- Editar `index.tpl`, `index-twig.tpl` o `builder.json` (autogenerados).
@@ -130,3 +110,4 @@ Este documento es un **resumen ejecutable** de las reglas críticas, los tipos d
- Crear archivos JSON de i18n (usa `| translate` + tabla `textos_generales`).
- Usar Twig dentro de `script.js` o `style.css` (estáticos — pasa valores via `data-*`).
- Llamar `mkdir` (usa `acai-write` directamente — crea el directorio padre).
- Usar `upload_record_image` para "reemplazar" una imagen existente (añade un upload nuevo encima — usa `replace_record_image`).

86
docs/12-glossary.md Normal file
View File

@@ -0,0 +1,86 @@
---
title: "Glosario de Acai CMS"
tags: [glossary, terms, planner]
load_priority: 85
load_when: [glossary, planner_only]
summary: "Definiciones cortas de términos clave de Acai CMS: sectionId, recordNum, apartados, builder_custom, custom-{tableName}, enlace, controlador, thisrecord, multiv2, c-form, hook middleware, JWT acai_token, web-base, template/estandar/, Builder vs Standard."
---
# Glosario Acai CMS
Definiciones cortas de los términos que aparecen en docs y prompts. Si te pierdes con un concepto, lo encuentras aquí en una línea.
## Estructura del proyecto
**`template/estandar/`** — directorio donde viven los archivos custom del proyecto: módulos, CSS/JS globales, imágenes del template. Lo que el desarrollador edita.
**`web-base`** — código compartido del CMS (motor de render, admin, APIs). Vive aparte y se monta como volumen Docker. **No tocar**.
**`apartados`** — tabla principal de páginas del sitio. Cada registro es una página. Tiene `enlace`, `controlador`, jerarquía padre-hijo (`parentNum`).
**`hooks/`** — directorio de hooks PHP globales. Convención: `hooks/hooks.<id>.php` → endpoint `/hooks/<id>/`.
## Páginas y registros
**Builder vs Standard** — modos de renderizado de una página. Lo decide el campo `controlador` del registro:
- **Builder**: `controlador.php` → contenido modular (módulos drag-drop).
- **Standard**: `controlador_tabla.php` → contenido en campos del registro (`content` HTML).
**`enlace`** — URL pública de un registro (con barras incluidas, ej. `/servicios/`). NUNCA modificar a posteriori (rompe SEO y enlaces internos).
**`controlador`** — campo que define Builder vs Standard. NUNCA modificar a posteriori.
**`recordNum` / `num`** — Primary key. Acai siempre usa `num` (entero), nunca `id`.
**`<table>_num`** — convención de foreign keys. `categoria_num` apunta al `num` de la tabla `categorias`.
## Módulos y secciones
**Módulo** — componente visual reutilizable. Vive en `template/estandar/modulos/<id>/`. Se coloca en páginas Builder vía drag-drop. Archivos: `index-base.tpl` (source), `style.css`, `script.js`, `hook.php` (opcional). El compilador genera `index.tpl`, `index-twig.tpl`, `builder.json`.
**Sección general** — módulo especial que el CMS enlaza por convención de nombre. Renderiza el detalle de un registro de una tabla con `enlace`. Convención: `template/estandar/modulos/custom-{tableName}/`. Recibe el registro como `thisrecord`.
**`custom-{tableName}`** — convención de nombre de la sección general. NO usar `_detailPage`, NO crear página por registro en `apartados`.
**`thisrecord`** — variable Twig disponible en secciones generales con el registro actual. Acceso a campos: `thisrecord.titulo`, `thisrecord.imagen[0].urlPath`, `thisrecord.categoria_num`.
**`builder_custom`** — tabla interna de Acai donde el CMS guarda los valores de los módulos. Cuando el usuario rellena un módulo en una página Builder, los valores se persisten ahí. El `recordNum` para `upload_record_image` cuando subes a un módulo es el `num` de la fila correspondiente en `builder_custom`.
**`sectionId`** — identificador único de una instancia de módulo en una página Builder. Lo devuelve `add_module_to_record`. **No es** el `recordNum` para uploads (eso es `num` de `builder_custom`).
**`multiv2`** — tipo de campo del builder que permite arrays de objetos repetidos (ej. lista de servicios con título + descripción + icono cada uno). Se itera con `c-for`.
## Layout global
**`layout.json`** — fichero (`cms/lib/plugins/builder_saas/layout.json`) con el header, footer, librerías globales, javascript/style globales, y los hooks registrados. **NUNCA editar a mano** — usar `set_layout_field` o las tools de hooks/librerías.
**Hook middleware** — un hook global puede configurarse para auto-ejecutarse antes de renderizar páginas: vacío (solo on-demand), `["allurls"]` (todas las páginas) o `["cms_<table>-<num>", ...]` (páginas específicas). Se configura con `set_hook_middleware`.
**Auto-registro de hooks** — cuando creas/borras un fichero `hooks/hooks.<X>.php` con `acai-write`/`acai-delete`, el backend sincroniza automáticamente la entrada en `layout.json["hooks"]`. NO tocar `layout.json` a mano.
## Builder UI
**`c-form`** — atributo que convierte un `<form>` en un formulario que persiste a una tabla del CMS. Sintaxis: `<c-form tableName="'contacto'" captcha="true">`. Se renderiza como form HTML con submit a un endpoint Acai.
**`data-field-*`** — familia de atributos que marca un elemento como editable en el builder visual. Tipos: `textfield`, `headfield`, `textbox`, `wysiwyg`, `link`, `upload`, `uploadMulti`, `list`, `multiv2`, `checkbox`, `colorpicker`.
**`c-if`, `c-for`, `c-class`, `c-hidden`, `c-required`** — atributos de lógica visual. **`c-if` usa un solo `=`** (`c-if="x = 1"`), Twig `{% if %}` usa **doble** `==`.
## Datos / API
**CmsApi (alias `CocoDB`)** — librería PHP server-side para CRUD sobre las tablas del CMS. Métodos: `CmsApi::get(opts)`, `::insert(table, data)`, `::update(table, where, data)`, `::delete(table, where)`. Soporta `uploads`, `relations`, `translates`, `groupBy`, `aggregates` como opciones.
**JWT `acai_token`** — token de auth del proyecto que vive en `.acai`. Caduca y se renueva con `refresh_acai_token` cuando da error 403.
**`X-MCP-Secret`** — token de auth para clientes MCP externos (Claude Code, extensión VS Code). Vive en Redis. Es user-wide (autoriza todos los proyectos del usuario).
## Filtros Twig clave
**`| get`** — query a una tabla del CMS. `'productos' | get('activo=1', 'orden ASC', 10)`.
**`| queryDB`** — SQL crudo (con `cms_` prefix). `'SELECT * FROM cms_productos WHERE...' | queryDB()`.
**`| hook`** — invoca un hook PHP desde Twig. `'hooks/calcular/' | hook({precio: 100})`.
**`| imagec`** — optimiza una imagen al ancho dado. `imagen[0].urlPath | imagec(800)`.
**`| translate`** — traduce vía tabla `textos_generales`. `'texto a traducir' | translate`.