Merge branch 'dry-review'
This commit is contained in:
Generated
+503
-3
@@ -22,6 +22,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"get-port-cli": "^3.0.0",
|
"get-port-cli": "^3.0.0",
|
||||||
|
"knip": "^5.82.1",
|
||||||
"patch-package": "^8.0.1",
|
"patch-package": "^8.0.1",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
@@ -1848,9 +1849,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@emnapi/core": {
|
"node_modules/@emnapi/core": {
|
||||||
"version": "1.6.0",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
|
||||||
"integrity": "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==",
|
"integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
@@ -6340,6 +6341,306 @@
|
|||||||
"node": ">=8.0.0"
|
"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": {
|
"node_modules/@paseo/app": {
|
||||||
"resolved": "packages/app",
|
"resolved": "packages/app",
|
||||||
"link": true
|
"link": true
|
||||||
@@ -14834,6 +15135,16 @@
|
|||||||
"asap": "~2.0.3"
|
"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": {
|
"node_modules/fdir": {
|
||||||
"version": "6.5.0",
|
"version": "6.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||||
@@ -15123,6 +15434,22 @@
|
|||||||
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==",
|
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/formdata-node": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
|
||||||
@@ -17208,6 +17535,124 @@
|
|||||||
"node": ">=6"
|
"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": {
|
"node_modules/lan-network": {
|
||||||
"version": "0.1.7",
|
"version": "0.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/lan-network/-/lan-network-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/lan-network/-/lan-network-0.1.7.tgz",
|
||||||
@@ -19226,6 +19671,38 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/p-limit": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||||
@@ -22504,6 +22981,19 @@
|
|||||||
"node": ">=8.0.0"
|
"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": {
|
"node_modules/sonic-boom": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
|
||||||
@@ -24520,6 +25010,16 @@
|
|||||||
"integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==",
|
"integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/walker": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"get-port-cli": "^3.0.0",
|
"get-port-cli": "^3.0.0",
|
||||||
|
"knip": "^5.82.1",
|
||||||
"patch-package": "^8.0.1",
|
"patch-package": "^8.0.1",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// App name and package ID are controlled by Gradle product flavors (dev/production)
|
// App name and package ID are controlled by Gradle product flavors (dev/production)
|
||||||
// See plugins/with-android-product-flavors.js for flavor configuration
|
// See plugins/with-android-product-flavors.js for flavor configuration
|
||||||
|
|
||||||
const withAndroidProductFlavors = require("./plugins/with-android-product-flavors");
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
expo: {
|
expo: {
|
||||||
name: "Paseo",
|
name: "Paseo",
|
||||||
@@ -91,7 +89,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
withAndroidProductFlavors,
|
"./plugins/with-android-product-flavors",
|
||||||
],
|
],
|
||||||
experiments: {
|
experiments: {
|
||||||
typedRoutes: true,
|
typedRoutes: true,
|
||||||
|
|||||||
@@ -347,7 +347,6 @@ export default function RootLayout() {
|
|||||||
>
|
>
|
||||||
<Stack.Screen name="index" />
|
<Stack.Screen name="index" />
|
||||||
<Stack.Screen name="agents" />
|
<Stack.Screen name="agents" />
|
||||||
<Stack.Screen name="orchestrator" />
|
|
||||||
<Stack.Screen name="agent/[id]" options={{ gestureEnabled: false }} />
|
<Stack.Screen name="agent/[id]" options={{ gestureEnabled: false }} />
|
||||||
<Stack.Screen name="agent/[serverId]/[agentId]" options={{ gestureEnabled: false }} />
|
<Stack.Screen name="agent/[serverId]/[agentId]" options={{ gestureEnabled: false }} />
|
||||||
<Stack.Screen name="settings" />
|
<Stack.Screen name="settings" />
|
||||||
|
|||||||
@@ -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<ScrollView>(null);
|
|
||||||
const [currentArtifact, setCurrentArtifact] = useState<Artifact | null>(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 (
|
|
||||||
<View style={styles.container}>
|
|
||||||
{/* Header */}
|
|
||||||
<BackHeader title="Activity" />
|
|
||||||
|
|
||||||
{/* Content Area with Keyboard Animation */}
|
|
||||||
<ReanimatedAnimated.View style={[styles.content, animatedKeyboardStyle]}>
|
|
||||||
<OrchestratorMessagesView
|
|
||||||
ref={scrollViewRef}
|
|
||||||
messages={aggregatedMessages}
|
|
||||||
currentAssistantMessage={streamingAssistantMessage}
|
|
||||||
onArtifactClick={handleArtifactClick}
|
|
||||||
/>
|
|
||||||
</ReanimatedAnimated.View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create((theme) => ({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: theme.colors.surface0,
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
@@ -34,7 +34,6 @@ import {
|
|||||||
UserMessage,
|
UserMessage,
|
||||||
ActivityLog,
|
ActivityLog,
|
||||||
ToolCall,
|
ToolCall,
|
||||||
AgentThoughtMessage,
|
|
||||||
TodoListCard,
|
TodoListCard,
|
||||||
TurnCopyButton,
|
TurnCopyButton,
|
||||||
MessageOuterSpacingProvider,
|
MessageOuterSpacingProvider,
|
||||||
@@ -49,7 +48,6 @@ import type { DaemonClientV2 } from "@server/client/daemon-client-v2";
|
|||||||
import { parseToolCallDisplay } from "@/utils/tool-call-parsers";
|
import { parseToolCallDisplay } from "@/utils/tool-call-parsers";
|
||||||
import { ToolCallDetailsContent } from "./tool-call-details";
|
import { ToolCallDetailsContent } from "./tool-call-details";
|
||||||
import { ToolCallSheetProvider } from "./tool-call-sheet";
|
import { ToolCallSheetProvider } from "./tool-call-sheet";
|
||||||
import { ThinkingSheetProvider } from "./thinking-sheet";
|
|
||||||
import { createMarkdownStyles } from "@/styles/markdown-styles";
|
import { createMarkdownStyles } from "@/styles/markdown-styles";
|
||||||
import { MAX_CONTENT_WIDTH } from "@/constants/layout";
|
import { MAX_CONTENT_WIDTH } from "@/constants/layout";
|
||||||
import { isPerfLoggingEnabled, measurePayload, perfLog } from "@/utils/perf";
|
import { isPerfLoggingEnabled, measurePayload, perfLog } from "@/utils/perf";
|
||||||
@@ -274,9 +272,10 @@ export function AgentStreamView({
|
|||||||
const isLastInSequence =
|
const isLastInSequence =
|
||||||
nextItem?.kind !== "tool_call" && nextItem?.kind !== "thought";
|
nextItem?.kind !== "tool_call" && nextItem?.kind !== "thought";
|
||||||
return (
|
return (
|
||||||
<AgentThoughtMessage
|
<ToolCall
|
||||||
message={item.text}
|
toolName="thinking"
|
||||||
status={item.status}
|
args={item.text}
|
||||||
|
status={item.status === "ready" ? "completed" : "executing"}
|
||||||
isLastInSequence={isLastInSequence}
|
isLastInSequence={isLastInSequence}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -533,8 +532,7 @@ export function AgentStreamView({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolCallSheetProvider>
|
<ToolCallSheetProvider>
|
||||||
<ThinkingSheetProvider>
|
<View style={stylesheet.container}>
|
||||||
<View style={stylesheet.container}>
|
|
||||||
<MessageOuterSpacingProvider disableOuterSpacing>
|
<MessageOuterSpacingProvider disableOuterSpacing>
|
||||||
<FlatList
|
<FlatList
|
||||||
ref={flatListRef}
|
ref={flatListRef}
|
||||||
@@ -590,7 +588,6 @@ export function AgentStreamView({
|
|||||||
</Animated.View>
|
</Animated.View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</ThinkingSheetProvider>
|
|
||||||
</ToolCallSheetProvider>
|
</ToolCallSheetProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = {}) => (
|
|
||||||
<Text key={node.key} style={[inheritedStyles, styles.text]}>
|
|
||||||
{node.content}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
textgroup: (node: any, children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => (
|
|
||||||
<Text key={node.key} style={[inheritedStyles, styles.textgroup]}>
|
|
||||||
{children}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
code_block: (node: any, _children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => (
|
|
||||||
<Text key={node.key} style={[inheritedStyles, styles.code_block]}>
|
|
||||||
{node.content}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
fence: (node: any, _children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => (
|
|
||||||
<Text key={node.key} style={[inheritedStyles, styles.fence]}>
|
|
||||||
{node.content}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
code_inline: (node: any, _children: any[], _parent: any, styles: any, inheritedStyles: any = {}) => (
|
|
||||||
<Text key={node.key} style={[inheritedStyles, styles.code_inline]}>
|
|
||||||
{node.content}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
bullet_list: (node: any, children: any[], _parent: any, styles: any) => (
|
|
||||||
<View key={node.key} style={styles.bullet_list}>
|
|
||||||
{children}
|
|
||||||
</View>
|
|
||||||
),
|
|
||||||
ordered_list: (node: any, children: any[], _parent: any, styles: any) => (
|
|
||||||
<View key={node.key} style={styles.ordered_list}>
|
|
||||||
{children}
|
|
||||||
</View>
|
|
||||||
),
|
|
||||||
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 (
|
|
||||||
<View key={node.key} style={styles.list_item}>
|
|
||||||
<Text style={iconStyle}>{bullet}</Text>
|
|
||||||
<View style={[contentStyle, { flex: 1, flexShrink: 1, minWidth: 0 }]}>
|
|
||||||
{children}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!markdownContent) {
|
|
||||||
return <Text style={styles.emptyText}>No captured thinking</Text>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Markdown style={markdownStyles} rules={markdownRules}>
|
|
||||||
{markdownContent}
|
|
||||||
</Markdown>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const styles = StyleSheet.create((theme) => ({
|
|
||||||
emptyText: {
|
|
||||||
color: theme.colors.foregroundMuted,
|
|
||||||
fontSize: theme.fontSize.xs,
|
|
||||||
fontStyle: "italic" as const,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
@@ -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<Conversation[]>([]);
|
|
||||||
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 (
|
|
||||||
<View>
|
|
||||||
<Pressable onPress={() => setIsOpen(true)} style={styles.triggerButton}>
|
|
||||||
<MessageSquare size={20} color="white" />
|
|
||||||
</Pressable>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
visible={isOpen}
|
|
||||||
animationType="slide"
|
|
||||||
transparent={true}
|
|
||||||
onRequestClose={() => setIsOpen(false)}
|
|
||||||
>
|
|
||||||
<View style={styles.modalOverlay}>
|
|
||||||
<Pressable
|
|
||||||
style={styles.modalBackdrop}
|
|
||||||
onPress={() => setIsOpen(false)}
|
|
||||||
/>
|
|
||||||
<View style={styles.modalContent}>
|
|
||||||
<View style={styles.modalInner}>
|
|
||||||
{/* Header */}
|
|
||||||
<View style={styles.header}>
|
|
||||||
<Text style={styles.headerTitle}>
|
|
||||||
Conversations{" "}
|
|
||||||
{conversations.length > 0 && `(${conversations.length})`}
|
|
||||||
</Text>
|
|
||||||
<Pressable onPress={() => setIsOpen(false)}>
|
|
||||||
<X size={24} color="white" />
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* New Conversation Button */}
|
|
||||||
<View style={styles.newButtonContainer}>
|
|
||||||
<Pressable
|
|
||||||
onPress={handleNewConversation}
|
|
||||||
style={styles.newButton}
|
|
||||||
>
|
|
||||||
<Plus size={20} color="white" />
|
|
||||||
<Text style={styles.newButtonText}>New Conversation</Text>
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Conversations List */}
|
|
||||||
<ScrollView
|
|
||||||
style={styles.scrollView}
|
|
||||||
contentContainerStyle={styles.scrollContent}
|
|
||||||
keyboardShouldPersistTaps="handled"
|
|
||||||
keyboardDismissMode="on-drag"
|
|
||||||
>
|
|
||||||
{isLoading && (
|
|
||||||
<View style={styles.loadingContainer}>
|
|
||||||
<Text style={styles.loadingText}>Loading...</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isLoading && conversations.length === 0 && (
|
|
||||||
<View style={styles.emptyContainer}>
|
|
||||||
<Text style={styles.emptyText}>No saved conversations</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isLoading &&
|
|
||||||
conversations.map((conversation) => (
|
|
||||||
<View
|
|
||||||
key={conversation.id}
|
|
||||||
style={[
|
|
||||||
styles.conversationItem,
|
|
||||||
conversation.id === currentVoiceConversationId &&
|
|
||||||
styles.conversationItemActive,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Pressable
|
|
||||||
style={styles.conversationContent}
|
|
||||||
onPress={() =>
|
|
||||||
handleSelectConversation(conversation.id)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text style={styles.conversationTitle}>
|
|
||||||
{conversation.messageCount} messages
|
|
||||||
</Text>
|
|
||||||
<Text style={styles.conversationDate}>
|
|
||||||
{formatDate(conversation.lastUpdated)}
|
|
||||||
</Text>
|
|
||||||
</Pressable>
|
|
||||||
<Pressable
|
|
||||||
onPress={() =>
|
|
||||||
handleDeleteConversation(conversation.id)
|
|
||||||
}
|
|
||||||
style={styles.deleteButton}
|
|
||||||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
||||||
>
|
|
||||||
<Trash2 size={20} color="#ef4444" />
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{!isLoading && conversations.length > 0 && (
|
|
||||||
<View style={styles.clearAllContainer}>
|
|
||||||
<Pressable
|
|
||||||
onPress={handleClearAll}
|
|
||||||
style={styles.clearAllButton}
|
|
||||||
>
|
|
||||||
<Trash2 size={18} color="#ef4444" />
|
|
||||||
<Text style={styles.clearAllText}>Clear All</Text>
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</Modal>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
@@ -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 (
|
|
||||||
<ScreenHeader
|
|
||||||
left={
|
|
||||||
<Pressable
|
|
||||||
onPress={() => router.push("/settings")}
|
|
||||||
style={styles.iconButton}
|
|
||||||
>
|
|
||||||
<Settings size={20} color={theme.colors.foreground} />
|
|
||||||
</Pressable>
|
|
||||||
}
|
|
||||||
right={
|
|
||||||
<>
|
|
||||||
<Pressable
|
|
||||||
onPress={() => router.push("/orchestrator")}
|
|
||||||
style={styles.iconButton}
|
|
||||||
>
|
|
||||||
<MessageSquare size={20} color={theme.colors.foreground} />
|
|
||||||
</Pressable>
|
|
||||||
<Pressable onPress={handleImportPress} style={styles.iconButton}>
|
|
||||||
<Download size={20} color={theme.colors.foreground} />
|
|
||||||
</Pressable>
|
|
||||||
<Pressable onPress={handleCreatePress} style={styles.iconButton}>
|
|
||||||
<Plus size={20} color={theme.colors.foreground} />
|
|
||||||
</Pressable>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create((theme) => ({
|
|
||||||
iconButton: {
|
|
||||||
padding: {
|
|
||||||
xs: theme.spacing[3],
|
|
||||||
md: theme.spacing[2],
|
|
||||||
},
|
|
||||||
borderRadius: theme.borderRadius.lg,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
@@ -51,13 +51,11 @@ import {
|
|||||||
} from "@/styles/markdown-styles";
|
} from "@/styles/markdown-styles";
|
||||||
import { Colors, Fonts } from "@/constants/theme";
|
import { Colors, Fonts } from "@/constants/theme";
|
||||||
import * as Clipboard from "expo-clipboard";
|
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 { extractPrincipalParam } from "@/utils/tool-call-parsers";
|
||||||
import { getNowMs, isPerfLoggingEnabled, perfLog } from "@/utils/perf";
|
import { getNowMs, isPerfLoggingEnabled, perfLog } from "@/utils/perf";
|
||||||
import { resolveToolCallPreview } from "./tool-call-preview";
|
import { resolveToolCallPreview } from "./tool-call-preview";
|
||||||
import { useToolCallSheet } from "./tool-call-sheet";
|
import { useToolCallSheet } from "./tool-call-sheet";
|
||||||
import { useThinkingSheet } from "./thinking-sheet";
|
|
||||||
import { AgentThoughtContent } from "./agent-thought-content";
|
|
||||||
import {
|
import {
|
||||||
ToolCallDetailsContent,
|
ToolCallDetailsContent,
|
||||||
useToolCallDetails,
|
useToolCallDetails,
|
||||||
@@ -1051,13 +1049,6 @@ export const TodoListCard = memo(function TodoListCard({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
interface AgentThoughtMessageProps {
|
|
||||||
message: string;
|
|
||||||
status?: ThoughtStatus;
|
|
||||||
isLastInSequence?: boolean;
|
|
||||||
disableOuterSpacing?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ExpandableBadgeProps {
|
interface ExpandableBadgeProps {
|
||||||
label: string;
|
label: string;
|
||||||
secondaryLabel?: 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 <AgentThoughtContent message={message} />;
|
|
||||||
}, [message]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ExpandableBadge
|
|
||||||
label="Thinking"
|
|
||||||
icon={status === "ready" ? Brain : undefined}
|
|
||||||
isExpanded={!isMobile && isExpanded}
|
|
||||||
onToggle={handleToggle}
|
|
||||||
renderDetails={isMobile ? () => null : renderDetails}
|
|
||||||
isLoading={status !== "ready"}
|
|
||||||
isLastInSequence={isLastInSequence}
|
|
||||||
style={isLastInSequence ? undefined : { marginBottom: theme.spacing[1] }}
|
|
||||||
disableOuterSpacing={disableOuterSpacing}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
interface ToolCallProps {
|
interface ToolCallProps {
|
||||||
toolName: string;
|
toolName: string;
|
||||||
args: any;
|
args: any;
|
||||||
@@ -1252,6 +1203,7 @@ const toolKindIcons: Record<string, any> = {
|
|||||||
read: Eye,
|
read: Eye,
|
||||||
execute: SquareTerminal,
|
execute: SquareTerminal,
|
||||||
search: Search,
|
search: Search,
|
||||||
|
thinking: Brain,
|
||||||
};
|
};
|
||||||
const TOOL_CALL_LOG_TAG = "[ToolCall]";
|
const TOOL_CALL_LOG_TAG = "[ToolCall]";
|
||||||
const TOOL_CALL_COMMIT_THRESHOLD_MS = 16;
|
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
|
// Derive tool kind from tool name for icon selection
|
||||||
function getToolKindFromName(toolName: string): string {
|
function getToolKindFromName(toolName: string): string {
|
||||||
const lower = toolName.toLowerCase();
|
const lower = toolName.toLowerCase();
|
||||||
|
if (lower === "thinking") return "thinking";
|
||||||
if (lower === "read" || lower === "read_file" || lower.startsWith("read"))
|
if (lower === "read" || lower === "read_file" || lower.startsWith("read"))
|
||||||
return "read";
|
return "read";
|
||||||
if (lower === "edit" || lower === "write" || lower === "apply_patch")
|
if (lower === "edit" || lower === "write" || lower === "apply_patch")
|
||||||
|
|||||||
@@ -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<ScrollView, OrchestratorMessagesViewProps>(
|
|
||||||
function OrchestratorMessagesView({ messages, currentAssistantMessage, onArtifactClick }, ref) {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ToolCallSheetProvider>
|
|
||||||
<ThinkingSheetProvider>
|
|
||||||
<ScrollView
|
|
||||||
ref={ref}
|
|
||||||
style={styles.scrollView}
|
|
||||||
contentContainerStyle={styles.scrollContent}
|
|
||||||
keyboardShouldPersistTaps="handled"
|
|
||||||
keyboardDismissMode="on-drag"
|
|
||||||
>
|
|
||||||
{messages.map((msg) => {
|
|
||||||
if (msg.type === "user") {
|
|
||||||
return (
|
|
||||||
<UserMessage
|
|
||||||
key={msg.id}
|
|
||||||
message={msg.message}
|
|
||||||
timestamp={msg.timestamp}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.type === "assistant") {
|
|
||||||
return (
|
|
||||||
<AssistantMessage
|
|
||||||
key={msg.id}
|
|
||||||
message={msg.message}
|
|
||||||
timestamp={msg.timestamp}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.type === "activity") {
|
|
||||||
return (
|
|
||||||
<ActivityLog
|
|
||||||
key={msg.id}
|
|
||||||
type={msg.activityType}
|
|
||||||
message={msg.message}
|
|
||||||
timestamp={msg.timestamp}
|
|
||||||
metadata={msg.metadata}
|
|
||||||
onArtifactClick={onArtifactClick}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.type === "artifact") {
|
|
||||||
return (
|
|
||||||
<ActivityLog
|
|
||||||
key={msg.id}
|
|
||||||
type="artifact"
|
|
||||||
message=""
|
|
||||||
timestamp={msg.timestamp}
|
|
||||||
artifactId={msg.artifactId}
|
|
||||||
artifactType={msg.artifactType}
|
|
||||||
title={msg.title}
|
|
||||||
onArtifactClick={onArtifactClick}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.type === "tool_call") {
|
|
||||||
return (
|
|
||||||
<ToolCall
|
|
||||||
key={msg.id}
|
|
||||||
toolName={msg.toolName}
|
|
||||||
args={msg.args}
|
|
||||||
result={msg.result}
|
|
||||||
error={msg.error}
|
|
||||||
status={msg.status}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
})}
|
|
||||||
|
|
||||||
{/* Streaming assistant message */}
|
|
||||||
{currentAssistantMessage && (
|
|
||||||
<AssistantMessage
|
|
||||||
message={currentAssistantMessage}
|
|
||||||
timestamp={Date.now()}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ScrollView>
|
|
||||||
</ThinkingSheetProvider>
|
|
||||||
</ToolCallSheetProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const styles = StyleSheet.create((theme) => ({
|
|
||||||
scrollView: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
scrollContent: {
|
|
||||||
paddingHorizontal: theme.spacing[4],
|
|
||||||
paddingTop: theme.spacing[4],
|
|
||||||
paddingBottom: theme.spacing[6],
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
@@ -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<ThinkingSheetContextValue | null>(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<BottomSheetModal>(null);
|
|
||||||
const [sheetData, setSheetData] = React.useState<ThinkingSheetData | null>(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<typeof BottomSheetBackdrop>) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
opacity={0.5}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const contextValue = useMemo(
|
|
||||||
() => ({ openThinking, closeThinking }),
|
|
||||||
[openThinking, closeThinking]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ThinkingSheetContext.Provider value={contextValue}>
|
|
||||||
{children}
|
|
||||||
<BottomSheetModal
|
|
||||||
ref={bottomSheetRef}
|
|
||||||
snapPoints={snapPoints}
|
|
||||||
index={0}
|
|
||||||
enableDynamicSizing={false}
|
|
||||||
onChange={handleSheetChange}
|
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
enablePanDownToClose
|
|
||||||
backgroundStyle={styles.sheetBackground}
|
|
||||||
handleIndicatorStyle={styles.handleIndicator}
|
|
||||||
>
|
|
||||||
{sheetData && (
|
|
||||||
<ThinkingSheetContent data={sheetData} onClose={closeThinking} />
|
|
||||||
)}
|
|
||||||
</BottomSheetModal>
|
|
||||||
</ThinkingSheetContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ThinkingSheetContent({
|
|
||||||
data,
|
|
||||||
onClose,
|
|
||||||
}: {
|
|
||||||
data: ThinkingSheetData;
|
|
||||||
onClose: () => void;
|
|
||||||
}) {
|
|
||||||
const { message } = data;
|
|
||||||
return (
|
|
||||||
<View style={styles.container}>
|
|
||||||
<View style={styles.header}>
|
|
||||||
<View style={styles.headerLeft}>
|
|
||||||
<Brain size={20} color={styles.headerIcon.color} />
|
|
||||||
<Text style={styles.headerTitle} numberOfLines={1}>
|
|
||||||
Thinking
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<Pressable onPress={onClose} style={styles.closeButton}>
|
|
||||||
<X size={20} color={styles.closeIcon.color} />
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<BottomSheetScrollView
|
|
||||||
style={styles.content}
|
|
||||||
contentContainerStyle={styles.contentContainer}
|
|
||||||
>
|
|
||||||
<AgentThoughtContent message={message} />
|
|
||||||
</BottomSheetScrollView>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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],
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
@@ -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:
|
default:
|
||||||
return assertNever(display);
|
return assertNever(display);
|
||||||
}
|
}
|
||||||
@@ -227,6 +235,20 @@ export function ToolCallDetailsContent({
|
|||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
} else if (display.type === "thinking") {
|
||||||
|
// Thinking: display the content as plain text
|
||||||
|
sections.push(
|
||||||
|
<View key="thinking" style={styles.section}>
|
||||||
|
<ScrollView
|
||||||
|
style={[styles.scrollArea, { maxHeight }]}
|
||||||
|
contentContainerStyle={styles.scrollContent}
|
||||||
|
nestedScrollEnabled
|
||||||
|
showsVerticalScrollIndicator={true}
|
||||||
|
>
|
||||||
|
<Text selectable style={styles.scrollText}>{display.content}</Text>
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// Generic tool: show input/output as key-value pairs
|
// Generic tool: show input/output as key-value pairs
|
||||||
if (display.input.length > 0) {
|
if (display.input.length > 0) {
|
||||||
|
|||||||
@@ -1168,6 +1168,7 @@ const TOOL_NAME_MAP: Record<string, string> = {
|
|||||||
set_branch: "Set branch",
|
set_branch: "Set branch",
|
||||||
"mcp__agent-control__set_title": "Set title",
|
"mcp__agent-control__set_title": "Set title",
|
||||||
"mcp__agent-control__set_branch": "Set branch",
|
"mcp__agent-control__set_branch": "Set branch",
|
||||||
|
thinking: "Thinking",
|
||||||
};
|
};
|
||||||
|
|
||||||
const ToolCallDisplaySchema = z
|
const ToolCallDisplaySchema = z
|
||||||
@@ -1179,6 +1180,16 @@ const ToolCallDisplaySchema = z
|
|||||||
.transform((data) => {
|
.transform((data) => {
|
||||||
const normalizedToolName = TOOL_NAME_MAP[data.toolName] ?? data.toolName;
|
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
|
// Try each schema in order
|
||||||
const shellParsed = ShellToolCallSchema.safeParse({ input: data.input, result: data.result });
|
const shellParsed = ShellToolCallSchema.safeParse({ input: data.input, result: data.result });
|
||||||
if (shellParsed.success) {
|
if (shellParsed.success) {
|
||||||
|
|||||||
Reference in New Issue
Block a user