CreaRack-SL

Cuaderno — Vista full-screen mobile y URL sync

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íntomaCausa
Editor de notas casi invisible en móvilCSS @media max-width:1100px colapsaba a 1 columna pero mantenía height: calc(100vh - 110px)
Lista y editor competían por viewport verticalEl layout multi-pane no estaba diseñado para pantallas < 768 px
No había forma de navegar atrás desde una notaSin 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ónClase CSSDescripción
Mobile sin nota activa.cuaderno-mobile-listVista 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
DesktopSin clase mobileMulti-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=N o 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.
  • popstate listener — el botón “atrás” del browser cierra el editor mobile y vuelve a la lista (URL queda en /tools/notebook).
  • ?new=1 consumo único — ref newParamConsumed evita 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, hace pushNoteIdToUrl(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

ArchivoTipo de cambio
src/components/notebook/NotebookPage.tsxRefactor mayor (+~270 LOC, render dual mobile/desktop)
src/components/shell/BottomNav.tsxFix menor (renombrar divisor, quitar ítem)
src/styles/tools.cssNuevas 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.pushState manual — permite URL compartible y back-button nativo sin React Router.
  • useRef para newParamConsumed — no es estado reactivo; solo guarda si el param ?new=1 ya fue procesado para no crear notas duplicadas en re-renders.
  • position: fixed; inset: 0 para 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]]