{"_id":"@chenglou/pretext","_rev":"10-536797afa8933ba8b93f59ed1c13005e","name":"@chenglou/pretext","dist-tags":{"latest":"0.0.9"},"versions":{"0.0.0":{"name":"@chenglou/pretext","version":"0.0.0","license":"MIT","_id":"@chenglou/pretext@0.0.0","maintainers":[{"name":"chenglou","email":"chenglou92@gmail.com"}],"dist":{"shasum":"f1ae56b90b34b8be94e3da3ea1023c9bb107a165","tarball":"https://registry.npmjs.org/@chenglou/pretext/-/pretext-0.0.0.tgz","fileCount":20,"integrity":"sha512-NJI3lKChGmPgk2x/sA3r4PbZoYNPcsjOqGyZeZhYu3luioQ1ld4ZY/1jcmcpg0hhOUpfbf9/mSEWdD3yQ90vtQ==","signatures":[{"sig":"MEQCIDgaHbDpYCDZn1zu/JF4hnWAwqMnaMElW80Qg5mEv1HYAiBG+UhvT6CkSsDpa5B8NHN6t6SGRFQzqv6rxxT9DMSFqg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":397708},"type":"module","types":"./src/layout.ts","exports":{".":"./src/layout.ts","./demos/*":"./pages/demos/*","./assets/*":"./pages/assets/*","./package.json":"./package.json"},"gitHead":"0662f0baba693175e7ec1478e0d6832d6b9475bd","scripts":{"check":"tsc && oxlint --type-aware src","start":"HOST=${HOST:-127.0.0.1}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --watch --no-clear-screen --host=$HOST:$PORT","start:lan":"HOST=0.0.0.0 bun run start","probe-check":"bun run scripts/probe-check.ts","corpus-check":"bun run scripts/corpus-check.ts","corpus-sweep":"bun run scripts/corpus-sweep.ts","gatsby-check":"bun run scripts/gatsby-check.ts","gatsby-sweep":"bun run scripts/gatsby-sweep.ts","accuracy-check":"bun run scripts/accuracy-check.ts","benchmark-check":"bun run scripts/benchmark-check.ts","corpus-taxonomy":"bun run scripts/corpus-taxonomy.ts","accuracy-snapshot":"bun run scripts/accuracy-check.ts --full --output=accuracy/chrome.json","corpus-font-matrix":"bun run scripts/corpus-font-matrix.ts","probe-check:safari":"PROBE_CHECK_BROWSER=safari bun run scripts/probe-check.ts","corpus-check:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-check.ts","corpus-sweep:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-sweep.ts","gatsby-check:safari":"GATSBY_CHECK_BROWSER=safari bun run scripts/gatsby-check.ts","gatsby-sweep:safari":"GATSBY_CHECK_BROWSER=safari bun run scripts/gatsby-sweep.ts","accuracy-check:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts","corpus-representative":"bun run scripts/corpus-representative.ts --output=corpora/representative.json","accuracy-check:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts","benchmark-check:safari":"BENCHMARK_CHECK_BROWSER=safari bun run scripts/benchmark-check.ts","accuracy-snapshot:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts --full --output=accuracy/safari.json","accuracy-snapshot:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts --full --output=accuracy/firefox.json","corpus-font-matrix:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-font-matrix.ts"},"_npmUser":{"name":"chenglou","email":"chenglou92@gmail.com"},"_npmVersion":"10.9.2","description":"Pure JavaScript/TypeScript library for multiline text measurement & layout. Fast, accurate & supports all the languages you didn't even know about. Allows rendering to DOM, Canvas, SVG and soon, server-side.","directories":{},"_nodeVersion":"23.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"oxlint":"^1.51.0","tsgolint":"^0.0.1","@types/bun":"latest","harfbuzzjs":"^0.10.0","oxlint-tsgolint":"^0.15.0"},"peerDependencies":{"typescript":"^5"},"_npmOperationalInternal":{"tmp":"tmp/pretext_0.0.0_1774580679359_0.9586565655372601","host":"s3://npm-registry-packages-npm-production"}},"0.0.1":{"name":"@chenglou/pretext","version":"0.0.1","license":"MIT","_id":"@chenglou/pretext@0.0.1","maintainers":[{"name":"chenglou","email":"chenglou92@gmail.com"}],"dist":{"shasum":"0a54286d9e5d0f9f63676ff06ce8801ad6a44d04","tarball":"https://registry.npmjs.org/@chenglou/pretext/-/pretext-0.0.1.tgz","fileCount":23,"integrity":"sha512-PWUuN7kGuWOtbBRgabWG56jrz1cIjEVR5reWUiRVtrKnM7fnSprKh4H9vfCv2JB1xhkjFqZojEJC/paFDurgng==","signatures":[{"sig":"MEQCIAdyL4kopzyP9gCzsztLW+etfxwFVoVqfv+A4RMZVCU2AiAh0m+hepPfrYehYz6HCgCyJmxg1dQgORoxrdz8nVBMQw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":424622},"type":"module","types":"./src/layout.ts","exports":{".":"./src/layout.ts","./demos/*":"./pages/demos/*","./assets/*":"./pages/assets/*","./package.json":"./package.json"},"gitHead":"b7323f2faa7896d53c671eebd0187db3d97e4240","scripts":{"check":"tsc && oxlint --type-aware src","start":"HOST=${HOST:-127.0.0.1}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=$HOST:$PORT","start:lan":"HOST=0.0.0.0 bun run start","probe-check":"bun run scripts/probe-check.ts","start:watch":"HOST=${HOST:-127.0.0.1}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --watch --no-clear-screen --host=$HOST:$PORT","corpus-check":"bun run scripts/corpus-check.ts","corpus-sweep":"bun run scripts/corpus-sweep.ts","gatsby-check":"bun run scripts/gatsby-check.ts","gatsby-sweep":"bun run scripts/gatsby-sweep.ts","accuracy-check":"bun run scripts/accuracy-check.ts","benchmark-check":"bun run scripts/benchmark-check.ts","corpus-taxonomy":"bun run scripts/corpus-taxonomy.ts","accuracy-snapshot":"bun run scripts/accuracy-check.ts --full --output=accuracy/chrome.json","corpus-font-matrix":"bun run scripts/corpus-font-matrix.ts","probe-check:safari":"PROBE_CHECK_BROWSER=safari bun run scripts/probe-check.ts","corpus-check:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-check.ts","corpus-sweep:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-sweep.ts","gatsby-check:safari":"GATSBY_CHECK_BROWSER=safari bun run scripts/gatsby-check.ts","gatsby-sweep:safari":"GATSBY_CHECK_BROWSER=safari bun run scripts/gatsby-sweep.ts","accuracy-check:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts","corpus-representative":"bun run scripts/corpus-representative.ts --output=corpora/representative.json","accuracy-check:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts","benchmark-check:safari":"BENCHMARK_CHECK_BROWSER=safari bun run scripts/benchmark-check.ts","accuracy-snapshot:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts --full --output=accuracy/safari.json","accuracy-snapshot:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts --full --output=accuracy/firefox.json","corpus-font-matrix:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-font-matrix.ts"},"_npmUser":{"name":"chenglou","email":"chenglou92@gmail.com"},"_npmVersion":"10.9.2","description":"Pure JavaScript/TypeScript library for multiline text measurement & layout. Fast, accurate & supports all the languages you didn't even know about. Allows rendering to DOM, Canvas, SVG and soon, server-side.","directories":{},"_nodeVersion":"23.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"oxlint":"^1.51.0","tsgolint":"^0.0.1","@types/bun":"latest","harfbuzzjs":"^0.10.0","oxlint-tsgolint":"^0.15.0"},"peerDependencies":{"typescript":"^5"},"_npmOperationalInternal":{"tmp":"tmp/pretext_0.0.1_1774612061502_0.6686825545571469","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@chenglou/pretext","version":"0.0.2","license":"MIT","_id":"@chenglou/pretext@0.0.2","maintainers":[{"name":"chenglou","email":"chenglou92@gmail.com"}],"dist":{"shasum":"625436a62b267c1777510efe978baa893e1653fd","tarball":"https://registry.npmjs.org/@chenglou/pretext/-/pretext-0.0.2.tgz","fileCount":29,"integrity":"sha512-bMPySDxbU1h+qU3D7HzTsz2vhBFIeO19Yp8gsYTJ219tjuX7WFsKJzk2oPVpwxl2P1VEuQGo6whg6Kr8gztphA==","signatures":[{"sig":"MEYCIQD4X6Vb6eY3mlOZ8peMFPNoPTjN+4H+KYcEzSGMwg1EkQIhAJWL5RKhlao8Y8CUWFSkOcC05feYocer1C0eUbMTTPtn","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":512701},"type":"module","types":"./src/layout.ts","exports":{".":"./src/layout.ts","./demos/*":"./pages/demos/*","./assets/*":"./pages/assets/*","./package.json":"./package.json"},"gitHead":"2604c28ff32a437d1eaa7eadcc1800444b441e1c","scripts":{"check":"tsc && oxlint --type-aware src","start":"HOST=${HOST:-127.0.0.1}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=$HOST:$PORT","start:lan":"HOST=0.0.0.0 bun run start","site:build":"rm -rf site && bun run scripts/build-demo-site.ts","probe-check":"bun run scripts/probe-check.ts","start:watch":"HOST=${HOST:-127.0.0.1}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --watch --no-clear-screen --host=$HOST:$PORT","corpus-check":"bun run scripts/corpus-check.ts","corpus-sweep":"bun run scripts/corpus-sweep.ts","gatsby-check":"bun run scripts/gatsby-check.ts","gatsby-sweep":"bun run scripts/gatsby-sweep.ts","accuracy-check":"bun run scripts/accuracy-check.ts","pre-wrap-check":"bun run scripts/pre-wrap-check.ts","benchmark-check":"bun run scripts/benchmark-check.ts","corpus-taxonomy":"bun run scripts/corpus-taxonomy.ts","accuracy-snapshot":"bun run scripts/accuracy-check.ts --full --output=accuracy/chrome.json","corpus-font-matrix":"bun run scripts/corpus-font-matrix.ts","probe-check:safari":"PROBE_CHECK_BROWSER=safari bun run scripts/probe-check.ts","corpus-check:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-check.ts","corpus-sweep:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-sweep.ts","gatsby-check:safari":"GATSBY_CHECK_BROWSER=safari bun run scripts/gatsby-check.ts","gatsby-sweep:safari":"GATSBY_CHECK_BROWSER=safari bun run scripts/gatsby-sweep.ts","accuracy-check:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts","corpus-representative":"bun run scripts/corpus-representative.ts --output=corpora/representative.json","accuracy-check:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts","benchmark-check:safari":"BENCHMARK_CHECK_BROWSER=safari bun run scripts/benchmark-check.ts","accuracy-snapshot:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts --full --output=accuracy/safari.json","accuracy-snapshot:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts --full --output=accuracy/firefox.json","corpus-font-matrix:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-font-matrix.ts"},"_npmUser":{"name":"chenglou","email":"chenglou92@gmail.com"},"_npmVersion":"10.9.2","description":"Pure JavaScript/TypeScript library for multiline text measurement & layout. Fast, accurate & supports all the languages you didn't even know about. Allows rendering to DOM, Canvas, SVG and soon, server-side.","directories":{},"_nodeVersion":"23.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"oxlint":"^1.51.0","tsgolint":"^0.0.1","@types/bun":"latest","harfbuzzjs":"^0.10.0","oxlint-tsgolint":"^0.15.0"},"peerDependencies":{"typescript":"^5"},"_npmOperationalInternal":{"tmp":"tmp/pretext_0.0.2_1774692288350_0.5738594800512049","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@chenglou/pretext","version":"0.0.3","license":"MIT","_id":"@chenglou/pretext@0.0.3","maintainers":[{"name":"chenglou","email":"chenglou92@gmail.com"}],"dist":{"shasum":"877f847afa8ea6a268d530a214369cfa0475dde1","tarball":"https://registry.npmjs.org/@chenglou/pretext/-/pretext-0.0.3.tgz","fileCount":42,"integrity":"sha512-RQmqMqUAPRCyv4R3LlRi/ao6KbNWYclqLA+V1HS7sWgyUUbjn3JmmlfXZSY/BjM4rbmIaMSyIVisYocYGYftiQ==","signatures":[{"sig":"MEUCIQDWQl4uYQo5aXiyrqR/eEyZtbzcXIdMtoMUKXjzlXjYgAIgOA+bCd+vpdDyun7FyxoZp1fCS02gOk/YFbKCVHpvICo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":641608},"main":"./dist/layout.js","type":"module","types":"./dist/layout.d.ts","exports":{".":{"types":"./dist/layout.d.ts","import":"./dist/layout.js","default":"./dist/layout.js"},"./demos/*":"./pages/demos/*","./assets/*":"./pages/assets/*","./package.json":"./package.json"},"gitHead":"19451bcdbdf597e3c92183393eaf7e2f3406c7b4","scripts":{"check":"tsc && oxlint --type-aware src","start":"HOST=${HOST:-127.0.0.1}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=$HOST:$PORT","prepack":"rm -rf dist && tsc -p tsconfig.build.json","start:lan":"HOST=0.0.0.0 bun run start","site:build":"rm -rf site && bun run scripts/build-demo-site.ts","probe-check":"bun run scripts/probe-check.ts","start:watch":"HOST=${HOST:-127.0.0.1}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --watch --no-clear-screen --host=$HOST:$PORT","corpus-check":"bun run scripts/corpus-check.ts","corpus-sweep":"bun run scripts/corpus-sweep.ts","gatsby-check":"bun run scripts/gatsby-check.ts","gatsby-sweep":"bun run scripts/gatsby-sweep.ts","build:package":"rm -rf dist && tsc -p tsconfig.build.json","accuracy-check":"bun run scripts/accuracy-check.ts","pre-wrap-check":"bun run scripts/pre-wrap-check.ts","benchmark-check":"bun run scripts/benchmark-check.ts","corpus-taxonomy":"bun run scripts/corpus-taxonomy.ts","accuracy-snapshot":"bun run scripts/accuracy-check.ts --full --output=accuracy/chrome.json","corpus-font-matrix":"bun run scripts/corpus-font-matrix.ts","package-smoke-test":"bun run scripts/package-smoke-test.ts","probe-check:safari":"PROBE_CHECK_BROWSER=safari bun run scripts/probe-check.ts","corpus-check:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-check.ts","corpus-sweep:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-sweep.ts","gatsby-check:safari":"GATSBY_CHECK_BROWSER=safari bun run scripts/gatsby-check.ts","gatsby-sweep:safari":"GATSBY_CHECK_BROWSER=safari bun run scripts/gatsby-sweep.ts","accuracy-check:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts","corpus-representative":"bun run scripts/corpus-representative.ts --output=corpora/representative.json","accuracy-check:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts","benchmark-check:safari":"BENCHMARK_CHECK_BROWSER=safari bun run scripts/benchmark-check.ts","accuracy-snapshot:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts --full --output=accuracy/safari.json","accuracy-snapshot:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts --full --output=accuracy/firefox.json","corpus-font-matrix:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-font-matrix.ts"},"_npmUser":{"name":"chenglou","email":"chenglou92@gmail.com"},"_npmVersion":"10.9.2","description":"Pure JavaScript/TypeScript library for multiline text measurement & layout. Fast, accurate & supports all the languages you didn't even know about. Allows rendering to DOM, Canvas, SVG and soon, server-side.","directories":{},"_nodeVersion":"23.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"oxlint":"^1.51.0","tsgolint":"^0.0.1","@types/bun":"latest","typescript":"6.0.2","oxlint-tsgolint":"^0.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretext_0.0.3_1774776912435_0.04257439531458229","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"@chenglou/pretext","version":"0.0.4","license":"MIT","_id":"@chenglou/pretext@0.0.4","maintainers":[{"name":"chenglou","email":"chenglou92@gmail.com"}],"dist":{"shasum":"6f9b955f406acd7160d5531adb4c20ec9917b0fb","tarball":"https://registry.npmjs.org/@chenglou/pretext/-/pretext-0.0.4.tgz","fileCount":47,"integrity":"sha512-FnPAFMid1/p1j2V2gRPUVBarGUIb2PhkkC9YNnTOfPtTDgHKh8siO8PP9pCxpFfYlcodWPJpE1UbSHGQqt8pQQ==","signatures":[{"sig":"MEQCIDA/5EfWPdteXwoTbJhEz5mtZR0nkiBsfwA3YPW45kcuAiBEE2ZzUT4TSDNlTJ+NbY1mT4z+YF8UAPkwxofSlpq2uQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":693392},"main":"./dist/layout.js","type":"module","types":"./dist/layout.d.ts","exports":{".":{"types":"./dist/layout.d.ts","import":"./dist/layout.js","default":"./dist/layout.js"},"./demos/*":"./pages/demos/*","./assets/*":"./pages/assets/*","./package.json":"./package.json"},"gitHead":"4e7139033640e09a299b06c240981eadb5894d74","scripts":{"check":"tsc && oxlint --type-aware src","start":"HOST=${HOST:-127.0.0.1}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=$HOST:$PORT","prepack":"rm -rf dist && tsc -p tsconfig.build.json","start:lan":"HOST=0.0.0.0 bun run start","site:build":"rm -rf site && bun run scripts/build-demo-site.ts","probe-check":"bun run scripts/probe-check.ts","start:watch":"HOST=${HOST:-127.0.0.1}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --watch --no-clear-screen --host=$HOST:$PORT","corpus-check":"bun run scripts/corpus-check.ts","corpus-sweep":"bun run scripts/corpus-sweep.ts","gatsby-check":"bun run scripts/gatsby-check.ts","gatsby-sweep":"bun run scripts/gatsby-sweep.ts","build:package":"rm -rf dist && tsc -p tsconfig.build.json","corpus-status":"bun run scripts/corpus-status.ts","accuracy-check":"bun run scripts/accuracy-check.ts","pre-wrap-check":"bun run scripts/pre-wrap-check.ts","benchmark-check":"bun run scripts/benchmark-check.ts","corpus-taxonomy":"bun run scripts/corpus-taxonomy.ts","status-dashboard":"bun run scripts/status-dashboard.ts","accuracy-snapshot":"bun run scripts/accuracy-check.ts --full --output=accuracy/chrome.json","corpus-font-matrix":"bun run scripts/corpus-font-matrix.ts","package-smoke-test":"bun run scripts/package-smoke-test.ts","probe-check:safari":"PROBE_CHECK_BROWSER=safari bun run scripts/probe-check.ts","corpus-check:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-check.ts","corpus-sweep:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-sweep.ts","gatsby-check:safari":"GATSBY_CHECK_BROWSER=safari bun run scripts/gatsby-check.ts","gatsby-sweep:safari":"GATSBY_CHECK_BROWSER=safari bun run scripts/gatsby-sweep.ts","accuracy-check:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts","corpus-representative":"bun run scripts/corpus-representative.ts --output=corpora/representative.json","corpus-status:refresh":"bun run scripts/corpus-representative.ts --output=corpora/representative.json && bun run scripts/corpus-sweep.ts --all --samples=9 --output=corpora/chrome-sampled.json && bun run scripts/corpus-sweep.ts --all --start=300 --end=900 --step=10 --output=corpora/chrome-step10.json && bun run scripts/corpus-status.ts","accuracy-check:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts","benchmark-check:safari":"BENCHMARK_CHECK_BROWSER=safari bun run scripts/benchmark-check.ts","accuracy-snapshot:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts --full --output=accuracy/safari.json","accuracy-snapshot:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts --full --output=accuracy/firefox.json","corpus-font-matrix:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-font-matrix.ts"},"_npmUser":{"name":"chenglou","email":"chenglou92@gmail.com"},"_npmVersion":"10.9.2","description":"Pure JavaScript/TypeScript library for multiline text measurement & layout. Fast, accurate & supports all the languages you didn't even know about. Allows rendering to DOM, Canvas, SVG and soon, server-side.","directories":{},"_nodeVersion":"23.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"oxlint":"^1.51.0","tsgolint":"^0.0.1","@types/bun":"latest","typescript":"6.0.2","oxlint-tsgolint":"^0.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretext_0.0.4_1775067857522_0.8105399379773799","host":"s3://npm-registry-packages-npm-production"}},"0.0.5":{"name":"@chenglou/pretext","version":"0.0.5","keywords":["text-layout","text-measurement","line-breaking","typography","canvas","svg","unicode"],"license":"MIT","_id":"@chenglou/pretext@0.0.5","maintainers":[{"name":"chenglou","email":"chenglou92@gmail.com"}],"homepage":"https://github.com/chenglou/pretext#readme","bugs":{"url":"https://github.com/chenglou/pretext/issues"},"dist":{"shasum":"1a6734df005c951c6aecaa0decced0d2a82034f2","tarball":"https://registry.npmjs.org/@chenglou/pretext/-/pretext-0.0.5.tgz","fileCount":58,"integrity":"sha512-A8GZN10REdFGsyuiUgLV8jjPDDFMg5GmgxGWV0I3igxBOnzj+jgz2VMmVD7g+SFyoctfeqHFxbNatKSzVRWtRg==","signatures":[{"sig":"MEUCICg9npIWMSx7MXIPsl7h2WA9yLy0xXWWJ6RVNin95btrAiEAlaK6s1CHqoP5aeMzEOX1Jw0DdOCE6rGfM7c7PDyBcOM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":861347},"main":"./dist/layout.js","type":"module","types":"./dist/layout.d.ts","exports":{".":{"types":"./dist/layout.d.ts","import":"./dist/layout.js","default":"./dist/layout.js"},"./demos/*":"./pages/demos/*","./assets/*":"./pages/assets/*","./rich-inline":{"types":"./dist/rich-inline.d.ts","import":"./dist/rich-inline.js","default":"./dist/rich-inline.js"},"./package.json":"./package.json"},"scripts":{"check":"tsc && oxlint --type-aware src","start":"HOST=${HOST:-0.0.0.0}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=$HOST:$PORT","prepack":"rm -rf dist && tsc -p tsconfig.build.json","site:build":"rm -rf site && bun run scripts/build-demo-site.ts","probe-check":"bun run scripts/probe-check.ts","corpus-check":"bun run scripts/corpus-check.ts","corpus-sweep":"bun run scripts/corpus-sweep.ts","build:package":"rm -rf dist && tsc -p tsconfig.build.json","corpus-status":"bun run scripts/corpus-status.ts","start:windows":"bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=0.0.0.0:3000","accuracy-check":"bun run scripts/accuracy-check.ts","keep-all-check":"bun run scripts/keep-all-check.ts","pre-wrap-check":"bun run scripts/pre-wrap-check.ts","benchmark-check":"bun run scripts/benchmark-check.ts","corpus-taxonomy":"bun run scripts/corpus-taxonomy.ts","status-dashboard":"bun run scripts/status-dashboard.ts","accuracy-snapshot":"bun run scripts/accuracy-check.ts --full --output=accuracy/chrome.json","corpus-font-matrix":"bun run scripts/corpus-font-matrix.ts","generate:bidi-data":"bun run scripts/generate-bidi-data.ts","package-smoke-test":"bun run scripts/package-smoke-test.ts","probe-check:safari":"PROBE_CHECK_BROWSER=safari bun run scripts/probe-check.ts","corpus-check:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-check.ts","corpus-sweep:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-sweep.ts","accuracy-check:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts","corpus-status:refresh":"bun run scripts/corpus-sweep.ts --all --start=300 --end=900 --step=10 --output=corpora/chrome-step10.json && bun run scripts/corpus-sweep.ts --browser=safari --all --start=300 --end=900 --step=10 --output=corpora/safari-step10.json && bun run scripts/corpus-status.ts","accuracy-check:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts","benchmark-check:safari":"BENCHMARK_CHECK_BROWSER=safari bun run scripts/benchmark-check.ts","accuracy-snapshot:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts --full --output=accuracy/safari.json","accuracy-snapshot:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts --full --output=accuracy/firefox.json","corpus-font-matrix:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-font-matrix.ts"},"_npmUser":{"name":"chenglou","email":"chenglou92@gmail.com"},"repository":{"url":"git+https://github.com/chenglou/pretext.git","type":"git"},"_npmVersion":"10.9.2","description":"Fast, accurate & comprehensive text measurement & layout","directories":{},"_nodeVersion":"23.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"marked":"^17.0.5","oxlint":"^1.51.0","tsgolint":"^0.0.1","@types/bun":"latest","typescript":"6.0.2","oxlint-tsgolint":"^0.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretext_0.0.5_1775666950182_0.850236511549961","host":"s3://npm-registry-packages-npm-production"}},"0.0.6":{"name":"@chenglou/pretext","version":"0.0.6","keywords":["text-layout","text-measurement","line-breaking","typography","canvas","svg","unicode"],"license":"MIT","_id":"@chenglou/pretext@0.0.6","maintainers":[{"name":"chenglou","email":"chenglou92@gmail.com"}],"homepage":"https://github.com/chenglou/pretext#readme","bugs":{"url":"https://github.com/chenglou/pretext/issues"},"dist":{"shasum":"1f4a37d3c8cb24c0e6a2e6b863601ee1337063e5","tarball":"https://registry.npmjs.org/@chenglou/pretext/-/pretext-0.0.6.tgz","fileCount":61,"integrity":"sha512-U10s4tFeyu3oVHfXuNWwZSKqHXefhaigpcBkGj60qQFRJ+yUoQ+ez3cGJelP7BWDAB58HCgjcTSmOcg+77afBQ==","signatures":[{"sig":"MEYCIQCdeh+4hXy06I4erqYbsAWax/GmcXDVyJqtld/dChLFUQIhAMm9CPgWnwhy+l1syZ30oBr35uAZmywmpv0cGdfte1NF","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":880230},"main":"./dist/layout.js","type":"module","types":"./dist/layout.d.ts","exports":{".":{"types":"./dist/layout.d.ts","import":"./dist/layout.js","default":"./dist/layout.js"},"./demos/*":"./pages/demos/*","./assets/*":"./pages/assets/*","./rich-inline":{"types":"./dist/rich-inline.d.ts","import":"./dist/rich-inline.js","default":"./dist/rich-inline.js"},"./package.json":"./package.json"},"scripts":{"check":"tsc && oxlint --type-aware src","start":"HOST=${HOST:-0.0.0.0}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=$HOST:$PORT","prepack":"rm -rf dist && tsc -p tsconfig.build.json","site:build":"rm -rf site && bun run scripts/build-demo-site.ts","probe-check":"bun run scripts/probe-check.ts","corpus-check":"bun run scripts/corpus-check.ts","corpus-sweep":"bun run scripts/corpus-sweep.ts","build:package":"rm -rf dist && tsc -p tsconfig.build.json","corpus-status":"bun run scripts/corpus-status.ts","start:windows":"bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=0.0.0.0:3000","accuracy-check":"bun run scripts/accuracy-check.ts","keep-all-check":"bun run scripts/keep-all-check.ts","pre-wrap-check":"bun run scripts/pre-wrap-check.ts","benchmark-check":"bun run scripts/benchmark-check.ts","corpus-taxonomy":"bun run scripts/corpus-taxonomy.ts","status-dashboard":"bun run scripts/status-dashboard.ts","accuracy-snapshot":"bun run scripts/accuracy-check.ts --full --output=accuracy/chrome.json","corpus-font-matrix":"bun run scripts/corpus-font-matrix.ts","generate:bidi-data":"bun run scripts/generate-bidi-data.ts","package-smoke-test":"bun run scripts/package-smoke-test.ts","probe-check:safari":"PROBE_CHECK_BROWSER=safari bun run scripts/probe-check.ts","corpus-check:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-check.ts","corpus-sweep:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-sweep.ts","letter-spacing-check":"bun run scripts/letter-spacing-check.ts","accuracy-check:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts","corpus-status:refresh":"bun run scripts/corpus-sweep.ts --all --start=300 --end=900 --step=10 --output=corpora/chrome-step10.json && bun run scripts/corpus-sweep.ts --browser=safari --all --start=300 --end=900 --step=10 --output=corpora/safari-step10.json && bun run scripts/corpus-status.ts","accuracy-check:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts","benchmark-check:safari":"BENCHMARK_CHECK_BROWSER=safari bun run scripts/benchmark-check.ts","letter-spacing-snapshot":"bun run scripts/letter-spacing-check.ts --output=accuracy/letter-spacing.json","accuracy-snapshot:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts --full --output=accuracy/safari.json","accuracy-snapshot:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts --full --output=accuracy/firefox.json","corpus-font-matrix:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-font-matrix.ts"},"_npmUser":{"name":"chenglou","email":"chenglou92@gmail.com"},"repository":{"url":"git+https://github.com/chenglou/pretext.git","type":"git"},"_npmVersion":"10.9.2","description":"Fast, accurate & comprehensive text measurement & layout","directories":{},"_nodeVersion":"23.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"marked":"^17.0.5","oxlint":"^1.51.0","tsgolint":"^0.0.1","@types/bun":"latest","typescript":"6.0.2","oxlint-tsgolint":"^0.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretext_0.0.6_1776837105193_0.8758002017488102","host":"s3://npm-registry-packages-npm-production"}},"0.0.7":{"name":"@chenglou/pretext","version":"0.0.7","keywords":["text-layout","text-measurement","line-breaking","typography","canvas","svg","unicode"],"license":"MIT","_id":"@chenglou/pretext@0.0.7","maintainers":[{"name":"chenglou","email":"chenglou92@gmail.com"}],"homepage":"https://github.com/chenglou/pretext#readme","bugs":{"url":"https://github.com/chenglou/pretext/issues"},"dist":{"shasum":"f31fb73cbf13f381d07e80f1d20c1018e86d2f57","tarball":"https://registry.npmjs.org/@chenglou/pretext/-/pretext-0.0.7.tgz","fileCount":61,"integrity":"sha512-FV5hj3fGqpBlzbANUbR+s+6XuNRrghVVyuNs33zdH2SzU7MvK+7GlW6xREjDCreixKbexEn7OEkDgAFeWuu5Hg==","signatures":[{"sig":"MEQCIGcj8Eoty/pnUKzJ0rzq0KrGhhwKjpX8YiN45xRa7A/gAiAU/+0vqhD4jCvx54xkmYnAlHGKTOx3KKv9VSSIbfazZQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":879261},"main":"./dist/layout.js","type":"module","types":"./dist/layout.d.ts","exports":{".":{"types":"./dist/layout.d.ts","import":"./dist/layout.js","default":"./dist/layout.js"},"./rich-inline":{"types":"./dist/rich-inline.d.ts","import":"./dist/rich-inline.js","default":"./dist/rich-inline.js"},"./package.json":"./package.json"},"gitHead":"bd956e3267aea8b516280889dba4e1ab0ffbac72","scripts":{"check":"tsc && oxlint --type-aware src","start":"HOST=${HOST:-0.0.0.0}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=$HOST:$PORT","prepack":"rm -rf dist && tsc -p tsconfig.build.json","site:build":"rm -rf site && bun run scripts/build-demo-site.ts","probe-check":"bun run scripts/probe-check.ts","corpus-check":"bun run scripts/corpus-check.ts","corpus-sweep":"bun run scripts/corpus-sweep.ts","build:package":"rm -rf dist && tsc -p tsconfig.build.json","corpus-status":"bun run scripts/corpus-status.ts","start:windows":"bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=0.0.0.0:3000","accuracy-check":"bun run scripts/accuracy-check.ts","keep-all-check":"bun run scripts/keep-all-check.ts","pre-wrap-check":"bun run scripts/pre-wrap-check.ts","benchmark-check":"bun run scripts/benchmark-check.ts","corpus-taxonomy":"bun run scripts/corpus-taxonomy.ts","status-dashboard":"bun run scripts/status-dashboard.ts","accuracy-snapshot":"bun run scripts/accuracy-check.ts --full --output=accuracy/chrome.json","corpus-font-matrix":"bun run scripts/corpus-font-matrix.ts","generate:bidi-data":"bun run scripts/generate-bidi-data.ts","package-smoke-test":"bun run scripts/package-smoke-test.ts","probe-check:safari":"PROBE_CHECK_BROWSER=safari bun run scripts/probe-check.ts","corpus-check:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-check.ts","corpus-sweep:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-sweep.ts","letter-spacing-check":"bun run scripts/letter-spacing-check.ts","accuracy-check:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts","corpus-status:refresh":"bun run scripts/corpus-sweep.ts --all --start=300 --end=900 --step=10 --output=corpora/chrome-step10.json && bun run scripts/corpus-sweep.ts --browser=safari --all --start=300 --end=900 --step=10 --output=corpora/safari-step10.json && bun run scripts/corpus-status.ts","accuracy-check:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts","benchmark-check:safari":"BENCHMARK_CHECK_BROWSER=safari bun run scripts/benchmark-check.ts","letter-spacing-snapshot":"bun run scripts/letter-spacing-check.ts --output=accuracy/letter-spacing.json","accuracy-snapshot:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts --full --output=accuracy/safari.json","accuracy-snapshot:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts --full --output=accuracy/firefox.json","corpus-font-matrix:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-font-matrix.ts"},"_npmUser":{"name":"chenglou","email":"chenglou92@gmail.com"},"repository":{"url":"git+https://github.com/chenglou/pretext.git","type":"git"},"_npmVersion":"10.9.2","description":"Fast, accurate & comprehensive text measurement & layout","directories":{},"sideEffects":false,"_nodeVersion":"23.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"marked":"^17.0.5","oxlint":"^1.51.0","tsgolint":"^0.0.1","@types/bun":"latest","typescript":"6.0.2","oxlint-tsgolint":"^0.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretext_0.0.7_1778398590542_0.16890908240282654","host":"s3://npm-registry-packages-npm-production"}},"0.0.8":{"name":"@chenglou/pretext","version":"0.0.8","keywords":["text-layout","text-measurement","line-breaking","typography","canvas","svg","unicode"],"license":"MIT","_id":"@chenglou/pretext@0.0.8","maintainers":[{"name":"chenglou","email":"chenglou92@gmail.com"}],"homepage":"https://github.com/chenglou/pretext#readme","bugs":{"url":"https://github.com/chenglou/pretext/issues"},"dist":{"shasum":"e8cac3533b7229816c3c8a11401d8e8b14bda9a5","tarball":"https://registry.npmjs.org/@chenglou/pretext/-/pretext-0.0.8.tgz","fileCount":69,"integrity":"sha512-yqm2GMxnPI7VHcHwe84P8ZF0JK/2d2DMKPqMN+s95jQhwDMYYXKVFVJUMEaVWckQStdsjdLav/0Vu+d9YbtGxA==","signatures":[{"sig":"MEUCIQDozMgCRy0roLtfteD3tCGUdeLrbnmIpfidYIS6ZTqFLwIgUMGO5Z52gnd+3SXSyb3IYrzLt0XciSuH3ogsBqm4GcU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":902216},"main":"./dist/layout.js","type":"module","types":"./dist/layout.d.ts","exports":{".":{"types":"./dist/layout.d.ts","import":"./dist/layout.js","default":"./dist/layout.js"},"./rich-inline":{"types":"./dist/rich-inline.d.ts","import":"./dist/rich-inline.js","default":"./dist/rich-inline.js"},"./package.json":"./package.json"},"gitHead":"a79a6a595d7ea8a07874637b5234ee255b6a4a14","scripts":{"check":"tsc && oxlint --type-aware src && knip","start":"HOST=${HOST:-0.0.0.0}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=$HOST:$PORT","prepack":"rm -rf dist && tsc -p tsconfig.build.json","site:build":"rm -rf site && bun run scripts/build-demo-site.ts","probe-check":"bun run scripts/probe-check.ts","corpus-check":"bun run scripts/corpus-check.ts","corpus-sweep":"bun run scripts/corpus-sweep.ts","symbol-check":"bun run scripts/symbol-check.ts","build:package":"rm -rf dist && tsc -p tsconfig.build.json","corpus-status":"bun run scripts/corpus-status.ts","start:windows":"bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=0.0.0.0:3000","accuracy-check":"bun run scripts/accuracy-check.ts","keep-all-check":"bun run scripts/keep-all-check.ts","pre-wrap-check":"bun run scripts/pre-wrap-check.ts","benchmark-check":"bun run scripts/benchmark-check.ts","corpus-taxonomy":"bun run scripts/corpus-taxonomy.ts","status-dashboard":"bun run scripts/status-dashboard.ts","accuracy-snapshot":"bun run scripts/accuracy-check.ts --full --output=accuracy/chrome.json","corpus-font-matrix":"bun run scripts/corpus-font-matrix.ts","generate:bidi-data":"bun run scripts/generate-bidi-data.ts","package-smoke-test":"bun run scripts/package-smoke-test.ts","probe-check:safari":"PROBE_CHECK_BROWSER=safari bun run scripts/probe-check.ts","corpus-check:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-check.ts","corpus-sweep:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-sweep.ts","letter-spacing-check":"bun run scripts/letter-spacing-check.ts","accuracy-check:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts","corpus-status:refresh":"bun run scripts/corpus-sweep.ts --all --start=300 --end=900 --step=10 --output=corpora/chrome-step10.json && bun run scripts/corpus-sweep.ts --browser=safari --all --start=300 --end=900 --step=10 --output=corpora/safari-step10.json && bun run scripts/corpus-status.ts","accuracy-check:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts","benchmark-check:safari":"BENCHMARK_CHECK_BROWSER=safari bun run scripts/benchmark-check.ts","letter-spacing-snapshot":"bun run scripts/letter-spacing-check.ts --output=accuracy/letter-spacing.json","accuracy-snapshot:safari":"ACCURACY_CHECK_BROWSER=safari bun run scripts/accuracy-check.ts --full --output=accuracy/safari.json","accuracy-snapshot:firefox":"ACCURACY_CHECK_BROWSER=firefox bun run scripts/accuracy-check.ts --full --output=accuracy/firefox.json","corpus-font-matrix:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-font-matrix.ts"},"_npmUser":{"name":"chenglou","email":"chenglou92@gmail.com"},"repository":{"url":"git+https://github.com/chenglou/pretext.git","type":"git"},"_npmVersion":"10.9.2","description":"Fast, accurate & comprehensive text measurement & layout","directories":{},"sideEffects":false,"_nodeVersion":"23.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^6.14.2","marked":"^17.0.5","oxlint":"^1.51.0","tsgolint":"^0.0.1","@types/bun":"latest","typescript":"6.0.2","oxlint-tsgolint":"^0.15.0"},"_npmOperationalInternal":{"tmp":"tmp/pretext_0.0.8_1781248625431_0.5786217666226929","host":"s3://npm-registry-packages-npm-production"}},"0.0.9":{"_id":"@chenglou/pretext@0.0.9","bugs":{"url":"https://github.com/chenglou/pretext/issues"},"dist":{"shasum":"e158b60285cce891097b76126b7851a84011ac9b","tarball":"https://registry.npmjs.org/@chenglou/pretext/-/pretext-0.0.9.tgz","fileCount":69,"integrity":"sha512-vInlpC//3vjRfWTs78k2BBIq/0bUdpjIp8TIn9fXe0NvnChhTQZfjioVzERQQbkQvCVpPpw7PDOrTWMHpq27VA==","signatures":[{"sig":"MEYCIQCtpekFWOmK6+U042qFXv8ebYOB4OqomEHUfIS7oPjM5AIhANujJCAHMHzFl7+R/NBekrLd2Rb6v+3e6YfPj8SlwpiL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDB7KpFx4ShyM8lrEOOCiK0mKmrjDQO4+kUCmpcPAtmeAiAli9zmSQ3X8u/UT31LQfpFXq9hWbzkx02n8+QJemNmQw=="}],"unpackedSize":887142},"main":"./dist/layout.js","name":"@chenglou/pretext","type":"module","types":"./dist/layout.d.ts","exports":{".":{"types":"./dist/layout.d.ts","import":"./dist/layout.js","default":"./dist/layout.js"},"./rich-inline":{"types":"./dist/rich-inline.d.ts","import":"./dist/rich-inline.js","default":"./dist/rich-inline.js"},"./package.json":"./package.json"},"license":"MIT","scripts":{"check":"tsc && oxlint --type-aware src tests/wrapping scripts/wrapping-check.ts && knip","start":"HOST=${HOST:-0.0.0.0}; PORT=3000; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Freeing port $PORT: terminating $pids\"; kill $pids 2>/dev/null || true; sleep 1; pids=$(lsof -tiTCP:$PORT -sTCP:LISTEN 2>/dev/null); if [ -n \"$pids\" ]; then echo \"Port $PORT still busy: killing $pids\"; kill -9 $pids 2>/dev/null || true; fi; fi; bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=$HOST:$PORT","prepack":"rm -rf dist && tsc -p tsconfig.build.json","font-probe":"bun run scripts/font-probe.ts","site:build":"rm -rf site && bun run scripts/build-demo-site.ts","probe-check":"bun run scripts/probe-check.ts","corpus-check":"bun run scripts/corpus-check.ts","build:package":"rm -rf dist && tsc -p tsconfig.build.json","corpus-status":"bun run scripts/corpus-status.ts","start:windows":"bun pages/*.html pages/demos/*.html pages/demos/*/index.html --host=0.0.0.0:3000","test:wrapping":"bun run scripts/wrapping-check.ts","benchmark-check":"bun run scripts/benchmark-check.ts","corpus-taxonomy":"bun run scripts/corpus-taxonomy.ts","status-dashboard":"bun run scripts/status-dashboard.ts","corpus-font-matrix":"bun run scripts/corpus-font-matrix.ts","generate:bidi-data":"bun run scripts/generate-bidi-data.ts","package-smoke-test":"bun run scripts/package-smoke-test.ts","probe-check:safari":"PROBE_CHECK_BROWSER=safari bun run scripts/probe-check.ts","corpus-check:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-check.ts","justification-check":"bun run scripts/justification-check.ts","benchmark-check:safari":"BENCHMARK_CHECK_BROWSER=safari bun run scripts/benchmark-check.ts","test:wrapping:snapshot":"bun run scripts/wrapping-check.ts --browser=all --snapshot && bun run scripts/corpus-status.ts && bun run scripts/status-dashboard.ts","corpus-font-matrix:safari":"CORPUS_CHECK_BROWSER=safari bun run scripts/corpus-font-matrix.ts"},"version":"0.0.9","_npmUser":{"name":"chenglou","email":"chenglou92@gmail.com"},"homepage":"https://github.com/chenglou/pretext#readme","keywords":["text-layout","text-measurement","line-breaking","typography","canvas","svg","unicode"],"repository":{"url":"git+https://github.com/chenglou/pretext.git","type":"git"},"_npmVersion":"10.9.2","description":"Fast, accurate & comprehensive text measurement & layout","directories":{},"maintainers":[{"name":"chenglou","email":"chenglou92@gmail.com"}],"sideEffects":false,"_nodeVersion":"23.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^6.14.2","marked":"^17.0.5","oxlint":"^1.51.0","tsgolint":"^0.0.1","@types/bun":"latest","typescript":"6.0.2","oxlint-tsgolint":"^0.15.0","playwright-core":"^1.62.1"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pretext_0.0.9_1788774420759_0.27537795513667374"}}},"time":{"created":"2026-03-27T03:04:39.239Z","modified":"2026-09-07T09:47:00.991Z","0.0.0":"2026-03-27T03:04:39.505Z","0.0.1":"2026-03-27T11:47:41.681Z","0.0.2":"2026-03-28T10:04:48.552Z","0.0.3":"2026-03-29T09:35:12.651Z","0.0.4":"2026-04-01T18:24:17.649Z","0.0.5":"2026-04-08T16:49:10.450Z","0.0.6":"2026-04-22T05:51:45.515Z","0.0.7":"2026-05-10T07:36:30.751Z","0.0.8":"2026-06-12T07:17:05.585Z","0.0.9":"2026-09-07T09:47:00.853Z"},"bugs":{"url":"https://github.com/chenglou/pretext/issues"},"license":"MIT","homepage":"https://github.com/chenglou/pretext#readme","keywords":["text-layout","text-measurement","line-breaking","typography","canvas","svg","unicode"],"repository":{"url":"git+https://github.com/chenglou/pretext.git","type":"git"},"description":"Fast, accurate & comprehensive text measurement & layout","maintainers":[{"name":"chenglou","email":"chenglou92@gmail.com"}],"readme":"# Pretext\n\nPure JavaScript/TypeScript library for multiline text measurement & layout. Fast, accurate & supports all the languages you didn't even know about. Allows rendering to DOM, Canvas, SVG and soon, server-side.\n\nPretext side-steps the need for DOM measurements (e.g. `getBoundingClientRect`, `offsetHeight`), which trigger layout reflow, one of the most expensive operations in the browser. It implements its own text measurement logic, using the browsers' own font engine as ground truth (very AI-friendly iteration method).\n\n## Installation\n\n```sh\nnpm install @chenglou/pretext\n```\n\n## Demos\n\nClone the repo, run `bun install`, then `bun start`, and open `/demos/index` in your browser. On Windows, use `bun run start:windows`.\nAlternatively, see them live at [chenglou.me/pretext](https://chenglou.me/pretext/). Some more at [somnai-dreams.github.io/pretext-demos](https://somnai-dreams.github.io/pretext-demos/)\n\n## API\n\nPretext serves 2 use cases:\n\n### 1. Measure a paragraph's height _without ever touching DOM_\n\n```ts\nimport { prepare, layout } from '@chenglou/pretext'\n\nconst prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀‎', '16px Inter')\nconst { height, lineCount } = layout(prepared, 320, 20) // pure arithmetic. No DOM layout & reflow!\n```\n\n`prepare()` does the one-time work: normalize whitespace, segment the text, apply glue rules, measure the segments with canvas, and return an opaque handle. `layout()` is the cheap hot path after that: pure arithmetic over cached widths. Do not rerun `prepare()` for the same text and configs; that'd defeat its precomputation. For example, on resize, only rerun `layout()`.\n\nIf you want textarea-like text where ordinary spaces, `\\t` tabs, and `\\n` hard breaks stay visible, pass `{ whiteSpace: 'pre-wrap' }` to `prepare()`:\n\n```ts\nconst prepared = prepare(textareaValue, '16px Inter', { whiteSpace: 'pre-wrap' })\nconst { height } = layout(prepared, textareaWidth, 20)\n```\n\nOther `prepare()` options are `{ wordBreak: 'keep-all' }` for CSS-like `word-break: keep-all`, and `{ letterSpacing: n }` to match CSS `letter-spacing` (`n` is treated as a px value).\n\nThe returned height is the crucial last piece for unlocking web UIs:\n- proper virtualization/occlusion without guesstimates & caching\n- fancy userland layouts: masonry, JS-driven flexbox-like implementations, nudging a few layout values without CSS hacks (imagine that), etc.\n- _development time_ verification (especially now with AI) that labels on e.g. buttons don't overflow to the next line, browser-free\n- prevent layout shift when new text loads and you wanna re-anchor the scroll position\n\n### 2. Lay out the paragraph lines manually yourself\n\nSwitch out `prepare` with `prepareWithSegments`, then:\n\n- `layoutWithLines()` gives you all the lines at a fixed width:\n\n```ts\nimport { prepareWithSegments, layoutWithLines } from '@chenglou/pretext'\n\nconst prepared = prepareWithSegments('AGI 春天到了. بدأت الرحلة 🚀', '18px \"Helvetica Neue\"')\nconst { lines } = layoutWithLines(prepared, 320, 26) // 320px max width, 26px line height\nfor (let i = 0; i < lines.length; i++) ctx.fillText(lines[i].text, 0, i * 26)\n```\n\n- `measureLineStats()` and `walkLineRanges()` give you line counts, widths and cursors without building the text strings:\n\n```ts\nimport { measureLineStats, walkLineRanges } from '@chenglou/pretext'\n\nconst { lineCount, maxLineWidth } = measureLineStats(prepared, 320)\nlet maxW = 0\nwalkLineRanges(prepared, 320, line => { if (line.width > maxW) maxW = line.width })\n// maxW is now the widest line — the tightest container width that still fits the text! This multiline \"shrink wrap\" has been missing from web\n```\n\n- `layoutNextLineRange()` lets you route text one row at a time when width changes as you go. If you want the actual string too, `materializeLineRange()` turns that one range back into a full line:\n\n```ts\nimport { layoutNextLineRange, materializeLineRange, prepareWithSegments, type LayoutCursor } from '@chenglou/pretext'\n\nconst prepared = prepareWithSegments(article, BODY_FONT)\nlet cursor: LayoutCursor = { segmentIndex: 0, graphemeIndex: 0 }\nlet y = 0\n\n// Flow text around a floated image: lines beside the image are narrower\nwhile (true) {\n  const width = y < image.bottom ? columnWidth - image.width : columnWidth\n  const range = layoutNextLineRange(prepared, cursor, width)\n  if (range === null) break\n\n  const line = materializeLineRange(prepared, range)\n  ctx.fillText(line.text, 0, y)\n  cursor = range.end\n  y += 26\n}\n```\n\nThis usage allows rendering to canvas, SVG, WebGL and (eventually) server-side. See the `/demos/dynamic-layout` demo for a richer example.\n\nFor hyphenation in manual layout, insert soft hyphens before `prepare()` / `prepareWithSegments()`. Pretext treats them as optional break points: unchosen soft hyphens stay invisible, while chosen breaks materialize as a trailing `-`. A soft hyphen at the end of the paragraph is consumed without painting a hyphen. For mixed-language or user-generated app text, prefer conservative, locale-aware insertion over aggressive pattern hyphenation. Automatic hyphenation is not built in today.\n\nVery narrow SHY fallback remains approximate: Safari can overflow a prefix plus hyphen, while Chromium and Gecko may move part of the prefix to another line.\n\nIf your manual layout needs a small helper for rich-text inline flow, code spans, mentions, chips, and browser-like boundary whitespace collapse, there is a helper at `@chenglou/pretext/rich-inline`. It stays inline-only and `white-space: normal`-only on purpose:\n\n```ts\nimport { materializeRichInlineLineRange, prepareRichInline, walkRichInlineLineRanges } from '@chenglou/pretext/rich-inline'\n\nconst prepared = prepareRichInline([\n  { text: 'Ship ', font: '500 17px Inter' },\n  { text: '@maya', font: '700 12px Inter', break: 'never', extraWidth: 22 },\n  { text: \"'s rich-note\", font: '500 17px Inter' },\n])\n\nwalkRichInlineLineRanges(prepared, 320, range => {\n  const line = materializeRichInlineLineRange(prepared, range)\n  // each fragment keeps its source item index, text slice, gapBefore, and cursors\n})\n```\n\nIt is intentionally narrow:\n- raw inline text in, including boundary spaces\n- caller-owned `extraWidth` for pill chrome\n- `break: 'never'` for atomic items like chips and mentions\n- `white-space: normal` only\n- not a nested markup tree and not a general CSS inline formatting engine\n\nFragment and cursor `itemIndex` values refer to that original list, including when it contains empty items. A collapsed boundary space uses the first space's font and letter spacing; `gapBefore` can be zero or negative. Zero-width content can still occupy a line and carry a break opportunity.\n\n### API Glossary\n\nUse-case 1 APIs:\n```ts\nprepare(text: string, font: string, options?: { whiteSpace?: 'normal' | 'pre-wrap', wordBreak?: 'normal' | 'keep-all', letterSpacing?: number }): PreparedText // one-time text analysis + measurement pass, returns an opaque value to pass to `layout()`. Make sure `font` and `letterSpacing` are synced with your CSS for the text you're measuring. `font` is the same format as what you'd use for `myCanvasContext.font = ...`, e.g. `16px Inter`; `letterSpacing` is a CSS pixel value.\nlayout(prepared: PreparedText, maxWidth: number, lineHeight: number): { height: number, lineCount: number } // calculates text height given a max width and lineHeight. Make sure `lineHeight` is synced with your css `line-height` declaration for the text you're measuring.\n```\n\nUse-case 2 APIs:\n```ts\nprepareWithSegments(text: string, font: string, options?: { whiteSpace?: 'normal' | 'pre-wrap', wordBreak?: 'normal' | 'keep-all', letterSpacing?: number }): PreparedTextWithSegments // same as `prepare()`, but returns a richer structure for manual line layout needs\nlayoutWithLines(prepared: PreparedTextWithSegments, maxWidth: number, lineHeight: number): { height: number, lineCount: number, lines: LayoutLine[] } // high-level api for manual layout needs. Accepts a fixed max width for all lines. Similar to `layout()`'s return, but additionally returns the lines info\nwalkLineRanges(prepared: PreparedTextWithSegments, maxWidth: number, onLine: (line: LayoutLineRange) => void): number // low-level api for manual layout needs. Accepts a fixed max width for all lines. Calls `onLine` once per line with its actual calculated line width and start/end cursors, without building line text strings. Very useful for certain cases where you wanna speculatively test a few width and height boundaries (e.g. binary search a nice width value by repeatedly calling walkLineRanges and checking the line count, and therefore height, is \"nice\" too). You can have text messages shrinkwrap and balanced text layout this way. After walkLineRanges calls, you'd call layoutWithLines once, with your satisfying max width, to get the actual lines info.\nmeasureLineStats(prepared: PreparedTextWithSegments, maxWidth: number): { lineCount: number, maxLineWidth: number } // returns only how many lines this width produces, and how wide the widest one is. Avoids line/string allocations.\nmeasureNaturalWidth(prepared: PreparedTextWithSegments): number // returns the widest forced line when width itself is not the thing causing wraps\nlayoutNextLine(prepared: PreparedTextWithSegments, start: LayoutCursor, maxWidth: number): LayoutLine | null // iterator-like api for laying out each line with a different width! Returns the LayoutLine starting from `start`, or `null` when the paragraph's exhausted. Pass the previous line's `end` cursor as the next `start`.\nlayoutNextLineRange(prepared: PreparedTextWithSegments, start: LayoutCursor, maxWidth: number): LayoutLineRange | null // same as layoutNextLine(), but without allocating line text strings. Useful for variable-width manual layout, occlusion, and virtualization measurements.\nmaterializeLineRange(prepared: PreparedTextWithSegments, line: LayoutLineRange): LayoutLine // turns a LayoutLineRange from layoutNextLineRange() or walkLineRanges() into a full line with text\ntype LineStats = {\n  lineCount: number // Number of wrapped lines, e.g. 3\n  maxLineWidth: number // Widest wrapped line, e.g. 192.5\n}\ntype LayoutLine = {\n  text: string // Full text content of this line, e.g. 'hello world'\n  width: number // Measured width of this line, e.g. 87.5\n  start: LayoutCursor // Inclusive start cursor in prepared segments/graphemes\n  end: LayoutCursor // Exclusive end cursor in prepared segments/graphemes\n}\ntype LayoutLineRange = {\n  width: number // Measured width of this line, e.g. 87.5\n  start: LayoutCursor // Inclusive start cursor in prepared segments/graphemes\n  end: LayoutCursor // Exclusive end cursor in prepared segments/graphemes\n}\ntype LayoutCursor = {\n  segmentIndex: number // Segment index in prepareWithSegments' prepared rich segment stream\n  graphemeIndex: number // Grapheme index within that segment; `0` at segment boundaries\n}\n```\n\nHelper for rich-text inline flow:\n```ts\nprepareRichInline(items: RichInlineItem[]): PreparedRichInline // compile raw inline items with their original text. The compiler owns cross-item collapsed whitespace and caches each item's natural width\nlayoutNextRichInlineLineRange(prepared: PreparedRichInline, maxWidth: number, start?: RichInlineCursor): RichInlineLineRange | null // stream one line of rich-text inline flow at a time without building fragment text strings\nwalkRichInlineLineRanges(prepared: PreparedRichInline, maxWidth: number, onLine: (line: RichInlineLineRange) => void): number // non-materializing line walker for rich-text inline flow shrinkwrap/stats work\nmaterializeRichInlineLineRange(prepared: PreparedRichInline, line: RichInlineLineRange): RichInlineLine // turns one previously computed rich-inline line range back into full fragment text\nmeasureRichInlineStats(prepared: PreparedRichInline, maxWidth: number): { lineCount: number, maxLineWidth: number } // returns only how many lines this width produces, and how wide the widest one is. Avoids fragment-text allocations.\ntype RichInlineItem = {\n  text: string // raw author text, including leading/trailing collapsible spaces\n  font: string // canvas font shorthand for this item\n  letterSpacing?: number // extra horizontal spacing between graphemes, in CSS px\n  break?: 'normal' | 'never' // `never` keeps the item atomic, like a chip\n  extraWidth?: number // caller-owned horizontal chrome, e.g. padding + border width\n}\ntype RichInlineCursor = {\n  itemIndex: number // Which source RichInlineItem this cursor is currently in\n  segmentIndex: number // Segment index within that item's prepared text\n  graphemeIndex: number // Grapheme index within that segment; `0` at segment boundaries\n}\ntype RichInlineFragment = {\n  itemIndex: number // index back into the original RichInlineItem array\n  text: string // Text slice for this fragment\n  gapBefore: number // collapsed boundary gap paid before this fragment on this line\n  occupiedWidth: number // text width plus extraWidth\n  start: LayoutCursor // Start cursor within the item's prepared text\n  end: LayoutCursor // End cursor within the item's prepared text\n}\ntype RichInlineLine = {\n  fragments: RichInlineFragment[] // Materialized fragments on this line\n  width: number // Measured width of this line, including gapBefore/extraWidth\n  end: RichInlineCursor // Exclusive end cursor for continuing the next line\n}\ntype RichInlineFragmentRange = {\n  itemIndex: number // index back into the original RichInlineItem array\n  gapBefore: number // collapsed boundary gap paid before this fragment on this line\n  occupiedWidth: number // text width plus extraWidth\n  start: LayoutCursor // Start cursor within the item's prepared text\n  end: LayoutCursor // End cursor within the item's prepared text\n}\ntype RichInlineLineRange = {\n  fragments: RichInlineFragmentRange[] // Non-materialized fragment ownership/ranges on this line\n  width: number // Measured width of this line, including gapBefore/extraWidth\n  end: RichInlineCursor // Exclusive end cursor for continuing the next line\n}\ntype RichInlineStats = {\n  lineCount: number // Number of wrapped lines, e.g. 3\n  maxLineWidth: number // Widest wrapped line, e.g. 192.5\n}\n```\n\nOther helpers:\n```ts\nclearCache(): void // clears Pretext's shared internal caches used by prepare() and prepareWithSegments(). Useful if your app cycles through many different fonts or text variants and you want to release the accumulated cache\nsetLocale(locale?: string): void // optional (by default we use the current locale). Sets locale for future prepare() and prepareWithSegments(). Internally, it also calls clearCache(). Setting a new locale doesn't affect existing prepare() and prepareWithSegments() states (no mutations to them)\n```\n\nNotes:\n- `PreparedText` is the opaque fast-path handle. `PreparedTextWithSegments` is the richer manual-layout handle.\n- `LayoutCursor` is a segment/grapheme cursor, not a raw string offset.\n- `layout()` with an empty string returns `{ lineCount: 0, height: 0 }`. Browsers still size an empty block to one `line-height`, so clamp with `Math.max(1, lineCount) * lineHeight` if you need that behavior.\n- The richer handle also includes approximate `segLevels` for custom bidi-aware rendering. Base direction and weak/neutral state restart at Unicode bidi paragraph separators in the normalized text. In `pre-wrap`, normalized newlines start fresh paragraphs; in `normal`, ASCII newlines collapse to spaces first. Tabs and U+2028 LINE SEPARATOR do not restart paragraph direction. This is not a full Unicode Bidirectional Algorithm implementation, and the line-breaking APIs do not read these levels.\n- Segment widths are browser-canvas widths for line breaking, not exact glyph-position data for custom Arabic or mixed-direction x-coordinate reconstruction.\n- If a soft hyphen wins the break, materialized line text includes the visible trailing `-`.\n- `measureNaturalWidth()` returns the widest forced line. Hard breaks still count.\n- `prepare()` and `prepareWithSegments()` do horizontal-only work. `lineHeight` stays a layout-time input.\n\n## Caveats\n\nPretext doesn't try to be a full font rendering engine (yet?). It currently targets the common text setup:\n- `white-space: normal` and `pre-wrap`\n- `word-break: normal` and `keep-all`\n- `overflow-wrap: break-word`. Very narrow widths can still break inside words and independent symbol runs, but only at grapheme boundaries.\n- `line-break: auto`\n- `letter-spacing` as a numeric pixel value passed to `prepare()` / `prepareWithSegments()`\n- Tabs follow the default browser-style `tab-size: 8`\n- `{ wordBreak: 'keep-all' }` is supported too. It behaves like you'd expect for CJK/Hangul and no-space mixed Latin/numeric/CJK text, while keeping the same `overflow-wrap: break-word` fallback for overlong runs.\n- `system-ui` and `-apple-system` are unsafe for `layout()` accuracy on macOS. Use a named font. See the [platform bug ledger](PLATFORM_BUGS.md) for the Chrome and Firefox issues.\n- Emoji next to punctuation can still wrap differently from the browser.\n- Text containing zero-width spaces can still wrap differently from the browser. The rich-inline helper preserves standalone ZWSP items, but still inherits the flat text engine's wrapping limits inside each item.\n- Some fonts, such as Shantell Sans, can produce different line breaks inside long words in Pretext and the browser.\n- If your page sets `lang`, a generic font like `sans-serif` may select a different font from the one Pretext measures. Use a named font and check the result in your browser.\n- Runtime requires `Intl.Segmenter` and Canvas 2D text measurement. Browsers or runtimes without `Intl.Segmenter` are currently unsupported.\n- CSS text features outside the canvas `font` shorthand, such as `font-optical-sizing`, `font-feature-settings`, and standalone `font-variation-settings`, are not modeled separately. Variable-font axes only help when the active axis is reflected in the canvas font string, for example via weight.\n\n## Develop\n\nSee [DEVELOPMENT.md](https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md) for the dev setup and commands.\n\n## Credits\n\nSebastian Markbage first planted the seed with [text-layout](https://github.com/chenglou/text-layout) last decade. His design — canvas `measureText` for shaping, bidi from pdf.js, streaming line breaking — informed the architecture we kept pushing forward here.\n","readmeFilename":"README.md"}