refactor: integrate ink-picture as source directory
Browse files- Move ink-picture dependencies from file: reference to direct source integration
- Remove ink-picture's own node_modules, build, package.json, tsconfig.json, biome.json
- Update all ink-picture imports to use src/ink-picture path alias
- Add ink-picture path alias to tsconfig.json
- Add is-unicode-supported, iterm2-version, jimp, node-fetch, sixel as direct deps
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- bun.lock +187 -31
- package.json +5 -1
- src/components/InlineImage.tsx +1 -1
- src/ink-picture/InkPictureProvider.tsx +318 -0
- src/ink-picture/__tests__/LocalImageShow.test.ts +0 -0
- src/ink-picture/components/ImageBox.tsx +44 -0
- src/ink-picture/components/image/Ascii.tsx +48 -0
- src/ink-picture/components/image/Braille.tsx +46 -0
- src/ink-picture/components/image/HalfBlock.tsx +46 -0
- src/ink-picture/components/image/ITerm2.tsx +63 -0
- src/ink-picture/components/image/Kitty.tsx +127 -0
- src/ink-picture/components/image/Sixel.tsx +63 -0
- src/ink-picture/components/image/index.tsx +130 -0
- src/ink-picture/components/image/protocol.ts +31 -0
- src/ink-picture/hooks/useBackgroundColor.ts +22 -0
- src/ink-picture/hooks/useDirectRenderer.ts +123 -0
- src/ink-picture/hooks/useImage.ts +71 -0
- src/ink-picture/hooks/useMeasuredSize.ts +27 -0
- src/ink-picture/hooks/usePosition.ts +137 -0
- src/ink-picture/hooks/useProtocol.ts +24 -0
- src/ink-picture/hooks/useVisibility.ts +84 -0
- src/ink-picture/index.ts +67 -0
- src/ink-picture/renderers/ascii.ts +41 -0
- src/ink-picture/renderers/braille.ts +74 -0
- src/ink-picture/renderers/halfBlock.ts +35 -0
- src/ink-picture/renderers/index.ts +11 -0
- src/ink-picture/renderers/iterm2.ts +19 -0
- src/ink-picture/renderers/kitty.ts +46 -0
- src/ink-picture/renderers/sixel.ts +8 -0
- src/ink-picture/renderers/types.ts +16 -0
- src/ink-picture/utils/ansiEscapes.ts +28 -0
- src/ink-picture/utils/bgColorize.ts +48 -0
- src/ink-picture/utils/generateKittyId.ts +56 -0
- src/ink-picture/utils/getBestProtocol.ts +43 -0
- src/ink-picture/utils/image.ts +115 -0
- src/ink-picture/utils/imageCache.ts +39 -0
- src/ink-picture/utils/queryTerminal.ts +222 -0
- src/ink/components/StdoutContext.tsx +15 -0
- src/tools/ImageShowTool/ImageShowTool.tsx +26 -2
- src/tools/ImageShowTool/__tests__/ShowLocalImage.test.tsx +51 -0
- src/tools/ImageShowTool/__tests__/ShowUrlImage.test.tsx +53 -0
- tsconfig.json +3 -1
bun.lock
CHANGED
|
@@ -75,11 +75,14 @@
|
|
| 75 |
"ignore": "^7.0.5",
|
| 76 |
"indent-string": "^5.0.0",
|
| 77 |
"ink": "^6.8.0",
|
|
|
|
|
|
|
|
|
|
| 78 |
"jsonc-parser": "^3.3.1",
|
| 79 |
"lodash-es": "^4.17.23",
|
| 80 |
"lru-cache": "^11.2.7",
|
| 81 |
"marked": "^17.0.5",
|
| 82 |
-
"node-
|
| 83 |
"onnxruntime-web": "^1.27.0",
|
| 84 |
"p-map": "^7.0.4",
|
| 85 |
"picomatch": "^4.0.4",
|
|
@@ -93,6 +96,7 @@
|
|
| 93 |
"sharp": "^0.34.5",
|
| 94 |
"shell-quote": "^1.8.3",
|
| 95 |
"signal-exit": "^4.1.0",
|
|
|
|
| 96 |
"stack-utils": "^2.0.6",
|
| 97 |
"strip-ansi": "^7.2.0",
|
| 98 |
"supports-hyperlinks": "^4.4.0",
|
|
@@ -240,6 +244,8 @@
|
|
| 240 |
|
| 241 |
"@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="],
|
| 242 |
|
|
|
|
|
|
|
| 243 |
"@commander-js/extra-typings": ["@commander-js/extra-typings@14.0.0", "", { "peerDependencies": { "commander": "~14.0.0" } }, "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg=="],
|
| 244 |
|
| 245 |
"@emnapi/runtime": ["@emnapi/runtime@1.11.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg=="],
|
|
@@ -332,6 +338,62 @@
|
|
| 332 |
|
| 333 |
"@inquirer/type": ["@inquirer/type@2.0.0", "", { "dependencies": { "mute-stream": "^1.0.0" } }, "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag=="],
|
| 334 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
"@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="],
|
| 336 |
|
| 337 |
"@larksuite/channel": ["@larksuite/channel@0.1.2", "", { "dependencies": { "@larksuiteoapi/node-sdk": "^1.66.1", "https-proxy-agent": "^9.0.0" } }, "sha512-9bhxF8lCFsYiOq+E9Swg1/5nqJLJNPsVLTGIdEE8hcqRKg/Dwazh4UKP0jHjiFf8yI8KKTfsEkA2P6PUFmSZQw=="],
|
|
@@ -478,6 +540,10 @@
|
|
| 478 |
|
| 479 |
"@tavily/core": ["@tavily/core@0.6.4", "", { "dependencies": { "axios": "^1.7.7", "https-proxy-agent": "^7.0.6", "js-tiktoken": "^1.0.14" } }, "sha512-PppC0p2SwkoImLiYFT/uqDyWKPivpVsIM16HUf1Apmtbqg1YhI7Yg5Hq6eYSojC6COVCGXE4CotBnWqUmrai+A=="],
|
| 480 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 481 |
"@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
|
| 482 |
|
| 483 |
"@types/lodash": ["@types/lodash@4.17.24", "", {}, "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ=="],
|
|
@@ -488,6 +554,8 @@
|
|
| 488 |
|
| 489 |
"@types/node": ["@types/node@25.9.3", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg=="],
|
| 490 |
|
|
|
|
|
|
|
| 491 |
"@types/wrap-ansi": ["@types/wrap-ansi@3.0.0", "", {}, "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g=="],
|
| 492 |
|
| 493 |
"@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.6", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og=="],
|
|
@@ -514,10 +582,14 @@
|
|
| 514 |
|
| 515 |
"ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
| 516 |
|
|
|
|
|
|
|
| 517 |
"any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="],
|
| 518 |
|
| 519 |
"anynum": ["anynum@1.0.0", "", {}, "sha512-xjR9/zBVnUOP6ztMIIgShjsxui80nQUQH+5xJnvrYLs+90bF25/KJqaAi8mk+B4RDtX1Nspi6fmp4YTEts8SfA=="],
|
| 520 |
|
|
|
|
|
|
|
| 521 |
"asciichart": ["asciichart@1.5.25", "", {}, "sha512-PNxzXIPPOtWq8T7bgzBtk9cI2lgS4SJZthUHEiQ1aoIc3lNzGfUvIvo9LiAnq26TACo9t1/4qP6KTGAUbzX9Xg=="],
|
| 522 |
|
| 523 |
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
|
@@ -526,6 +598,8 @@
|
|
| 526 |
|
| 527 |
"auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="],
|
| 528 |
|
|
|
|
|
|
|
| 529 |
"axios": ["axios@1.17.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw=="],
|
| 530 |
|
| 531 |
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
|
@@ -538,6 +612,8 @@
|
|
| 538 |
|
| 539 |
"bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
|
| 540 |
|
|
|
|
|
|
|
| 541 |
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
| 542 |
|
| 543 |
"bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
|
|
@@ -604,6 +680,8 @@
|
|
| 604 |
|
| 605 |
"cssfilter": ["cssfilter@0.0.10", "", {}, "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw=="],
|
| 606 |
|
|
|
|
|
|
|
| 607 |
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
| 608 |
|
| 609 |
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
|
@@ -672,6 +750,8 @@
|
|
| 672 |
|
| 673 |
"execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="],
|
| 674 |
|
|
|
|
|
|
|
| 675 |
"express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
| 676 |
|
| 677 |
"express-rate-limit": ["express-rate-limit@8.5.2", "", { "dependencies": { "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A=="],
|
|
@@ -694,6 +774,8 @@
|
|
| 694 |
|
| 695 |
"figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
|
| 696 |
|
|
|
|
|
|
|
| 697 |
"file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
|
| 698 |
|
| 699 |
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
|
@@ -738,6 +820,8 @@
|
|
| 738 |
|
| 739 |
"get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
|
| 740 |
|
|
|
|
|
|
|
| 741 |
"glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
|
| 742 |
|
| 743 |
"global-agent": ["global-agent@4.1.3", "", { "dependencies": { "globalthis": "^1.0.2", "matcher": "^4.0.0", "semver": "^7.3.5", "serialize-error": "^8.1.0" } }, "sha512-KUJEViiuFT3I97t+GYMikLPJS2Lfo/S2F+DQuBWzuzaMPnvt5yyZePzArx36fBzpGTxZjIpDbXLeySLgh+k76g=="],
|
|
@@ -782,8 +866,12 @@
|
|
| 782 |
|
| 783 |
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
| 784 |
|
|
|
|
|
|
|
| 785 |
"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
| 786 |
|
|
|
|
|
|
|
| 787 |
"indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
|
| 788 |
|
| 789 |
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
|
@@ -816,8 +904,14 @@
|
|
| 816 |
|
| 817 |
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
| 818 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 819 |
"jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="],
|
| 820 |
|
|
|
|
|
|
|
| 821 |
"js-tiktoken": ["js-tiktoken@1.0.21", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="],
|
| 822 |
|
| 823 |
"json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
|
|
@@ -880,6 +974,10 @@
|
|
| 880 |
|
| 881 |
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
| 882 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 883 |
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
| 884 |
|
| 885 |
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
|
@@ -908,8 +1006,6 @@
|
|
| 908 |
|
| 909 |
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
| 910 |
|
| 911 |
-
"node-edge-tts": ["node-edge-tts@1.2.10", "", { "dependencies": { "https-proxy-agent": "^7.0.1", "ws": "^8.13.0", "yargs": "^17.7.2" }, "bin": { "node-edge-tts": "bin.js" } }, "sha512-bV2i4XU54D45+US0Zm1HcJRkifuB3W438dWyuJEHLQdKxnuqlI1kim2MOvR6Q3XUQZvfF9PoDyR1Rt7aeXhPdQ=="],
|
| 912 |
-
|
| 913 |
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
| 914 |
|
| 915 |
"node-forge": ["node-forge@1.4.0", "", {}, "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ=="],
|
|
@@ -922,6 +1018,8 @@
|
|
| 922 |
|
| 923 |
"object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
|
| 924 |
|
|
|
|
|
|
|
| 925 |
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
| 926 |
|
| 927 |
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
|
@@ -948,6 +1046,14 @@
|
|
| 948 |
|
| 949 |
"p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
|
| 950 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 951 |
"parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="],
|
| 952 |
|
| 953 |
"parse5": ["parse5@5.1.1", "", {}, "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="],
|
|
@@ -970,6 +1076,8 @@
|
|
| 970 |
|
| 971 |
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
| 972 |
|
|
|
|
|
|
|
| 973 |
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
|
| 974 |
|
| 975 |
"platform": ["platform@1.3.6", "", {}, "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="],
|
|
@@ -1026,6 +1134,8 @@
|
|
| 1026 |
|
| 1027 |
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
| 1028 |
|
|
|
|
|
|
|
| 1029 |
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
| 1030 |
|
| 1031 |
"semver": ["semver@7.8.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA=="],
|
|
@@ -1058,6 +1168,10 @@
|
|
| 1058 |
|
| 1059 |
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
| 1060 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1061 |
"slice-ansi": ["slice-ansi@8.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg=="],
|
| 1062 |
|
| 1063 |
"ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="],
|
|
@@ -1074,22 +1188,30 @@
|
|
| 1074 |
|
| 1075 |
"strnum": ["strnum@2.4.0", "", { "dependencies": { "anynum": "^1.0.0" } }, "sha512-sHrVyWWdq28RbhjuJdZsA1SnGRJV6NiXbk6AXBxDOsgAcA+lmpUZCYjOdLBxkXMwis6RRe7dlZt4VlIWFVzkmg=="],
|
| 1076 |
|
|
|
|
|
|
|
| 1077 |
"supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
|
| 1078 |
|
| 1079 |
"supports-hyperlinks": ["supports-hyperlinks@4.4.0", "", { "dependencies": { "has-flag": "^5.0.1", "supports-color": "^10.2.2" } }, "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg=="],
|
| 1080 |
|
| 1081 |
"tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
|
| 1082 |
|
|
|
|
|
|
|
| 1083 |
"terminal-size": ["terminal-size@4.0.1", "", {}, "sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ=="],
|
| 1084 |
|
| 1085 |
"thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="],
|
| 1086 |
|
| 1087 |
"thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="],
|
| 1088 |
|
|
|
|
|
|
|
| 1089 |
"tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="],
|
| 1090 |
|
| 1091 |
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
| 1092 |
|
|
|
|
|
|
|
| 1093 |
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
| 1094 |
|
| 1095 |
"tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
|
|
@@ -1106,6 +1228,8 @@
|
|
| 1106 |
|
| 1107 |
"typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="],
|
| 1108 |
|
|
|
|
|
|
|
| 1109 |
"undici": ["undici@7.27.2", "", {}, "sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA=="],
|
| 1110 |
|
| 1111 |
"undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="],
|
|
@@ -1118,6 +1242,8 @@
|
|
| 1118 |
|
| 1119 |
"usehooks-ts": ["usehooks-ts@3.1.1", "", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="],
|
| 1120 |
|
|
|
|
|
|
|
| 1121 |
"uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
| 1122 |
|
| 1123 |
"vanipath": ["vanipath@1.0.10", "", {}, "sha512-50l1CcT4Me+1p2SI51sm/KYcADNC3pLDB70jMlGWdm61TIyryO4K6IvV8PCJxM/V6AILISNqidpzkFKScsxWfg=="],
|
|
@@ -1154,6 +1280,10 @@
|
|
| 1154 |
|
| 1155 |
"xml-naming": ["xml-naming@0.1.0", "", {}, "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw=="],
|
| 1156 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1157 |
"xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
|
| 1158 |
|
| 1159 |
"xss": ["xss@1.0.15", "", { "dependencies": { "commander": "^2.20.3", "cssfilter": "0.0.10" }, "bin": { "xss": "bin/xss" } }, "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg=="],
|
|
@@ -1284,6 +1414,40 @@
|
|
| 1284 |
|
| 1285 |
"@inquirer/select/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
|
| 1286 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1287 |
"@larksuite/channel/https-proxy-agent": ["https-proxy-agent@9.1.0", "", { "dependencies": { "agent-base": "9.0.0", "debug": "^4.3.4", "proxy-agent-negotiate": "1.1.0" } }, "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA=="],
|
| 1288 |
|
| 1289 |
"@larksuiteoapi/node-sdk/axios": ["axios@1.13.6", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ=="],
|
|
@@ -1394,6 +1558,8 @@
|
|
| 1394 |
|
| 1395 |
"@typespec/ts-http-runtime/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
| 1396 |
|
|
|
|
|
|
|
| 1397 |
"axios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
|
| 1398 |
|
| 1399 |
"cli-highlight/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
|
@@ -1418,6 +1584,8 @@
|
|
| 1418 |
|
| 1419 |
"http-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
| 1420 |
|
|
|
|
|
|
|
| 1421 |
"ink/@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.5", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-3NX/MpTdroi0aKz134A6RC2Gb2iXVECN4QaAXnvCIxxIm3C3AVB1mkUe8NaaiyvOpDfsrqWhYtj+Q6a62RrTsw=="],
|
| 1422 |
|
| 1423 |
"ink/cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
|
|
@@ -1432,14 +1600,12 @@
|
|
| 1432 |
|
| 1433 |
"minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
| 1434 |
|
| 1435 |
-
"node-edge-tts/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
| 1436 |
-
|
| 1437 |
-
"node-edge-tts/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
| 1438 |
-
|
| 1439 |
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
| 1440 |
|
| 1441 |
"parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],
|
| 1442 |
|
|
|
|
|
|
|
| 1443 |
"proper-lockfile/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
| 1444 |
|
| 1445 |
"qrcode/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
|
|
@@ -1452,6 +1618,8 @@
|
|
| 1452 |
|
| 1453 |
"vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@9.0.0", "", {}, "sha512-+VvMmQPJhtvJ+8O+zu2JKIRiLxXF8NW7krWgyMGeOHrp4Cn23T5hc0v2LknNeopDOB70wghHAds7mKtcZ0I4Sg=="],
|
| 1454 |
|
|
|
|
|
|
|
| 1455 |
"xss/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
| 1456 |
|
| 1457 |
"yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
|
@@ -1506,6 +1674,18 @@
|
|
| 1506 |
|
| 1507 |
"@typespec/ts-http-runtime/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
| 1508 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1509 |
"axios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
| 1510 |
|
| 1511 |
"cli-highlight/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
|
@@ -1532,14 +1712,6 @@
|
|
| 1532 |
|
| 1533 |
"ink/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
| 1534 |
|
| 1535 |
-
"node-edge-tts/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
| 1536 |
-
|
| 1537 |
-
"node-edge-tts/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
| 1538 |
-
|
| 1539 |
-
"node-edge-tts/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
| 1540 |
-
|
| 1541 |
-
"node-edge-tts/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
| 1542 |
-
|
| 1543 |
"qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
|
| 1544 |
|
| 1545 |
"qrcode/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
|
@@ -1590,16 +1762,6 @@
|
|
| 1590 |
|
| 1591 |
"gtoken/gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
| 1592 |
|
| 1593 |
-
"node-edge-tts/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
| 1594 |
-
|
| 1595 |
-
"node-edge-tts/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
| 1596 |
-
|
| 1597 |
-
"node-edge-tts/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
| 1598 |
-
|
| 1599 |
-
"node-edge-tts/yargs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
| 1600 |
-
|
| 1601 |
-
"node-edge-tts/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
| 1602 |
-
|
| 1603 |
"qrcode/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
| 1604 |
|
| 1605 |
"qrcode/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
@@ -1620,12 +1782,6 @@
|
|
| 1620 |
|
| 1621 |
"@grpc/proto-loader/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1622 |
|
| 1623 |
-
"node-edge-tts/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1624 |
-
|
| 1625 |
-
"node-edge-tts/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
| 1626 |
-
|
| 1627 |
-
"node-edge-tts/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1628 |
-
|
| 1629 |
"qrcode/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1630 |
|
| 1631 |
"qrcode/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
|
|
|
| 75 |
"ignore": "^7.0.5",
|
| 76 |
"indent-string": "^5.0.0",
|
| 77 |
"ink": "^6.8.0",
|
| 78 |
+
"is-unicode-supported": "^2.1.0",
|
| 79 |
+
"iterm2-version": "^6.0.0",
|
| 80 |
+
"jimp": "^1.6.1",
|
| 81 |
"jsonc-parser": "^3.3.1",
|
| 82 |
"lodash-es": "^4.17.23",
|
| 83 |
"lru-cache": "^11.2.7",
|
| 84 |
"marked": "^17.0.5",
|
| 85 |
+
"node-fetch": "^3.3.2",
|
| 86 |
"onnxruntime-web": "^1.27.0",
|
| 87 |
"p-map": "^7.0.4",
|
| 88 |
"picomatch": "^4.0.4",
|
|
|
|
| 96 |
"sharp": "^0.34.5",
|
| 97 |
"shell-quote": "^1.8.3",
|
| 98 |
"signal-exit": "^4.1.0",
|
| 99 |
+
"sixel": "^0.16.0",
|
| 100 |
"stack-utils": "^2.0.6",
|
| 101 |
"strip-ansi": "^7.2.0",
|
| 102 |
"supports-hyperlinks": "^4.4.0",
|
|
|
|
| 244 |
|
| 245 |
"@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="],
|
| 246 |
|
| 247 |
+
"@borewit/text-codec": ["@borewit/text-codec@0.2.2", "", {}, "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ=="],
|
| 248 |
+
|
| 249 |
"@commander-js/extra-typings": ["@commander-js/extra-typings@14.0.0", "", { "peerDependencies": { "commander": "~14.0.0" } }, "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg=="],
|
| 250 |
|
| 251 |
"@emnapi/runtime": ["@emnapi/runtime@1.11.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg=="],
|
|
|
|
| 338 |
|
| 339 |
"@inquirer/type": ["@inquirer/type@2.0.0", "", { "dependencies": { "mute-stream": "^1.0.0" } }, "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag=="],
|
| 340 |
|
| 341 |
+
"@jimp/core": ["@jimp/core@1.6.1", "", { "dependencies": { "@jimp/file-ops": "1.6.1", "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "await-to-js": "^3.0.0", "exif-parser": "^0.1.12", "file-type": "^21.3.3", "mime": "3" } }, "sha512-+BoKC5G6hkrSy501zcJ2EpfnllP+avPevcBfRcZe/CW+EwEfY6X1EZ8QWyT7NpDIvEEJb1fdJnMMfUnFkxmw9A=="],
|
| 342 |
+
|
| 343 |
+
"@jimp/diff": ["@jimp/diff@1.6.1", "", { "dependencies": { "@jimp/plugin-resize": "1.6.1", "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "pixelmatch": "^5.3.0" } }, "sha512-YkKDPdHjLgo1Api3+Bhc0GLAygldlpt97NfOKoNg1U6IUNXA6X2MgosCjPfSBiSvJvrrz1fsIR+/4cfYXBI/HQ=="],
|
| 344 |
+
|
| 345 |
+
"@jimp/file-ops": ["@jimp/file-ops@1.6.1", "", {}, "sha512-T+gX6osHjprbDRad0/B71Evyre7ZdVY1z/gFGEG9Z8KOtZPKboWvPeP2UjbZYWQLy9UKCPQX1FNAnDiOPkJL7w=="],
|
| 346 |
+
|
| 347 |
+
"@jimp/js-bmp": ["@jimp/js-bmp@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "bmp-ts": "^1.0.9" } }, "sha512-xzWzNT4/u5zGrTT3Tme9sGU7YzIKxi13+BCQwLqACbt5DXf9SAfdzRkopZQnmDko+6In5nqaT89Gjs43/WdnYQ=="],
|
| 348 |
+
|
| 349 |
+
"@jimp/js-gif": ["@jimp/js-gif@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/types": "1.6.1", "gifwrap": "^0.10.1", "omggif": "^1.0.10" } }, "sha512-YjY2W26rQa05XhanYhRZ7dingCiNN+T2Ymb1JiigIbABY0B28wHE3v3Cf1/HZPWGu0hOg36ylaKgV5KxF2M58w=="],
|
| 350 |
+
|
| 351 |
+
"@jimp/js-jpeg": ["@jimp/js-jpeg@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/types": "1.6.1", "jpeg-js": "^0.4.4" } }, "sha512-HT9H3yOmlOFzYmdI15IYdfy6ggQhSRIaHeA+OTJSEORXBqEo97sUZu/DsgHIcX5NJ7TkJBTgZ9BZXsV6UbsyMg=="],
|
| 352 |
+
|
| 353 |
+
"@jimp/js-png": ["@jimp/js-png@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/types": "1.6.1", "pngjs": "^7.0.0" } }, "sha512-SZ/KVhI5UjcSzzlXsXdIi/LhJ7UShf2NkMOtVrbZQcGzsqNtynAelrOXeoTxcanfVqmNhAoVHg8yR2cYoqrYjA=="],
|
| 354 |
+
|
| 355 |
+
"@jimp/js-tiff": ["@jimp/js-tiff@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/types": "1.6.1", "utif2": "^4.1.0" } }, "sha512-jDG/eJquID1M4MBlKMmDRBmz2TpXMv7TUyu2nIRUxhlUc2ogC82T+VQUkca9GJH1BBJ9dx5sSE5dGkWNjIbZxw=="],
|
| 356 |
+
|
| 357 |
+
"@jimp/plugin-blit": ["@jimp/plugin-blit@1.6.1", "", { "dependencies": { "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "zod": "^3.23.8" } }, "sha512-MwnI7C7K81uWddY9FLw1fCOIy6SsPIUftUz36Spt7jisCn8/40DhQMlSxpxTNelnZb/2SnloFimQfRZAmHLOqQ=="],
|
| 358 |
+
|
| 359 |
+
"@jimp/plugin-blur": ["@jimp/plugin-blur@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/utils": "1.6.1" } }, "sha512-lIo7Tzp5jQu30EFFSK/phXANK3citKVEjepDjQ6ljHoIFtuMRrnybnmI2Md24ulvWlDaz+hh3n6qrMb8ydwhZQ=="],
|
| 360 |
+
|
| 361 |
+
"@jimp/plugin-circle": ["@jimp/plugin-circle@1.6.1", "", { "dependencies": { "@jimp/types": "1.6.1", "zod": "^3.23.8" } }, "sha512-kK1PavY6cKHNNKce37vdV4Tmpc1/zDKngGoeOV3j+EMatoHFZUinV3s6F9aWryPs3A0xhCLZgdJ6Zeea1d5LCQ=="],
|
| 362 |
+
|
| 363 |
+
"@jimp/plugin-color": ["@jimp/plugin-color@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "tinycolor2": "^1.6.0", "zod": "^3.23.8" } }, "sha512-LtUN1vAP+LRlZAtTNVhDRSiXx+26Kbz3zJaG6a5k59gQ95jgT5mknnF8lxkHcqJthM4MEk3/tPxkdJpEybyF/A=="],
|
| 364 |
+
|
| 365 |
+
"@jimp/plugin-contain": ["@jimp/plugin-contain@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/plugin-blit": "1.6.1", "@jimp/plugin-resize": "1.6.1", "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "zod": "^3.23.8" } }, "sha512-m0qhrfA8jkTqretGv4w+T/ADFR4GwBpE0sCOC2uJ0dzr44/ddOMsIdrpi89kabqYiPYIrxkgdCVCLm3zn1Vkkg=="],
|
| 366 |
+
|
| 367 |
+
"@jimp/plugin-cover": ["@jimp/plugin-cover@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/plugin-crop": "1.6.1", "@jimp/plugin-resize": "1.6.1", "@jimp/types": "1.6.1", "zod": "^3.23.8" } }, "sha512-hZytnsth0zoll6cPf434BrT+p/v569Wr5tyO6Dp0dH1IDPhzhB5F38sZGMLDo7bzQiN9JFVB3fxkcJ/WYCJ3Mg=="],
|
| 368 |
+
|
| 369 |
+
"@jimp/plugin-crop": ["@jimp/plugin-crop@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "zod": "^3.23.8" } }, "sha512-EerRSLlclXyKDnYc/H9w/1amZW7b7v3OGi/VlerPd2M/pAu5X8TkyYWtfqYCXnNp1Ixtd8oCo9zGfY9zoXT4rg=="],
|
| 370 |
+
|
| 371 |
+
"@jimp/plugin-displace": ["@jimp/plugin-displace@1.6.1", "", { "dependencies": { "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "zod": "^3.23.8" } }, "sha512-K07QVl7xQwIfD6KfxRV/c3E9e7ZBXxUXdWuvoTWcKHL2qV48MOF5Nqbz/aJW4ThnQARIsxvYlZjPFiqkCjlU+g=="],
|
| 372 |
+
|
| 373 |
+
"@jimp/plugin-dither": ["@jimp/plugin-dither@1.6.1", "", { "dependencies": { "@jimp/types": "1.6.1" } }, "sha512-+2V+GCV2WycMoX1/z977TkZ8Zq/4MVSKElHYatgUqtwXMi2fDK2gKYU2g9V39IqFvTJsTIsK0+58VFz/ROBVew=="],
|
| 374 |
+
|
| 375 |
+
"@jimp/plugin-fisheye": ["@jimp/plugin-fisheye@1.6.1", "", { "dependencies": { "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "zod": "^3.23.8" } }, "sha512-XtS5ZyoZ0vxZxJ6gkqI63SivhtI58vX95foMPM+cyzYkRsJXMOYCr8DScxF5bp4Xr003NjYm/P+7+08tibwzHA=="],
|
| 376 |
+
|
| 377 |
+
"@jimp/plugin-flip": ["@jimp/plugin-flip@1.6.1", "", { "dependencies": { "@jimp/types": "1.6.1", "zod": "^3.23.8" } }, "sha512-ws38W/sGj7LobNRayQ83garxiktOyWxM5vO/y4a/2cy9v65SLEUzVkrj+oeAaUSSObdz4HcCEla7XtGlnAGAaA=="],
|
| 378 |
+
|
| 379 |
+
"@jimp/plugin-hash": ["@jimp/plugin-hash@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/js-bmp": "1.6.1", "@jimp/js-jpeg": "1.6.1", "@jimp/js-png": "1.6.1", "@jimp/js-tiff": "1.6.1", "@jimp/plugin-color": "1.6.1", "@jimp/plugin-resize": "1.6.1", "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "any-base": "^1.1.0" } }, "sha512-sZt6ZcMX6i8vFWb4GYnw0pR/o9++ef0dTVcboTB5B/g7nrxCODIB4wfEkJ/YqZM5wUvol77K1qeS0/rVO6z21A=="],
|
| 380 |
+
|
| 381 |
+
"@jimp/plugin-mask": ["@jimp/plugin-mask@1.6.1", "", { "dependencies": { "@jimp/types": "1.6.1", "zod": "^3.23.8" } }, "sha512-SIG0/FcmEj3tkwFxc7fAGLO8o4uNzMpSOdQOhbCgxefQKq5wOVMk9BQx/sdMPBwtMLr9WLq0GzLA/rk6t2v20A=="],
|
| 382 |
+
|
| 383 |
+
"@jimp/plugin-print": ["@jimp/plugin-print@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/js-jpeg": "1.6.1", "@jimp/js-png": "1.6.1", "@jimp/plugin-blit": "1.6.1", "@jimp/types": "1.6.1", "parse-bmfont-ascii": "^1.0.6", "parse-bmfont-binary": "^1.0.6", "parse-bmfont-xml": "^1.1.6", "simple-xml-to-json": "^1.2.2", "zod": "^3.23.8" } }, "sha512-BYVz/X3Xzv8XYilVeDy11NOp0h7BTDjlOtu0BekIFHP1yHVd24AXNzbOy52XlzYZWQ0Dl36HOHEpl/nSNrzc6w=="],
|
| 384 |
+
|
| 385 |
+
"@jimp/plugin-quantize": ["@jimp/plugin-quantize@1.6.1", "", { "dependencies": { "image-q": "^4.0.0", "zod": "^3.23.8" } }, "sha512-J2En9PLURfP+vwYDtuZ9T8yBW6BWYZBScydAjRiPBmJfEhTcNQqiiQODrZf7EqbbX/Sy5H6dAeRiqkgoV9N6Ww=="],
|
| 386 |
+
|
| 387 |
+
"@jimp/plugin-resize": ["@jimp/plugin-resize@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/types": "1.6.1", "zod": "^3.23.8" } }, "sha512-CLkrtJoIz2HdWnpYiN6p8KYcPc00rCH/SUu6o+lfZL05Q4uhecJlnvXuj9x+U6mDn3ldPmJj6aZqMHuUJzdVqg=="],
|
| 388 |
+
|
| 389 |
+
"@jimp/plugin-rotate": ["@jimp/plugin-rotate@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/plugin-crop": "1.6.1", "@jimp/plugin-resize": "1.6.1", "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "zod": "^3.23.8" } }, "sha512-nOjVjbbj705B02ksysKnh0POAwEBXZtJ9zQ5qC+X7Tavl3JNn+P3BzQovbBxLPSbUSld6XID9z5ijin4PtOAUg=="],
|
| 390 |
+
|
| 391 |
+
"@jimp/plugin-threshold": ["@jimp/plugin-threshold@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/plugin-color": "1.6.1", "@jimp/plugin-hash": "1.6.1", "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1", "zod": "^3.23.8" } }, "sha512-JOKv9F8s6tnVLf4sB/2fF0F339EFnHvgEdFYugO6VhowKLsap0pEZmLyE/DlRnYtIj2RddHZVxVMp/eKJ04l2Q=="],
|
| 392 |
+
|
| 393 |
+
"@jimp/types": ["@jimp/types@1.6.1", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-leI7YbveTNi565m910XgIOwXyuu074H5qazAD1357HImJSv2hqxnWXpwxQbadGWZ7goZRYBDZy5lpqud0p7q5w=="],
|
| 394 |
+
|
| 395 |
+
"@jimp/utils": ["@jimp/utils@1.6.1", "", { "dependencies": { "@jimp/types": "1.6.1", "tinycolor2": "^1.6.0" } }, "sha512-veFPRd93FCnS7AgmCkPgARVGoDRrJ9cm1ujuNyA+UfQ5VKbED2002sm5XfFLFwTsKC8j04heTrwe+tU1dluXOw=="],
|
| 396 |
+
|
| 397 |
"@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="],
|
| 398 |
|
| 399 |
"@larksuite/channel": ["@larksuite/channel@0.1.2", "", { "dependencies": { "@larksuiteoapi/node-sdk": "^1.66.1", "https-proxy-agent": "^9.0.0" } }, "sha512-9bhxF8lCFsYiOq+E9Swg1/5nqJLJNPsVLTGIdEE8hcqRKg/Dwazh4UKP0jHjiFf8yI8KKTfsEkA2P6PUFmSZQw=="],
|
|
|
|
| 540 |
|
| 541 |
"@tavily/core": ["@tavily/core@0.6.4", "", { "dependencies": { "axios": "^1.7.7", "https-proxy-agent": "^7.0.6", "js-tiktoken": "^1.0.14" } }, "sha512-PppC0p2SwkoImLiYFT/uqDyWKPivpVsIM16HUf1Apmtbqg1YhI7Yg5Hq6eYSojC6COVCGXE4CotBnWqUmrai+A=="],
|
| 542 |
|
| 543 |
+
"@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="],
|
| 544 |
+
|
| 545 |
+
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
|
| 546 |
+
|
| 547 |
"@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
|
| 548 |
|
| 549 |
"@types/lodash": ["@types/lodash@4.17.24", "", {}, "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ=="],
|
|
|
|
| 554 |
|
| 555 |
"@types/node": ["@types/node@25.9.3", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg=="],
|
| 556 |
|
| 557 |
+
"@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="],
|
| 558 |
+
|
| 559 |
"@types/wrap-ansi": ["@types/wrap-ansi@3.0.0", "", {}, "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g=="],
|
| 560 |
|
| 561 |
"@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.6", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og=="],
|
|
|
|
| 582 |
|
| 583 |
"ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
| 584 |
|
| 585 |
+
"any-base": ["any-base@1.1.0", "", {}, "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="],
|
| 586 |
+
|
| 587 |
"any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="],
|
| 588 |
|
| 589 |
"anynum": ["anynum@1.0.0", "", {}, "sha512-xjR9/zBVnUOP6ztMIIgShjsxui80nQUQH+5xJnvrYLs+90bF25/KJqaAi8mk+B4RDtX1Nspi6fmp4YTEts8SfA=="],
|
| 590 |
|
| 591 |
+
"app-path": ["app-path@4.0.0", "", { "dependencies": { "execa": "^5.0.0" } }, "sha512-mgBO9PZJ3MpbKbwFTljTi36ZKBvG5X/fkVR1F85ANsVcVllEb+C0LGNdJfGUm84GpC4xxgN6HFkmkMU8VEO4mA=="],
|
| 592 |
+
|
| 593 |
"asciichart": ["asciichart@1.5.25", "", {}, "sha512-PNxzXIPPOtWq8T7bgzBtk9cI2lgS4SJZthUHEiQ1aoIc3lNzGfUvIvo9LiAnq26TACo9t1/4qP6KTGAUbzX9Xg=="],
|
| 594 |
|
| 595 |
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
|
|
|
| 598 |
|
| 599 |
"auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="],
|
| 600 |
|
| 601 |
+
"await-to-js": ["await-to-js@3.0.0", "", {}, "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g=="],
|
| 602 |
+
|
| 603 |
"axios": ["axios@1.17.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw=="],
|
| 604 |
|
| 605 |
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
|
|
|
| 612 |
|
| 613 |
"bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
|
| 614 |
|
| 615 |
+
"bmp-ts": ["bmp-ts@1.0.9", "", {}, "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw=="],
|
| 616 |
+
|
| 617 |
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
| 618 |
|
| 619 |
"bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
|
|
|
|
| 680 |
|
| 681 |
"cssfilter": ["cssfilter@0.0.10", "", {}, "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw=="],
|
| 682 |
|
| 683 |
+
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
| 684 |
+
|
| 685 |
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
| 686 |
|
| 687 |
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
|
|
|
| 750 |
|
| 751 |
"execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="],
|
| 752 |
|
| 753 |
+
"exif-parser": ["exif-parser@0.1.12", "", {}, "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="],
|
| 754 |
+
|
| 755 |
"express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
| 756 |
|
| 757 |
"express-rate-limit": ["express-rate-limit@8.5.2", "", { "dependencies": { "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A=="],
|
|
|
|
| 774 |
|
| 775 |
"figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
|
| 776 |
|
| 777 |
+
"file-type": ["file-type@21.3.4", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g=="],
|
| 778 |
+
|
| 779 |
"file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
|
| 780 |
|
| 781 |
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
|
|
|
| 820 |
|
| 821 |
"get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
|
| 822 |
|
| 823 |
+
"gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
|
| 824 |
+
|
| 825 |
"glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
|
| 826 |
|
| 827 |
"global-agent": ["global-agent@4.1.3", "", { "dependencies": { "globalthis": "^1.0.2", "matcher": "^4.0.0", "semver": "^7.3.5", "serialize-error": "^8.1.0" } }, "sha512-KUJEViiuFT3I97t+GYMikLPJS2Lfo/S2F+DQuBWzuzaMPnvt5yyZePzArx36fBzpGTxZjIpDbXLeySLgh+k76g=="],
|
|
|
|
| 866 |
|
| 867 |
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
| 868 |
|
| 869 |
+
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
| 870 |
+
|
| 871 |
"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
| 872 |
|
| 873 |
+
"image-q": ["image-q@4.0.0", "", { "dependencies": { "@types/node": "16.9.1" } }, "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw=="],
|
| 874 |
+
|
| 875 |
"indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
|
| 876 |
|
| 877 |
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
|
|
|
| 904 |
|
| 905 |
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
| 906 |
|
| 907 |
+
"iterm2-version": ["iterm2-version@6.0.0", "", { "dependencies": { "app-path": "^4.0.0", "plist": "^3.1.0", "terminal-query": "^0.1.0" } }, "sha512-oH14QLmk+49KBsxlVBRe9piHHIY0vn41Mmop/LTzkCMVyGcikhMLJCpbR4LWB+WRdDF1v66u39CuRj+LLfZ9QQ=="],
|
| 908 |
+
|
| 909 |
+
"jimp": ["jimp@1.6.1", "", { "dependencies": { "@jimp/core": "1.6.1", "@jimp/diff": "1.6.1", "@jimp/js-bmp": "1.6.1", "@jimp/js-gif": "1.6.1", "@jimp/js-jpeg": "1.6.1", "@jimp/js-png": "1.6.1", "@jimp/js-tiff": "1.6.1", "@jimp/plugin-blit": "1.6.1", "@jimp/plugin-blur": "1.6.1", "@jimp/plugin-circle": "1.6.1", "@jimp/plugin-color": "1.6.1", "@jimp/plugin-contain": "1.6.1", "@jimp/plugin-cover": "1.6.1", "@jimp/plugin-crop": "1.6.1", "@jimp/plugin-displace": "1.6.1", "@jimp/plugin-dither": "1.6.1", "@jimp/plugin-fisheye": "1.6.1", "@jimp/plugin-flip": "1.6.1", "@jimp/plugin-hash": "1.6.1", "@jimp/plugin-mask": "1.6.1", "@jimp/plugin-print": "1.6.1", "@jimp/plugin-quantize": "1.6.1", "@jimp/plugin-resize": "1.6.1", "@jimp/plugin-rotate": "1.6.1", "@jimp/plugin-threshold": "1.6.1", "@jimp/types": "1.6.1", "@jimp/utils": "1.6.1" } }, "sha512-hNQh6rZtWfSVWSNVmvq87N5BPJsNH7k7I7qyrXf9DOma9xATQk3fsyHazCQe51nCjdkoWdTmh0vD7bjVSLoxxw=="],
|
| 910 |
+
|
| 911 |
"jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="],
|
| 912 |
|
| 913 |
+
"jpeg-js": ["jpeg-js@0.4.4", "", {}, "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="],
|
| 914 |
+
|
| 915 |
"js-tiktoken": ["js-tiktoken@1.0.21", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="],
|
| 916 |
|
| 917 |
"json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
|
|
|
|
| 974 |
|
| 975 |
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
| 976 |
|
| 977 |
+
"merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
|
| 978 |
+
|
| 979 |
+
"mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="],
|
| 980 |
+
|
| 981 |
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
| 982 |
|
| 983 |
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
|
|
|
| 1006 |
|
| 1007 |
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
| 1008 |
|
|
|
|
|
|
|
| 1009 |
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
| 1010 |
|
| 1011 |
"node-forge": ["node-forge@1.4.0", "", {}, "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ=="],
|
|
|
|
| 1018 |
|
| 1019 |
"object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
|
| 1020 |
|
| 1021 |
+
"omggif": ["omggif@1.0.10", "", {}, "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="],
|
| 1022 |
+
|
| 1023 |
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
| 1024 |
|
| 1025 |
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
|
|
|
| 1046 |
|
| 1047 |
"p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
|
| 1048 |
|
| 1049 |
+
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
|
| 1050 |
+
|
| 1051 |
+
"parse-bmfont-ascii": ["parse-bmfont-ascii@1.0.6", "", {}, "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA=="],
|
| 1052 |
+
|
| 1053 |
+
"parse-bmfont-binary": ["parse-bmfont-binary@1.0.6", "", {}, "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA=="],
|
| 1054 |
+
|
| 1055 |
+
"parse-bmfont-xml": ["parse-bmfont-xml@1.1.6", "", { "dependencies": { "xml-parse-from-string": "^1.0.0", "xml2js": "^0.5.0" } }, "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA=="],
|
| 1056 |
+
|
| 1057 |
"parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="],
|
| 1058 |
|
| 1059 |
"parse5": ["parse5@5.1.1", "", {}, "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="],
|
|
|
|
| 1076 |
|
| 1077 |
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
| 1078 |
|
| 1079 |
+
"pixelmatch": ["pixelmatch@5.3.0", "", { "dependencies": { "pngjs": "^6.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" } }, "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q=="],
|
| 1080 |
+
|
| 1081 |
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
|
| 1082 |
|
| 1083 |
"platform": ["platform@1.3.6", "", {}, "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="],
|
|
|
|
| 1134 |
|
| 1135 |
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
| 1136 |
|
| 1137 |
+
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
| 1138 |
+
|
| 1139 |
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
| 1140 |
|
| 1141 |
"semver": ["semver@7.8.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA=="],
|
|
|
|
| 1168 |
|
| 1169 |
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
| 1170 |
|
| 1171 |
+
"simple-xml-to-json": ["simple-xml-to-json@1.2.7", "", {}, "sha512-mz9VXphOxQWX3eQ/uXCtm6upltoN0DLx8Zb5T4TFC4FHB7S9FDPGre8CfLWqPWQQH/GrQYd2AXhhVM5LDpYx6Q=="],
|
| 1172 |
+
|
| 1173 |
+
"sixel": ["sixel@0.16.0", "", {}, "sha512-xicu6Y6Cyhmv5rjyHxq2r5RnKerlL/nyZEGjOU5bLCshXkZryc9JFJThTCKPOAtWXCfeWquEKFVFfMPcTD25PA=="],
|
| 1174 |
+
|
| 1175 |
"slice-ansi": ["slice-ansi@8.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg=="],
|
| 1176 |
|
| 1177 |
"ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="],
|
|
|
|
| 1188 |
|
| 1189 |
"strnum": ["strnum@2.4.0", "", { "dependencies": { "anynum": "^1.0.0" } }, "sha512-sHrVyWWdq28RbhjuJdZsA1SnGRJV6NiXbk6AXBxDOsgAcA+lmpUZCYjOdLBxkXMwis6RRe7dlZt4VlIWFVzkmg=="],
|
| 1190 |
|
| 1191 |
+
"strtok3": ["strtok3@10.3.5", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA=="],
|
| 1192 |
+
|
| 1193 |
"supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
|
| 1194 |
|
| 1195 |
"supports-hyperlinks": ["supports-hyperlinks@4.4.0", "", { "dependencies": { "has-flag": "^5.0.1", "supports-color": "^10.2.2" } }, "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg=="],
|
| 1196 |
|
| 1197 |
"tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
|
| 1198 |
|
| 1199 |
+
"terminal-query": ["terminal-query@0.1.1", "", {}, "sha512-MlkY08iL6mCFtdiyOKRzJqTHvUFEKCTRZZDLOdN1Vk79rX6ALq2tk2p3IfQhJJbnNLxHZnQu+WS2JT9kwLW2vQ=="],
|
| 1200 |
+
|
| 1201 |
"terminal-size": ["terminal-size@4.0.1", "", {}, "sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ=="],
|
| 1202 |
|
| 1203 |
"thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="],
|
| 1204 |
|
| 1205 |
"thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="],
|
| 1206 |
|
| 1207 |
+
"tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
|
| 1208 |
+
|
| 1209 |
"tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="],
|
| 1210 |
|
| 1211 |
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
| 1212 |
|
| 1213 |
+
"token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="],
|
| 1214 |
+
|
| 1215 |
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
| 1216 |
|
| 1217 |
"tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
|
|
|
|
| 1228 |
|
| 1229 |
"typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="],
|
| 1230 |
|
| 1231 |
+
"uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
|
| 1232 |
+
|
| 1233 |
"undici": ["undici@7.27.2", "", {}, "sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA=="],
|
| 1234 |
|
| 1235 |
"undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="],
|
|
|
|
| 1242 |
|
| 1243 |
"usehooks-ts": ["usehooks-ts@3.1.1", "", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="],
|
| 1244 |
|
| 1245 |
+
"utif2": ["utif2@4.1.0", "", { "dependencies": { "pako": "^1.0.11" } }, "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w=="],
|
| 1246 |
+
|
| 1247 |
"uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
| 1248 |
|
| 1249 |
"vanipath": ["vanipath@1.0.10", "", {}, "sha512-50l1CcT4Me+1p2SI51sm/KYcADNC3pLDB70jMlGWdm61TIyryO4K6IvV8PCJxM/V6AILISNqidpzkFKScsxWfg=="],
|
|
|
|
| 1280 |
|
| 1281 |
"xml-naming": ["xml-naming@0.1.0", "", {}, "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw=="],
|
| 1282 |
|
| 1283 |
+
"xml-parse-from-string": ["xml-parse-from-string@1.0.1", "", {}, "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g=="],
|
| 1284 |
+
|
| 1285 |
+
"xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
|
| 1286 |
+
|
| 1287 |
"xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
|
| 1288 |
|
| 1289 |
"xss": ["xss@1.0.15", "", { "dependencies": { "commander": "^2.20.3", "cssfilter": "0.0.10" }, "bin": { "xss": "bin/xss" } }, "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg=="],
|
|
|
|
| 1414 |
|
| 1415 |
"@inquirer/select/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
|
| 1416 |
|
| 1417 |
+
"@jimp/js-png/pngjs": ["pngjs@7.0.0", "", {}, "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="],
|
| 1418 |
+
|
| 1419 |
+
"@jimp/plugin-blit/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1420 |
+
|
| 1421 |
+
"@jimp/plugin-circle/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1422 |
+
|
| 1423 |
+
"@jimp/plugin-color/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1424 |
+
|
| 1425 |
+
"@jimp/plugin-contain/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1426 |
+
|
| 1427 |
+
"@jimp/plugin-cover/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1428 |
+
|
| 1429 |
+
"@jimp/plugin-crop/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1430 |
+
|
| 1431 |
+
"@jimp/plugin-displace/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1432 |
+
|
| 1433 |
+
"@jimp/plugin-fisheye/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1434 |
+
|
| 1435 |
+
"@jimp/plugin-flip/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1436 |
+
|
| 1437 |
+
"@jimp/plugin-mask/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1438 |
+
|
| 1439 |
+
"@jimp/plugin-print/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1440 |
+
|
| 1441 |
+
"@jimp/plugin-quantize/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1442 |
+
|
| 1443 |
+
"@jimp/plugin-resize/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1444 |
+
|
| 1445 |
+
"@jimp/plugin-rotate/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1446 |
+
|
| 1447 |
+
"@jimp/plugin-threshold/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1448 |
+
|
| 1449 |
+
"@jimp/types/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 1450 |
+
|
| 1451 |
"@larksuite/channel/https-proxy-agent": ["https-proxy-agent@9.1.0", "", { "dependencies": { "agent-base": "9.0.0", "debug": "^4.3.4", "proxy-agent-negotiate": "1.1.0" } }, "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA=="],
|
| 1452 |
|
| 1453 |
"@larksuiteoapi/node-sdk/axios": ["axios@1.13.6", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ=="],
|
|
|
|
| 1558 |
|
| 1559 |
"@typespec/ts-http-runtime/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
| 1560 |
|
| 1561 |
+
"app-path/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
|
| 1562 |
+
|
| 1563 |
"axios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
|
| 1564 |
|
| 1565 |
"cli-highlight/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
|
|
|
| 1584 |
|
| 1585 |
"http-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
| 1586 |
|
| 1587 |
+
"image-q/@types/node": ["@types/node@16.9.1", "", {}, "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="],
|
| 1588 |
+
|
| 1589 |
"ink/@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.5", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-3NX/MpTdroi0aKz134A6RC2Gb2iXVECN4QaAXnvCIxxIm3C3AVB1mkUe8NaaiyvOpDfsrqWhYtj+Q6a62RrTsw=="],
|
| 1590 |
|
| 1591 |
"ink/cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
|
|
|
|
| 1600 |
|
| 1601 |
"minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
| 1602 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1603 |
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
| 1604 |
|
| 1605 |
"parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],
|
| 1606 |
|
| 1607 |
+
"pixelmatch/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="],
|
| 1608 |
+
|
| 1609 |
"proper-lockfile/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
| 1610 |
|
| 1611 |
"qrcode/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
|
|
|
|
| 1618 |
|
| 1619 |
"vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@9.0.0", "", {}, "sha512-+VvMmQPJhtvJ+8O+zu2JKIRiLxXF8NW7krWgyMGeOHrp4Cn23T5hc0v2LknNeopDOB70wghHAds7mKtcZ0I4Sg=="],
|
| 1620 |
|
| 1621 |
+
"xml2js/xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
|
| 1622 |
+
|
| 1623 |
"xss/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
| 1624 |
|
| 1625 |
"yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
|
|
|
| 1674 |
|
| 1675 |
"@typespec/ts-http-runtime/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
| 1676 |
|
| 1677 |
+
"app-path/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
|
| 1678 |
+
|
| 1679 |
+
"app-path/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
|
| 1680 |
+
|
| 1681 |
+
"app-path/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
| 1682 |
+
|
| 1683 |
+
"app-path/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
|
| 1684 |
+
|
| 1685 |
+
"app-path/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
| 1686 |
+
|
| 1687 |
+
"app-path/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
|
| 1688 |
+
|
| 1689 |
"axios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
| 1690 |
|
| 1691 |
"cli-highlight/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
|
|
|
| 1712 |
|
| 1713 |
"ink/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
| 1714 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1715 |
"qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
|
| 1716 |
|
| 1717 |
"qrcode/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
|
|
|
| 1762 |
|
| 1763 |
"gtoken/gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
| 1764 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1765 |
"qrcode/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
| 1766 |
|
| 1767 |
"qrcode/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
|
|
| 1782 |
|
| 1783 |
"@grpc/proto-loader/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1784 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1785 |
"qrcode/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 1786 |
|
| 1787 |
"qrcode/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
package.json
CHANGED
|
@@ -96,7 +96,11 @@
|
|
| 96 |
"lodash-es": "^4.17.23",
|
| 97 |
"lru-cache": "^11.2.7",
|
| 98 |
"marked": "^17.0.5",
|
| 99 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
"onnxruntime-web": "^1.27.0",
|
| 101 |
"p-map": "^7.0.4",
|
| 102 |
"picomatch": "^4.0.4",
|
|
|
|
| 96 |
"lodash-es": "^4.17.23",
|
| 97 |
"lru-cache": "^11.2.7",
|
| 98 |
"marked": "^17.0.5",
|
| 99 |
+
"is-unicode-supported": "^2.1.0",
|
| 100 |
+
"iterm2-version": "^6.0.0",
|
| 101 |
+
"jimp": "^1.6.1",
|
| 102 |
+
"node-fetch": "^3.3.2",
|
| 103 |
+
"sixel": "^0.16.0",
|
| 104 |
"onnxruntime-web": "^1.27.0",
|
| 105 |
"p-map": "^7.0.4",
|
| 106 |
"picomatch": "^4.0.4",
|
src/components/InlineImage.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
import React from 'react'
|
| 2 |
-
import Image, { InkPictureProvider } from 'ink-picture'
|
| 3 |
import Link from '../ink/components/Link.js'
|
| 4 |
import { supportsHyperlinks } from '../ink/supports-hyperlinks.js'
|
| 5 |
import { Box, Text } from '../ink.js'
|
|
|
|
| 1 |
import React from 'react'
|
| 2 |
+
import Image, { InkPictureProvider } from 'src/ink-picture/index.ts'
|
| 3 |
import Link from '../ink/components/Link.js'
|
| 4 |
import { supportsHyperlinks } from '../ink/supports-hyperlinks.js'
|
| 5 |
import { Box, Text } from '../ink.js'
|
src/ink-picture/InkPictureProvider.tsx
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useStdin } from "src/ink";
|
| 2 |
+
import checkIsUnicodeSupported from "is-unicode-supported";
|
| 3 |
+
import iterm2Version from "iterm2-version";
|
| 4 |
+
import React, {
|
| 5 |
+
createContext,
|
| 6 |
+
Profiler,
|
| 7 |
+
useCallback,
|
| 8 |
+
useContext,
|
| 9 |
+
useEffect,
|
| 10 |
+
useMemo,
|
| 11 |
+
useRef,
|
| 12 |
+
useState,
|
| 13 |
+
} from "react";
|
| 14 |
+
import supportsColor from "supports-color";
|
| 15 |
+
import { ImageCache } from "./utils/imageCache.js";
|
| 16 |
+
import { queryTerminal } from "./utils/queryTerminal.js";
|
| 17 |
+
|
| 18 |
+
function resolveConfig(
|
| 19 |
+
key: string,
|
| 20 |
+
propValue: number | undefined,
|
| 21 |
+
fallback: number,
|
| 22 |
+
min: number,
|
| 23 |
+
): number {
|
| 24 |
+
const env = process.env[key];
|
| 25 |
+
if (env !== undefined) {
|
| 26 |
+
const n = parseInt(env, 10);
|
| 27 |
+
if (Number.isFinite(n) && n >= min) return n;
|
| 28 |
+
}
|
| 29 |
+
if (propValue !== undefined) return Math.max(min, propValue);
|
| 30 |
+
return fallback;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
export interface InkPictureConfig {
|
| 34 |
+
pollIntervalMs: number;
|
| 35 |
+
/** @deprecated Unused since v2.0.1 — images are now repainted after each React render via the Profiler. */
|
| 36 |
+
paintIntervalMs: number;
|
| 37 |
+
cacheSize: number;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
export const defaultConfig: InkPictureConfig = {
|
| 41 |
+
pollIntervalMs: 16,
|
| 42 |
+
/** @deprecated */
|
| 43 |
+
paintIntervalMs: 16,
|
| 44 |
+
cacheSize: 10,
|
| 45 |
+
};
|
| 46 |
+
|
| 47 |
+
const InkPictureConfigContext = createContext<InkPictureConfig>(defaultConfig);
|
| 48 |
+
|
| 49 |
+
export function useInkPictureConfig(): InkPictureConfig {
|
| 50 |
+
return useContext(InkPictureConfigContext);
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
const ImageCacheContext = createContext<ImageCache | null>(null);
|
| 54 |
+
|
| 55 |
+
export function useImageCache(): ImageCache | null {
|
| 56 |
+
return useContext(ImageCacheContext);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
type RenderListener = () => void;
|
| 60 |
+
const RenderListenerContext = createContext<Set<RenderListener> | null>(null);
|
| 61 |
+
|
| 62 |
+
export function useOnRender(cb: RenderListener): void {
|
| 63 |
+
const listeners = useContext(RenderListenerContext);
|
| 64 |
+
useEffect(() => {
|
| 65 |
+
if (listeners === null) return;
|
| 66 |
+
listeners.add(cb);
|
| 67 |
+
return () => {
|
| 68 |
+
listeners.delete(cb);
|
| 69 |
+
};
|
| 70 |
+
}, [cb, listeners]);
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
export interface TerminalInfo {
|
| 74 |
+
/** Terminal viewport width in pixels */
|
| 75 |
+
terminalWidth: number;
|
| 76 |
+
/** Terminal viewport height in pixels */
|
| 77 |
+
terminalHeight: number;
|
| 78 |
+
/** Width of a single character cell in pixels */
|
| 79 |
+
cellWidth: number;
|
| 80 |
+
/** Height of a single character cell in pixels */
|
| 81 |
+
cellHeight: number;
|
| 82 |
+
/** Whether the terminal supports Unicode characters */
|
| 83 |
+
supportsUnicode: boolean;
|
| 84 |
+
/** Whether the terminal supports color output */
|
| 85 |
+
supportsColor: boolean;
|
| 86 |
+
/** Whether the terminal supports Sixel graphics protocol */
|
| 87 |
+
supportsSixelGraphics: boolean;
|
| 88 |
+
/** Whether the terminal supports Kitty graphics protocol */
|
| 89 |
+
supportsKittyGraphics: boolean;
|
| 90 |
+
/** Whether the terminal supports iTerm2 inline images */
|
| 91 |
+
supportsITerm2Graphics: boolean;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
export const defaultTerminalInfo: TerminalInfo = {
|
| 95 |
+
terminalWidth: 6 * process.stdout.columns,
|
| 96 |
+
terminalHeight: 12 * process.stdout.rows,
|
| 97 |
+
cellWidth: 6,
|
| 98 |
+
cellHeight: 12,
|
| 99 |
+
supportsUnicode: false,
|
| 100 |
+
supportsColor: false,
|
| 101 |
+
supportsSixelGraphics: false,
|
| 102 |
+
supportsKittyGraphics: false,
|
| 103 |
+
supportsITerm2Graphics: false,
|
| 104 |
+
};
|
| 105 |
+
|
| 106 |
+
export const TerminalInfoContext = createContext<TerminalInfo | undefined>(
|
| 107 |
+
undefined,
|
| 108 |
+
);
|
| 109 |
+
|
| 110 |
+
export const useTerminalInfo = () => {
|
| 111 |
+
const terminalInfo = useContext(TerminalInfoContext);
|
| 112 |
+
|
| 113 |
+
return terminalInfo ?? defaultTerminalInfo;
|
| 114 |
+
};
|
| 115 |
+
|
| 116 |
+
function supportsITerm2(context?: {
|
| 117 |
+
supportsSixelGraphics: boolean;
|
| 118 |
+
hasITermCellSizeResponse: boolean;
|
| 119 |
+
}) {
|
| 120 |
+
if (context?.hasITermCellSizeResponse) return true;
|
| 121 |
+
if (process.env.TERM_PROGRAM === "iTerm.app") {
|
| 122 |
+
const version = iterm2Version();
|
| 123 |
+
if (!version || Number(version[0]) < 3) return false;
|
| 124 |
+
return true;
|
| 125 |
+
} else if (process.env.TERM_PROGRAM === "WezTerm") {
|
| 126 |
+
// WezTerm is compatible with iTerm2 inline images starting from version 20220319-142410-0fcdea07
|
| 127 |
+
// See https://wezterm.org/imgcat.html
|
| 128 |
+
const version = process.env.TERM_PROGRAM_VERSION;
|
| 129 |
+
if (!version) return false;
|
| 130 |
+
const date = parseInt(version.split("-")[0], 10);
|
| 131 |
+
if (!Number.isNaN(date) && date >= 20220319) {
|
| 132 |
+
return true;
|
| 133 |
+
}
|
| 134 |
+
} else if (process.env.KONSOLE_VERSION) {
|
| 135 |
+
// Konsole supports iTerm2 inline images since some time around 2022
|
| 136 |
+
// See https://www.reddit.com/r/kde/comments/ul0irg/konsole_2204_with_sixel_support_is_out_of_beta_now/
|
| 137 |
+
const version = process.env.KONSOLE_VERSION;
|
| 138 |
+
if (!version) return false;
|
| 139 |
+
const date = parseInt(version, 10);
|
| 140 |
+
if (!Number.isNaN(date) && date >= 220400) {
|
| 141 |
+
return true;
|
| 142 |
+
}
|
| 143 |
+
} else if (process.env.TERM_PROGRAM === "rio") {
|
| 144 |
+
// Rio terminal supports iTerm2 inline images since version 0.1.13
|
| 145 |
+
// See https://github.com/raphamorim/rio/releases/tag/v0.1.13
|
| 146 |
+
const version = process.env.TERM_PROGRAM_VERSION;
|
| 147 |
+
if (!version) return false;
|
| 148 |
+
const [major, minor, patch] = version
|
| 149 |
+
.split(".")
|
| 150 |
+
.map((v) => parseInt(v, 10));
|
| 151 |
+
if (
|
| 152 |
+
!Number.isNaN(major) &&
|
| 153 |
+
!Number.isNaN(minor) &&
|
| 154 |
+
!Number.isNaN(patch) &&
|
| 155 |
+
(major > 0 ||
|
| 156 |
+
(major === 0 && minor > 1) ||
|
| 157 |
+
(major === 0 && minor === 1 && patch >= 13))
|
| 158 |
+
) {
|
| 159 |
+
return true;
|
| 160 |
+
}
|
| 161 |
+
} else if (process.env.TERM_PROGRAM === "vscode") {
|
| 162 |
+
// VS Code's integrated terminal can be configured to support Sixel and iTerm2 graphics
|
| 163 |
+
// If Sixel is supported, iTerm2 images are also supported
|
| 164 |
+
if (context?.supportsSixelGraphics) {
|
| 165 |
+
return true;
|
| 166 |
+
}
|
| 167 |
+
} else if (process.env.TERM_PROGRAM === "WarpTerminal") {
|
| 168 |
+
const version = process.env.TERM_PROGRAM_VERSION;
|
| 169 |
+
// Supported since v0.2025.03.05.08.02
|
| 170 |
+
// See https://docs.warp.dev/getting-started/changelog
|
| 171 |
+
if (version) {
|
| 172 |
+
const [, year, month, day] = version
|
| 173 |
+
.slice(1)
|
| 174 |
+
.split(".")
|
| 175 |
+
.map((v) => parseInt(v, 10));
|
| 176 |
+
if (
|
| 177 |
+
!Number.isNaN(year) &&
|
| 178 |
+
!Number.isNaN(month) &&
|
| 179 |
+
!Number.isNaN(day) &&
|
| 180 |
+
(year > 2025 ||
|
| 181 |
+
(year === 2025 && month > 3) ||
|
| 182 |
+
(year === 2025 && month === 3 && day >= 5))
|
| 183 |
+
) {
|
| 184 |
+
return true;
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
}
|
| 188 |
+
return false;
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
interface InkPictureProviderProps {
|
| 192 |
+
children: React.ReactNode;
|
| 193 |
+
config?: Partial<InkPictureConfig>;
|
| 194 |
+
terminalInfo?: Partial<TerminalInfo>;
|
| 195 |
+
onTerminalInfoDetection?: (terminalInfo: TerminalInfo) => void;
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
export function InkPictureProvider({
|
| 199 |
+
children,
|
| 200 |
+
config,
|
| 201 |
+
terminalInfo: overrides,
|
| 202 |
+
onTerminalInfoDetection,
|
| 203 |
+
}: InkPictureProviderProps) {
|
| 204 |
+
const { stdin, setRawMode } = useStdin();
|
| 205 |
+
const [terminalInfo, setTerminalInfo] = useState<TerminalInfo | undefined>(
|
| 206 |
+
undefined,
|
| 207 |
+
);
|
| 208 |
+
|
| 209 |
+
const resolvedConfig: InkPictureConfig = useMemo(() => {
|
| 210 |
+
const c: InkPictureConfig = {
|
| 211 |
+
pollIntervalMs: resolveConfig(
|
| 212 |
+
"INK_PICTURE_POLL_INTERVAL",
|
| 213 |
+
config?.pollIntervalMs,
|
| 214 |
+
16,
|
| 215 |
+
1,
|
| 216 |
+
),
|
| 217 |
+
paintIntervalMs: resolveConfig(
|
| 218 |
+
"INK_PICTURE_PAINT_INTERVAL",
|
| 219 |
+
config?.paintIntervalMs,
|
| 220 |
+
16,
|
| 221 |
+
1,
|
| 222 |
+
),
|
| 223 |
+
cacheSize: resolveConfig(
|
| 224 |
+
"INK_PICTURE_CACHE_SIZE",
|
| 225 |
+
config?.cacheSize,
|
| 226 |
+
10,
|
| 227 |
+
0,
|
| 228 |
+
),
|
| 229 |
+
};
|
| 230 |
+
return c;
|
| 231 |
+
}, [config]);
|
| 232 |
+
|
| 233 |
+
useEffect(() => {
|
| 234 |
+
const controller = new AbortController();
|
| 235 |
+
|
| 236 |
+
const queryTerminalInfo = async () => {
|
| 237 |
+
const result = await queryTerminal(stdin, setRawMode, controller.signal);
|
| 238 |
+
|
| 239 |
+
const info = { ...defaultTerminalInfo, ...overrides };
|
| 240 |
+
|
| 241 |
+
if (result.cellWidth && result.cellHeight) {
|
| 242 |
+
info.cellWidth = result.cellWidth;
|
| 243 |
+
info.cellHeight = result.cellHeight;
|
| 244 |
+
} else if (result.terminalWidth && result.terminalHeight) {
|
| 245 |
+
info.cellWidth = result.terminalWidth / process.stdout.columns;
|
| 246 |
+
info.cellHeight = result.terminalHeight / process.stdout.rows;
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
info.supportsUnicode = checkIsUnicodeSupported();
|
| 250 |
+
info.supportsColor = !!supportsColor.stdout;
|
| 251 |
+
|
| 252 |
+
const supportsITerm2Graphics = supportsITerm2({
|
| 253 |
+
hasITermCellSizeResponse: result.iterm2CellWidth !== undefined,
|
| 254 |
+
supportsSixelGraphics: result.supportsSixelGraphics,
|
| 255 |
+
});
|
| 256 |
+
info.supportsSixelGraphics = result.supportsSixelGraphics;
|
| 257 |
+
info.supportsKittyGraphics = result.supportsKittyGraphics;
|
| 258 |
+
info.supportsITerm2Graphics = supportsITerm2Graphics;
|
| 259 |
+
|
| 260 |
+
// Apply iTerm2 cell size override for HiDPI scaling
|
| 261 |
+
// See https://iterm2.com/documentation-escape-codes.html#report-cell-size
|
| 262 |
+
if (
|
| 263 |
+
result.iterm2CellWidth !== undefined &&
|
| 264 |
+
result.iterm2CellHeight !== undefined &&
|
| 265 |
+
result.iterm2Scale !== undefined
|
| 266 |
+
) {
|
| 267 |
+
info.cellWidth = result.iterm2CellWidth * result.iterm2Scale;
|
| 268 |
+
info.cellHeight = result.iterm2CellHeight * result.iterm2Scale;
|
| 269 |
+
info.terminalWidth = info.cellWidth * process.stdout.columns;
|
| 270 |
+
info.terminalHeight = info.cellHeight * process.stdout.rows;
|
| 271 |
+
}
|
| 272 |
+
setTerminalInfo(info);
|
| 273 |
+
onTerminalInfoDetection?.(info);
|
| 274 |
+
};
|
| 275 |
+
queryTerminalInfo();
|
| 276 |
+
|
| 277 |
+
return () => controller.abort();
|
| 278 |
+
}, [stdin, setRawMode, overrides, onTerminalInfoDetection]);
|
| 279 |
+
|
| 280 |
+
const resolvedInfo = useMemo(
|
| 281 |
+
() => ({ ...defaultTerminalInfo, ...terminalInfo, ...overrides }),
|
| 282 |
+
[terminalInfo, overrides],
|
| 283 |
+
);
|
| 284 |
+
|
| 285 |
+
const { current: cache } = useRef(
|
| 286 |
+
resolvedConfig.cacheSize === 0
|
| 287 |
+
? null
|
| 288 |
+
: new ImageCache(resolvedConfig.cacheSize),
|
| 289 |
+
);
|
| 290 |
+
|
| 291 |
+
const renderListeners = useMemo(() => new Set<() => void>(), []);
|
| 292 |
+
|
| 293 |
+
const handleRender = useCallback(() => {
|
| 294 |
+
for (const cb of renderListeners) {
|
| 295 |
+
cb();
|
| 296 |
+
}
|
| 297 |
+
}, [renderListeners]);
|
| 298 |
+
|
| 299 |
+
if (!terminalInfo && !overrides) {
|
| 300 |
+
return null;
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
return (
|
| 304 |
+
<InkPictureConfigContext.Provider value={resolvedConfig}>
|
| 305 |
+
<ImageCacheContext.Provider value={cache}>
|
| 306 |
+
<TerminalInfoContext.Provider value={resolvedInfo}>
|
| 307 |
+
<RenderListenerContext.Provider value={renderListeners}>
|
| 308 |
+
<Profiler id="ink-picture" onRender={handleRender}>
|
| 309 |
+
{children}
|
| 310 |
+
</Profiler>
|
| 311 |
+
</RenderListenerContext.Provider>
|
| 312 |
+
</TerminalInfoContext.Provider>
|
| 313 |
+
</ImageCacheContext.Provider>
|
| 314 |
+
</InkPictureConfigContext.Provider>
|
| 315 |
+
);
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
export const TerminalInfoProvider = InkPictureProvider;
|
src/ink-picture/__tests__/LocalImageShow.test.ts
ADDED
|
File without changes
|
src/ink-picture/components/ImageBox.tsx
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Box, type DOMElement, Newline, Text } from "src/ink";
|
| 2 |
+
import React, { forwardRef } from "react";
|
| 3 |
+
|
| 4 |
+
export interface ImageBoxProps {
|
| 5 |
+
width: number | string;
|
| 6 |
+
height: number | string;
|
| 7 |
+
alt?: string;
|
| 8 |
+
error?: boolean;
|
| 9 |
+
loaded?: boolean;
|
| 10 |
+
children?: React.ReactNode;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
const ImageBox = forwardRef<DOMElement, ImageBoxProps>(function ImageBox(
|
| 14 |
+
{ width, height, alt, error, loaded, children },
|
| 15 |
+
ref,
|
| 16 |
+
) {
|
| 17 |
+
return (
|
| 18 |
+
<Box ref={ref} flexDirection="column" width={width} height={height}>
|
| 19 |
+
{loaded && children ? (
|
| 20 |
+
children
|
| 21 |
+
) : (
|
| 22 |
+
<Box
|
| 23 |
+
flexDirection="column"
|
| 24 |
+
alignItems="center"
|
| 25 |
+
justifyContent="center"
|
| 26 |
+
overflow="hidden"
|
| 27 |
+
>
|
| 28 |
+
{alt ? (
|
| 29 |
+
<Text color="gray">{alt}</Text>
|
| 30 |
+
) : error ? (
|
| 31 |
+
<Text color="red">
|
| 32 |
+
X<Newline />
|
| 33 |
+
Load failed
|
| 34 |
+
</Text>
|
| 35 |
+
) : (
|
| 36 |
+
<Text color="gray">Loading...</Text>
|
| 37 |
+
)}
|
| 38 |
+
</Box>
|
| 39 |
+
)}
|
| 40 |
+
</Box>
|
| 41 |
+
);
|
| 42 |
+
});
|
| 43 |
+
|
| 44 |
+
export default ImageBox;
|
src/ink-picture/components/image/Ascii.tsx
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Text } from "src/ink";
|
| 2 |
+
import React, { useMemo } from "react";
|
| 3 |
+
import { useImage } from "../../hooks/useImage.js";
|
| 4 |
+
import { useMeasuredSize } from "../../hooks/useMeasuredSize.js";
|
| 5 |
+
import { useTerminalInfo } from "../../InkPictureProvider.js";
|
| 6 |
+
import { renderAscii } from "../../renderers/ascii.js";
|
| 7 |
+
import ImageBox from "../ImageBox.js";
|
| 8 |
+
import type { ImageProps } from "./protocol.js";
|
| 9 |
+
|
| 10 |
+
function AsciiImage(props: ImageProps) {
|
| 11 |
+
const terminalInfo = useTerminalInfo();
|
| 12 |
+
const { src, width, height, alt } = props;
|
| 13 |
+
|
| 14 |
+
const { containerRef, resolvedWidth, resolvedHeight } = useMeasuredSize(
|
| 15 |
+
width,
|
| 16 |
+
height,
|
| 17 |
+
);
|
| 18 |
+
|
| 19 |
+
const { imageData, error } = useImage({
|
| 20 |
+
src,
|
| 21 |
+
pixelWidth: resolvedWidth,
|
| 22 |
+
pixelHeight: resolvedHeight,
|
| 23 |
+
mode: "pixels",
|
| 24 |
+
});
|
| 25 |
+
|
| 26 |
+
const imageOutput = useMemo(() => {
|
| 27 |
+
if (!imageData) return null;
|
| 28 |
+
return renderAscii(imageData, { colored: terminalInfo?.supportsColor });
|
| 29 |
+
}, [imageData, terminalInfo?.supportsColor]);
|
| 30 |
+
|
| 31 |
+
return (
|
| 32 |
+
<ImageBox
|
| 33 |
+
ref={containerRef}
|
| 34 |
+
width={width}
|
| 35 |
+
height={height}
|
| 36 |
+
alt={alt}
|
| 37 |
+
error={error}
|
| 38 |
+
loaded={!!imageOutput}
|
| 39 |
+
>
|
| 40 |
+
{imageOutput?.split("\n").map((line, idx) => (
|
| 41 |
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, won't change
|
| 42 |
+
<Text key={`${line}-${idx}`}>{line}</Text>
|
| 43 |
+
))}
|
| 44 |
+
</ImageBox>
|
| 45 |
+
);
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
export default AsciiImage;
|
src/ink-picture/components/image/Braille.tsx
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Text } from "src/ink";
|
| 2 |
+
import React, { useMemo } from "react";
|
| 3 |
+
import { useImage } from "../../hooks/useImage.js";
|
| 4 |
+
import { useMeasuredSize } from "../../hooks/useMeasuredSize.js";
|
| 5 |
+
import { renderBraille } from "../../renderers/braille.js";
|
| 6 |
+
import ImageBox from "../ImageBox.js";
|
| 7 |
+
import type { ImageProps } from "./protocol.js";
|
| 8 |
+
|
| 9 |
+
function BrailleImage(props: ImageProps) {
|
| 10 |
+
const { src, width, height, alt } = props;
|
| 11 |
+
|
| 12 |
+
const { containerRef, resolvedWidth, resolvedHeight } = useMeasuredSize(
|
| 13 |
+
width,
|
| 14 |
+
height,
|
| 15 |
+
);
|
| 16 |
+
|
| 17 |
+
const { imageData, error } = useImage({
|
| 18 |
+
src,
|
| 19 |
+
pixelWidth: resolvedWidth * 2,
|
| 20 |
+
pixelHeight: resolvedHeight * 4,
|
| 21 |
+
mode: "pixels",
|
| 22 |
+
});
|
| 23 |
+
|
| 24 |
+
const imageOutput = useMemo(() => {
|
| 25 |
+
if (!imageData) return null;
|
| 26 |
+
return renderBraille(imageData);
|
| 27 |
+
}, [imageData]);
|
| 28 |
+
|
| 29 |
+
return (
|
| 30 |
+
<ImageBox
|
| 31 |
+
ref={containerRef}
|
| 32 |
+
width={width}
|
| 33 |
+
height={height}
|
| 34 |
+
alt={alt}
|
| 35 |
+
error={error}
|
| 36 |
+
loaded={!!imageOutput}
|
| 37 |
+
>
|
| 38 |
+
{imageOutput?.split("\n").map((line, idx) => (
|
| 39 |
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, won't change
|
| 40 |
+
<Text key={`${line}-${idx}`}>{line}</Text>
|
| 41 |
+
))}
|
| 42 |
+
</ImageBox>
|
| 43 |
+
);
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
export default BrailleImage;
|
src/ink-picture/components/image/HalfBlock.tsx
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Text } from "src/ink";
|
| 2 |
+
import React, { useMemo } from "react";
|
| 3 |
+
import { useImage } from "../../hooks/useImage.js";
|
| 4 |
+
import { useMeasuredSize } from "../../hooks/useMeasuredSize.js";
|
| 5 |
+
import { renderHalfBlock } from "../../renderers/halfBlock.js";
|
| 6 |
+
import ImageBox from "../ImageBox.js";
|
| 7 |
+
import type { ImageProps } from "./protocol.js";
|
| 8 |
+
|
| 9 |
+
function HalfBlockImage(props: ImageProps) {
|
| 10 |
+
const { src, width, height, alt } = props;
|
| 11 |
+
|
| 12 |
+
const { containerRef, resolvedWidth, resolvedHeight } = useMeasuredSize(
|
| 13 |
+
width,
|
| 14 |
+
height,
|
| 15 |
+
);
|
| 16 |
+
|
| 17 |
+
const { imageData, error } = useImage({
|
| 18 |
+
src,
|
| 19 |
+
pixelWidth: resolvedWidth,
|
| 20 |
+
pixelHeight: resolvedHeight * 2,
|
| 21 |
+
mode: "pixels",
|
| 22 |
+
});
|
| 23 |
+
|
| 24 |
+
const imageOutput = useMemo(() => {
|
| 25 |
+
if (!imageData) return null;
|
| 26 |
+
return renderHalfBlock(imageData);
|
| 27 |
+
}, [imageData]);
|
| 28 |
+
|
| 29 |
+
return (
|
| 30 |
+
<ImageBox
|
| 31 |
+
ref={containerRef}
|
| 32 |
+
width={width}
|
| 33 |
+
height={height}
|
| 34 |
+
alt={alt}
|
| 35 |
+
error={error}
|
| 36 |
+
loaded={!!imageOutput}
|
| 37 |
+
>
|
| 38 |
+
{imageOutput?.split("\n").map((line, idx) => (
|
| 39 |
+
// biome-ignore lint/suspicious/noArrayIndexKey: static content, won't change
|
| 40 |
+
<Text key={`${line}-${idx}`}>{line}</Text>
|
| 41 |
+
))}
|
| 42 |
+
</ImageBox>
|
| 43 |
+
);
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
export default HalfBlockImage;
|
src/ink-picture/components/image/ITerm2.tsx
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useStdout } from "src/ink";
|
| 2 |
+
import React, { useMemo } from "react";
|
| 3 |
+
import useBackgroundColor from "../../hooks/useBackgroundColor.js";
|
| 4 |
+
import { useDirectRenderer } from "../../hooks/useDirectRenderer.js";
|
| 5 |
+
import { useImage } from "../../hooks/useImage.js";
|
| 6 |
+
import { useMeasuredSize } from "../../hooks/useMeasuredSize.js";
|
| 7 |
+
import usePosition from "../../hooks/usePosition.js";
|
| 8 |
+
import { useTerminalInfo } from "../../InkPictureProvider.js";
|
| 9 |
+
import { renderITerm2 } from "../../renderers/iterm2.js";
|
| 10 |
+
import ImageBox from "../ImageBox.js";
|
| 11 |
+
import type { ImageProps } from "./protocol.js";
|
| 12 |
+
|
| 13 |
+
function ITerm2Image(props: ImageProps) {
|
| 14 |
+
const terminalInfo = useTerminalInfo();
|
| 15 |
+
const { stdout } = useStdout();
|
| 16 |
+
const { src, width, height, alt } = props;
|
| 17 |
+
|
| 18 |
+
const { containerRef, resolvedWidth, resolvedHeight } = useMeasuredSize(
|
| 19 |
+
width,
|
| 20 |
+
height,
|
| 21 |
+
);
|
| 22 |
+
|
| 23 |
+
const componentPosition = usePosition(containerRef);
|
| 24 |
+
const inheritedBackgroundColor = useBackgroundColor(containerRef);
|
| 25 |
+
|
| 26 |
+
const pixelWidth = resolvedWidth * (terminalInfo?.cellWidth ?? 0);
|
| 27 |
+
const pixelHeight = resolvedHeight * (terminalInfo?.cellHeight ?? 0);
|
| 28 |
+
|
| 29 |
+
const { imageData, error } = useImage({
|
| 30 |
+
src,
|
| 31 |
+
pixelWidth,
|
| 32 |
+
pixelHeight,
|
| 33 |
+
mode: "png",
|
| 34 |
+
});
|
| 35 |
+
|
| 36 |
+
const imageOutput = useMemo(() => {
|
| 37 |
+
if (!imageData) return undefined;
|
| 38 |
+
return renderITerm2(imageData, { width: pixelWidth, height: pixelHeight });
|
| 39 |
+
}, [imageData, pixelWidth, pixelHeight]);
|
| 40 |
+
|
| 41 |
+
useDirectRenderer({
|
| 42 |
+
enabled: !!imageOutput && !!componentPosition,
|
| 43 |
+
imageOutput: imageOutput ?? "",
|
| 44 |
+
position: componentPosition,
|
| 45 |
+
stdout,
|
| 46 |
+
width: resolvedWidth,
|
| 47 |
+
height: resolvedHeight,
|
| 48 |
+
backgroundColor: inheritedBackgroundColor,
|
| 49 |
+
});
|
| 50 |
+
|
| 51 |
+
return (
|
| 52 |
+
<ImageBox
|
| 53 |
+
ref={containerRef}
|
| 54 |
+
width={width}
|
| 55 |
+
height={height}
|
| 56 |
+
alt={alt}
|
| 57 |
+
error={error}
|
| 58 |
+
loaded={!!imageOutput}
|
| 59 |
+
/>
|
| 60 |
+
);
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
export default ITerm2Image;
|
src/ink-picture/components/image/Kitty.tsx
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useStdout } from "src/ink";
|
| 2 |
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
| 3 |
+
import { useImage } from "../../hooks/useImage.js";
|
| 4 |
+
import { useMeasuredSize } from "../../hooks/useMeasuredSize.js";
|
| 5 |
+
import usePosition from "../../hooks/usePosition.js";
|
| 6 |
+
import { defaultVisibility } from "../../hooks/useVisibility.js";
|
| 7 |
+
import { useTerminalInfo } from "../../InkPictureProvider.js";
|
| 8 |
+
import {
|
| 9 |
+
makeKittyDeletion,
|
| 10 |
+
makeKittyPlacement,
|
| 11 |
+
makeKittyTransmitChunks,
|
| 12 |
+
} from "../../renderers/kitty.js";
|
| 13 |
+
import { cursorForward, cursorUp } from "../../utils/ansiEscapes.js";
|
| 14 |
+
import generateKittyId from "../../utils/generateKittyId.js";
|
| 15 |
+
import ImageBox from "../ImageBox.js";
|
| 16 |
+
import type { ImageProps } from "./protocol.js";
|
| 17 |
+
|
| 18 |
+
function KittyImage(props: ImageProps) {
|
| 19 |
+
const terminalInfo = useTerminalInfo();
|
| 20 |
+
const { stdout } = useStdout();
|
| 21 |
+
const { src, width, height, alt } = props;
|
| 22 |
+
|
| 23 |
+
const { containerRef, resolvedWidth, resolvedHeight } = useMeasuredSize(
|
| 24 |
+
width,
|
| 25 |
+
height,
|
| 26 |
+
);
|
| 27 |
+
|
| 28 |
+
const componentPosition = usePosition(containerRef);
|
| 29 |
+
|
| 30 |
+
const pixelWidth = resolvedWidth * (terminalInfo?.cellWidth ?? 0);
|
| 31 |
+
const pixelHeight = resolvedHeight * (terminalInfo?.cellHeight ?? 0);
|
| 32 |
+
|
| 33 |
+
const { imageData, error } = useImage({
|
| 34 |
+
src,
|
| 35 |
+
pixelWidth,
|
| 36 |
+
pixelHeight,
|
| 37 |
+
mode: "png",
|
| 38 |
+
});
|
| 39 |
+
|
| 40 |
+
const [imageId, setImageId] = useState<number | undefined>(undefined);
|
| 41 |
+
const shouldCleanupRef = useRef(true);
|
| 42 |
+
const wasPlacedRef = useRef(false);
|
| 43 |
+
|
| 44 |
+
useEffect(() => {
|
| 45 |
+
if (!imageData) return;
|
| 46 |
+
|
| 47 |
+
const id = generateKittyId();
|
| 48 |
+
const base64Data = imageData.data.toString("base64");
|
| 49 |
+
const chunks = makeKittyTransmitChunks(id, base64Data);
|
| 50 |
+
for (const chunk of chunks) {
|
| 51 |
+
stdout.write(chunk);
|
| 52 |
+
}
|
| 53 |
+
setImageId(id);
|
| 54 |
+
}, [imageData, stdout.write]);
|
| 55 |
+
|
| 56 |
+
useEffect(() => {
|
| 57 |
+
if (!imageId) return;
|
| 58 |
+
if (!componentPosition) return;
|
| 59 |
+
|
| 60 |
+
if (
|
| 61 |
+
defaultVisibility(componentPosition, stdout.rows, stdout.columns) !==
|
| 62 |
+
"full"
|
| 63 |
+
) {
|
| 64 |
+
if (wasPlacedRef.current) {
|
| 65 |
+
stdout.write(makeKittyDeletion(imageId, 1));
|
| 66 |
+
wasPlacedRef.current = false;
|
| 67 |
+
}
|
| 68 |
+
return;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
stdout.write("\x1b7");
|
| 72 |
+
stdout.write(
|
| 73 |
+
cursorUp(componentPosition.appHeight - componentPosition.row, {
|
| 74 |
+
appHeight: componentPosition.appHeight,
|
| 75 |
+
terminalHeight: stdout.rows,
|
| 76 |
+
}),
|
| 77 |
+
);
|
| 78 |
+
stdout.write("\r");
|
| 79 |
+
stdout.write(cursorForward(componentPosition.col));
|
| 80 |
+
|
| 81 |
+
stdout.write(makeKittyPlacement(imageId, 1));
|
| 82 |
+
|
| 83 |
+
stdout.write("\x1b8");
|
| 84 |
+
|
| 85 |
+
wasPlacedRef.current = true;
|
| 86 |
+
});
|
| 87 |
+
|
| 88 |
+
const onExit = useCallback(() => {
|
| 89 |
+
shouldCleanupRef.current = false;
|
| 90 |
+
}, []);
|
| 91 |
+
|
| 92 |
+
const onSigInt = useCallback(() => {
|
| 93 |
+
shouldCleanupRef.current = false;
|
| 94 |
+
process.exit();
|
| 95 |
+
}, []);
|
| 96 |
+
|
| 97 |
+
useEffect(() => {
|
| 98 |
+
process.on("exit", onExit);
|
| 99 |
+
process.on("SIGINT", onSigInt);
|
| 100 |
+
process.on("SIGTERM", onSigInt);
|
| 101 |
+
|
| 102 |
+
return () => {
|
| 103 |
+
process.removeListener("exit", onExit);
|
| 104 |
+
process.removeListener("SIGINT", onSigInt);
|
| 105 |
+
process.removeListener("SIGTERM", onSigInt);
|
| 106 |
+
if (!shouldCleanupRef.current) return;
|
| 107 |
+
if (!imageId) return;
|
| 108 |
+
|
| 109 |
+
// We always delete during unmount regardless of whether the image is placed
|
| 110 |
+
// in order to remove the image data from the terminal
|
| 111 |
+
stdout.write(makeKittyDeletion(imageId));
|
| 112 |
+
};
|
| 113 |
+
}, [imageId, onExit, onSigInt, stdout.write]);
|
| 114 |
+
|
| 115 |
+
return (
|
| 116 |
+
<ImageBox
|
| 117 |
+
ref={containerRef}
|
| 118 |
+
width={width}
|
| 119 |
+
height={height}
|
| 120 |
+
alt={alt}
|
| 121 |
+
error={error}
|
| 122 |
+
loaded={!!imageId}
|
| 123 |
+
/>
|
| 124 |
+
);
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
export default KittyImage;
|
src/ink-picture/components/image/Sixel.tsx
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useStdout } from "src/ink";
|
| 2 |
+
import React, { useMemo } from "react";
|
| 3 |
+
import useBackgroundColor from "../../hooks/useBackgroundColor.js";
|
| 4 |
+
import { useDirectRenderer } from "../../hooks/useDirectRenderer.js";
|
| 5 |
+
import { useImage } from "../../hooks/useImage.js";
|
| 6 |
+
import { useMeasuredSize } from "../../hooks/useMeasuredSize.js";
|
| 7 |
+
import usePosition from "../../hooks/usePosition.js";
|
| 8 |
+
import { useTerminalInfo } from "../../InkPictureProvider.js";
|
| 9 |
+
import { renderSixel } from "../../renderers/sixel.js";
|
| 10 |
+
import ImageBox from "../ImageBox.js";
|
| 11 |
+
import type { ImageProps } from "./protocol.js";
|
| 12 |
+
|
| 13 |
+
function SixelImage(props: ImageProps) {
|
| 14 |
+
const terminalInfo = useTerminalInfo();
|
| 15 |
+
const { stdout } = useStdout();
|
| 16 |
+
const { src, width, height, alt } = props;
|
| 17 |
+
|
| 18 |
+
const { containerRef, resolvedWidth, resolvedHeight } = useMeasuredSize(
|
| 19 |
+
width,
|
| 20 |
+
height,
|
| 21 |
+
);
|
| 22 |
+
|
| 23 |
+
const componentPosition = usePosition(containerRef);
|
| 24 |
+
const inheritedBackgroundColor = useBackgroundColor(containerRef);
|
| 25 |
+
|
| 26 |
+
const pixelWidth = resolvedWidth * (terminalInfo?.cellWidth ?? 0);
|
| 27 |
+
const pixelHeight = resolvedHeight * (terminalInfo?.cellHeight ?? 0);
|
| 28 |
+
|
| 29 |
+
const { imageData, error } = useImage({
|
| 30 |
+
src,
|
| 31 |
+
pixelWidth,
|
| 32 |
+
pixelHeight,
|
| 33 |
+
mode: "pixels",
|
| 34 |
+
});
|
| 35 |
+
|
| 36 |
+
const imageOutput = useMemo(() => {
|
| 37 |
+
if (!imageData) return undefined;
|
| 38 |
+
return renderSixel(imageData);
|
| 39 |
+
}, [imageData]);
|
| 40 |
+
|
| 41 |
+
useDirectRenderer({
|
| 42 |
+
enabled: !!imageOutput && !!componentPosition,
|
| 43 |
+
imageOutput: imageOutput ?? "",
|
| 44 |
+
position: componentPosition,
|
| 45 |
+
stdout,
|
| 46 |
+
width: resolvedWidth,
|
| 47 |
+
height: resolvedHeight,
|
| 48 |
+
backgroundColor: inheritedBackgroundColor,
|
| 49 |
+
});
|
| 50 |
+
|
| 51 |
+
return (
|
| 52 |
+
<ImageBox
|
| 53 |
+
ref={containerRef}
|
| 54 |
+
width={width}
|
| 55 |
+
height={height}
|
| 56 |
+
alt={alt}
|
| 57 |
+
error={error}
|
| 58 |
+
loaded={!!imageOutput}
|
| 59 |
+
/>
|
| 60 |
+
);
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
export default SixelImage;
|
src/ink-picture/components/image/index.tsx
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Box, useIsScreenReaderEnabled, useStdout } from "src/ink";
|
| 2 |
+
import React, { useMemo } from "react";
|
| 3 |
+
import { useMeasuredSize } from "../../hooks/useMeasuredSize.js";
|
| 4 |
+
import usePosition from "../../hooks/usePosition.js";
|
| 5 |
+
import useProtocol from "../../hooks/useProtocol.js";
|
| 6 |
+
import type { GetVisibility, Visibility } from "../../hooks/useVisibility.js";
|
| 7 |
+
import { useVisibility } from "../../hooks/useVisibility.js";
|
| 8 |
+
import { useTerminalInfo } from "../../InkPictureProvider.js";
|
| 9 |
+
import AsciiImage from "./Ascii.js";
|
| 10 |
+
import BrailleImage from "./Braille.js";
|
| 11 |
+
import HalfBlockImage from "./HalfBlock.js";
|
| 12 |
+
import ITerm2Image from "./ITerm2.js";
|
| 13 |
+
import KittyImage from "./Kitty.js";
|
| 14 |
+
import type { ImageProps } from "./protocol.js";
|
| 15 |
+
import SixelImage from "./Sixel.js";
|
| 16 |
+
|
| 17 |
+
const imageProtocols = {
|
| 18 |
+
ascii: AsciiImage,
|
| 19 |
+
braille: BrailleImage,
|
| 20 |
+
halfBlock: HalfBlockImage,
|
| 21 |
+
iterm2: ITerm2Image,
|
| 22 |
+
kitty: KittyImage,
|
| 23 |
+
sixel: SixelImage,
|
| 24 |
+
};
|
| 25 |
+
|
| 26 |
+
export type ImageProtocolName = keyof typeof imageProtocols;
|
| 27 |
+
|
| 28 |
+
export type ImageProtocolHint = Partial<Record<Visibility, ImageProtocolName>>;
|
| 29 |
+
|
| 30 |
+
const ImageRenderer = (props: ImageProps & { protocol: ImageProtocolName }) => {
|
| 31 |
+
const ProtocolComponent = imageProtocols[props.protocol];
|
| 32 |
+
return <ProtocolComponent {...props} />;
|
| 33 |
+
};
|
| 34 |
+
|
| 35 |
+
type ImageComponentProps = Omit<ImageProps, "width" | "height"> & {
|
| 36 |
+
width?: number | string;
|
| 37 |
+
height?: number | string;
|
| 38 |
+
protocol?: ImageProtocolName | ImageProtocolHint;
|
| 39 |
+
getVisibility?: GetVisibility;
|
| 40 |
+
};
|
| 41 |
+
|
| 42 |
+
function Image({
|
| 43 |
+
protocol: specifiedProtocol,
|
| 44 |
+
getVisibility,
|
| 45 |
+
width = "100%",
|
| 46 |
+
height = "100%",
|
| 47 |
+
...props
|
| 48 |
+
}: ImageComponentProps) {
|
| 49 |
+
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
| 50 |
+
const terminalInfo = useTerminalInfo();
|
| 51 |
+
const { stdout } = useStdout();
|
| 52 |
+
const protocol = useProtocol(
|
| 53 |
+
typeof specifiedProtocol === "string" ? specifiedProtocol : undefined,
|
| 54 |
+
);
|
| 55 |
+
|
| 56 |
+
const { containerRef, resolvedWidth, resolvedHeight } = useMeasuredSize(
|
| 57 |
+
width,
|
| 58 |
+
height,
|
| 59 |
+
);
|
| 60 |
+
|
| 61 |
+
const position = usePosition(containerRef);
|
| 62 |
+
const visibility = useVisibility(
|
| 63 |
+
position,
|
| 64 |
+
stdout.rows,
|
| 65 |
+
stdout.columns,
|
| 66 |
+
getVisibility,
|
| 67 |
+
);
|
| 68 |
+
|
| 69 |
+
const effectiveProtocol = useMemo((): ImageProtocolName => {
|
| 70 |
+
if (typeof specifiedProtocol === "string") return specifiedProtocol;
|
| 71 |
+
|
| 72 |
+
const hints: ImageProtocolHint = specifiedProtocol ?? {};
|
| 73 |
+
const hint = hints[visibility];
|
| 74 |
+
if (hint) return hint;
|
| 75 |
+
|
| 76 |
+
if (visibility === "full") return protocol;
|
| 77 |
+
|
| 78 |
+
if (
|
| 79 |
+
protocol === "ascii" ||
|
| 80 |
+
protocol === "braille" ||
|
| 81 |
+
protocol === "halfBlock"
|
| 82 |
+
)
|
| 83 |
+
return protocol;
|
| 84 |
+
|
| 85 |
+
if (terminalInfo.supportsUnicode && terminalInfo.supportsColor)
|
| 86 |
+
return "halfBlock";
|
| 87 |
+
if (terminalInfo.supportsUnicode) return "braille";
|
| 88 |
+
return "ascii";
|
| 89 |
+
}, [
|
| 90 |
+
visibility,
|
| 91 |
+
protocol,
|
| 92 |
+
specifiedProtocol,
|
| 93 |
+
terminalInfo.supportsUnicode,
|
| 94 |
+
terminalInfo.supportsColor,
|
| 95 |
+
]);
|
| 96 |
+
|
| 97 |
+
if (isScreenReaderEnabled) {
|
| 98 |
+
const { src, alt } = props;
|
| 99 |
+
const label = `image: ${alt || (typeof src === "string" ? src : "binary image data")}`;
|
| 100 |
+
|
| 101 |
+
return (
|
| 102 |
+
<Box
|
| 103 |
+
ref={containerRef}
|
| 104 |
+
width={width}
|
| 105 |
+
height={height}
|
| 106 |
+
aria-label={label}
|
| 107 |
+
flexDirection="column"
|
| 108 |
+
/>
|
| 109 |
+
);
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
return (
|
| 113 |
+
<Box
|
| 114 |
+
ref={containerRef}
|
| 115 |
+
width={width}
|
| 116 |
+
height={height}
|
| 117 |
+
flexDirection="column"
|
| 118 |
+
>
|
| 119 |
+
<ImageRenderer
|
| 120 |
+
protocol={effectiveProtocol}
|
| 121 |
+
key={effectiveProtocol}
|
| 122 |
+
width={resolvedWidth}
|
| 123 |
+
height={resolvedHeight}
|
| 124 |
+
{...props}
|
| 125 |
+
/>
|
| 126 |
+
</Box>
|
| 127 |
+
);
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
export default Image;
|
src/ink-picture/components/image/protocol.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { Jimp } from "jimp";
|
| 2 |
+
import type { JSX } from "react";
|
| 3 |
+
|
| 4 |
+
export interface ImageProps {
|
| 5 |
+
/**
|
| 6 |
+
* The source URL, file path or ArrayBuffer of the image to render
|
| 7 |
+
* Supports all image formats supported by jimp (JPEG, PNG, WebP, GIF, BMP, TIFF).
|
| 8 |
+
*/
|
| 9 |
+
src: Parameters<typeof Jimp.read>[0];
|
| 10 |
+
/** Width in terminal cells or a percentage */
|
| 11 |
+
width: number | string;
|
| 12 |
+
/** Height in terminal cells or a percentage */
|
| 13 |
+
height: number | string;
|
| 14 |
+
/** Alternative text displayed while loading or on error */
|
| 15 |
+
alt?: string;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
/**
|
| 19 |
+
* Interface defining an image rendering protocol.
|
| 20 |
+
*
|
| 21 |
+
* Each protocol represents a different method of displaying images in the terminal,
|
| 22 |
+
* such as ASCII art, Sixel graphics, Braille patterns, or Unicode half-blocks.
|
| 23 |
+
*
|
| 24 |
+
* @interface ImageProtocol
|
| 25 |
+
*/
|
| 26 |
+
export interface ImageProtocol {
|
| 27 |
+
/** Unique identifier for this protocol */
|
| 28 |
+
name: string;
|
| 29 |
+
/** Function that renders the image using this protocol */
|
| 30 |
+
render(props: ImageProps): JSX.Element | null;
|
| 31 |
+
}
|
src/ink-picture/hooks/useBackgroundColor.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { DOMElement } from "src/ink";
|
| 2 |
+
import { type RefObject } from "react";
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Walks up the DOMElement tree from the given ref to find the nearest
|
| 6 |
+
* ancestor Box that has a background color set.
|
| 7 |
+
*
|
| 8 |
+
* This provides the same information as Ink's internal `backgroundContext`.
|
| 9 |
+
*/
|
| 10 |
+
export default function useBackgroundColor(
|
| 11 |
+
ref: RefObject<DOMElement | null>,
|
| 12 |
+
): string | undefined {
|
| 13 |
+
let currentNode: DOMElement | undefined = ref.current ?? undefined;
|
| 14 |
+
while (currentNode) {
|
| 15 |
+
if (currentNode.style?.backgroundColor) {
|
| 16 |
+
return currentNode.style.backgroundColor as string;
|
| 17 |
+
}
|
| 18 |
+
currentNode = currentNode.parentNode;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
return undefined;
|
| 22 |
+
}
|
src/ink-picture/hooks/useDirectRenderer.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useCallback, useEffect, useLayoutEffect, useRef } from "react";
|
| 2 |
+
import { useOnRender } from "../InkPictureProvider.js";
|
| 3 |
+
import { cursorForward, cursorUp } from "../utils/ansiEscapes.js";
|
| 4 |
+
import bgColorize from "../utils/bgColorize.js";
|
| 5 |
+
import type { Position } from "./usePosition.js";
|
| 6 |
+
import { defaultVisibility } from "./useVisibility.js";
|
| 7 |
+
|
| 8 |
+
interface DirectRendererOptions {
|
| 9 |
+
enabled: boolean;
|
| 10 |
+
imageOutput: string;
|
| 11 |
+
position: Position | undefined;
|
| 12 |
+
stdout: NodeJS.WriteStream;
|
| 13 |
+
width: number;
|
| 14 |
+
height: number;
|
| 15 |
+
backgroundColor?: string;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
function writeImageToStdout(
|
| 19 |
+
pos: Position,
|
| 20 |
+
output: string,
|
| 21 |
+
stream: NodeJS.WriteStream,
|
| 22 |
+
w: number,
|
| 23 |
+
h: number,
|
| 24 |
+
): { row: number; col: number; width: number; height: number } {
|
| 25 |
+
stream.write("\x1b7");
|
| 26 |
+
stream.write(
|
| 27 |
+
cursorUp(pos.appHeight - pos.row, {
|
| 28 |
+
appHeight: pos.appHeight,
|
| 29 |
+
terminalHeight: stream.rows,
|
| 30 |
+
}),
|
| 31 |
+
);
|
| 32 |
+
stream.write("\r");
|
| 33 |
+
stream.write(cursorForward(pos.col));
|
| 34 |
+
stream.write(output);
|
| 35 |
+
stream.write("\x1b8");
|
| 36 |
+
|
| 37 |
+
return {
|
| 38 |
+
row: stream.rows - pos.appHeight + pos.row,
|
| 39 |
+
col: pos.col,
|
| 40 |
+
width: w,
|
| 41 |
+
height: h,
|
| 42 |
+
};
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
export function useDirectRenderer(options: DirectRendererOptions) {
|
| 46 |
+
const { position, stdout, backgroundColor } = options;
|
| 47 |
+
const shouldCleanupRef = useRef(true);
|
| 48 |
+
const previousBboxRef = useRef<
|
| 49 |
+
{ row: number; col: number; width: number; height: number } | undefined
|
| 50 |
+
>(undefined);
|
| 51 |
+
|
| 52 |
+
const optionsRef = useRef(options);
|
| 53 |
+
optionsRef.current = options;
|
| 54 |
+
|
| 55 |
+
const writeNow = useCallback(() => {
|
| 56 |
+
const opt = optionsRef.current;
|
| 57 |
+
if (!opt.enabled || !opt.position) return;
|
| 58 |
+
if (
|
| 59 |
+
defaultVisibility(opt.position, opt.stdout.rows, opt.stdout.columns) !==
|
| 60 |
+
"full"
|
| 61 |
+
)
|
| 62 |
+
return;
|
| 63 |
+
|
| 64 |
+
previousBboxRef.current = writeImageToStdout(
|
| 65 |
+
opt.position,
|
| 66 |
+
opt.imageOutput,
|
| 67 |
+
opt.stdout,
|
| 68 |
+
opt.width,
|
| 69 |
+
opt.height,
|
| 70 |
+
);
|
| 71 |
+
}, []);
|
| 72 |
+
|
| 73 |
+
// Repaint after every React render commit, since Ink clears the terminal
|
| 74 |
+
// on each render cycle, wiping any direct-to-stdout image output.
|
| 75 |
+
useOnRender(writeNow);
|
| 76 |
+
|
| 77 |
+
useLayoutEffect(() => {
|
| 78 |
+
return () => {
|
| 79 |
+
if (!shouldCleanupRef.current) return;
|
| 80 |
+
|
| 81 |
+
const bbox = previousBboxRef.current;
|
| 82 |
+
if (!bbox || !position) return;
|
| 83 |
+
|
| 84 |
+
stdout.write("\x1b7");
|
| 85 |
+
stdout.write(
|
| 86 |
+
cursorUp(position.appHeight - position.row, {
|
| 87 |
+
appHeight: position.appHeight,
|
| 88 |
+
terminalHeight: stdout.rows,
|
| 89 |
+
}),
|
| 90 |
+
);
|
| 91 |
+
for (let i = 0; i < bbox.height; i++) {
|
| 92 |
+
stdout.write("\r");
|
| 93 |
+
stdout.write(cursorForward(bbox.col));
|
| 94 |
+
if (backgroundColor) {
|
| 95 |
+
stdout.write(bgColorize(" ".repeat(bbox.width), backgroundColor));
|
| 96 |
+
} else {
|
| 97 |
+
stdout.write(" ".repeat(bbox.width));
|
| 98 |
+
}
|
| 99 |
+
stdout.write("\n");
|
| 100 |
+
}
|
| 101 |
+
stdout.write("\x1b8");
|
| 102 |
+
};
|
| 103 |
+
});
|
| 104 |
+
|
| 105 |
+
useEffect(() => {
|
| 106 |
+
function onExit() {
|
| 107 |
+
shouldCleanupRef.current = false;
|
| 108 |
+
}
|
| 109 |
+
function onSigInt() {
|
| 110 |
+
shouldCleanupRef.current = false;
|
| 111 |
+
process.exit();
|
| 112 |
+
}
|
| 113 |
+
process.on("exit", onExit);
|
| 114 |
+
process.on("SIGINT", onSigInt);
|
| 115 |
+
process.on("SIGTERM", onSigInt);
|
| 116 |
+
|
| 117 |
+
return () => {
|
| 118 |
+
process.removeListener("exit", onExit);
|
| 119 |
+
process.removeListener("SIGINT", onSigInt);
|
| 120 |
+
process.removeListener("SIGTERM", onSigInt);
|
| 121 |
+
};
|
| 122 |
+
}, []);
|
| 123 |
+
}
|
src/ink-picture/hooks/useImage.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { type Jimp } from "jimp";
|
| 2 |
+
import { useEffect, useState } from "react";
|
| 3 |
+
import { useImageCache } from "../InkPictureProvider.js";
|
| 4 |
+
import type { PixelData, PngData } from "../renderers/types.js";
|
| 5 |
+
import { fetchImage, getPngBuffer, getRawPixels } from "../utils/image.js";
|
| 6 |
+
|
| 7 |
+
export function useImage<T extends "pixels" | "png" = "pixels">(options: {
|
| 8 |
+
src: Parameters<typeof Jimp.read>[0];
|
| 9 |
+
pixelWidth: number;
|
| 10 |
+
pixelHeight: number;
|
| 11 |
+
mode?: T;
|
| 12 |
+
}): T extends "png"
|
| 13 |
+
? { imageData: PngData | undefined; error: boolean }
|
| 14 |
+
: { imageData: PixelData | undefined; error: boolean } {
|
| 15 |
+
const { src, pixelWidth, pixelHeight, mode = "pixels" } = options;
|
| 16 |
+
const [imageData, setImageData] = useState<PngData | PixelData | undefined>(
|
| 17 |
+
undefined,
|
| 18 |
+
);
|
| 19 |
+
const [error, setError] = useState(false);
|
| 20 |
+
const cache = useImageCache();
|
| 21 |
+
|
| 22 |
+
useEffect(() => {
|
| 23 |
+
if (pixelWidth === 0 || pixelHeight === 0) return;
|
| 24 |
+
|
| 25 |
+
let cancelled = false;
|
| 26 |
+
|
| 27 |
+
const load = async () => {
|
| 28 |
+
let image = typeof src === "string" ? cache?.get(src) : undefined;
|
| 29 |
+
|
| 30 |
+
if (!image) {
|
| 31 |
+
image = await fetchImage(src);
|
| 32 |
+
if (cancelled) return;
|
| 33 |
+
|
| 34 |
+
if (!image) {
|
| 35 |
+
setError(true);
|
| 36 |
+
setImageData(undefined);
|
| 37 |
+
return;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
if (typeof src === "string") {
|
| 41 |
+
cache?.set(src, image);
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
setError(false);
|
| 46 |
+
image.resize({ w: pixelWidth, h: pixelHeight });
|
| 47 |
+
|
| 48 |
+
if (mode === "png") {
|
| 49 |
+
const result = await getPngBuffer(image);
|
| 50 |
+
if (!cancelled) {
|
| 51 |
+
setImageData(result);
|
| 52 |
+
}
|
| 53 |
+
} else {
|
| 54 |
+
const result = await getRawPixels(image);
|
| 55 |
+
if (!cancelled) {
|
| 56 |
+
setImageData(result);
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
};
|
| 60 |
+
|
| 61 |
+
load();
|
| 62 |
+
|
| 63 |
+
return () => {
|
| 64 |
+
cancelled = true;
|
| 65 |
+
};
|
| 66 |
+
}, [src, pixelWidth, pixelHeight, mode, cache]);
|
| 67 |
+
|
| 68 |
+
return { imageData, error } as T extends "png"
|
| 69 |
+
? { imageData: PngData | undefined; error: boolean }
|
| 70 |
+
: { imageData: PixelData | undefined; error: boolean };
|
| 71 |
+
}
|
src/ink-picture/hooks/useMeasuredSize.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { type DOMElement, measureElement } from "src/ink";
|
| 2 |
+
import { useEffect, useRef, useState } from "react";
|
| 3 |
+
|
| 4 |
+
export function useMeasuredSize(
|
| 5 |
+
width: number | string,
|
| 6 |
+
height: number | string,
|
| 7 |
+
) {
|
| 8 |
+
const containerRef = useRef<DOMElement | null>(null);
|
| 9 |
+
const [measuredWidth, setMeasuredWidth] = useState(0);
|
| 10 |
+
const [measuredHeight, setMeasuredHeight] = useState(0);
|
| 11 |
+
|
| 12 |
+
const needsMeasure = typeof width === "string" || typeof height === "string";
|
| 13 |
+
useEffect(() => {
|
| 14 |
+
if (!needsMeasure) return;
|
| 15 |
+
if (!containerRef.current) return;
|
| 16 |
+
|
| 17 |
+
const { width: w, height: h } = measureElement(containerRef.current);
|
| 18 |
+
if (w > 0) setMeasuredWidth(w);
|
| 19 |
+
if (h > 0) setMeasuredHeight(h);
|
| 20 |
+
});
|
| 21 |
+
|
| 22 |
+
return {
|
| 23 |
+
containerRef,
|
| 24 |
+
resolvedWidth: typeof width === "number" ? width : measuredWidth,
|
| 25 |
+
resolvedHeight: typeof height === "number" ? height : measuredHeight,
|
| 26 |
+
};
|
| 27 |
+
}
|
src/ink-picture/hooks/usePosition.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { DOMElement } from "src/ink";
|
| 2 |
+
import {
|
| 3 |
+
type RefObject,
|
| 4 |
+
useCallback,
|
| 5 |
+
useEffect,
|
| 6 |
+
useRef,
|
| 7 |
+
useState,
|
| 8 |
+
} from "react";
|
| 9 |
+
import { useInkPictureConfig } from "../InkPictureProvider.js";
|
| 10 |
+
|
| 11 |
+
export type Position = {
|
| 12 |
+
/**
|
| 13 |
+
* Column position (x-coordinate) in terminal cells.
|
| 14 |
+
*/
|
| 15 |
+
col: number;
|
| 16 |
+
/**
|
| 17 |
+
* Row position (y-coordinate) in terminal cells.
|
| 18 |
+
*/
|
| 19 |
+
row: number;
|
| 20 |
+
/**
|
| 21 |
+
* Element width in terminal cells.
|
| 22 |
+
*/
|
| 23 |
+
width: number;
|
| 24 |
+
/**
|
| 25 |
+
* Element height in terminal cells.
|
| 26 |
+
*/
|
| 27 |
+
height: number;
|
| 28 |
+
/**
|
| 29 |
+
* Application width in terminal cells.
|
| 30 |
+
*/
|
| 31 |
+
appWidth: number;
|
| 32 |
+
/**
|
| 33 |
+
* Application height in terminal cells.
|
| 34 |
+
*/
|
| 35 |
+
appHeight: number;
|
| 36 |
+
};
|
| 37 |
+
|
| 38 |
+
/**
|
| 39 |
+
* Get the current position and dimensions of a mounted component in terminal cells.
|
| 40 |
+
*
|
| 41 |
+
* @param ref - React ref pointing to a Box component
|
| 42 |
+
* @returns Position object with col, row, width, and height
|
| 43 |
+
*/
|
| 44 |
+
const usePosition = (
|
| 45 |
+
ref: RefObject<DOMElement | null>,
|
| 46 |
+
): Position | undefined => {
|
| 47 |
+
const [position, setPosition] = useState<Position | undefined>(undefined);
|
| 48 |
+
|
| 49 |
+
const updatePosition = useCallback(() => {
|
| 50 |
+
const node = ref.current;
|
| 51 |
+
if (!node?.yogaNode) {
|
| 52 |
+
return;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
const { yogaNode } = node;
|
| 56 |
+
|
| 57 |
+
// Calculate absolute position by traversing up the tree
|
| 58 |
+
let absoluteCol = 0;
|
| 59 |
+
let absoluteRow = 0;
|
| 60 |
+
let appWidth = 0;
|
| 61 |
+
let appHeight = 0;
|
| 62 |
+
let currentNode: DOMElement | undefined = node;
|
| 63 |
+
|
| 64 |
+
while (currentNode) {
|
| 65 |
+
if (currentNode.yogaNode) {
|
| 66 |
+
absoluteCol += currentNode.yogaNode.getComputedLeft();
|
| 67 |
+
absoluteRow += currentNode.yogaNode.getComputedTop();
|
| 68 |
+
appWidth = currentNode.yogaNode.getComputedWidth();
|
| 69 |
+
appHeight = currentNode.yogaNode.getComputedHeight();
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
currentNode = currentNode.parentNode;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
const newPosition: Position = {
|
| 76 |
+
col: absoluteCol,
|
| 77 |
+
row: absoluteRow,
|
| 78 |
+
width: yogaNode.getComputedWidth(),
|
| 79 |
+
height: yogaNode.getComputedHeight(),
|
| 80 |
+
appWidth,
|
| 81 |
+
appHeight,
|
| 82 |
+
};
|
| 83 |
+
|
| 84 |
+
setPosition((previousPosition) => {
|
| 85 |
+
// Only update if position actually changed to avoid unnecessary re-renders
|
| 86 |
+
if (
|
| 87 |
+
!previousPosition ||
|
| 88 |
+
previousPosition.col !== newPosition.col ||
|
| 89 |
+
previousPosition.row !== newPosition.row ||
|
| 90 |
+
previousPosition.width !== newPosition.width ||
|
| 91 |
+
previousPosition.height !== newPosition.height ||
|
| 92 |
+
previousPosition.appWidth !== newPosition.appWidth ||
|
| 93 |
+
previousPosition.appHeight !== newPosition.appHeight
|
| 94 |
+
) {
|
| 95 |
+
return newPosition;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
return previousPosition;
|
| 99 |
+
});
|
| 100 |
+
}, [ref]);
|
| 101 |
+
|
| 102 |
+
// Calculate position after every render
|
| 103 |
+
// This captures layout changes caused by the component's own re-renders
|
| 104 |
+
useEffect(() => {
|
| 105 |
+
updatePosition();
|
| 106 |
+
});
|
| 107 |
+
|
| 108 |
+
// Poll to detect layout changes that happen without re-rendering
|
| 109 |
+
// e.g. parent layout changes, where React bails out from re-rendering children
|
| 110 |
+
const config = useInkPictureConfig();
|
| 111 |
+
const configRef = useRef(config);
|
| 112 |
+
configRef.current = config;
|
| 113 |
+
|
| 114 |
+
useEffect(() => {
|
| 115 |
+
let timeout: ReturnType<typeof setTimeout>;
|
| 116 |
+
|
| 117 |
+
function schedule() {
|
| 118 |
+
timeout = setTimeout(tick, configRef.current.pollIntervalMs);
|
| 119 |
+
timeout.unref();
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
function tick() {
|
| 123 |
+
updatePosition();
|
| 124 |
+
schedule();
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
schedule();
|
| 128 |
+
|
| 129 |
+
return () => {
|
| 130 |
+
clearTimeout(timeout);
|
| 131 |
+
};
|
| 132 |
+
}, [updatePosition]);
|
| 133 |
+
|
| 134 |
+
return position;
|
| 135 |
+
};
|
| 136 |
+
|
| 137 |
+
export default usePosition;
|
src/ink-picture/hooks/useProtocol.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useTerminalInfo } from "../InkPictureProvider.js";
|
| 2 |
+
import { getBestProtocol } from "../utils/getBestProtocol.js";
|
| 3 |
+
|
| 4 |
+
export default function useProtocol(
|
| 5 |
+
specifiedProtocol?:
|
| 6 |
+
| "ascii"
|
| 7 |
+
| "braille"
|
| 8 |
+
| "halfBlock"
|
| 9 |
+
| "iterm2"
|
| 10 |
+
| "kitty"
|
| 11 |
+
| "sixel",
|
| 12 |
+
): "ascii" | "braille" | "halfBlock" | "iterm2" | "kitty" | "sixel" {
|
| 13 |
+
const terminalInfo = useTerminalInfo();
|
| 14 |
+
|
| 15 |
+
if (specifiedProtocol) {
|
| 16 |
+
return specifiedProtocol;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
if (!terminalInfo) {
|
| 20 |
+
return "ascii";
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
return getBestProtocol(terminalInfo);
|
| 24 |
+
}
|
src/ink-picture/hooks/useVisibility.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useMemo } from "react";
|
| 2 |
+
import type { Position } from "./usePosition.js";
|
| 3 |
+
|
| 4 |
+
export type Visibility = "full" | "partial" | "hidden";
|
| 5 |
+
|
| 6 |
+
export interface VisibilityInfo {
|
| 7 |
+
position: Position;
|
| 8 |
+
terminalWidth: number;
|
| 9 |
+
terminalHeight: number;
|
| 10 |
+
defaultVisibility: Visibility;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
export type GetVisibility = (info: VisibilityInfo) => Visibility;
|
| 14 |
+
|
| 15 |
+
export function defaultVisibility(
|
| 16 |
+
position: Position,
|
| 17 |
+
terminalHeight: number,
|
| 18 |
+
terminalWidth: number,
|
| 19 |
+
): Visibility {
|
| 20 |
+
const viewportStartRow = terminalHeight - position.appHeight + position.row;
|
| 21 |
+
const viewportEndRow = viewportStartRow + position.height;
|
| 22 |
+
const inAppStartRow = position.row;
|
| 23 |
+
const inAppEndRow = inAppStartRow + position.height;
|
| 24 |
+
|
| 25 |
+
if (
|
| 26 |
+
viewportEndRow <= 0 ||
|
| 27 |
+
inAppEndRow <= 0 ||
|
| 28 |
+
viewportStartRow >= terminalHeight ||
|
| 29 |
+
inAppStartRow >= position.appHeight
|
| 30 |
+
) {
|
| 31 |
+
return "hidden";
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
const viewportEndCol = position.col + position.width;
|
| 35 |
+
if (
|
| 36 |
+
viewportEndCol <= 0 ||
|
| 37 |
+
position.col >= terminalWidth ||
|
| 38 |
+
position.col >= position.appWidth
|
| 39 |
+
) {
|
| 40 |
+
return "hidden";
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
if (
|
| 44 |
+
viewportStartRow < 0 ||
|
| 45 |
+
inAppStartRow < 0 ||
|
| 46 |
+
viewportEndRow > terminalHeight ||
|
| 47 |
+
inAppEndRow > position.appHeight ||
|
| 48 |
+
position.col < 0 ||
|
| 49 |
+
viewportEndCol > terminalWidth ||
|
| 50 |
+
viewportEndCol > position.appWidth
|
| 51 |
+
) {
|
| 52 |
+
return "partial";
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
return "full";
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
export function useVisibility(
|
| 59 |
+
position: Position | undefined,
|
| 60 |
+
terminalHeight: number,
|
| 61 |
+
terminalWidth: number,
|
| 62 |
+
getVisibility?: GetVisibility,
|
| 63 |
+
): Visibility {
|
| 64 |
+
return useMemo(() => {
|
| 65 |
+
if (!position) return "hidden";
|
| 66 |
+
|
| 67 |
+
const visibility = defaultVisibility(
|
| 68 |
+
position,
|
| 69 |
+
terminalHeight,
|
| 70 |
+
terminalWidth,
|
| 71 |
+
);
|
| 72 |
+
|
| 73 |
+
if (getVisibility) {
|
| 74 |
+
return getVisibility({
|
| 75 |
+
position,
|
| 76 |
+
terminalWidth,
|
| 77 |
+
terminalHeight,
|
| 78 |
+
defaultVisibility: visibility,
|
| 79 |
+
});
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
return visibility;
|
| 83 |
+
}, [position, terminalHeight, terminalWidth, getVisibility]);
|
| 84 |
+
}
|
src/ink-picture/index.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* ink-picture - Better image component for Ink CLI/TUIs
|
| 3 |
+
*
|
| 4 |
+
* This library provides components for rendering images in terminal applications
|
| 5 |
+
* built with Ink. It supports multiple rendering protocols with automatic fallback:
|
| 6 |
+
* Kitty, iTerm2, Sixel, Half-Block, Braille, and ASCII.
|
| 7 |
+
*
|
| 8 |
+
* @example
|
| 9 |
+
* ```tsx
|
| 10 |
+
* import React from 'react';
|
| 11 |
+
* import { Box } from 'ink';
|
| 12 |
+
* import Image, { InkPictureProvider } from 'ink-picture';
|
| 13 |
+
*
|
| 14 |
+
* function App() {
|
| 15 |
+
* return (
|
| 16 |
+
* <InkPictureProvider>
|
| 17 |
+
* <Box flexDirection="column">
|
| 18 |
+
* <Image
|
| 19 |
+
* src="https://example.com/image.jpg"
|
| 20 |
+
* width={40}
|
| 21 |
+
* height={20}
|
| 22 |
+
* alt="Example image"
|
| 23 |
+
* />
|
| 24 |
+
* </Box>
|
| 25 |
+
* </InkPictureProvider>
|
| 26 |
+
* );
|
| 27 |
+
* }
|
| 28 |
+
* ```
|
| 29 |
+
*
|
| 30 |
+
* The Image component must be used within an InkPictureProvider.
|
| 31 |
+
* This ensures terminal capabilities and configuration are detected and provided.
|
| 32 |
+
*/
|
| 33 |
+
|
| 34 |
+
export { default as AsciiImage } from "./components/image/Ascii.js";
|
| 35 |
+
export { default as BrailleImage } from "./components/image/Braille.js";
|
| 36 |
+
export { default as HalfBlockImage } from "./components/image/HalfBlock.js";
|
| 37 |
+
export { default as ITerm2Image } from "./components/image/ITerm2.js";
|
| 38 |
+
export type {
|
| 39 |
+
ImageProtocolHint,
|
| 40 |
+
ImageProtocolName,
|
| 41 |
+
} from "./components/image/index.js";
|
| 42 |
+
export { default } from "./components/image/index.js";
|
| 43 |
+
export { default as KittyImage } from "./components/image/Kitty.js";
|
| 44 |
+
export type { ImageProps, ImageProtocol } from "./components/image/protocol.js";
|
| 45 |
+
export { default as SixelImage } from "./components/image/Sixel.js";
|
| 46 |
+
|
| 47 |
+
export { default as usePosition } from "./hooks/usePosition.js";
|
| 48 |
+
export type {
|
| 49 |
+
GetVisibility,
|
| 50 |
+
Visibility,
|
| 51 |
+
VisibilityInfo,
|
| 52 |
+
} from "./hooks/useVisibility.js";
|
| 53 |
+
export { useVisibility } from "./hooks/useVisibility.js";
|
| 54 |
+
|
| 55 |
+
export {
|
| 56 |
+
defaultConfig as defaultInkPictureConfig,
|
| 57 |
+
defaultTerminalInfo,
|
| 58 |
+
type InkPictureConfig,
|
| 59 |
+
InkPictureProvider,
|
| 60 |
+
type TerminalInfo,
|
| 61 |
+
TerminalInfoContext,
|
| 62 |
+
TerminalInfoProvider,
|
| 63 |
+
useImageCache,
|
| 64 |
+
useInkPictureConfig,
|
| 65 |
+
useOnRender,
|
| 66 |
+
useTerminalInfo,
|
| 67 |
+
} from "./InkPictureProvider.js";
|
src/ink-picture/renderers/ascii.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import chalk from "chalk";
|
| 2 |
+
import type { PixelData } from "./types.js";
|
| 3 |
+
|
| 4 |
+
const ASCII_CHARS =
|
| 5 |
+
"$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. ";
|
| 6 |
+
|
| 7 |
+
export function renderAscii(
|
| 8 |
+
pixels: PixelData,
|
| 9 |
+
options: { colored?: boolean } = {},
|
| 10 |
+
): string {
|
| 11 |
+
const { data, info } = pixels;
|
| 12 |
+
const { width, height, channels } = info;
|
| 13 |
+
const colored = options.colored ?? true;
|
| 14 |
+
|
| 15 |
+
let result = "";
|
| 16 |
+
for (let y = 0; y < height; y++) {
|
| 17 |
+
for (let x = 0; x < width; x++) {
|
| 18 |
+
const pixelIndex = (y * width + x) * channels;
|
| 19 |
+
|
| 20 |
+
const r = data[pixelIndex] as number;
|
| 21 |
+
const g = data[pixelIndex + 1] as number;
|
| 22 |
+
const b = data[pixelIndex + 2] as number;
|
| 23 |
+
const a = channels === 4 ? (data[pixelIndex + 3] as number) : 255;
|
| 24 |
+
|
| 25 |
+
const sum = r + g + b + a;
|
| 26 |
+
const intensity = sum === 0 ? 0 : sum / (255 * 4);
|
| 27 |
+
const char =
|
| 28 |
+
ASCII_CHARS[
|
| 29 |
+
ASCII_CHARS.length -
|
| 30 |
+
1 -
|
| 31 |
+
Math.floor(intensity * (ASCII_CHARS.length - 1))
|
| 32 |
+
];
|
| 33 |
+
|
| 34 |
+
result += colored ? chalk.rgb(r, g, b)(char) : char;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
result += "\n";
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
return result.slice(0, -1);
|
| 41 |
+
}
|
src/ink-picture/renderers/braille.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { PixelData } from "./types.js";
|
| 2 |
+
|
| 3 |
+
export function renderBraille(pixels: PixelData): string {
|
| 4 |
+
const { data, info } = pixels;
|
| 5 |
+
const { width, height, channels } = info;
|
| 6 |
+
|
| 7 |
+
let result = "";
|
| 8 |
+
for (let y = 0; y < height - 3; y += 4) {
|
| 9 |
+
for (let x = 0; x < width - 1; x += 2) {
|
| 10 |
+
const dot1Index = (y * width + x) * channels;
|
| 11 |
+
const dot2Index = ((y + 1) * width + x) * channels;
|
| 12 |
+
const dot3Index = ((y + 2) * width + x) * channels;
|
| 13 |
+
const dot4Index = (y * width + x + 1) * channels;
|
| 14 |
+
const dot5Index = ((y + 1) * width + x + 1) * channels;
|
| 15 |
+
const dot6Index = ((y + 2) * width + x + 1) * channels;
|
| 16 |
+
const dot7Index = ((y + 3) * width + x) * channels;
|
| 17 |
+
const dot8Index = ((y + 3) * width + x + 1) * channels;
|
| 18 |
+
|
| 19 |
+
const getRgba = (index: number) => {
|
| 20 |
+
const r = data[index] as number;
|
| 21 |
+
const g = data[index + 1] as number;
|
| 22 |
+
const b = data[index + 2] as number;
|
| 23 |
+
const a = channels === 4 ? (data[index + 3] as number) : 1;
|
| 24 |
+
return { r, g, b, a };
|
| 25 |
+
};
|
| 26 |
+
|
| 27 |
+
const dot1 = rgbaToBlackOrWhite(getRgba(dot1Index));
|
| 28 |
+
const dot2 = rgbaToBlackOrWhite(getRgba(dot2Index));
|
| 29 |
+
const dot3 = rgbaToBlackOrWhite(getRgba(dot3Index));
|
| 30 |
+
const dot4 = rgbaToBlackOrWhite(getRgba(dot4Index));
|
| 31 |
+
const dot5 = rgbaToBlackOrWhite(getRgba(dot5Index));
|
| 32 |
+
const dot6 = rgbaToBlackOrWhite(getRgba(dot6Index));
|
| 33 |
+
const dot7 = rgbaToBlackOrWhite(getRgba(dot7Index));
|
| 34 |
+
const dot8 = rgbaToBlackOrWhite(getRgba(dot8Index));
|
| 35 |
+
|
| 36 |
+
const brailleChar = String.fromCharCode(
|
| 37 |
+
0x2800 +
|
| 38 |
+
(dot8 << 7) +
|
| 39 |
+
(dot7 << 6) +
|
| 40 |
+
(dot6 << 5) +
|
| 41 |
+
(dot5 << 4) +
|
| 42 |
+
(dot4 << 3) +
|
| 43 |
+
(dot3 << 2) +
|
| 44 |
+
(dot2 << 1) +
|
| 45 |
+
dot1,
|
| 46 |
+
);
|
| 47 |
+
result += brailleChar;
|
| 48 |
+
}
|
| 49 |
+
result += "\n";
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
return result.slice(0, -1);
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
function rgbaToBlackOrWhite({
|
| 56 |
+
r,
|
| 57 |
+
g,
|
| 58 |
+
b,
|
| 59 |
+
a,
|
| 60 |
+
}: {
|
| 61 |
+
r: number;
|
| 62 |
+
g: number;
|
| 63 |
+
b: number;
|
| 64 |
+
a: number;
|
| 65 |
+
}) {
|
| 66 |
+
const alpha = a / 255;
|
| 67 |
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
| 68 |
+
const alphaAdjustedLuminance = luminance * alpha + 255 * (1 - alpha);
|
| 69 |
+
|
| 70 |
+
if (alphaAdjustedLuminance > 128) {
|
| 71 |
+
return 1;
|
| 72 |
+
}
|
| 73 |
+
return 0;
|
| 74 |
+
}
|
src/ink-picture/renderers/halfBlock.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import chalk from "chalk";
|
| 2 |
+
import type { PixelData } from "./types.js";
|
| 3 |
+
|
| 4 |
+
const HALF_BLOCK = "\u2584";
|
| 5 |
+
|
| 6 |
+
export function renderHalfBlock(pixels: PixelData): string {
|
| 7 |
+
const { data, info } = pixels;
|
| 8 |
+
const { width, height, channels } = info;
|
| 9 |
+
|
| 10 |
+
let result = "";
|
| 11 |
+
for (let y = 0; y < height - 1; y += 2) {
|
| 12 |
+
for (let x = 0; x < width; x++) {
|
| 13 |
+
const topPixelIndex = (y * width + x) * channels;
|
| 14 |
+
const bottomPixelIndex = ((y + 1) * width + x) * channels;
|
| 15 |
+
|
| 16 |
+
const r = data[topPixelIndex] as number;
|
| 17 |
+
const g = data[topPixelIndex + 1] as number;
|
| 18 |
+
const b = data[topPixelIndex + 2] as number;
|
| 19 |
+
const a = channels === 4 ? (data[topPixelIndex + 3] as number) : 255;
|
| 20 |
+
|
| 21 |
+
const r2 = data[bottomPixelIndex] as number;
|
| 22 |
+
const g2 = data[bottomPixelIndex + 1] as number;
|
| 23 |
+
const b2 = data[bottomPixelIndex + 2] as number;
|
| 24 |
+
|
| 25 |
+
result +=
|
| 26 |
+
a === 0
|
| 27 |
+
? chalk.reset(" ")
|
| 28 |
+
: chalk.bgRgb(r, g, b).rgb(r2, g2, b2)(HALF_BLOCK);
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
result += "\n";
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
return result.slice(0, -1);
|
| 35 |
+
}
|
src/ink-picture/renderers/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export { renderAscii } from "./ascii.js";
|
| 2 |
+
export { renderBraille } from "./braille.js";
|
| 3 |
+
export { renderHalfBlock } from "./halfBlock.js";
|
| 4 |
+
export { renderITerm2 } from "./iterm2.js";
|
| 5 |
+
export {
|
| 6 |
+
makeKittyDeletion,
|
| 7 |
+
makeKittyPlacement,
|
| 8 |
+
makeKittyTransmitChunks,
|
| 9 |
+
} from "./kitty.js";
|
| 10 |
+
export { renderSixel } from "./sixel.js";
|
| 11 |
+
export type { PixelData, PngData } from "./types.js";
|
src/ink-picture/renderers/iterm2.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { PngData } from "./types.js";
|
| 2 |
+
|
| 3 |
+
export function renderITerm2(
|
| 4 |
+
png: PngData,
|
| 5 |
+
options: { width: number; height: number },
|
| 6 |
+
): string {
|
| 7 |
+
const { data } = png;
|
| 8 |
+
const { width, height } = options;
|
| 9 |
+
|
| 10 |
+
return (
|
| 11 |
+
"\x1b]1337;File=" +
|
| 12 |
+
`size=${data.length};` +
|
| 13 |
+
`width=${width}px;height=${height}px;` +
|
| 14 |
+
`preserveAspectRatio=1;` +
|
| 15 |
+
`inline=1:` +
|
| 16 |
+
data.toString("base64") +
|
| 17 |
+
"\x07"
|
| 18 |
+
);
|
| 19 |
+
}
|
src/ink-picture/renderers/kitty.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const DEFAULT_CHUNK_SIZE = 4096;
|
| 2 |
+
|
| 3 |
+
export function makeKittyTransmitChunks(
|
| 4 |
+
imageId: number,
|
| 5 |
+
pngBase64: string,
|
| 6 |
+
chunkSize: number = DEFAULT_CHUNK_SIZE,
|
| 7 |
+
): string[] {
|
| 8 |
+
const chunks: string[] = [];
|
| 9 |
+
|
| 10 |
+
if (pngBase64.length <= chunkSize) {
|
| 11 |
+
chunks.push(`\x1b_Gf=100,t=d,i=${imageId},m=0,q=2;${pngBase64}\x1b\\`);
|
| 12 |
+
return chunks;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
const firstChunk = pngBase64.slice(0, chunkSize);
|
| 16 |
+
chunks.push(`\x1b_Gf=100,t=d,i=${imageId},m=1,q=2;${firstChunk}\x1b\\`);
|
| 17 |
+
|
| 18 |
+
let bufferOffset = chunkSize;
|
| 19 |
+
while (bufferOffset < pngBase64.length - chunkSize) {
|
| 20 |
+
const chunk = pngBase64.slice(bufferOffset, bufferOffset + chunkSize);
|
| 21 |
+
bufferOffset += chunkSize;
|
| 22 |
+
chunks.push(`\x1b_Gm=1,q=2;${chunk}\x1b\\`);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
const lastChunk = pngBase64.slice(bufferOffset);
|
| 26 |
+
chunks.push(`\x1b_Gm=0,q=2;${lastChunk}\x1b\\`);
|
| 27 |
+
|
| 28 |
+
return chunks;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
export function makeKittyPlacement(
|
| 32 |
+
imageId: number,
|
| 33 |
+
placementId: number = 1,
|
| 34 |
+
): string {
|
| 35 |
+
return `\x1b_Ga=p,i=${imageId},p=${placementId},C=1,q=2\x1b\\`;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
export function makeKittyDeletion(
|
| 39 |
+
imageId: number,
|
| 40 |
+
placementId?: number,
|
| 41 |
+
): string {
|
| 42 |
+
if (placementId !== undefined) {
|
| 43 |
+
return `\x1b_Ga=d,d=i,p=${placementId},i=${imageId}\x1b\\`;
|
| 44 |
+
}
|
| 45 |
+
return `\x1b_Ga=d,d=I,i=${imageId}\x1b\\`;
|
| 46 |
+
}
|
src/ink-picture/renderers/sixel.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { image2sixel } from "sixel";
|
| 2 |
+
import type { PixelData } from "./types.js";
|
| 3 |
+
|
| 4 |
+
export function renderSixel(pixels: PixelData): string {
|
| 5 |
+
const { data, info } = pixels;
|
| 6 |
+
const u8Data = new Uint8Array(data);
|
| 7 |
+
return image2sixel(u8Data, info.width, info.height);
|
| 8 |
+
}
|
src/ink-picture/renderers/types.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export interface PixelData {
|
| 2 |
+
data: Buffer;
|
| 3 |
+
info: {
|
| 4 |
+
width: number;
|
| 5 |
+
height: number;
|
| 6 |
+
channels: number;
|
| 7 |
+
};
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
export interface PngData {
|
| 11 |
+
data: Buffer;
|
| 12 |
+
info: {
|
| 13 |
+
width: number;
|
| 14 |
+
height: number;
|
| 15 |
+
};
|
| 16 |
+
}
|
src/ink-picture/utils/ansiEscapes.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Moves cursor forward (right) by specified number of columns.
|
| 3 |
+
* @param count - Number of columns to move forward (default: 1)
|
| 4 |
+
* @returns ANSI escape sequence string
|
| 5 |
+
*/
|
| 6 |
+
export function cursorForward(count: number = 1) {
|
| 7 |
+
if (count === 0) return "";
|
| 8 |
+
return `\x1b[${count}C`;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
/**
|
| 12 |
+
* Moves cursor up by specified number of rows.
|
| 13 |
+
* @param count - Number of rows to move up (default: 1)
|
| 14 |
+
* @param context - Object containing appHeight and terminalHeight
|
| 15 |
+
* @returns ANSI escape sequence string
|
| 16 |
+
*/
|
| 17 |
+
export function cursorUp(
|
| 18 |
+
count: number = 1,
|
| 19 |
+
context: { appHeight: number; terminalHeight: number },
|
| 20 |
+
) {
|
| 21 |
+
// Ink appends a newline after output unless the app height exceeds the terminal height
|
| 22 |
+
// See https://github.com/vadimdemedes/ink/blob/76d221c3639f62c8c2f6c3599d51a1bf51ed1b7b/src/ink.tsx#L1040-L1042
|
| 23 |
+
// So we need to adjust the cursor movement by one row in that case to account for the absence of the newline
|
| 24 |
+
const movementCount =
|
| 25 |
+
context.appHeight >= context.terminalHeight ? count - 1 : count;
|
| 26 |
+
if (movementCount <= 0) return "";
|
| 27 |
+
return `\x1b[${movementCount}A`;
|
| 28 |
+
}
|
src/ink-picture/utils/bgColorize.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import chalk from "chalk";
|
| 2 |
+
|
| 3 |
+
const rgbRegex = /^rgb\(\s?(\d+),\s?(\d+),\s?(\d+)\s?\)$/;
|
| 4 |
+
const ansiRegex = /^ansi256\(\s?(\d+)\s?\)$/;
|
| 5 |
+
|
| 6 |
+
const isNamedColor = (color: string): color is keyof typeof chalk =>
|
| 7 |
+
color in chalk;
|
| 8 |
+
|
| 9 |
+
export default function bgColorize(str: string, color: string): string {
|
| 10 |
+
if (isNamedColor(color)) {
|
| 11 |
+
const methodName = `bg${color[0].toUpperCase() + color.slice(1)}` as
|
| 12 |
+
| keyof typeof chalk
|
| 13 |
+
| undefined;
|
| 14 |
+
if (methodName && methodName in chalk) {
|
| 15 |
+
return (chalk[methodName] as (s: string) => string)(str);
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
return str;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
if (color.startsWith("#")) {
|
| 22 |
+
return chalk.bgHex(color)(str);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
if (color.startsWith("ansi256")) {
|
| 26 |
+
const matches = ansiRegex.exec(color);
|
| 27 |
+
if (matches) {
|
| 28 |
+
return chalk.bgAnsi256(Number(matches[1]))(str);
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
return str;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
if (color.startsWith("rgb")) {
|
| 35 |
+
const matches = rgbRegex.exec(color);
|
| 36 |
+
if (matches) {
|
| 37 |
+
return chalk.bgRgb(
|
| 38 |
+
Number(matches[1]),
|
| 39 |
+
Number(matches[2]),
|
| 40 |
+
Number(matches[3]),
|
| 41 |
+
)(str);
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
return str;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
return str;
|
| 48 |
+
}
|
src/ink-picture/utils/generateKittyId.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Generates unique IDs for Kitty image rendering.
|
| 3 |
+
*/
|
| 4 |
+
|
| 5 |
+
const MIN_ID = 1048576;
|
| 6 |
+
const MAX_ID = 16777216;
|
| 7 |
+
|
| 8 |
+
class KittyIdGenerator {
|
| 9 |
+
private currentId = MIN_ID;
|
| 10 |
+
private freedIds = new Set<number>();
|
| 11 |
+
|
| 12 |
+
generateId(): number {
|
| 13 |
+
// Reuse freed ID if available
|
| 14 |
+
if (this.freedIds.size > 0) {
|
| 15 |
+
const result = this.freedIds.values().next();
|
| 16 |
+
if (result.done) {
|
| 17 |
+
throw new Error(
|
| 18 |
+
"Kitty ID reuse error: Set iterator unexpectedly empty",
|
| 19 |
+
);
|
| 20 |
+
}
|
| 21 |
+
const reusedId = result.value;
|
| 22 |
+
this.freedIds.delete(reusedId);
|
| 23 |
+
return reusedId;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
// Generate new sequential ID
|
| 27 |
+
if (this.currentId >= MAX_ID) {
|
| 28 |
+
throw new Error("Kitty ID overflow: Maximum ID value reached");
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
return this.currentId++;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
freeId(id: number): boolean {
|
| 35 |
+
// Validate ID is in our managed range and was previously allocated
|
| 36 |
+
if (id >= MIN_ID && id < this.currentId) {
|
| 37 |
+
return this.freedIds.add(id).size > this.freedIds.size - 1;
|
| 38 |
+
}
|
| 39 |
+
return false;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
// Utility methods
|
| 43 |
+
getActiveIdCount(): number {
|
| 44 |
+
return this.currentId - MIN_ID - this.freedIds.size;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
getAvailableIdCount(): number {
|
| 48 |
+
return MAX_ID - this.currentId + this.freedIds.size;
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
// Export singleton instance
|
| 53 |
+
const generator = new KittyIdGenerator();
|
| 54 |
+
export const generateId = () => generator.generateId();
|
| 55 |
+
export const freeId = (id: number) => generator.freeId(id);
|
| 56 |
+
export default generateId;
|
src/ink-picture/utils/getBestProtocol.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { TerminalInfo } from "../InkPictureProvider.js";
|
| 2 |
+
|
| 3 |
+
// Heuristic for selecting the best image protocol based on terminal capabilities
|
| 4 |
+
function getBestProtocol(
|
| 5 |
+
caps: TerminalInfo,
|
| 6 |
+
): "sixel" | "kitty" | "iterm2" | "braille" | "halfBlock" | "ascii" {
|
| 7 |
+
if (process.env.TERM_PROGRAM === "iTerm.app" && caps.supportsITerm2Graphics) {
|
| 8 |
+
// Testing has shown that iTerm2 might be adding kitty support in stealth but it's not mature yet
|
| 9 |
+
return "iterm2";
|
| 10 |
+
} else if (
|
| 11 |
+
process.env.TERM_PROGRAM === "WarpTerminal" &&
|
| 12 |
+
caps.supportsITerm2Graphics
|
| 13 |
+
) {
|
| 14 |
+
// Warp terminal only supports a incomplete subset of kitty graphics so we are not using it for now
|
| 15 |
+
return "iterm2";
|
| 16 |
+
} else if (
|
| 17 |
+
process.env.TERM_PROGRAM === "WezTerm" &&
|
| 18 |
+
caps.supportsITerm2Graphics
|
| 19 |
+
) {
|
| 20 |
+
// WezTerm doesn't support placing image above text so it may introduce race conditions during rerenders like sixel and iterm2
|
| 21 |
+
// See https://github.com/wezterm/wezterm/issues/7222
|
| 22 |
+
// a workaround is implemented for sixel and iterm2 to mitigate race conditions but I don't think it's a good idea to do the same for kitty specifically for wezterm
|
| 23 |
+
return "iterm2";
|
| 24 |
+
} else if (process.env.KONSOLE_VERSION && caps.supportsKittyGraphics) {
|
| 25 |
+
// Kitty is the only properly working protocol out of the three on Konsole
|
| 26 |
+
// because it has non-standard implementation for overwriting cells containing graphics with text which makes sixel and iterm2 leave artifacts
|
| 27 |
+
return "kitty";
|
| 28 |
+
} else if (caps.supportsKittyGraphics) {
|
| 29 |
+
return "kitty";
|
| 30 |
+
} else if (caps.supportsITerm2Graphics) {
|
| 31 |
+
return "iterm2";
|
| 32 |
+
} else if (caps.supportsSixelGraphics) {
|
| 33 |
+
return "sixel";
|
| 34 |
+
} else if (caps.supportsUnicode && caps.supportsColor) {
|
| 35 |
+
return "halfBlock";
|
| 36 |
+
} else if (caps.supportsUnicode) {
|
| 37 |
+
return "braille";
|
| 38 |
+
} else {
|
| 39 |
+
return "ascii";
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
export { getBestProtocol };
|
src/ink-picture/utils/image.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { fileURLToPath } from "node:url";
|
| 2 |
+
import { Jimp } from "jimp";
|
| 3 |
+
|
| 4 |
+
export type JimpImage = Awaited<ReturnType<typeof Jimp.read>>;
|
| 5 |
+
|
| 6 |
+
export interface ImageOutputInfo {
|
| 7 |
+
width: number;
|
| 8 |
+
height: number;
|
| 9 |
+
channels: number;
|
| 10 |
+
size?: number;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
export async function fetchImage(
|
| 14 |
+
src: Parameters<typeof Jimp.read>[0],
|
| 15 |
+
): Promise<JimpImage | undefined> {
|
| 16 |
+
try {
|
| 17 |
+
if (typeof src === "string" && src.startsWith("file://")) {
|
| 18 |
+
return await Jimp.read(fileURLToPath(src));
|
| 19 |
+
}
|
| 20 |
+
return await Jimp.read(src);
|
| 21 |
+
} catch {
|
| 22 |
+
return undefined;
|
| 23 |
+
}
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
export async function getRawPixels(
|
| 27 |
+
image: JimpImage,
|
| 28 |
+
): Promise<{ data: Buffer; info: ImageOutputInfo }> {
|
| 29 |
+
return {
|
| 30 |
+
data: image.bitmap.data,
|
| 31 |
+
info: {
|
| 32 |
+
width: image.bitmap.width,
|
| 33 |
+
height: image.bitmap.height,
|
| 34 |
+
channels: 4,
|
| 35 |
+
},
|
| 36 |
+
};
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
export async function getPngBuffer(
|
| 40 |
+
image: JimpImage,
|
| 41 |
+
): Promise<{ data: Buffer; info: ImageOutputInfo }> {
|
| 42 |
+
const buffer = await image.getBuffer("image/png");
|
| 43 |
+
return {
|
| 44 |
+
data: buffer,
|
| 45 |
+
info: {
|
| 46 |
+
width: image.bitmap.width,
|
| 47 |
+
height: image.bitmap.height,
|
| 48 |
+
channels: 4,
|
| 49 |
+
size: buffer.length,
|
| 50 |
+
},
|
| 51 |
+
};
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
export function calculateImageSize({
|
| 55 |
+
maxWidth,
|
| 56 |
+
maxHeight,
|
| 57 |
+
originalAspectRatio,
|
| 58 |
+
specifiedWidth,
|
| 59 |
+
specifiedHeight,
|
| 60 |
+
}: {
|
| 61 |
+
maxWidth: number;
|
| 62 |
+
maxHeight: number;
|
| 63 |
+
originalAspectRatio: number;
|
| 64 |
+
specifiedWidth?: number;
|
| 65 |
+
specifiedHeight?: number;
|
| 66 |
+
}): { width: number; height: number } {
|
| 67 |
+
// Both width and height specified
|
| 68 |
+
if (specifiedWidth && specifiedHeight) {
|
| 69 |
+
const width = Math.min(specifiedWidth, maxWidth);
|
| 70 |
+
const height = Math.min(specifiedHeight, maxHeight);
|
| 71 |
+
return { width: Math.round(width), height: Math.round(height) };
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
// Only width specified
|
| 75 |
+
if (specifiedWidth) {
|
| 76 |
+
let width = Math.min(specifiedWidth, maxWidth);
|
| 77 |
+
let height = width / originalAspectRatio;
|
| 78 |
+
|
| 79 |
+
if (height > maxHeight) {
|
| 80 |
+
height = maxHeight;
|
| 81 |
+
width = height * originalAspectRatio;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
return { width: Math.round(width), height: Math.round(height) };
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
// Only height specified
|
| 88 |
+
if (specifiedHeight) {
|
| 89 |
+
let height = Math.min(specifiedHeight, maxHeight);
|
| 90 |
+
let width = height * originalAspectRatio;
|
| 91 |
+
|
| 92 |
+
if (width > maxWidth) {
|
| 93 |
+
width = maxWidth;
|
| 94 |
+
height = width / originalAspectRatio;
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
return { width: Math.round(width), height: Math.round(height) };
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
// No dimensions specified - scale to fit while maintaining aspect ratio
|
| 101 |
+
let height = maxHeight;
|
| 102 |
+
let width = height * originalAspectRatio;
|
| 103 |
+
|
| 104 |
+
if (width > maxWidth) {
|
| 105 |
+
width = maxWidth;
|
| 106 |
+
height = width / originalAspectRatio;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
if (height > maxHeight) {
|
| 110 |
+
height = maxHeight;
|
| 111 |
+
width = height * originalAspectRatio;
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
return { width: Math.round(width), height: Math.round(height) };
|
| 115 |
+
}
|
src/ink-picture/utils/imageCache.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { JimpImage } from "./image.js";
|
| 2 |
+
|
| 3 |
+
export class ImageCache {
|
| 4 |
+
#cache = new Map<string, JimpImage>();
|
| 5 |
+
#maxEntries: number;
|
| 6 |
+
|
| 7 |
+
constructor(maxEntries: number) {
|
| 8 |
+
this.#maxEntries = Math.max(1, maxEntries);
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
get(key: string): JimpImage | undefined {
|
| 12 |
+
const entry = this.#cache.get(key);
|
| 13 |
+
if (!entry) return undefined;
|
| 14 |
+
|
| 15 |
+
this.#cache.delete(key);
|
| 16 |
+
this.#cache.set(key, entry);
|
| 17 |
+
|
| 18 |
+
return entry.clone();
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
set(key: string, image: JimpImage): void {
|
| 22 |
+
this.#cache.delete(key);
|
| 23 |
+
|
| 24 |
+
while (this.#cache.size >= this.#maxEntries) {
|
| 25 |
+
const lru = this.#cache.keys().next().value;
|
| 26 |
+
if (lru !== undefined) this.#cache.delete(lru);
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
this.#cache.set(key, image.clone());
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
clear(): void {
|
| 33 |
+
this.#cache.clear();
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
get size(): number {
|
| 37 |
+
return this.#cache.size;
|
| 38 |
+
}
|
| 39 |
+
}
|
src/ink-picture/utils/queryTerminal.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as fs from "node:fs";
|
| 2 |
+
|
| 3 |
+
const HIDDEN_MODE = "\x1b[8m";
|
| 4 |
+
const CLEAR_LINE_AND_RETURN = "\x1b[2K\r";
|
| 5 |
+
const RESET_ATTRIBUTES = "\x1b[0m";
|
| 6 |
+
|
| 7 |
+
const CELL_SIZE_QUERY = "\x1b[16t";
|
| 8 |
+
const TERMINAL_SIZE_QUERY = "\x1b[14t";
|
| 9 |
+
const KITTY_QUERY = "\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\";
|
| 10 |
+
const ITERM_CELL_SIZE_QUERY = "\x1b]1337;ReportCellSize\x07";
|
| 11 |
+
const DEVICE_ATTRIBUTES_QUERY = "\x1b[c";
|
| 12 |
+
|
| 13 |
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Intentionally parsing escape sequence responses
|
| 14 |
+
const CELL_SIZE_REGEX = /\x1b\[6;(\d+);(\d+);?t/;
|
| 15 |
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Intentionally parsing escape sequence responses
|
| 16 |
+
const TERMINAL_SIZE_REGEX = /\x1b\[4;(\d+);(\d+);?t/;
|
| 17 |
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Intentionally parsing escape sequence responses
|
| 18 |
+
const KITTY_RESPONSE_REGEX = /\x1b_Gi=31;(.+?)\x1b\\/;
|
| 19 |
+
const ITERM_CELL_SIZE_REGEX = /ReportCellSize=([\d.]+);([\d.]+);([\d.]+)/;
|
| 20 |
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Intentionally parsing escape sequence responses
|
| 21 |
+
const DEVICE_ATTRIBUTES_REGEX = /\x1b\[\?(\d+(?:;\d+)*)c/;
|
| 22 |
+
|
| 23 |
+
const RESPONSE_PATTERNS_FOR_STRIPPING: RegExp[] = [
|
| 24 |
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Intentionally stripping escape sequence responses
|
| 25 |
+
/\x1b\[6;\d+;\d+;?t/g,
|
| 26 |
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Intentionally stripping escape sequence responses
|
| 27 |
+
/\x1b\[4;\d+;\d+;?t/g,
|
| 28 |
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Intentionally stripping escape sequence responses
|
| 29 |
+
/\x1b_Gi=31;.+?\x1b\\/g,
|
| 30 |
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Intentionally stripping escape sequence responses
|
| 31 |
+
/\x1b\]1337;ReportCellSize=[\d.]+;[\d.]+;[\d.]+\x07/g,
|
| 32 |
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Intentionally stripping escape sequence responses
|
| 33 |
+
/\x1b\[\?\d+(?:;\d+)*c/g,
|
| 34 |
+
];
|
| 35 |
+
|
| 36 |
+
export interface TerminalQueryResult {
|
| 37 |
+
cellWidth: number | undefined;
|
| 38 |
+
cellHeight: number | undefined;
|
| 39 |
+
terminalWidth: number | undefined;
|
| 40 |
+
terminalHeight: number | undefined;
|
| 41 |
+
supportsKittyGraphics: boolean;
|
| 42 |
+
supportsSixelGraphics: boolean;
|
| 43 |
+
iterm2CellWidth: number | undefined;
|
| 44 |
+
iterm2CellHeight: number | undefined;
|
| 45 |
+
iterm2Scale: number | undefined;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
/**
|
| 49 |
+
* Detect terminal capabilities by sending a batch of escape sequence queries
|
| 50 |
+
* and accumulating the responses in a buffer matched by regex.
|
| 51 |
+
*
|
| 52 |
+
* The Device Attributes response (`\x1b[?Nc`) is used as a sentinel to signal
|
| 53 |
+
* end of terminal response, with a 1000ms fallback timeout.
|
| 54 |
+
* The function overrides `stdin.push`, preventing data from being picked up
|
| 55 |
+
* by Ink's `useInput` hook. `stdin.push` is restored on cleanup, and any
|
| 56 |
+
* user input during this period is pushed back into the stream.
|
| 57 |
+
*/
|
| 58 |
+
export function queryTerminal(
|
| 59 |
+
stdin: NodeJS.ReadStream,
|
| 60 |
+
setRawMode: (value: boolean) => void,
|
| 61 |
+
signal?: AbortSignal,
|
| 62 |
+
): Promise<TerminalQueryResult> {
|
| 63 |
+
return new Promise((resolve) => {
|
| 64 |
+
if (signal?.aborted || !process.stdin.isTTY || !process.stdout.isTTY) {
|
| 65 |
+
resolve({
|
| 66 |
+
cellWidth: undefined,
|
| 67 |
+
cellHeight: undefined,
|
| 68 |
+
terminalWidth: undefined,
|
| 69 |
+
terminalHeight: undefined,
|
| 70 |
+
supportsKittyGraphics: false,
|
| 71 |
+
supportsSixelGraphics: false,
|
| 72 |
+
iterm2CellWidth: undefined,
|
| 73 |
+
iterm2CellHeight: undefined,
|
| 74 |
+
iterm2Scale: undefined,
|
| 75 |
+
});
|
| 76 |
+
return;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
setRawMode(true);
|
| 80 |
+
|
| 81 |
+
let buffer = "";
|
| 82 |
+
let cellSizeReceived = false;
|
| 83 |
+
let terminalSizeReceived = false;
|
| 84 |
+
let kittyReceived = false;
|
| 85 |
+
let itermCellSizeReceived = false;
|
| 86 |
+
let sentinelReceived = false;
|
| 87 |
+
let timeoutId: NodeJS.Timeout;
|
| 88 |
+
let done = false;
|
| 89 |
+
|
| 90 |
+
const result: TerminalQueryResult = {
|
| 91 |
+
cellWidth: undefined,
|
| 92 |
+
cellHeight: undefined,
|
| 93 |
+
terminalWidth: undefined,
|
| 94 |
+
terminalHeight: undefined,
|
| 95 |
+
supportsKittyGraphics: false,
|
| 96 |
+
supportsSixelGraphics: false,
|
| 97 |
+
iterm2CellWidth: undefined,
|
| 98 |
+
iterm2CellHeight: undefined,
|
| 99 |
+
iterm2Scale: undefined,
|
| 100 |
+
};
|
| 101 |
+
|
| 102 |
+
const origPush: typeof stdin.push = stdin.push.bind(stdin);
|
| 103 |
+
|
| 104 |
+
const cleanup = () => {
|
| 105 |
+
if (done) return;
|
| 106 |
+
done = true;
|
| 107 |
+
if (timeoutId) {
|
| 108 |
+
clearTimeout(timeoutId);
|
| 109 |
+
}
|
| 110 |
+
signal?.removeEventListener("abort", onAbort);
|
| 111 |
+
stdin.push = origPush;
|
| 112 |
+
setRawMode(false);
|
| 113 |
+
|
| 114 |
+
let filtered = buffer;
|
| 115 |
+
for (const regex of RESPONSE_PATTERNS_FOR_STRIPPING) {
|
| 116 |
+
filtered = filtered.replace(regex, "");
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
if (filtered.length > 0) {
|
| 120 |
+
stdin.push(filtered);
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
resolve(result);
|
| 124 |
+
};
|
| 125 |
+
|
| 126 |
+
const onAbort = () => {
|
| 127 |
+
cleanup();
|
| 128 |
+
};
|
| 129 |
+
|
| 130 |
+
signal?.addEventListener("abort", onAbort, { once: true });
|
| 131 |
+
|
| 132 |
+
timeoutId = setTimeout(cleanup, 1000);
|
| 133 |
+
|
| 134 |
+
const processChunk = (data: string) => {
|
| 135 |
+
buffer += data;
|
| 136 |
+
|
| 137 |
+
if (!cellSizeReceived) {
|
| 138 |
+
const match = buffer.match(CELL_SIZE_REGEX);
|
| 139 |
+
if (match?.[1] && match?.[2]) {
|
| 140 |
+
cellSizeReceived = true;
|
| 141 |
+
const height = parseInt(match[1], 10);
|
| 142 |
+
const width = parseInt(match[2], 10);
|
| 143 |
+
if (!Number.isNaN(height) && !Number.isNaN(width)) {
|
| 144 |
+
result.cellWidth = width;
|
| 145 |
+
result.cellHeight = height;
|
| 146 |
+
}
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
if (!terminalSizeReceived) {
|
| 151 |
+
const match = buffer.match(TERMINAL_SIZE_REGEX);
|
| 152 |
+
if (match?.[1] && match?.[2]) {
|
| 153 |
+
terminalSizeReceived = true;
|
| 154 |
+
const height = parseInt(match[1], 10);
|
| 155 |
+
const width = parseInt(match[2], 10);
|
| 156 |
+
if (!Number.isNaN(height) && !Number.isNaN(width)) {
|
| 157 |
+
result.terminalWidth = width;
|
| 158 |
+
result.terminalHeight = height;
|
| 159 |
+
}
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
if (!kittyReceived) {
|
| 164 |
+
const match = buffer.match(KITTY_RESPONSE_REGEX);
|
| 165 |
+
if (match?.[1]?.includes("OK")) {
|
| 166 |
+
kittyReceived = true;
|
| 167 |
+
result.supportsKittyGraphics = true;
|
| 168 |
+
}
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
if (!itermCellSizeReceived) {
|
| 172 |
+
const match = buffer.match(ITERM_CELL_SIZE_REGEX);
|
| 173 |
+
if (match?.[1] && match?.[2] && match?.[3]) {
|
| 174 |
+
itermCellSizeReceived = true;
|
| 175 |
+
const height = parseFloat(match[1]);
|
| 176 |
+
const width = parseFloat(match[2]);
|
| 177 |
+
const scale = parseFloat(match[3]);
|
| 178 |
+
if (
|
| 179 |
+
!Number.isNaN(height) &&
|
| 180 |
+
!Number.isNaN(width) &&
|
| 181 |
+
!Number.isNaN(scale)
|
| 182 |
+
) {
|
| 183 |
+
result.iterm2CellWidth = width;
|
| 184 |
+
result.iterm2CellHeight = height;
|
| 185 |
+
result.iterm2Scale = scale;
|
| 186 |
+
}
|
| 187 |
+
}
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
if (!sentinelReceived && DEVICE_ATTRIBUTES_REGEX.test(buffer)) {
|
| 191 |
+
sentinelReceived = true;
|
| 192 |
+
const daMatch = buffer.match(DEVICE_ATTRIBUTES_REGEX);
|
| 193 |
+
if (daMatch?.[1]) {
|
| 194 |
+
result.supportsSixelGraphics = daMatch[1].split(";").includes("4");
|
| 195 |
+
}
|
| 196 |
+
cleanup();
|
| 197 |
+
}
|
| 198 |
+
};
|
| 199 |
+
|
| 200 |
+
stdin.push = (chunk) => {
|
| 201 |
+
const str = Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk);
|
| 202 |
+
processChunk(str);
|
| 203 |
+
return true;
|
| 204 |
+
};
|
| 205 |
+
|
| 206 |
+
const query =
|
| 207 |
+
HIDDEN_MODE +
|
| 208 |
+
CELL_SIZE_QUERY +
|
| 209 |
+
TERMINAL_SIZE_QUERY +
|
| 210 |
+
KITTY_QUERY +
|
| 211 |
+
ITERM_CELL_SIZE_QUERY +
|
| 212 |
+
DEVICE_ATTRIBUTES_QUERY +
|
| 213 |
+
CLEAR_LINE_AND_RETURN +
|
| 214 |
+
RESET_ATTRIBUTES;
|
| 215 |
+
|
| 216 |
+
try {
|
| 217 |
+
fs.writeSync(process.stdout.fd, query);
|
| 218 |
+
} catch {
|
| 219 |
+
cleanup();
|
| 220 |
+
}
|
| 221 |
+
});
|
| 222 |
+
}
|
src/ink/components/StdoutContext.tsx
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { createContext, useContext } from 'react'
|
| 2 |
+
|
| 3 |
+
export type Props = {
|
| 4 |
+
readonly stdout: NodeJS.WriteStream & { readonly rows: number; readonly columns: number }
|
| 5 |
+
readonly write: (data: string) => void
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
const StdoutContext = createContext<Props>({
|
| 9 |
+
stdout: process.stdout as NodeJS.WriteStream & { rows: number; columns: number },
|
| 10 |
+
write() {},
|
| 11 |
+
})
|
| 12 |
+
|
| 13 |
+
StdoutContext.displayName = 'StdoutContext'
|
| 14 |
+
|
| 15 |
+
export default StdoutContext
|
src/tools/ImageShowTool/ImageShowTool.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import { z } from 'zod/v4'
|
|
| 5 |
import { Text } from '../../ink.js'
|
| 6 |
import { buildTool, type ToolDef } from '../../Tool.js'
|
| 7 |
import { logForDebugging } from '../../utils/debug.js'
|
| 8 |
-
import Image, { InkPictureProvider } from 'ink-picture'
|
| 9 |
|
| 10 |
const IMAGE_TOOL_NAME = 'ImageShow'
|
| 11 |
|
|
@@ -111,6 +111,22 @@ async function loadImage(url: string): Promise<{ buffer: Buffer; format: string
|
|
| 111 |
return fetchImage(normalizedUrl)
|
| 112 |
}
|
| 113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
function getToolUseSummary(input: Partial<Input>): string | null {
|
| 115 |
return input?.url ? `Show: ${input.url.split('/').pop() ?? input.url}` : null
|
| 116 |
}
|
|
@@ -215,8 +231,16 @@ export const ImageShowTool = buildTool({
|
|
| 215 |
const imgWidth = Math.floor(cols * 0.6)
|
| 216 |
const imgHeight = Math.floor(rows * 0.4)
|
| 217 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
return (
|
| 219 |
-
<InkPictureProvider>
|
| 220 |
<Image
|
| 221 |
src={content.src}
|
| 222 |
width={imgWidth}
|
|
|
|
| 5 |
import { Text } from '../../ink.js'
|
| 6 |
import { buildTool, type ToolDef } from '../../Tool.js'
|
| 7 |
import { logForDebugging } from '../../utils/debug.js'
|
| 8 |
+
import Image, { InkPictureProvider, type TerminalInfo } from 'src/ink-picture/index.ts'
|
| 9 |
|
| 10 |
const IMAGE_TOOL_NAME = 'ImageShow'
|
| 11 |
|
|
|
|
| 111 |
return fetchImage(normalizedUrl)
|
| 112 |
}
|
| 113 |
|
| 114 |
+
/**
|
| 115 |
+
* Lightweight synchronous Kitty graphics protocol detection.
|
| 116 |
+
* Uses only env vars — no module dependencies, no async queries.
|
| 117 |
+
*/
|
| 118 |
+
function detectKittySync(): boolean {
|
| 119 |
+
return !!(
|
| 120 |
+
process.env.TERM?.includes('kitty') ||
|
| 121 |
+
process.env.KITTY_WINDOW_ID ||
|
| 122 |
+
process.env.TERM_PROGRAM === 'kitty' ||
|
| 123 |
+
process.env.TERM_PROGRAM === 'ghostty' ||
|
| 124 |
+
process.env.TERM_PROGRAM === 'WezTerm' ||
|
| 125 |
+
process.env.TERM_PROGRAM === 'konsole' ||
|
| 126 |
+
process.env.TERM_PROGRAM === 'foot'
|
| 127 |
+
)
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
function getToolUseSummary(input: Partial<Input>): string | null {
|
| 131 |
return input?.url ? `Show: ${input.url.split('/').pop() ?? input.url}` : null
|
| 132 |
}
|
|
|
|
| 231 |
const imgWidth = Math.floor(cols * 0.6)
|
| 232 |
const imgHeight = Math.floor(rows * 0.4)
|
| 233 |
|
| 234 |
+
// Sync terminal info prevents InkPictureProvider's async terminal
|
| 235 |
+
// query from delaying the first render and ensures the correct
|
| 236 |
+
// protocol is used from frame one.
|
| 237 |
+
const terminalInfo: Partial<TerminalInfo> = {
|
| 238 |
+
supportsKittyGraphics: detectKittySync(),
|
| 239 |
+
supportsUnicode: true,
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
return (
|
| 243 |
+
<InkPictureProvider terminalInfo={terminalInfo}>
|
| 244 |
<Image
|
| 245 |
src={content.src}
|
| 246 |
width={imgWidth}
|
src/tools/ImageShowTool/__tests__/ShowLocalImage.test.tsx
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bun
|
| 2 |
+
/**
|
| 3 |
+
* Visual test: display a local image using ink-picture + Ink.
|
| 4 |
+
*
|
| 5 |
+
* Usage:
|
| 6 |
+
* bun run src/tools/ImageShowTool/__tests__/ShowLocalImage.test.tsx
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
import React, { useEffect } from 'react'
|
| 10 |
+
import { render, Box, Text, useApp } from 'ink'
|
| 11 |
+
import Image, { InkPictureProvider, type TerminalInfo } from 'src/ink-picture/index.ts'
|
| 12 |
+
|
| 13 |
+
const IMAGE_PATH = '/home/yuki/Pictures/Wallpapers/3god.jpg'
|
| 14 |
+
|
| 15 |
+
const cols = process.stdout.columns ?? 80
|
| 16 |
+
const rows = process.stdout.rows ?? 40
|
| 17 |
+
const imgWidth = Math.floor(cols * 0.6)
|
| 18 |
+
const imgHeight = Math.floor(rows * 0.4)
|
| 19 |
+
|
| 20 |
+
const terminalInfo: Partial<TerminalInfo> = {
|
| 21 |
+
supportsKittyGraphics: !!(
|
| 22 |
+
process.env.TERM?.includes('kitty') ||
|
| 23 |
+
process.env.KITTY_WINDOW_ID ||
|
| 24 |
+
process.env.TERM_PROGRAM === 'kitty' ||
|
| 25 |
+
process.env.TERM_PROGRAM === 'ghostty' ||
|
| 26 |
+
process.env.TERM_PROGRAM === 'WezTerm' ||
|
| 27 |
+
process.env.TERM_PROGRAM === 'konsole' ||
|
| 28 |
+
process.env.TERM_PROGRAM === 'foot'
|
| 29 |
+
),
|
| 30 |
+
supportsUnicode: true,
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
function App() {
|
| 34 |
+
const { exit } = useApp()
|
| 35 |
+
|
| 36 |
+
useEffect(() => {
|
| 37 |
+
const timer = setTimeout(() => exit(), 3000)
|
| 38 |
+
return () => clearTimeout(timer)
|
| 39 |
+
}, [exit])
|
| 40 |
+
|
| 41 |
+
return (
|
| 42 |
+
<Box flexDirection="column">
|
| 43 |
+
<InkPictureProvider terminalInfo={terminalInfo}>
|
| 44 |
+
<Image src={IMAGE_PATH} width={imgWidth} height={imgHeight} alt="3god" />
|
| 45 |
+
</InkPictureProvider>
|
| 46 |
+
</Box>
|
| 47 |
+
)
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
const { waitUntilExit } = render(<App />)
|
| 51 |
+
await waitUntilExit()
|
src/tools/ImageShowTool/__tests__/ShowUrlImage.test.tsx
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bun
|
| 2 |
+
/**
|
| 3 |
+
* Visual test: display a remote image using ink-picture + Ink.
|
| 4 |
+
*
|
| 5 |
+
* Usage:
|
| 6 |
+
* bun run src/tools/ImageShowTool/__tests__/ShowUrlImage.test.tsx
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
import React, { useEffect } from 'react'
|
| 10 |
+
import { render, Box, Text, useApp } from 'ink'
|
| 11 |
+
import Image, { InkPictureProvider, type TerminalInfo } from 'src/ink-picture/index.ts'
|
| 12 |
+
|
| 13 |
+
const IMAGE_URL =
|
| 14 |
+
'https://kimi-web-img.moonshot.cn/img/images.pexels.com/8f909bffa5353ba43aac1db1f83b1bf5450a8cd5.jpeg'
|
| 15 |
+
|
| 16 |
+
const cols = process.stdout.columns ?? 80
|
| 17 |
+
const rows = process.stdout.rows ?? 40
|
| 18 |
+
const imgWidth = Math.floor(cols * 0.6)
|
| 19 |
+
const imgHeight = Math.floor(rows * 0.4)
|
| 20 |
+
|
| 21 |
+
const terminalInfo: Partial<TerminalInfo> = {
|
| 22 |
+
supportsKittyGraphics: !!(
|
| 23 |
+
process.env.TERM?.includes('kitty') ||
|
| 24 |
+
process.env.KITTY_WINDOW_ID ||
|
| 25 |
+
process.env.TERM_PROGRAM === 'kitty' ||
|
| 26 |
+
process.env.TERM_PROGRAM === 'ghostty' ||
|
| 27 |
+
process.env.TERM_PROGRAM === 'WezTerm' ||
|
| 28 |
+
process.env.TERM_PROGRAM === 'konsole' ||
|
| 29 |
+
process.env.TERM_PROGRAM === 'foot'
|
| 30 |
+
),
|
| 31 |
+
supportsUnicode: true,
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
function App() {
|
| 35 |
+
const { exit } = useApp()
|
| 36 |
+
|
| 37 |
+
useEffect(() => {
|
| 38 |
+
// Longer timeout for remote image (network fetch)
|
| 39 |
+
const timer = setTimeout(() => exit(), 15000)
|
| 40 |
+
return () => clearTimeout(timer)
|
| 41 |
+
}, [exit])
|
| 42 |
+
|
| 43 |
+
return (
|
| 44 |
+
<Box flexDirection="column">
|
| 45 |
+
<InkPictureProvider terminalInfo={terminalInfo}>
|
| 46 |
+
<Image src={IMAGE_URL} width={imgWidth} height={imgHeight} alt="remote" />
|
| 47 |
+
</InkPictureProvider>
|
| 48 |
+
</Box>
|
| 49 |
+
)
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
const { waitUntilExit } = render(<App />)
|
| 53 |
+
await waitUntilExit()
|
tsconfig.json
CHANGED
|
@@ -10,7 +10,9 @@
|
|
| 10 |
"types": ["bun"],
|
| 11 |
"baseUrl": ".",
|
| 12 |
"paths": {
|
| 13 |
-
"src/*": ["src/*"]
|
|
|
|
|
|
|
| 14 |
},
|
| 15 |
"moduleResolution": "bundler",
|
| 16 |
"allowImportingTsExtensions": true,
|
|
|
|
| 10 |
"types": ["bun"],
|
| 11 |
"baseUrl": ".",
|
| 12 |
"paths": {
|
| 13 |
+
"src/*": ["src/*"],
|
| 14 |
+
"ink-picture": ["src/ink-picture"],
|
| 15 |
+
"ink-picture/*": ["src/ink-picture/*"]
|
| 16 |
},
|
| 17 |
"moduleResolution": "bundler",
|
| 18 |
"allowImportingTsExtensions": true,
|