diff --git a/package-lock.json b/package-lock.json index b0292d26c..8821e145e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "devDependencies": { "concurrently": "^9.2.1", "get-port-cli": "^3.0.0", + "knip": "^5.82.1", "patch-package": "^8.0.1", "typescript": "^5.9.3" } @@ -1848,9 +1849,9 @@ } }, "node_modules/@emnapi/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.6.0.tgz", - "integrity": "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", "dev": true, "license": "MIT", "optional": true, @@ -6340,6 +6341,306 @@ "node": ">=8.0.0" } }, + "node_modules/@oxc-resolver/binding-android-arm-eabi": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.16.4.tgz", + "integrity": "sha512-6XUHilmj8D6Ggus+sTBp64x/DUQ7LgC/dvTDdUOt4iMQnDdSep6N1mnvVLIiG+qM5tRnNHravNzBJnUlYwRQoA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-android-arm64": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.16.4.tgz", + "integrity": "sha512-5ODwd1F5mdkm6JIg1CNny9yxIrCzrkKpxmqas7Alw23vE0Ot8D4ykqNBW5Z/nIZkXVEo5VDmnm0sMBBIANcpeQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-arm64": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.16.4.tgz", + "integrity": "sha512-egwvDK9DMU4Q8F4BG74/n4E22pQ0lT5ukOVB6VXkTj0iG2fnyoStHoFaBnmDseLNRA4r61Mxxz8k940CIaJMDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-x64": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.16.4.tgz", + "integrity": "sha512-HMkODYrAG4HaFNCpaYzSQFkxeiz2wzl+smXwxeORIQVEo1WAgUrWbvYT/0RNJg/A8z2aGMGK5KWTUr2nX5GiMw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-freebsd-x64": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.16.4.tgz", + "integrity": "sha512-mkcKhIdSlUqnndD928WAVVFMEr1D5EwHOBGHadypW0PkM0h4pn89ZacQvU7Qs/Z2qquzvbyw8m4Mq3jOYI+4Dw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.16.4.tgz", + "integrity": "sha512-ZJvzbmXI/cILQVcJL9S2Fp7GLAIY4Yr6mpGb+k6LKLUSEq85yhG+rJ9eWCqgULVIf2BFps/NlmPTa7B7oj8jhQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.16.4.tgz", + "integrity": "sha512-iZUB0W52uB10gBUDAi79eTnzqp1ralikCAjfq7CdokItwZUVJXclNYANnzXmtc0Xr0ox+YsDsG2jGcj875SatA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.16.4.tgz", + "integrity": "sha512-qNQk0H6q1CnwS9cnvyjk9a+JN8BTbxK7K15Bb5hYfJcKTG1hfloQf6egndKauYOO0wu9ldCMPBrEP1FNIQEhaA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-musl": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.16.4.tgz", + "integrity": "sha512-wEXSaEaYxGGoVSbw0i2etjDDWcqErKr8xSkTdwATP798efsZmodUAcLYJhN0Nd4W35Oq6qAvFGHpKwFrrhpTrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.16.4.tgz", + "integrity": "sha512-CUFOlpb07DVOFLoYiaTfbSBRPIhNgwc/MtlYeg3p6GJJw+kEm/vzc9lohPSjzF2MLPB5hzsJdk+L/GjrTT3UPw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.16.4.tgz", + "integrity": "sha512-d8It4AH8cN9ReK1hW6ZO4x3rMT0hB2LYH0RNidGogV9xtnjLRU+Y3MrCeClLyOSGCibmweJJAjnwB7AQ31GEhg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.16.4.tgz", + "integrity": "sha512-d09dOww9iKyEHSxuOQ/Iu2aYswl0j7ExBcyy14D6lJ5ijQSP9FXcJYJsJ3yvzboO/PDEFjvRuF41f8O1skiPVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.16.4.tgz", + "integrity": "sha512-lhjyGmUzTWHduZF3MkdUSEPMRIdExnhsqv8u1upX3A15epVn6YVwv4msFQPJl1x1wszkACPeDHGOtzHsITXGdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-gnu": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.16.4.tgz", + "integrity": "sha512-ZtqqiI5rzlrYBm/IMMDIg3zvvVj4WO/90Dg/zX+iA8lWaLN7K5nroXb17MQ4WhI5RqlEAgrnYDXW+hok1D9Kaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-musl": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.16.4.tgz", + "integrity": "sha512-LM424h7aaKcMlqHnQWgTzO+GRNLyjcNnMpqm8SygEtFRVW693XS+XGXYvjORlmJtsyjo84ej1FMb3U2HE5eyjg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-openharmony-arm64": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.16.4.tgz", + "integrity": "sha512-8w8U6A5DDWTBv3OUxSD9fNk37liZuEC5jnAc9wQRv9DeYKAXvuUtBfT09aIZ58swaci0q1WS48/CoMVEO6jdCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.16.4.tgz", + "integrity": "sha512-hnjb0mDVQOon6NdfNJ1EmNquonJUjoYkp7UyasjxVa4iiMcApziHP4czzzme6WZbp+vzakhVv2Yi5ACTon3Zlw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.16.4.tgz", + "integrity": "sha512-+i0XtNfSP7cfnh1T8FMrMm4HxTeh0jxKP/VQCLWbjdUxaAQ4damho4gN9lF5dl0tZahtdszXLUboBFNloSJNOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxc-resolver/binding-win32-ia32-msvc": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.16.4.tgz", + "integrity": "sha512-ePW1islJrv3lPnef/iWwrjrSpRH8kLlftdKf2auQNWvYLx6F0xvcnv9d+r/upnVuttoQY9amLnWJf+JnCRksTw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxc-resolver/binding-win32-x64-msvc": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.16.4.tgz", + "integrity": "sha512-qnjQhjHI4TDL3hkidZyEmQRK43w2NHl6TP5Rnt/0XxYuLdEgx/1yzShhYidyqWzdnhGhSPTM/WVP2mK66XLegA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@paseo/app": { "resolved": "packages/app", "link": true @@ -14834,6 +15135,16 @@ "asap": "~2.0.3" } }, + "node_modules/fd-package-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz", + "integrity": "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "walk-up-path": "^4.0.0" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -15123,6 +15434,22 @@ "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", "license": "MIT" }, + "node_modules/formatly": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz", + "integrity": "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fd-package-json": "^2.0.0" + }, + "bin": { + "formatly": "bin/index.mjs" + }, + "engines": { + "node": ">=18.3.0" + } + }, "node_modules/formdata-node": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", @@ -17208,6 +17535,124 @@ "node": ">=6" } }, + "node_modules/knip": { + "version": "5.82.1", + "resolved": "https://registry.npmjs.org/knip/-/knip-5.82.1.tgz", + "integrity": "sha512-1nQk+5AcnkqL40kGQXfouzAEXkTR+eSrgo/8m1d0BMei4eAzFwghoXC4gOKbACgBiCof7hE8wkBVDsEvznf85w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/webpro" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/knip" + } + ], + "license": "ISC", + "dependencies": { + "@nodelib/fs.walk": "^1.2.3", + "fast-glob": "^3.3.3", + "formatly": "^0.3.0", + "jiti": "^2.6.0", + "js-yaml": "^4.1.1", + "minimist": "^1.2.8", + "oxc-resolver": "^11.15.0", + "picocolors": "^1.1.1", + "picomatch": "^4.0.1", + "smol-toml": "^1.5.2", + "strip-json-comments": "5.0.3", + "zod": "^4.1.11" + }, + "bin": { + "knip": "bin/knip.js", + "knip-bun": "bin/knip-bun.js" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "@types/node": ">=18", + "typescript": ">=5.0.4 <7" + } + }, + "node_modules/knip/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/knip/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/knip/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/knip/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/knip/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/knip/node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lan-network": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/lan-network/-/lan-network-0.1.7.tgz", @@ -19226,6 +19671,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oxc-resolver": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.16.4.tgz", + "integrity": "sha512-nvJr3orFz1wNaBA4neRw7CAn0SsjgVaEw1UHpgO/lzVW12w+nsFnvU/S6vVX3kYyFaZdxZheTExi/fa8R8PrZA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-resolver/binding-android-arm-eabi": "11.16.4", + "@oxc-resolver/binding-android-arm64": "11.16.4", + "@oxc-resolver/binding-darwin-arm64": "11.16.4", + "@oxc-resolver/binding-darwin-x64": "11.16.4", + "@oxc-resolver/binding-freebsd-x64": "11.16.4", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.16.4", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.16.4", + "@oxc-resolver/binding-linux-arm64-gnu": "11.16.4", + "@oxc-resolver/binding-linux-arm64-musl": "11.16.4", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.16.4", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.16.4", + "@oxc-resolver/binding-linux-riscv64-musl": "11.16.4", + "@oxc-resolver/binding-linux-s390x-gnu": "11.16.4", + "@oxc-resolver/binding-linux-x64-gnu": "11.16.4", + "@oxc-resolver/binding-linux-x64-musl": "11.16.4", + "@oxc-resolver/binding-openharmony-arm64": "11.16.4", + "@oxc-resolver/binding-wasm32-wasi": "11.16.4", + "@oxc-resolver/binding-win32-arm64-msvc": "11.16.4", + "@oxc-resolver/binding-win32-ia32-msvc": "11.16.4", + "@oxc-resolver/binding-win32-x64-msvc": "11.16.4" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -22504,6 +22981,19 @@ "node": ">=8.0.0" } }, + "node_modules/smol-toml": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", + "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, "node_modules/sonic-boom": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", @@ -24520,6 +25010,16 @@ "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", "license": "MIT" }, + "node_modules/walk-up-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 0f9cf3601..91765d94a 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "devDependencies": { "concurrently": "^9.2.1", "get-port-cli": "^3.0.0", + "knip": "^5.82.1", "patch-package": "^8.0.1", "typescript": "^5.9.3" }, diff --git a/packages/app/app.config.js b/packages/app/app.config.js index 4f7492a8a..734d11027 100644 --- a/packages/app/app.config.js +++ b/packages/app/app.config.js @@ -1,8 +1,6 @@ // App name and package ID are controlled by Gradle product flavors (dev/production) // See plugins/with-android-product-flavors.js for flavor configuration -const withAndroidProductFlavors = require("./plugins/with-android-product-flavors"); - export default { expo: { name: "Paseo", @@ -91,7 +89,7 @@ export default { }, }, ], - withAndroidProductFlavors, + "./plugins/with-android-product-flavors", ], experiments: { typedRoutes: true, diff --git a/packages/app/src/app/_layout.tsx b/packages/app/src/app/_layout.tsx index c32aa04fb..7aab73946 100644 --- a/packages/app/src/app/_layout.tsx +++ b/packages/app/src/app/_layout.tsx @@ -347,7 +347,6 @@ export default function RootLayout() { > - diff --git a/packages/app/src/app/orchestrator.tsx b/packages/app/src/app/orchestrator.tsx deleted file mode 100644 index 505e78104..000000000 --- a/packages/app/src/app/orchestrator.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { View } from "react-native"; -import { useMemo, useRef, useState } from "react"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller"; -import ReanimatedAnimated, { useAnimatedStyle } from "react-native-reanimated"; -import { StyleSheet, useUnistyles } from "react-native-unistyles"; -import { BackHeader } from "@/components/headers/back-header"; -import { OrchestratorMessagesView } from "@/components/orchestrator-messages-view"; -import { useSessionDirectory } from "@/hooks/use-session-directory"; -import type { ScrollView } from "react-native"; -import type { Artifact } from "@/components/artifact-drawer"; -import type { MessageEntry } from "@/contexts/session-context"; - -export default function OrchestratorScreen() { - const { theme } = useUnistyles(); - const insets = useSafeAreaInsets(); - const sessionDirectory = useSessionDirectory(); - const scrollViewRef = useRef(null); - const [currentArtifact, setCurrentArtifact] = useState(null); - const aggregatedMessages = useMemo(() => { - const merged: MessageEntry[] = []; - sessionDirectory.forEach((session) => { - if (!session) { - return; - } - merged.push(...session.messages); - }); - merged.sort((left, right) => left.timestamp - right.timestamp); - return merged; - }, [sessionDirectory]); - - const streamingAssistantMessage = useMemo(() => { - for (const session of sessionDirectory.values()) { - if (session?.currentAssistantMessage) { - return session.currentAssistantMessage; - } - } - return ""; - }, [sessionDirectory]); - - // Keyboard animation - const { height: keyboardHeight } = useReanimatedKeyboardAnimation(); - const animatedKeyboardStyle = useAnimatedStyle(() => { - "worklet"; - const absoluteHeight = Math.abs(keyboardHeight.value); - const padding = Math.max(0, absoluteHeight - insets.bottom); - return { - paddingBottom: padding, - }; - }); - - function handleArtifactClick(artifactId: string) { - // TODO: Implement artifact drawer - console.log("[Orchestrator] Artifact clicked:", artifactId); - } - - return ( - - {/* Header */} - - - {/* Content Area with Keyboard Animation */} - - - - - ); -} - -const styles = StyleSheet.create((theme) => ({ - container: { - flex: 1, - backgroundColor: theme.colors.surface0, - }, - content: { - flex: 1, - }, -})); diff --git a/packages/app/src/components/agent-stream-view.tsx b/packages/app/src/components/agent-stream-view.tsx index c7136e166..f2c4565ec 100644 --- a/packages/app/src/components/agent-stream-view.tsx +++ b/packages/app/src/components/agent-stream-view.tsx @@ -34,7 +34,6 @@ import { UserMessage, ActivityLog, ToolCall, - AgentThoughtMessage, TodoListCard, TurnCopyButton, MessageOuterSpacingProvider, @@ -49,7 +48,6 @@ import type { DaemonClientV2 } from "@server/client/daemon-client-v2"; import { parseToolCallDisplay } from "@/utils/tool-call-parsers"; import { ToolCallDetailsContent } from "./tool-call-details"; import { ToolCallSheetProvider } from "./tool-call-sheet"; -import { ThinkingSheetProvider } from "./thinking-sheet"; import { createMarkdownStyles } from "@/styles/markdown-styles"; import { MAX_CONTENT_WIDTH } from "@/constants/layout"; import { isPerfLoggingEnabled, measurePayload, perfLog } from "@/utils/perf"; @@ -274,9 +272,10 @@ export function AgentStreamView({ const isLastInSequence = nextItem?.kind !== "tool_call" && nextItem?.kind !== "thought"; return ( - ); @@ -533,8 +532,7 @@ export function AgentStreamView({ return ( - - + )} - ); } diff --git a/packages/app/src/components/agent-thought-content.tsx b/packages/app/src/components/agent-thought-content.tsx deleted file mode 100644 index 1515b548d..000000000 --- a/packages/app/src/components/agent-thought-content.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { memo, useMemo } from "react"; -import { Text, View } from "react-native"; -import Markdown from "react-native-markdown-display"; -import { StyleSheet, useUnistyles } from "react-native-unistyles"; -import { createCompactMarkdownStyles } from "@/styles/markdown-styles"; - -export const AgentThoughtContent = memo(function AgentThoughtContent({ - message, -}: { - message: string; -}) { - const { theme } = useUnistyles(); - const markdownContent = useMemo(() => message?.trim() ?? "", [message]); - const markdownStyles = useMemo( - () => createCompactMarkdownStyles(theme), - [theme] - ); - - const markdownRules = useMemo(() => { - return { - text: (node: any, _children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => ( - - {node.content} - - ), - textgroup: (node: any, children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => ( - - {children} - - ), - code_block: (node: any, _children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => ( - - {node.content} - - ), - fence: (node: any, _children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => ( - - {node.content} - - ), - code_inline: (node: any, _children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => ( - - {node.content} - - ), - bullet_list: (node: any, children: any[], _parent: any, styles: any) => ( - - {children} - - ), - ordered_list: (node: any, children: any[], _parent: any, styles: any) => ( - - {children} - - ), - list_item: (node: any, children: any[], parent: any, styles: any) => { - const isOrdered = parent?.type === "ordered_list"; - const index = parent?.children?.indexOf(node) ?? 0; - const bullet = isOrdered ? `${index + 1}.` : "•"; - const iconStyle = isOrdered ? styles.ordered_list_icon : styles.bullet_list_icon; - const contentStyle = isOrdered - ? styles.ordered_list_content - : styles.bullet_list_content; - - return ( - - {bullet} - - {children} - - - ); - }, - }; - }, []); - - if (!markdownContent) { - return No captured thinking; - } - - return ( - - {markdownContent} - - ); -}); - -const styles = StyleSheet.create((theme) => ({ - emptyText: { - color: theme.colors.foregroundMuted, - fontSize: theme.fontSize.xs, - fontStyle: "italic" as const, - }, -})); - diff --git a/packages/app/src/components/conversation-selector.tsx b/packages/app/src/components/conversation-selector.tsx deleted file mode 100644 index f52e850f3..000000000 --- a/packages/app/src/components/conversation-selector.tsx +++ /dev/null @@ -1,437 +0,0 @@ -import { useState, useEffect, useCallback } from "react"; -import { - Modal, - View, - Text, - Pressable, - ScrollView, - Alert, - KeyboardAvoidingView, - Platform, -} from "react-native"; -import { SafeAreaView } from "react-native-safe-area-context"; -import { MessageSquare, X, Plus, Trash2 } from "lucide-react-native"; -import AsyncStorage from "@react-native-async-storage/async-storage"; -import { StyleSheet } from "react-native-unistyles"; -import type { DaemonClientV2 } from "@server/client/daemon-client-v2"; - -const STORAGE_KEY = "@paseo:voice-conversation-id"; - -interface Conversation { - id: string; - lastUpdated: string; - messageCount: number; -} - -interface ConversationSelectorProps { - currentVoiceConversationId: string | null; - onSelectVoiceConversation: (voiceConversationId: string | null) => void; - client: DaemonClientV2 | null; -} - -export function ConversationSelector({ - currentVoiceConversationId, - onSelectVoiceConversation, - client, -}: ConversationSelectorProps) { - const [conversations, setConversations] = useState([]); - const [isOpen, setIsOpen] = useState(false); - const [isLoading, setIsLoading] = useState(false); - - const fetchConversations = useCallback(async () => { - setIsLoading(true); - - if (!client) { - setIsLoading(false); - Alert.alert("Error", "Daemon unavailable."); - return; - } - - try { - console.log("[ConversationSelector] Fetching conversations"); - const response = await client.listVoiceConversations(); - setConversations(response.conversations); - } catch (error) { - console.error( - "[ConversationSelector] Failed to list conversations:", - error - ); - Alert.alert("Error", "Failed to load conversations."); - } finally { - setIsLoading(false); - } - }, [client]); - - // Fetch conversations when modal opens - useEffect(() => { - if (isOpen) { - void fetchConversations(); - } - }, [fetchConversations, isOpen]); - - const handleDeleteConversation = useCallback( - (id: string) => { - Alert.alert( - "Delete Conversation", - "Are you sure you want to delete this conversation?", - [ - { text: "Cancel", style: "cancel" }, - { - text: "Delete", - style: "destructive", - onPress: () => { - if (!client) { - Alert.alert("Error", "Daemon unavailable."); - return; - } - - console.log("[ConversationSelector] Deleting conversation:", id); - void (async () => { - try { - const response = await client.deleteVoiceConversation(id); - if (response.success) { - await fetchConversations(); - if (response.voiceConversationId === currentVoiceConversationId) { - await handleNewConversation(); - } - } else { - Alert.alert( - "Error", - `Failed to delete conversation: ${response.error}` - ); - } - } catch (error) { - console.error( - "[ConversationSelector] Failed to delete conversation:", - error - ); - Alert.alert("Error", "Failed to delete conversation."); - } - })(); - }, - }, - ] - ); - }, - [currentVoiceConversationId, fetchConversations, handleNewConversation, client] - ); - - const handleClearAll = useCallback(() => { - Alert.alert( - "Clear All Conversations", - "Are you sure you want to delete all conversations?", - [ - { text: "Cancel", style: "cancel" }, - { - text: "Clear All", - style: "destructive", - onPress: () => { - if (!client) { - Alert.alert("Error", "Daemon unavailable."); - return; - } - - console.log("[ConversationSelector] Clearing all conversations"); - void (async () => { - setIsLoading(true); - try { - for (const conv of conversations) { - await client.deleteVoiceConversation(conv.id); - } - setConversations([]); - await handleNewConversation(); - setIsOpen(false); - } catch (error) { - console.error( - "[ConversationSelector] Failed to clear conversations:", - error - ); - Alert.alert("Error", "Failed to clear conversations."); - } finally { - setIsLoading(false); - } - })(); - }, - }, - ] - ); - }, [conversations, handleNewConversation, client]); - - async function handleSelectConversation(id: string) { - try { - // Save to AsyncStorage for persistence - await AsyncStorage.setItem(STORAGE_KEY, id); - onSelectVoiceConversation(id); - setIsOpen(false); - } catch (error) { - console.error( - "[ConversationSelector] Failed to save conversation ID:", - error - ); - } - } - - async function handleNewConversation() { - try { - // Clear saved conversation ID - await AsyncStorage.removeItem(STORAGE_KEY); - onSelectVoiceConversation(null); - setIsOpen(false); - } catch (error) { - console.error( - "[ConversationSelector] Failed to clear conversation ID:", - error - ); - } - } - - function formatDate(dateString: string): string { - const date = new Date(dateString); - const now = new Date(); - const diffMs = now.getTime() - date.getTime(); - const diffMins = Math.floor(diffMs / 60000); - const diffHours = Math.floor(diffMs / 3600000); - const diffDays = Math.floor(diffMs / 86400000); - - if (diffMins < 1) return "Just now"; - if (diffMins < 60) return `${diffMins}m ago`; - if (diffHours < 24) return `${diffHours}h ago`; - if (diffDays < 7) return `${diffDays}d ago`; - return date.toLocaleDateString(); - } - - return ( - - setIsOpen(true)} style={styles.triggerButton}> - - - - setIsOpen(false)} - > - - setIsOpen(false)} - /> - - - {/* Header */} - - - Conversations{" "} - {conversations.length > 0 && `(${conversations.length})`} - - setIsOpen(false)}> - - - - - {/* New Conversation Button */} - - - - New Conversation - - - - {/* Conversations List */} - - {isLoading && ( - - Loading... - - )} - - {!isLoading && conversations.length === 0 && ( - - No saved conversations - - )} - - {!isLoading && - conversations.map((conversation) => ( - - - handleSelectConversation(conversation.id) - } - > - - {conversation.messageCount} messages - - - {formatDate(conversation.lastUpdated)} - - - - handleDeleteConversation(conversation.id) - } - style={styles.deleteButton} - hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} - > - - - - ))} - - {!isLoading && conversations.length > 0 && ( - - - - Clear All - - - )} - - - - - - - ); -} - -const styles = StyleSheet.create((theme) => ({ - triggerButton: { - backgroundColor: theme.colors.surface2, - padding: theme.spacing[3], - borderRadius: theme.borderRadius.lg, - }, - modalOverlay: { - flex: 1, - backgroundColor: "rgba(0,0,0,0.5)", - }, - modalBackdrop: { - flex: 1, - }, - modalContent: { - backgroundColor: theme.colors.surface2, - borderTopLeftRadius: theme.spacing[6], - borderTopRightRadius: theme.spacing[6], - height: "80%", - }, - modalInner: { - flex: 1, - }, - header: { - flexDirection: "row", - alignItems: "center", - justifyContent: "space-between", - padding: theme.spacing[6], - borderBottomWidth: theme.borderWidth[1], - borderBottomColor: theme.colors.border, - }, - headerTitle: { - color: theme.colors.foreground, - fontSize: theme.fontSize.lg, - fontWeight: theme.fontWeight.semibold, - }, - newButtonContainer: { - padding: theme.spacing[4], - borderBottomWidth: theme.borderWidth[1], - borderBottomColor: theme.colors.border, - }, - newButton: { - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - gap: theme.spacing[2], - backgroundColor: theme.colors.surface2, - paddingVertical: theme.spacing[3], - borderRadius: theme.borderRadius.lg, - }, - newButtonText: { - color: theme.colors.foreground, - fontWeight: theme.fontWeight.semibold, - }, - scrollView: { - flex: 1, - }, - scrollContent: { - paddingBottom: theme.spacing[4] * 5, - }, - loadingContainer: { - padding: theme.spacing[8], - alignItems: "center", - }, - loadingText: { - color: theme.colors.foregroundMuted, - }, - emptyContainer: { - padding: theme.spacing[8], - alignItems: "center", - }, - emptyText: { - color: theme.colors.foregroundMuted, - }, - conversationItem: { - flexDirection: "row", - alignItems: "center", - justifyContent: "space-between", - padding: theme.spacing[4], - borderBottomWidth: theme.borderWidth[1], - borderBottomColor: theme.colors.border, - backgroundColor: "transparent", - }, - conversationItemActive: { - backgroundColor: theme.colors.surface2, - }, - conversationContent: { - flex: 1, - }, - conversationTitle: { - color: theme.colors.foreground, - fontWeight: theme.fontWeight.semibold, - fontSize: theme.fontSize.base, - }, - conversationDate: { - color: theme.colors.foregroundMuted, - fontSize: theme.fontSize.sm, - marginTop: theme.spacing[1], - }, - deleteButton: { - padding: theme.spacing[2], - }, - clearAllContainer: { - padding: theme.spacing[4], - }, - clearAllButton: { - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - gap: theme.spacing[2], - backgroundColor: "rgba(127, 29, 29, 0.2)", - paddingVertical: theme.spacing[3], - borderRadius: theme.borderRadius.lg, - borderWidth: theme.borderWidth[1], - borderColor: theme.colors.palette.red[900], - }, - clearAllText: { - color: theme.colors.destructive, - fontWeight: theme.fontWeight.semibold, - }, -})); diff --git a/packages/app/src/components/headers/home-header.tsx b/packages/app/src/components/headers/home-header.tsx deleted file mode 100644 index 4af7603db..000000000 --- a/packages/app/src/components/headers/home-header.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { useCallback } from "react"; -import { Pressable } from "react-native"; -import { router } from "expo-router"; -import { StyleSheet, useUnistyles } from "react-native-unistyles"; -import { Settings, MessageSquare, Download, Plus } from "lucide-react-native"; -import { ScreenHeader } from "./screen-header"; - -interface HomeHeaderProps { - onCreateAgent: () => void; - onImportAgent: () => void; -} - -export function HomeHeader({ onCreateAgent, onImportAgent }: HomeHeaderProps) { - const { theme } = useUnistyles(); - const handleCreatePress = useCallback(() => { - onCreateAgent(); - }, [onCreateAgent]); - const handleImportPress = useCallback(() => { - onImportAgent(); - }, [onImportAgent]); - - return ( - router.push("/settings")} - style={styles.iconButton} - > - - - } - right={ - <> - router.push("/orchestrator")} - style={styles.iconButton} - > - - - - - - - - - - } - /> - ); -} - -const styles = StyleSheet.create((theme) => ({ - iconButton: { - padding: { - xs: theme.spacing[3], - md: theme.spacing[2], - }, - borderRadius: theme.borderRadius.lg, - }, -})); diff --git a/packages/app/src/components/message.tsx b/packages/app/src/components/message.tsx index 537577f25..e1b330b56 100644 --- a/packages/app/src/components/message.tsx +++ b/packages/app/src/components/message.tsx @@ -51,13 +51,11 @@ import { } from "@/styles/markdown-styles"; import { Colors, Fonts } from "@/constants/theme"; import * as Clipboard from "expo-clipboard"; -import type { TodoEntry, ThoughtStatus } from "@/types/stream"; +import type { TodoEntry } from "@/types/stream"; import { extractPrincipalParam } from "@/utils/tool-call-parsers"; import { getNowMs, isPerfLoggingEnabled, perfLog } from "@/utils/perf"; import { resolveToolCallPreview } from "./tool-call-preview"; import { useToolCallSheet } from "./tool-call-sheet"; -import { useThinkingSheet } from "./thinking-sheet"; -import { AgentThoughtContent } from "./agent-thought-content"; import { ToolCallDetailsContent, useToolCallDetails, @@ -1051,13 +1049,6 @@ export const TodoListCard = memo(function TodoListCard({ ); }); -interface AgentThoughtMessageProps { - message: string; - status?: ThoughtStatus; - isLastInSequence?: boolean; - disableOuterSpacing?: boolean; -} - interface ExpandableBadgeProps { label: string; secondaryLabel?: string; @@ -1195,46 +1186,6 @@ const ExpandableBadge = memo(function ExpandableBadge({ ); }); -export const AgentThoughtMessage = memo(function AgentThoughtMessage({ - message, - status = "ready", - isLastInSequence = false, - disableOuterSpacing, -}: AgentThoughtMessageProps) { - const { openThinking } = useThinkingSheet(); - const [isExpanded, setIsExpanded] = useState(false); - - const isMobile = - UnistylesRuntime.breakpoint === "xs" || - UnistylesRuntime.breakpoint === "sm"; - - const handleToggle = useCallback(() => { - if (isMobile) { - openThinking({ message, status }); - return; - } - setIsExpanded((prev) => !prev); - }, [isMobile, openThinking, message, status]); - - const renderDetails = useCallback(() => { - return ; - }, [message]); - - return ( - null : renderDetails} - isLoading={status !== "ready"} - isLastInSequence={isLastInSequence} - style={isLastInSequence ? undefined : { marginBottom: theme.spacing[1] }} - disableOuterSpacing={disableOuterSpacing} - /> - ); -}); - interface ToolCallProps { toolName: string; args: any; @@ -1252,6 +1203,7 @@ const toolKindIcons: Record = { read: Eye, execute: SquareTerminal, search: Search, + thinking: Brain, }; const TOOL_CALL_LOG_TAG = "[ToolCall]"; const TOOL_CALL_COMMIT_THRESHOLD_MS = 16; @@ -1259,6 +1211,7 @@ const TOOL_CALL_COMMIT_THRESHOLD_MS = 16; // Derive tool kind from tool name for icon selection function getToolKindFromName(toolName: string): string { const lower = toolName.toLowerCase(); + if (lower === "thinking") return "thinking"; if (lower === "read" || lower === "read_file" || lower.startsWith("read")) return "read"; if (lower === "edit" || lower === "write" || lower === "apply_patch") diff --git a/packages/app/src/components/orchestrator-messages-view.tsx b/packages/app/src/components/orchestrator-messages-view.tsx deleted file mode 100644 index c3598f389..000000000 --- a/packages/app/src/components/orchestrator-messages-view.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { ScrollView } from "react-native"; -import { StyleSheet } from "react-native-unistyles"; -import { forwardRef } from "react"; -import { - UserMessage, - AssistantMessage, - ActivityLog, - ToolCall, -} from "@/components/message"; -import { ToolCallSheetProvider } from "@/components/tool-call-sheet"; -import { ThinkingSheetProvider } from "@/components/thinking-sheet"; -import type { MessageEntry } from "@/contexts/session-context"; - -interface OrchestratorMessagesViewProps { - messages: MessageEntry[]; - currentAssistantMessage: string; - onArtifactClick: (artifactId: string) => void; -} - -export const OrchestratorMessagesView = forwardRef( - function OrchestratorMessagesView({ messages, currentAssistantMessage, onArtifactClick }, ref) { - - return ( - - - - {messages.map((msg) => { - if (msg.type === "user") { - return ( - - ); - } - - if (msg.type === "assistant") { - return ( - - ); - } - - if (msg.type === "activity") { - return ( - - ); - } - - if (msg.type === "artifact") { - return ( - - ); - } - - if (msg.type === "tool_call") { - return ( - - ); - } - - return null; - })} - - {/* Streaming assistant message */} - {currentAssistantMessage && ( - - )} - - - - ); - } -); - -const styles = StyleSheet.create((theme) => ({ - scrollView: { - flex: 1, - }, - scrollContent: { - paddingHorizontal: theme.spacing[4], - paddingTop: theme.spacing[4], - paddingBottom: theme.spacing[6], - }, -})); diff --git a/packages/app/src/components/thinking-sheet.tsx b/packages/app/src/components/thinking-sheet.tsx deleted file mode 100644 index d61646464..000000000 --- a/packages/app/src/components/thinking-sheet.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import React, { - createContext, - useCallback, - useContext, - useMemo, - useRef, - ReactNode, -} from "react"; -import { View, Text, Pressable } from "react-native"; -import { StyleSheet } from "react-native-unistyles"; -import { - BottomSheetModal, - BottomSheetScrollView, - BottomSheetBackdrop, -} from "@gorhom/bottom-sheet"; -import { Brain, X } from "lucide-react-native"; -import type { ThoughtStatus } from "@/types/stream"; -import { AgentThoughtContent } from "./agent-thought-content"; - -export interface ThinkingSheetData { - message: string; - status?: ThoughtStatus; -} - -interface ThinkingSheetContextValue { - openThinking: (data: ThinkingSheetData) => void; - closeThinking: () => void; -} - -const ThinkingSheetContext = createContext(null); - -export function useThinkingSheet(): ThinkingSheetContextValue { - const context = useContext(ThinkingSheetContext); - if (!context) { - throw new Error("useThinkingSheet must be used within a ThinkingSheetProvider"); - } - return context; -} - -export function ThinkingSheetProvider({ children }: { children: ReactNode }) { - const bottomSheetRef = useRef(null); - const [sheetData, setSheetData] = React.useState(null); - - const snapPoints = useMemo(() => ["60%", "95%"], []); - - const openThinking = useCallback((data: ThinkingSheetData) => { - setSheetData(data); - bottomSheetRef.current?.present(); - }, []); - - const closeThinking = useCallback(() => { - bottomSheetRef.current?.dismiss(); - }, []); - - const handleSheetChange = useCallback((index: number) => { - if (index === -1) { - setSheetData(null); - } - }, []); - - const renderBackdrop = useCallback( - (props: React.ComponentProps) => ( - - ), - [] - ); - - const contextValue = useMemo( - () => ({ openThinking, closeThinking }), - [openThinking, closeThinking] - ); - - return ( - - {children} - - {sheetData && ( - - )} - - - ); -} - -function ThinkingSheetContent({ - data, - onClose, -}: { - data: ThinkingSheetData; - onClose: () => void; -}) { - const { message } = data; - return ( - - - - - - Thinking - - - - - - - - - - - - ); -} - -const styles = StyleSheet.create((theme) => ({ - sheetBackground: { - backgroundColor: theme.colors.surface2, - }, - handleIndicator: { - backgroundColor: theme.colors.palette.zinc[600], - }, - container: { - flex: 1, - backgroundColor: theme.colors.surface2, - }, - header: { - flexDirection: "row", - alignItems: "center", - justifyContent: "space-between", - paddingHorizontal: theme.spacing[4], - paddingVertical: theme.spacing[3], - borderBottomWidth: theme.borderWidth[1], - borderBottomColor: theme.colors.border, - }, - headerLeft: { - flexDirection: "row", - alignItems: "center", - gap: theme.spacing[2], - flex: 1, - }, - headerIcon: { - color: theme.colors.foreground, - }, - headerTitle: { - fontSize: theme.fontSize.lg, - fontWeight: theme.fontWeight.semibold, - color: theme.colors.foreground, - flex: 1, - }, - closeButton: { - padding: theme.spacing[2], - }, - closeIcon: { - color: theme.colors.foregroundMuted, - }, - content: { - flex: 1, - backgroundColor: theme.colors.surface2, - }, - contentContainer: { - paddingHorizontal: theme.spacing[4], - paddingTop: theme.spacing[4], - paddingBottom: theme.spacing[8], - }, -})); diff --git a/packages/app/src/components/tool-call-details.tsx b/packages/app/src/components/tool-call-details.tsx index 87022ef32..16a46c8a2 100644 --- a/packages/app/src/components/tool-call-details.tsx +++ b/packages/app/src/components/tool-call-details.tsx @@ -87,6 +87,14 @@ function summarizeToolCallDisplay(display: ToolCallDisplay): ToolCallDisplaySumm }, }; } + case "thinking": { + const contentLength = display.content.length; + return { + displayType: display.type, + totalChars: contentLength, + detail: { contentLength }, + }; + } default: return assertNever(display); } @@ -227,6 +235,20 @@ export function ToolCallDetailsContent({ ) : null} ); + } else if (display.type === "thinking") { + // Thinking: display the content as plain text + sections.push( + + + {display.content} + + + ); } else { // Generic tool: show input/output as key-value pairs if (display.input.length > 0) { diff --git a/packages/app/src/utils/tool-call-parsers.ts b/packages/app/src/utils/tool-call-parsers.ts index 47c7369ba..205596a7f 100644 --- a/packages/app/src/utils/tool-call-parsers.ts +++ b/packages/app/src/utils/tool-call-parsers.ts @@ -1168,6 +1168,7 @@ const TOOL_NAME_MAP: Record = { set_branch: "Set branch", "mcp__agent-control__set_title": "Set title", "mcp__agent-control__set_branch": "Set branch", + thinking: "Thinking", }; const ToolCallDisplaySchema = z @@ -1179,6 +1180,16 @@ const ToolCallDisplaySchema = z .transform((data) => { const normalizedToolName = TOOL_NAME_MAP[data.toolName] ?? data.toolName; + // Handle thinking - input is the thinking text content + if (data.toolName === "thinking") { + const content = typeof data.input === "string" ? data.input : ""; + return { + type: "thinking" as const, + content, + toolName: normalizedToolName, + }; + } + // Try each schema in order const shellParsed = ShellToolCallSchema.safeParse({ input: data.input, result: data.result }); if (shellParsed.success) {