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
| Modo | Tecla visual | Descripción |
|---|---|---|
| Source (default) | 📝 MD | Solo el editor CodeMirror con resaltado estilo Notepad++ |
| Split | ⊟ Split | Source a la izquierda (50 %) + Preview HTML a la derecha (50 %) |
| Preview | 👁 Preview | Solo 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 sichecked - 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ón | Razón |
|---|---|
| Scroll independiente en split (sin sync) | Simplicidad inicial; se puede añadir scroll-sync si la UX lo pide |
dangerouslySetInnerHTML | El 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 marked | Evita saltos de línea no deseados en listas y párrafos cortos |
Modo default 'source' | Mantiene el comportamiento anterior para usuarios existentes |
Archivos afectados
| Archivo | Cambio |
|---|---|
src/components/notebook/NoteEditor.tsx | Estado viewMode + liveDoc + useMemo(previewHtml) + layout condicional |
src/components/notebook/Toolbar.tsx | Export 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-labelpero los checkboxes generados por marked sondisabledpor 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]]