CreaRack-SL

Cuaderno: Toggle de vista previa (Source / Split / Preview) en tiempo real

Resumen

El editor del Cuaderno (módulo de notas del Workspace) incorpora tres modos de visualización que el usuario puede alternar desde la barra de herramientas. El modo seleccionado se persiste en localStorage para sobrevivir recargas de página.

Los tres modos

ModoTecla visualDescripción
Source (default)📝 MDSolo el editor CodeMirror con resaltado estilo Notepad++
Split⊟ SplitSource a la izquierda (50 %) + Preview HTML a la derecha (50 %)
Preview👁 PreviewSolo el panel de preview HTML renderizado; CodeMirror oculto

Comportamiento técnico

Renderizado live (sin esperar al auto-save)

El listener EditorView.updateListener extrae el contenido del documento en cada docChanged y actualiza el estado liveDoc inmediatamente. El useMemo de previewHtml recalcula el HTML resultante en cada cambio de liveDoc o de viewMode. Esto garantiza que el preview refleja cada pulsación de tecla sin depender del auto-save (que tiene un debounce de varios segundos).

keystroke → updateListener → setLiveDoc → useMemo(marked.parse) → dangerouslySetInnerHTML

Librería de renderizado

Se usa marked con el perfil GFM activado (gfm: true, breaks: false). Esto soporta:

  • Tablas GFM (pipe tables)
  • Tareas (- [ ] / - [x]) — renderizadas como <input type="checkbox"> con tachado CSS si checked
  • Strikethrough (~~texto~~)

El parsing se ejecuta en modo síncrono (async: false) para simplificar el flujo de React.

Persistencia del modo

// Lectura inicial (lazy initializer del useState)
(localStorage.getItem('workspace.notebook.viewMode') as ViewMode) ?? 'source'

// Persistencia en cada cambio
useEffect(() => {
  localStorage.setItem('workspace.notebook.viewMode', viewMode);
}, [viewMode]);

La clave de localStorage es workspace.notebook.viewMode. Valores posibles: 'source', 'split', 'preview'.

Estructura CSS

El contenedor raíz cambia de clase según el modo:

.cuaderno-view-source   → .cuaderno-cm-container tiene flex:1
.cuaderno-view-preview  → .cuaderno-preview tiene flex:1
.cuaderno-view-split    → ambos tienen flex: 1 1 50% con separador border-right

El panel .cuaderno-preview comparte las CSS custom properties del tema MD del editor (--cuaderno-color-h1, --cuaderno-color-code, etc.), por lo que el Tema MD elegido se aplica de forma idéntica en source y en preview.

Decisiones de diseño

DecisiónRazón
Scroll independiente en split (sin sync)Simplicidad inicial; se puede añadir scroll-sync si la UX lo pide
dangerouslySetInnerHTMLEl HTML lo genera marked a partir del propio contenido del usuario; el riesgo XSS es bajo en un contexto de notas personales
breaks: false en markedEvita saltos de línea no deseados en listas y párrafos cortos
Modo default 'source'Mantiene el comportamiento anterior para usuarios existentes

Archivos afectados

ArchivoCambio
src/components/notebook/NoteEditor.tsxEstado viewMode + liveDoc + useMemo(previewHtml) + layout condicional
src/components/notebook/Toolbar.tsxExport ViewMode, props viewMode/onChangeViewMode, grupo de botones
src/styles/tools.css~120 líneas nuevas: .cuaderno-view, .cuaderno-preview y todos sus estilos hijos

Limitaciones conocidas / trabajo futuro

  • Scroll sync en split: los paneles no están sincronizados; puede resultar confuso en notas largas.
  • Sanitización HTML: no se usa DOMPurify; apropiado para notas propias, no recomendable si el contenido viene de terceros.
  • Highlighting en preview: el bloque <pre><code> no tiene resaltado de sintaxis (solo estilo CSS); se podría integrar highlight.js.
  • Accesibilidad: el panel preview tiene aria-label pero los checkboxes generados por marked son disabled por defecto; revisar si deben ser interactivos.

Véase también

  • [[workspace—que-es-workspace]]
  • [[workspace—tareas-notas]]
  • [[entity—cuaderno—component—noteeditor]]
  • [[entity—cuaderno—component—toolbar]]
  • [[workspace—informes]]