4 poin oleh GN⁺ 21 hari lalu | 1 komentar | Bagikan ke WhatsApp
  • Pretext adalah library JavaScript/TypeScript murni untuk menghitung tinggi dan susunan baris teks multiline tanpa akses DOM, serta mendukung lingkungan browser maupun server
  • Karena tidak menggunakan API pengukuran DOM seperti getBoundingClientRect, library ini menghilangkan biaya reflow layout dan menjaga akurasi dengan logika pengukuran internal berbasis mesin font
  • Melalui API prepare() / layout(), teks dipraproses lalu perhitungan tinggi yang cepat dilakukan dengan operasi aritmetika murni menggunakan data lebar yang telah di-cache
  • Mendukung emoji, teks dengan arah campuran (bidi), dan berbagai bahasa, serta memberikan hasil yang sama di Canvas, SVG, WebGL, dan server rendering
  • Ini adalah mesin teks berperforma tinggi yang dapat digunakan untuk implementasi layout UI presisi seperti virtualized scrolling, validasi text overflow, dan penempatan teks mengambang

Ikhtisar

  • Pretext adalah library JavaScript/TypeScript murni untuk pengukuran dan tata letak teks multiline, dengan dukungan untuk DOM, Canvas, SVG, hingga server-side rendering
  • Tidak menggunakan API pengukuran DOM (getBoundingClientRect, offsetHeight, dll.), sehingga menghilangkan biaya reflow layout
  • Memberikan akurasi dan performa tinggi melalui logika pengukuran internal yang mengacu pada mesin font browser
  • Mendukung semua bahasa, emoji, dan teks dengan arah campuran (bidi), serta menangani perbedaan antar-browser

Instalasi dan demo

Fitur utama

  • Pretext menyediakan dua pola penggunaan utama
  • 1. Mengukur tinggi paragraf tanpa akses DOM

    • prepare() mempraproses teks, melakukan normalisasi spasi, pemisahan segmen, penerapan aturan glue, dan pengukuran berbasis canvas untuk mengembalikan opaque handle
    • layout() menggunakan data lebar yang telah di-cache untuk menghitung tinggi dan jumlah baris dengan operasi aritmetika murni
    • Untuk teks dan konfigurasi yang sama, prepare() tidak perlu dipanggil berulang; saat resize cukup jalankan kembali layout()
    • Opsi { whiteSpace: 'pre-wrap' } mempertahankan spasi, tab(\t), dan line break(\n) apa adanya
    • Hasil benchmark: prepare() sekitar 19ms (berdasarkan 500 teks), layout() sekitar 0.09ms
    • Nilai tinggi yang dikembalikan dapat digunakan untuk fitur UI seperti:
      • Perhitungan tinggi yang akurat pada virtualization dan occlusion handling
      • Sistem layout berbasis JS (misalnya masonry, struktur mirip flexbox)
      • Validasi text overflow berbasis AI
      • Mempertahankan posisi scroll saat teks dimuat
  • 2. Menyusun tata letak paragraf secara manual

    • prepareWithSegments() menghasilkan data per segmen
    • layoutWithLines() mengembalikan teks dan informasi lebar untuk tiap baris pada lebar tetap
    • walkLineRanges() menelusuri lebar dan rentang kursor tiap baris tanpa membentuk string teks
      • Contoh: memungkinkan penyesuaian layout dengan binary search untuk menguji beberapa lebar dan menemukan jumlah baris serta tinggi yang sesuai
    • layoutNextLine() menyusun layout satu per satu ketika lebar tiap baris berbeda
      • Contoh: penempatan teks mengambang untuk membuat teks mengalir di sekitar gambar
    • Pendekatan ini dapat diterapkan dengan hasil yang sama pada Canvas, SVG, WebGL, dan server-side rendering

Ringkasan API

  • API untuk pengukuran dasar

    • prepare(text, font, options?): menganalisis dan mengukur teks, lalu mengembalikan handle untuk diberikan ke layout()
    • layout(prepared, maxWidth, lineHeight): menghitung tinggi teks dan jumlah baris sesuai lebar dan tinggi baris yang diberikan
  • API untuk layout manual

    • prepareWithSegments(text, font, options?): mengembalikan data per segmen
    • layoutWithLines(prepared, maxWidth, lineHeight): mencakup teks, lebar, dan informasi kursor tiap baris
    • walkLineRanges(prepared, maxWidth, onLine): mengirimkan lebar dan rentang kursor tiap baris melalui callback
    • layoutNextLine(prepared, start, maxWidth): melakukan layout dalam bentuk iterator per baris
    • Menyertakan definisi tipe LayoutLine, LayoutLineRange, LayoutCursor
  • Utilitas lainnya

    • clearCache(): menginisialisasi ulang cache internal
    • setLocale(locale?): mengatur locale dan menginisialisasi ulang cache (tidak memengaruhi state yang sudah ada)

