L’architecture inversée
Comment un commit React devient un appel console.log dans DevTools.
Dans un framework DOM, les composants deviennent des nœuds DOM que le navigateur dispose et dessine dans la page. Dans Text.js, les composants deviennent des cellules, et chaque frame de cellules modifiée devient un appel à console.log dans DevTools. Nous appelons cela l’architecture inversée.
Le pipeline
React ─▶ reconciler ─▶ Yoga ─▶ CellBuffer ─▶ row-hash gate ─▶ RLE ─▶ console.logChaque étape existe parce que la console est un affichage inhabituel. Elle utilise une police à chasse fixe et s’organise en lignes, ses styles sont appliqués avec %c, et DevTools décide quand la redessiner.
Réconciliateur
@textjs/react est un réconciliateur React 19 ([email protected]) avec deux éléments hôtes, tx-box et tx-text, ainsi que des instances de texte pour les enfants de type chaîne de caractères. Tout ce que vous importez, de <Box> à <Markdown>, repose sur ces éléments. Les commits sont planifiés sur requestAnimationFrame, et un appui sur une touche ou un collage déclenche un rendu synchrone dans la même tâche.
Layout
Yoga, compilé en WebAssembly, dispose l’arbre avec pointScaleFactor = 1, de sorte que chaque boîte occupe des cellules entières. Le texte est mesuré avec Intl.Segmenter et des tables de largeur des caractères d’Asie de l’Est, si bien que 你好 occupe quatre cellules et 🚀 en occupe deux. Si une modification ne peut pas changer la taille d’un nœud de texte, Yoga n’est pas sollicité à nouveau.
CellBuffer
La frame est une grille de tableaux typés alloués une seule fois : les caractères dans un Uint32Array, les couleurs de premier plan et d’arrière-plan compactées en 0xRRGGBBAA, les attributs sous forme de masque de bits Uint16Array. Le dessin écrit dans ces tableaux et garde la trace des lignes touchées.
Le filtre
Chaque ligne possède un hash FNV-1a de 32 bits en cache. Seules les lignes écrites depuis la dernière frame voient leur hash recalculé. Si aucune ligne n’a changé, le pilote retourne avant de construire une chaîne et n’effectue aucun appel à la console. Un indicateur de chargement dans un onglet masqué ne coûte rien, car il ne s’anime pas.
Encodage par plages
Les lignes modifiées sont à nouveau parcourues pour former des plages de style identique, et chaque plage devient un span %c. Les plages se poursuivent au-delà des sauts de ligne. Les chaînes CSS sont stockées dans une table et référencées par des identifiants de style entiers, de sorte que le chemin critique ne construit jamais deux fois la même chaîne. Un tableau de bord de 100×32 représente 3200 cellules et 149 spans.
La chaîne de format protège aussi contre deux comportements de DevTools. Un % littéral est écrit %%. Un word joiner est inséré après ://, à l’intérieur de www. et dans path:line, afin que DevTools ne transforme pas votre texte en lien et ne le redessine pas avec ses propres couleurs.
Un seul appel
Chaque frame modifiée correspond à un seul appel :
console.log("%c\u200b%c\n%c▼ OpenCode Lite%c …", "margin-top:100vh;", "", "color:#fafafa;…", …);Les frames sont ajoutées à la suite, cadencées selon le mécanisme de limitation de DevTools à 50 ms et remplacées au cours d’un seul rafraîchissement. Turbolog : éliminer le flash traite de cette partie.
Saisie
La console ne peut pas recevoir les frappes clavier, c’est donc la page qui les reçoit. Un <textarea> masqué recueille les frappes, le collage et la composition IME, et les événements wheel font défiler les composants ScrollBox. Sur textjs.org, les pages Playground et OpenCode constituent une grande zone de saisie. Ouvrez la console, puis cliquez sur la page pour saisir du texte.
Ce que voit la page
La page ne peut pas lire la taille de la console, seulement celle de sa propre fenêtre, alors Text.js la déduit : un panneau ancré réduit innerWidth ou innerHeight tandis que outer* reste inchangé, et chaque hypothèse d’ancrage est évaluée pour les niveaux de zoom de Chrome. La meilleure correspondance est retenue. Les constantes (56 px d’espacement horizontal, 104 px d’interface de la console, un pas de ligne de 14 px) ont été mesurées dans l’interface réelle de DevTools.