{"_id":"@dude1wudv/pi-tui","_rev":"9-6676a1fc457f660c0fb6532152cbb858","name":"@dude1wudv/pi-tui","dist-tags":{"latest":"20.1.9"},"versions":{"20.1.0":{"name":"@dude1wudv/pi-tui","version":"20.1.0","keywords":["tui","terminal","ui","text-editor","differential-rendering","typescript","cli"],"author":{"name":"Can Boluk"},"license":"MIT","_id":"@dude1wudv/pi-tui@20.1.0","maintainers":[{"name":"dude1wudv","email":"1602858215@qq.com"}],"contributors":[{"name":"Mario Zechner"}],"homepage":"https://omp.sh","bugs":{"url":"https://github.com/can1357/oh-my-pi/issues"},"dist":{"shasum":"3157afc2885b9f2c4c3b40c38b27ffb541adc382","tarball":"https://registry.npmjs.org/@dude1wudv/pi-tui/-/pi-tui-20.1.0.tgz","fileCount":77,"integrity":"sha512-yCcPPIcssqpALjqMeAqVM7a29yOchUG9BmyQRtTa9icWBpEI2o/W7gQ4T29v1vkUJUF5tEsgNlpVW5hKErOoIA==","signatures":[{"sig":"MEQCIE/SW9MYOlT+FbOhRJd8Qj1z3NIwUA57m2QukSVY501CAiAOP5NxuV05xxT6RleCcuy7QZNc/zS9/ZSNRLLVrgmbrw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1362001},"main":"./src/index.ts","type":"module","_from":"file:C:/Users/sun/AppData/Local/Temp/omp-pack-QR1XcI/dude1wudv-pi-tui-20.1.0.tgz","types":"./dist/types/index.d.ts","engines":{"bun":">=1.3.14"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./src/index.ts"},"./*":{"types":"./dist/types/*.d.ts","import":"./src/*.ts"},"./*.js":"./src/*.ts","./components/*":{"types":"./dist/types/components/*.d.ts","import":"./src/components/*.ts"}},"scripts":{"fix":"biome check --write --unsafe .","fmt":"biome format --write .","lint":"biome lint .","test":"bun test --parallel test/*.test.ts","check":"biome check . && bun run check:types","check:types":"tsgo -p tsconfig.json --noEmit"},"_npmUser":{"name":"dude1wudv","email":"1602858215@qq.com"},"_resolved":"C:\\Users\\sun\\AppData\\Local\\Temp\\omp-pack-QR1XcI\\dude1wudv-pi-tui-20.1.0.tgz","_integrity":"sha512-yCcPPIcssqpALjqMeAqVM7a29yOchUG9BmyQRtTa9icWBpEI2o/W7gQ4T29v1vkUJUF5tEsgNlpVW5hKErOoIA==","repository":{"url":"git+https://github.com/can1357/oh-my-pi.git","type":"git","directory":"packages/tui"},"_npmVersion":"11.12.1","description":"Terminal User Interface library with differential rendering for efficient text-based applications","directories":{},"_nodeVersion":"24.15.0","dependencies":{"@dude1wudv/pi-utils":"20.1.0","@dude1wudv/pi-natives":"20.1.0"},"_hasShrinkwrap":false,"devDependencies":{"ghostty-web":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-tui_20.1.0_1786730793721_0.1196617961849562","host":"s3://npm-registry-packages-npm-production"}},"20.1.2":{"name":"@dude1wudv/pi-tui","version":"20.1.2","keywords":["tui","terminal","ui","text-editor","differential-rendering","typescript","cli"],"author":{"name":"Can Boluk"},"license":"MIT","_id":"@dude1wudv/pi-tui@20.1.2","maintainers":[{"name":"dude1wudv","email":"1602858215@qq.com"}],"contributors":[{"name":"Mario Zechner"}],"homepage":"https://omp.sh","bugs":{"url":"https://github.com/can1357/oh-my-pi/issues"},"dist":{"shasum":"a1b291dbcff96b364da59167342e9739598e884c","tarball":"https://registry.npmjs.org/@dude1wudv/pi-tui/-/pi-tui-20.1.2.tgz","fileCount":77,"integrity":"sha512-bkG44cf62cFFPY81pAg1xP3eezLMhz2FtZfa4d4+/CNGHgH0Nnt00l4fwHpJNoyaVKAEY1LUpjQZxL47IhnUgQ==","signatures":[{"sig":"MEUCIEfwy+XNgI+8KxQ1CXrcvbePy8dPqK+aIUJHtjQR2zNDAiEAlbbYE+2blpzK/IIEURwTuHVEjOHoaZEmeXLCjUb8zBU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1362001},"main":"./src/index.ts","type":"module","_from":"file:C:/Users/sun/AppData/Local/Temp/omp-pack-fRN9Tc/dude1wudv-pi-tui-20.1.2.tgz","types":"./dist/types/index.d.ts","engines":{"bun":">=1.3.14"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./src/index.ts"},"./*":{"types":"./dist/types/*.d.ts","import":"./src/*.ts"},"./*.js":"./src/*.ts","./components/*":{"types":"./dist/types/components/*.d.ts","import":"./src/components/*.ts"}},"scripts":{"fix":"biome check --write --unsafe .","fmt":"biome format --write .","lint":"biome lint .","test":"bun test --parallel test/*.test.ts","check":"biome check . && bun run check:types","check:types":"tsgo -p tsconfig.json --noEmit"},"_npmUser":{"name":"dude1wudv","email":"1602858215@qq.com"},"_resolved":"C:\\Users\\sun\\AppData\\Local\\Temp\\omp-pack-fRN9Tc\\dude1wudv-pi-tui-20.1.2.tgz","_integrity":"sha512-bkG44cf62cFFPY81pAg1xP3eezLMhz2FtZfa4d4+/CNGHgH0Nnt00l4fwHpJNoyaVKAEY1LUpjQZxL47IhnUgQ==","repository":{"url":"git+https://github.com/can1357/oh-my-pi.git","type":"git","directory":"packages/tui"},"_npmVersion":"11.12.1","description":"Terminal User Interface library with differential rendering for efficient text-based applications","directories":{},"_nodeVersion":"24.15.0","dependencies":{"@dude1wudv/pi-utils":"20.1.2","@dude1wudv/pi-natives":"20.1.2"},"_hasShrinkwrap":false,"devDependencies":{"ghostty-web":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-tui_20.1.2_1786776397311_0.29970885679622916","host":"s3://npm-registry-packages-npm-production"}},"20.1.3":{"name":"@dude1wudv/pi-tui","version":"20.1.3","keywords":["tui","terminal","ui","text-editor","differential-rendering","typescript","cli"],"author":{"name":"Can Boluk"},"license":"MIT","_id":"@dude1wudv/pi-tui@20.1.3","maintainers":[{"name":"dude1wudv","email":"1602858215@qq.com"}],"contributors":[{"name":"Mario Zechner"}],"homepage":"https://omp.sh","bugs":{"url":"https://github.com/can1357/oh-my-pi/issues"},"dist":{"shasum":"ca866edff49b12d111f328141e6c992eb4f9d9a8","tarball":"https://registry.npmjs.org/@dude1wudv/pi-tui/-/pi-tui-20.1.3.tgz","fileCount":77,"integrity":"sha512-8IVNPl1wjV208sxVnT/QEwkczIkisU+sgYS9fn7SIV/66ocYmxv78c20Sq3670zXB2ljfdNDvAaM7T9T6F79Kw==","signatures":[{"sig":"MEYCIQCw4MO+IkrrG62NzfXdi1/AYS/axtn6vg1L8/t6u2JgiQIhAJAvLaEBr/7JiiVRoTCM22xTreI6RUjIuLVyp42KLIp6","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1362001},"main":"./src/index.ts","type":"module","_from":"file:C:/Users/RUNNER~1/AppData/Local/Temp/omp-pack-cjMyjy/dude1wudv-pi-tui-20.1.3.tgz","types":"./dist/types/index.d.ts","engines":{"bun":">=1.3.14"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./src/index.ts"},"./*":{"types":"./dist/types/*.d.ts","import":"./src/*.ts"},"./*.js":"./src/*.ts","./components/*":{"types":"./dist/types/components/*.d.ts","import":"./src/components/*.ts"}},"scripts":{"fix":"biome check --write --unsafe .","fmt":"biome format --write .","lint":"biome lint .","test":"bun test --parallel test/*.test.ts","check":"biome check . && bun run check:types","check:types":"tsgo -p tsconfig.json --noEmit"},"_npmUser":{"name":"dude1wudv","email":"1602858215@qq.com"},"_resolved":"C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\omp-pack-cjMyjy\\dude1wudv-pi-tui-20.1.3.tgz","_integrity":"sha512-8IVNPl1wjV208sxVnT/QEwkczIkisU+sgYS9fn7SIV/66ocYmxv78c20Sq3670zXB2ljfdNDvAaM7T9T6F79Kw==","repository":{"url":"git+https://github.com/can1357/oh-my-pi.git","type":"git","directory":"packages/tui"},"_npmVersion":"10.9.8","description":"Terminal User Interface library with differential rendering for efficient text-based applications","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@dude1wudv/pi-utils":"20.1.3","@dude1wudv/pi-natives":"20.1.3"},"_hasShrinkwrap":false,"devDependencies":{"ghostty-web":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-tui_20.1.3_1786786400048_0.7907016613098912","host":"s3://npm-registry-packages-npm-production"}},"20.1.4":{"name":"@dude1wudv/pi-tui","version":"20.1.4","keywords":["tui","terminal","ui","text-editor","differential-rendering","typescript","cli"],"author":{"name":"Can Boluk"},"license":"MIT","_id":"@dude1wudv/pi-tui@20.1.4","maintainers":[{"name":"dude1wudv","email":"1602858215@qq.com"}],"contributors":[{"name":"Mario Zechner"}],"homepage":"https://omp.sh","bugs":{"url":"https://github.com/can1357/oh-my-pi/issues"},"dist":{"shasum":"bf752e8a2528d03047f511329eaa946e4f5ca08d","tarball":"https://registry.npmjs.org/@dude1wudv/pi-tui/-/pi-tui-20.1.4.tgz","fileCount":77,"integrity":"sha512-UtavjGkRdH0ZCIQ6a6y61bfGl11x5TIhWXbz7rrq40p+9NBPAUZ8L5KDYGFibzFwb5NQ+kx7q7WZu1R+rfJC+A==","signatures":[{"sig":"MEQCIBNzCWTPq+MXpWiKsn1XXJfzgrZzDNAERFdcVgbsNIgiAiAvw6hU9pM2F7hPD7DHZo3fLVpInvZYNm+GOdqcEX63IA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1362001},"main":"./src/index.ts","type":"module","_from":"file:C:/Users/RUNNER~1/AppData/Local/Temp/omp-pack-DVO9aN/dude1wudv-pi-tui-20.1.4.tgz","types":"./dist/types/index.d.ts","engines":{"bun":">=1.3.14"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./src/index.ts"},"./*":{"types":"./dist/types/*.d.ts","import":"./src/*.ts"},"./*.js":"./src/*.ts","./components/*":{"types":"./dist/types/components/*.d.ts","import":"./src/components/*.ts"}},"scripts":{"fix":"biome check --write --unsafe .","fmt":"biome format --write .","lint":"biome lint .","test":"bun test --parallel test/*.test.ts","check":"biome check . && bun run check:types","check:types":"tsgo -p tsconfig.json --noEmit"},"_npmUser":{"name":"dude1wudv","email":"1602858215@qq.com"},"_resolved":"C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\omp-pack-DVO9aN\\dude1wudv-pi-tui-20.1.4.tgz","_integrity":"sha512-UtavjGkRdH0ZCIQ6a6y61bfGl11x5TIhWXbz7rrq40p+9NBPAUZ8L5KDYGFibzFwb5NQ+kx7q7WZu1R+rfJC+A==","repository":{"url":"git+https://github.com/can1357/oh-my-pi.git","type":"git","directory":"packages/tui"},"_npmVersion":"10.9.8","description":"Terminal User Interface library with differential rendering for efficient text-based applications","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@dude1wudv/pi-utils":"20.1.4","@dude1wudv/pi-natives":"20.1.4"},"_hasShrinkwrap":false,"devDependencies":{"ghostty-web":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-tui_20.1.4_1786817582218_0.8545321513069193","host":"s3://npm-registry-packages-npm-production"}},"20.1.5":{"name":"@dude1wudv/pi-tui","version":"20.1.5","keywords":["tui","terminal","ui","text-editor","differential-rendering","typescript","cli"],"author":{"name":"Can Boluk"},"license":"MIT","_id":"@dude1wudv/pi-tui@20.1.5","maintainers":[{"name":"dude1wudv","email":"1602858215@qq.com"}],"contributors":[{"name":"Mario Zechner"}],"homepage":"https://omp.sh","bugs":{"url":"https://github.com/can1357/oh-my-pi/issues"},"dist":{"shasum":"411e66b040fb98569693213564ac056a7ec43d23","tarball":"https://registry.npmjs.org/@dude1wudv/pi-tui/-/pi-tui-20.1.5.tgz","fileCount":77,"integrity":"sha512-EnzD5atMSxMVKxCfrZEXpLCAm+qNQ9+KVsYk2AP7No+DRQMjN/0/FMqKA0vcf4rCYlr709suKG7bA2Zl1U5uVA==","signatures":[{"sig":"MEUCIFjga/4KE6JW4DZfCgtN4IG0l7Ymbb0RAB8R9Jiux06JAiEA+90WpPO2dbIu8MlzHAr8W0rPJwRVZDYYGxumCKc4/5A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1364441},"main":"./src/index.ts","type":"module","_from":"file:C:/Users/RUNNER~1/AppData/Local/Temp/omp-pack-ARMlnZ/dude1wudv-pi-tui-20.1.5.tgz","types":"./dist/types/index.d.ts","engines":{"bun":">=1.3.14"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./src/index.ts"},"./*":{"types":"./dist/types/*.d.ts","import":"./src/*.ts"},"./*.js":"./src/*.ts","./components/*":{"types":"./dist/types/components/*.d.ts","import":"./src/components/*.ts"}},"scripts":{"fix":"biome check --write --unsafe .","fmt":"biome format --write .","lint":"biome lint .","test":"bun test --parallel test/*.test.ts","check":"biome check . && bun run check:types","check:types":"tsgo -p tsconfig.json --noEmit"},"_npmUser":{"name":"dude1wudv","email":"1602858215@qq.com"},"_resolved":"C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\omp-pack-ARMlnZ\\dude1wudv-pi-tui-20.1.5.tgz","_integrity":"sha512-EnzD5atMSxMVKxCfrZEXpLCAm+qNQ9+KVsYk2AP7No+DRQMjN/0/FMqKA0vcf4rCYlr709suKG7bA2Zl1U5uVA==","repository":{"url":"git+https://github.com/can1357/oh-my-pi.git","type":"git","directory":"packages/tui"},"_npmVersion":"10.9.8","description":"Terminal User Interface library with differential rendering for efficient text-based applications","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@dude1wudv/pi-utils":"20.1.5","@dude1wudv/pi-natives":"20.1.5"},"_hasShrinkwrap":false,"devDependencies":{"ghostty-web":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-tui_20.1.5_1786864012477_0.570060249606795","host":"s3://npm-registry-packages-npm-production"}},"20.1.6":{"name":"@dude1wudv/pi-tui","version":"20.1.6","keywords":["tui","terminal","ui","text-editor","differential-rendering","typescript","cli"],"author":{"name":"Can Boluk"},"license":"MIT","_id":"@dude1wudv/pi-tui@20.1.6","maintainers":[{"name":"dude1wudv","email":"1602858215@qq.com"}],"contributors":[{"name":"Mario Zechner"}],"homepage":"https://omp.sh","bugs":{"url":"https://github.com/can1357/oh-my-pi/issues"},"dist":{"shasum":"5d75c48a4478994e73f9f966955ecd1363251083","tarball":"https://registry.npmjs.org/@dude1wudv/pi-tui/-/pi-tui-20.1.6.tgz","fileCount":77,"integrity":"sha512-DysPFOWM9ysXN5xwmUlD0Mhp8A0a8DH2PFL6fUMlrUL0rg6F0KUiK/27wZ663xFdAvPJY1fm75EF92ANp48NVw==","signatures":[{"sig":"MEUCIQDfhfiE60dhq8VGrJjvVq2aYij/CXA3cbf8PJFGoHyZ7QIgEv3lxN+rMLwN9hFfBYOI3Q4RBi1ioFENcXV5CLPtBZ8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1365210},"main":"./src/index.ts","type":"module","_from":"file:C:/Users/RUNNER~1/AppData/Local/Temp/omp-pack-K0QlCl/dude1wudv-pi-tui-20.1.6.tgz","types":"./dist/types/index.d.ts","engines":{"bun":">=1.3.14"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./src/index.ts"},"./*":{"types":"./dist/types/*.d.ts","import":"./src/*.ts"},"./*.js":"./src/*.ts","./components/*":{"types":"./dist/types/components/*.d.ts","import":"./src/components/*.ts"}},"scripts":{"fix":"biome check --write --unsafe .","fmt":"biome format --write .","lint":"biome lint .","test":"bun test --parallel test/*.test.ts","check":"biome check . && bun run check:types","check:types":"tsgo -p tsconfig.json --noEmit"},"_npmUser":{"name":"dude1wudv","email":"1602858215@qq.com"},"_resolved":"C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\omp-pack-K0QlCl\\dude1wudv-pi-tui-20.1.6.tgz","_integrity":"sha512-DysPFOWM9ysXN5xwmUlD0Mhp8A0a8DH2PFL6fUMlrUL0rg6F0KUiK/27wZ663xFdAvPJY1fm75EF92ANp48NVw==","repository":{"url":"git+https://github.com/can1357/oh-my-pi.git","type":"git","directory":"packages/tui"},"_npmVersion":"10.9.8","description":"Terminal User Interface library with differential rendering for efficient text-based applications","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@dude1wudv/pi-utils":"20.1.6","@dude1wudv/pi-natives":"20.1.6"},"_hasShrinkwrap":false,"devDependencies":{"ghostty-web":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-tui_20.1.6_1786872877429_0.10056670552199876","host":"s3://npm-registry-packages-npm-production"}},"20.1.7":{"name":"@dude1wudv/pi-tui","version":"20.1.7","keywords":["tui","terminal","ui","text-editor","differential-rendering","typescript","cli"],"author":{"name":"Can Boluk"},"license":"MIT","_id":"@dude1wudv/pi-tui@20.1.7","maintainers":[{"name":"dude1wudv","email":"1602858215@qq.com"}],"contributors":[{"name":"Mario Zechner"}],"homepage":"https://omp.sh","bugs":{"url":"https://github.com/can1357/oh-my-pi/issues"},"dist":{"shasum":"be73413b9ad241eaffa045cf7ce619ac701a5b5e","tarball":"https://registry.npmjs.org/@dude1wudv/pi-tui/-/pi-tui-20.1.7.tgz","fileCount":77,"integrity":"sha512-gIunne1ah66oRkPtZQi+tS2GOBrlJ7ms1mo1HXQ8z7QDjtGixde4Gy8jLAEldvFxc8uJz/q5ofm/mnsoEXlhQg==","signatures":[{"sig":"MEYCIQDnTNRCR3YCDeFA4jwFw1uNo/EJNQiY2FkuKNRdW6Y2QQIhAI6PEOsdNawQFQFtCWm25lEvp/T8F83q2ztWkYxHvkem","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1368712},"main":"./src/index.ts","type":"module","_from":"file:C:/Users/RUNNER~1/AppData/Local/Temp/omp-pack-AsIhaB/dude1wudv-pi-tui-20.1.7.tgz","types":"./dist/types/index.d.ts","engines":{"bun":">=1.3.14"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./src/index.ts"},"./*":{"types":"./dist/types/*.d.ts","import":"./src/*.ts"},"./*.js":"./src/*.ts","./components/*":{"types":"./dist/types/components/*.d.ts","import":"./src/components/*.ts"}},"scripts":{"fix":"biome check --write --unsafe .","fmt":"biome format --write .","lint":"biome lint .","test":"bun test --parallel test/*.test.ts","check":"biome check . && bun run check:types","check:types":"tsgo -p tsconfig.json --noEmit"},"_npmUser":{"name":"dude1wudv","email":"1602858215@qq.com"},"_resolved":"C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\omp-pack-AsIhaB\\dude1wudv-pi-tui-20.1.7.tgz","_integrity":"sha512-gIunne1ah66oRkPtZQi+tS2GOBrlJ7ms1mo1HXQ8z7QDjtGixde4Gy8jLAEldvFxc8uJz/q5ofm/mnsoEXlhQg==","repository":{"url":"git+https://github.com/can1357/oh-my-pi.git","type":"git","directory":"packages/tui"},"_npmVersion":"10.9.8","description":"Terminal User Interface library with differential rendering for efficient text-based applications","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@dude1wudv/pi-utils":"20.1.7","@dude1wudv/pi-natives":"20.1.7"},"_hasShrinkwrap":false,"devDependencies":{"ghostty-web":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-tui_20.1.7_1787078910797_0.499314310677895","host":"s3://npm-registry-packages-npm-production"}},"20.1.8":{"name":"@dude1wudv/pi-tui","version":"20.1.8","keywords":["tui","terminal","ui","text-editor","differential-rendering","typescript","cli"],"author":{"name":"Stencil Labs, Inc."},"license":"MIT","_id":"@dude1wudv/pi-tui@20.1.8","maintainers":[{"name":"dude1wudv","email":"1602858215@qq.com"}],"contributors":[{"name":"Mario Zechner"}],"homepage":"https://omp.sh","bugs":{"url":"https://github.com/can1357/oh-my-pi/issues"},"dist":{"shasum":"afca6cead691f825f03bf91e367ca6979b11c1d2","tarball":"https://registry.npmjs.org/@dude1wudv/pi-tui/-/pi-tui-20.1.8.tgz","fileCount":99,"integrity":"sha512-UUe3DX++UzV41HRUMWfSnWbX3tg3T6UMJWZbuU1u5CbNGbLq1mECWelBXrBNiLOTkzmuj68qY91lwFFWG44dQw==","signatures":[{"sig":"MEUCIDQiOQZE5AkkZaSceeqpd6/1eRuke0+ZuZkHoqD8Em1dAiEA+EHkre8+niVYL5l8iumVT4vki0lDo3m7rXOa7P2iUBc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dude1wudv%2fpi-tui@20.1.8","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":2480717},"main":"./src/index.ts","type":"module","_from":"file:C:/Users/RUNNER~1/AppData/Local/Temp/omp-pack-d2mizD/dude1wudv-pi-tui-20.1.8.tgz","types":"./dist/types/index.d.ts","engines":{"bun":">=1.3.14"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./src/index.ts"},"./*":{"types":"./dist/types/*.d.ts","import":"./src/*.ts"},"./*.js":"./src/*.ts","./components/*":{"types":"./dist/types/components/*.d.ts","import":"./src/components/*.ts"}},"scripts":{"fix":"biome check --write --unsafe .","fmt":"biome format --write .","lint":"biome lint .","test":"bun test --parallel test/*.test.ts","check":"biome check . && bun run check:types","check:types":"tsgo -p tsconfig.json --noEmit"},"_npmUser":{"name":"dude1wudv","email":"1602858215@qq.com"},"_resolved":"C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\omp-pack-d2mizD\\dude1wudv-pi-tui-20.1.8.tgz","_integrity":"sha512-UUe3DX++UzV41HRUMWfSnWbX3tg3T6UMJWZbuU1u5CbNGbLq1mECWelBXrBNiLOTkzmuj68qY91lwFFWG44dQw==","repository":{"url":"git+https://github.com/dude1wudv/oh-my-pi.git","type":"git"},"_npmVersion":"10.9.8","description":"Terminal User Interface library with differential rendering for efficient text-based applications","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@dude1wudv/pi-utils":"20.1.8","@dude1wudv/pi-natives":"20.1.8"},"_hasShrinkwrap":false,"devDependencies":{"ghostty-web":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-tui_20.1.8_1787329654182_0.4774327913920666","host":"s3://npm-registry-packages-npm-production"}},"20.1.9":{"type":"module","name":"@dude1wudv/pi-tui","version":"20.1.9","description":"Terminal User Interface library with differential rendering for efficient text-based applications","homepage":"https://omp.sh","author":{"name":"Stencil Labs, Inc."},"contributors":[{"name":"Mario Zechner"}],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/dude1wudv/oh-my-pi.git"},"bugs":{"url":"https://github.com/can1357/oh-my-pi/issues"},"keywords":["tui","terminal","ui","text-editor","differential-rendering","typescript","cli"],"main":"./src/index.ts","types":"./dist/types/index.d.ts","scripts":{"check":"biome check . && bun run check:types","check:types":"tsgo -p tsconfig.json --noEmit","lint":"biome lint .","test":"bun test --parallel test/*.test.ts","fix":"biome check --write --unsafe .","fmt":"biome format --write ."},"dependencies":{"@dude1wudv/pi-natives":"20.1.9","@dude1wudv/pi-utils":"20.1.9"},"devDependencies":{"ghostty-web":"^0.4.0"},"engines":{"bun":">=1.3.14"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./src/index.ts"},"./*":{"types":"./dist/types/*.d.ts","import":"./src/*.ts"},"./components/*":{"types":"./dist/types/components/*.d.ts","import":"./src/components/*.ts"},"./*.js":"./src/*.ts"},"_id":"@dude1wudv/pi-tui@20.1.9","_integrity":"sha512-uA7hStVfq8pP2JSRTitgmgtDTo5xUhLqgHYK6dC0HaLTUdQ5gfUaNEPfzHVSj9UkDoxlKxQScXHlrRirhCUizA==","_resolved":"C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\omp-pack-PU7UBv\\dude1wudv-pi-tui-20.1.9.tgz","_from":"file:C:/Users/RUNNER~1/AppData/Local/Temp/omp-pack-PU7UBv/dude1wudv-pi-tui-20.1.9.tgz","_nodeVersion":"22.23.2","_npmVersion":"10.9.8","dist":{"integrity":"sha512-uA7hStVfq8pP2JSRTitgmgtDTo5xUhLqgHYK6dC0HaLTUdQ5gfUaNEPfzHVSj9UkDoxlKxQScXHlrRirhCUizA==","shasum":"f9b6687e537c9ce106f8e15654eb6340aa04efcc","tarball":"https://registry.npmjs.org/@dude1wudv/pi-tui/-/pi-tui-20.1.9.tgz","fileCount":99,"unpackedSize":2480717,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dude1wudv%2fpi-tui@20.1.9","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBI+700xVYabmpf3mHEisrKLbeJKC67+dP2cQgVzculIAiEAoP4un+V3f3aGyA7OCSmmMVtFXZ+xQ7ZMTkTDyGSQDUk="}]},"_npmUser":{"name":"dude1wudv","email":"1602858215@qq.com"},"directories":{},"maintainers":[{"name":"dude1wudv","email":"1602858215@qq.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-tui_20.1.9_1787369176378_0.29766864419709327"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-14T18:06:33.556Z","modified":"2026-08-22T03:26:16.934Z","20.1.0":"2026-08-14T18:06:33.955Z","20.1.2":"2026-08-15T06:46:37.465Z","20.1.3":"2026-08-15T09:33:20.217Z","20.1.4":"2026-08-15T18:13:02.380Z","20.1.5":"2026-08-16T07:06:52.654Z","20.1.6":"2026-08-16T09:34:37.579Z","20.1.7":"2026-08-18T18:48:30.948Z","20.1.8":"2026-08-21T16:27:34.330Z","20.1.9":"2026-08-22T03:26:16.527Z"},"bugs":{"url":"https://github.com/can1357/oh-my-pi/issues"},"author":{"name":"Stencil Labs, Inc."},"license":"MIT","homepage":"https://omp.sh","keywords":["tui","terminal","ui","text-editor","differential-rendering","typescript","cli"],"repository":{"type":"git","url":"git+https://github.com/dude1wudv/oh-my-pi.git"},"description":"Terminal User Interface library with differential rendering for efficient text-based applications","contributors":[{"name":"Mario Zechner"}],"maintainers":[{"name":"dude1wudv","email":"1602858215@qq.com"}],"readme":"# @dude1wudv/pi-tui\n\nMinimal terminal UI framework with differential rendering and synchronized output for flicker-free interactive CLI applications.\n\n## Features\n\n- **Differential Rendering**: Three-strategy rendering system that only updates what changed\n- **Synchronized Output**: Uses CSI 2026 for atomic screen updates (no flicker)\n- **Bracketed Paste Mode**: Handles large pastes correctly with markers for >10 line pastes\n- **Component-based**: Simple Component interface with render() method\n- **Theme Support**: Components accept theme interfaces for customizable styling\n- **Built-in Components**: Text, TruncatedText, Input, Editor, Markdown, Loader, SelectList, SettingsList, Spacer, Image, Box, Container\n- **Inline Images**: Renders images in terminals that support Kitty or iTerm2 graphics protocols\n- **Autocomplete Support**: File paths and slash commands\n\n## Quick Start\n\n```typescript\nimport { TUI, Text, Editor, ProcessTerminal } from \"@dude1wudv/pi-tui\";\n\n// Create terminal\nconst terminal = new ProcessTerminal();\n\n// Create TUI\nconst tui = new TUI(terminal);\n\n// Add components\ntui.addChild(new Text(\"Welcome to my app!\"));\n\nconst editor = new Editor(editorTheme);\neditor.onSubmit = (text) => {\n\tconsole.log(\"Submitted:\", text);\n\ttui.addChild(new Text(`You said: ${text}`));\n};\ntui.addChild(editor);\n\n// Start\ntui.start();\n```\n\n## Core API\n\n### TUI\n\nMain container that manages components and rendering.\n\n```typescript\nconst tui = new TUI(terminal);\ntui.addChild(component);\ntui.removeChild(component);\ntui.start();\ntui.stop();\ntui.requestRender(); // Request a re-render\ntui.requestComponentRender(component); // Re-render only the root subtree containing `component` when safe (falls back to a full render on resize, overlays, images, or concurrent full requests)\n\n// Global debug key handler (Shift+Ctrl+D)\ntui.onDebug = () => console.log(\"Debug triggered\");\n```\n\n### Component Interface\n\nAll components implement:\n\n```typescript\ninterface Component {\n\trender(width: number): readonly string[];\n\thandleInput?(data: string): void;\n\tinvalidate?(): void;\n}\n```\n\n| Method               | Description                                                                                                                                                        |\n| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `render(width)`      | Returns an array of strings, one per line. Each line **must not exceed `width`** or the TUI will error. Use `truncateToWidth()` or manual wrapping to ensure this. The result is component-owned and immutable to callers; return the same array reference when unchanged (enables renderer memoization) and a new array when content changed. |\n| `handleInput?(data)` | Called when the component has focus and receives keyboard input. The `data` string contains raw terminal input (may include ANSI escape sequences).                |\n| `invalidate?()`      | Called to clear any cached render state. Components should re-render from scratch on the next `render()` call.                                                     |\n\n## Built-in Components\n\n### Container\n\nGroups child components.\n\n```typescript\nconst container = new Container();\ncontainer.addChild(component);\ncontainer.removeChild(component);\n```\n\n### Box\n\nContainer that applies padding and background color to all children.\n\n```typescript\nconst box = new Box(\n\t1, // paddingX (default: 1)\n\t1, // paddingY (default: 1)\n\t(text) => chalk.bgGray(text), // optional background function\n);\nbox.addChild(new Text(\"Content\"));\nbox.setBgFn((text) => chalk.bgBlue(text)); // Change background dynamically\n```\n\n### Text\n\nDisplays multi-line text with word wrapping and padding.\n\n```typescript\nconst text = new Text(\n\t\"Hello World\", // text content\n\t1, // paddingX (default: 1)\n\t1, // paddingY (default: 1)\n\t(text) => chalk.bgGray(text), // optional background function\n);\ntext.setText(\"Updated text\");\ntext.setCustomBgFn((text) => chalk.bgBlue(text));\n```\n\n### TruncatedText\n\nSingle-line text that truncates to fit viewport width. Useful for status lines and headers.\n\n```typescript\nconst truncated = new TruncatedText(\n\t\"This is a very long line that will be truncated...\",\n\t0, // paddingX (default: 0)\n\t0, // paddingY (default: 0)\n);\n```\n\n### Input\n\nSingle-line text input with horizontal scrolling.\n\n```typescript\nconst input = new Input();\ninput.onSubmit = (value) => console.log(value);\ninput.setValue(\"initial\");\ninput.getValue();\n```\n\n**Key Bindings:**\n\n- `Enter` - Submit\n- `Ctrl+A` / `Ctrl+E` - Line start/end\n- `Ctrl+W` or `Alt+Backspace` - Delete word backwards\n- `Ctrl+U` - Delete to start of line\n- `Ctrl+K` - Delete to end of line\n- `Ctrl+Left` / `Ctrl+Right` - Word navigation\n- `Alt+Left` / `Alt+Right` - Word navigation\n- Arrow keys, Backspace, Delete work as expected\n\n### Editor\n\nMulti-line text editor with autocomplete, file completion, and paste handling.\n\n```typescript\ninterface SymbolTheme {\n\tcursor: string;\n\tellipsis: string;\n\tboxRound: {\n\t\ttopLeft: string;\n\t\ttopRight: string;\n\t\tbottomLeft: string;\n\t\tbottomRight: string;\n\t\thorizontal: string;\n\t\tvertical: string;\n\t};\n\tboxSharp: {\n\t\ttopLeft: string;\n\t\ttopRight: string;\n\t\tbottomLeft: string;\n\t\tbottomRight: string;\n\t\thorizontal: string;\n\t\tvertical: string;\n\t\tteeDown: string;\n\t\tteeUp: string;\n\t\tteeLeft: string;\n\t\tteeRight: string;\n\t\tcross: string;\n\t};\n\ttable: {\n\t\ttopLeft: string;\n\t\ttopRight: string;\n\t\tbottomLeft: string;\n\t\tbottomRight: string;\n\t\thorizontal: string;\n\t\tvertical: string;\n\t\tteeDown: string;\n\t\tteeUp: string;\n\t\tteeLeft: string;\n\t\tteeRight: string;\n\t\tcross: string;\n\t};\n\tquoteBorder: string;\n\thrChar: string;\n\tspinnerFrames: string[];\n}\n\ninterface EditorTheme {\n\tborderColor: (str: string) => string;\n\tselectList: SelectListTheme;\n\tsymbols: SymbolTheme;\n}\n\nconst editor = new Editor(theme);\neditor.onSubmit = (text) => console.log(text);\neditor.onChange = (text) => console.log(\"Changed:\", text);\neditor.disableSubmit = true; // Disable submit temporarily\neditor.setAutocompleteProvider(provider);\neditor.borderColor = (s) => chalk.blue(s); // Change border dynamically\n```\n\n**Features:**\n\n- Multi-line editing with word wrap\n- Slash command autocomplete (type `/`)\n- File path autocomplete (press `Tab`)\n- Large paste handling (>10 lines creates `[paste #1 +50 lines]` marker)\n- Horizontal lines above/below editor\n- Fake cursor rendering (hidden real cursor)\n\n**Key Bindings:**\n\n- `Enter` - Submit\n- `Shift+Enter`, `Ctrl+Enter`, or `Alt+Enter` - New line (terminal-dependent, Alt+Enter most reliable)\n- `Tab` - Autocomplete\n- `Ctrl+K` - Delete line\n- `Alt+D` / `Alt+Delete` - Delete word forward\n- `Ctrl+A` / `Ctrl+E` - Line start/end\n- `Ctrl+-` - Undo last edit\n- Arrow keys, Backspace, Delete work as expected\n\n### Markdown\n\nRenders markdown with syntax highlighting and theming support.\n\n```typescript\ninterface MarkdownTheme {\n\theading: (text: string) => string;\n\tlink: (text: string) => string;\n\tlinkUrl: (text: string) => string;\n\tcode: (text: string) => string;\n\tcodeBlock: (text: string) => string;\n\tcodeBlockBorder: (text: string) => string;\n\tquote: (text: string) => string;\n\tquoteBorder: (text: string) => string;\n\thr: (text: string) => string;\n\tlistBullet: (text: string) => string;\n\tbold: (text: string) => string;\n\titalic: (text: string) => string;\n\tstrikethrough: (text: string) => string;\n\tunderline: (text: string) => string;\n\thighlightCode?: (code: string, lang?: string) => string[];\n\tsymbols: SymbolTheme;\n}\n\ninterface DefaultTextStyle {\n\tcolor?: (text: string) => string;\n\tbgColor?: (text: string) => string;\n\tbold?: boolean;\n\titalic?: boolean;\n\tstrikethrough?: boolean;\n\tunderline?: boolean;\n}\n\nconst md = new Markdown(\n\t\"# Hello\\n\\nSome **bold** text\",\n\t1, // paddingX\n\t1, // paddingY\n\ttheme, // MarkdownTheme\n\tdefaultStyle, // optional DefaultTextStyle\n\t2, // optional code block indent (spaces)\n);\nmd.setText(\"Updated markdown\");\n```\n\n**Features:**\n\n- Headings, bold, italic, code blocks, lists, links, blockquotes\n- HTML tags rendered as plain text\n- Optional syntax highlighting via `highlightCode`\n- Padding support\n- Render caching for performance\n\n### Loader\n\nAnimated loading spinner.\n\n```typescript\nconst loader = new Loader(\n\ttui, // TUI instance for render updates\n\t(s) => chalk.cyan(s), // spinner color function\n\t(s) => chalk.gray(s), // message color function\n\t\"Loading...\", // message (default: \"Loading...\")\n);\nloader.start();\nloader.setMessage(\"Still loading...\");\nloader.stop();\n```\n\n### CancellableLoader\n\nExtends Loader with Escape key handling and an AbortSignal for cancelling async operations.\n\n```typescript\nconst loader = new CancellableLoader(\n\ttui, // TUI instance for render updates\n\t(s) => chalk.cyan(s), // spinner color function\n\t(s) => chalk.gray(s), // message color function\n\t\"Working...\", // message\n);\nloader.onAbort = () => done(null); // Called when user presses Escape\ndoAsyncWork(loader.signal).then(done);\n```\n\n**Properties:**\n\n- `signal: AbortSignal` - Aborted when user presses Escape\n- `aborted: boolean` - Whether the loader was aborted\n- `onAbort?: () => void` - Callback when user presses Escape\n\n### SelectList\n\nInteractive selection list with keyboard navigation.\n\n```typescript\ninterface SelectItem {\n\tvalue: string;\n\tlabel: string;\n\tdescription?: string;\n}\n\ninterface SelectListTheme {\n\tselectedPrefix: (text: string) => string;\n\tselectedText: (text: string) => string;\n\tdescription: (text: string) => string;\n\tscrollInfo: (text: string) => string;\n\tnoMatch: (text: string) => string;\n\tsymbols: SymbolTheme;\n}\n\nconst list = new SelectList(\n\t[\n\t\t{ value: \"opt1\", label: \"Option 1\", description: \"First option\" },\n\t\t{ value: \"opt2\", label: \"Option 2\", description: \"Second option\" },\n\t],\n\t5, // maxVisible\n\ttheme, // SelectListTheme\n);\n\nlist.onSelect = (item) => console.log(\"Selected:\", item);\nlist.onCancel = () => console.log(\"Cancelled\");\nlist.onSelectionChange = (item) => console.log(\"Highlighted:\", item);\nlist.setFilter(\"opt\"); // Filter items\n```\n\n**Controls:**\n\n- Arrow keys: Navigate\n- Enter: Select\n- Escape: Cancel\n\n### SettingsList\n\nSettings panel with value cycling and submenus.\n\n```typescript\ninterface SettingItem {\n\tid: string;\n\tlabel: string;\n\tdescription?: string;\n\tcurrentValue: string;\n\tvalues?: string[]; // If provided, Enter/Space cycles through these\n\tsubmenu?: (currentValue: string, done: (selectedValue?: string) => void) => Component;\n}\n\ninterface SettingsListTheme {\n\tlabel: (text: string, selected: boolean) => string;\n\tvalue: (text: string, selected: boolean) => string;\n\tdescription: (text: string) => string;\n\tcursor: string;\n\thint: (text: string) => string;\n}\n\nconst settings = new SettingsList(\n\t[\n\t\t{ id: \"theme\", label: \"Theme\", currentValue: \"dark\", values: [\"dark\", \"light\"] },\n\t\t{ id: \"model\", label: \"Model\", currentValue: \"gpt-4\", submenu: (val, done) => modelSelector },\n\t],\n\t10, // maxVisible\n\ttheme, // SettingsListTheme\n\t(id, newValue) => console.log(`${id} changed to ${newValue}`),\n\t() => console.log(\"Cancelled\"),\n);\nsettings.updateValue(\"theme\", \"light\");\n```\n\n**Controls:**\n\n- Arrow keys: Navigate\n- Enter/Space: Activate (cycle value or open submenu)\n- Escape: Cancel\n\n### Spacer\n\nEmpty lines for vertical spacing.\n\n```typescript\nconst spacer = new Spacer(2); // 2 empty lines (default: 1)\n```\n\n### Image\n\nRenders images inline for terminals that support the Kitty graphics protocol (Kitty, Ghostty, WezTerm, and Warp on macOS/Linux) or iTerm2 inline images. Falls back to a text placeholder on unsupported terminals.\n\n```typescript\ninterface ImageTheme {\n\tfallbackColor: (str: string) => string;\n}\n\ninterface ImageOptions {\n\tmaxWidthCells?: number;\n\tmaxHeightCells?: number;\n\tfilename?: string;\n}\n\nconst image = new Image(\n\tbase64Data, // base64-encoded image data\n\t\"image/png\", // MIME type\n\ttheme, // ImageTheme\n\toptions, // optional ImageOptions\n);\ntui.addChild(image);\n```\n\nSupported formats: PNG, JPEG, GIF, WebP. Dimensions are parsed from the image headers automatically.\n\n## Autocomplete\n\n### CombinedAutocompleteProvider\n\nSupports both slash commands and file paths.\n\n```typescript\nimport { CombinedAutocompleteProvider } from \"@dude1wudv/pi-tui\";\nimport { getProjectDir } from \"@dude1wudv/pi-utils\";\n\nconst provider = new CombinedAutocompleteProvider(\n\t[\n\t\t{ name: \"help\", description: \"Show help\" },\n\t\t{ name: \"clear\", description: \"Clear screen\" },\n\t\t{ name: \"delete\", description: \"Delete last message\" },\n\t],\n\tgetProjectDir(), // base path for file completion\n);\n\neditor.setAutocompleteProvider(provider);\n```\n\n**Features:**\n\n- Type `/` to see slash commands\n- Press `Tab` for file path completion\n- Works with `~/`, `./`, `../`, and `@` prefix\n- Filters to attachable files for `@` prefix\n\n## Key Detection\n\nHelper functions for detecting keyboard input (supports Kitty keyboard protocol):\n\n```typescript\nimport {\n\tisEnter,\n\tisEscape,\n\tisTab,\n\tisShiftTab,\n\tisArrowUp,\n\tisArrowDown,\n\tisArrowLeft,\n\tisArrowRight,\n\tisCtrlA,\n\tisCtrlC,\n\tisCtrlE,\n\tisCtrlK,\n\tisCtrlO,\n\tisCtrlP,\n\tisCtrlLeft,\n\tisCtrlRight,\n\tisAltLeft,\n\tisAltRight,\n\tisShiftEnter,\n\tisAltEnter,\n\tisShiftCtrlO,\n\tisShiftCtrlD,\n\tisShiftCtrlP,\n\tisBackspace,\n\tisDelete,\n\tisHome,\n\tisEnd,\n\t// ... and more\n} from \"@dude1wudv/pi-tui\";\n\nif (isCtrlC(data)) {\n\tprocess.exit(0);\n}\n```\n\n## Differential Rendering\n\nThe TUI uses three rendering strategies:\n\n1. **First Render**: Output all lines without clearing scrollback\n2. **Width Changed or Change Above Viewport**: Clear screen and full re-render\n3. **Normal Update**: Move cursor to first changed line, clear to end, render changed lines\n\nAll updates are wrapped in **synchronized output** (`\\x1b[?2026h` ... `\\x1b[?2026l`) for atomic, flicker-free rendering unless `PI_NO_SYNC_OUTPUT=1` is set. The opt-out removes only the DEC 2026 wrapper; paint writes still guard terminal autowrap to avoid pending-wrap cursor artifacts.\n\n## Terminal Interface\n\nThe TUI works with any object implementing the `Terminal` interface:\n\n```typescript\ninterface Terminal {\n\tstart(onInput: (data: string) => void, onResize: () => void, onDisconnect?: () => void): void;\n\tstop(): void;\n\twrite(data: string): void;\n\tget columns(): number;\n\tget rows(): number;\n\tmoveBy(lines: number): void;\n\thideCursor(force?: boolean): void;\n\tshowCursor(force?: boolean): void;\n\tclearLine(): void;\n\tclearFromCursor(): void;\n\tclearScreen(): void;\n}\n```\n\n**Built-in implementations:**\n\n- `ProcessTerminal` - Uses `process.stdin/stdout`\n- `VirtualTerminal` - For testing (uses ghostty-web)\n\n## Utilities\n\n```typescript\nimport { Ellipsis, visibleWidth, truncateToWidth, wrapTextWithAnsi } from \"@dude1wudv/pi-tui\";\n\n// Get visible width of string (ignoring ANSI codes, uses Bun.stringWidth)\nconst width = visibleWidth(\"\\x1b[31mHello\\x1b[0m\"); // 5\n\n// Truncate string to width (preserving ANSI codes, adds ellipsis)\nconst truncated = truncateToWidth(\"Hello World\", 8); // \"Hello…\" (default: Ellipsis.Unicode)\n\n// Truncate without ellipsis\nconst truncatedNoEllipsis = truncateToWidth(\"Hello World\", 8, Ellipsis.Omit); // \"Hello Wo\"\n\n// Wrap text to width (Bun.wrapAnsi word wrap, trims line ends, preserves ANSI)\nconst lines = wrapTextWithAnsi(\"This is a long line that needs wrapping\", 20);\n// [\"This is a long line\", \"that needs wrapping\"]\n```\n\n## Creating Custom Components\n\nWhen creating custom components, **each line returned by `render()` must not exceed the `width` parameter**. The TUI will error if any line is wider than the terminal.\n\n### Handling Input\n\nUse the key detection utilities to handle keyboard input:\n\n```typescript\nimport { isEnter, isEscape, isArrowUp, isArrowDown, isCtrlC, isTab, isBackspace } from \"@dude1wudv/pi-tui\";\nimport type { Component } from \"@dude1wudv/pi-tui\";\n\nclass MyInteractiveComponent implements Component {\n\tprivate selectedIndex = 0;\n\tprivate items = [\"Option 1\", \"Option 2\", \"Option 3\"];\n\n\tonSelect?: (index: number) => void;\n\tonCancel?: () => void;\n\n\thandleInput(data: string): void {\n\t\tif (isArrowUp(data)) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t} else if (isArrowDown(data)) {\n\t\t\tthis.selectedIndex = Math.min(this.items.length - 1, this.selectedIndex + 1);\n\t\t} else if (isEnter(data)) {\n\t\t\tthis.onSelect?.(this.selectedIndex);\n\t\t} else if (isEscape(data) || isCtrlC(data)) {\n\t\t\tthis.onCancel?.();\n\t\t}\n\t}\n\n\trender(width: number): readonly string[] {\n\t\treturn this.items.map((item, i) => {\n\t\t\tconst prefix = i === this.selectedIndex ? \"> \" : \"  \";\n\t\t\treturn truncateToWidth(prefix + item, width);\n\t\t});\n\t}\n}\n```\n\n### Handling Line Width\n\nUse the provided utilities to ensure lines fit:\n\n```typescript\nimport { visibleWidth, truncateToWidth } from \"@dude1wudv/pi-tui\";\nimport type { Component } from \"@dude1wudv/pi-tui\";\n\nclass MyComponent implements Component {\n\tprivate text: string;\n\n\tconstructor(text: string) {\n\t\tthis.text = text;\n\t}\n\n\trender(width: number): readonly string[] {\n\t\t// Option 1: Truncate long lines\n\t\treturn [truncateToWidth(this.text, width)];\n\n\t\t// Option 2: Check and pad to exact width\n\t\tconst line = this.text;\n\t\tconst visible = visibleWidth(line);\n\t\tif (visible > width) {\n\t\t\treturn [truncateToWidth(line, width)];\n\t\t}\n\t\t// Pad to exact width (optional, for backgrounds)\n\t\treturn [line + \" \".repeat(width - visible)];\n\t}\n}\n```\n\n### ANSI Code Considerations\n\n`visibleWidth()`, `truncateToWidth()`, and `wrapTextWithAnsi()` correctly handle ANSI escape codes:\n\n- `visibleWidth()` ignores ANSI codes when calculating width (via `Bun.stringWidth`)\n- `truncateToWidth()` preserves ANSI codes and properly closes them when truncating\n- `wrapTextWithAnsi()` preserves ANSI codes while word-wrapping and trimming line ends\n\n```typescript\nimport chalk from \"@dude1wudv/pi-utils/chalk\";\n\nconst styled = chalk.red(\"Hello\") + \" \" + chalk.blue(\"World\");\nconst width = visibleWidth(styled); // 11 (not counting ANSI codes)\nconst truncated = truncateToWidth(styled, 8); // Red \"Hello\" + \" W...\" with proper reset\n```\n\n### Caching\n\nFor performance, components should cache their rendered output and only re-render when necessary:\n\n```typescript\nclass CachedComponent implements Component {\n\tprivate text: string;\n\tprivate cachedWidth?: number;\n\tprivate cachedLines?: string[];\n\n\trender(width: number): readonly string[] {\n\t\tif (this.cachedLines && this.cachedWidth === width) {\n\t\t\treturn this.cachedLines;\n\t\t}\n\n\t\tconst lines = [truncateToWidth(this.text, width)];\n\n\t\tthis.cachedWidth = width;\n\t\tthis.cachedLines = lines;\n\t\treturn lines;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n}\n```\n\n## Example\n\nSee `test/chat-simple.ts` for a complete chat interface example with:\n\n- Markdown messages with custom background colors\n- Loading spinner during responses\n- Editor with autocomplete and slash commands\n- Spacers between messages\n\nRun it:\n\n```bash\nnpx tsx test/chat-simple.ts\n```\n\n## Development\n\n```bash\n# Install dependencies (from monorepo root)\nnpm install\n\n# Run type checking\nnpm run check\n\n# Run the demo\nnpx tsx test/chat-simple.ts\n```\n","readmeFilename":"README.md"}