Batasan dan hal yang perlu diperhatikan

  • Pretext bukan mesin rendering font yang sepenuhnya lengkap
  • Properti CSS target dasar
    • white-space: normal
    • word-break: normal
    • overflow-wrap: break-word
    • line-break: auto
  • Saat menggunakan { whiteSpace: 'pre-wrap' }, spasi, tab, dan line break dipertahankan, dengan tab-size: 8 diterapkan
  • Pada macOS, font system-ui tidak cocok untuk akurasi layout(), sehingga disarankan memakai nama font eksplisit
  • Karena overflow-wrap: break-word, pada lebar yang sangat sempit pemenggalan bisa terjadi di dalam kata, tetapi tetap hanya dipisahkan berdasarkan unit karakter grapheme

Terkait pengembangan

  • Untuk lingkungan pengembangan dan perintah yang tersedia, lihat DEVELOPMENT.md

Kontribusi dan latar belakang

  • Melanjutkan ide dari proyek text-layout milik Sebastian Markbage
  • Mewarisi dan mengembangkan arsitektur yang dibangun di atas shaping berbasis canvas measureText, penanganan bidi dari pdf.js, dan desain streaming line breaking

1 komentar

 
GN⁺ 21 hari lalu
Komentar Hacker News
  • Proyek ini benar-benar mengesankan
    Ini memecahkan masalah menghitung tinggi teks yang dibungkus baris secara efisien tanpa benar-benar merender teks di halaman web
    Proyek ini menyimpan cache lebar dan tinggi segmen yang dipecah per kata, lalu mengimplementasikan sendiri algoritme line break browser
    Ini pekerjaan yang sangat sulit karena harus menangani berbagai jenis karakter seperti tanda hubung, emoji, bahasa Tionghoa, serta perbedaan rendering antar-browser, termasuk Safari
    Untuk pengujian terhadap browser nyata, digunakan dataset corpora dan halaman uji akurasi

    • Saya juga pernah membuat sesuatu yang mirip. Versi sederhana 2KB bernama uWrap.js, dan saya membuatnya tanpa AI
      Untuk teks ASCII, kode saya butuh 80ms, sedangkan pretext 2200ms
      Akurasinya belum saya uji, tapi malam ini saya berencana mencobanya
      PR peningkatan performa sudah dibuka di issue #18
    • Mesin layout teks memang terkenal sangat sulit
      Dulu saya juga pernah kesulitan saat mencoba merender teks multiline di canvas
      Proyek ini jauh lebih berguna karena terhubung langsung dengan DOM
      Contoh: demo Scrawl
    • Dari penjelasannya, kelihatannya sebenarnya segmen-segmen itu dirender ke canvas untuk diukur
      Ini bisa lebih lambat daripada API native, dan tidak ada jaminan menggunakan logika yang sama dengan rendering browser non-canvas
    • Pada praktiknya ini bukan benar-benar implementasi langsung algoritme rendering teks browser
      Pendekatannya adalah merender ke canvas lalu mengukurnya, jadi lebih seperti API untuk analisis layout teks
    • Saya sempat kesulitan menghitung tinggi teks saat membuat subtitle dinamis untuk video Remotion, dan library ini tampaknya akan sangat membantu
  • Ini benar-benar fitur yang sudah lama ditunggu
    Sejak dulu sulit mengimplementasikan hal seperti accordion responsif dengan benar
    Pola evolusi web selalu seperti ini: ① kebutuhan kompleks muncul → ② hack JS/CSS → ③ standardisasi
    Kali ini rasanya bukan sekadar hack, melainkan tahap 2 yang benar-benar matang
    Jika melihat RESEARCH.md, bahkan perbedaan pengukuran emoji antar-browser pun diteliti dengan rinci
    Perawatannya pasti berat, tapi ini terasa seperti titik balik besar bagi perkembangan web

    • Accordion responsif sekarang memang bisa dilakukan dengan CSS, tapi API seperti ini tetap dibutuhkan
      Yang menarik, kali ini AI dipakai aktif dalam proses pengembangan. Sepertinya sebagian besar implementasi dibuat memakai agen Cursor
  • Menurut pembuat library, Claude Code dan Codex diberi ground truth data browser untuk dipelajari, lalu pengukuran diulang selama beberapa minggu
    Lihat tweet terkait
    Sepertinya Autoresearch juga dipakai sebagian

  • Saya terutama suka contoh reflow berbasis shape
    Saya sempat ingin menerapkannya di Ensō(enso.sonnet.io), tetapi saya menahan diri demi menjaga kesederhanaan
    Contoh accordion juga bisa dibuat dengan CSS interpolate-size
    Lihat artikel Josh Comeau
    Contoh gelembung teks juga bisa dibuat dengan cara serupa memakai text-wrap: balance | pretty

    • Tapi balance atau pretty bukan solusi lengkap
      Sering kali kita tidak ingin panjang tiap baris dibuat rata
      Issue CSSWG terkait: #191
    • text-wrap memang membantu menyamakan jumlah kata per baris, tetapi masalah ruang kosong di sisi kanan tetap ada
  • pretext tidak memakai canvas.measureText secara langsung; Anda cukup mengirim teks dan atributnya ke API JS, lalu layout dihitung otomatis
    Dulu kita harus langsung memakai measureText atau memindahkan harfbuzz ke browser
    Ini terasa bukan terobosan teknis besar, melainkan hasil dari menggabungkan elemen-elemen yang sudah ada dengan baik
    Tapi saya penasaran bedanya dengan Skia-wasm / Canvaskit

    • Bedanya sederhana. pretext bukan wasm
    • Skia adalah mesin rendering besar
      Ia menyediakan API rendering independen-perangkat seperti Flutter
      Perbedaan pretext adalah implementasi rendering glyph berbasis TypeScript murni dengan bantuan AI
      Rasanya seperti perbedaan antara mengimplementasikan ffmpeg langsung dalam C dan memanggilnya dari Dart
      Upaya seperti ini menunjukkan kemungkinan baru bagi FOSS sisi klien
    • Jika pembuatnya benar, ini akan membawa perubahan besar bagi framework GUI web dan editor teks kaya
  • Tahun lalu saya membuat sistem tata letak brosur cetak berbasis HTML, dan untuk line break serta pencegahan widow/orphan saya berulang kali menghitung batas kotak dengan Selection API
    Sampai sekarang masih berjalan baik, tetapi ada hack off-by-one yang alasannya sendiri saya tidak pahami
    Fitur pembuatan baris iteratif di pretext benar-benar terasa sangat disambut

  • Di Fedora + Firefox, semua demo terlihat rusak
    Contoh: screenshot

    • Ini menunjukkan bahwa proyek seperti ini pada akhirnya adalah rangkaian pengejaran edge case tanpa akhir
  • Fitur seperti ini seharusnya memang disediakan sebagai API standar browser
    Saya penasaran bagaimana cara mengajukan permintaan fitur ke W3C, dan apakah ada semacam voting komunitas

    • Sudah ada proposal Font Metrics API
      Tetapi vendor browser tidak menaruh prioritas di sana
      Saat ini Chrome tampaknya lebih fokus pada API terkait AI
  • Di mesin Sciter sudah ada fitur Graphics.Text
    Ini adalah elemen rendering teks berbasis canvas yang bisa langsung menerapkan style CSS

  • Pencarian teks browser (Ctrl+F) tidak bekerja dengan baik pada daftar virtual scroll
    Untuk menyelesaikan masalah seperti ini, mungkin dibutuhkan API “Search” baru, bukan sekadar JS

    • Pernah ada Virtual Scroller API, tetapi hampir tidak ada perkembangan
      Proyek terkait: Display Locking, dokumentasi MDN
    • Pencarian native hanya menelusuri node yang ada di DOM
      Item off-screen dalam daftar tervirtualisasi tidak ada di DOM, jadi tidak bisa dicari
      Untuk menyelesaikannya dibutuhkan kontrak browser baru yang mengintegrasikan seleksi, fokus, posisi scroll, dan navigasi hasil cocok
      Tetapi kecil kemungkinan situs-situs akan memakainya secara konsisten