Teks kanan ke kiri di konsol
Text.js menerapkan Unicode Bidirectional Algorithm dan lulus semua 770.241 kasus BidiTest.txt. Teks Ibrani dan Arab di-render dalam urutan visual dengan pembentukan huruf Arab, selaras dengan kisi sel.
Text.js kini me-render aksara Ibrani, Arab, Persia, dan aksara kanan-ke-kiri lainnya dalam urutan yang benar, dengan huruf Arab tersambung, pada kisi sel yang sama dengan konten lainnya.
import { Box, Text } from "@textjs/react";export default function Page() { return ( <Box flexDirection="column" padding={1}> <Text>שלום עולם! זהו טקסט בעברית.</Text> <Text>مرحبا بالعالم! هذا نص عربي.</Text> <Text>Order #1234 (<Text color="accent">שלום</Text>) total: 25.50 ₪</Text> <Text>user <Text dir="auto" color="success">שלום!</Text> replied 3 times</Text> </Box> );}Setiap paragraf menentukan arah dasarnya dari karakter kuat pertamanya, seperti dir="auto" dalam HTML, dan paragraf kanan-ke-kiri diratakan ke kanan. Pada <Text> bersarang, dir mengisolasi span seperti <bdi>, sehingga nama pengguna yang arahnya tidak diketahui tidak dapat mengubah urutan teks di sekitarnya.
Algoritma
Dukungan kanan-ke-kiri merupakan implementasi Unicode Bidirectional Algorithm (UAX #9, Unicode 18.0), yang ditulis dari awal di @textjs/core. Algoritma ini menentukan tingkat penyematan untuk setiap paragraf, termasuk penyematan eksplisit dan isolat, tipe lemah dan netral, serta tanda kurung berpasangan. Lalu setiap baris hasil pemenggalan diurutkan ulang secara terpisah, dan karakter bercermin ditukar pada tingkat kanan-ke-kiri, sehingga (abc) tetap (abc) di dalam teks Ibrani.
Unicode menerbitkan dua berkas kesesuaian untuk algoritma ini. Keduanya disimpan di repositori dalam format gzip, dan rangkaian pengujian menjalankan setiap kasus:
BidiTest.txtmencantumkan input sebagai urutan kelas bidi, masing-masing beserta arah paragraf yang berlaku. Text.js lulus seluruh 770.241 kasus.BidiCharacterTest.txtmencantumkan string nyata beserta tingkat dan urutan yang diharapkan. Text.js lulus seluruh 91.707 kasus.
for (const [bit, dir] of [[1, -1], [2, 0], [4, 1]] as const) { if ((bitset & bit) === 0) continue; cases++; const r = run(types, null, dir); const gotLevels = fmtLevels(r.levels); const gotOrder = r.order.join(" "); if (gotLevels !== levels || gotOrder !== reorder) { if (failures.length < 20) failures.push(`${input.trim()} [dir ${dir}]: levels ${gotLevels} (want ${levels}), order ${gotOrder} (want ${reorder})`); else failures.push(""); }}Dari urutan logis ke sel
Sebaris teks kanan-ke-kiri melalui empat langkah sebelum mencapai konsol:
- Penggal dalam urutan logis. Baris dipenggal pada posisi yang sama seperti teks kiri-ke-kanan, berdasarkan lebar sel.
- Urutkan ulang setiap baris. Tingkat yang telah ditentukan menetapkan urutan tampilan grafem pada baris tersebut.
- Bentuk huruf Arab. Huruf mengambil bentuk kontekstualnya (terpisah, awal, tengah, akhir) dari blok Arabic Presentation Forms. Satu huruf menempati satu sel, dan bentuk tersambung tetap terjaga meski ada kursor atau perubahan warna di tengah kata.
- Kunci urutannya. Sel kini memuat glif dalam urutan tampilan.
Langkah terakhir diperlukan karena DevTools akan menjalankan bidi pada teks untuk kedua kalinya dan membaliknya lagi. Gaya %c tidak dapat menonaktifkannya, karena sanitizer DevTools menghapus direction dan unicode-bidi. Jadi driver membungkus setiap span yang berisi karakter kanan-ke-kiri dengan LRO dan PDF (U+202D dan U+202C), yang memaksa tampilan kiri-ke-kanan persis sesuai isi sel.
Pergeseran setengah sel
Baik SF Mono maupun Menlo tidak memiliki glif Ibrani atau Arab, sehingga browser menggunakan font cadangan per karakter. Di macOS, font tersebut adalah Courier New, yang juga berlebar tetap, dengan lebar 7,2012 px per glif pada ukuran 12 px dibandingkan Menlo yang lebarnya 7,2246 px. Lebarnya 0,3% lebih sempit, cukup untuk menggeser satu baris penuh teks Ibrani sekitar setengah sel.
CssConsoleDriver mengukur setiap span kanan-ke-kiri menggunakan kanvas dengan font konsol, yang memakai font cadangan Blink yang sama dengan DevTools, lalu menambahkan ruang pada span sebesar selisih tepatnya dalam LayoutUnits (1/64 px). Setiap span akhirnya selebar sel kisi yang dicakupnya. Span dipersempit paling banyak setengah sel, sehingga font cadangan yang jauh lebih lebar akan meluap alih-alih menimpa span berikutnya.
bun bench/devtools/bidi-shot.ts mengukur hasilnya di frontend DevTools yang sebenarnya, pada frame 64×21 berisi baris Ibrani, Arab, campuran, dan emoji dalam teks Ibrani:
| Frame | Pergeseran tepi kanan antarbaris |
|---|---|
| Sel dicatat tanpa penguncian urutan | 14,5 px |
| Urutan dikunci, tanpa penyesuaian lebar | 2,22 px |
| Driver yang dirilis | 0,156 px |
Kontrol: setiap glif RTL diganti dengan x | 1,67 px |
Kontrol menggunakan frame yang sama dengan teks Latin. Driver yang dirilis menghasilkan perataan yang lebih baik daripada kontrol karena emoji di dalam span kanan-ke-kiri ikut disesuaikan bersama span tersebut.
Teks kiri-ke-kanan tidak berubah
Teks tanpa karakter kanan-ke-kiri melewati seluruh proses ini. Pemeriksaan berlangsung saat segmentasi grafem, satu perbandingan per grafem, dan teks yang sepenuhnya kiri-ke-kanan di-render melalui jalur yang sama seperti sebelumnya, sehingga frame konsolnya identik hingga tingkat byte.
Teks kanan-ke-kiri hadir di Text.js 1.0. Internasionalisasi membahas input, penyuntingan, terminal, dan API.