1 Punkte von GN⁺ 2024-11-19 | 1 Kommentare | Auf WhatsApp teilen
  • 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- und script-Elemente sichtbar auf der Seite darstellen, mit contenteditable und 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 wie counter, 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/celine verö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 .pdf exportiert.
  • 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- und script-Elemente mit der CSS-Klasse echo direkt im Dokument sichtbar zu machen.
  • Mit einer Schriftart mit eingebautem Syntax-Highlighting und dem Attribut contenteditable lässt sich das wie ein einfacher Code-Editor verwenden.
  • Ein script mit contenteditable wird erneut ausgewertet, wenn es den Fokus verliert.
    • Dazu wird das bestehende script geklont, ein neues script-Element erstellt und das Original entfernt.
    • Dieser Ansatz ist nötig, weil eval bei Code mit import-Anweisungen nicht funktioniert.
  • Für die Observable-basierte Ausführung werden zwei Bibliotheken geladen:
  • Global werden nur die beiden Symbole library und cell offengelegt.

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.
  • Der Observable Inspector wird verwendet, um die Ausgabe einer Zelle oberhalb des script-Blocks anzuzeigen.
  • Die Zelle counter ist als asynchroner Generator aufgebaut, der jede Sekunde eine Zahl ausgibt.
    • Das id-Attribut des script und das Argument name von cell sind identisch.
    • Wenn der Anfangswert i geändert und anschließend der Fokus entfernt wird, wird der neue Wert übernommen.
  • Auch Zellen, die von counter abhängen, lassen sich erstellen.
    • Hypertext Literal wird geladen, um den Wert von counter als FizzBuzz zu rendern.
    • htl implementiert einen HTML5-Parser und behandelt automatisches Escaping sowie die Interpolation nicht serialisierbarer Werte wie Event Listener, Style-Objekte und DOM-Nodes.

Sichtbare Zellen, versteckte Zellen und Zellen ohne Ausgabe

  • Wird die Klasse echo nicht 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) setzt observerVisibility auf 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 reallyNegative von der später deklarierten Zelle negative ab.
  • 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 % 40 eine vertikale Regelmarke anzuzeigen.

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 tex gibt ein span-Element zurück.
    • Die Zelle md gibt ein table-Element zurück.
    • Die Zelle dot gibt ein svg-Element zurück.
  • Zellen können ein Promise zurückgeben oder einen Error werfen.
    • Der Observable Inspector wendet auf das äußere div einer laufenden Zelle die Klasse observablehq--running an.
    • Auf fehlerhafte Zellen wird die Klasse observablehq--error angewendet.
    • Das Dokument legt passend zu diesen Zuständen Styles fest.

SQLite, Python und R im Browser

Eingaben und veränderbarer Zustand

  • Der Zelltyp viewof wurde für die Verwendung mit Observable Inputs erstellt.
  • viewof deklariert zwei reaktive Zellen:
    • NAME: der Wert der Eingabe
    • viewof NAME: das DOM-Element selbst
  • Um die Eingabe oberhalb der Zelle anzuzeigen, wird die Zell-id auf viewof NAME gesetzt.
  • Im Beispiel wird mit Inputs.range([0, 100], { step: 1 }) eine Range-Eingabe erstellt, und die Zelle rangePlot aktualisiert 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.
    • mutable registriert ein Mutable-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 incel angedacht, später sollte sie celine heiß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 Folie
    • Shift+B: vorherige Folie
    • Shift+E: Slideshow beenden
  • Der Implementierungsquellcode ist als source code veröffentlicht.

1 Kommentare

 
GN⁺ 2024-11-19
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.

    • „Observable ist zwar großartig, weicht aber von Standard-JS ab“ stimmt für das Frontend, aber die darunterliegende Runtime führt einfach JavaScript aus. Auch der Quellcode ist eher JavaScript mit trivialen Makros, die vollständig im MIT-lizenzierten acorn-Parser enthalten sind.
      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...
    • Ich frage mich, ob hier auch das Observable Framework berücksichtigt wurde. Es erschien im März, und eines seiner Hauptmerkmale war, dass es Standard-JavaScript verwendet statt der Syntax-Hacks aus Observable Notebooks: https://observablehq.com/blog/observable-2-0#a-better-develo...
  • 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.

    • Autor: Leider stimme ich zu, dass die Usability dieses Ansatzes furchtbar ist, und ein guter Teil der Motivation für @celine/celine kam genau daher (https://maxbo.me/celine, also Texte als Library zu paketieren).
      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.
    • Auf den ersten Blick scheint das eher als „explorative DOM-Analyse“ positioniert zu sein. Die Einführungsdemo erinnert an Scheme/Smalltalk, weil man beim Arbeiten Struktur und Grundelemente aufbaut; die Szene, in der ivory zu red geä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.
    • Es heißt zwar „wenn man nur explorative Datenanalyse machen will“, aber der Kern scheint doch zu sein, eine integrierte Plattform zu haben, mit der man explorative Datenanalyse betreibt und die Ergebnisse leicht veröffentlichen kann. Es ist nicht für Jupyter-Notebooks gedacht, die man danach wegwirft.
      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

    • Ich wusste nicht, dass er öffentlich gemacht hat, woran er seitdem arbeitet. Danke für den Hinweis.
  • 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?

    • Der interaktive/reaktive Teil ist JavaScript, aber das Notebook ist in einer selbstenthaltenen HTML-Datei implementiert. Es kümmert sich um alles, von der Dokumentstruktur bis zum Bearbeiten und Anzeigen.
      Abgesehen vom Browser hängt es nicht von Drittanbieter-Software ab und ist, wie HTML nun einmal ist, langlebig, portabel und einfach zu verwenden.
    • Es fühlt sich an wie: „Warum nutzen alle Colab oder Observable, um Notebooks im Browser auszuführen? Jedenfalls präsentiere ich als Alternative Observable für die schwierigen Teile.“
  • 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/

    • Welche Technologie verwendet man denn, wenn man ein Jupyter Notebook im Browser startet? Welche Technologie verwendet man, wenn man die Website des OP öffnet?
    • Im Großen und Ganzen ein guter Punkt
      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
    • Stimme zu. Wer ein reaktives Notebook will, sollte Emacs ernsthaft in Betracht ziehen. Viele nutzen Emacs nur für ein paar Zwecke, und ein reaktives Notebook ist oft einer davon
  • 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