Resumen
Refactor de NotebookPage para resolver un bug crítico de usabilidad en móvil: el layout desktop multi-pane (sidebar 280 px + panes editor) colapsaba en viewports estrechos (≤ 1100 px) dejando el canvas de CodeMirror con ~15 mm inutilizables. La solución introduce un render dual sin nuevas rutas Astro dinámicas ni SSR.
Commit de referencia: 8875282 (2026-05-13) — feedback post-push mobile 06a9c51.
Problema original
| Síntoma | Causa |
|---|---|
| Editor de notas casi invisible en móvil | CSS @media max-width:1100px colapsaba a 1 columna pero mantenía height: calc(100vh - 110px) |
| Lista y editor competían por viewport vertical | El layout multi-pane no estaba diseñado para pantallas < 768 px |
| No había forma de navegar atrás desde una nota | Sin historial de browser, el botón físico “atrás” abandonaba la sección |
Solución implementada
1. Detección de dispositivo
const MOBILE_BREAKPOINT = 768;
function detectMobile(): boolean {
if (typeof window === 'undefined') return false;
return window.innerWidth <= MOBILE_BREAKPOINT;
}
Estado reactivo isMobile + listener resize para cubrir rotaciones de pantalla.
2. Render condicional (dos modos)
| Condición | Clase CSS | Descripción |
|---|---|---|
| Mobile sin nota activa | .cuaderno-mobile-list | Vista lista full-screen (sidebar ocupa todo) |
| Mobile con nota activa | .cuaderno-mobile-editor (position: fixed, inset: 0, z-index: 40) | Editor ocupa toda la pantalla |
| Desktop | Sin clase mobile | Multi-pane original intacto (5 layouts) |
3. URL sync via history.pushState
Las rutas mobile se sincronizan con la URL sin rerenders de Astro:
/tools/notebook → vista lista
/tools/notebook?id=N → editor full-screen (nota N abierta)
/tools/notebook?new=1 → crear nueva nota + abrir editor (FAB)
pushNoteIdToUrl(noteId)— escribe?id=No limpia el param al volver a la lista.readNoteIdFromUrl()— hidratación al montar: si la URL ya trae?id=N, abre esa nota en pane 0.popstatelistener — el botón “atrás” del browser cierra el editor mobile y vuelve a la lista (URL queda en/tools/notebook).?new=1consumo único — refnewParamConsumedevita re-crear la nota si el effect se dispara varias veces.
4. Barra superior mobile (.cuaderno-mobile-bar)
Nueva barra fija visible solo en modo editor mobile:
- Botón ‹ Lista (
closeMobileEditor) → limpia pane 0, hacepushNoteIdToUrl(null). - Título de la nota activa (truncado con CSS).
- Botón ↧ Exportar → abre
ExportModal.
5. BottomNav — renombrado divisor
Cambio menor en src/components/shell/BottomNav.tsx:
- “Herramientas” → “Tools” (más corto, terminología del equipo).
- Eliminado ítem “Hub /tools” (redundante: el divisor ya agrupa las 3 tools).
- Resultado: 8 ítems en grupo Operaciones (5 originales + divisor “Tools” + Cuaderno + Quick Links + Direcciones).
Archivos afectados
| Archivo | Tipo de cambio |
|---|---|
src/components/notebook/NotebookPage.tsx | Refactor mayor (+~270 LOC, render dual mobile/desktop) |
src/components/shell/BottomNav.tsx | Fix menor (renombrar divisor, quitar ítem) |
src/styles/tools.css | Nuevas clases .cuaderno-mobile-* |
Comportamiento en desktop
El refactor es aditivo: el modo desktop (isMobile = false) sigue usando exactamente el mismo árbol de componentes multi-pane con sus 5 layouts (1x1, 1x2, 2x1, 1x3, 2x2). Los selectores CSS de layout, los localStorage keys (workspace.notebook.layout, workspace.notebook.paneNoteIds) y el NoteEditor no cambian.
Decisiones técnicas
- Sin nueva ruta Astro dinámica — evita SSR y complejidad de hydration en Astro islands.
history.pushStatemanual — permite URL compartible y back-button nativo sin React Router.useRefparanewParamConsumed— no es estado reactivo; solo guarda si el param?new=1ya fue procesado para no crear notas duplicadas en re-renders.position: fixed; inset: 0para el editor mobile — garantiza que ocupa el 100 % del viewport independientemente del scroll del documento padre.
Véase también
- [[workspace—que-es-workspace]]
- [[workspace—informes]]