Texto de derecha a izquierda en la consola
Text.js implementa el algoritmo bidireccional de Unicode y supera los 770.241 casos de BidiTest.txt. El hebreo y el árabe se renderizan en orden visual con conformación de caracteres árabes, alineados con la cuadrícula de celdas.
Text.js ahora renderiza hebreo, árabe, persa y otras escrituras de derecha a izquierda en el orden correcto, con las letras árabes unidas, en la misma cuadrícula de celdas que todo lo demás.
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> );}Cada párrafo elige su dirección base a partir de su primer carácter fuerte, como hace dir="auto" en HTML, y los párrafos de derecha a izquierda se alinean a la derecha. En un <Text> anidado, dir aísla el span como lo hace <bdi>, de modo que un nombre de usuario de dirección desconocida no puede reordenar el texto que lo rodea.
El algoritmo
La compatibilidad con texto de derecha a izquierda es una implementación del algoritmo bidireccional de Unicode (UAX #9, Unicode 18.0), escrita desde cero en @textjs/core. Resuelve los niveles de incrustación de cada párrafo, incluidas las incrustaciones explícitas y los aislamientos, los tipos débiles y neutros y los paréntesis emparejados. Después, cada línea ajustada se reordena por separado y los caracteres con reflejo se intercambian en los niveles de derecha a izquierda, de modo que (abc) sigue siendo (abc) dentro del hebreo.
Unicode publica dos archivos de conformidad para el algoritmo. Ambos están incluidos en el repositorio, comprimidos con gzip, y la suite de pruebas ejecuta todos los casos:
BidiTest.txtenumera las entradas como secuencias de clases bidi, cada una con las direcciones de párrafo a las que se aplica. Text.js supera los 770.241 casos.BidiCharacterTest.txtenumera cadenas reales con los niveles y el orden esperados. Text.js supera los 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(""); }}Del orden lógico a las celdas
Una línea de texto de derecha a izquierda pasa por cuatro pasos antes de llegar a la consola:
- Ajustar en orden lógico. Las líneas se dividen donde lo harían en un texto de izquierda a derecha, según el ancho de las celdas.
- Reordenar cada línea. Los niveles resueltos determinan el orden de visualización de los grafemas de la línea.
- Dar forma al árabe. Las letras adoptan sus formas contextuales (aislada, inicial, medial, final) de los bloques de formas de presentación árabes. Una letra ocupa una celda, y las formas unidas se mantienen aunque haya un cursor o un cambio de color en medio de una palabra.
- Fijar el orden. Las celdas ahora contienen glifos en el orden de visualización.
El último paso existe porque DevTools aplicaría bidi al texto una segunda vez y volvería a invertirlo. Un estilo %c no puede desactivar ese comportamiento, porque el saneador de DevTools elimina direction y unicode-bidi. Por eso, el controlador envuelve cada span que contiene caracteres de derecha a izquierda en LRO y PDF (U+202D y U+202C), lo que fuerza la visualización de izquierda a derecha de exactamente lo que contienen las celdas.
Un desfase de media celda
Ni SF Mono ni Menlo tienen glifos hebreos o árabes, por lo que el navegador recurre a una fuente alternativa para cada carácter. En macOS es Courier New, también monoespaciada, con 7,2012 px por glifo a 12 px frente a los 7,2246 px de Menlo. Es un 0,3 % más estrecha, suficiente para desplazar una fila completa de hebreo alrededor de media celda.
CssConsoleDriver mide cada span de derecha a izquierda con un canvas que usa la fuente de la consola, con el mismo mecanismo de fuentes alternativas de Blink que DevTools, y añade al span la diferencia exacta en LayoutUnits (1/64 px). Cada span acaba teniendo el mismo ancho que las celdas de la cuadrícula que ocupa. Un span se reduce como máximo media celda, de modo que una fuente alternativa mucho más ancha se desborda en lugar de superponerse al siguiente span.
bun bench/devtools/bidi-shot.ts mide el resultado en la interfaz real de DevTools, en un fotograma de 64×21 con líneas en hebreo, en árabe, mixtas y con emojis dentro del hebreo:
| Fotograma | Desfase del borde derecho entre filas |
|---|---|
| Celdas registradas sin fijar el orden | 14,5 px |
| Orden fijado, sin ajuste de ancho | 2,22 px |
| Controlador distribuido | 0,156 px |
Control: cada glifo RTL sustituido por x | 1,67 px |
El control es el mismo fotograma con texto latino. El controlador distribuido se alinea mejor que el control porque los emojis dentro de los spans de derecha a izquierda se ajustan junto con ellos.
El texto de izquierda a derecha no cambia
El texto sin caracteres de derecha a izquierda omite todo este proceso. La comprobación se hace mientras se segmentan los grafemas, con una comparación por grafema, y el texto exclusivamente de izquierda a derecha se dibuja por la misma ruta que antes, por lo que sus fotogramas de consola son idénticos byte a byte.
El texto de derecha a izquierda está disponible en Text.js 1.0. Internacionalización cubre la entrada, la edición, los terminales y la API.