Notas
Crepe fuera, BlockNote dentro: editor nuevo y biblioteca de medios
Seis horas y catorce despliegues contra el salto de selección, cambio de
3 min de lectura
Ayer cerré la jornada con el panel rediseñado —el que tomaba forma en Del Resumen al editor: el panel toma forma— y hoy me llevé la peor tarde de las últimas semanas y, a la vez, una de las más productivas: cambio de editor y biblioteca de medios entera. Voy por partes.
El salto de selección: seis horas y catorce despliegues
En el editor Crepe/Milkdown, seleccionar texto hacía saltar la página. Parecía una tontería de CSS (el lenguaje que da aspecto a una web), pero no lo era. La lista de parches fallidos es larga: scroll-padding, scroll único en el lienzo, overflow-anchor, TopBar fuera del scroll (rompió el layout), toolbar portalizada (página en blanco), position fixed con variables CSS. Catorce despliegues y 3 reverts sin tocar la causa real: las toolbars flotantes de Crepe con el scroll anidado no se entendían con Safari.
La lección me ha quedado grabada: si tres parches estructurales fallan, el problema es la herramienta, no el parche. Y nunca más retirar funcionalidades que el usuario valora como experimento de diagnóstico: lo hice con el menú «/» y los puntos de bloque y fue un error.
BlockNote sustituye a Crepe
Cambié de herramienta: BlockNote con el adaptador Mantine oficial. Trae de fábrica el menú «/», el asa de puntos por bloque y el menú de selección con cambio de párrafo a H1-H3, todo lo que exigíamos. Entra y sale Markdown, así que el campo content de la API no cambia: migración cero de datos. Milkdown desinstalado, 155 paquetes fuera. Después de tres ajustes de CSS (tema, espaciados, selección teal), el editor pasó todas las pruebas y entró en producción.
Biblioteca de medios propia
Hasta ahora las imágenes iban como data-URLs base64, el fichero entero incrustado dentro del texto: llenaban la base de datos y las páginas. Ahora hay una tabla media, archivos en un volumen Docker (un disco virtual del contenedor) y una página /admin/medios con rejilla, subida múltiple, vista previa y copia de URL. El editor sube con uploadFile. SVG rechazado por XSS almacenado (un SVG puede llevar código ejecutable dentro) y máximo 25 MB por archivo.
WebP y 2.000px automáticos
Toda imagen subida se convierte a WebP, un formato moderno mucho más ligero, calidad 85 y, si supera 2.000x2.000, se reduce manteniendo la proporción. EXIF transpose incluido para fotos de móvil (el giro de la cámara se respeta). Un PNG de 4.000x1.000 pasa a WEBP de 2.000x500.
Dos 520, dos causas
Un 520 es un error del servidor intermedio que esconde una causa que él mismo no sabe decir. El primero era estructural: en la red edge compartida del servidor, otro proyecto tiene un servicio llamado web, y el DNS de Docker (la libreta interna que traduce nombres a direcciones) resolvía el nombre a dos contenedores. NPM repartía las peticiones al azar y la mitad iban a parar a un servidor que no es el mío. Alias único web-base-astro y solucionado.
El segundo era mío: el nuevo MediaLibrary leía localStorage (la memoria propia del navegador) dentro del useState. Las islas de Astro (trozos interactivos de la página) se renderizan también en el servidor, donde localStorage no existe, y la isla explotaba a mitad de respuesta. Valor por defecto en render y lectura dentro de useEffect.
Fases 1 y 2: carpetas y ajustes
Fase 1 de medios: carpetas jerárquicas con arrastrar y soltar, metadatos (nombre, alt, descripción), selección múltiple y vista en tarjetas o lista. Fase 2: sección nueva en Ajustes con dimensión máxima y formato (WebP o AVIF) que el servicio lee en cada subida.
Con la spec escrita antes, las fases salieron a la primera, salvo typos. Al día siguiente, la IA entraba de lleno: La IA entra en los Ajustes: proveedores, SEO y artículos automáticos.