From 10d253f46105ee4026cedbba97c2431540fc6a4f Mon Sep 17 00:00:00 2001 From: Henry Li Date: Tue, 20 Jan 2026 23:43:21 +0800 Subject: [PATCH] feat: support settings --- frontend/package.json | 3 +- frontend/pnpm-lock.yaml | 425 ++++++++++-------- frontend/src/components/ui/empty.tsx | 104 +++++ frontend/src/components/ui/item.tsx | 193 ++++++++ frontend/src/components/ui/switch.tsx | 31 ++ .../workspace/settings/acknowledge-page.tsx | 5 + .../settings/appearance-settings-page.tsx | 193 ++++++++ .../components/workspace/settings/index.ts | 1 + .../workspace/settings/settings-dialog.tsx | 100 +++++ .../workspace/settings/settings-section.tsx | 25 ++ .../settings/skill-settings-page.tsx | 91 ++++ .../workspace/settings/tool-settings-page.tsx | 68 +++ .../workspace/workspace-sidebar.tsx | 64 +-- frontend/src/core/i18n/locales/en-US.ts | 42 ++ frontend/src/core/i18n/locales/types.ts | 41 ++ frontend/src/core/i18n/locales/zh-CN.ts | 41 ++ frontend/src/core/mcp/api.ts | 24 + frontend/src/core/mcp/hooks.ts | 44 ++ frontend/src/core/mcp/index.ts | 2 + frontend/src/core/mcp/types.ts | 8 + frontend/src/core/skills/api.ts | 25 ++ frontend/src/core/skills/hooks.ts | 31 ++ frontend/src/core/skills/index.ts | 2 + frontend/src/core/skills/type.ts | 7 + frontend/tsconfig.json | 2 +- 25 files changed, 1355 insertions(+), 217 deletions(-) create mode 100644 frontend/src/components/ui/empty.tsx create mode 100644 frontend/src/components/ui/item.tsx create mode 100644 frontend/src/components/ui/switch.tsx create mode 100644 frontend/src/components/workspace/settings/acknowledge-page.tsx create mode 100644 frontend/src/components/workspace/settings/appearance-settings-page.tsx create mode 100644 frontend/src/components/workspace/settings/settings-section.tsx create mode 100644 frontend/src/components/workspace/settings/skill-settings-page.tsx create mode 100644 frontend/src/components/workspace/settings/tool-settings-page.tsx create mode 100644 frontend/src/core/mcp/api.ts create mode 100644 frontend/src/core/mcp/hooks.ts create mode 100644 frontend/src/core/mcp/index.ts create mode 100644 frontend/src/core/mcp/types.ts create mode 100644 frontend/src/core/skills/api.ts create mode 100644 frontend/src/core/skills/hooks.ts create mode 100644 frontend/src/core/skills/index.ts create mode 100644 frontend/src/core/skills/type.ts diff --git a/frontend/package.json b/frontend/package.json index 8eb5b84..bf69998 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,6 +27,7 @@ "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-toggle": "^1.1.10", "@radix-ui/react-toggle-group": "^1.1.11", "@radix-ui/react-tooltip": "^1.2.8", @@ -47,7 +48,7 @@ "lucide-react": "^0.562.0", "motion": "^12.26.2", "nanoid": "^5.1.6", - "next": "^15.2.8", + "next": "^16.1.4", "next-themes": "^0.4.6", "nuxt-og-image": "^5.1.13", "react": "^19.0.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 5b582ee..305f97b 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: '@radix-ui/react-slot': specifier: ^1.2.4 version: 1.2.4(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-switch': + specifier: ^1.2.6 + version: 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-toggle': specifier: ^1.1.10 version: 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -73,7 +76,7 @@ importers: version: 1.2.1 better-auth: specifier: ^1.3 - version: 1.4.12(next@15.2.8(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.26(typescript@5.9.3)) + version: 1.4.12(next@16.1.4(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.26(typescript@5.9.3)) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -102,8 +105,8 @@ importers: specifier: ^5.1.6 version: 5.1.6 next: - specifier: ^15.2.8 - version: 15.2.8(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: ^16.1.4 + version: 16.1.4(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -514,119 +517,155 @@ packages: '@iconify/utils@3.1.0': resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} - '@img/sharp-darwin-arm64@0.33.5': - resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.33.5': - resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.0.4': - resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.0.4': - resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.0.4': - resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.0.5': - resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.0.4': - resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.0.4': - resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': - resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.0.4': - resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-linux-arm64@0.33.5': - resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.33.5': - resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.33.5': - resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.33.5': - resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-linuxmusl-arm64@0.33.5': - resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.33.5': - resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-wasm32@0.33.5': - resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-ia32@0.33.5': - resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.33.5': - resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] @@ -671,60 +710,60 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@next/env@15.2.8': - resolution: {integrity: sha512-TaEsAki14R7BlgywA05t2PFYfwZiNlGUHyIQHVyloXX3y+Dm0HUITe5YwTkjtuOQuDhuuLotNEad4VtnmE11Uw==} + '@next/env@16.1.4': + resolution: {integrity: sha512-gkrXnZyxPUy0Gg6SrPQPccbNVLSP3vmW8LU5dwEttEEC1RwDivk8w4O+sZIjFvPrSICXyhQDCG+y3VmjlJf+9A==} '@next/eslint-plugin-next@15.5.9': resolution: {integrity: sha512-kUzXx0iFiXw27cQAViE1yKWnz/nF8JzRmwgMRTMh8qMY90crNsdXJRh2e+R0vBpFR3kk1yvAR7wev7+fCCb79Q==} - '@next/swc-darwin-arm64@15.2.5': - resolution: {integrity: sha512-4OimvVlFTbgzPdA0kh8A1ih6FN9pQkL4nPXGqemEYgk+e7eQhsst/p35siNNqA49eQA6bvKZ1ASsDtu9gtXuog==} + '@next/swc-darwin-arm64@16.1.4': + resolution: {integrity: sha512-T8atLKuvk13XQUdVLCv1ZzMPgLPW0+DWWbHSQXs0/3TjPrKNxTmUIhOEaoEyl3Z82k8h/gEtqyuoZGv6+Ugawg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.2.5': - resolution: {integrity: sha512-ohzRaE9YbGt1ctE0um+UGYIDkkOxHV44kEcHzLqQigoRLaiMtZzGrA11AJh2Lu0lv51XeiY1ZkUvkThjkVNBMA==} + '@next/swc-darwin-x64@16.1.4': + resolution: {integrity: sha512-AKC/qVjUGUQDSPI6gESTx0xOnOPQ5gttogNS3o6bA83yiaSZJek0Am5yXy82F1KcZCx3DdOwdGPZpQCluonuxg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.2.5': - resolution: {integrity: sha512-FMSdxSUt5bVXqqOoZCc/Seg4LQep9w/fXTazr/EkpXW2Eu4IFI9FD7zBDlID8TJIybmvKk7mhd9s+2XWxz4flA==} + '@next/swc-linux-arm64-gnu@16.1.4': + resolution: {integrity: sha512-POQ65+pnYOkZNdngWfMEt7r53bzWiKkVNbjpmCt1Zb3V6lxJNXSsjwRuTQ8P/kguxDC8LRkqaL3vvsFrce4dMQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@15.2.5': - resolution: {integrity: sha512-4ZNKmuEiW5hRKkGp2HWwZ+JrvK4DQLgf8YDaqtZyn7NYdl0cHfatvlnLFSWUayx9yFAUagIgRGRk8pFxS8Qniw==} + '@next/swc-linux-arm64-musl@16.1.4': + resolution: {integrity: sha512-3Wm0zGYVCs6qDFAiSSDL+Z+r46EdtCv/2l+UlIdMbAq9hPJBvGu/rZOeuvCaIUjbArkmXac8HnTyQPJFzFWA0Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@15.2.5': - resolution: {integrity: sha512-bE6lHQ9GXIf3gCDE53u2pTl99RPZW5V1GLHSRMJ5l/oB/MT+cohu9uwnCK7QUph2xIOu2a6+27kL0REa/kqwZw==} + '@next/swc-linux-x64-gnu@16.1.4': + resolution: {integrity: sha512-lWAYAezFinaJiD5Gv8HDidtsZdT3CDaCeqoPoJjeB57OqzvMajpIhlZFce5sCAH6VuX4mdkxCRqecCJFwfm2nQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@15.2.5': - resolution: {integrity: sha512-y7EeQuSkQbTAkCEQnJXm1asRUuGSWAchGJ3c+Qtxh8LVjXleZast8Mn/rL7tZOm7o35QeIpIcid6ufG7EVTTcA==} + '@next/swc-linux-x64-musl@16.1.4': + resolution: {integrity: sha512-fHaIpT7x4gA6VQbdEpYUXRGyge/YbRrkG6DXM60XiBqDM2g2NcrsQaIuj375egnGFkJow4RHacgBOEsHfGbiUw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@15.2.5': - resolution: {integrity: sha512-gQMz0yA8/dskZM2Xyiq2FRShxSrsJNha40Ob/M2n2+JGRrZ0JwTVjLdvtN6vCxuq4ByhOd4a9qEf60hApNR2gQ==} + '@next/swc-win32-arm64-msvc@16.1.4': + resolution: {integrity: sha512-MCrXxrTSE7jPN1NyXJr39E+aNFBrQZtO154LoCz7n99FuKqJDekgxipoodLNWdQP7/DZ5tKMc/efybx1l159hw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.2.5': - resolution: {integrity: sha512-tBDNVUcI7U03+3oMvJ11zrtVin5p0NctiuKmTGyaTIEAVj9Q77xukLXGXRnWxKRIIdFG4OTA2rUVGZDYOwgmAA==} + '@next/swc-win32-x64-msvc@16.1.4': + resolution: {integrity: sha512-JSVlm9MDhmTXw/sO2PE/MRj+G6XOSMZB+BcZ0a7d6KwVFZVpkHcb2okyoYFBaco6LeiL53BBklRlOrDDbOeE5w==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1094,6 +1133,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-switch@1.2.6': + resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-toggle-group@1.1.11': resolution: {integrity: sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==} peerDependencies: @@ -1485,9 +1537,6 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@swc/counter@0.1.3': - resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -2114,6 +2163,10 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.9.16: + resolution: {integrity: sha512-KeUZdBuxngy825i8xvzaK1Ncnkx0tBmb3k8DkEuqjKRkmtvNTjey2ZsNeh8Dw4lfKvbCOu9oeNx2TKm2vHqcRw==} + hasBin: true + best-effort-json-parser@1.2.1: resolution: {integrity: sha512-UICSLibQdzS1f+PBsi3u2YE3SsdXcWicHUg3IMvfuaePS2AYnZJdJeKhGv5OM8/mqJwPt79aDrEJ1oa84tELvw==} @@ -2194,10 +2247,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} - c12@3.3.3: resolution: {integrity: sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q==} peerDependencies: @@ -2297,13 +2346,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - - color@4.2.3: - resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} - engines: {node: '>=12.5.0'} - comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -3140,9 +3182,6 @@ packages: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} - is-arrayish@0.3.4: - resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} - is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} @@ -3746,13 +3785,13 @@ packages: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - next@15.2.8: - resolution: {integrity: sha512-pe2trLKZTdaCuvNER0S9Wp+SP2APf7SfFmyUP9/w1SFA2UqmW0u+IsxCKkiky3n6um7mryaQIlgiDnKrf1ZwIw==} - engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + next@16.1.4: + resolution: {integrity: sha512-gKSecROqisnV7Buen5BfjmXAm7Xlpx9o2ueVQRo5DxQcjC8d330dOM1xiGWc2k3Dcnz0In3VybyRPOsudwgiqQ==} + engines: {node: '>=20.9.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.41.2 + '@playwright/test': ^1.51.1 babel-plugin-react-compiler: '*' react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 @@ -4265,8 +4304,8 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} - sharp@0.33.5: - resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -4300,9 +4339,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - simple-swizzle@0.2.4: - resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} - simple-wcswidth@1.1.2: resolution: {integrity: sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==} @@ -4343,10 +4379,6 @@ packages: peerDependencies: react: ^18.0.0 || ^19.0.0 - streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - string.prototype.codepointat@0.2.1: resolution: {integrity: sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==} @@ -5095,79 +5127,101 @@ snapshots: '@iconify/types': 2.0.0 mlly: 1.8.0 - '@img/sharp-darwin-arm64@0.33.5': + '@img/colour@1.0.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true - '@img/sharp-darwin-x64@0.33.5': + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.0.4': + '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true - '@img/sharp-libvips-darwin-x64@1.0.4': + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true - '@img/sharp-libvips-linux-arm64@1.0.4': + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true - '@img/sharp-libvips-linux-arm@1.0.5': + '@img/sharp-libvips-linux-arm@1.2.4': optional: true - '@img/sharp-libvips-linux-s390x@1.0.4': + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true - '@img/sharp-libvips-linux-x64@1.0.4': + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.0.4': + '@img/sharp-libvips-linux-x64@1.2.4': optional: true - '@img/sharp-linux-arm64@0.33.5': + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true - '@img/sharp-linux-arm@0.33.5': + '@img/sharp-linux-arm@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm': 1.2.4 optional: true - '@img/sharp-linux-s390x@0.33.5': + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true - '@img/sharp-linux-x64@0.33.5': + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true - '@img/sharp-linuxmusl-arm64@0.33.5': + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true - '@img/sharp-linuxmusl-x64@0.33.5': + '@img/sharp-linux-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.2.4 optional: true - '@img/sharp-wasm32@0.33.5': + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': dependencies: '@emnapi/runtime': 1.8.1 optional: true - '@img/sharp-win32-ia32@0.33.5': + '@img/sharp-win32-arm64@0.34.5': optional: true - '@img/sharp-win32-x64@0.33.5': + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': optional: true '@jridgewell/gen-mapping@0.3.13': @@ -5228,34 +5282,34 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@next/env@15.2.8': {} + '@next/env@16.1.4': {} '@next/eslint-plugin-next@15.5.9': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@15.2.5': + '@next/swc-darwin-arm64@16.1.4': optional: true - '@next/swc-darwin-x64@15.2.5': + '@next/swc-darwin-x64@16.1.4': optional: true - '@next/swc-linux-arm64-gnu@15.2.5': + '@next/swc-linux-arm64-gnu@16.1.4': optional: true - '@next/swc-linux-arm64-musl@15.2.5': + '@next/swc-linux-arm64-musl@16.1.4': optional: true - '@next/swc-linux-x64-gnu@15.2.5': + '@next/swc-linux-x64-gnu@16.1.4': optional: true - '@next/swc-linux-x64-musl@15.2.5': + '@next/swc-linux-x64-musl@16.1.4': optional: true - '@next/swc-win32-arm64-msvc@15.2.5': + '@next/swc-win32-arm64-msvc@16.1.4': optional: true - '@next/swc-win32-x64-msvc@15.2.5': + '@next/swc-win32-x64-msvc@16.1.4': optional: true '@noble/ciphers@2.1.1': {} @@ -5647,6 +5701,21 @@ snapshots: optionalDependencies: '@types/react': 19.2.8 + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -5934,8 +6003,6 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@swc/counter@0.1.3': {} - '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -6601,9 +6668,11 @@ snapshots: base64-js@1.5.1: {} + baseline-browser-mapping@2.9.16: {} + best-effort-json-parser@1.2.1: {} - better-auth@1.4.12(next@15.2.8(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.26(typescript@5.9.3)): + better-auth@1.4.12(next@16.1.4(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.26(typescript@5.9.3)): dependencies: '@better-auth/core': 1.4.12(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.7(zod@3.25.76))(jose@6.1.3)(kysely@0.28.9)(nanostores@1.1.0) '@better-auth/telemetry': 1.4.12(@better-auth/core@1.4.12(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.7(zod@3.25.76))(jose@6.1.3)(kysely@0.28.9)(nanostores@1.1.0)) @@ -6618,7 +6687,7 @@ snapshots: nanostores: 1.1.0 zod: 4.3.5 optionalDependencies: - next: 15.2.8(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + next: 16.1.4(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) vue: 3.5.26(typescript@5.9.3) @@ -6645,10 +6714,6 @@ snapshots: dependencies: fill-range: 7.1.1 - busboy@1.6.0: - dependencies: - streamsearch: 1.1.0 - c12@3.3.3: dependencies: chokidar: 5.0.0 @@ -6763,18 +6828,6 @@ snapshots: color-name@1.1.4: {} - color-string@1.9.1: - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.4 - optional: true - - color@4.2.3: - dependencies: - color-convert: 2.0.1 - color-string: 1.9.1 - optional: true - comma-separated-tokens@2.0.3: {} commander@7.2.0: {} @@ -7861,9 +7914,6 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 - is-arrayish@0.3.4: - optional: true - is-async-function@2.1.1: dependencies: async-function: 1.0.0 @@ -8641,28 +8691,27 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - next@15.2.8(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + next@16.1.4(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - '@next/env': 15.2.8 - '@swc/counter': 0.1.3 + '@next/env': 16.1.4 '@swc/helpers': 0.5.15 - busboy: 1.6.0 + baseline-browser-mapping: 2.9.16 caniuse-lite: 1.0.30001764 postcss: 8.4.31 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) styled-jsx: 5.1.6(react@19.2.3) optionalDependencies: - '@next/swc-darwin-arm64': 15.2.5 - '@next/swc-darwin-x64': 15.2.5 - '@next/swc-linux-arm64-gnu': 15.2.5 - '@next/swc-linux-arm64-musl': 15.2.5 - '@next/swc-linux-x64-gnu': 15.2.5 - '@next/swc-linux-x64-musl': 15.2.5 - '@next/swc-win32-arm64-msvc': 15.2.5 - '@next/swc-win32-x64-msvc': 15.2.5 + '@next/swc-darwin-arm64': 16.1.4 + '@next/swc-darwin-x64': 16.1.4 + '@next/swc-linux-arm64-gnu': 16.1.4 + '@next/swc-linux-arm64-musl': 16.1.4 + '@next/swc-linux-x64-gnu': 16.1.4 + '@next/swc-linux-x64-musl': 16.1.4 + '@next/swc-win32-arm64-msvc': 16.1.4 + '@next/swc-win32-x64-msvc': 16.1.4 '@opentelemetry/api': 1.9.0 - sharp: 0.33.5 + sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -9270,31 +9319,36 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 - sharp@0.33.5: + sharp@0.34.5: dependencies: - color: 4.2.3 + '@img/colour': 1.0.0 detect-libc: 2.1.2 semver: 7.7.3 optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.5 - '@img/sharp-darwin-x64': 0.33.5 - '@img/sharp-libvips-darwin-arm64': 1.0.4 - '@img/sharp-libvips-darwin-x64': 1.0.4 - '@img/sharp-libvips-linux-arm': 1.0.5 - '@img/sharp-libvips-linux-arm64': 1.0.4 - '@img/sharp-libvips-linux-s390x': 1.0.4 - '@img/sharp-libvips-linux-x64': 1.0.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 - '@img/sharp-linux-arm': 0.33.5 - '@img/sharp-linux-arm64': 0.33.5 - '@img/sharp-linux-s390x': 0.33.5 - '@img/sharp-linux-x64': 0.33.5 - '@img/sharp-linuxmusl-arm64': 0.33.5 - '@img/sharp-linuxmusl-x64': 0.33.5 - '@img/sharp-wasm32': 0.33.5 - '@img/sharp-win32-ia32': 0.33.5 - '@img/sharp-win32-x64': 0.33.5 + '@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 optional: true shebang-command@2.0.0: @@ -9344,11 +9398,6 @@ snapshots: signal-exit@4.1.0: {} - simple-swizzle@0.2.4: - dependencies: - is-arrayish: 0.3.4 - optional: true - simple-wcswidth@1.1.2: {} sirv@3.0.2: @@ -9400,8 +9449,6 @@ snapshots: - '@types/react' - supports-color - streamsearch@1.1.0: {} - string.prototype.codepointat@0.2.1: {} string.prototype.includes@2.0.1: diff --git a/frontend/src/components/ui/empty.tsx b/frontend/src/components/ui/empty.tsx new file mode 100644 index 0000000..df91e9d --- /dev/null +++ b/frontend/src/components/ui/empty.tsx @@ -0,0 +1,104 @@ +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +function Empty({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +const emptyMediaVariants = cva( + "flex shrink-0 items-center justify-center mb-2 [&_svg]:pointer-events-none [&_svg]:shrink-0", + { + variants: { + variant: { + default: "bg-transparent", + icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function EmptyMedia({ + className, + variant = "default", + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) { + return ( +
a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4", + className + )} + {...props} + /> + ) +} + +function EmptyContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Empty, + EmptyHeader, + EmptyTitle, + EmptyDescription, + EmptyContent, + EmptyMedia, +} diff --git a/frontend/src/components/ui/item.tsx b/frontend/src/components/ui/item.tsx new file mode 100644 index 0000000..d97de21 --- /dev/null +++ b/frontend/src/components/ui/item.tsx @@ -0,0 +1,193 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" +import { Separator } from "@/components/ui/separator" + +function ItemGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +const itemVariants = cva( + "group/item flex items-center border border-transparent text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", + { + variants: { + variant: { + default: "bg-transparent", + outline: "border-border", + muted: "bg-muted/50", + }, + size: { + default: "p-4 gap-4 ", + sm: "py-3 px-4 gap-2.5", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Item({ + className, + variant = "default", + size = "default", + asChild = false, + ...props +}: React.ComponentProps<"div"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "div" + return ( + + ) +} + +const itemMediaVariants = cva( + "flex shrink-0 items-center justify-center gap-2 group-has-[[data-slot=item-description]]/item:self-start [&_svg]:pointer-events-none group-has-[[data-slot=item-description]]/item:translate-y-0.5", + { + variants: { + variant: { + default: "bg-transparent", + icon: "size-8 border rounded-sm bg-muted [&_svg:not([class*='size-'])]:size-4", + image: + "size-10 rounded-sm overflow-hidden [&_img]:size-full [&_img]:object-cover", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function ItemMedia({ + className, + variant = "default", + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function ItemContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemDescription({ className, ...props }: React.ComponentProps<"p">) { + return ( +

a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", + className + )} + {...props} + /> + ) +} + +function ItemActions({ className, ...props }: React.ComponentProps<"div">) { + return ( +

+ ) +} + +function ItemHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Item, + ItemMedia, + ItemContent, + ItemActions, + ItemGroup, + ItemSeparator, + ItemTitle, + ItemDescription, + ItemHeader, + ItemFooter, +} diff --git a/frontend/src/components/ui/switch.tsx b/frontend/src/components/ui/switch.tsx new file mode 100644 index 0000000..6a2b524 --- /dev/null +++ b/frontend/src/components/ui/switch.tsx @@ -0,0 +1,31 @@ +"use client" + +import * as React from "react" +import * as SwitchPrimitive from "@radix-ui/react-switch" + +import { cn } from "@/lib/utils" + +function Switch({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +export { Switch } diff --git a/frontend/src/components/workspace/settings/acknowledge-page.tsx b/frontend/src/components/workspace/settings/acknowledge-page.tsx new file mode 100644 index 0000000..4d3c20c --- /dev/null +++ b/frontend/src/components/workspace/settings/acknowledge-page.tsx @@ -0,0 +1,5 @@ +"use client"; + +export function AcknowledgePage() { + return null; +} diff --git a/frontend/src/components/workspace/settings/appearance-settings-page.tsx b/frontend/src/components/workspace/settings/appearance-settings-page.tsx new file mode 100644 index 0000000..c9166aa --- /dev/null +++ b/frontend/src/components/workspace/settings/appearance-settings-page.tsx @@ -0,0 +1,193 @@ +"use client"; + +import { MonitorSmartphoneIcon, MoonIcon, SunIcon } from "lucide-react"; +import { useTheme } from "next-themes"; +import { useMemo, type ComponentType, type SVGProps } from "react"; + +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Separator } from "@/components/ui/separator"; +import { enUS, zhCN, type Locale } from "@/core/i18n"; +import { useI18n } from "@/core/i18n/hooks"; +import { cn } from "@/lib/utils"; + +import { SettingsSection } from "./settings-section"; + +const languageOptions: { value: Locale; label: string }[] = [ + { value: "en-US", label: enUS.locale.localName }, + { value: "zh-CN", label: zhCN.locale.localName }, +]; + +export function AppearanceSettingsPage() { + const { t, locale, changeLocale } = useI18n(); + const { theme, setTheme, resolvedTheme } = useTheme(); + const currentTheme = (theme ?? "system") as "system" | "light" | "dark"; + + const themeOptions = useMemo( + () => [ + { + id: "system", + label: t.settings.appearance.system, + description: t.settings.appearance.systemDescription, + icon: MonitorSmartphoneIcon, + }, + { + id: "light", + label: t.settings.appearance.light, + description: t.settings.appearance.lightDescription, + icon: SunIcon, + }, + { + id: "dark", + label: t.settings.appearance.dark, + description: t.settings.appearance.darkDescription, + icon: MoonIcon, + }, + ], + [ + t.settings.appearance.dark, + t.settings.appearance.darkDescription, + t.settings.appearance.light, + t.settings.appearance.lightDescription, + t.settings.appearance.system, + t.settings.appearance.systemDescription, + ], + ); + + return ( +
+ +
+ {themeOptions.map((option) => ( + setTheme(value)} + /> + ))} +
+
+ + + + + + +
+ ); +} + +function ThemePreviewCard({ + icon: Icon, + label, + description, + active, + mode, + resolvedTheme, + onSelect, +}: { + icon: ComponentType>; + label: string; + description: string; + active: boolean; + mode: "system" | "light" | "dark"; + resolvedTheme?: string; + onSelect: (mode: "system" | "light" | "dark") => void; +}) { + const previewMode = + mode === "system" ? (resolvedTheme === "dark" ? "dark" : "light") : mode; + + return ( + + ); +} diff --git a/frontend/src/components/workspace/settings/index.ts b/frontend/src/components/workspace/settings/index.ts index e69de29..6584506 100644 --- a/frontend/src/components/workspace/settings/index.ts +++ b/frontend/src/components/workspace/settings/index.ts @@ -0,0 +1 @@ +export { SettingsDialog } from "./settings-dialog"; diff --git a/frontend/src/components/workspace/settings/settings-dialog.tsx b/frontend/src/components/workspace/settings/settings-dialog.tsx index e69de29..ba872ca 100644 --- a/frontend/src/components/workspace/settings/settings-dialog.tsx +++ b/frontend/src/components/workspace/settings/settings-dialog.tsx @@ -0,0 +1,100 @@ +"use client"; + +import { PaletteIcon, SparklesIcon, WrenchIcon } from "lucide-react"; +import { useMemo, useState } from "react"; + +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { AcknowledgePage } from "@/components/workspace/settings/acknowledge-page"; +import { AppearanceSettingsPage } from "@/components/workspace/settings/appearance-settings-page"; +import { SkillSettingsPage } from "@/components/workspace/settings/skill-settings-page"; +import { ToolSettingsPage } from "@/components/workspace/settings/tool-settings-page"; +import { useI18n } from "@/core/i18n/hooks"; +import { cn } from "@/lib/utils"; + +type SettingsSection = "appearance" | "tools" | "skills" | "acknowledge"; + +type SettingsDialogProps = React.ComponentProps & { + defaultSection?: SettingsSection; +}; + +export function SettingsDialog({ + defaultSection = "appearance", + ...dialogProps +}: SettingsDialogProps) { + const { t } = useI18n(); + const [activeSection, setActiveSection] = + useState(defaultSection); + + const sections = useMemo( + () => [ + { + id: "appearance", + label: t.settings.sections.appearance, + icon: PaletteIcon, + }, + { id: "tools", label: t.settings.sections.tools, icon: WrenchIcon }, + { id: "skills", label: t.settings.sections.skills, icon: SparklesIcon }, + ], + [ + t.settings.sections.appearance, + t.settings.sections.tools, + t.settings.sections.skills, + ], + ); + + return ( + + + + {t.settings.title} +

+ {t.settings.description} +

+
+
+ + +
+ {activeSection === "appearance" && } + {activeSection === "tools" && } + {activeSection === "skills" && } + {activeSection === "acknowledge" && } +
+
+
+
+
+ ); +} diff --git a/frontend/src/components/workspace/settings/settings-section.tsx b/frontend/src/components/workspace/settings/settings-section.tsx new file mode 100644 index 0000000..95bc40e --- /dev/null +++ b/frontend/src/components/workspace/settings/settings-section.tsx @@ -0,0 +1,25 @@ +import { cn } from "@/lib/utils"; + +export function SettingsSection({ + className, + title, + description, + children, +}: { + className?: string; + title: React.ReactNode; + description?: React.ReactNode; + children: React.ReactNode; +}) { + return ( +
+
+

{title}

+ {description && ( +

{description}

+ )} +
+
{children}
+
+ ); +} diff --git a/frontend/src/components/workspace/settings/skill-settings-page.tsx b/frontend/src/components/workspace/settings/skill-settings-page.tsx new file mode 100644 index 0000000..1317953 --- /dev/null +++ b/frontend/src/components/workspace/settings/skill-settings-page.tsx @@ -0,0 +1,91 @@ +"use client"; + +import { SparklesIcon } from "lucide-react"; + +import { Badge } from "@/components/ui/badge"; +import { + Empty, + EmptyDescription, + EmptyHeader, + EmptyMedia, + EmptyTitle, +} from "@/components/ui/empty"; +import { + Item, + ItemActions, + ItemTitle, + ItemContent, + ItemDescription, +} from "@/components/ui/item"; +import { Switch } from "@/components/ui/switch"; +import { useI18n } from "@/core/i18n/hooks"; +import { useEnableSkill, useSkills } from "@/core/skills/hooks"; +import type { Skill } from "@/core/skills/type"; + +import { SettingsSection } from "./settings-section"; + +export function SkillSettingsPage() { + const { t } = useI18n(); + const { skills, isLoading, error } = useSkills(); + return ( + + {isLoading ? ( +
Loading...
+ ) : error ? ( +
Error: {error.message}
+ ) : ( + + )} +
+ ); +} + +function SkillSettingsList({ skills }: { skills: Skill[] }) { + const { mutate: enableSkill } = useEnableSkill(); + if (skills.length === 0) { + return ( + + + + + + No skill yet + + Put your skill folders under the `/skills/custom` folder under the + root folder of DeerFlow. + + + + ); + } + return ( +
+ {skills.map((skill) => ( + + + +
+
{skill.name}
+ {skill.category} +
+
+ + {skill.description} + +
+ + + enableSkill({ skillName: skill.name, enabled: checked }) + } + /> + +
+ ))} +
+ ); +} diff --git a/frontend/src/components/workspace/settings/tool-settings-page.tsx b/frontend/src/components/workspace/settings/tool-settings-page.tsx new file mode 100644 index 0000000..73b0bb3 --- /dev/null +++ b/frontend/src/components/workspace/settings/tool-settings-page.tsx @@ -0,0 +1,68 @@ +"use client"; + +import { + Item, + ItemActions, + ItemContent, + ItemDescription, + ItemTitle, +} from "@/components/ui/item"; +import { Switch } from "@/components/ui/switch"; +import { useI18n } from "@/core/i18n/hooks"; +import { useMCPConfig, useEnableMCPServer } from "@/core/mcp/hooks"; +import type { MCPServerConfig } from "@/core/mcp/types"; + +import { SettingsSection } from "./settings-section"; + +export function ToolSettingsPage() { + const { t } = useI18n(); + const { config, isLoading, error } = useMCPConfig(); + return ( + + {isLoading ? ( +
Loading...
+ ) : error ? ( +
Error: {error.message}
+ ) : ( + config && + )} +
+ ); +} + +function MCPServerList({ + servers, +}: { + servers: Record; +}) { + const { mutate: enableMCPServer } = useEnableMCPServer(); + return ( +
+ {Object.entries(servers).map(([name, config]) => ( + + + +
+
{name}
+
+
+ + {config.description} + +
+ + + enableMCPServer({ serverName: name, enabled: checked }) + } + /> + +
+ ))} +
+ ); +} diff --git a/frontend/src/components/workspace/workspace-sidebar.tsx b/frontend/src/components/workspace/workspace-sidebar.tsx index b7240e4..092324e 100644 --- a/frontend/src/components/workspace/workspace-sidebar.tsx +++ b/frontend/src/components/workspace/workspace-sidebar.tsx @@ -1,6 +1,7 @@ "use client"; import { SettingsIcon } from "lucide-react"; +import { useState } from "react"; import { Sidebar, @@ -14,6 +15,7 @@ import { SidebarGroup, SidebarGroupContent, } from "@/components/ui/sidebar"; +import { SettingsDialog } from "@/components/workspace/settings"; import { useI18n } from "@/core/i18n/hooks"; import { RecentChatList } from "./recent-chat-list"; @@ -24,32 +26,42 @@ export function WorkspaceSidebar({ ...props }: React.ComponentProps) { const { t } = useI18n(); + const [settingsOpen, setSettingsOpen] = useState(false); + return ( - - - - - - - - - - - - - - -
- - {t.common.settings} -
-
-
-
-
-
-
- -
+ <> + + + + + + + + + + + + + + + + + + + + + + + + + + ); } diff --git a/frontend/src/core/i18n/locales/en-US.ts b/frontend/src/core/i18n/locales/en-US.ts index a38d736..2f87746 100644 --- a/frontend/src/core/i18n/locales/en-US.ts +++ b/frontend/src/core/i18n/locales/en-US.ts @@ -1,6 +1,11 @@ import type { Translations } from "./types"; export const enUS: Translations = { + // Locale meta + locale: { + localName: "English", + }, + // Common common: { home: "Home", @@ -83,4 +88,41 @@ export const enUS: Translations = { readFile: "Read file", writeFile: "Write file", }, + + // Settings + settings: { + title: "Settings", + description: "Adjust how DeerFlow looks and behaves for you.", + sections: { + appearance: "Appearance", + tools: "Tools", + skills: "Skills", + acknowledge: "Acknowledge", + }, + appearance: { + themeTitle: "Theme", + themeDescription: + "Choose how the interface follows your device or stays fixed.", + system: "System", + light: "Light", + dark: "Dark", + systemDescription: "Match the operating system preference automatically.", + lightDescription: "Bright palette with higher contrast for daytime.", + darkDescription: "Dim palette that reduces glare for focus.", + languageTitle: "Language", + languageDescription: "Switch between languages.", + }, + tools: { + title: "Tools", + description: "Manage the configuration and enabled status of MCP tools.", + }, + skills: { + title: "Skills", + description: "Manage the configuration and enabled status of the skills.", + }, + acknowledge: { + emptyTitle: "Acknowledgements", + emptyDescription: "Credits and acknowledgements will show here.", + }, + }, }; diff --git a/frontend/src/core/i18n/locales/types.ts b/frontend/src/core/i18n/locales/types.ts index 1af3e52..5586983 100644 --- a/frontend/src/core/i18n/locales/types.ts +++ b/frontend/src/core/i18n/locales/types.ts @@ -1,4 +1,9 @@ export interface Translations { + // Locale meta + locale: { + localName: string; + }; + // Common common: { home: string; @@ -80,4 +85,40 @@ export interface Translations { readFile: string; writeFile: string; }; + + // Settings + settings: { + title: string; + description: string; + sections: { + appearance: string; + tools: string; + skills: string; + acknowledge: string; + }; + appearance: { + themeTitle: string; + themeDescription: string; + system: string; + light: string; + dark: string; + systemDescription: string; + lightDescription: string; + darkDescription: string; + languageTitle: string; + languageDescription: string; + }; + tools: { + title: string; + description: string; + }; + skills: { + title: string; + description: string; + }; + acknowledge: { + emptyTitle: string; + emptyDescription: string; + }; + }; } diff --git a/frontend/src/core/i18n/locales/zh-CN.ts b/frontend/src/core/i18n/locales/zh-CN.ts index 1ec1281..334039c 100644 --- a/frontend/src/core/i18n/locales/zh-CN.ts +++ b/frontend/src/core/i18n/locales/zh-CN.ts @@ -1,6 +1,11 @@ import type { Translations } from "./types"; export const zhCN: Translations = { + // Locale meta + locale: { + localName: "中文", + }, + // Common common: { home: "首页", @@ -83,4 +88,40 @@ export const zhCN: Translations = { readFile: "读取文件", writeFile: "写入文件", }, + + // Settings + settings: { + title: "设置", + description: "根据你的偏好调整 DeerFlow 的界面和行为。", + sections: { + appearance: "外观", + tools: "工具", + skills: "技能", + acknowledge: "致谢", + }, + appearance: { + themeTitle: "主题", + themeDescription: "跟随系统或选择固定的界面模式。", + system: "系统", + light: "浅色", + dark: "深色", + systemDescription: "自动匹配操作系统偏好。", + lightDescription: "更明亮的配色,适合日间使用。", + darkDescription: "更暗的配色,减少眩光方便专注。", + languageTitle: "语言", + languageDescription: "在不同语言之间切换。", + }, + tools: { + title: "工具", + description: "管理 MCP 工具的配置和启用状态。", + }, + skills: { + title: "技能", + description: "管理智能体的技能配置和启用状态。", + }, + acknowledge: { + emptyTitle: "致谢", + emptyDescription: "相关的致谢信息会展示在这里。", + }, + }, }; diff --git a/frontend/src/core/mcp/api.ts b/frontend/src/core/mcp/api.ts new file mode 100644 index 0000000..edc4561 --- /dev/null +++ b/frontend/src/core/mcp/api.ts @@ -0,0 +1,24 @@ +import { env } from "@/env"; + +import type { MCPConfig } from "./types"; + +export async function loadMCPConfig() { + const response = await fetch( + `${env.NEXT_PUBLIC_BACKEND_BASE_URL}/api/mcp/config`, + ); + return response.json() as Promise; +} + +export async function updateMCPConfig(config: MCPConfig) { + const response = await fetch( + `${env.NEXT_PUBLIC_BACKEND_BASE_URL}/api/mcp/config`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(config), + }, + ); + return response.json(); +} diff --git a/frontend/src/core/mcp/hooks.ts b/frontend/src/core/mcp/hooks.ts new file mode 100644 index 0000000..dc17ed8 --- /dev/null +++ b/frontend/src/core/mcp/hooks.ts @@ -0,0 +1,44 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; + +import { loadMCPConfig, updateMCPConfig } from "./api"; + +export function useMCPConfig() { + const { data, isLoading, error } = useQuery({ + queryKey: ["mcpConfig"], + queryFn: () => loadMCPConfig(), + }); + return { config: data, isLoading, error }; +} + +export function useEnableMCPServer() { + const queryClient = useQueryClient(); + const { config } = useMCPConfig(); + return useMutation({ + mutationFn: async ({ + serverName, + enabled, + }: { + serverName: string; + enabled: boolean; + }) => { + if (!config) { + throw new Error("MCP config not found"); + } + if (!config.mcp_servers[serverName]) { + throw new Error(`MCP server ${serverName} not found`); + } + await updateMCPConfig({ + mcp_servers: { + ...config.mcp_servers, + [serverName]: { + ...config.mcp_servers[serverName], + enabled, + }, + }, + }); + }, + onSuccess: () => { + void queryClient.invalidateQueries({ queryKey: ["mcpConfig"] }); + }, + }); +} diff --git a/frontend/src/core/mcp/index.ts b/frontend/src/core/mcp/index.ts new file mode 100644 index 0000000..1d58e1a --- /dev/null +++ b/frontend/src/core/mcp/index.ts @@ -0,0 +1,2 @@ +export * from "./api"; +export * from "./types"; diff --git a/frontend/src/core/mcp/types.ts b/frontend/src/core/mcp/types.ts new file mode 100644 index 0000000..5d4f571 --- /dev/null +++ b/frontend/src/core/mcp/types.ts @@ -0,0 +1,8 @@ +export interface MCPServerConfig extends Record { + enabled: boolean; + description: string; +} + +export interface MCPConfig { + mcp_servers: Record; +} diff --git a/frontend/src/core/skills/api.ts b/frontend/src/core/skills/api.ts new file mode 100644 index 0000000..242d932 --- /dev/null +++ b/frontend/src/core/skills/api.ts @@ -0,0 +1,25 @@ +import { env } from "@/env"; + +import type { Skill } from "./type"; + +export async function loadSkills() { + const skills = await fetch(`${env.NEXT_PUBLIC_BACKEND_BASE_URL}/api/skills`); + const json = await skills.json(); + return json.skills as Skill[]; +} + +export async function enableSkill(skillName: string, enabled: boolean) { + const response = await fetch( + `${env.NEXT_PUBLIC_BACKEND_BASE_URL}/api/skills/${skillName}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + enabled, + }), + }, + ); + return response.json(); +} diff --git a/frontend/src/core/skills/hooks.ts b/frontend/src/core/skills/hooks.ts new file mode 100644 index 0000000..9135223 --- /dev/null +++ b/frontend/src/core/skills/hooks.ts @@ -0,0 +1,31 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; + +import { enableSkill } from "./api"; + +import { loadSkills } from "."; + +export function useSkills() { + const { data, isLoading, error } = useQuery({ + queryKey: ["skills"], + queryFn: () => loadSkills(), + }); + return { skills: data ?? [], isLoading, error }; +} + +export function useEnableSkill() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ + skillName, + enabled, + }: { + skillName: string; + enabled: boolean; + }) => { + await enableSkill(skillName, enabled); + }, + onSuccess: () => { + void queryClient.invalidateQueries({ queryKey: ["skills"] }); + }, + }); +} diff --git a/frontend/src/core/skills/index.ts b/frontend/src/core/skills/index.ts new file mode 100644 index 0000000..7ac274e --- /dev/null +++ b/frontend/src/core/skills/index.ts @@ -0,0 +1,2 @@ +export * from "./api"; +export * from "./type"; diff --git a/frontend/src/core/skills/type.ts b/frontend/src/core/skills/type.ts new file mode 100644 index 0000000..7bb76a2 --- /dev/null +++ b/frontend/src/core/skills/type.ts @@ -0,0 +1,7 @@ +export interface Skill { + name: string; + description: string; + category: string; + license: string; + enabled: boolean; +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 09861ce..f760fdb 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -22,7 +22,7 @@ "noEmit": true, "module": "ESNext", "moduleResolution": "Bundler", - "jsx": "preserve", + "jsx": "react-jsx", "plugins": [ { "name": "next"