diff --git a/bun.lock b/bun.lock index 7e4ee14a..92876a58 100644 --- a/bun.lock +++ b/bun.lock @@ -55,6 +55,7 @@ "react": "^19.2.4", "react-reconciler": "^0.33.0", "semver": "^7.7.4", + "sharp": "0.34.5", "shell-quote": "^1.8.3", "signal-exit": "^4.1.0", "stack-utils": "^2.0.6", @@ -172,6 +173,8 @@ "@commander-js/extra-typings": ["@commander-js/extra-typings@14.0.0", "https://registry.npmmirror.com/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz", { "peerDependencies": { "commander": "~14.0.0" } }, "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg=="], + "@emnapi/runtime": ["@emnapi/runtime@1.11.3", "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.11.3.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + "@growthbook/growthbook": ["@growthbook/growthbook@1.6.5", "https://registry.npmmirror.com/@growthbook/growthbook/-/growthbook-1.6.5.tgz", { "dependencies": { "dom-mutator": "^0.6.0" } }, "sha512-mUaMsgeUTpRIUOTn33EUXHRK6j7pxBjwqH4WpQyq+pukjd1AIzWlEa6w7i6bInJUcweGgP2beXZmaP6b6UPn7A=="], "@hono/node-server": ["@hono/node-server@1.19.12", "https://registry.npmmirror.com/@hono/node-server/-/node-server-1.19.12.tgz", { "peerDependencies": { "hono": "^4" } }, "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw=="], @@ -180,6 +183,56 @@ "@iconify/utils": ["@iconify/utils@3.1.0", "https://registry.npmmirror.com/@iconify/utils/-/utils-3.1.0.tgz", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], + "@img/colour": ["@img/colour@1.1.0", "https://registry.npmmirror.com/@img/colour/-/colour-1.1.0.tgz", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "https://registry.npmmirror.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "https://registry.npmmirror.com/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "https://registry.npmmirror.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "https://registry.npmmirror.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "https://registry.npmmirror.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + "@mermaid-js/parser": ["@mermaid-js/parser@1.1.0", "https://registry.npmmirror.com/@mermaid-js/parser/-/parser-1.1.0.tgz", { "dependencies": { "langium": "^4.0.0" } }, "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw=="], "@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "https://registry.npmmirror.com/@mixmark-io/domino/-/domino-2.2.0.tgz", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="], @@ -546,6 +599,8 @@ "depd": ["depd@2.0.0", "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + "detect-libc": ["detect-libc@2.1.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "diff": ["diff@8.0.4", "https://registry.npmmirror.com/diff/-/diff-8.0.4.tgz", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="], "dijkstrajs": ["dijkstrajs@1.0.3", "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="], @@ -870,6 +925,8 @@ "setprototypeof": ["setprototypeof@1.2.0", "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + "sharp": ["sharp@0.34.5", "https://registry.npmmirror.com/sharp/-/sharp-0.34.5.tgz", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + "shebang-command": ["shebang-command@2.0.0", "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], diff --git a/desktop/bun.lock b/desktop/bun.lock index cb6223d8..a49e026c 100644 --- a/desktop/bun.lock +++ b/desktop/bun.lock @@ -32,6 +32,7 @@ "react-diff-viewer-continued": "^4.2.0", "react-dom": "^18.3.1", "react-shiki": "^0.9.2", + "sharp": "0.34.5", "shiki": "^4.0.2", "zustand": "^5.0.3", }, @@ -257,6 +258,56 @@ "@iconify/utils": ["@iconify/utils@3.1.0", "https://registry.npmmirror.com/@iconify/utils/-/utils-3.1.0.tgz", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], + "@img/colour": ["@img/colour@1.1.0", "https://registry.npmmirror.com/@img/colour/-/colour-1.1.0.tgz", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "https://registry.npmmirror.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "https://registry.npmmirror.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "https://registry.npmmirror.com/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "https://registry.npmmirror.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "https://registry.npmmirror.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "https://registry.npmmirror.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "https://registry.npmmirror.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], @@ -1479,6 +1530,8 @@ "set-blocking": ["set-blocking@2.0.0", "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="], + "sharp": ["sharp@0.34.5", "https://registry.npmmirror.com/sharp/-/sharp-0.34.5.tgz", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + "shebang-command": ["shebang-command@2.0.0", "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], diff --git a/desktop/package.json b/desktop/package.json index 7270cfa1..261b1864 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -17,6 +17,8 @@ "asarUnpack": [ "dist/**", "node_modules/node-pty/**", + "node_modules/sharp/**", + "node_modules/@img/**", "src-tauri/binaries/**" ], "artifactName": "Claude-Code-Haha-${version}-${os}-${arch}.${ext}", @@ -131,6 +133,7 @@ "react-diff-viewer-continued": "^4.2.0", "react-dom": "^18.3.1", "react-shiki": "^0.9.2", + "sharp": "0.34.5", "shiki": "^4.0.2", "zustand": "^5.0.3" }, diff --git a/desktop/scripts/build-sidecars.ts b/desktop/scripts/build-sidecars.ts index e90b64c8..849f9e89 100644 --- a/desktop/scripts/build-sidecars.ts +++ b/desktop/scripts/build-sidecars.ts @@ -206,10 +206,9 @@ async function compileExecutable({ minify: { whitespace: true, identifiers: true, syntax: true }, sourcemap: 'none', target: 'bun', - // 可选 npm 包:开 telemetry / 用 sharp 图像 / 用 Bedrock/Vertex 等 - // 替代 provider 时才需要,全部不在顶层 package.json 里。标 external - // 让 bun build 跳过解析;运行时 import 在没装时自然失败,由 try/catch - // 或 feature() gate 兜底。 + // 运行时可选 npm 包保持 external,避免将原生模块嵌入 sidecar。 + // sharp 由 desktop/package.json 声明并随 Electron 应用分发;其他 + // provider/telemetry 模块在未安装时由 try/catch 或 feature() gate 兜底。 external: [ // OpenTelemetry exporters(开 OTEL_* env 时才加载) '@opentelemetry/exporter-trace-otlp-grpc', diff --git a/desktop/src/__tests__/generalSettings.test.tsx b/desktop/src/__tests__/generalSettings.test.tsx index 7ec0013e..38265339 100644 --- a/desktop/src/__tests__/generalSettings.test.tsx +++ b/desktop/src/__tests__/generalSettings.test.tsx @@ -2051,6 +2051,62 @@ describe('Settings > Providers tab', () => { expect(within(dialog).getByText('Requests will be translated via the local proxy')).toBeInTheDocument() }) + it('uses the proxy in settings JSON and connection tests when nested tool media is unsupported', async () => { + providerStoreState.testConfig = vi.fn().mockResolvedValue({ + connectivity: { success: true, latencyMs: 1 }, + proxy: { success: true, latencyMs: 1 }, + }) + providerStoreState.presets = [{ + id: 'custom', + name: 'Custom', + baseUrl: 'https://api.example.com/anthropic', + apiFormat: 'anthropic', + defaultModels: { + main: 'model-main', + haiku: '', + sonnet: '', + opus: '', + }, + needsApiKey: true, + websiteUrl: '', + }] + + render() + fireEvent.click(screen.getByRole('button', { name: /Add Provider/i })) + + const dialog = screen.getByRole('dialog') + const mediaSupport = within(dialog).getByLabelText('Preserve nested tool result media') + expect(mediaSupport).toBeChecked() + fireEvent.click(mediaSupport) + + const settingsTextarea = await waitFor(() => { + const textarea = dialog.querySelector('textarea') as HTMLTextAreaElement + const settings = JSON.parse(textarea.value) as { + env?: { + ANTHROPIC_API_KEY?: string + ANTHROPIC_AUTH_TOKEN?: string + ANTHROPIC_BASE_URL?: string + } + } + expect(settings.env?.ANTHROPIC_BASE_URL).toMatch(/\/proxy$/) + expect(settings.env?.ANTHROPIC_API_KEY).toBe('proxy-managed') + expect(settings.env?.ANTHROPIC_AUTH_TOKEN).toBeUndefined() + return textarea + }) + expect(settingsTextarea.value).not.toContain('"ANTHROPIC_BASE_URL": "https://api.example.com/anthropic"') + + fireEvent.change(within(dialog).getByPlaceholderText('sk-...'), { target: { value: 'sk-test' } }) + fireEvent.click(within(dialog).getByRole('button', { name: /Test Connection/i })) + + await waitFor(() => { + expect(providerStoreState.testConfig).toHaveBeenCalledWith(expect.objectContaining({ + baseUrl: 'https://api.example.com/anthropic', + apiFormat: 'anthropic', + supportsNestedToolResultMedia: false, + })) + }) + }) + it('localizes the main model placeholder in the provider form', () => { useSettingsStore.setState({ locale: 'zh' }) providerStoreState.presets = [ diff --git a/desktop/src/i18n/locales/en.ts b/desktop/src/i18n/locales/en.ts index e2faadee..7630d7de 100644 --- a/desktop/src/i18n/locales/en.ts +++ b/desktop/src/i18n/locales/en.ts @@ -797,6 +797,9 @@ Row 9, all 8 cells: continuing from straight down, turning left through lower-le 'settings.providers.toolSearchConfirmEnable': 'Enable anyway', 'settings.providers.disableExperimentalBetas': 'Disable experimental beta headers', 'settings.providers.disableExperimentalBetasDesc': 'Sets CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 to avoid beta API shapes that third-party gateways may reject. GPT and o-series models still receive the reasoning effort selected for the Session; other direct Anthropic-compatible models may fall back to the gateway default.', + 'settings.providers.supportsNestedToolResultMedia': 'Preserve nested tool result media', + 'settings.providers.nestedToolResultMediaDesc': 'Keeps images and files inside tool results when the Anthropic-compatible endpoint supports nested media. Turn this off for third-party endpoints that cannot display nested media; images and files are then lifted into standalone content blocks.', + 'settings.providers.nestedToolResultMediaUnsupported': 'Only Anthropic Messages providers can configure nested tool result media.', 'settings.providers.imageGenerationEnabled': 'Enable image generation', 'settings.providers.imageGenerationEnabledDesc': 'Let chat generate images through an OpenAI-compatible Images API. Credentials stay in provider settings, not in the skill.', 'settings.providers.imageGenerationModel': 'Image model', diff --git a/desktop/src/i18n/locales/jp.ts b/desktop/src/i18n/locales/jp.ts index 07a8d0cf..008d0e6b 100644 --- a/desktop/src/i18n/locales/jp.ts +++ b/desktop/src/i18n/locales/jp.ts @@ -799,6 +799,9 @@ export const jp: Record = { 'settings.providers.toolSearchConfirmEnable': '有効にする', 'settings.providers.disableExperimentalBetas': '実験的な Beta ヘッダーを無効化', 'settings.providers.disableExperimentalBetasDesc': 'このプロバイダーに CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 を設定し、サードパーティゲートウェイが拒否する可能性のある beta API 形式を避けます。GPT および o シリーズのモデルには Session で選択した推論強度が引き続き転送されます。その他の Anthropic 互換モデルへの直接接続では、ゲートウェイの既定値に戻る場合があります。', + 'settings.providers.supportsNestedToolResultMedia': 'ツール結果のメディアを保持', + 'settings.providers.nestedToolResultMediaDesc': 'Anthropic 互換エンドポイントがネストされたメディアに対応している場合、画像とファイルを tool result 内に保持します。ネストされたメディアを表示できないサードパーティエンドポイントでは、このオプションをオフにすると画像とファイルが独立したコンテンツブロックに引き上げられます。', + 'settings.providers.nestedToolResultMediaUnsupported': 'ツール結果のネストされたメディアを設定できるのは Anthropic Messages プロバイダーのみです。', 'settings.providers.imageGenerationEnabled': '画像生成を有効にする', 'settings.providers.imageGenerationEnabledDesc': 'OpenAI 互換 Images API を通じてチャットから画像を生成します。認証情報は Skill ではなくプロバイダー設定に保存されます。', 'settings.providers.imageGenerationModel': '画像モデル', diff --git a/desktop/src/i18n/locales/kr.ts b/desktop/src/i18n/locales/kr.ts index e64510a0..5e751195 100644 --- a/desktop/src/i18n/locales/kr.ts +++ b/desktop/src/i18n/locales/kr.ts @@ -799,6 +799,9 @@ export const kr: Record = { 'settings.providers.toolSearchConfirmEnable': '계속 사용', 'settings.providers.disableExperimentalBetas': '실험적 Beta 헤더 비활성화', 'settings.providers.disableExperimentalBetasDesc': '이 공급자에 CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1을 설정해 타사 게이트웨이가 거부할 수 있는 beta API 형식을 피합니다. GPT 및 o 시리즈 모델에는 Session에서 선택한 추론 강도가 계속 전달됩니다. 다른 Anthropic 호환 모델에 직접 연결할 때는 게이트웨이 기본값으로 돌아갈 수 있습니다.', + 'settings.providers.supportsNestedToolResultMedia': '도구 결과의 미디어 보존', + 'settings.providers.nestedToolResultMediaDesc': 'Anthropic 호환 엔드포인트가 중첩 미디어를 지원하면 이미지와 파일을 tool result 안에 유지합니다. 중첩 미디어를 표시할 수 없는 타사 엔드포인트에서는 이 옵션을 끄면 이미지와 파일이 독립 콘텐츠 블록으로 올라갑니다.', + 'settings.providers.nestedToolResultMediaUnsupported': 'Anthropic Messages 공급자만 도구 결과의 중첩 미디어를 설정할 수 있습니다.', 'settings.providers.imageGenerationEnabled': '이미지 생성 사용', 'settings.providers.imageGenerationEnabledDesc': 'OpenAI 호환 Images API를 통해 채팅에서 이미지를 생성합니다. 인증 정보는 Skill이 아니라 공급자 설정에 저장됩니다.', 'settings.providers.imageGenerationModel': '이미지 모델', diff --git a/desktop/src/i18n/locales/zh-TW.ts b/desktop/src/i18n/locales/zh-TW.ts index 767fcf2c..67b1fd27 100644 --- a/desktop/src/i18n/locales/zh-TW.ts +++ b/desktop/src/i18n/locales/zh-TW.ts @@ -798,6 +798,9 @@ export const zh: Record = { 'settings.providers.toolSearchConfirmEnable': '仍然啟用', 'settings.providers.disableExperimentalBetas': '關閉實驗性 Beta 標頭', 'settings.providers.disableExperimentalBetasDesc': '為此服務商設定 CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1,避免第三方通道拒絕 beta API 形態。GPT 與 o 系列模型仍會轉送 Session 中選擇的推理強度;其他直連 Anthropic 相容模型可能退回通道預設值。', + 'settings.providers.supportsNestedToolResultMedia': '保留工具結果中的媒體', + 'settings.providers.nestedToolResultMediaDesc': '當 Anthropic 相容端點支援巢狀媒體時,將圖片和檔案保留在 tool result 內。若第三方端點無法顯示巢狀媒體,請關閉此選項,圖片和檔案將提升為獨立的內容區塊。', + 'settings.providers.nestedToolResultMediaUnsupported': '只有 Anthropic Messages 服務商可以設定工具結果中的巢狀媒體。', 'settings.providers.imageGenerationEnabled': '啟用圖片生成', 'settings.providers.imageGenerationEnabledDesc': '允許聊天透過 OpenAI 相容的 Images API 生成圖片。憑證保存在服務商設定中,不寫入 Skill。', 'settings.providers.imageGenerationModel': '圖片模型', diff --git a/desktop/src/i18n/locales/zh.ts b/desktop/src/i18n/locales/zh.ts index 5b61a306..3b3e8ecb 100644 --- a/desktop/src/i18n/locales/zh.ts +++ b/desktop/src/i18n/locales/zh.ts @@ -798,6 +798,9 @@ export const zh: Record = { 'settings.providers.toolSearchConfirmEnable': '仍然启用', 'settings.providers.disableExperimentalBetas': '关闭实验性 Beta 头', 'settings.providers.disableExperimentalBetasDesc': '为此服务商设置 CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1,避免第三方通道拒绝 beta API 形态。GPT 和 o 系列模型仍会转发 Session 中选择的推理强度;其他直连 Anthropic 兼容模型可能回退为通道默认值。', + 'settings.providers.supportsNestedToolResultMedia': '保留工具结果中的媒体', + 'settings.providers.nestedToolResultMediaDesc': '当 Anthropic 兼容端点支持嵌套媒体时,将图片和文件保留在 tool result 内。若第三方端点无法显示嵌套媒体,请关闭此选项,图片和文件将提升为独立的内容块。', + 'settings.providers.nestedToolResultMediaUnsupported': '仅 Anthropic Messages 服务商可以配置工具结果中的嵌套媒体。', 'settings.providers.imageGenerationEnabled': '启用图片生成', 'settings.providers.imageGenerationEnabledDesc': '允许聊天通过 OpenAI 兼容的 Images API 生图。凭证保存在服务商设置中,不写进 Skill。', 'settings.providers.imageGenerationModel': '生图模型', diff --git a/desktop/src/pages/settings/ProviderSettings.tsx b/desktop/src/pages/settings/ProviderSettings.tsx index 57cc7cbc..22d99cbf 100644 --- a/desktop/src/pages/settings/ProviderSettings.tsx +++ b/desktop/src/pages/settings/ProviderSettings.tsx @@ -571,12 +571,12 @@ function getProviderAuthValue(apiKey: string, preset: ProviderPreset): string { } function buildSettingsJsonAuthEnv( - apiFormat: ApiFormat, + needsProxy: boolean, authStrategy: ProviderAuthStrategy, apiKey: string, preset: ProviderPreset, ): Record { - if (apiFormat !== 'anthropic') { + if (needsProxy) { return { ANTHROPIC_API_KEY: 'proxy-managed' } } @@ -903,6 +903,13 @@ function updateSettingsJsonModels( } } +function providerNeedsProxy( + apiFormat: ApiFormat, + supportsNestedToolResultMedia: boolean, +): boolean { + return apiFormat !== 'anthropic' || !supportsNestedToolResultMedia +} + function updateSettingsJsonProviderConnection( raw: string, apiFormat: ApiFormat, @@ -913,6 +920,7 @@ function updateSettingsJsonProviderConnection( proxyBaseUrl: string, toolSearchEnabled = false, disableExperimentalBetas = false, + supportsNestedToolResultMedia = true, ): string { try { const parsed = JSON.parse(raw || '{}') as { env?: Record } @@ -924,8 +932,13 @@ function updateSettingsJsonProviderConnection( delete env.ANTHROPIC_AUTH_TOKEN applyToolSearchEnv(env, apiFormat, toolSearchEnabled) applyDisableExperimentalBetasEnv(env, disableExperimentalBetas) - env.ANTHROPIC_BASE_URL = apiFormat !== 'anthropic' ? proxyBaseUrl : baseUrl - Object.assign(env, buildSettingsJsonAuthEnv(apiFormat, authStrategy, apiKey, preset)) + env.ANTHROPIC_BASE_URL = providerNeedsProxy(apiFormat, supportsNestedToolResultMedia) ? proxyBaseUrl : baseUrl + Object.assign(env, buildSettingsJsonAuthEnv( + providerNeedsProxy(apiFormat, supportsNestedToolResultMedia), + authStrategy, + apiKey, + preset, + )) parsed.env = env return JSON.stringify(parsed, null, 2) } catch { @@ -1011,6 +1024,7 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF const [toolSearchEnabled, setToolSearchEnabled] = useState(provider?.toolSearchEnabled ?? false) const [toolSearchConfirmOpen, setToolSearchConfirmOpen] = useState(false) const [disableExperimentalBetas, setDisableExperimentalBetas] = useState(provider?.disableExperimentalBetas ?? false) + const [supportsNestedToolResultMedia, setSupportsNestedToolResultMedia] = useState(provider?.supportsNestedToolResultMedia ?? true) const [imageGeneration, setImageGeneration] = useState({ enabled: Boolean(provider?.imageGeneration), model: provider?.imageGeneration?.model ?? '', @@ -1043,6 +1057,7 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF autoCompactWindow, toolSearchEnabled, disableExperimentalBetas, + supportsNestedToolResultMedia, } const providerSettingsRef = useRef(currentProviderSettings) providerSettingsRef.current = currentProviderSettings @@ -1071,8 +1086,9 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF autoCompactWindow, toolSearchEnabled, disableExperimentalBetas, + supportsNestedToolResultMedia, } = providerSettingsRef.current - const needsProxy = apiFormat !== 'anthropic' + const needsProxy = providerNeedsProxy(apiFormat, supportsNestedToolResultMedia) const autoCompactWindowEnv = autoCompactWindow.trim() const modelContextWindows = buildModelContextWindows(models, modelContextInputs) const normalizedModels = normalizeModelMapping(models) @@ -1087,7 +1103,7 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF ? { [MODEL_CONTEXT_WINDOWS_ENV_KEY]: JSON.stringify(modelContextWindows) } : {}), ANTHROPIC_BASE_URL: needsProxy ? providerProxyBaseUrl : baseUrl, - ...buildSettingsJsonAuthEnv(apiFormat, authStrategy, apiKey, selectedPreset), + ...buildSettingsJsonAuthEnv(needsProxy, authStrategy, apiKey, selectedPreset), ANTHROPIC_MODEL: runtimeModels.main, ...(runtimeModels.fable ? { ANTHROPIC_DEFAULT_FABLE_MODEL: runtimeModels.fable } : {}), ANTHROPIC_DEFAULT_HAIKU_MODEL: runtimeModels.haiku, @@ -1148,6 +1164,7 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF setToolSearchEnabled(false) setToolSearchConfirmOpen(false) setDisableExperimentalBetas(false) + setSupportsNestedToolResultMedia(true) setShowContextSettings(false) setTestResult(null) } @@ -1238,6 +1255,10 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF ] satisfies Array<{ value: ProviderAuthStrategy; label: string; description: string; icon: ReactNode }> const selectedAuthStrategyLabel = authStrategyItems.find((item) => item.value === authStrategy)?.label ?? t('settings.providers.authStrategyAuthToken') const toolSearchUnsupported = apiFormat !== 'anthropic' + const nestedToolResultMediaUnsupported = apiFormat !== 'anthropic' + const nestedToolResultMediaDescription = nestedToolResultMediaUnsupported + ? t('settings.providers.nestedToolResultMediaUnsupported') + : t('settings.providers.nestedToolResultMediaDesc') const toolSearchDescription = toolSearchUnsupported ? t('settings.providers.toolSearchUnsupported') : t('settings.providers.toolSearchDesc') @@ -1261,20 +1282,37 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF } const handleBaseUrlChange = (value: string) => { setBaseUrl(value) - setSettingsJson((current) => updateSettingsJsonProviderConnection(current, apiFormat, authStrategy, apiKey, selectedPreset, value, providerProxyBaseUrl, toolSearchEnabled, disableExperimentalBetas)) + setSettingsJson((current) => updateSettingsJsonProviderConnection(current, apiFormat, authStrategy, apiKey, selectedPreset, value, providerProxyBaseUrl, toolSearchEnabled, disableExperimentalBetas, supportsNestedToolResultMedia)) } const handleApiKeyChange = (value: string) => { setApiKey(value) - setSettingsJson((current) => updateSettingsJsonProviderConnection(current, apiFormat, authStrategy, value, selectedPreset, baseUrl, providerProxyBaseUrl, toolSearchEnabled, disableExperimentalBetas)) + setSettingsJson((current) => updateSettingsJsonProviderConnection(current, apiFormat, authStrategy, value, selectedPreset, baseUrl, providerProxyBaseUrl, toolSearchEnabled, disableExperimentalBetas, supportsNestedToolResultMedia)) } const handleApiFormatChange = (value: ApiFormat) => { setApiFormat(value) - setSettingsJson((current) => updateSettingsJsonProviderConnection(current, value, authStrategy, apiKey, selectedPreset, baseUrl, providerProxyBaseUrl, toolSearchEnabled, disableExperimentalBetas)) + setSettingsJson((current) => updateSettingsJsonProviderConnection(current, value, authStrategy, apiKey, selectedPreset, baseUrl, providerProxyBaseUrl, toolSearchEnabled, disableExperimentalBetas, supportsNestedToolResultMedia)) } const handleAuthStrategyChange = (value: ProviderAuthStrategy) => { setAuthStrategy(value) - setSettingsJson((current) => updateSettingsJsonProviderConnection(current, apiFormat, value, apiKey, selectedPreset, baseUrl, providerProxyBaseUrl, toolSearchEnabled, disableExperimentalBetas)) + setSettingsJson((current) => updateSettingsJsonProviderConnection(current, apiFormat, value, apiKey, selectedPreset, baseUrl, providerProxyBaseUrl, toolSearchEnabled, disableExperimentalBetas, supportsNestedToolResultMedia)) } + const handleNestedToolResultMediaToggle = (enabled: boolean) => { + if (nestedToolResultMediaUnsupported) return + setSupportsNestedToolResultMedia(enabled) + setSettingsJson((current) => updateSettingsJsonProviderConnection( + current, + apiFormat, + authStrategy, + apiKey, + selectedPreset, + baseUrl, + providerProxyBaseUrl, + toolSearchEnabled, + disableExperimentalBetas, + enabled, + )) + } + const handleToolSearchToggle = (enabled: boolean) => { if (toolSearchUnsupported) return if (enabled) { @@ -1293,6 +1331,7 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF setDisableExperimentalBetas(disabled) setSettingsJson((current) => updateSettingsJsonDisableExperimentalBetas(current, disabled)) } + const handleModelChange = (slot: ModelSlot, value: string) => { const hasMarker = hasModel1mMarker(value) const nextModels = { ...models, [slot]: stripModel1mMarker(value) } @@ -1457,6 +1496,7 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF ...(Object.keys(parsedModelContextWindows).length > 0 && { modelContextWindows: parsedModelContextWindows }), toolSearchEnabled, ...(disableExperimentalBetas && { disableExperimentalBetas }), + supportsNestedToolResultMedia, ...(storedImageGeneration !== undefined && { imageGeneration: storedImageGeneration }), notes: notes.trim() || undefined, }) @@ -1474,6 +1514,7 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF : null, toolSearchEnabled, disableExperimentalBetas, + supportsNestedToolResultMedia, imageGeneration: storedImageGeneration ?? null, notes: notes.trim() || undefined, } @@ -1503,7 +1544,8 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF const savedConfigUnchanged = mode === 'edit' && provider && !apiKey.trim() && baseUrl.trim() === provider.baseUrl.trim() && apiFormat === provider.apiFormat && - authStrategy === provider.authStrategy + authStrategy === provider.authStrategy && + supportsNestedToolResultMedia === (provider.supportsNestedToolResultMedia ?? true) if (savedConfigUnchanged && provider) { result = await useProviderStore.getState().testProvider(provider.id, { modelId: models.main.trim(), @@ -1516,6 +1558,7 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF modelId: models.main.trim(), authStrategy, apiFormat, + supportsNestedToolResultMedia, }) } setTestResult(result) @@ -1698,6 +1741,26 @@ function ProviderFormModal({ open, onClose, mode, provider, presets }: ProviderF + +