Kısaca
Pretext.js, DOM işlemleri yerine saf aritmetik kullanarak çok satırlı metni ölçen ve konumlandıran, sıfır bağımlılıklı bir TypeScript kütüphanesidir. Zorunlu senkron yeniden akışları ortadan kaldırır, getBoundingClientRect()'tan yaklaşık 500 kat daha hızlı metin ölçümü sağlar ve tüm büyük yazı sistemlerini destekler. Sanal kaydırıcılar, sohbet arayüzleri veya veri ızgaraları geliştiriyorsanız, bu kütüphane tarayıcıların 30 yıldır göz ardı ettiği bir sorunu çözer.
Giriş
JavaScript'iniz her getBoundingClientRect() çağırdığında veya offsetHeight okuduğunda, tarayıcı tüm işlemleri durdurur. Bekleyen stil değişikliklerini temizler, düzeni yeniden hesaplar ve tam bir render geçişi zorlar. Bu zorunlu senkron yeniden akış, tarayıcıda en maliyetli işlemlerden biridir.
Bunu sanal bir listede 1.000 sohbet balonu veya bir veri ızgarasında 10.000 satır ile çarptığınızda sonuç: Düşen kareler, takılmalar ve kullanıcı deneyiminde bozulmalar.
💡 Apidog ekipleri API odaklı ön uçlar geliştirirken bu sorunu iyi bilir; düzen motorunuz her adımda size karşı savaşırken, yanıt verilerini dinamik arayüzlere sorunsuz bir şekilde aktarmak sürekli bir mücadeledir.
react-motion'ın (21.700+ GitHub yıldızı) geliştiricisi ve Meta/ReasonML'in temel katkıcılarından Cheng Lou, bu sorunu çözmek için Pretext.js'i geliştirdi. Kütüphane Mart 2026'da yayınlandı, birkaç gün içinde 14.000'den fazla GitHub yıldızına ulaştı ve yılın en büyük Hacker News konularından biri oldu.
Bu makalede Pretext.js'in temel çalışma şekli, nasıl uygulandığı ve teknik sınırlamaları pratik uygulama adımları ve kod örnekleriyle ele alınmaktadır.
Pretext.js Nedir?
Pretext.js, saf JavaScript/TypeScript ile çok satırlı metni DOM olmadan ölçen ve konumlandıran bir motordur. DOM ölçümü veya yeniden akış yok; sadece aritmetik.
Ana fikir: Tarayıcıya “bu metin ne kadar yüksek?” diye sormak yerine Canvas API'den alınan font ölçülerini kullanıp matematiksel olarak hesaplama yapmak.
Pretext.js API'sinin Uygulamalı Kullanımı
import { prepare, layout } from '@chenglou/pretext';
// 1. Metni hazırlayın (bir kez, önbelleğe alınabilir)
const handle = prepare('Hello, pretext.js', '16px "Inter"');
// 2. Herhangi bir genişlikte düzeni hesaplayın
const { height, lineCount } = layout(handle, 400, 24);
İki fonksiyon. prepare() metni ölçüp önbelleğe alır. Sadece bir kez Canvas'a erişir. Ardından layout() fonksiyonu, saf matematikle yüksekliği ve satır sayısını döndürür.
API Yoğun Uygulamalarda Neden Önemli?
Akış API yanıtlarını kullanan uygulamalarda (AI asistanları, gerçek zamanlı dashboardlar, işbirlikçi editörler vb.) gelen metnin yüksekliği DOM'a dokunmadan bilinmelidir. Pretext.js bu ölçümü mikrosaniyeler içinde sunar ve sanal kaydırıcı veya sohbet arayüzlerinde takılmayı engeller.
Pretext.js'in Çözdüğü Temel Sorun
Zorunlu Senkron Yeniden Akışın Maliyeti
Aşağıdaki kodda olduğu gibi:
const elements = document.querySelectorAll('.text-block');
elements.forEach(el => {
const height = el.getBoundingClientRect().height; // REFLOW!
// kullan...
});
Her getBoundingClientRect() çağrısı:
- JavaScript yürütmeyi durdurur
- Tüm stil değişikliklerini temizler
- Belge veya alt ağaç için düzeni hesaplar
- Değeri döndürür
1.000 öğe için 1.000 tam düzen yeniden hesaplama olur (~94ms, 6 kare düşüşü).
Sanal Kaydırma ve Değişken Yüksekliğin Kabusu
Standart çözüm, öğeleri ekran dışında render edip ölçmek. Ancak bu, DOM'a fazla yük bindirir ve amaçlanan performansı sağlamaz. Pretext.js ile DOM oluşmadan yükseklikler hesaplanır.
Gerçek Performans Sayıları
| Yaklaşım | 1.000 metin bloğu | 500 metin bloğu |
|---|---|---|
DOM (getBoundingClientRect) |
~94ms (6 kare) | ~47ms |
Pretext.js (layout()) |
~2ms | ~0.09ms |
| Hız farkı | ~47 kat daha hızlı | ~500 kat daha hızlı |
Pretext.js Nasıl Çalışır? (Kaputun Altında)
1. Aşama: Metin Segmentasyonu
prepare() fonksiyonu, metni Unicode satır sonu kurallarıyla böler. Çok dilli destek:
- CJK karakterleri (Çince, Japonca, Korece)
- Arapça ve İbranice (sağdan sola, çift yönlü)
- Tayca (kelime arası boşluk yok)
- Hintçe/Devanagari (ligatürler)
- Emoji (çok kod noktası)
- Yumuşak kısa çizgiler (
­)
2. Aşama: Canvas Ölçümü
Segmentler, Canvas measureText() ile ölçülür.
const ctx = offscreenCanvas.getContext('2d');
ctx.font = '16px "Inter"';
const metrics = ctx.measureText('Hello');
const width = metrics.width;
Aynı metin ve font kombinasyonunda ölçümler önbellekten alınır.
3. Aşama: Saf Aritmetik Düzen
layout() fonksiyonu segment genişliklerini toplar, satır sonlarını belirler ve toplam yüksekliği hesaplar. DOM yok, Canvas yok, sadece toplama ve karşılaştırma.
Yeniden Kullanılabilir İşleyici
Bir kez prepare(), istediğiniz kadar farklı genişlikte layout():
const handle = prepare(longArticleText, '16px "Inter"');
const mobile = layout(handle, 375, 24);
const tablet = layout(handle, 768, 24);
const desktop = layout(handle, 1200, 24);
Pratik Kullanım Durumları
1. Değişken Yükseklikte Metinle Sanal Kaydırma
import { prepare, layout } from '@chenglou/pretext';
interface TextItem {
id: string;
content: string;
}
function computeHeights(items: TextItem[], containerWidth: number) {
return items.map(item => {
const handle = prepare(item.content, '14px "Inter"');
const { height } = layout(handle, containerWidth, 20);
return { id: item.id, height: height + 32 }; // padding dahil
});
}
const heights = computeHeights(chatMessages, 600); // 10.000 öğe ~4ms
DOM'a dokunmadan doğru yükseklikler.
2. Yapay Zeka Sohbet Arayüzleri
Her yeni token sonrası DOM ölçümü yerine Pretext.js ile yükseklik anında hesaplanır:
let streamedText = '';
const font = '15px "SF Pro"';
socket.on('token', (token: string) => {
streamedText += token;
const handle = prepare(streamedText, font);
const { height } = layout(handle, bubbleWidth, 22);
scroller.updateItemHeight(messageId, height + padding);
});
3. Metin Sütunlu Veri Izgaraları
function computeColumnWidth(values: string[], font: string, padding: number) {
let maxWidth = 0;
for (const value of values) {
const handle = prepare(value, font);
// Tek satır genişliğini almak için sonsuz genişlik
const { height } = layout(handle, Infinity, 20);
// handle'ın iç width bilgisini kullanın
maxWidth = Math.max(maxWidth, /* width hesaplaması */);
}
return maxWidth + padding;
}
4. Çok Dilli İçerik Akışları
const posts = [
{ text: 'This library changed everything', lang: 'en' },
{ text: 'RTL text with correct bidirectional layout', lang: 'ar' },
{ text: 'CJK text gets proper character-level breaks', lang: 'zh' },
];
posts.forEach(post => {
const handle = prepare(post.text, '16px system-ui');
const { height } = layout(handle, 400, 24);
});
Apidog ile Metin Düzeninizi Test Etme
API destekli metin yoğun arayüzler geliştirirken sadece düzen değil, veri katmanınızın da güvenilir olması gerekir.
Apidog ile Pretext.js entegrasyonlarınızı test edin. Farklı metin uzunlukları, diller ve Unicode uç durumlarını API yanıtlarında simüle edin; sanal kaydırıcınızın doğru çalıştığını doğrulayın.
Yapay zeka sohbet ürünleri geliştiren ekipler için Apidog ile:
- Parçalı metinle akış yanıtlarını taklit edin
- Çok dilli yüklerle testler oluşturun
- Yanıt şemalarını doğrulayın
- Otomatik test paketleri ile uç durumları kapsayın
Ne kadar hızlı ölçüm yaparsanız yapın, API verisi bozuksa düzen de bozuk olur.
Bilinen Sınırlamalar ve Eleştiriler
Render Doğruluğu Uç Durumlar
Bazı yazı tiplerinde veya tarayıcılarda metin kutu dışına taşabilir. Özellikle şunlarda sorun yaşanabilir:
- Alışılmadık karakter aralığı
- Karışık font boyutları
- Canvas ve DOM alt piksel farklılıkları
- Tarayıcıya özgü şekillendirme farkları
Sanal kaydırma için çoğu durumda bu hatalar görünmezdir, ancak piksel mükemmel dizgi için DOM ölçümü daha güvenilirdir.
Canvas Ölçümü Tamamen Ücretsiz Değil
prepare() çağrısı Canvas'a erişir. Binlerce benzersiz metin için çok sık çağırırsanız darboğaz olabilir. Önbellekleme ve toplu işlem önerilir.
CSS Özellikleri Desteği Yok
Pretext.js sadece font belirtimiyle ölçüm yapar. letter-spacing, word-spacing, text-indent, text-transform gibi CSS özellikleri hesaba katılmaz.
DOM Render Etmeyi Değiştirmez
Pretext.js yalnızca ölçüm yapar, metni render etmez. Render için yine DOM veya Canvas/SVG gereklidir.
Pretext.js vs. Geleneksel Yaklaşımlar
| Özellik | Pretext.js | DOM ölçümü | Tahmini yükseklikler |
|---|---|---|---|
| Hız (1K öğe) | ~2ms | ~94ms | ~0ms |
| Doğruluk | Yüksek (Canvas) | Mükemmel | Düşük |
| DOM bağımlılığı |
prepare() sonrası yok |
Tam | Yok |
| Yeniden akış tetikleyici | Sıfır | Bir | Sıfır |
| Çok Dilli | Tam Unicode | Tam | Zayıf |
| CSS özelliği desteği | Sınırlı (font) | Tam | Yok |
| Bellek yükü | Önbellekli segmentler | DOM düğümleri | Minimal |
| Duyarlı düzenler | Bir prepare(), çok layout()
|
Genişlik başına yeniden ölç | Genişlik başına yeniden tahmin |
Kısıtlamalarınıza göre seçim yapın. Piksel mükemmelliği ve CSS özelliği gerekiyorsa DOM ölçümü, performans ve ölçek gerekiyorsa Pretext.js tercih edin.
Başlarken
Kurulum
npm install @chenglou/pretext
# veya
pnpm add @chenglou/pretext
# veya
bun add @chenglou/pretext
Temel Kullanım
import { prepare, layout } from '@chenglou/pretext';
// Paragraf ölçümü
const handle = prepare(
'Pretext.js computes text layout without touching the DOM.',
'16px "Inter"'
);
// Belirli genişlikte yüksekliği al
const result = layout(handle, 600, 24);
console.log(result.height); // Örneğin: 48 (2 satır x 24px)
console.log(result.lineCount); // Örneğin: 2
React ile Entegrasyon
import { prepare, layout } from '@chenglou/pretext';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useMemo, useRef } from 'react';
function VirtualChat({ messages }: { messages: string[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const containerWidth = 600;
const font = '14px "Inter"';
const lineHeight = 20;
const heights = useMemo(() => {
return messages.map(msg => {
const handle = prepare(msg, font);
const { height } = layout(handle, containerWidth, lineHeight);
return height + 24; // padding
});
}, [messages]);
const virtualizer = useVirtualizer({
count: messages.length,
getScrollElement: () => parentRef.current,
estimateSize: (index) => heights[index],
});
return (
<div ref={parentRef} style={{ height: '100vh', overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>
{virtualizer.getVirtualItems().map(virtualRow => (
<div
key={virtualRow.key}
style={{
position: 'absolute',
top: virtualRow.start,
width: containerWidth,
}}
>
{messages[virtualRow.index]}
</div>
))}
</div>
</div>
);
}
Herhangi bir mesaj DOM'a render edilmeden önce doğru yükseklik ile sanal sohbet listesi.
Etkileşimli Oyun Alanı
Pretext.js web sitesinde pretextjs.dev/playground adresinde metin yapıştırıp font seçip düzeni gerçek zamanlı görebilirsiniz. Entegrasyondan önce davranışı test edin.
Pretext.js'i Ne Zaman Kullanmamalısınız?
- Statik içerikli sayfalarda: Metin değişmiyorsa ve sanallaştırma yoksa Pretext.js'e gerek yok.
- Piksel mükemmel baskı dizgisi: Alt piksel farklılıklar önemliyse DOM ölçümü kullanın.
- Yoğun CSS metin stilleri: CSS özellikleriyle uyumlu ölçüm gerekirse DOM daha doğru.
- Sunucu tarafı render: Canvas API olmadan çalışmaz; SSR için henüz destek yok.
- Küçük, statik listeler: <50 öğe için DOM ölçümü yeterince hızlıdır.
Sıkça Sorulan Sorular
Pretext.js Üretim İçin Hazır mı?
Mart 2026'da yayınlandı, 14.000+ GitHub yıldızı aldı. Yaratıcısı Cheng Lou, milyonlarca kullanıcıya sahip Midjourney'nin ön ucunu yönetiyor. Test paketi onlarca dil ve uç durumu kapsıyor. Yine de yeni bir kütüphane; yazı tipinize ve içeriğinize göre test edin ve sürümünüzü sabitleyin.
Pretext.js React, Vue, Svelte ile Uyumlu mu?
Evet. Çerçeve bağımsızdır. prepare() ve layout() fonksiyonlarını React hook'larında, Vue composable'larında, Svelte store'larında veya düz JavaScript'te kullanabilirsiniz.
Web Fontlar Nasıl İşlenir?
prepare() çağrısı sırasında kullanılan fontun tarayıcıda yüklü olması gerekir. Yüklenmediyse yedek font ile ölçer ve yanlış sonuç verebilir. Ölçüm öncesi fontun yüklü olduğundan emin olmak için document.fonts.ready kullanın.
Canvas veya SVG Render İçin Kullanabilir miyim?
Evet. Pretext.js render hedefinden bağımsız ölçüm yapar. Satır sonları ve yükseklikleri ile Canvas 2D, WebGL, SVG veya DOM'da konumlandırma yapabilirsiniz.
RTL (Sağdan Sola) Dilleri Destekliyor mu?
Evet. Arapça, İbranice ve diğer RTL dillerde çift yönlü metin desteği ile doğru ölçüm sağlar.
Paket Boyutu Nedir?
Sıfır bağımlılıkla 15KB küçültülmüş. Sadece standart tarayıcı API'leri (Canvas measureText(), Intl.Segmenter) kullanılır.
DOM Ölçümüyle Doğruluk Karşılaştırması
Çoğu içerikte 1-2 piksel içinde eşleşir. letter-spacing, word-spacing gibi CSS özellikleri hesaba katılmaz; bu özelliklerde fark daha büyük olabilir.
Stilize Edilmiş Metinleri Ölçebilir mi?
Her prepare() çağrısı tek bir font ile çalışır. Karışık stiller için metni segmentlere ayırıp her stil için ayrı işleyici oluşturun.
Sonuç
Pretext.js, DOM yeniden akışı olmadan hızlı ve doğru metin ölçümü sunar. Sanal kaydırıcılar, sohbet arayüzleri, veri ızgaraları veya binlerce metin bloğunu ölçmesi gereken her arayüz için iki fonksiyon çağrısıyla büyük performans farkı sağlar.
Kütüphane CSS metin özelliklerini tam desteklemez, alt piksel farklılıklar olabilir ve henüz sunucu tarafı desteği yoktur. Ancak büyük, dinamik ve çok dilli metin listelerinde sanal kaydırma hızında rakipsizdir.
Daha hızlı metin yoğun arayüzler oluşturmaya hazır mısınız? Veri katmanınızı Apidog ile test edin, ardından Pretext.js'i render hattınıza entegre edin.


Top comments (0)