Cuaderno HTML reactivo
(maxbo.me)- Con solo un archivo HTML único que se ejecuta en el navegador, es posible reunir en un solo documento la exploración, el análisis, la visualización de datos, la composición tipográfica y la publicación, reduciendo así el flujo tradicional de cuaderno → herramienta de publicación → PDF
- La implementación central combina celdas echo que muestran
styleyscripten pantalla,contenteditabley Observable Runtime para crear celdas reactivas editables cell(name, inputs, definition)sigue el modelo de dependencias de Observable para componer unidades de ejecución conectadas entre sí, comocounter, el renderizado de FizzBuzz, celdas ocultas y celdas intermedias sin salida visible- Dentro del mismo documento HTML se demuestran Observable Plot, TeX, Markdown, Graphviz, SQLite basado en WASM, Python basado en Pyodide, R basado en WebR, Observable Inputs y estado mutable
- El resultado es un documento científico o técnico que se ejecuta en el navegador sin una plataforma adicional, y más adelante la implementación se publicó como la librería
@celine/celine
Reducir la separación entre cuaderno y publicación con un solo archivo HTML
- HTML se usa ampliamente para composición tipográfica, pero se ha aprovechado relativamente menos como plataforma de exploración, análisis y visualización de datos
- El flujo de trabajo habitual pasa por varias herramientas
- Si un solo archivo HTML asume estas tres etapas, se pueden reducir procedimientos manuales, herramientas CLI, pasos de CI y la dependencia de plataformas de terceros
- La implementación recibe una fuerte influencia de In-browser code playgrounds de Anton Zhiyanov, Self-modifying HTML notes de Cristóbal Sciutto, Quarto y Observable Framework
Cómo crear celdas editables
- El primer paso consiste en usar la clase CSS
echopara hacer que los elementosstyleyscriptse vean tal cual dentro del documento - Si además se agrega una fuente con resaltado de sintaxis integrado y el atributo
contenteditable, puede usarse como un editor de código básico - Un
scriptconcontenteditablese vuelve a evaluar cuando pierde el foco- Se clona el
scriptexistente para crear un nuevo elementoscripty se elimina el original - Este método es necesario porque
evalno funciona con código que contiene sentenciasimport
- Se clona el
- Para la ejecución basada en Observable se cargan dos librerías
- En el ámbito global solo se exponen dos símbolos:
libraryycell
Flujo de datos reactivo con Observable Runtime
- La función
cell(name, inputs, definition, observerVisibility)define celdas usando el alcance de módulo de Observable Runtime - Si ya existe una variable con el mismo nombre, recupera la variable existente; si no, crea una nueva y la define
- Gracias a esta estructura, al reevaluar una celda
contenteditable, la nueva definición se propaga a las celdas conectadas
- Gracias a esta estructura, al reevaluar una celda
Inspectorde Observable se usa para mostrar la salida de la celda encima del bloquescript- La celda
counterestá compuesta como un generador asíncrono que emite un número cada segundo- El atributo
iddelscripty el argumentonamedecellson iguales - Si se cambia el valor inicial
iy luego se quita el foco, se refleja el nuevo valor
- El atributo
- También se pueden crear celdas que dependan de
counter- Se carga Hypertext Literal para renderizar el valor de
counteren formato FizzBuzz htlimplementa un parser de HTML5 y maneja el escape automático, listeners de eventos, objetos de estilo e interpolación de valores no serializables como nodos DOM
- Se carga Hypertext Literal para renderizar el valor de
Celdas visibles, ocultas y sin salida
- Si no se agrega la clase
echo, se puede ocultar la definición de la celda y aun así observar su salida- Estas celdas pueden usarse como primitivas de renderizado
- También se puede crear un tipo de celda que no muestre la salida en absoluto
silent(name, inputs, definition)configuraobserverVisibilitycomo oculto para usarla con valores intermedios o almacenamiento de estructuras de datos
- Las celdas pueden funcionar sin importar el orden de declaración
- En el ejemplo, la celda
reallyNegativedepende de la celdanegative, declarada más adelante
- En el ejemplo, la celda
- El mismo valor de una celda también puede usarse en salidas más complejas
- Se carga Observable Plot para dibujar un gráfico de líneas a partir de un arreglo de números y mostrar una regla vertical en la posición
counter % 40
- Se carga Observable Plot para dibujar un gráfico de líneas a partir de un arreglo de números y mostrar una regla vertical en la posición
Ampliación de las salidas de documento y visualización
- Una celda puede devolver cualquier elemento DOM
- Las demos de TeX, Markdown y Graphviz devuelven cada una un tipo distinto de elemento
- La celda
texdevuelve un elementospan - La celda
mddevuelve un elementotable - La celda
dotdevuelve un elementosvg
- La celda
- Una celda puede devolver un
Promiseo lanzar unErrorInspectorde Observable aplica la claseobservablehq--runningaldivexterno de una celda en ejecución- A una celda con error le aplica la clase
observablehq--error - El documento define estilos de acuerdo con esos estados
SQLite, Python y R dentro del navegador
- La demo de SQLite publica la Chinook sample database en
https://maxbo.me/chinook.dby la consulta con un cliente SQLite basado en WASM- Se puede probar agregando
WHERE Milliseconds < 1000000a la consulta SQL
- Se puede probar agregando
- La demo de Python usa la distribución WASM de CPython de Pyodide
- Pyodide incluye NumPy, Pandas, Matplotlib, scikit-learn, Scipy
- Reproduce el gráfico creado antes usando Matplotlib y el módulo
sqlite3de Python
- La demo de R usa WebR
- No se logró implementar el renderizado con ggplot2, pero parece posible
- Esa celda parece algo inestable en iOS y no se investigó más
Entradas y estado mutable
- El tipo de celda
viewofse creó para usarse junto con Observable Inputs viewofdeclara dos celdas reactivasNAME: el valor de la entradaviewof NAME: el propio elemento DOM
- Para mostrar la entrada encima de la celda, hay que establecer el
idde la celda comoviewof NAME - En el ejemplo se crea una entrada de rango con
Inputs.range([0, 100], { step: 1 }), y la celdarangePlotse actualiza según ese valor - El funcionamiento de Observable Inputs es algo difícil de entender, y la demo Synchronized Inputs puede ser útil
- También se creó el helper
mutablepara los casos en que no basta con un flujo de datos puramente funcionalmutableregistra un objetoMutableque emite un nuevo valor cuando este cambia
Publicación de la librería y diapositivas para presentaciones
- Al principio se pensó en el nombre
incelpara esta implementación, pero luego se decidió llamarlaceline - La librería se publicó como @celine/celine
- Este documento se mostró en SydJS
- También incluye código para convertir el documento en una presentación de diapositivas
Shift+N: iniciar la presentación o avanzar a la siguiente diapositivaShift+B: volver a la diapositiva anteriorShift+E: salir de la presentación
- La implementación está publicada como source code
1 comentarios
Comentarios en Hacker News
Coincido fuertemente con la premisa del artículo. HTML puede ser una base excelente para cuadernos computacionales
Dicho eso, no me convence la elección de implementación aquí. El HTML dinámico y reactivo puede hacerse de forma mucho más declarativa que esto, y Observable es genial, pero en ciertos aspectos se aparta del JavaScript estándar
Empecé a crear un sistema de HTML reactivo llamado Heximal, y más adelante planeo agregar soporte para cuadernos. Es un sistema declarativo basado en plantillas HTML y elementos personalizados, con un sistema de expresiones/reactividad encima basado en la propuesta TC39 Signals
https://github.com/elematic/heximal
Se parece más a una mezcla de HTMX, Tangle, Curvenote y Polymer, y siento que así se vería HTML si hubiera sido reactivo desde el principio. Creo que encaja bastante bien con casos de uso de edición gráfica y cuadernos
Por eso las expresiones normales de depuración de JavaScript funcionan correctamente en Observablehq
https://github.com/observablehq/parser
Me gusta tanto el runtime de Observable que también hice un decompilador que puede convertir en ambos sentidos entre el código fuente del frontend y su representación compilada en JS puro
https://observablehq.com/@tomlarkworthy/observablejs-toolcha...
Está realmente genial. Me gustó especialmente la demo de Python y SQLite. Me parece interesante que parta de un ciclo de edición a través de la web (TTW) y lo llame un quine; tiene sentido, pero su debilidad principal es la persistencia
TiddlyWiki en esencia hace lo mismo, pero guardar el trabajo es un dolor de cabeza. Porque para que algo persista, al final necesitas un proceso de servidor headless aparte
Cuando quise crear algo parecido[0], personalmente empecé por la persistencia, y eso naturalmente significaba archivos. Eran archivos de texto plano que podían editarse con un editor para programadores y guardarse/publicarse a través del sistema de archivos. Así se evita el problema de tener que reimplementar dentro del navegador un editor para programadores, que no es nada trivial
A cambio aparece el problema de tener que escribir un servidor rápido, pero también es un problema bastante divertido[1]. Aquí esquivaron eso usando secciones básicas de contenteditable, pero después de la persistencia, ese es el siguiente problema más grande
El hecho de que no se pueda hacer
exportdentro de scripts embebidos también parece la misma pared. Sería bueno tener esa capacidad para no ensuciar el objeto global window, y en mi caso de uso también haría más fácil la generación de código0 - Literate Markdown: https://simpatico.io/lit.md
1 - Reflector, un pequeño servidor node que transforma, comprime y cachea markdown, y hace invalidación por vigilancia de archivos: https://simpatico.io/reflector
Respeto las ideas y el esfuerzo invertido, pero la usabilidad de este enfoque es terrible. Si estoy tratando de hacer análisis exploratorio de datos, no entiendo por qué también tendría que preocuparme por elementos de estilo
Justamente por eso herramientas como Jupyter Notebook funcionan tan bien. Aun así, valoro mucho que hayan implementado una idea alternativa con curiosidad
Todavía no es suficiente como plataforma de análisis exploratorio de datos. No tiene la capacidad de respuesta inmediata de un editor web de código completo como Observable Notebooks, ni es Observable Framework con hot reloading basado en file watching. El nuevo kernel de Jupyter para Deno + VSCode también ofrece una experiencia bastante fluida
Así que sí, diría que la usabilidad para análisis exploratorio es, mm… mala, pero no me parece que la usabilidad para despliegue sea tan mala. De hecho, es buena. Porque simplemente es un solo archivo. Si quieres enviarle a alguien una gráfica y el resultado del procesamiento de datos, no necesitas mantener una cadena enorme de herramientas ni pagar por un servicio de terceros; basta con mandar un archivo HTML por Slack o alojarlo en algún lado
Gracias a la flexibilidad para dar estilo a los resultados del análisis, también se puede publicar en entornos donde el estilo importa, como blogs o artículos de investigación
ivoryporredy el resaltado de sintaxis se actualiza en tiempo real lo muestra bienEntiendo el contexto que llevó a esto, pero da pena que no sea más fácil jugar con frameworks y bibliotecas de JavaScript en el navegador. Al final solo es JS; debería poder experimentarse rápido en un entorno liviano. Este enfoque tiene fortaleza en ese punto
Es una manera de recuperar el potencial juguetón y experimental de HTML/JS. Ojalá hubiera más intentos así que cosas como TS, rollup o webpack
Extra: leyendo un poco más, veo que lo están proponiendo para análisis de datos. Para ese uso, no me parece que encaje bien
Creo que hay mucho valor en una alternativa a Jupyter Notebook que no termine siendo algo para tirar. La experiencia de usuario actual sí está muy mal, pero eso se puede mejorar. La cuestión es si esta configuración es mejor que el caos en JSON de Jupyter Notebook, y en lo personal creo que sí
El autor original de Lit dejó Google recientemente y, hasta donde sé, está trabajando en algo muy parecido a esto
https://github.com/elematic/heximal
Me gusta el formato de este texto. Empieza desde lo más básico y va construyendo hasta llegar a algo interesante. No sigue esa corriente exagerada de meter un montón de dependencias o pegar frameworks por todos lados
Para seguirlo, parecería que basta con copiar y pegar los fragmentos presentados y entender paso a paso cómo funcionan. Si yo escribiera algo así, probablemente lo haría con programación literaria en org-mode y luego lo exportaría a HTML para convertirlo en una entrada de blog
Aun así, sigo teniendo mis dudas sobre la tipografía con resaltado de sintaxis integrado
Le dicen HTML reactivo, pero ¿no es casi puro JavaScript, o estoy entendiendo algo mal?
Fuera del navegador, no depende de software de terceros y, como HTML, es duradero, portable y fácil de escribir
Pronto intentaré dar soporte a este tipo de notebook HTML en Raku, o para Raku
En este momento, las “soluciones de notebook” de Raku están basadas en Jupyter o en Mathematica
No entiendo bien la lógica de usar tecnologías web para complicar este proceso. ¿Qué resuelve la complejidad añadida? Más bien parece que lo vuelve más lento y elimina funciones necesarias como autocompletado, snippets y extensiones
Además, es muy probable que otras personas no estén familiarizadas con esta configuración, así que colaborar también se vuelve más difícil
El entorno se configura una sola vez y luego se puede seguir usando, agregando solo pequeñas mejoras a medida que cambia la tecnología
Si usas n/vim, con esto basta. 1- Exploración de datos: usa https://github.com/untitled-ai/jupyter_ascending para conectar texto con notebooks de Jupyter. Como usa jupytext, puedes editar y ejecutar código de forma eficiente desde el editor de texto
2- Escritura: puedes usar https://github.com/lervag/vimtex para LaTeX
Si además le sumas tmux y tmuxp, puedes abrir proyectos al instante. En Emacs, se puede crear un flujo de trabajo fluido con org mode[1][2] y/o Auctex
[1] https://sqrtminusone.xyz/posts/2021-05-01-org-python/
[2] https://martibosch.github.io/jupyter-emacs-universe/
Pero no me gusta mucho Python, así que quiero usar herramientas que no estén centradas en Python. Por eso mencioné Raku y Mathematica
Por cierto, gracias por mostrarme https://github.com/imbue-ai/jupyter_ascending
En general, estoy de acuerdo con la premisa de este texto: que HTML no se está aprovechando lo suficiente como medio de publicación para la escritura científica
Relacionado con esto, está el trabajo reciente de Will Crichton
https://willcrichton.net/nota/
https://willcrichton.net/notes/portable-epubs/
Últimamente los notebooks están en auge. Nosotros también lanzamos una versión de notebooks en TypeScript[1], pero tomamos una decisión bastante distinta en cuanto a los trade-offs
Queríamos ejecutar código de node en el backend, así que, a diferencia del enfoque de este texto u Observable, no intentamos ejecutarlo en el entorno del navegador. Aun así, para muchas aplicaciones esta idea podría ser una mejor solución
Aplausos para el autor
https://github.com/srcbookdev/srcbook