← ब्लॉग

कंसोल में दाएं से बाएं टेक्स्ट

Text.js में Unicode Bidirectional Algorithm लागू है और यह BidiTest.txt के सभी 7,70,241 केस पास करता है। हिब्रू और अरबी विज़ुअल क्रम में, अरबी शेपिंग के साथ, सेल ग्रिड से संरेखित होकर रेंडर होते हैं।

Adam

Text.js अब हिब्रू, अरबी, फ़ारसी और दाएं से बाएं लिखी जाने वाली अन्य लिपियों को सही क्रम में, अरबी अक्षरों को जोड़कर, बाकी सबके समान सेल ग्रिड पर रेंडर करता है।

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>  );}

हर पैराग्राफ़ अपने पहले स्ट्रॉन्ग कैरेक्टर से अपनी मूल दिशा चुनता है, जैसे HTML में dir="auto" करता है, और दाएं से बाएं वाले पैराग्राफ़ दाईं ओर अलाइन होते हैं। नेस्ट किए गए <Text> पर dir span को उसी तरह आइसोलेट करता है जैसे <bdi> करता है, ताकि अज्ञात दिशा वाला यूज़र नाम अपने आसपास के टेक्स्ट का क्रम न बदल सके।

एल्गोरिदम

दाएं से बाएं का सपोर्ट Unicode Bidirectional Algorithm (UAX #9, Unicode 18.0) का इंप्लीमेंटेशन है, जिसे @textjs/core में शुरू से लिखा गया है। यह हर पैराग्राफ़ के एम्बेडिंग लेवल तय करता है, जिनमें एक्सप्लिसिट एम्बेडिंग और आइसोलेट, वीक और न्यूट्रल टाइप और जोड़ीदार ब्रैकेट शामिल हैं। फिर हर रैप की गई लाइन का क्रम अलग से बदला जाता है, और दाएं से बाएं वाले लेवल पर मिरर किए गए कैरेक्टर बदलते हैं, ताकि हिब्रू के अंदर (abc) वैसा ही (abc) रहे।

Unicode इस एल्गोरिदम के लिए दो अनुरूपता फ़ाइलें प्रकाशित करता है। दोनों gzip फ़ॉर्मैट में रिपॉज़िटरी में मौजूद हैं, और टेस्ट सूट हर केस चलाता है:

  • BidiTest.txt में इनपुट bidi क्लास के क्रम के रूप में दिए हैं, और हर इनपुट के साथ वे पैराग्राफ़ दिशाएं हैं जिन पर वह लागू होता है। Text.js सभी 7,70,241 केस पास करता है।
  • BidiCharacterTest.txt में अपेक्षित लेवल और क्रम के साथ वास्तविक स्ट्रिंग दी हैं। Text.js सभी 91,707 केस पास करता है।
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("");  }}

लॉजिकल क्रम से सेल तक

दाएं से बाएं टेक्स्ट की एक लाइन कंसोल तक पहुंचने से पहले चार चरणों से गुज़रती है:

  1. लॉजिकल क्रम में रैप करें। सेल की चौड़ाई के हिसाब से लाइनें वहीं टूटती हैं जहां बाएं से दाएं टेक्स्ट की लाइनें टूटतीं।
  2. हर लाइन का क्रम बदलें। तय किए गए लेवल लाइन के ग्रैफ़ीम का डिस्प्ले क्रम तय करते हैं।
  3. अरबी को आकार दें। अक्षर Arabic Presentation Forms ब्लॉक से संदर्भ के अनुसार अपने रूप (अलग, शुरुआती, बीच के, अंतिम) लेते हैं। एक अक्षर एक सेल होता है, और शब्द के बीच में कर्सर या रंग बदलने पर भी जुड़े हुए रूप बने रहते हैं।
  4. क्रम पिन करें। अब सेल में ग्लिफ़ डिस्प्ले क्रम में रहते हैं।

आख़िरी चरण इसलिए है क्योंकि DevTools टेक्स्ट पर दूसरी बार bidi चलाकर उसे फिर उलट देगा। %c स्टाइल इसे बंद नहीं कर सकती, क्योंकि DevTools का सैनिटाइज़र direction और unicode-bidi हटा देता है। इसलिए ड्राइवर दाएं से बाएं कैरेक्टर वाले हर span को LRO और PDF (U+202D और U+202C) में लपेटता है, जिससे सेल में मौजूद सामग्री ठीक उसी क्रम में बाएं से दाएं दिखती है।

आधे सेल का खिसकाव

SF Mono और Menlo, दोनों में हिब्रू या अरबी ग्लिफ़ नहीं हैं, इसलिए ब्राउज़र हर कैरेक्टर के लिए फ़ॉलबैक इस्तेमाल करता है। macOS पर यह Courier New है, जो मोनोस्पेस्ड भी है, और 12 px पर इसके हर ग्लिफ़ की चौड़ाई 7.2012 px है जबकि Menlo की 7.2246 px है। यह 0.3% संकरा है, जो हिब्रू की पूरी पंक्ति को लगभग आधा सेल खिसकाने के लिए काफ़ी है।

CssConsoleDriver कंसोल फ़ॉन्ट वाले कैनवस से दाएं से बाएं वाले हर span को मापता है, जो DevTools जैसा ही Blink फ़ॉन्ट फ़ॉलबैक इस्तेमाल करता है, और LayoutUnits (1/64 px) में सटीक अंतर के बराबर span में पैडिंग जोड़ता है। हर span की चौड़ाई अंत में उतनी ही होती है जितनी उसके ग्रिड सेल की कुल चौड़ाई है। किसी span को ज़्यादा से ज़्यादा आधा सेल अंदर खींचा जाता है, इसलिए बहुत चौड़ा फ़ॉलबैक फ़ॉन्ट अगले span पर चढ़ने के बजाय ओवरफ़्लो होता है।

bun bench/devtools/bidi-shot.ts कमांड वास्तविक DevTools फ़्रंटएंड में, हिब्रू, अरबी, मिश्रित और हिब्रू में इमोजी वाली लाइनों के 64×21 फ़्रेम पर नतीजा मापती है:

फ़्रेमपंक्तियों में दाएं किनारे का खिसकाव
बिना पिन किए लॉग किए गए सेल14.5 px
पिन किया गया, चौड़ाई फ़िट किए बिना2.22 px
रिलीज़ में शामिल ड्राइवर0.156 px
कंट्रोल: हर RTL ग्लिफ़ की जगह x1.67 px

कंट्रोल वही फ़्रेम है जिसमें लैटिन टेक्स्ट है। रिलीज़ में शामिल ड्राइवर कंट्रोल से बेहतर अलाइन होता है क्योंकि दाएं से बाएं वाले span के अंदर के इमोजी भी उनके साथ फ़िट होते हैं।

बाएं से दाएं टेक्स्ट में कोई बदलाव नहीं

दाएं से बाएं कैरेक्टर के बिना टेक्स्ट यह सब छोड़ देता है। जांच ग्रैफ़ीम को अलग करते समय होती है, हर ग्रैफ़ीम पर एक तुलना के साथ, और पूरी तरह बाएं से दाएं वाला टेक्स्ट पहले वाले पाथ से ही पेंट होता है, इसलिए उसके कंसोल फ़्रेम बाइट के स्तर पर समान रहते हैं।

दाएं से बाएं टेक्स्ट का सपोर्ट Text.js 1.0 में शामिल है। अंतरराष्ट्रीयकरण में इनपुट, एडिटिंग, टर्मिनल और API की जानकारी है।