lf-pagebuilder-vue
v0.0.122
Published
Constructor de páginas visual basado en Vue.js que permite crear y diseñar páginas web de forma intuitiva mediante componentes arrastrables y configurables
Readme
lf-pagebuilder-vue
Editor visual para construir páginas HTML usando los componentes de libreria-astro-lefebvre.
⚡ Referencia rápida — Todas las opciones
Modo 1 — Componente Vue
npm install lf-pagebuilder-vue<template>
<Pagebuilder
:isProduction="false"
:debugMode="false"
:submitForm="false"
inputId="mi-input-hidden"
:excludeComponentTypes="['SEO', 'Footer']"
:excludeComponentTags="['boton', 'destacado']"
:includeOnlyComponentTags="['articulo']"
:visibleSections="['Header', 'Body', 'Footer']"
allowRenderMode="open"
limboToken="eyJ..."
ateneaToken="eyJ..."
:ledithorAiTools="['improve', 'improve-stream']"
client:only="vue"
/>
</template>
<script setup>
import { Pagebuilder } from 'lf-pagebuilder-vue';
import 'lf-pagebuilder-vue/styles';
</script>| Prop | Tipo | Default | Descripción |
|------|------|---------|-------------|
| isProduction | boolean | false | true = API producción · false = API desarrollo |
| debugMode | boolean | false | Muestra panel de import/export JSON y botón de carga desde localStorage |
| submitForm | boolean | false | Al guardar, busca el <form> padre y hace .submit() automáticamente |
| inputId | string \| null | null | ID del <input hidden> donde se vuelca el JSON al guardar y del que se lee el estado inicial |
| excludeComponentTypes | string[] | [] | Categorías de componentes que NO aparecerán en el selector. "Repetidor" siempre se excluye. |
| excludeComponentTags | string[] | [] | Tags de componentes a excluir (lista negra). Un componente con cualquiera de estos tags no aparecerá. |
| includeOnlyComponentTags | string[] | [] | Solo muestra componentes que tengan al menos uno de estos tags (lista blanca). La exclusión tiene prioridad. |
| visibleSections | string[] | — | Secciones visibles. Si no se indica, se muestran todas. Valores: Header, Body, Footer, Sidebar |
| allowRenderMode | 'open' \| 'fullpage' \| 'onlybody' | 'open' | open = el usuario elige · fullpage = forzado página completa · onlybody = forzado solo body |
| limboToken | string | — | Token JWT para el gestor de imágenes Limbo. Obtenerlo server-side con fetchLimboToken() |
| ateneaToken | string | — | Token JWT de Atenea para las funcionalidades de IA en el editor de texto (LedithorEditor). Obtenerlo server-side con fetchAteneaToken(). Sin este token el botón de IA no aparece. |
| ledithorAiTools | string[] | — | Herramientas de IA a habilitar en el editor de texto. Solo se aplican si también se proporciona ateneaToken. Ej: ['improve', 'improve-stream'] |
| viewOnly | boolean | false | Modo solo visualización: oculta toda la UI de edición (toolbar, panel de componentes). Usar para la vista pública. |
Categorías disponibles para
excludeComponentTypes:Call to Action,Contenido,Separador,Texto,Cabecera,Footer,Imagen,Repetidor,Formulario,Título,SEO
Modo 2 — Script IIFE (Symfony / Twig / cualquier backend)
<link rel="stylesheet" href="/build/lf-pagebuilder-iife.css">
<script src="/build/lf-pagebuilder-iife.iife.js"></script>
<!-- EDITOR (crear) -->
<input
type="hidden"
class="js-lf-pagebuilder-vue-input"
name="pagebuilder[pageConfig]"
id="pagebuilder-new"
value=""
data-is-production="false"
data-debug-mode="true"
data-submit-form="true"
data-allow-render-mode="open"
data-limbo-token="eyJ..."
data-atenea-token="eyJ..."
data-ledithor-ai-tools='["improve","improve-stream"]'
/>
<!-- EDITOR (editar) -->
<input
type="hidden"
class="js-lf-pagebuilder-vue-input"
name="pagebuilder[pageConfig]"
id="pagebuilder-123"
value="{JSON existente del pageConfig}"
data-is-production="false"
data-submit-form="true"
data-exclude-component-types="SEO,Footer,Cabecera"
data-limbo-token="eyJ..."
/>
<!-- VISTA PÚBLICA (solo lectura) -->
<div id="lf-pagebuilder-render"></div>
<script>
window.LfPagebuilder.render({
el: '#lf-pagebuilder-render',
pageConfig: {{ pageConfig|raw }}, {# string JSON o objeto #}
isProduction: true
});
</script>| Atributo data-* | Valores | Default | Descripción |
|-------------------|---------|---------|-------------|
| data-is-production | "true" | "false" | "false" | Entorno de la API de renderizado |
| data-debug-mode | "true" | "false" | "false" | Muestra panel de import/export JSON y botón de carga desde localStorage |
| data-submit-form | "true" | "false" | "false" | Al guardar, busca el <form> padre y hace .submit() automáticamente |
| data-exclude-component-types | string (comas) | — | Categorías de componentes a excluir. Ej: "SEO,Footer,Cabecera" |
| data-exclude-component-tags | string (comas) | — | Tags de componentes a excluir (lista negra). Ej: "boton,destacado" |
| data-include-only-component-tags | string (comas) | — | Solo muestra componentes con estos tags (lista blanca). La exclusión tiene prioridad. |
| data-visible-sections | string (comas) | — | Secciones visibles. Ej: "Header,Body" |
| data-allow-render-mode | "open" | "fullpage" | "onlybody" | "open" | Controla el modo de renderizado |
| data-limbo-token | string | — | Token JWT de Limbo para el gestor de imágenes |
| data-atenea-token | string | — | Token JWT de Atenea para las funcionalidades de IA. Sin este token el botón de IA no aparece. |
| data-ledithor-ai-tools | string (JSON array) | — | Herramientas de IA a habilitar. Solo se aplican si hay data-atenea-token. Ej: '["improve","improve-stream"]' |
El
iddel input se usa como clave de localStorage y como referencia interna. Si no tieneid, se genera uno automáticamente.
Compatibilidad de atributos: El componente acepta también los tokens como atributos planos (sin prefijo
data-):limboToken="...",ateneaToken="...",ledithorAiTools='[...]'. La forma recomendada es siempredata-*.
API global window.LfPagebuilder
| Método | Descripción |
|--------|-------------|
| LfPagebuilder.init(config?) | Inicializa el editor en todos los elementos .js-lf-pagebuilder-vue-input de la página |
| LfPagebuilder.render({ el, pageConfig, isProduction? }) | Renderiza la página en modo solo visualización (sin UI de edición) |
| LfPagebuilder.destroyAll() | Destruye todas las instancias activas |
| LfPagebuilder.getInstances() | Devuelve todas las instancias activas |
| LfPagebuilder.setComponents(components) | Reemplaza la lista de componentes disponibles |
| LfPagebuilder.addComponent(component) | Añade un componente a la lista disponible |
El IIFE se auto-inicializa en DOMContentLoaded si hay elementos con el selector .js-lf-pagebuilder-vue-input en el DOM. No es necesario llamar a init() manualmente.
Integración con Symfony/Twig — Contrato completo
Cómo funciona el intercambio de datos con el formulario
El componente actúa como un "enhanced textarea":
Lee su valor inicial de
input.value— enonMounted, el editor lee el JSON delvaluedel input y lo carga como estado inicial del canvas. Si está vacío, el editor empieza en blanco (modo crear). Si contiene JSON, lo inicializa con esa configuración (modo editar).Escribe en
input.valueal guardar — cuando el usuario pulsa "Guardar" dentro del editor, el JSON actualizado se escribe eninput.value.Envía el formulario si
data-submit-form="true"— después de escribir eninput.value, busca el<form>padre y llama a.submit(). Esto permite que Symfony valide y persista elpageConfig.
{# Crear #}
<form method="post" action="{{ path('landing_create') }}">
<input type="hidden"
id="pagebuilder-new"
name="pagebuilder[pageConfig]"
class="js-lf-pagebuilder-vue-input"
value=""
data-is-production="{{ app.environment == 'prod' ? 'true' : 'false' }}"
data-submit-form="true"
data-limbo-token="{{ limboToken }}"
data-atenea-token="{{ ateneaToken }}"
data-ledithor-ai-tools='["improve","improve-stream"]'
/>
{# El CSRF token y otros campos del formulario van aquí #}
</form>
{# Editar #}
<form method="post" action="{{ path('landing_edit', {id: landing.id}) }}">
<input type="hidden"
id="pagebuilder-{{ landing.id }}"
name="pagebuilder[pageConfig]"
class="js-lf-pagebuilder-vue-input"
value="{{ landing.pageConfig|json_encode }}"
data-is-production="{{ app.environment == 'prod' ? 'true' : 'false' }}"
data-submit-form="true"
data-exclude-component-types="SEO,Footer,Cabecera"
data-limbo-token="{{ limboToken }}"
/>
</form>
{# Vista pública #}
<div id="lf-pagebuilder-render"></div>
<script>
window.LfPagebuilder.render({
el: '#lf-pagebuilder-render',
pageConfig: {{ landing.pageConfig|raw }},
isProduction: {{ app.environment == 'prod' ? 'true' : 'false' }}
});
</script>Instalación
npm i lf-pagebuilder-vueLuego en tu package.json, cambia la versión a latest para tener siempre la última:
{
"dependencies": {
"lf-pagebuilder-vue": "latest"
}
}¿Qué es?
El Pagebuilder permite al usuario arrastrar y configurar componentes de la librería de componentes Astro de Lefebvre para diseñar páginas de forma visual.
El Pagebuilder NO renderiza la página final. Genera un JSON de configuración que se envía a una API externa renderizadora, que es la encargada de generar el HTML final.
Integración con Limbo (gestión de imágenes)
Si se quiere usar el sistema de imágenes Limbo desde el Pagebuilder:
Variables de entorno (.env):
PUBLIC_LIMBO_PUBLIC_KEY=pk_tu_public_key
IS_PROD=FALSEObtener token server-side y pasarlo al componente:
---
import { Pagebuilder } from 'lf-pagebuilder-vue';
import { fetchLimboToken } from 'lf-pagebuilder-vue/limbo';
let limboToken = '';
try {
const result = await fetchLimboToken({
publicKey: import.meta.env.PUBLIC_LIMBO_PUBLIC_KEY || '',
isProduction: import.meta.env.IS_PROD === 'TRUE'
});
limboToken = result.token;
} catch (e) {
console.error('[Pagebuilder] Error obteniendo token de Limbo:', e);
}
---
<Pagebuilder
limboToken={limboToken}
isProduction={import.meta.env.IS_PROD === 'TRUE'}
inputId="mi-input"
client:only="vue"
/>El token se obtiene en el frontmatter (server-side), evitando exponer la Public Key al cliente.
fetchLimboToken — opciones
import { fetchLimboToken } from 'lf-pagebuilder-vue/limbo';
const { token, expires_in } = await fetchLimboToken({
publicKey: 'pk_xxx', // requerido
isProduction: false, // opcional, default: false
limboApiUrl: 'https://...' // opcional, sobreescribe isProduction
});Integración con Atenea (IA en el editor de texto)
Si se quiere usar las funcionalidades de IA de LedithorEditor (botón "Improve", etc.):
Variables de entorno (.env):
ATENEA_USERNAME=ledithor
ATENEA_APIKEY=tu_apikey_aqui
IS_PROD=FALSEObtener token server-side y pasarlo al componente:
---
import { Pagebuilder } from 'lf-pagebuilder-vue';
import { fetchAteneaToken } from 'lf-pagebuilder-vue/atenea';
let ateneaToken = '';
try {
const result = await fetchAteneaToken({
username: import.meta.env.ATENEA_USERNAME || '',
apiKey: import.meta.env.ATENEA_APIKEY || '',
isProduction: import.meta.env.IS_PROD === 'TRUE'
});
ateneaToken = result.token;
} catch (e) {
console.error('[Pagebuilder] Error obteniendo token de Atenea:', e);
}
---
<Pagebuilder
ateneaToken={ateneaToken}
:ledithorAiTools="['improve', 'improve-stream']"
client:only="vue"
/>Si
ateneaTokenno se proporciona (o está vacío), el botón de IA no aparece en el editor de texto.ledithorAiToolssolo tiene efecto si hay token.
fetchAteneaToken — opciones
import { fetchAteneaToken } from 'lf-pagebuilder-vue/atenea';
const { token, expiresAt } = await fetchAteneaToken({
username: 'ledithor', // requerido
apiKey: 'tu_apikey', // requerido
isProduction: false, // opcional, default: false
ateneaApiUrl: 'https://...' // opcional, sobreescribe isProduction
});
// expiresAt: Unix timestamp (segundos) del claim `exp` del JWT| Parámetro | Tipo | Default | Descripción |
|-----------|------|---------|-------------|
| username | string | — | Usuario de Atenea |
| apiKey | string | — | API Key del usuario |
| isProduction | boolean | false | true → https://atenea.lefebvre.es · false → URL de desarrollo |
| ateneaApiUrl | string | — | URL base personalizada (sobreescribe isProduction) |
Funcionalidades del editor
Color de fondo en filas y columnas
Cada fila y cada columna del editor puede tener un color de fondo personalizado con soporte de canal alpha (opacidad). El color se configura desde el panel de configuración de la fila o columna y se exporta junto al resto de la configuración del layout.
El color se almacena en el JSON de configuración como un valor rgba(r, g, b, a) dentro de la propiedad backgroundColor:
{
"rows": [
{
"config": {
"backgroundColor": "rgba(255, 200, 100, 0.75)"
},
"columns": [
{
"config": {
"backgroundColor": "rgba(240, 240, 240, 1)"
},
"components": []
}
]
}
]
}Resaltado de elemento en edición
Cuando se abre el panel de configuración de una fila, columna o componente, el elemento correspondiente se resalta visualmente con un borde ámbar y un halo de sombra. El resaltado desaparece al cerrar el panel.
Build
El proyecto tiene dos builds:
Build estándar (librería npm)
npm run buildGenera en dist/:
index.js— ES Moduleindex.cjs— CommonJSindex.d.ts— Tipos TypeScriptlimbo.js/limbo.cjs— Utilidades de Limbo (server-side)atenea.js/atenea.cjs— Utilidades de Atenea (server-side)styles.css— Estilos
Build Symfony (IIFE)
npm run build:symfonyGenera en dist-symfony/:
lf-pagebuilder-iife.iife.js— Script autocontenido (incluye Vue y todas las dependencias)lf-pagebuilder-iife.css— Estilos con utilidades Tailwind prefijadas (mecano:)
Build completo
npm run build:allEjecuta ambos builds.
⚠️ Desarrollo local
IMPORTANTE: Lee esta sección completa antes de trabajar con este proyecto localmente.
Trabajar con npm link
Para probar cambios localmente en otro proyecto sin publicar:
# 1. En lf-pagebuilder-vue
cd /ruta/a/lf-pagebuilder-vue
npm link
# 2. En tu proyecto consumidor
cd /ruta/a/tu-proyecto
npm link lf-pagebuilder-vue⚠️ CRÍTICO: Múltiples librerías con npm link
Este proyecto depende de libreria-astro-lefebvre. Si necesitas trabajar con ambas librerías localmente:
# ❌ INCORRECTO - El segundo comando ROMPE el primero
npm link lf-pagebuilder-vue
npm link libreria-astro-lefebvre
# ✅ CORRECTO - Linkear TODAS en UN SOLO comando
npm link lf-pagebuilder-vue libreria-astro-lefebvreFlujo completo para trabajar con ambas librerías
# 1. En lf-pagebuilder-vue
cd /ruta/a/lf-pagebuilder-vue
npm link
# 2. En libreria-astro-lefebvre
cd /ruta/a/libreria-astro-lefebvre
npm link
# 3. En tu proyecto consumidor — AMBAS EN UN SOLO COMANDO
cd /ruta/a/tu-proyecto
npm link lf-pagebuilder-vue libreria-astro-lefebvreDeshacer los enlaces
cd /ruta/a/tu-proyecto
npm unlink lf-pagebuilder-vue libreria-astro-lefebvre
npm installWatch mode
npm run devEjecuta vite build --watch y recompila automáticamente cuando hay cambios.
📘 Publicar en npm
Para publicar una nueva versión:
- Subir la versión en
package.json - Compilar y publicar:
npm run build:all npm publish --access public
Configurar acceso a npm (primera vez)
- Solicitar acceso al equipo
desarrollowebteamlefen npm - Crear un token de acceso en tu perfil de npm → Access Tokens
- Añadir el token en
~/.npmrc://registry.npmjs.org/:_authToken=TU_TOKEN_AQUI
Instalación y dependencias
Dependencias requeridas en el proyecto padre
npm install lf-pagebuilder-vue@latest vue@^3.3.0 vuedraggable@^4.1.0Configuración en proyectos Astro
npm install @astrojs/vue tailwindcss @tailwindcss/viteEn astro.config.mjs:
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
integrations: [vue()],
vite: {
plugins: [tailwindcss()],
ssr: {
noExternal: ['libreria-astro-lefebvre', 'lf-pagebuilder-vue'],
},
},
});