โน้ตบุ๊ก HTML แบบรีแอคทีฟ
(maxbo.me)- สามารถรวมการสำรวจข้อมูล การวิเคราะห์ การแสดงผลข้อมูล การจัดหน้า และการเผยแพร่ไว้ในเอกสารเดียว ด้วย ไฟล์ HTML ไฟล์เดียวที่เบราว์เซอร์รันได้ ช่วยลดขั้นตอนแบบเดิมจากโน้ตบุ๊ก→เครื่องมือจัดพิมพ์→PDF
- แกนหลักของการนำไปใช้คือการผสาน echo cell ที่แสดง
style·scriptบนหน้าจอ,contenteditable, และ Observable Runtime เพื่อสร้างเซลล์แบบรีแอคทีฟที่แก้ไขได้ cell(name, inputs, definition)สร้างหน่วยการทำงานที่เชื่อมโยงกันตามโมเดล dependency ของ Observable เช่น การเรนเดอร์counter, FizzBuzz, เซลล์ที่ซ่อนอยู่ และเซลล์ค่ากลางที่ไม่มีเอาต์พุต- ภายในเอกสาร HTML เดียวกัน มีการสาธิต Observable Plot, TeX, Markdown, Graphviz, SQLite บน WASM, Python บน Pyodide, R บน WebR, Observable Inputs และสถานะ Mutable
- ผลลัพธ์คือเอกสารด้านวิทยาศาสตร์·เทคนิคที่รันในเบราว์เซอร์โดยไม่ต้องมีแพลตฟอร์มแยกต่างหาก และการนำไปใช้ถูกเปิดเผยภายหลังเป็นไลบรารี
@celine/celine
ลดการแยกโน้ตบุ๊กกับการจัดพิมพ์ให้เหลือไฟล์ HTML เดียว
- HTML ถูกใช้อย่างแพร่หลายสำหรับการจัดหน้า แต่ยังถูกใช้เป็น แพลตฟอร์มสำรวจข้อมูล·วิเคราะห์·แสดงผลข้อมูล ค่อนข้างน้อย
- เวิร์กโฟลว์ทั่วไปต้องผ่านหลายเครื่องมือ
- หาก ไฟล์ HTML ไฟล์เดียวรับหน้าที่ทั้งสามขั้นตอนนี้ได้ ก็จะลดกระบวนการแบบแมนนวล เครื่องมือ CLI ขั้นตอน CI และการพึ่งพาแพลตฟอร์มบุคคลที่สามได้
- การนำไปใช้ได้รับอิทธิพลอย่างมากจาก In-browser code playgrounds ของ Anton Zhiyanov, Self-modifying HTML notes ของ Cristóbal Sciutto, Quarto และ Observable Framework
สร้างเซลล์ที่แก้ไขได้
- ขั้นตอนแรกคือการใช้คลาส CSS
echoเพื่อทำให้องค์ประกอบstyleและscriptแสดงออกมา ในเอกสารตามจริง - เมื่อเพิ่มฟอนต์ที่มี syntax highlighting ในตัวและแอตทริบิวต์
contenteditableก็สามารถใช้เหมือน เอดิเตอร์โค้ด พื้นฐานได้ scriptที่ใช้contenteditableจะถูกประเมินใหม่เมื่อสูญเสียโฟกัส- โคลน
scriptเดิมเพื่อสร้างองค์ประกอบscriptใหม่ แล้วลบต้นฉบับออก - วิธีนี้จำเป็นเพราะ
evalไม่ทำงานกับโค้ดที่มีคำสั่งimport
- โคลน
- เพื่อการทำงานบนฐาน Observable มีการโหลดไลบรารีสองตัว
- ใน global scope เปิดเผยเฉพาะสองสัญลักษณ์คือ
libraryและcell
โฟลว์ข้อมูลแบบรีแอคทีฟบน Observable Runtime
- ฟังก์ชัน
cell(name, inputs, definition, observerVisibility)ใช้ module scope ของ Observable Runtime เพื่อกำหนดเซลล์ - หากมีตัวแปรชื่อเดียวกันอยู่แล้ว จะดึงตัวแปรเดิมมาใช้ หากไม่มีจะสร้างตัวแปรใหม่แล้วกำหนดนิยาม
- ด้วยโครงสร้างนี้ เมื่อประเมินเซลล์
contenteditableใหม่ นิยามใหม่จะถูกแพร่ต่อไปยังเซลล์ที่เชื่อมโยงอยู่
- ด้วยโครงสร้างนี้ เมื่อประเมินเซลล์
- Observable
Inspectorใช้สำหรับแสดงเอาต์พุตของเซลล์เหนือบล็อกscript - เซลล์
counterสร้างเป็น async generator ที่ปล่อยตัวเลขทุก 1 วินาที- แอตทริบิวต์
idของscriptและอาร์กิวเมนต์nameของcellตรงกัน - เมื่อเปลี่ยนค่าเริ่มต้น
iแล้วปล่อยโฟกัส ค่าใหม่จะถูกสะท้อนออกมา
- แอตทริบิวต์
- สามารถสร้างเซลล์ที่พึ่งพา
counterได้เช่นกัน- โหลด Hypertext Literal มาเรนเดอร์ค่า
counterในรูปแบบ FizzBuzz htlใช้งานตัวแยกวิเคราะห์ HTML5 และจัดการการ escape อัตโนมัติ รวมถึงการแทรกค่าแบบไม่ serialize เช่น event listener, อ็อบเจ็กต์สไตล์ และ DOM node
- โหลด Hypertext Literal มาเรนเดอร์ค่า
เซลล์ที่มองเห็น เซลล์ที่ซ่อน และเซลล์ที่ไม่มีเอาต์พุต
- หากไม่ใส่คลาส
echoจะซ่อนนิยามของเซลล์ได้ แต่ยัง สังเกตเอาต์พุต ได้- เซลล์แบบนี้สามารถใช้เป็น primitive สำหรับการเรนเดอร์ได้
- ยังสามารถสร้างชนิดเซลล์ที่ไม่แสดงเอาต์พุตเองได้ด้วย
silent(name, inputs, definition)ตั้งค่าobserverVisibilityเป็นซ่อน เพื่อใช้เก็บค่ากลางหรือโครงสร้างข้อมูล
- เซลล์ทำงานได้โดยไม่ขึ้นกับลำดับการประกาศ
- ในตัวอย่าง เซลล์
reallyNegativeพึ่งพาเซลล์negativeที่ถูกประกาศภายหลัง
- ในตัวอย่าง เซลล์
- ค่าเซลล์เดียวกันยังนำไปใช้กับเอาต์พุตที่ซับซ้อนกว่าได้
- โหลด Observable Plot เพื่อวาดอาร์เรย์ตัวเลขเป็นกราฟเส้น และแสดงเส้น rule แนวตั้งที่ตำแหน่ง
counter % 40
- โหลด Observable Plot เพื่อวาดอาร์เรย์ตัวเลขเป็นกราฟเส้น และแสดงเส้น rule แนวตั้งที่ตำแหน่ง
ขยายเอาต์พุตเอกสารและการแสดงผลข้อมูล
- เซลล์สามารถคืนค่า DOM element ใดก็ได้
- เดโม TeX, Markdown และ Graphviz ต่างคืนค่าองค์ประกอบคนละรูปแบบ
- เซลล์
texคืนค่าองค์ประกอบspan - เซลล์
mdคืนค่าองค์ประกอบtable - เซลล์
dotคืนค่าองค์ประกอบsvg
- เซลล์
- เซลล์สามารถคืนค่า
PromiseหรือโยนErrorได้- Observable
Inspectorจะใช้คลาสobservablehq--runningกับdivภายนอกของเซลล์ที่กำลังรันอยู่ - ใช้คลาส
observablehq--errorกับเซลล์ที่เกิดข้อผิดพลาด - เอกสารกำหนดสไตล์ให้สอดคล้องกับสถานะเหล่านี้
- Observable
SQLite, Python, R ในเบราว์เซอร์
- เดโม SQLite นำ Chinook sample database ไปวางไว้ที่
https://maxbo.me/chinook.dbแล้ว query ด้วย ไคลเอนต์ SQLite บน WASM- สามารถลองเพิ่ม
WHERE Milliseconds < 1000000ใน SQL query ได้
- สามารถลองเพิ่ม
- เดโม Python ใช้ดิสทริบิวชัน CPython บน WASM อย่าง Pyodide
- Pyodide มี NumPy, Pandas, Matplotlib, scikit-learn และ Scipy รวมอยู่ด้วย
- สร้างพล็อตที่ทำไว้ก่อนหน้านี้อีกครั้งด้วย Matplotlib และโมดูล Python
sqlite3
- เดโม R ใช้ WebR
- ยังใช้งาน การเรนเดอร์ ggplot2 ไม่ได้ แต่คาดว่าน่าจะเป็นไปได้
- เซลล์ดังกล่าวดูค่อนข้างไม่เสถียรบน iOS และไม่ได้ตรวจสอบเพิ่มเติม
อินพุตและสถานะแบบเปลี่ยนแปลงได้
- ชนิดเซลล์
viewofถูกสร้างขึ้นเพื่อใช้ร่วมกับ Observable Inputs viewofประกาศเซลล์แบบรีแอคทีฟสองตัวNAME: ค่าของอินพุตviewof NAME: DOM element เอง
- หากต้องการแสดงอินพุตเหนือเซลล์ ให้ตั้งค่า
idของเซลล์เป็นviewof NAME - ในตัวอย่าง สร้างอินพุตช่วงด้วย
Inputs.range([0, 100], { step: 1 })และเซลล์rangePlotจะอัปเดตตามค่านั้น - วิธีทำงานของ Observable Inputs ค่อนข้างเข้าใจยาก และเดโม Synchronized Inputs อาจช่วยได้
- เพื่อกรณีที่โฟลว์ข้อมูลแบบฟังก์ชันล้วนไม่เพียงพอ จึงสร้าง helper
mutableด้วยmutableลงทะเบียนอ็อบเจ็กต์Mutableที่ปล่อยค่าใหม่เมื่อค่าถูกเปลี่ยน
การเปิดไลบรารีและสไลด์สำหรับการนำเสนอ
- ตอนแรกการนำไปใช้นี้คิดจะใช้ชื่อ
incelแต่ภายหลังตั้งใจจะเรียกว่าceline - ไลบรารีถูกเปิดเผยในชื่อ @celine/celine
- เอกสารนี้ถูกเดโมที่ SydJS
- มีโค้ดสำหรับเปลี่ยนเอกสารเป็นสไลด์โชว์รวมอยู่ด้วย
Shift+N: เริ่มสไลด์โชว์หรือไปยังสไลด์ถัดไปShift+B: กลับไปสไลด์ก่อนหน้าShift+E: จบสไลด์โชว์
- ซอร์สการนำไปใช้เปิดเผยเป็น source code
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
เห็นด้วยอย่างมากกับสมมติฐานของบทความนี้ HTML สามารถเป็นรากฐานที่ยอดเยี่ยมสำหรับโน้ตบุ๊กเชิงคำนวณได้
แต่ไม่ค่อยชอบตัวเลือกการนำไปใช้งานตรงนี้ HTML แบบไดนามิกและ reactive สามารถทำให้เป็นเชิงประกาศได้มากกว่านี้มาก และ Observable แม้จะยอดเยี่ยม แต่ก็มีส่วนที่ออกห่างจาก JavaScript มาตรฐาน
ผมเริ่มสร้างระบบ HTML แบบ reactive ชื่อ Heximal และต่อไปมีแผนจะใส่การรองรับโน้ตบุ๊กด้วย เป็นระบบเชิงประกาศที่อิง HTML template และ custom elements โดยวางระบบนิพจน์/ความเป็น reactive ที่อิงข้อเสนอ TC39 Signals ไว้ด้านบน
https://github.com/elematic/heximal
ให้ความรู้สึกเหมือนผสม HTMX, Tangle, Curvenote, Polymer เข้าด้วยกัน และถ้า HTML เป็น reactive มาตั้งแต่แรก ก็น่าจะออกมาหน้าตาประมาณนี้ คิดว่าน่าจะเหมาะกับกรณีใช้งานอย่างการแก้ไขกราฟิกและโน้ตบุ๊กได้ค่อนข้างดี
ดังนั้น expression สำหรับดีบัก JavaScript ทั่วไปจึงทำงานได้ถูกต้องใน Observablehq
https://github.com/observablehq/parser
เพราะชอบ Observable runtime เลยทำ decompiler ที่แปลงไป-กลับระหว่างซอร์สฟรอนต์เอนด์กับรูปแบบ JS ล้วนที่คอมไพล์แล้วได้ด้วย
https://observablehq.com/@tomlarkworthy/observablejs-toolcha...
เจ๋งมาก โดยเฉพาะ เดโม Python กับ SQLite ชอบมาก น่าสนใจที่เริ่มจากวงจรแก้ไขผ่านเว็บ (TTW) แล้วเรียกมันว่า quine ซึ่งก็พอสมเหตุสมผล แต่จุดอ่อนหลักคือ persistence
TiddlyWiki ก็ทำสิ่งเดียวกันโดยพื้นฐาน แต่การบันทึกงานเป็นเรื่องยุ่งยาก เพราะถ้าจะทำให้อะไรคงอยู่ สุดท้ายก็ต้องมีโปรเซสเซิร์ฟเวอร์แบบ headless แยกต่างหาก
ตอนที่อยากทำอะไรคล้าย ๆ กัน[0] ส่วนตัวเริ่มจาก persistence ก่อน และแน่นอนว่านั่นหมายถึงไฟล์ เป็น ไฟล์ข้อความล้วน ที่แก้ไขด้วย editor สำหรับโปรแกรมเมอร์ และบันทึก/ดีพลอยผ่านระบบไฟล์ได้ วิธีนี้ช่วยหลีกเลี่ยงปัญหาที่ต้องสร้าง editor สำหรับโปรแกรมเมอร์ขึ้นมาใหม่ในเบราว์เซอร์ ซึ่งไม่ใช่เรื่องง่าย
แต่แทนที่จะเป็นอย่างนั้น ก็เกิดปัญหาที่ต้องเขียนเซิร์ฟเวอร์ให้เร็วขึ้นมา ซึ่งก็เป็นปัญหาที่ค่อนข้างสนุกเหมือนกัน[1] ตรงนี้ใช้ section แบบ contenteditable พื้นฐาน จึงเลี่ยงปัญหานั้นไปได้ แต่ก็เป็นปัญหาใหญ่รองจาก persistence
ข้อจำกัดที่ไม่สามารถ export ภายในสคริปต์แบบฝังได้ ก็ดูเหมือนจะชนกำแพงเดียวกัน ถ้าไม่อยากทำให้ global window object รก การมีฟีเจอร์แบบนี้ก็น่าจะดี และสำหรับกรณีใช้งานของผม การสร้างโค้ดก็จะง่ายขึ้นด้วย
0 - Literate Markdown: https://simpatico.io/lit.md
1 - Reflector, เซิร์ฟเวอร์ node ขนาดเล็กที่แปลง บีบอัด และแคช Markdown พร้อมทำ invalidation จากการเฝ้าดูไฟล์: https://simpatico.io/reflector
ผมเคารพความคิดและความพยายามที่ทุ่มลงไปนะ แต่ ความสามารถในการใช้งานของแนวทางนี้แย่มาก ไม่เข้าใจว่าทำไมระหว่างที่กำลังจะทำ exploratory data analysis ถึงต้องมาคอยใส่ใจองค์ประกอบด้านสไตล์ด้วย
นี่แหละคือเหตุผลที่เครื่องมืออย่าง Jupyter Notebook ได้ผลดี ถึงอย่างนั้นก็ชื่นชมมากที่มีความอยากรู้อยากเห็นและลงมือทำไอเดียทางเลือกขึ้นมา
ในฐานะแพลตฟอร์มสำหรับ exploratory data analysis มันยังไม่เพียงพอ ไม่มีความตอบสนองทันทีแบบเว็บ code editor เต็มรูปแบบอย่าง Observable Notebooks และก็ไม่ใช่ Observable Framework ที่มี hot reloading แบบเฝ้าดูไฟล์ เคอร์เนล Jupyter ใหม่สำหรับ Deno + VSCode ก็ให้ประสบการณ์ที่ค่อนข้างลื่นไหล
ดังนั้นในแง่ความสามารถในการใช้งานสำหรับการวิเคราะห์เชิงสำรวจ อืม… ผมเห็นด้วยว่ามันแย่ แต่ผมมองว่า ความสามารถในการใช้งานด้านการเผยแพร่ ไม่ได้แย่ขนาดนั้น จริง ๆ แล้วดีเลย เพราะมันเป็นแค่ไฟล์เดียว ถ้าจะส่งกราฟกับผลลัพธ์การประมวลผลข้อมูลให้ใครสักคน ก็ไม่ต้องดูแล toolchain ขนาดใหญ่หรือจ่ายเงินให้บริการบุคคลที่สาม แค่โยนไฟล์ HTML ไฟล์เดียวเข้า Slack หรือเอาไปโฮสต์ไว้ที่ไหนสักแห่งก็พอ
ความยืดหยุ่นในการจัดสไตล์ผลการวิเคราะห์ยังทำให้สามารถเผยแพร่ในสภาพแวดล้อมที่สไตล์สำคัญ เช่น บล็อกหรือบทความวิจัยได้ด้วย
ivoryเป็นredแล้ว syntax highlighting เปลี่ยนแบบเรียลไทม์ก็แสดงให้เห็นจุดนั้นผมเข้าใจบริบทว่าทำไมถึงมาถึงตรงนี้ แต่ก็น่าเสียดายที่เราไม่สามารถลองเล่นกับ JavaScript framework และไลบรารี ในเบราว์เซอร์ได้ง่ายกว่านี้ สุดท้ายมันก็เป็นแค่ JS น่าจะทดลองอย่างรวดเร็วในสภาพแวดล้อมเบา ๆ ได้ แนวทางนี้มีจุดแข็งตรงนั้น
เป็นวิธีดึงความเป็นของเล่นและความเป็นการทดลองของ HTML/JS กลับมา อยากให้มีความพยายามแบบนี้มากกว่า TS, rollup, webpack
เพิ่มเติม: อ่านต่ออีกนิดแล้วเห็นว่าเสนอสิ่งนี้สำหรับงานวิเคราะห์ข้อมูล ผมคิดว่ามันไม่ค่อยเหมาะกับการใช้งานนั้น
ผมคิดว่าทางเลือกแทน Jupyter Notebook ที่ไม่ได้ใช้แล้วทิ้งมีคุณค่าสูง ประสบการณ์ผู้ใช้ตอนนี้แย่มากจริง ๆ แต่ปรับปรุงได้ คำถามคือโครงแบบนี้ดีกว่าความวุ่นวายแบบ JSON ของ Jupyter Notebook หรือไม่ ซึ่งโดยส่วนตัวผมเอนเอียงว่าใช่
ผู้เขียนดั้งเดิมของ Lit เพิ่งออกจาก Google และเท่าที่ทราบกำลังทำสิ่งที่คล้ายกับสิ่งนี้มากอยู่
https://github.com/elematic/heximal
ชอบรูปแบบของบทความนี้ เริ่มจากพื้นฐานสุด ๆ แล้วค่อย ๆ สร้างขึ้นไปจนถึงบางอย่างที่น่าสนใจ ไม่ใช่กระแสโอ้อวดที่พ่วงกอง dependency หรือเอา framework มาต่อแปะไปทั่ว
ถ้าจะตามไปด้วย ก็น่าจะคัดลอกชิ้นส่วนที่ให้มาแล้ววาง จากนั้นทำความเข้าใจทีละขั้นว่ามันทำงานอย่างไร ถ้าผมเขียนอะไรแบบนี้ คงเขียนด้วย literate programming ของ org-mode แล้ว export เป็น HTML เพื่อทำเป็นบทความบล็อก
แต่ฟอนต์ที่มี syntax highlighting ในตัวก็ยังน่าสงสัยอยู่ดี
บอกว่าเป็น HTML แบบ reactive แต่แทบทั้งหมดไม่ใช่ JavaScript หรอกหรือ หรือผมเข้าใจอะไรผิดไป?
นอกจากเบราว์เซอร์แล้ว ไม่พึ่งพาซอฟต์แวร์บุคคลที่สาม และเหมือนกับ HTML คืออยู่ได้นาน พกพาได้ และใช้ง่าย
เร็ว ๆ นี้ผมจะลองรองรับ HTML notebook ประเภทนี้ ใน Raku หรือสำหรับ Raku
ตอนนี้ “โซลูชันโน้ตบุ๊ก” ของ Raku อิงกับ Jupyter หรือ Mathematica
ผมไม่ค่อยเข้าใจเหตุผลที่นำเทคโนโลยีเว็บมาใช้จนทำให้กระบวนการนี้ซับซ้อนขึ้น ความซับซ้อนที่เพิ่มเข้ามาแก้อะไรได้? กลับดูเหมือนจะทำให้กระบวนการช้าลง และตัดฟีเจอร์ที่จำเป็นอย่างการเติมคำอัตโนมัติ, snippets, ส่วนขยาย ฯลฯ ออกไปมากกว่า
แถมยังมีโอกาสสูงที่คนอื่นจะไม่คุ้นกับการตั้งค่านี้ ทำให้การทำงานร่วมกันยากขึ้นด้วย
สภาพแวดล้อมควรตั้งค่าไว้ครั้งเดียว แล้วใช้งานต่อไปโดยค่อย ๆ เพิ่มการปรับปรุงเล็ก ๆ ตามเทคโนโลยีที่เปลี่ยนไปก็พอ
ถ้าเป็นผู้ใช้ n/vim สิ่งต่อไปนี้ก็น่าจะเพียงพอแล้ว 1- การสำรวจข้อมูล: ใช้ https://github.com/untitled-ai/jupyter_ascending เพื่อเชื่อมข้อความเข้ากับ Jupyter notebook เนื่องจากใช้ jupytext จึงสามารถแก้ไขและรันโค้ดได้อย่างมีประสิทธิภาพใน text editor
2- การเขียน: สำหรับ LaTeX ใช้ https://github.com/lervag/vimtex ได้
ถ้าเสริม tmux และ tmuxp เข้าไป ก็เปิดโปรเจกต์ได้ทันที ส่วนใน Emacs สามารถใช้ org mode[1][2] และ/หรือ Auctex เพื่อสร้างกระบวนการที่ลื่นไหลได้
[1] https://sqrtminusone.xyz/posts/2021-05-01-org-python/
[2] https://martibosch.github.io/jupyter-emacs-universe/
แต่ผมไม่ได้ชอบ Python มากนัก จึงอยากใช้ เครื่องมือที่ไม่ได้มี Python เป็นศูนย์กลาง นั่นเป็นเหตุผลที่พูดถึง Raku และ Mathematica
อย่างไรก็ตาม ขอบคุณที่แนะนำ https://github.com/imbue-ai/jupyter_ascending
โดยรวมผมเห็นด้วยกับ premise ของบทความนี้ นั่นคือ HTML ยังถูกนำมาใช้เป็นสื่อเผยแพร่งานเขียนทางวิทยาศาสตร์ไม่มากพอ
มีงานล่าสุดของ Will Crichton ที่เกี่ยวข้องกับเรื่องนี้
https://willcrichton.net/nota/
https://willcrichton.net/notes/portable-epubs/
ช่วงนี้ notebook กำลังเป็นกระแสจริง ๆ เราก็เปิดตัว TypeScript notebook เวอร์ชันหนึ่งเช่นกัน[1] แต่เลือกจุดยืนด้าน trade-off ค่อนข้างต่างออกไป
เราต้องการรันโค้ด node ฝั่ง backend ดังนั้นจึงไม่ได้พยายามรันในสภาพแวดล้อมเบราว์เซอร์แบบวิธีในบทความนี้หรือแบบ Observable ถึงอย่างนั้น สำหรับแอปพลิเคชันจำนวนมาก ไอเดียนี้อาจเป็นทางออกที่ดีกว่า
ขอปรบมือให้ผู้เขียน
https://github.com/srcbookdev/srcbook