diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12d8419d..12248532 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,10 +19,51 @@ jobs: with: node-version: 22.21.1 + - name: setup macOS shells + if: matrix.os == 'macos-latest' + shell: bash + run: | + brew install fish + brew install zsh + + for dir in /usr/local/share/zsh /usr/local/share/zsh/site-functions /usr/share/zsh /opt/homebrew/share/zsh /opt/homebrew/share/zsh/site-functions /opt/homebrew/share/fish /opt/homebrew/share/fish/vendor_completions.d; do + [ -d "$dir" ] && sudo chmod g-w "$dir" + done + + - name: setup linux shells + if: matrix.os == 'ubuntu-latest' + shell: bash + run: | + sudo apt-add-repository ppa:fish-shell/release-3 + sudo apt-get update + sudo apt install fish zsh + + sudo chmod -R 755 /usr/share/zsh/vendor-completions + sudo chown -R root:root /usr/share/zsh/vendor-completions + sudo chmod -R 755 /usr/share/zsh + sudo chown -R root:root /usr/share/zsh + + - name: setup windows shells + if: matrix.os == 'windows-latest' + shell: pwsh + run: | + python -m pip install 'xonsh[full]' + + - name: setup oh-my-zsh + if: matrix.os != 'windows-latest' + shell: bash + run: | + sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended --keep-zshrc + git clone --depth=1 https://github.com/romkatv/powerlevel10k.git "${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k" + - run: npm ci - run: npm run lint - run: npm test - - run: npm run build \ No newline at end of file + - run: npm run build + + - run: npm link + + - run: npm run test:e2e \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 720237f6..445bee6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "MIT", "dependencies": { "@withfig/autocomplete": "2.675.0", - "@xterm/addon-unicode11": "^0.8.0", - "@xterm/headless": "^5.5.0", + "@xterm/addon-unicode11": "^0.9.0", + "@xterm/headless": "^6.0.0", "ajv": "^8.12.0", "ansi-escapes": "^6.2.0", "ansi-styles": "^6.2.1", @@ -31,6 +31,7 @@ "is": "build/index.js" }, "devDependencies": { + "@microsoft/tui-test": "^0.0.3", "@tsconfig/node18": "^18.2.2", "@types/color-convert": "^2.0.3", "@types/jest": "^29.5.5", @@ -41,7 +42,7 @@ "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", "@withfig/autocomplete-types": "^1.28.0", - "@xterm/xterm": "^5.5.0", + "@xterm/xterm": "^6.0.0", "esbuild": "^0.27.2", "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", @@ -1246,6 +1247,24 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1821,6 +1840,97 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@microsoft/tui-test": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/tui-test/-/tui-test-0.0.3.tgz", + "integrity": "sha512-dBiGQxxQcw57ZEhl7FmfP7KoI2wbcVe031mNj8j+elNGAqFihqiBKStPNxZMcT0PyAFmJBvj8C7wFPNT108cww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@swc/core": "^1.3.102", + "@xterm/headless": "^6.0.0", + "chalk": "^5.3.0", + "color-convert": "^2.0.1", + "commander": "^11.1.0", + "expect": "^29.7.0", + "glob": "^10.3.10", + "jest-diff": "^29.7.0", + "pretty-ms": "^8.0.0", + "proper-lockfile": "^4.1.2", + "which": "^4.0.0", + "workerpool": "^9.1.0" + }, + "bin": { + "tui-test": "index.js" + }, + "engines": { + "bun": ">=1.3.5", + "node": ">=16.6.0 <25.0.0" + }, + "optionalDependencies": { + "node-pty": "1.2.0-beta.11" + } + }, + "node_modules/@microsoft/tui-test/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@microsoft/tui-test/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@microsoft/tui-test/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@microsoft/tui-test/node_modules/node-pty": { + "version": "1.2.0-beta.11", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.2.0-beta.11.tgz", + "integrity": "sha512-THcUyu1WwdgoIyUvgXOZ70EOMXzheGa0q3tbEb5kUIfKgcpBJ+AJ9Q1kq0bKtYmQzr77usXiTORZTLmAUQlnoQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^7.1.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1856,6 +1966,17 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1887,8 +2008,6 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", - "optional": true, - "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.17" @@ -1934,7 +2053,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=10" } @@ -1952,7 +2070,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=10" } @@ -1970,7 +2087,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } @@ -1988,7 +2104,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } @@ -2006,7 +2121,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } @@ -2024,7 +2138,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } @@ -2042,7 +2155,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } @@ -2060,7 +2172,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=10" } @@ -2078,7 +2189,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=10" } @@ -2096,7 +2206,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=10" } @@ -2105,9 +2214,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@swc/types": { "version": "0.1.17", @@ -2115,8 +2222,6 @@ "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", "dev": true, "license": "Apache-2.0", - "optional": true, - "peer": true, "dependencies": { "@swc/counter": "^0.1.3" } @@ -2548,22 +2653,29 @@ "dev": true }, "node_modules/@xterm/addon-unicode11": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.8.0.tgz", - "integrity": "sha512-LxinXu8SC4OmVa6FhgwsVCBZbr8WoSGzBl2+vqe8WcQ6hb1r6Gj9P99qTNdPiFPh4Ceiu2pC8xukZ6+2nnh49Q==", - "peerDependencies": { - "@xterm/xterm": "^5.0.0" - } + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0.tgz", + "integrity": "sha512-FxDnYcyuXhNl+XSqGZL/t0U9eiNb/q3EWT5rYkQT/zuig8Gz/VagnQANKHdDWFM2lTMk9ly0EFQxxxtZUoRetw==", + "license": "MIT" }, "node_modules/@xterm/headless": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.5.0.tgz", - "integrity": "sha512-5xXB7kdQlFBP82ViMJTwwEc3gKCLGKR/eoxQm4zge7GPBl86tCdI0IdPJjoKd8mUSFXz5V7i/25sfsEkP4j46g==" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-6.0.0.tgz", + "integrity": "sha512-5Yj1QINYCyzrZtf8OFIHi47iQtI+0qYFPHmouEfG8dHNxbZ9Tb9YGSuLcsEwj9Z+OL75GJqPyJbyoFer80a2Hw==", + "license": "MIT", + "workspaces": [ + "addons/*" + ] }, "node_modules/@xterm/xterm": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", - "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz", + "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "addons/*" + ] }, "node_modules/acorn": { "version": "8.10.0", @@ -4459,10 +4571,11 @@ } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" }, "node_modules/for-each": { "version": "0.3.3", @@ -4473,6 +4586,36 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5365,6 +5508,22 @@ "set-function-name": "^2.0.1" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -6901,6 +7060,13 @@ "loose-envify": "cli.js" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -6993,6 +7159,16 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -7250,6 +7426,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7280,6 +7463,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-ms": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-3.0.0.tgz", + "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7313,6 +7509,23 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -7423,6 +7636,22 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/pretty-ms": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz", + "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -7453,6 +7682,18 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -7657,6 +7898,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -7936,6 +8187,62 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", @@ -8015,6 +8322,30 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -9018,6 +9349,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -9034,8 +9372,98 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true @@ -9911,6 +10339,20 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -10339,6 +10781,71 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@microsoft/tui-test": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/tui-test/-/tui-test-0.0.3.tgz", + "integrity": "sha512-dBiGQxxQcw57ZEhl7FmfP7KoI2wbcVe031mNj8j+elNGAqFihqiBKStPNxZMcT0PyAFmJBvj8C7wFPNT108cww==", + "dev": true, + "requires": { + "@swc/core": "^1.3.102", + "@xterm/headless": "^6.0.0", + "chalk": "^5.3.0", + "color-convert": "^2.0.1", + "commander": "^11.1.0", + "expect": "^29.7.0", + "glob": "^10.3.10", + "jest-diff": "^29.7.0", + "node-pty": "1.2.0-beta.11", + "pretty-ms": "^8.0.0", + "proper-lockfile": "^4.1.2", + "which": "^4.0.0", + "workerpool": "^9.1.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.2" + } + }, + "node-pty": { + "version": "1.2.0-beta.11", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.2.0-beta.11.tgz", + "integrity": "sha512-THcUyu1WwdgoIyUvgXOZ70EOMXzheGa0q3tbEb5kUIfKgcpBJ+AJ9Q1kq0bKtYmQzr77usXiTORZTLmAUQlnoQ==", + "dev": true, + "optional": true, + "requires": { + "node-addon-api": "^7.1.0" + } + } + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -10365,6 +10872,13 @@ "fastq": "^1.6.0" } }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -10394,8 +10908,6 @@ "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.4.tgz", "integrity": "sha512-ut3zfiTLORMxhr6y/GBxkHmzcGuVpwJYX4qyXWuBKkpw/0g0S5iO1/wW7RnLnZbAi8wS/n0atRZoaZlXWBkeJg==", "dev": true, - "optional": true, - "peer": true, "requires": { "@swc/core-darwin-arm64": "1.10.4", "@swc/core-darwin-x64": "1.10.4", @@ -10416,96 +10928,82 @@ "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.4.tgz", "integrity": "sha512-sV/eurLhkjn/197y48bxKP19oqcLydSel42Qsy2zepBltqUx+/zZ8+/IS0Bi7kaWVFxerbW1IPB09uq8Zuvm3g==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-darwin-x64": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.4.tgz", "integrity": "sha512-gjYNU6vrAUO4+FuovEo9ofnVosTFXkF0VDuo1MKPItz6e2pxc2ale4FGzLw0Nf7JB1sX4a8h06CN16/pLJ8Q2w==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-linux-arm-gnueabihf": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.4.tgz", "integrity": "sha512-zd7fXH5w8s+Sfvn2oO464KDWl+ZX1MJiVmE4Pdk46N3PEaNwE0koTfgx2vQRqRG4vBBobzVvzICC3618WcefOA==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-linux-arm64-gnu": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.4.tgz", "integrity": "sha512-+UGfoHDxsMZgFD3tABKLeEZHqLNOkxStu+qCG7atGBhS4Slri6h6zijVvf4yI5X3kbXdvc44XV/hrP/Klnui2A==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-linux-arm64-musl": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.4.tgz", "integrity": "sha512-cDDj2/uYsOH0pgAnDkovLZvKJpFmBMyXkxEG6Q4yw99HbzO6QzZ5HDGWGWVq/6dLgYKlnnmpjZCPPQIu01mXEg==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-linux-x64-gnu": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.4.tgz", "integrity": "sha512-qJXh9D6Kf5xSdGWPINpLGixAbB5JX8JcbEJpRamhlDBoOcQC79dYfOMEIxWPhTS1DGLyFakAx2FX/b2VmQmj0g==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-linux-x64-musl": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.4.tgz", "integrity": "sha512-A76lIAeyQnHCVt0RL/pG+0er8Qk9+acGJqSZOZm67Ve3B0oqMd871kPtaHBM0BW3OZAhoILgfHW3Op9Q3mx3Cw==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-win32-arm64-msvc": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.4.tgz", "integrity": "sha512-e6j5kBu4fIY7fFxFxnZI0MlEovRvp50Lg59Fw+DVbtqHk3C85dckcy5xKP+UoXeuEmFceauQDczUcGs19SRGSQ==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-win32-ia32-msvc": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.4.tgz", "integrity": "sha512-RSYHfdKgNXV/amY5Tqk1EWVsyQnhlsM//jeqMLw5Fy9rfxP592W9UTumNikNRPdjI8wKKzNMXDb1U29tQjN0dg==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/core-win32-x64-msvc": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.4.tgz", "integrity": "sha512-1ujYpaqfqNPYdwKBlvJnOqcl+Syn3UrQ4XE0Txz6zMYgyh6cdU6a3pxqLqIUSJ12MtXRA9ZUhEz1ekU3LfLWXw==", "dev": true, - "optional": true, - "peer": true + "optional": true }, "@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@swc/types": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz", "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", "dev": true, - "optional": true, - "peer": true, "requires": { "@swc/counter": "^0.1.3" } @@ -10843,20 +11341,20 @@ "dev": true }, "@xterm/addon-unicode11": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.8.0.tgz", - "integrity": "sha512-LxinXu8SC4OmVa6FhgwsVCBZbr8WoSGzBl2+vqe8WcQ6hb1r6Gj9P99qTNdPiFPh4Ceiu2pC8xukZ6+2nnh49Q==", - "requires": {} + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0.tgz", + "integrity": "sha512-FxDnYcyuXhNl+XSqGZL/t0U9eiNb/q3EWT5rYkQT/zuig8Gz/VagnQANKHdDWFM2lTMk9ly0EFQxxxtZUoRetw==" }, "@xterm/headless": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.5.0.tgz", - "integrity": "sha512-5xXB7kdQlFBP82ViMJTwwEc3gKCLGKR/eoxQm4zge7GPBl86tCdI0IdPJjoKd8mUSFXz5V7i/25sfsEkP4j46g==" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-6.0.0.tgz", + "integrity": "sha512-5Yj1QINYCyzrZtf8OFIHi47iQtI+0qYFPHmouEfG8dHNxbZ9Tb9YGSuLcsEwj9Z+OL75GJqPyJbyoFer80a2Hw==" }, "@xterm/xterm": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", - "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz", + "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==", + "dev": true }, "acorn": { "version": "8.10.0", @@ -12232,9 +12730,9 @@ } }, "flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, "for-each": { @@ -12246,6 +12744,24 @@ "is-callable": "^1.1.3" } }, + "foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -12854,6 +13370,16 @@ "set-function-name": "^2.0.1" } }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -13947,6 +14473,12 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -14014,6 +14546,12 @@ "brace-expansion": "^1.1.7" } }, + "minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -14205,6 +14743,12 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -14226,6 +14770,12 @@ "lines-and-columns": "^1.1.6" } }, + "parse-ms": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-3.0.0.tgz", + "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", + "dev": true + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -14250,6 +14800,16 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -14320,6 +14880,15 @@ } } }, + "pretty-ms": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz", + "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", + "dev": true, + "requires": { + "parse-ms": "^3.0.0" + } + }, "prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -14349,6 +14918,17 @@ } } }, + "proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, "punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -14474,6 +15054,12 @@ } } }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -14671,6 +15257,46 @@ "strip-ansi": "^7.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "string.prototype.matchall": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", @@ -14729,6 +15355,23 @@ "ansi-regex": "^6.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + } + } + }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -15281,6 +15924,12 @@ "has-tostringtag": "^1.0.0" } }, + "workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, "wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -15291,6 +15940,66 @@ "strip-ansi": "^7.0.1" } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index c0dcc10c..af734013 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "package:base": "tsx ./scripts/pkg-base.ts", "dev": "node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON --import=tsx src/index.ts -V", "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", + "test:e2e": "npx @microsoft/tui-test", "lint": "eslint src/ --ext .ts,.tsx && prettier src/ --check", "lint:fix": "eslint src/ --ext .ts,.tsx --fix && prettier src/ --write", "debug": "node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON --inspect --import=tsx src/index.ts -V" @@ -41,8 +42,8 @@ "homepage": "https://github.com/microsoft/inshellisense#readme", "dependencies": { "@withfig/autocomplete": "2.675.0", - "@xterm/addon-unicode11": "^0.8.0", - "@xterm/headless": "^5.5.0", + "@xterm/addon-unicode11": "^0.9.0", + "@xterm/headless": "^6.0.0", "ajv": "^8.12.0", "ansi-escapes": "^6.2.0", "ansi-styles": "^6.2.1", @@ -58,6 +59,7 @@ "wrap-ansi": "^8.1.0" }, "devDependencies": { + "@microsoft/tui-test": "^0.0.3", "@tsconfig/node18": "^18.2.2", "@types/color-convert": "^2.0.3", "@types/jest": "^29.5.5", @@ -68,7 +70,7 @@ "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", "@withfig/autocomplete-types": "^1.28.0", - "@xterm/xterm": "^5.5.0", + "@xterm/xterm": "^6.0.0", "esbuild": "^0.27.2", "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", diff --git a/shell/shellIntegration-rc.zsh b/shell/shellIntegration-rc.zsh index 16e217a9..a5402b7f 100644 --- a/shell/shellIntegration-rc.zsh +++ b/shell/shellIntegration-rc.zsh @@ -47,20 +47,26 @@ __is_update_cwd() { builtin printf '\e]6973;CWD;%s\a' "$(__is_escape_value "${PWD}")" } -__is_update_prompt() { - __is_prior_prompt="$PS1" +__is_precmd() { + __is_update_cwd if [[ $ISTERM_TESTING == "1" ]]; then - __is_prior_prompt="> " + PS1="%{$(__is_prompt_start)%}> %{$(__is_prompt_end)%}" fi - PS1="%{$(__is_prompt_start)%}$__is_prior_prompt%{$(__is_prompt_end)%}" } -__is_precmd() { - if [[ $PS1 != *"$(__is_prompt_start)"* ]]; then - __is_update_prompt - fi - __is_update_cwd -} +if (( ${+widgets[zle-line-init]} )); then + zle -A zle-line-init __is_orig_zle_line_init + __is_zle_line_init() { + __is_prompt_start + zle __is_orig_zle_line_init + __is_prompt_end + } +else + __is_zle_line_init() { + __is_prompt_start + __is_prompt_end + } +fi +zle -N zle-line-init __is_zle_line_init -__is_update_prompt add-zsh-hook precmd __is_precmd \ No newline at end of file diff --git a/src/commands/root.ts b/src/commands/root.ts index a780278e..18980176 100644 --- a/src/commands/root.ts +++ b/src/commands/root.ts @@ -48,7 +48,7 @@ export const action = (program: Command) => async (options: RootCommandOptions) program.error(`Unsupported shell: '${shell}', supported shells: ${supportedShells}`, { exitCode: 1 }); } - await Promise.all([loadLocalSpecsSet(), loadAliases(shell), shell == Shell.Zsh ? setupZshDotfiles() : Promise.resolve()]); + await Promise.all([loadLocalSpecsSet(), loadAliases(shell), shell == Shell.Zsh ? setupZshDotfiles(options.test ?? false) : Promise.resolve()]); await render(program, shell, options.test ?? false, options.login ?? false); }; diff --git a/src/isterm/commandManager.ts b/src/isterm/commandManager.ts index bfab4432..c01a7cf4 100644 --- a/src/isterm/commandManager.ts +++ b/src/isterm/commandManager.ts @@ -28,7 +28,6 @@ export class CommandManager { #activeCommand: TerminalCommand; #terminal: Terminal; #acceptedCommandLines: Set; - #maxCursorY: number; #shell: Shell; #promptRewrites: boolean; @@ -36,7 +35,6 @@ export class CommandManager { this.#terminal = terminal; this.#shell = shell; this.#activeCommand = {}; - this.#maxCursorY = 0; this.#acceptedCommandLines = new Set(); this.#promptRewrites = getShellPromptRewrites(shell); @@ -52,14 +50,13 @@ export class CommandManager { } handlePromptEnd() { - if (this.#activeCommand.promptEndMarker != null) return; if (this.#hasBeenAccepted()) { this.#activeCommand = {}; return; } this.#activeCommand.promptEndMarker = this.#terminal.registerMarker(0); - if (this.#activeCommand.promptEndMarker?.line === this.#terminal.buffer.active.cursorY) { + if (this.#activeCommand.promptEndMarker?.line === this.#terminal.buffer.active.baseY + this.#terminal.buffer.active.cursorY) { this.#activeCommand.promptEndX = this.#terminal.buffer.active.cursorX; } @@ -76,8 +73,8 @@ export class CommandManager { const whitespace = " ".repeat(commandWhitespaceTerminationWidth); const whitespaceIdx = promptLineText.indexOf(whitespace); if (whitespaceIdx != -1) { - this.#activeCommand.promptText = promptLineText.substring(0, whitespaceIdx) + 1; - this.#activeCommand.promptEndX = whitespaceIdx; + this.#activeCommand.promptText = promptLineText.substring(0, whitespaceIdx + 1); + this.#activeCommand.promptEndX = whitespaceIdx + 1; } } } @@ -90,7 +87,6 @@ export class CommandManager { handleClear() { this.handlePromptStart(); - this.#maxCursorY = 0; this.#acceptedCommandLines.clear(); } @@ -157,7 +153,7 @@ export class CommandManager { let suggestion = ""; const whitespace = " ".repeat(commandWhitespaceTerminationWidth); for (const [y, line] of commandLines.entries()) { - const startX = y == 0 ? this.#activeCommand.promptText?.length ?? 0 : 0; + const startX = y == 0 ? this.#activeCommand.promptEndX ?? this.#activeCommand.promptText?.length ?? 0 : 0; for (let x = startX; x < this.#terminal.cols; x++) { if (postCursorCommand.endsWith(whitespace)) break; // assume that a command that ends with 4 spaces is terminated, avoids capturing right prompts @@ -203,16 +199,13 @@ export class CommandManager { } const globalCursorPosition = this.#terminal.buffer.active.baseY + this.#terminal.buffer.active.cursorY; - this.#maxCursorY = Math.max(this.#maxCursorY, globalCursorPosition); - if (globalCursorPosition < this.#activeCommand.promptStartMarker.line || globalCursorPosition < this.#maxCursorY) { - this.handleClear(); + if (this.#activeCommand.promptStartMarker.isDisposed || this.#activeCommand.promptEndMarker.isDisposed) { + log.debug({ msg: "markers disposed, re-registering" }); + this.#activeCommand.promptStartMarker = this.#terminal.registerMarker(0); this.#activeCommand.promptEndMarker = this.#terminal.registerMarker(0); - return; } - if (this.#activeCommand.promptEndMarker == null) return; - // if the prompt is set, now parse out the values from the terminal if (this.#activeCommand.promptText != null) { const commandLines = this._getCommandLines(); diff --git a/src/isterm/pty.ts b/src/isterm/pty.ts index d0fff557..d8d1d990 100644 --- a/src/isterm/pty.ts +++ b/src/isterm/pty.ts @@ -13,7 +13,12 @@ import type { IPty, IEvent } from "node-pty"; import { Shell, userZdotdir, zdotdir } from "../utils/shell.js"; import { IsTermOscPs, IstermOscPt, IstermPromptStart, IstermPromptEnd } from "../utils/ansi.js"; import xterm from "@xterm/headless"; -import type { ICellData } from "@xterm/xterm/src/common/Types.js"; +import type { IBufferCell } from "@xterm/xterm"; + +interface ICellData extends IBufferCell { + extended: { underlineStyle: number }; + hasExtendedAttrs(): number; +} import { CommandManager, CommandState } from "./commandManager.js"; import log from "../utils/log.js"; import { gitBashPath } from "../utils/shell.js"; @@ -286,7 +291,10 @@ export class ISTerm implements IPty { getPatch(height: number, patches: ISTermPatch[], direction: "below" | "above"): string { const currentCursorPosition = this.#term.buffer.active.cursorY + this.#term.buffer.active.baseY; + const viewportStart = this.#term.buffer.active.baseY; + const viewportEnd = this.#term.buffer.active.baseY + this.#term.rows - 1; const writeLine = (y: number, patch?: ISTermPatch): string => { + if (y < viewportStart || y > viewportEnd) return ""; const line = this.#term.buffer.active.getLine(y); const hasPatch = patch != null; const ansiPrePatch = [ansi.resetColor, ansi.resetLine]; @@ -437,7 +445,7 @@ const convertToPtyEnv = (shell: Shell, underTest: boolean, login: boolean) => { return { ...env, PROMPT: `${IstermPromptStart}${prompt}${IstermPromptEnd}` }; } case Shell.Zsh: { - return { ...env, ZDOTDIR: zdotdir, USER_ZDOTDIR: userZdotdir }; + return { ...env, ZDOTDIR: zdotdir(underTest), USER_ZDOTDIR: userZdotdir }; } } diff --git a/src/tests/fixtures/ohmyzsh/.p10k.zsh b/src/tests/fixtures/ohmyzsh/.p10k.zsh new file mode 100644 index 00000000..93836369 --- /dev/null +++ b/src/tests/fixtures/ohmyzsh/.p10k.zsh @@ -0,0 +1,39 @@ +# Minimal powerlevel10k config for E2E testing. +# Produces a deterministic "> " prompt with no colors or async rendering. + +'builtin' 'local' '-a' 'p10k_config_opts' +[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases') +[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob') +[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand') +'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand' + +() { + emulate -L zsh -o extended_glob + + unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR' + + typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(prompt_char) + typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=() + + typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=false + typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=off + typeset -g POWERLEVEL9K_INSTANT_PROMPT=off + + typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_CONTENT_EXPANSION='>' + typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_CONTENT_EXPANSION='>' + + typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND= + typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND= + + typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=false + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL=' ' + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_{LEFT,RIGHT}_WHITESPACE= + + typeset -g POWERLEVEL9K_BACKGROUND= + typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR= + typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_SEPARATOR= + typeset -g POWERLEVEL9K_DISABLE_RPROMPT=true + + (( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]} + 'builtin' 'unset' 'p10k_config_opts' +} diff --git a/src/tests/fixtures/ohmyzsh/.zshrc b/src/tests/fixtures/ohmyzsh/.zshrc new file mode 100644 index 00000000..7ac6b865 --- /dev/null +++ b/src/tests/fixtures/ohmyzsh/.zshrc @@ -0,0 +1,10 @@ +# Minimal .zshrc for oh-my-zsh + powerlevel10k E2E testing. +# This file is used as ZDOTDIR/.zshrc by the test fixture. + +export ZSH="$HOME/.oh-my-zsh" +ZSH_THEME="powerlevel10k/powerlevel10k" +plugins=(git) + +source "$ZSH/oh-my-zsh.sh" + +[[ ! -f "${ZDOTDIR:-$HOME}/.p10k.zsh" ]] || source "${ZDOTDIR:-$HOME}/.p10k.zsh" diff --git a/src/tests/ui/autocomplete.test.ts b/src/tests/ui/autocomplete.test.ts new file mode 100644 index 00000000..64469d89 --- /dev/null +++ b/src/tests/ui/autocomplete.test.ts @@ -0,0 +1,201 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { test, expect, Shell } from "@microsoft/tui-test"; +import os from "node:os"; +import path from "node:path"; +import fs from "node:fs"; +import url from "node:url"; + +type ShellConfig = { + label: string; + shell: string; + env?: Record; +}; + +const ohmyzshFixtureDir = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), "..", "fixtures", "ohmyzsh"); +const hasOhMyZsh = os.platform() !== "win32" && fs.existsSync(path.join(os.homedir(), ".oh-my-zsh")); + +const windowsConfigs: ShellConfig[] = [ + { label: Shell.Cmd, shell: Shell.Cmd }, + { label: Shell.Powershell, shell: Shell.Powershell }, + { label: Shell.WindowsPowershell, shell: Shell.WindowsPowershell }, + { label: Shell.Xonsh, shell: Shell.Xonsh }, +]; +const unixConfigs: ShellConfig[] = [ + { label: Shell.Bash, shell: Shell.Bash }, + { label: Shell.Fish, shell: Shell.Fish }, + { label: Shell.Zsh, shell: Shell.Zsh }, + ...(hasOhMyZsh ? [{ label: "zsh-ohmyzsh", shell: Shell.Zsh, env: { ...process.env, USER_ZDOTDIR: ohmyzshFixtureDir } }] : []), +]; +const configs = os.platform() == "win32" ? windowsConfigs : unixConfigs; + +configs.map((config) => { + const returnChar = config.shell == "xonsh" ? "\n" : "\r"; + test.describe(`[${config.label}]`, () => { + test.use({ program: { file: "is", args: ["-V", "-T", "-s", config.shell] }, ...(config.env ? { env: config.env } : {}) }); + + test("basic git suggestions", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + terminal.write("git "); + + await expect(terminal.getByText("blame")).toBeVisible(); + await expect(terminal.getByText("archive", { strict: false })).toHaveBgColor("7d56f4"); + }); + + test("cursor up when at top of list", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + terminal.write("git "); + + await expect(terminal.getByText("archive", { strict: false })).toHaveBgColor("7d56f4"); + terminal.keyUp(); + await expect(terminal.getByText("archive", { strict: false })).toHaveBgColor("7d56f4"); + }); + + test("move cursor backwards to hide suggestions", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + terminal.write("git "); + + await expect(terminal.getByText("archive", { strict: false })).toBeVisible(); + + terminal.keyLeft(); + await expect(terminal.getByText("archive", { strict: false })).not.toBeVisible(); + }); + + test("cursor down when at top of list", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + terminal.write("git "); + + await expect(terminal.getByText("archive", { strict: false })).toBeVisible(); + terminal.keyDown(2); + + await expect(terminal.getByText("repository")).toBeVisible(); + await expect(terminal.getByText("commit")).toHaveBgColor("7d56f4"); + await expect(terminal.getByText("archive", { strict: false })).not.toHaveBgColor("7d56f4"); + }); + + test("scroll down a full page when at top of list", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + terminal.write("git "); + + await expect(terminal.getByText("archive", { strict: false })).toBeVisible(); + terminal.keyDown(5); + + await expect(terminal.getByText("archive", { strict: false })).not.toBeVisible(); + await expect(terminal.getByText("add")).toHaveBgColor("7d56f4"); + }); + + // excluding cmd since it doesn't support CWD tracking + test.when(config.shell !== Shell.Cmd, "generator results lead suggestions", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + terminal.write("ls "); + + await expect(terminal.getByText("📄", { strict: false })).toBeVisible(); + }); + + test("tab completion", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + terminal.write("git "); + + await expect(terminal.getByText("archive", { strict: false })).toBeVisible(); + terminal.write("\t"); + + await expect(terminal.getByText("--format")).toBeVisible(); + }); + + test("backspacing after accepting tab completion", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + terminal.write("git "); + + await expect(terminal.getByText("archive", { strict: false })).toBeVisible(); + terminal.write("\t"); + + await expect(terminal.getByText("--format")).toBeVisible(); + terminal.keyBackspace(3); + + await expect(terminal.getByText("archive", { strict: false })).toBeVisible(); + }); + + test("suggestion cursor resets between views", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + terminal.write("git "); + + await expect(terminal.getByText("archive", { strict: false })).toBeVisible(); + terminal.keyDown(2); + + await expect(terminal.getByText("repository")).toBeVisible(); + await expect(terminal.getByText("commit")).toHaveBgColor("7d56f4"); + + terminal.keyBackspace(1); + await expect(terminal.getByText("repository")).not.toBeVisible(); + + terminal.write(" "); + await expect(terminal.getByText("archive", { strict: false })).toHaveBgColor("7d56f4"); + }); + + test("ui on bottom of the screen", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + terminal.resize(80, 10); + terminal.write(returnChar.repeat(10)); + await expect(terminal.getByText("> ")).toBeVisible(); + + terminal.write("git "); + await expect(terminal.getByText("archive", { strict: false })).toBeVisible(); + }); + + test("command detection after command execution", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + + terminal.write(`echo "hello"${returnChar}`); + await expect(terminal.getByText("hello", { strict: false })).toBeVisible(); + await expect(terminal.getByText("> ")).toBeVisible(); + + terminal.write("git "); + await expect(terminal.getByText("archive", { strict: false })).toBeVisible(); + }); + + test("suggestions clear after command execution", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + + terminal.write("git "); + await expect(terminal.getByText("archive", { strict: false })).toBeVisible(); + + terminal.write(returnChar); + await expect(terminal.getByText("archive", { strict: false })).not.toBeVisible(); + }); + + test.skip("access history when no suggestions exist", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + + terminal.write("clear"); + await expect(terminal.getByText("clear")).toBeVisible(); + + terminal.write(returnChar); + await expect(terminal.getByText("clear")).not.toBeVisible(); + + terminal.keyUp(); + await expect(terminal.getByText("clear")).toBeVisible(); + }); + + test("proper overflow truncation in command", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + + terminal.write("dotnet add package Holoon.Newtonsoft"); + await expect(terminal.getByText("CanBeUndefi…│")).toBeVisible(); + }); + + test.skip("command detection with suggestions", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + + terminal.write(`dotnet add item${returnChar}`); + await expect(terminal.getByText("dotnet", { strict: false })).toBeVisible(); + + terminal.write(`clear${returnChar}`); + await expect(terminal.getByText("dotnet", { strict: false })).not.toBeVisible(); + + terminal.write("dotnet add "); + await expect(terminal.getByText("item")).toBeVisible(); + await expect(terminal.getByText("package", { strict: false })).toBeVisible(); + }); + }); +}); diff --git a/src/tests/ui/status.test.ts b/src/tests/ui/status.test.ts new file mode 100644 index 00000000..31f97c56 --- /dev/null +++ b/src/tests/ui/status.test.ts @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { test, expect, Shell } from "@microsoft/tui-test"; +import os from "node:os"; + +const shell = os.platform() == "darwin" ? Shell.Zsh : os.platform() == "linux" ? Shell.Bash : Shell.Powershell; + +test.describe("status checks", () => { + test.describe("inside inshellisense session", () => { + test.use({ program: { file: "is", args: ["-T", "-s", shell] } }); + + test("current status", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible({ timeout: 30_000 }); + + terminal.write("is -c\r"); + await expect(terminal.getByText("live")).toBeVisible(); + await expect(terminal.getByText("live")).toHaveFgColor(2); + }); + }); + + test.describe("outside inshellisense session", () => { + test("current status", async ({ terminal }) => { + await expect(terminal.getByText("> ")).toBeVisible(); + + terminal.write("is -c\r"); + await expect(terminal.getByText("not found")).toBeVisible(); + await expect(terminal.getByText("not found")).toHaveFgColor(1); + }); + }); +}); diff --git a/src/utils/shell.ts b/src/utils/shell.ts index 137712d4..c14d958c 100644 --- a/src/utils/shell.ts +++ b/src/utils/shell.ts @@ -52,7 +52,7 @@ export const initSupportedShells = supportedShells.filter((shell) => shell != Sh export const aliasSupportedShells = [Shell.Bash, Shell.Zsh]; export const userZdotdir = process.env?.ZDOTDIR ?? os.homedir() ?? `~`; -export const zdotdir = path.join(os.tmpdir(), `is-zsh`); +export const zdotdir = (underTest: boolean) => path.join(os.tmpdir(), underTest ? `is-zsh-${process.pid}` : `is-zsh`); export const checkShellConfigs = (): Shell[] => { const shellsWithoutConfigs: Shell[] = []; @@ -153,11 +153,13 @@ const getShellConfigName = (shell: Shell) => { } }; -export const setupZshDotfiles = async () => { - await fsAsync.cp(path.join(shellResourcesPath, "shellIntegration-rc.zsh"), path.join(zdotdir, ".zshrc")); - await fsAsync.cp(path.join(shellResourcesPath, "shellIntegration-profile.zsh"), path.join(zdotdir, ".zprofile")); - await fsAsync.cp(path.join(shellResourcesPath, "shellIntegration-env.zsh"), path.join(zdotdir, ".zshenv")); - await fsAsync.cp(path.join(shellResourcesPath, "shellIntegration-login.zsh"), path.join(zdotdir, ".zlogin")); +export const setupZshDotfiles = async (underTest: boolean) => { + const dir = zdotdir(underTest); + await fsAsync.mkdir(dir, { recursive: true }); + await fsAsync.cp(path.join(shellResourcesPath, "shellIntegration-rc.zsh"), path.join(dir, ".zshrc"), { force: true }); + await fsAsync.cp(path.join(shellResourcesPath, "shellIntegration-profile.zsh"), path.join(dir, ".zprofile"), { force: true }); + await fsAsync.cp(path.join(shellResourcesPath, "shellIntegration-env.zsh"), path.join(dir, ".zshenv"), { force: true }); + await fsAsync.cp(path.join(shellResourcesPath, "shellIntegration-login.zsh"), path.join(dir, ".zlogin"), { force: true }); }; const findParentProcess = async () => { diff --git a/tui-test.config.ts b/tui-test.config.ts new file mode 100644 index 00000000..3947edc7 --- /dev/null +++ b/tui-test.config.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { defineConfig } from "@microsoft/tui-test"; + +export default defineConfig({ + testMatch: "src/tests/ui/**/*.@(spec|test).?(c|m)[jt]s?(x)", + retries: 3, + expect: { + timeout: 10_000, + }, +}); \ No newline at end of file