Reaktives HTML-Notebook
(maxbo.me)- Alle Schritte – Datenexploration, Analyse, Visualisierung, Satz und Veröffentlichung – lassen sich in einem Dokument bündeln, allein mit einer einzelnen HTML-Datei, die im Browser ausgeführt wird. So kann der bisherige Ablauf Notebook → Publishing-Tool → PDF verkürzt werden.
- Die Kernumsetzung kombiniert Echo-Zellen, die
style- undscript-Elemente sichtbar auf der Seite darstellen, mitcontenteditableund der Observable Runtime, um editierbare reaktive Zellen zu erzeugen. cell(name, inputs, definition)folgt dem Abhängigkeitsmodell von Observable und bildet miteinander verbundene Ausführungseinheiten wiecounter, FizzBuzz-Rendering, versteckte Zellen und ausgabelose Zwischenwert-Zellen.- Im selben HTML-Dokument werden Observable Plot, TeX, Markdown, Graphviz, WASM-basiertes SQLite, Pyodide-basiertes Python, WebR-basiertes R, Observable Inputs und Mutable State demonstriert.
- Das Ergebnis ist ein wissenschaftlich-technisches Dokument, das ohne separate Plattform im Browser läuft; die Implementierung wurde später als Bibliothek
@celine/celineveröffentlicht.
Notebook und Publishing in einer einzelnen HTML-Datei zusammenführen
- HTML wird zwar breit für Satz und Layout genutzt, wurde als Plattform für Datenexploration, Analyse und Visualisierung aber vergleichsweise wenig eingesetzt.
- Ein typischer Workflow führt über mehrere Werkzeuge:
- In interaktiven Notebooks wie Jupyter, RStudio, Pluto.jl oder Observable werden Exploration, Analyse und Visualisierung durchgeführt.
- Anschließend wird das Ergebnis in Publishing-Plattformen wie Typst, Overleaf, LaTeX oder WYSIWYG-Editoren übertragen und gesetzt.
- Für die Veröffentlichung wird nach
.pdfexportiert.
- Wenn eine einzige HTML-Datei diese drei Schritte übernimmt, lassen sich manuelle Arbeitsschritte, CLI-Tools, CI-Schritte und Abhängigkeiten von Drittplattformen reduzieren.
- Die Umsetzung ist stark beeinflusst von Anton Zhiyanovs In-browser code playgrounds, Cristóbal Sciuttos Self-modifying HTML notes, Quarto und dem Observable Framework.
Editierbare Zellen erstellen
- Der erste Schritt besteht darin,
style- undscript-Elemente mit der CSS-Klasseechodirekt im Dokument sichtbar zu machen. - Mit einer Schriftart mit eingebautem Syntax-Highlighting und dem Attribut
contenteditablelässt sich das wie ein einfacher Code-Editor verwenden. - Ein
scriptmitcontenteditablewird erneut ausgewertet, wenn es den Fokus verliert.- Dazu wird das bestehende
scriptgeklont, ein neuesscript-Element erstellt und das Original entfernt. - Dieser Ansatz ist nötig, weil
evalbei Code mitimport-Anweisungen nicht funktioniert.
- Dazu wird das bestehende
- Für die Observable-basierte Ausführung werden zwei Bibliotheken geladen:
- Global werden nur die beiden Symbole
libraryundcelloffengelegt.
Reaktiver Datenfluss auf Basis der Observable Runtime
- Die Funktion
cell(name, inputs, definition, observerVisibility)definiert Zellen über den Modul-Scope der Observable Runtime. - Wenn bereits eine Variable mit demselben Namen existiert, wird die vorhandene Variable verwendet; andernfalls wird eine neue Variable erstellt und definiert.
- Dank dieser Struktur wird eine neue Definition beim erneuten Auswerten einer
contenteditable-Zelle an die verbundenen Zellen weitergereicht.
- Dank dieser Struktur wird eine neue Definition beim erneuten Auswerten einer
- Der Observable
Inspectorwird verwendet, um die Ausgabe einer Zelle oberhalb desscript-Blocks anzuzeigen. - Die Zelle
counterist als asynchroner Generator aufgebaut, der jede Sekunde eine Zahl ausgibt.- Das
id-Attribut desscriptund das Argumentnamevoncellsind identisch. - Wenn der Anfangswert
igeändert und anschließend der Fokus entfernt wird, wird der neue Wert übernommen.
- Das
- Auch Zellen, die von
counterabhängen, lassen sich erstellen.- Hypertext Literal wird geladen, um den Wert von
counterals FizzBuzz zu rendern. htlimplementiert einen HTML5-Parser und behandelt automatisches Escaping sowie die Interpolation nicht serialisierbarer Werte wie Event Listener, Style-Objekte und DOM-Nodes.
- Hypertext Literal wird geladen, um den Wert von
Sichtbare Zellen, versteckte Zellen und Zellen ohne Ausgabe
- Wird die Klasse
echonicht hinzugefügt, lässt sich die Zelldefinition verbergen, während die Ausgabe beobachtet werden kann.- Solche Zellen können als Rendering-Primitiven genutzt werden.
- Es lassen sich auch Zelltypen erstellen, die gar keine Ausgabe anzeigen.
silent(name, inputs, definition)setztobserverVisibilityauf verborgen und kann zum Speichern von Zwischenwerten oder Datenstrukturen genutzt werden.
- Zellen können unabhängig von ihrer Deklarationsreihenfolge funktionieren.
- Im Beispiel hängt die Zelle
reallyNegativevon der später deklarierten Zellenegativeab.
- Im Beispiel hängt die Zelle
- Derselbe Zellwert kann auch für komplexere Ausgaben genutzt werden.
- Observable Plot wird geladen, um ein Zahlen-Array als Liniendiagramm zu zeichnen und an der Position
counter % 40eine vertikale Regelmarke anzuzeigen.
- Observable Plot wird geladen, um ein Zahlen-Array als Liniendiagramm zu zeichnen und an der Position
Erweiterung von Dokument- und Visualisierungsausgaben
- Zellen können beliebige DOM-Elemente zurückgeben.
- Die Demos für TeX, Markdown und Graphviz geben jeweils unterschiedliche Elementtypen zurück.
- Die Zelle
texgibt einspan-Element zurück. - Die Zelle
mdgibt eintable-Element zurück. - Die Zelle
dotgibt einsvg-Element zurück.
- Die Zelle
- Zellen können ein
Promisezurückgeben oder einenErrorwerfen.- Der Observable
Inspectorwendet auf das äußerediveiner laufenden Zelle die Klasseobservablehq--runningan. - Auf fehlerhafte Zellen wird die Klasse
observablehq--errorangewendet. - Das Dokument legt passend zu diesen Zuständen Styles fest.
- Der Observable
SQLite, Python und R im Browser
- Die SQLite-Demo stellt die Chinook sample database unter
https://maxbo.me/chinook.dbbereit und fragt sie mit einem WASM-basierten SQLite-Client ab.- Man kann der SQL-Abfrage
WHERE Milliseconds < 1000000hinzufügen.
- Man kann der SQL-Abfrage
- Die Python-Demo nutzt die Pyodide-Distribution von CPython als WASM.
- Pyodide enthält NumPy, Pandas, Matplotlib, scikit-learn und Scipy.
- Der zuvor erstellte Plot wird mit Matplotlib und dem Python-Modul
sqlite3erneut erzeugt.
- Die R-Demo nutzt WebR.
Eingaben und veränderbarer Zustand
- Der Zelltyp
viewofwurde für die Verwendung mit Observable Inputs erstellt. viewofdeklariert zwei reaktive Zellen:NAME: der Wert der Eingabeviewof NAME: das DOM-Element selbst
- Um die Eingabe oberhalb der Zelle anzuzeigen, wird die Zell-
idaufviewof NAMEgesetzt. - Im Beispiel wird mit
Inputs.range([0, 100], { step: 1 })eine Range-Eingabe erstellt, und die ZellerangePlotaktualisiert sich entsprechend diesem Wert. - Die Funktionsweise von Observable Inputs ist etwas schwer nachvollziehbar; die Demo Synchronized Inputs kann dabei helfen.
- Für Fälle, in denen ein rein funktionaler Datenfluss nicht ausreicht, wurde außerdem ein
mutable-Helper erstellt.mutableregistriert einMutable-Objekt, das bei Wertänderungen den neuen Wert ausgibt.
Veröffentlichung der Bibliothek und Folien für Präsentationen
- Für diese Implementierung war zunächst der Name
incelangedacht, später sollte siecelineheißen. - Die Bibliothek wurde als @celine/celine veröffentlicht.
- Dieses Dokument wurde bei SydJS demonstriert.
- Es enthält auch Code, um das Dokument in eine Slideshow zu verwandeln.
Shift+N: Slideshow starten oder nächste FolieShift+B: vorherige FolieShift+E: Slideshow beenden
- Der Implementierungsquellcode ist als source code veröffentlicht.
1 Kommentare
Meinungen auf Hacker News
Der Grundannahme des Beitrags stimme ich nachdrücklich zu. HTML kann eine hervorragende Grundlage für rechnerische Notebooks sein.
Allerdings gefallen mir die hier getroffenen Implementierungsentscheidungen nicht. Dynamisches, reaktives HTML lässt sich deutlich deklarativer gestalten, und Observable ist zwar großartig, weicht aber in mancher Hinsicht von Standard-JavaScript ab.
Ich habe begonnen, ein reaktives HTML-System namens Heximal zu bauen, in das später auch Notebook-Unterstützung einfließen soll. Es ist ein deklaratives System auf Basis von HTML-Templates und Custom Elements, darüber liegt ein Ausdrucks-/Reaktivitätssystem auf Grundlage des TC39-Signals-Vorschlags.
https://github.com/elematic/heximal
Es fühlt sich eher wie eine Mischung aus HTMX, Tangle, Curvenote und Polymer an; wenn HTML von Anfang an reaktiv gewesen wäre, sähe es vermutlich so aus. Ich denke, es passt ziemlich gut zu Anwendungsfällen wie grafischer Bearbeitung und Notebooks.
Deshalb funktionieren in Observablehq normale JavaScript-Debugging-Ausdrücke korrekt.
https://github.com/observablehq/parser
Ich mag die Observable-Runtime so sehr, dass ich sogar einen Decompiler gebaut habe, der zwischen Frontend-Quellcode und der kompilierten reinen JS-Darstellung in beide Richtungen konvertieren kann.
https://observablehq.com/@tomlarkworthy/observablejs-toolcha...
Wirklich cool. Besonders die Python- und SQLite-Demos haben mir gefallen. Interessant ist, dass es beim Editier-Loop über das Web (TTW) ansetzt und das als Quine bezeichnet; das ist zwar plausibel, aber die zentrale Schwachstelle ist Persistenz.
TiddlyWiki macht im Grunde dasselbe, aber das Speichern der Arbeit ist mühsam. Denn um etwas dauerhaft zu speichern, braucht man am Ende doch einen separaten Headless-Serverprozess.
Als ich etwas Ähnliches bauen wollte[0], habe ich persönlich bei der Persistenz angefangen, und das bedeutete natürlich Dateien. Es waren Plain-Text-Dateien, die man mit einem Editor für Programmierer bearbeiten und über das Dateisystem speichern und deployen konnte. So vermeidet man das Problem, im Browser erneut einen Programmiereditor implementieren zu müssen – und das ist alles andere als trivial.
Stattdessen hat man das Problem, einen schnellen Server schreiben zu müssen, aber das ist auch ein ziemlich interessantes Problem[1]. Hier wurde es durch die Verwendung einfacher contenteditable-Abschnitte umgangen, aber nach der Persistenz ist es das nächstgrößere Problem.
Dass man innerhalb eingebetteter Skripte kein export verwenden kann, wirkt wie dieselbe Wand. Um das globale window-Objekt nicht zu vermüllen, wäre so eine Funktion wünschenswert, und in meinem Anwendungsfall würde sie auch die Codegenerierung erleichtern.
0 - Literate Markdown: https://simpatico.io/lit.md
1 - Reflector, ein kleiner node-Server, der Markdown transformiert, komprimiert und cached sowie Invalidierung per File-Watching durchführt: https://simpatico.io/reflector
Ich respektiere die Gedanken und die Mühe, die hineingeflossen sind, aber die Usability dieses Ansatzes ist furchtbar. Ich verstehe nicht, warum man sich beim explorativen Analysieren von Daten auch noch um Styling-Elemente kümmern soll.
Genau deshalb funktionieren Tools wie Jupyter Notebooks so gut. Trotzdem schätze ich es sehr, dass hier aus Neugier eine alternative Idee umgesetzt wurde.
Als Plattform für explorative Datenanalyse ist es noch nicht ausreichend. Es bietet weder die unmittelbare Reaktivität eines vollständigen Web-Code-Editors wie Observable Notebooks, noch ist es das Observable Framework mit dateiüberwachtem Hot Reloading. Auch der neue Jupyter-Kernel für Deno + VSCode bietet eine ziemlich reibungslose Erfahrung.
Für explorative Analyse ist die Usability also, nun ja … schlecht, da stimme ich zu. Aber die Usability bei der Bereitstellung finde ich gar nicht so schlecht. Eigentlich ist sie gut. Es ist schlicht eine einzelne Datei. Wenn man jemandem einen Graphen und Ergebnisse der Datenaufbereitung schicken will, muss man keine riesige Toolchain pflegen oder für einen Drittanbieter-Service zahlen; man wirft einfach eine HTML-Datei in Slack oder hostet sie irgendwo.
Die Flexibilität, Analyseergebnisse zu stylen, erlaubt außerdem die Veröffentlichung in Umgebungen, in denen Stil wichtig ist, etwa Blogs oder wissenschaftlichen Arbeiten.
ivoryzuredgeändert wird und sich das Syntax-Highlighting in Echtzeit ändert, zeigt das gut.Ich verstehe, wie man hierher gekommen ist, aber es ist schade, dass man mit JavaScript-Frameworks und -Libraries im Browser nicht leichter herumspielen kann. Am Ende ist es einfach JS, und man sollte in einer leichtgewichtigen Umgebung schnell experimentieren können. Genau darin liegt die Stärke dieses Ansatzes.
Es ist eine Art, die spielerischen und experimentellen Möglichkeiten von HTML/JS zurückzubringen. Ich würde mir mehr solcher Ansätze wünschen statt TS, rollup, webpack und dergleichen.
Nachtrag: Nachdem ich etwas mehr gelesen habe, wird das hier als Tool für Datenanalyse vorgeschlagen. Für diesen Zweck halte ich es nicht für gut geeignet.
Ich sehe großen Wert in einer Alternative zu Jupyter Notebooks, die nicht weggeworfen wird. Dass die aktuelle User Experience wirklich miserabel ist, lässt sich verbessern. Die Frage ist, ob diese Struktur besser ist als das JSON-Chaos von Jupyter Notebooks; persönlich tendiere ich zu Ja.
Der ursprüngliche Autor von Lit hat Google kürzlich verlassen und arbeitet meines Wissens an etwas sehr Ähnlichem.
https://github.com/elematic/heximal
Mir gefällt die Form dieses Beitrags. Er beginnt ganz unten und baut bis zu etwas Interessantem auf. Es ist kein aufgeblasener Ablauf mit einem Haufen Dependencies oder überall angeflanschten Frameworks.
Um ihm zu folgen, müsste man wohl die gezeigten Stücke kopieren und einfügen und Schritt für Schritt verstehen, wie sie funktionieren. Wenn ich so etwas schreiben würde, hätte ich es wahrscheinlich als Literate Programming in org-mode verfasst und dann als HTML exportiert, um daraus einen Blogbeitrag zu machen.
Allerdings bleiben mir Fonts mit eingebautem Syntax-Highlighting weiterhin suspekt.
Es heißt reaktives HTML, aber ist das nicht fast alles JavaScript, oder verstehe ich da etwas falsch?
Abgesehen vom Browser hängt es nicht von Drittanbieter-Software ab und ist, wie HTML nun einmal ist, langlebig, portabel und einfach zu verwenden.
Ich versuche demnächst, diese Art von HTML-Notebooks in Raku oder für Raku zu unterstützen.
Derzeit basieren Rakus „Notebook-Lösungen“ entweder auf Jupyter oder auf Mathematica.
Ich verstehe die Logik nicht so recht, diesen Prozess durch Webtechnologien komplizierter zu machen. Welches Problem löst die zusätzliche Komplexität? Eher macht sie den Ablauf langsamer und entfernt benötigte Funktionen wie Autovervollständigung, Snippets und Erweiterungen
Außerdem ist es gut möglich, dass andere mit diesem Setup nicht vertraut sind, was die Zusammenarbeit zusätzlich erschwert
Die Umgebung richtet man einmal ein und nutzt sie dann weiter, indem man mit dem Wandel der Technik nur kleine Verbesserungen ergänzt
Als n/vim-Nutzer kann man mit Folgendem völlig auskommen. 1- Datenexploration: Mit https://github.com/untitled-ai/jupyter_ascending verbindet man Text mit einem Jupyter Notebook. Da jupytext verwendet wird, kann man Code effizient im Texteditor bearbeiten und ausführen
2- Schreiben: Für LaTeX kann man https://github.com/lervag/vimtex verwenden
Wenn man darauf tmux und tmuxp legt, kann man Projekte sofort öffnen. In Emacs lässt sich mit org mode[1][2] und/oder Auctex ein nahtloser Prozess bauen
[1] https://sqrtminusone.xyz/posts/2021-05-01-org-python/
[2] https://martibosch.github.io/jupyter-emacs-universe/
Allerdings mag ich Python nicht besonders, daher möchte ich Werkzeuge verwenden, die nicht Python-zentriert sind. Deshalb habe ich Raku und Mathematica erwähnt
Danke übrigens für den Hinweis auf https://github.com/imbue-ai/jupyter_ascending
Der Grundannahme dieses Artikels, nämlich dass HTML als Distributionsmedium für wissenschaftliches Schreiben nicht ausreichend genutzt wird, stimme ich weitgehend zu
Dazu gibt es neuere Arbeiten von Will Crichton
https://willcrichton.net/nota/
https://willcrichton.net/notes/portable-epubs/
Notebooks sind derzeit ziemlich angesagt. Wir haben auch eine Version eines TypeScript-Notebooks veröffentlicht[1], haben bei den Trade-offs aber eine recht andere Richtung gewählt
Wir wollten Backend-Code in node ausführen, daher versuchen wir, anders als der Ansatz in diesem Artikel oder Observable, nicht in einer Browserumgebung zu laufen. Für viele Anwendungen könnte diese Idee aber dennoch die bessere Lösung sein
Applaus für den Autor
https://github.com/srcbookdev/srcbook