diff --git a/.npmrc b/.npmrc deleted file mode 100644 index ed4cf697..00000000 --- a/.npmrc +++ /dev/null @@ -1,10 +0,0 @@ -engine-strict=true -package-manager-strict=true -package-manager-strict-version=true - -node-linker=hoisted -public-hoist-pattern=* -shamefully-hoist=true - -supportedArchitectures.os=current -supportedArchitectures.cpu=current diff --git a/package.json b/package.json index ef1cb1f5..09635686 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,10 @@ "productName": "Adnify", "author": "adnaan ", "license": "SEE LICENSE IN LICENSE", - "packageManager": "pnpm@9.15.9", + "packageManager": "pnpm@11.22.0", "engines": { "node": "^24.19.0", - "pnpm": "9.15.9" + "pnpm": "11.22.0" }, "homepage": "https://github.com/ad-naan/adnify", "repository": { @@ -40,26 +40,6 @@ "rebuild": "npx electron-rebuild -f -w node-pty", "rebuild:all": "npx electron-rebuild -f -w" }, - "pnpm": { - "overrides": { - "brace-expansion@<1.1.18": "^1.1.18", - "brace-expansion@>=2.0.0 <2.1.4": "^2.1.4", - "brace-expansion@>=4.0.0 <5.0.9": "^5.0.9", - "fast-uri@>=3.0.0 <3.1.5": "^3.1.5", - "hono@<4.12.34": "^4.12.34", - "@hono/node-server@<2.0.5": "^2.0.5", - "ip-address@<=10.3.0": "^10.3.1", - "js-yaml@>=4.0.0 <4.3.1": "^4.3.1", - "dompurify@<3.4.13": "^3.4.13", - "sharp@<0.35.0": "^0.35.1", - "tar@<7.5.22": "^7.5.22", - "undici@<6.28.0": "^6.28.0", - "protobufjs@<7.6.3": "^7.6.3" - }, - "patchedDependencies": { - "extract-zip@2.0.1": "patches/extract-zip@2.0.1.patch" - } - }, "dependencies": { "@lancedb/lancedb": "^0.23.0", "@parcel/watcher": "^2.4.1", @@ -95,7 +75,6 @@ "@modelcontextprotocol/sdk": "^1.26.0", "@monaco-editor/react": "^4.6.0", "@types/adm-zip": "^0.5.7", - "@types/dompurify": "^3.0.5", "@types/node": "^24.0.0", "@types/pdf-parse": "^1.1.5", "@types/picomatch": "^4.0.2", @@ -118,6 +97,7 @@ "electron": "^39.8.5", "electron-builder": "^26.15.3", "esbuild": "^0.28.1", + "extract-zip": "2.0.1", "fast-check": "^4.4.0", "fast-json-stable-stringify": "^2.1.0", "framer-motion": "^12.25.0", @@ -126,6 +106,7 @@ "katex": "^0.16.45", "lucide-react": "^0.562.0", "monaco-editor": "^0.55.1", + "onnx-proto": "4.0.4", "png2icons": "^2.0.1", "postcss": "^8.5.23", "react": "^18.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89ad9722..bf97ed03 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,9 +20,7 @@ overrides: protobufjs@<7.6.3: ^7.6.3 patchedDependencies: - extract-zip@2.0.1: - hash: nevhi44iznjigonvauz2rkjwxi - path: patches/extract-zip@2.0.1.patch + extract-zip@2.0.1: 5300cf842856000f03aa544d2ace3dbf9587123c3471490a2384458ce0706856 importers: @@ -48,7 +46,7 @@ importers: version: 8.2.0 electron-updater: specifier: ^6.7.3 - version: 6.8.9 + version: 6.8.9(supports-color@8.1.1) jszip: specifier: ^3.10.1 version: 3.10.1 @@ -60,7 +58,7 @@ importers: version: 3.1.0 pdf-parse: specifier: 1.1.1 - version: 1.1.1 + version: 1.1.1(supports-color@8.1.1) picomatch: specifier: ^4.0.4 version: 4.0.5 @@ -76,10 +74,6 @@ importers: web-tree-sitter: specifier: ^0.20.8 version: 0.20.8 - optionalDependencies: - '@lancedb/lancedb-win32-x64-msvc': - specifier: ^0.22.3 - version: 0.22.3 devDependencies: '@ai-sdk/anthropic': specifier: ^4.0.39 @@ -110,10 +104,10 @@ importers: version: 3.2.2(react@18.3.1) '@electron/notarize': specifier: ^2.5.0 - version: 2.5.0 + version: 2.5.0(supports-color@8.1.1) '@electron/rebuild': specifier: ^4.0.4 - version: 4.2.0 + version: 4.2.0(supports-color@8.1.1) '@emnapi/core': specifier: ^1.11.2 version: 1.11.2 @@ -122,16 +116,13 @@ importers: version: 1.11.2 '@modelcontextprotocol/sdk': specifier: ^1.26.0 - version: 1.29.0(zod@3.25.76) + version: 1.29.0(supports-color@8.1.1)(zod@3.25.76) '@monaco-editor/react': specifier: ^4.6.0 version: 4.7.0(monaco-editor@0.55.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/adm-zip': specifier: ^0.5.7 version: 0.5.8 - '@types/dompurify': - specifier: ^3.0.5 - version: 3.2.0 '@types/node': specifier: ^24.0.0 version: 24.13.3 @@ -191,13 +182,16 @@ importers: version: 3.4.13 electron: specifier: ^39.8.5 - version: 39.8.10 + version: 39.8.10(supports-color@8.1.1) electron-builder: specifier: ^26.15.3 - version: 26.15.3(electron-builder-squirrel-windows@26.15.3) + version: 26.15.3(electron-builder-squirrel-windows@26.15.3)(supports-color@8.1.1) esbuild: specifier: ^0.28.1 version: 0.28.1 + extract-zip: + specifier: 2.0.1 + version: 2.0.1(patch_hash=5300cf842856000f03aa544d2ace3dbf9587123c3471490a2384458ce0706856)(supports-color@8.1.1) fast-check: specifier: ^4.4.0 version: 4.9.0 @@ -222,6 +216,9 @@ importers: monaco-editor: specifier: ^0.55.1 version: 0.55.1 + onnx-proto: + specifier: 4.0.4 + version: 4.0.4 png2icons: specifier: ^2.0.1 version: 2.0.1 @@ -236,7 +233,7 @@ importers: version: 18.3.1(react@18.3.1) react-markdown: specifier: ^9.0.1 - version: 9.1.0(@types/react@18.3.31)(react@18.3.1) + version: 9.1.0(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1) react-syntax-highlighter: specifier: ^16.1.0 version: 16.1.1(react@18.3.1) @@ -254,10 +251,10 @@ importers: version: 6.0.0 remark-gfm: specifier: ^4.0.1 - version: 4.0.1 + version: 4.0.1(supports-color@8.1.1) remark-math: specifier: ^6.0.0 - version: 6.0.0 + version: 6.0.0(supports-color@8.1.1) tailwindcss: specifier: ^3.4.16 version: 3.4.19(yaml@2.9.0) @@ -291,6 +288,10 @@ importers: zustand: specifier: ^5.0.2 version: 5.0.14(@types/react@18.3.31)(react@18.3.1) + optionalDependencies: + '@lancedb/lancedb-win32-x64-msvc': + specifier: ^0.22.3 + version: 0.22.3 packages: @@ -641,89 +642,105 @@ packages: resolution: {integrity: sha512-C0SqjoFKnszqa44EQ7xoaT48nnO0lOyXEULfXMWi8krrjOPGYkeK30Okzla6ATbBYsyZ0ySinK0FVkpv3DwzfQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.3.0': resolution: {integrity: sha512-A8UpHoUDW4DwnXoV6+q3C1s7QLRAHtPDEjWuNZjwHMyoCNZnm0GeNN8ls9f/bsEYTRQRW96C/n34XJQHJ2fT7A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.3.0': resolution: {integrity: sha512-WOpkVxAjFd369iaIzEgNRreFD+gWdUMIGD5zplhNKNeqS6mm5dac3q2AFyCBmzYoAdouzZvRBgxy4z8QHZb4/A==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.3.0': resolution: {integrity: sha512-DRWw0mOHusrCCuw2rqP87oLg6PGlkomVDFqw2hIwsSfwWpu4k3XLcBPaKKl6ct/GtL/cwNkgwjV/tc0Mqht3VA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.3.0': resolution: {integrity: sha512-9APy+nFWhHS+kzLgWZfLcyrUd7YqnAQVa4BPOo4xkoHpdoktOAPG4cEr9+Jpl0TtqfVmcMJimNL5qNTyyOHZNA==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.3.0': resolution: {integrity: sha512-y9RNUYDe2A1UAdhLyfeOodGRszQdaEoe4nfOpp/sNVPl2CWIcUyFaDoCh4vPLPxu19803j2naLqZup2WxDXCLA==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.3.0': resolution: {integrity: sha512-cC1wkC0Mlucd0KSiGrLkJnB/ZqPvZCntc/Lk7ZnYO5ZSbF2euNek4Xvxafojq+wN1q/W0eprdpUIjUr/EV2PBg==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.3.0': resolution: {integrity: sha512-LiYMhUZicB1QG//+RvmYZpXJO8fYRENfp+MZUCnG9aw+AKvGAy9gPaCnuwsPcBFs8EV66M0NNxj9VHcNklE8zw==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.35.1': resolution: {integrity: sha512-ErCRyGU7LeoaFBZ0xW8hhLlXzhAg80sc4vxePB86qvtEvW1jEhhmbiNBP4oEzZfPMnu6HwHXfzD2W2kBU+RnCw==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.35.1': resolution: {integrity: sha512-jygmR02PpCYypt7xB7nst1vqjZp/BpRA/Kf9nK7qRponJ/KrLPaZWEG4G15z1d2FZ6XqI+T0350ha3RSnKx24A==} engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.35.1': resolution: {integrity: sha512-LUWZ2+r2UoLCd8j0RLCwQ4gL6w47+Y7igxtVnPIDXOOEjV86LpBkAHq5VpJeg+GHbw0KN/JWlPJOdZjyZnFqFQ==} engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.35.1': resolution: {integrity: sha512-i7x6J3mwF4JgT0sM4V4WlAWdJ0bucPtA9rzO1bTji1n5qgBq/W5nn87RvOQPleuuxahNoLdTngByD8/vDDLArw==} engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.35.1': resolution: {integrity: sha512-0zSaTUjTF0kIWTSYxD4EG/nvCU4jez53+3RdURtoY3HvbXtIQ98W90JnrGz/oLRFuEnfIy9+7xeq883euc0ZWw==} engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.35.1': resolution: {integrity: sha512-NbJD4mWdeyrNQKluO/tR/wBDOelcowSVGNBWxI0e3ZtlXc6F/UOVKDj1MLD4zl3oHTuvKW3s+MA9N54YTldAYw==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.35.1': resolution: {integrity: sha512-VoW2sQCWI+0YIKQEmWJ8vzaQjTg9wIyfkFpvEfAS2h43X6iHu7GTk1hhOgB4IpSzCHe8UwQZIcx7b81VTaOrJA==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.35.1': resolution: {integrity: sha512-LjBoSd/c5JU0/K5MwzDMlgsSRP2bPn98JQGFFQAOLQ0bU/1z4ekxUdSKY9BmlwSh/cA+OrvpgsWqfZyYfVHBRw==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.35.1': resolution: {integrity: sha512-PCQUoQdZyE8tp3HpbevuihfUmgSP4qWI0FGEPWoeXqaS+cUrFfemabHQiebUmUmlUhCuNnQMxGrQ+CPqK4hnxg==} @@ -780,24 +797,28 @@ packages: engines: {node: '>= 18'} cpu: [arm64] os: [linux] + libc: [glibc] '@lancedb/lancedb-linux-arm64-musl@0.23.0': resolution: {integrity: sha512-c2UCtGoYjA3oDdw5y3RLK7J2th3rSjYBng+1I03vU9g092y8KATAJO/lV2AtyxSC+esSuyY1dMEaj8ADcXjZAA==} engines: {node: '>= 18'} cpu: [arm64] os: [linux] + libc: [musl] '@lancedb/lancedb-linux-x64-gnu@0.23.0': resolution: {integrity: sha512-OPL7tK3JCTx43ZxvbVs+CljfCer0KrojANQbcJ2V4VAp6XBhKx1sBAlIVGuCrd93pA8UOUP3iHsM7aglPo6rCg==} engines: {node: '>= 18'} cpu: [x64] os: [linux] + libc: [glibc] '@lancedb/lancedb-linux-x64-musl@0.23.0': resolution: {integrity: sha512-1ZEoQDwOrKvwPyAG+95/r1NYqX8Ca5bRek8Vr62CzWCEmHd/pFeEGWZ5STrkh+Bt3GLdi2JOivFtRbmuBAJypQ==} engines: {node: '>= 18'} cpu: [x64] os: [linux] + libc: [musl] '@lancedb/lancedb-win32-arm64-msvc@0.23.0': resolution: {integrity: sha512-OuD1mkrgXvijRlXdbx3LvfuorO04FD5qHegnTOWGXh1sIwwrvvhcJAvXUGBNLY4n/lsWvA+xTjtMwRjUitvPKg==} @@ -915,36 +936,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.6.0': resolution: {integrity: sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.6.0': resolution: {integrity: sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.6.0': resolution: {integrity: sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.6.0': resolution: {integrity: sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.6.0': resolution: {integrity: sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-win32-arm64@2.6.0': resolution: {integrity: sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==} @@ -1038,36 +1065,42 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.1.5': resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.1.5': resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.1.5': resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.1.5': resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.1.5': resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@rolldown/binding-openharmony-arm64@1.1.5': resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} @@ -1133,10 +1166,6 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/dompurify@3.2.0': - resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==} - deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed. - '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -2589,24 +2618,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.33.0: resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.33.0: resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.33.0: resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.33.0: resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} @@ -4143,46 +4176,46 @@ snapshots: fs-extra: 9.1.0 minimist: 1.2.8 - '@electron/get@2.0.3': + '@electron/get@2.0.3(supports-color@8.1.1)': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) env-paths: 2.2.1 fs-extra: 8.1.0 got: 11.8.6 progress: 2.0.3 semver: 6.3.1 - sumchecker: 3.0.1 + sumchecker: 3.0.1(supports-color@8.1.1) optionalDependencies: global-agent: 3.0.0 transitivePeerDependencies: - supports-color - '@electron/get@3.1.0': + '@electron/get@3.1.0(supports-color@8.1.1)': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) env-paths: 2.2.1 fs-extra: 8.1.0 got: 11.8.6 progress: 2.0.3 semver: 6.3.1 - sumchecker: 3.0.1 + sumchecker: 3.0.1(supports-color@8.1.1) optionalDependencies: global-agent: 3.0.0 transitivePeerDependencies: - supports-color - '@electron/notarize@2.5.0': + '@electron/notarize@2.5.0(supports-color@8.1.1)': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 9.1.0 promise-retry: 2.0.1 transitivePeerDependencies: - supports-color - '@electron/osx-sign@1.3.3': + '@electron/osx-sign@1.3.3(supports-color@8.1.1)': dependencies: compare-version: 0.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 10.1.0 isbinaryfile: 4.0.10 minimist: 1.2.8 @@ -4190,22 +4223,22 @@ snapshots: transitivePeerDependencies: - supports-color - '@electron/rebuild@4.2.0': + '@electron/rebuild@4.2.0(supports-color@8.1.1)': dependencies: '@malept/cross-spawn-promise': 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) node-abi: 4.33.0 node-api-version: 0.2.1 node-gyp: 12.4.0 - read-binary-file-arch: 1.0.6 + read-binary-file-arch: 1.0.6(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@electron/universal@2.0.3': + '@electron/universal@2.0.3(supports-color@8.1.1)': dependencies: '@electron/asar': 3.4.1 '@malept/cross-spawn-promise': 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) dir-compare: 4.2.0 fs-extra: 11.4.0 minimatch: 9.0.9 @@ -4213,10 +4246,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@electron/windows-sign@1.2.2': + '@electron/windows-sign@1.2.2(supports-color@8.1.1)': dependencies: cross-dirname: 0.1.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 11.4.0 minimist: 1.2.8 postject: 1.0.0-alpha.6 @@ -4497,16 +4530,16 @@ snapshots: dependencies: cross-spawn: 7.0.6 - '@malept/flatpak-bundler@0.4.0': + '@malept/flatpak-bundler@0.4.0(supports-color@8.1.1)': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 9.1.0 lodash: 4.18.1 tmp-promise: 3.0.3 transitivePeerDependencies: - supports-color - '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': + '@modelcontextprotocol/sdk@1.29.0(supports-color@8.1.1)(zod@3.25.76)': dependencies: '@hono/node-server': 2.1.0(hono@4.13.1) ajv: 8.20.0 @@ -4516,8 +4549,8 @@ snapshots: cross-spawn: 7.0.6 eventsource: 3.0.7 eventsource-parser: 3.1.0 - express: 5.2.1 - express-rate-limit: 8.6.0(express@5.2.1) + express: 5.2.1(supports-color@8.1.1) + express-rate-limit: 8.6.0(express@5.2.1(supports-color@8.1.1))(supports-color@8.1.1) hono: 4.13.1 jose: 6.2.3 json-schema-typed: 8.0.2 @@ -4759,10 +4792,6 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/dompurify@3.2.0': - dependencies: - dompurify: 3.4.13 - '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.9 @@ -5051,33 +5080,33 @@ snapshots: json-bignum: 0.0.3 tslib: 2.8.1 - app-builder-lib@26.15.3(dmg-builder@26.15.3)(electron-builder-squirrel-windows@26.15.3): + app-builder-lib@26.15.3(dmg-builder@26.15.3)(electron-builder-squirrel-windows@26.15.3)(supports-color@8.1.1): dependencies: '@electron/asar': 3.4.1 '@electron/fuses': 1.8.0 - '@electron/get': 3.1.0 - '@electron/notarize': 2.5.0 - '@electron/osx-sign': 1.3.3 - '@electron/rebuild': 4.2.0 - '@electron/universal': 2.0.3 - '@malept/flatpak-bundler': 0.4.0 + '@electron/get': 3.1.0(supports-color@8.1.1) + '@electron/notarize': 2.5.0(supports-color@8.1.1) + '@electron/osx-sign': 1.3.3(supports-color@8.1.1) + '@electron/rebuild': 4.2.0(supports-color@8.1.1) + '@electron/universal': 2.0.3(supports-color@8.1.1) + '@malept/flatpak-bundler': 0.4.0(supports-color@8.1.1) '@noble/hashes': 2.2.0 '@peculiar/webcrypto': 1.7.1 '@types/fs-extra': 9.0.13 ajv: 8.20.0 asn1js: 3.0.10 async-exit-hook: 2.0.1 - builder-util: 26.15.3 - builder-util-runtime: 9.7.0 + builder-util: 26.15.3(supports-color@8.1.1) + builder-util-runtime: 9.7.0(supports-color@8.1.1) chromium-pickle-js: 0.2.0 ci-info: 4.3.1 - debug: 4.4.3 - dmg-builder: 26.15.3(electron-builder-squirrel-windows@26.15.3) + debug: 4.4.3(supports-color@8.1.1) + dmg-builder: 26.15.3(electron-builder-squirrel-windows@26.15.3)(supports-color@8.1.1) dotenv: 16.6.1 dotenv-expand: 11.0.7 ejs: 3.1.10 - electron-builder-squirrel-windows: 26.15.3(dmg-builder@26.15.3) - electron-publish: 26.15.3 + electron-builder-squirrel-windows: 26.15.3(dmg-builder@26.15.3)(supports-color@8.1.1) + electron-publish: 26.15.3(supports-color@8.1.1) fs-extra: 10.1.0 hosted-git-info: 4.1.0 isbinaryfile: 5.0.7 @@ -5195,11 +5224,11 @@ snapshots: bluebird@3.7.2: {} - body-parser@2.3.0: + body-parser@2.3.0(supports-color@8.1.1): dependencies: bytes: 3.1.2 content-type: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) http-errors: 2.0.1 iconv-lite: 0.7.3 on-finished: 2.4.1 @@ -5246,23 +5275,23 @@ snapshots: buildcheck@0.0.7: optional: true - builder-util-runtime@9.7.0: + builder-util-runtime@9.7.0(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) sax: 1.6.0 transitivePeerDependencies: - supports-color - builder-util@26.15.3: + builder-util@26.15.3(supports-color@8.1.1): dependencies: '@types/debug': 4.1.13 - builder-util-runtime: 9.7.0 + builder-util-runtime: 9.7.0(supports-color@8.1.1) chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 10.1.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + http-proxy-agent: 7.0.2(supports-color@8.1.1) + https-proxy-agent: 7.0.6(supports-color@8.1.1) js-yaml: 4.3.1 sanitize-filename: 1.6.4 source-map-support: 0.5.21 @@ -5468,13 +5497,17 @@ snapshots: dependencies: mimic-fn: 3.1.0 - debug@3.2.7: + debug@3.2.7(supports-color@8.1.1): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 - debug@4.4.3: + debug@4.4.3(supports-color@8.1.1): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 decode-named-character-reference@1.3.0: dependencies: @@ -5526,10 +5559,10 @@ snapshots: dlv@1.1.3: {} - dmg-builder@26.15.3(electron-builder-squirrel-windows@26.15.3): + dmg-builder@26.15.3(electron-builder-squirrel-windows@26.15.3)(supports-color@8.1.1): dependencies: - app-builder-lib: 26.15.3(dmg-builder@26.15.3)(electron-builder-squirrel-windows@26.15.3) - builder-util: 26.15.3 + app-builder-lib: 26.15.3(dmg-builder@26.15.3)(electron-builder-squirrel-windows@26.15.3)(supports-color@8.1.1) + builder-util: 26.15.3(supports-color@8.1.1) fs-extra: 10.1.0 js-yaml: 4.3.1 transitivePeerDependencies: @@ -5593,23 +5626,23 @@ snapshots: dependencies: jake: 10.9.4 - electron-builder-squirrel-windows@26.15.3(dmg-builder@26.15.3): + electron-builder-squirrel-windows@26.15.3(dmg-builder@26.15.3)(supports-color@8.1.1): dependencies: - app-builder-lib: 26.15.3(dmg-builder@26.15.3)(electron-builder-squirrel-windows@26.15.3) - builder-util: 26.15.3 - electron-winstaller: 5.4.0 + app-builder-lib: 26.15.3(dmg-builder@26.15.3)(electron-builder-squirrel-windows@26.15.3)(supports-color@8.1.1) + builder-util: 26.15.3(supports-color@8.1.1) + electron-winstaller: 5.4.0(supports-color@8.1.1) transitivePeerDependencies: - dmg-builder - supports-color - electron-builder@26.15.3(electron-builder-squirrel-windows@26.15.3): + electron-builder@26.15.3(electron-builder-squirrel-windows@26.15.3)(supports-color@8.1.1): dependencies: - app-builder-lib: 26.15.3(dmg-builder@26.15.3)(electron-builder-squirrel-windows@26.15.3) - builder-util: 26.15.3 - builder-util-runtime: 9.7.0 + app-builder-lib: 26.15.3(dmg-builder@26.15.3)(electron-builder-squirrel-windows@26.15.3)(supports-color@8.1.1) + builder-util: 26.15.3(supports-color@8.1.1) + builder-util-runtime: 9.7.0(supports-color@8.1.1) chalk: 4.1.2 ci-info: 4.4.0 - dmg-builder: 26.15.3(electron-builder-squirrel-windows@26.15.3) + dmg-builder: 26.15.3(electron-builder-squirrel-windows@26.15.3)(supports-color@8.1.1) fs-extra: 10.1.0 lazy-val: 1.0.5 simple-update-notifier: 2.0.0 @@ -5618,12 +5651,12 @@ snapshots: - electron-builder-squirrel-windows - supports-color - electron-publish@26.15.3: + electron-publish@26.15.3(supports-color@8.1.1): dependencies: '@types/fs-extra': 9.0.13 aws4: 1.13.2 - builder-util: 26.15.3 - builder-util-runtime: 9.7.0 + builder-util: 26.15.3(supports-color@8.1.1) + builder-util-runtime: 9.7.0(supports-color@8.1.1) chalk: 4.1.2 form-data: 4.0.6 fs-extra: 10.1.0 @@ -5639,9 +5672,9 @@ snapshots: electron-to-chromium@1.5.393: {} - electron-updater@6.8.9: + electron-updater@6.8.9(supports-color@8.1.1): dependencies: - builder-util-runtime: 9.7.0 + builder-util-runtime: 9.7.0(supports-color@8.1.1) fs-extra: 10.1.0 js-yaml: 4.3.1 lazy-val: 1.0.5 @@ -5652,23 +5685,23 @@ snapshots: transitivePeerDependencies: - supports-color - electron-winstaller@5.4.0: + electron-winstaller@5.4.0(supports-color@8.1.1): dependencies: '@electron/asar': 3.4.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 7.0.1 lodash: 4.18.1 temp: 0.9.4 optionalDependencies: - '@electron/windows-sign': 1.2.2 + '@electron/windows-sign': 1.2.2(supports-color@8.1.1) transitivePeerDependencies: - supports-color - electron@39.8.10: + electron@39.8.10(supports-color@8.1.1): dependencies: - '@electron/get': 2.0.3 + '@electron/get': 2.0.3(supports-color@8.1.1) '@types/node': 22.20.1 - extract-zip: 2.0.1(patch_hash=nevhi44iznjigonvauz2rkjwxi) + extract-zip: 2.0.1(patch_hash=5300cf842856000f03aa544d2ace3dbf9587123c3471490a2384458ce0706856)(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -5770,28 +5803,28 @@ snapshots: exponential-backoff@3.1.3: {} - express-rate-limit@8.6.0(express@5.2.1): + express-rate-limit@8.6.0(express@5.2.1(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - debug: 4.4.3 - express: 5.2.1 + debug: 4.4.3(supports-color@8.1.1) + express: 5.2.1(supports-color@8.1.1) ip-address: 10.5.0 transitivePeerDependencies: - supports-color - express@5.2.1: + express@5.2.1(supports-color@8.1.1): dependencies: accepts: 2.0.0 - body-parser: 2.3.0 + body-parser: 2.3.0(supports-color@8.1.1) content-disposition: 1.1.0 content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 2.1.1 + finalhandler: 2.1.1(supports-color@8.1.1) fresh: 2.0.0 http-errors: 2.0.1 merge-descriptors: 2.0.0 @@ -5802,9 +5835,9 @@ snapshots: proxy-addr: 2.0.7 qs: 6.15.3 range-parser: 1.3.0 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 + router: 2.2.0(supports-color@8.1.1) + send: 1.2.1(supports-color@8.1.1) + serve-static: 2.2.1(supports-color@8.1.1) statuses: 2.0.2 type-is: 2.1.0 vary: 1.1.2 @@ -5813,9 +5846,9 @@ snapshots: extend@3.0.2: {} - extract-zip@2.0.1(patch_hash=nevhi44iznjigonvauz2rkjwxi): + extract-zip@2.0.1(patch_hash=5300cf842856000f03aa544d2ace3dbf9587123c3471490a2384458ce0706856)(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -5867,9 +5900,9 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@2.1.1: + finalhandler@2.1.1(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -6116,7 +6149,7 @@ snapshots: '@ungap/structured-clone': 1.3.3 unist-util-position: 5.0.0 - hast-util-to-jsx-runtime@2.3.6: + hast-util-to-jsx-runtime@2.3.6(supports-color@8.1.1): dependencies: '@types/estree': 1.0.9 '@types/hast': 3.0.5 @@ -6125,9 +6158,9 @@ snapshots: devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-mdxjs-esm: 2.0.1 + mdast-util-mdx-expression: 2.0.1(supports-color@8.1.1) + mdast-util-mdx-jsx: 3.2.0(supports-color@8.1.1) + mdast-util-mdxjs-esm: 2.0.1(supports-color@8.1.1) property-information: 7.2.0 space-separated-tokens: 2.0.2 style-to-js: 1.1.21 @@ -6193,10 +6226,10 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 - http-proxy-agent@7.0.2: + http-proxy-agent@7.0.2(supports-color@8.1.1): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -6205,10 +6238,10 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - https-proxy-agent@7.0.6: + https-proxy-agent@7.0.6(supports-color@8.1.1): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -6492,14 +6525,14 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - mdast-util-from-markdown@2.0.3: + mdast-util-from-markdown@2.0.3(supports-color@8.1.1): dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 - micromark: 4.0.2 + micromark: 4.0.2(supports-color@8.1.1) micromark-util-decode-numeric-character-reference: 2.0.2 micromark-util-decode-string: 2.0.1 micromark-util-normalize-identifier: 2.0.1 @@ -6517,79 +6550,79 @@ snapshots: mdast-util-find-and-replace: 3.0.2 micromark-util-character: 2.1.1 - mdast-util-gfm-footnote@2.1.0: + mdast-util-gfm-footnote@2.1.0(supports-color@8.1.1): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@8.1.1) mdast-util-to-markdown: 2.1.2 micromark-util-normalize-identifier: 2.0.1 transitivePeerDependencies: - supports-color - mdast-util-gfm-strikethrough@2.0.0: + mdast-util-gfm-strikethrough@2.0.0(supports-color@8.1.1): dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@8.1.1) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm-table@2.0.0: + mdast-util-gfm-table@2.0.0(supports-color@8.1.1): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 markdown-table: 3.0.4 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@8.1.1) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm-task-list-item@2.0.0: + mdast-util-gfm-task-list-item@2.0.0(supports-color@8.1.1): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@8.1.1) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm@3.1.0: + mdast-util-gfm@3.1.0(supports-color@8.1.1): dependencies: - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@8.1.1) mdast-util-gfm-autolink-literal: 2.0.1 - mdast-util-gfm-footnote: 2.1.0 - mdast-util-gfm-strikethrough: 2.0.0 - mdast-util-gfm-table: 2.0.0 - mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-gfm-footnote: 2.1.0(supports-color@8.1.1) + mdast-util-gfm-strikethrough: 2.0.0(supports-color@8.1.1) + mdast-util-gfm-table: 2.0.0(supports-color@8.1.1) + mdast-util-gfm-task-list-item: 2.0.0(supports-color@8.1.1) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-math@3.0.0: + mdast-util-math@3.0.0(supports-color@8.1.1): dependencies: '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 longest-streak: 3.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@8.1.1) mdast-util-to-markdown: 2.1.2 unist-util-remove-position: 5.0.0 transitivePeerDependencies: - supports-color - mdast-util-mdx-expression@2.0.1: + mdast-util-mdx-expression@2.0.1(supports-color@8.1.1): dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@8.1.1) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-mdx-jsx@3.2.0: + mdast-util-mdx-jsx@3.2.0(supports-color@8.1.1): dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.5 @@ -6597,7 +6630,7 @@ snapshots: '@types/unist': 3.0.3 ccount: 2.0.1 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@8.1.1) mdast-util-to-markdown: 2.1.2 parse-entities: 4.0.2 stringify-entities: 4.0.4 @@ -6606,13 +6639,13 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-mdxjs-esm@2.0.1: + mdast-util-mdxjs-esm@2.0.1(supports-color@8.1.1): dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@8.1.1) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -6835,10 +6868,10 @@ snapshots: micromark-util-types@2.0.2: {} - micromark@4.0.2: + micromark@4.0.2(supports-color@8.1.1): dependencies: '@types/debug': 4.1.13 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) decode-named-character-reference: 1.3.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -7077,9 +7110,9 @@ snapshots: pathe@2.0.3: {} - pdf-parse@1.1.1: + pdf-parse@1.1.1(supports-color@8.1.1): dependencies: - debug: 3.2.7 + debug: 3.2.7(supports-color@8.1.1) node-ensure: 0.0.0 transitivePeerDependencies: - supports-color @@ -7245,17 +7278,17 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 - react-markdown@9.1.0(@types/react@18.3.31)(react@18.3.1): + react-markdown@9.1.0(@types/react@18.3.31)(react@18.3.1)(supports-color@8.1.1): dependencies: '@types/hast': 3.0.5 '@types/mdast': 4.0.4 '@types/react': 18.3.31 devlop: 1.1.0 - hast-util-to-jsx-runtime: 2.3.6 + hast-util-to-jsx-runtime: 2.3.6(supports-color@8.1.1) html-url-attributes: 3.0.1 mdast-util-to-hast: 13.2.1 react: 18.3.1 - remark-parse: 11.0.0 + remark-parse: 11.0.0(supports-color@8.1.1) remark-rehype: 11.1.2 unified: 11.0.5 unist-util-visit: 5.1.0 @@ -7282,9 +7315,9 @@ snapshots: dependencies: loose-envify: 1.4.0 - read-binary-file-arch@1.0.6: + read-binary-file-arch@1.0.6(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -7338,30 +7371,30 @@ snapshots: '@types/hast': 3.0.5 hast-util-sanitize: 5.0.2 - remark-gfm@4.0.1: + remark-gfm@4.0.1(supports-color@8.1.1): dependencies: '@types/mdast': 4.0.4 - mdast-util-gfm: 3.1.0 + mdast-util-gfm: 3.1.0(supports-color@8.1.1) micromark-extension-gfm: 3.0.0 - remark-parse: 11.0.0 + remark-parse: 11.0.0(supports-color@8.1.1) remark-stringify: 11.0.0 unified: 11.0.5 transitivePeerDependencies: - supports-color - remark-math@6.0.0: + remark-math@6.0.0(supports-color@8.1.1): dependencies: '@types/mdast': 4.0.4 - mdast-util-math: 3.0.0 + mdast-util-math: 3.0.0(supports-color@8.1.1) micromark-extension-math: 3.1.0 unified: 11.0.5 transitivePeerDependencies: - supports-color - remark-parse@11.0.0: + remark-parse@11.0.0(supports-color@8.1.1): dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.0.3(supports-color@8.1.1) micromark-util-types: 2.0.2 unified: 11.0.5 transitivePeerDependencies: @@ -7443,9 +7476,9 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.1.5 '@rolldown/binding-win32-x64-msvc': 1.1.5 - router@2.2.0: + router@2.2.0(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 @@ -7486,9 +7519,9 @@ snapshots: semver@7.8.5: {} - send@1.2.1: + send@1.2.1(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -7507,12 +7540,12 @@ snapshots: type-fest: 0.13.1 optional: true - serve-static@2.2.1: + serve-static@2.2.1(supports-color@8.1.1): dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 1.2.1 + send: 1.2.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -7674,9 +7707,9 @@ snapshots: tinyglobby: 0.2.17 ts-interface-checker: 0.1.13 - sumchecker@3.0.1: + sumchecker@3.0.1(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 49bad88c..74c699fc 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,18 +2,19 @@ packages: - '.' -# 与 .npmrc 双写:pnpm 9 读 .npmrc;pnpm 11+ 以 workspace 为准。 +# pnpm 11 的唯一项目级配置入口。package.json 不再保留一份会被忽略的 +# overrides/patches 副本,避免安全约束看似存在、实际不生效。 # 强制本机 Node/pnpm 与 package.json engines + packageManager 一致。 engineStrict: true packageManagerStrict: true packageManagerStrictVersion: true +# 安装必须是显式步骤。运行 test/build 时发现依赖状态变化就直接执行目标, +# 不让 pnpm 在后台隐式重装整棵依赖树。 +verifyDepsBeforeRun: false + # 安全 override:把传递依赖顶到已修复的补丁版本。 # -# 必须与 package.json 的 pnpm.overrides 保持一致 —— 两份不是冗余: -# pnpm 9 只读 package.json 的 pnpm.overrides,pnpm 11 只读这里。 -# 本仓库用 packageManager 钉在 9.15.9;安装时 packageManagerStrictVersion -# 会拒绝其它 pnpm 大/小版本。overrides 仍双写,避免钉子松动后静默失效。 overrides: brace-expansion@<1.1.18: ^1.1.18 brace-expansion@>=2.0.0 <2.1.4: ^2.1.4 @@ -45,25 +46,14 @@ minimumReleaseAge: 0 # pnpm v11+ 默认会忽略某些包的构建脚本,在此明确批准 allowBuilds: - '0': '@parcel/watcher' - '1': core-js - '2': cpu-features - '3': dugite - '4': electron - '5': electron-winstaller - '6': esbuild - '7': node-pty - '8': protobufjs - '9': sharp - '10': ssh2 - '@parcel/watcher': set this to true or false - core-js: set this to true or false - cpu-features: set this to true or false - dugite: set this to true or false - electron: set this to true or false - electron-winstaller: set this to true or false - esbuild: set this to true or false - node-pty: set this to true or false - protobufjs: set this to true or false - sharp: set this to true or false - ssh2: set this to true or false + '@parcel/watcher': true + core-js: true + cpu-features: true + dugite: true + electron: true + electron-winstaller: true + esbuild: true + node-pty: true + protobufjs: true + sharp: true + ssh2: true diff --git a/src/main/preload.ts b/src/main/preload.ts index bf1bf4a4..711541d1 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -287,6 +287,7 @@ export interface ElectronAPI { readDir: (path: string) => Promise<{ name: string; path: string; isDirectory: boolean }[]> getFileTree: (path: string, maxDepth?: number) => Promise readFile: (path: string, encoding?: string, options?: { full?: boolean }) => Promise + readTextChunk: (path: string, offset?: number, maxBytes?: number) => Promise statFile: (path: string) => Promise<{ size: number; isDirectory: boolean; isFile: boolean; mtimeMs: number } | null> readRichContent: (path: string, options?: ReadRichContentOptions) => Promise readImageAnalysis: (request: ImageAnalysisRequest) => Promise @@ -627,6 +628,7 @@ contextBridge.exposeInMainWorld('electronAPI', { readDir: (path: string) => ipcRenderer.invoke('file:readDir', path), getFileTree: (path: string, maxDepth?: number) => ipcRenderer.invoke('file:getTree', path, maxDepth), readFile: (path: string, encoding?: string, options?: { full?: boolean }) => ipcRenderer.invoke('file:read', path, encoding, options), + readTextChunk: (path: string, offset?: number, maxBytes?: number) => ipcRenderer.invoke('file:readTextChunk', path, offset, maxBytes), statFile: (path: string) => ipcRenderer.invoke('file:stat', path), readBinaryFile: (path: string) => ipcRenderer.invoke('file:readBinary', path), readRichContent: (path: string, options?: ReadRichContentOptions) => ipcRenderer.invoke('file:readRichContent', path, options), diff --git a/src/main/security/fileUtils.ts b/src/main/security/fileUtils.ts index 9c03460b..3681e52a 100644 --- a/src/main/security/fileUtils.ts +++ b/src/main/security/fileUtils.ts @@ -2,6 +2,10 @@ import { promises as fsPromises } from 'fs' import type { FileHandle } from 'fs/promises' import { randomUUID } from 'crypto' import * as iconv from 'iconv-lite' +import type { TextFileChunk } from '@shared/types/fileChunk' +import { LARGE_FILE_PAGE_BYTES } from '@shared/types/largeFile' + +const MAX_TEXT_CHUNK_BYTES = 4 * 1024 * 1024 export type SupportedEncoding = 'utf-8' | 'utf-8-bom' | 'gbk' | 'gb18030' @@ -52,9 +56,66 @@ function encodeContent(content: string, encoding: SupportedEncoding): Buffer { return Buffer.from(content, 'utf-8') } -export async function readFileWithEncoding(filePath: string): Promise { - const result = await readFileWithEncodingInfo(filePath) - return result.content +function findCompleteUtf8Boundary(buffer: Buffer, end: number): number { + let leadIndex = end - 1 + while (leadIndex >= 0 && (buffer[leadIndex] & 0xc0) === 0x80) leadIndex -= 1 + if (leadIndex < 0) return end + + const leadByte = buffer[leadIndex] + const expectedBytes = leadByte < 0x80 ? 1 : leadByte < 0xe0 ? 2 : leadByte < 0xf0 ? 3 : 4 + return end - leadIndex < expectedBytes ? leadIndex : end +} + +export async function readTextFileChunk( + filePath: string, + offset = 0, + requestedBytes = LARGE_FILE_PAGE_BYTES, +): Promise { + const stats = await fsPromises.stat(filePath) + const totalSize = stats.size + const finiteOffset = Number.isFinite(offset) ? offset : 0 + const finiteRequestedBytes = Number.isFinite(requestedBytes) ? requestedBytes : LARGE_FILE_PAGE_BYTES + const startOffset = Math.min(Math.max(0, Math.floor(finiteOffset)), totalSize) + // Four bytes are enough for one complete UTF-8 code point, so even a + // pathological tiny request always makes progress without corrupt decoding. + const chunkBytes = Math.min(Math.max(4, Math.floor(finiteRequestedBytes)), MAX_TEXT_CHUNK_BYTES) + if (startOffset >= totalSize) { + return { content: '', startOffset, nextOffset: startOffset, totalSize, eof: true } + } + + const bytesToRead = Math.min(chunkBytes, totalSize - startOffset) + const buffer = Buffer.allocUnsafe(bytesToRead) + const handle = await fsPromises.open(filePath, 'r') + try { + const { bytesRead } = await handle.read(buffer, 0, bytesToRead, startOffset) + let end = bytesRead + const reachesEof = startOffset + bytesRead >= totalSize + + if (!reachesEof) { + const newline = buffer.lastIndexOf(10, bytesRead - 1) + if (newline >= 0) { + end = newline + 1 + } else { + end = findCompleteUtf8Boundary(buffer, bytesRead) + if (end === 0) { + const firstByte = buffer[0] + const firstCharacterBytes = firstByte < 0x80 ? 1 : firstByte < 0xe0 ? 2 : firstByte < 0xf0 ? 3 : 4 + end = Math.min(firstCharacterBytes, bytesRead) + } + } + } + + const nextOffset = startOffset + end + return { + content: decodeBuffer(buffer.subarray(0, end), 'utf-8'), + startOffset, + nextOffset, + totalSize, + eof: nextOffset >= totalSize, + } + } finally { + await handle.close() + } } export async function readFileWithEncodingInfo( @@ -92,7 +153,7 @@ export async function readFileWithEncodingInfo( * parses the result or writes it back — must use `readFileWithEncodingInfo`, * otherwise a truncated read round-trips as a destructive write. */ -export async function readLargeFile( +async function readByteRange( filePath: string, start: number, maxLength: number, @@ -134,7 +195,7 @@ export async function readFileSized( const stats = await fsPromises.stat(filePath) if (stats.size > previewByteLimit) { - const content = await readLargeFile(filePath, 0, previewByteLimit) + const content = await readByteRange(filePath, 0, previewByteLimit) return { content, encoding: normalizeEncoding(encoding), diff --git a/src/main/security/secureFile.ts b/src/main/security/secureFile.ts index 54d57e62..9de856e9 100644 --- a/src/main/security/secureFile.ts +++ b/src/main/security/secureFile.ts @@ -16,7 +16,7 @@ import { getUserConfigDir } from '../services/configPath' import { fileApprovalScope, isRecentAgentApprovalProof, type AgentApprovalProof } from '@shared/security/executionPolicy' // 导入拆分的模块 -import { readFileWithEncodingInfo, readFileSized, readLargeFile, writeFileAtomic, getFileStats } from './fileUtils' +import { readFileWithEncodingInfo, readFileSized, readTextFileChunk, writeFileAtomic, getFileStats } from './fileUtils' import { systemPrivilegeService } from '../services/systemPrivilegeService' import { isSystemPermissionError } from '@shared/utils/permissionError' import { mutationFailure, mutationFailureFromError, mutationSuccess } from '../services/fileMutationResult' @@ -111,7 +111,6 @@ function canAccessFile( * a truncated parse there reads as "empty", and empty round-trips as deletion. */ const PREVIEW_BYTE_LIMIT = 5 * 1024 * 1024 -const PREVIEW_SLICE_BYTES = 10000 function readFileSingleFlight( filePath: string, @@ -344,6 +343,21 @@ export function registerSecureFileHandlers( } }) + ipcMain.handle('file:readTextChunk', async (event, filePath: string, offset?: number, maxBytes?: number) => { + if (!filePath || !canAccessFile(filePath, getWorkspaceSessionFn(event), 'read')) return null + try { + const chunk = await readTextFileChunk(filePath, offset, maxBytes) + securityManager.logOperation(OperationType.FILE_READ, filePath, true, { + size: chunk.nextOffset - chunk.startOffset, + chunked: true, + }) + return chunk + } catch (err) { + logger.security.error('[File] read text chunk failed:', filePath, toAppError(err).message) + return null + } + }) + // 读取二进制文件为 base64 ipcMain.handle('file:readBinary', async (event, filePath: string) => { if (!filePath) return null @@ -971,4 +985,3 @@ export { securityManager } // 重新导出拆分模块的类型和函数,方便外部使用 export type { FileWatcherEvent, WindowManagerContext } export { setupFileWatcher, cleanupFileWatcher } from './fileWatcher' -export { readFileWithEncoding, readLargeFile } from './fileUtils' diff --git a/src/renderer/components/agent/ChatMessage.tsx b/src/renderer/components/agent/ChatMessage.tsx index 0570bf51..8a5fb47e 100644 --- a/src/renderer/components/agent/ChatMessage.tsx +++ b/src/renderer/components/agent/ChatMessage.tsx @@ -47,6 +47,7 @@ import { SystemAlert, parseSystemAlert } from './SystemAlert' import { CompressionDigestCard } from './CompressionDigestCard' import { t } from '../../i18n' import { api } from '@/renderer/services/electronAPI' +import { safeOpenFile } from '@renderer/utils/fileUtils' import { writeClipboardText } from '@/renderer/services/clipboardService' import { toFullPath, getFileName } from '@shared/utils/pathUtils' import { stripToolCallLeaks } from '@renderer/agent/utils/toolCallLeakFilter' @@ -276,10 +277,8 @@ const MessageMetaGroup = React.memo(({ autoSkills, manualSkills, searchContent, // ignore } - const content = await api.file.readFull(targetPath) - if (content !== null) { - openFile(targetPath, content) - setActiveFile(targetPath) + const result = await safeOpenFile(targetPath, { language, confirmLargeFile: false }) + if (result.success) { return } } @@ -613,22 +612,18 @@ const MarkdownContent = React.memo(({ content: rawContent, fontSize, isStreaming [keepStreamingLayout, smoothContent], ) - const { workspacePath, openFile, setActiveFile } = useStore(useShallow(s => ({ workspacePath: s.workspacePath, openFile: s.openFile, setActiveFile: s.setActiveFile }))) + const workspacePath = useStore(s => s.workspacePath) const handleOpenFile = React.useCallback(async (filePath: string) => { if (!workspacePath) return const resolvedPath = toFullPath(filePath, workspacePath) try { - const content = await api.file.readFull(resolvedPath) - if (content !== null) { - openFile(resolvedPath, content) - setActiveFile(resolvedPath) - } + await safeOpenFile(resolvedPath, { showWarning: false, confirmLargeFile: false }) } catch (err) { console.warn('Failed to open file from markdown:', err) } - }, [workspacePath, openFile, setActiveFile]) + }, [workspacePath]) const markdownComponents = React.useMemo(() => ({ code({ className, children, node, ...props }: any) { diff --git a/src/renderer/components/agent/FileChangeCard.tsx b/src/renderer/components/agent/FileChangeCard.tsx index 78c837be..018143fe 100644 --- a/src/renderer/components/agent/FileChangeCard.tsx +++ b/src/renderer/components/agent/FileChangeCard.tsx @@ -16,6 +16,7 @@ import { toast } from '@components/common/ToastProvider' import { isCreateActionLabel, resolveFileChangeActionLabel } from '@renderer/agent/utils/fileWriteDisplay' import { RollingNumber } from '@components/ui' import { ToolApprovalActions } from './ToolApprovalActions' +import { safeOpenFile } from '@renderer/utils/fileUtils' interface FileChangeCardProps { toolCall: ToolCall @@ -126,10 +127,8 @@ function FileChangeCard({ } try { - const content = await api.file.readFull(absPath) - if (content !== null) { - openFile(absPath, content) - setActiveFile(absPath) + const result = await safeOpenFile(absPath, { language, confirmLargeFile: false }) + if (result.success) { return } } catch { @@ -137,7 +136,7 @@ function FileChangeCard({ } toast.error(`Failed to open file: ${getFileName(absPath)}`) - }, [filePath, workspacePath, openFile, setActiveFile]) + }, [filePath, workspacePath, language]) const diffStats = useMemo(() => { // Prefer precise stats returned by the tool when available. diff --git a/src/renderer/components/common/TextWithFileLinks.tsx b/src/renderer/components/common/TextWithFileLinks.tsx index 1510fe87..cc34f6e6 100644 --- a/src/renderer/components/common/TextWithFileLinks.tsx +++ b/src/renderer/components/common/TextWithFileLinks.tsx @@ -1,8 +1,7 @@ import React, { useCallback, useMemo } from 'react' -import { api } from '@/renderer/services/electronAPI' import { useStore } from '@store' -import { useShallow } from 'zustand/react/shallow' import { joinPath } from '@shared/utils/pathUtils' +import { safeOpenFile } from '@renderer/utils/fileUtils' interface TextWithFileLinksProps { text: string @@ -10,7 +9,7 @@ interface TextWithFileLinksProps { } export function TextWithFileLinks({ text, className = '' }: TextWithFileLinksProps) { - const { workspacePath, openFile, setActiveFile } = useStore(useShallow(s => ({ workspacePath: s.workspacePath, openFile: s.openFile, setActiveFile: s.setActiveFile }))) + const workspacePath = useStore(s => s.workspacePath) const handleFileClick = useCallback(async (e: React.MouseEvent, filePath: string) => { e.stopPropagation() @@ -21,15 +20,11 @@ export function TextWithFileLinks({ text, className = '' }: TextWithFileLinksPro } try { - const content = await api.file.readFull(absPath) - if (content !== null) { - openFile(absPath, content) - setActiveFile(absPath) - } + await safeOpenFile(absPath, { showWarning: false, confirmLargeFile: false }) } catch (error) { // Ignore if not a valid file } - }, [workspacePath, openFile, setActiveFile]) + }, [workspacePath]) const elements = useMemo(() => { if (!text) return null diff --git a/src/renderer/components/dialogs/QuickOpen.tsx b/src/renderer/components/dialogs/QuickOpen.tsx index ae953ef9..c86df473 100644 --- a/src/renderer/components/dialogs/QuickOpen.tsx +++ b/src/renderer/components/dialogs/QuickOpen.tsx @@ -14,6 +14,7 @@ import { t } from '@renderer/i18n' import { Button } from '../ui' import FileIcon from '../common/FileIcon' import { useElevatedToastLayer } from '../common/toastLayerStore' +import { safeOpenFile } from '@renderer/utils/fileUtils' interface QuickOpenProps { onClose: () => void @@ -164,7 +165,7 @@ const FileMatchItem = memo(function FileMatchItem({ export default function QuickOpen({ onClose }: QuickOpenProps) { useElevatedToastLayer(true) - const { workspacePath, openFile, language } = useStore(useShallow(s => ({ workspacePath: s.workspacePath, openFile: s.openFile, language: s.language }))) + const { workspacePath, language } = useStore(useShallow(s => ({ workspacePath: s.workspacePath, language: s.language }))) const [query, setQuery] = useState('') const [allFiles, setAllFiles] = useState([]) const [matches, setMatches] = useState([]) @@ -252,13 +253,9 @@ export default function QuickOpen({ onClose }: QuickOpenProps) { if (!workspacePath) return const fullPath = `${workspacePath}/${filePath}` - const content = await api.file.readFull(fullPath) - - if (content !== null) { - openFile(fullPath, content) - onClose() - } - }, [workspacePath, openFile, onClose]) + const result = await safeOpenFile(fullPath, { language, confirmLargeFile: false }) + if (result.success) onClose() + }, [workspacePath, language, onClose]) // 键盘导航 const handleKeyDown = useCallback((e: React.KeyboardEvent) => { diff --git a/src/renderer/components/editor/Editor.tsx b/src/renderer/components/editor/Editor.tsx index e102f0b0..2a36755f 100644 --- a/src/renderer/components/editor/Editor.tsx +++ b/src/renderer/components/editor/Editor.tsx @@ -66,6 +66,7 @@ import { defineMonacoTheme } from './utils/monacoTheme' import { isPreviewDocumentPath } from '@shared/types/preview' import { PLAN_BOARD_PATH, isPlanBoardPath } from '@shared/types/planBoard' import type { EditorConfig as SharedEditorConfig } from '@shared/config/types' +import LargeFileViewer from './LargeFileViewer' loader.config({ monaco }) @@ -229,7 +230,10 @@ export default function Editor() { if (!file || file.contentState !== 'unloaded') return let cancelled = false - void api.file.readFull(file.path, file.encoding).then((content) => { + const readContent = file.kind === 'large-preview' + ? api.file.readTextChunk(file.path, 0, file.largeFileView?.chunkSize).then(chunk => chunk?.content ?? null) + : api.file.readFull(file.path, file.encoding) + void readContent.then((content) => { if (cancelled) return if (content === null) { setFileContentState(file.path, 'error') @@ -250,7 +254,7 @@ export default function Editor() { const file = activeFilePath ? useStore.getState().openFiles.find(candidate => candidate.path === activeFilePath) : undefined - if (file?.contentState === 'loaded' && !isPreviewDocument && !isPlanBoardDocument) { + if (file?.contentState === 'loaded' && file.kind !== 'large-preview' && !isPreviewDocument && !isPlanBoardDocument) { notifyFileOpened(file.path, file.content) // 检查是否有跨文件跳转定义的待定位请求 const nav = consumePendingNavigation(file.path) @@ -272,7 +276,7 @@ export default function Editor() { const validUris = new Set( useStore.getState().openFiles.map(f => { - if (f.path.startsWith('diff://') || f.kind === 'preview' || f.contentState !== 'loaded') return '' + if (f.path.startsWith('diff://') || f.kind === 'preview' || f.kind === 'large-preview' || f.contentState !== 'loaded') return '' return monacoInstance.Uri.file(f.path).toString() }) ) @@ -572,6 +576,8 @@ export default function Editor() { ) : activeFile && activeFile.contentState !== 'loaded' ? ( + ) : activeFile?.kind === 'large-preview' ? ( + ) : activeFile && ( <> {/* Markdown 工具栏 */} diff --git a/src/renderer/components/editor/EditorContextMenu.tsx b/src/renderer/components/editor/EditorContextMenu.tsx index 1a6032bc..7a573349 100644 --- a/src/renderer/components/editor/EditorContextMenu.tsx +++ b/src/renderer/components/editor/EditorContextMenu.tsx @@ -15,6 +15,7 @@ import type { editor } from 'monaco-editor' import { logger } from '@shared/utils/Logger' import { readClipboardText, writeClipboardText } from '@/renderer/services/clipboardService' import { navigateToDefinition } from './hooks/useEditorActions' +import { safeOpenFile } from '@renderer/utils/fileUtils' // 支持 Call Hierarchy 的语言(只有支持函数/方法调用的语言才有意义) const CALL_HIERARCHY_SUPPORTED_LANGUAGES = [ @@ -69,7 +70,7 @@ interface EditorContextMenuProps { } export default function EditorContextMenu({ x, y, editor, onClose }: EditorContextMenuProps) { - const { language, activeFilePath, openFile, setActiveFile } = useStore(useShallow(s => ({ language: s.language, activeFilePath: s.activeFilePath, openFile: s.openFile, setActiveFile: s.setActiveFile }))) + const { language, activeFilePath } = useStore(useShallow(s => ({ language: s.language, activeFilePath: s.activeFilePath }))) const menuRef = useRef(null) const [callHierarchyResult, setCallHierarchyResult] = useState(null) const [loading, setLoading] = useState(false) @@ -297,10 +298,8 @@ export default function EditorContextMenu({ x, y, editor, onClose }: EditorConte const handleJumpToCall = async (item: CallHierarchyResult['items'][0]) => { const filePath = lspUriToPath(item.uri) if (filePath) { - const content = await api.file.readFull(filePath) - if (content === null) return - openFile(filePath, content) - setActiveFile(filePath) + const opened = await safeOpenFile(filePath, { language, confirmLargeFile: false }) + if (!opened.success || opened.isLargeFile) return // 延迟设置光标位置,等待编辑器加载 setTimeout(() => { editor.setPosition({ lineNumber: item.line + 1, column: item.character + 1 }) diff --git a/src/renderer/components/editor/EditorTabs.tsx b/src/renderer/components/editor/EditorTabs.tsx index 0e5ce22d..53c95d80 100644 --- a/src/renderer/components/editor/EditorTabs.tsx +++ b/src/renderer/components/editor/EditorTabs.tsx @@ -9,6 +9,7 @@ import { useAgentStore } from '@renderer/agent/store/AgentStore' import { t } from '@renderer/i18n' import { isPreviewDocumentPath } from '@shared/types/preview' import { isPlanBoardPath } from '@shared/types/planBoard' +import type { EditorDocumentKind } from '@shared/types/editorDocument' interface EditorTabsProps { activeFilePath: string | null @@ -19,7 +20,7 @@ interface EditorTabsProps { lintWarningCount: number isLinting: boolean onRunLint: () => void - activeFileKind?: 'file' | 'diff' | 'preview' + activeFileKind?: EditorDocumentKind } /** diff --git a/src/renderer/components/editor/LargeFileViewer.tsx b/src/renderer/components/editor/LargeFileViewer.tsx new file mode 100644 index 00000000..ba870e90 --- /dev/null +++ b/src/renderer/components/editor/LargeFileViewer.tsx @@ -0,0 +1,108 @@ +import { useEffect, useState } from 'react' +import { ChevronLeft, ChevronRight, Loader2, Lock } from 'lucide-react' +import type { OpenFile } from '@store/slices/fileSlice' +import type { TextFileChunk } from '@shared/types/fileChunk' +import { api } from '@renderer/services/electronAPI' + +interface LargeFileViewerProps { + file: OpenFile + language: 'zh' | 'en' +} + +function formatBytes(bytes: number): string { + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB` + return `${(bytes / 1024 / 1024).toFixed(1)} MB` +} + +export default function LargeFileViewer({ file, language }: LargeFileViewerProps) { + const metadata = file.largeFileView + const [chunk, setChunk] = useState(() => metadata ? { + content: file.content, + startOffset: metadata.startOffset, + nextOffset: metadata.nextOffset, + totalSize: metadata.totalSize, + eof: metadata.eof, + } : null) + const [history, setHistory] = useState([]) + const [loading, setLoading] = useState(false) + const [error, setError] = useState(false) + + useEffect(() => { + if (!metadata) return + setChunk({ + content: file.content, + startOffset: metadata.startOffset, + nextOffset: metadata.nextOffset, + totalSize: metadata.totalSize, + eof: metadata.eof, + }) + setHistory([]) + setError(false) + }, [file.path, file.contentLoadVersion, metadata]) + + if (!metadata || !chunk) return null + + const loadChunk = async (offset: number, previousHistory: number[]) => { + setLoading(true) + setError(false) + try { + const next = await api.file.readTextChunk(file.path, offset, metadata.chunkSize) + if (!next) { + setError(true) + return + } + setChunk(next) + setHistory(previousHistory) + } catch { + setError(true) + } finally { + setLoading(false) + } + } + + const pageEnd = Math.min(chunk.nextOffset, chunk.totalSize) + + return ( +
+
+ + {language === 'zh' ? '超大文件只读模式' : 'Very large file · read only'} + + {formatBytes(chunk.startOffset)}–{formatBytes(pageEnd)} / {formatBytes(chunk.totalSize)} + + + +
+ {error && ( +
+ {language === 'zh' ? '读取这一页失败,请重试。' : 'Could not read this page. Please retry.'} +
+ )} +