Guía de Desarrollo: Editor Visual de Presentaciones

1. Resumen del Proyecto

El objetivo es construir una aplicación web de página única (SPA) que funcione como un editor visual (WYSIWYG) para modificar archivos de presentaciones HTML generados previamente. Los usuarios podrán cargar una presentación existente, editar textos e imágenes, añadir nuevos elementos con una interfaz de arrastrar y soltar, y guardar el resultado como un nuevo archivo HTML en el servidor.

Público Objetivo:

Administradores de contenido o usuarios que necesitan personalizar las presentaciones generadas por el agente de IA sin necesidad de editar código HTML directamente.

Stack Tecnológico Principal:

Frontend: HTML5, CSS3, JavaScript (ES6+), Bootstrap 5 Backend: PHP 8+

2. Arquitectura General

El sistema se divide en tres componentes lógicos principales que se comunican entre sí:

  1. Frontend (Interfaz del Editor): Es la capa de presentación con la que interactúa el usuario. Construida con HTML y Bootstrap, y controlada por JavaScript.
  2. Motor de Edición (Lógica del Cliente): Es el "cerebro" de la aplicación, escrito en JavaScript puro. Se encarga de leer, manipular y reconstruir el DOM de la presentación.
  3. Servicio de Guardado (Backend): Un script PHP simple que recibe el HTML final desde el cliente y lo escribe en un archivo en el servidor.

3. Componente 1: Frontend (Interfaz del Editor)

Se debe construir una única página HTML (`editor.html`) que contenga toda la interfaz del editor.

3.1. Estructura y Maquetación (HTML/Bootstrap)

La interfaz se dividirá en tres áreas principales utilizando un sistema de rejilla (grid) de Bootstrap:

Barra de Herramientas Superior:

Panel Izquierdo (Navegador de Diapositivas):

Área Central (Escenario de Edición):

Panel Derecho (Propiedades y Herramientas):

4. Componente 2: Motor de Edición (Lógica en JavaScript)

Este es el núcleo funcional de la aplicación. Se debe escribir en un archivo editor.js y modularizarlo en responsabilidades.

4.1. Carga y Parseo del Archivo

  1. Escuchar el Evento `change` del Input: Asociar un listener al #cargadorHtml.
  2. Leer el Archivo: Al seleccionar un archivo, usar la API FileReader para leer su contenido como texto (reader.readAsText(file)).
  3. Parsear el HTML: Una vez leído el contenido, usar DOMParser para convertir el string HTML en un documento DOM manipulable (new DOMParser().parseFromString(htmlText, "text/html")).
  4. Extraer Diapositivas: Del documento parseado, seleccionar todas las etiquetas <section>.
  5. Poblar Navegador: Iterar sobre cada <section>, generar una miniatura (se puede usar una librería como `html2canvas` para crear una imagen o simplemente mostrar el título) y añadirla al #panelNavegacion. Almacenar el contenido HTML de cada sección en un arreglo o un objeto para fácil acceso.
  6. Cargar Primera Diapositiva: Cargar automáticamente la primera <section> en el <iframe id="canvasFrame">.

4.2. Edición de Contenido

  1. Edición de Texto:
    • Al cargar una diapositiva en el iframe, se debe acceder a su documento (iframe.contentDocument) y buscar todos los elementos de texto (`h3`, `p`, `li`, etc.).
    • A cada elemento de texto, se le debe añadir el atributo contenteditable="true".
    • Se debe añadir un borde o resaltado visual con CSS (ej: [contenteditable="true"]:hover { outline: 1px dashed #3498db; }) para indicar que es editable.
  2. Reemplazo de Imágenes:
    • Al hacer clic en una <img> dentro del iframe, el panel de propiedades debe mostrar un <input type="file" accept="image/*">.
    • Al seleccionar una nueva imagen, se debe usar FileReader para leerla como una URL de datos base64 (reader.readAsDataURL(file)).
    • La propiedad src de la imagen seleccionada se debe actualizar con la nueva URL de datos.

4.3. Arrastrar y Soltar (Drag & Drop)

  1. Configurar Elementos Arrastrables: Los elementos en la pestaña "Herramientas" tendrán el atributo draggable="true".
  2. Configurar Zona de Destino: El documento del <iframe> debe tener listeners para los eventos dragover (para prevenir el comportamiento por defecto) y drop.
  3. Manejar el "Soltar": En el evento drop, se debe:
    • Obtener el tipo de elemento arrastrado (texto o imagen).
    • Crear el nuevo elemento HTML (ej: let p = iframe.contentDocument.createElement('p');).
    • Añadirle las propiedades necesarias (contenteditable="true", un texto por defecto, etc.).
    • Para que se pueda posicionar libremente, el nuevo elemento deberá tener position: absolute; y se calcularán sus coordenadas left y top basadas en la posición del puntero al momento de soltar.
    • Añadir el nuevo elemento al DOM de la diapositiva activa en el iframe.

4.4. Reconstrucción y Guardado

  1. Recolectar Contenido Modificado: Al hacer clic en #btnGuardar, JavaScript debe recorrer todas las diapositivas (almacenadas desde el paso de parseo) y actualizar su contenido HTML con la versión que está actualmente en el DOM del editor.
  2. Limpiar Atributos de Edición: Es crucial eliminar los atributos contenteditable="true" y cualquier estilo de resaltado antes de guardar.
  3. Reconstruir el String HTML: Volver a armar el string del archivo HTML completo, incluyendo el <head> original (con los enlaces a Reveal.js) y el nuevo contenido del <div class="slides">.
  4. Enviar al Backend: Usar la API fetch para enviar el string HTML completo y el nombre del archivo original a `guardar_presentacion.php` mediante una petición `POST`.

5. Componente 3: Servicio de Guardado (Backend - PHP)

Un script PHP simple (`guardar_presentacion.php`) que no requiere conexión a base de datos.

5.1. Funcionalidad

  1. Recibir Datos: El script debe esperar una petición `POST`. Debe leer el contenido del cuerpo de la petición, que será el string HTML, y una variable con el nombre original del archivo.
  2. Validar Datos (Básico): Verificar que los datos no estén vacíos.
  3. Generar Nuevo Nombre: Tomar el nombre original (ej: `presentacion_subtema_59.html`), quitarle la extensión `.html` y añadirle el sufijo `_editado.html`. El resultado sería `presentacion_subtema_59_editado.html`.
  4. Escribir el Archivo: Usar la función file_put_contents('ruta/a/presentaciones/' . $nuevo_nombre, $contenido_html) para guardar el archivo en el servidor.
  5. Devolver Respuesta: Enviar una respuesta JSON al cliente para indicar el éxito o fracaso de la operación. Ejemplo: echo json_encode(['success' => true, 'message' => 'Archivo guardado con éxito.']);