diff --git a/README.md b/README.md index 8a16fc3..16cc42d 100644 --- a/README.md +++ b/README.md @@ -441,6 +441,8 @@ If you find this project helpful, please consider sponsoring its development. Yo [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/F1F31GN2GM) +[Paypal](https://paypal.me/musistudio1999) + diff --git a/README_zh.md b/README_zh.md index 9bef1ee..371fe2c 100644 --- a/README_zh.md +++ b/README_zh.md @@ -436,6 +436,8 @@ jobs: [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/F1F31GN2GM) +[Paypal](https://paypal.me/musistudio1999) +
Alipay
diff --git a/package.json b/package.json index 371980c..d5e845d 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dependencies": { "@fastify/static": "^8.2.0", "@musistudio/llms": "^1.0.18", + "@radix-ui/react-tooltip": "^1.2.7", "dotenv": "^16.4.7", "json5": "^2.2.3", "openurl": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc6b93e..41d5783 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@musistudio/llms': specifier: ^1.0.18 version: 1.0.18(ws@8.18.3)(zod@3.25.67) + '@radix-ui/react-tooltip': + specifier: ^1.2.7 + version: 1.2.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1) dotenv: specifier: ^16.4.7 version: 16.6.1 @@ -232,6 +235,21 @@ packages: '@fastify/static@8.2.0': resolution: {integrity: sha512-PejC/DtT7p1yo3p+W7LiUtLMsV8fEvxAK15sozHy9t8kwo5r0uLYmhV/inURmGz1SkHZFz/8CNtHLPyhKcx4SQ==} + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.3': + resolution: {integrity: sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==} + + '@floating-ui/react-dom@2.1.5': + resolution: {integrity: sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@google/genai@1.8.0': resolution: {integrity: sha512-n3KiMFesQCy2R9iSdBIuJ0JWYQ1HZBJJkmt4PPZMGZKvlgHhBAGw1kUMyX+vsAIzprN3lK45DI755lm70wPOOg==} engines: {node: '>=20.0.0'} @@ -272,6 +290,215 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@radix-ui/primitive@1.1.2': + resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} + + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + 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-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.10': + resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==} + 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-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-popper@1.2.7': + resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==} + 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-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + 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-presence@1.1.4': + resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} + 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-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + 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-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tooltip@1.2.7': + resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==} + 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-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} + 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/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@types/node@24.0.15': resolution: {integrity: sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==} @@ -729,6 +956,15 @@ packages: quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + react-dom@19.1.1: + resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + peerDependencies: + react: ^19.1.1 + + react@19.1.1: + resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + engines: {node: '>=0.10.0'} + real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} @@ -770,6 +1006,9 @@ packages: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + secure-json-parse@4.0.0: resolution: {integrity: sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==} @@ -1073,6 +1312,23 @@ snapshots: fastq: 1.19.1 glob: 11.0.3 + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.3': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@floating-ui/dom': 1.7.3 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@floating-ui/utils@0.2.10': {} + '@google/genai@1.8.0': dependencies: google-auth-library: 9.15.1 @@ -1136,6 +1392,136 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@radix-ui/primitive@1.1.2': {} + + '@radix-ui/react-arrow@1.1.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@radix-ui/react-compose-refs@1.1.2(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@radix-ui/react-context@1.1.2(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@radix-ui/react-dismissable-layer@1.1.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(react@19.1.1) + '@radix-ui/react-use-escape-keydown': 1.1.1(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@radix-ui/react-id@1.1.1(react@19.1.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(react@19.1.1) + react: 19.1.1 + + '@radix-ui/react-popper@1.2.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@floating-ui/react-dom': 2.1.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-arrow': 1.1.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(react@19.1.1) + '@radix-ui/react-context': 1.1.2(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(react@19.1.1) + '@radix-ui/react-use-rect': 1.1.1(react@19.1.1) + '@radix-ui/react-use-size': 1.1.1(react@19.1.1) + '@radix-ui/rect': 1.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@radix-ui/react-portal@1.1.9(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@radix-ui/react-presence@1.1.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@radix-ui/react-primitive@2.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-slot': 1.2.3(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@radix-ui/react-slot@1.2.3(react@19.1.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(react@19.1.1) + react: 19.1.1 + + '@radix-ui/react-tooltip@1.2.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(react@19.1.1) + '@radix-ui/react-context': 1.1.2(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.1(react@19.1.1) + '@radix-ui/react-popper': 1.2.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-portal': 1.1.9(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': 1.1.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.2.3(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.2.2(react@19.1.1) + '@radix-ui/react-visually-hidden': 1.2.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@radix-ui/react-use-callback-ref@1.1.1(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@radix-ui/react-use-controllable-state@1.2.2(react@19.1.1)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(react@19.1.1) + react: 19.1.1 + + '@radix-ui/react-use-effect-event@0.0.2(react@19.1.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(react@19.1.1) + react: 19.1.1 + + '@radix-ui/react-use-escape-keydown@1.1.1(react@19.1.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(react@19.1.1) + react: 19.1.1 + + '@radix-ui/react-use-layout-effect@1.1.1(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@radix-ui/react-use-rect@1.1.1(react@19.1.1)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.1.1 + + '@radix-ui/react-use-size@1.1.1(react@19.1.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(react@19.1.1) + react: 19.1.1 + + '@radix-ui/react-visually-hidden@1.2.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@radix-ui/rect@1.1.1': {} + '@types/node@24.0.15': dependencies: undici-types: 7.8.0 @@ -1633,6 +2019,13 @@ snapshots: quick-format-unescaped@4.0.4: {} + react-dom@19.1.1(react@19.1.1): + dependencies: + react: 19.1.1 + scheduler: 0.26.0 + + react@19.1.1: {} + real-require@0.2.0: {} rechoir@0.6.2: @@ -1665,6 +2058,8 @@ snapshots: safe-stable-stringify@2.5.0: {} + scheduler@0.26.0: {} + secure-json-parse@4.0.0: {} semver@5.7.2: {} diff --git a/src/cli.ts b/src/cli.ts index b66a406..114e068 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -2,12 +2,16 @@ import { run } from "./index"; import { showStatus } from "./utils/status"; import { executeCodeCommand } from "./utils/codeCommand"; -import { cleanupPidFile, isServiceRunning, getServiceInfo } from "./utils/processCheck"; +import { + cleanupPidFile, + isServiceRunning, + getServiceInfo, +} from "./utils/processCheck"; import { version } from "../package.json"; import { spawn, exec } from "child_process"; import { PID_FILE, REFERENCE_COUNT_FILE } from "./constants"; import fs, { existsSync, readFileSync } from "fs"; -import {join} from "path"; +import { join } from "path"; const command = process.argv[2]; @@ -108,7 +112,9 @@ async function main() { startProcess.unref(); if (await waitForService()) { - executeCodeCommand(process.argv.slice(3)); + // Join all code arguments into a single string to preserve spaces within quotes + const codeArgs = process.argv.slice(3); + executeCodeCommand(codeArgs); } else { console.error( "Service startup timeout, please manually run `ccr start` to start the service" @@ -116,7 +122,9 @@ async function main() { process.exit(1); } } else { - executeCodeCommand(process.argv.slice(3)); + // Join all code arguments into a single string to preserve spaces within quotes + const codeArgs = process.argv.slice(3); + executeCodeCommand(codeArgs); } break; case "ui": @@ -138,49 +146,68 @@ async function main() { if (!(await waitForService())) { // If service startup fails, try to start with default config - console.log("Service startup timeout, trying to start with default configuration..."); - const { initDir, writeConfigFile, backupConfigFile } = require("./utils"); - + console.log( + "Service startup timeout, trying to start with default configuration..." + ); + const { + initDir, + writeConfigFile, + backupConfigFile, + } = require("./utils"); + try { // Initialize directories await initDir(); - + // Backup existing config file if it exists const backupPath = await backupConfigFile(); if (backupPath) { - console.log(`Backed up existing configuration file to ${backupPath}`); + console.log( + `Backed up existing configuration file to ${backupPath}` + ); } - + // Create a minimal default config file await writeConfigFile({ - "PORT": 3456, - "Providers": [], - "Router": {} + PORT: 3456, + Providers: [], + Router: {}, }); - console.log("Created minimal default configuration file at ~/.claude-code-router/config.json"); - console.log("Please edit this file with your actual configuration."); - + console.log( + "Created minimal default configuration file at ~/.claude-code-router/config.json" + ); + console.log( + "Please edit this file with your actual configuration." + ); + // Try starting the service again const restartProcess = spawn("node", [cliPath, "start"], { detached: true, stdio: "ignore", }); - + restartProcess.on("error", (error) => { - console.error("Failed to start service with default config:", error.message); + console.error( + "Failed to start service with default config:", + error.message + ); process.exit(1); }); - + restartProcess.unref(); - - if (!(await waitForService(15000))) { // Wait a bit longer for the first start + + if (!(await waitForService(15000))) { + // Wait a bit longer for the first start console.error( "Service startup still failing. Please manually run `ccr start` to start the service and check the logs." ); process.exit(1); } } catch (error: any) { - console.error("Failed to create default configuration:", error.message); + console.error( + "Failed to create default configuration:", + error.message + ); process.exit(1); } } @@ -190,11 +217,11 @@ async function main() { const serviceInfo = await getServiceInfo(); const uiUrl = `${serviceInfo.endpoint}/ui/`; console.log(`Opening UI at ${uiUrl}`); - + // Open URL in browser based on platform const platform = process.platform; let openCommand = ""; - + if (platform === "win32") { // Windows openCommand = `start ${uiUrl}`; @@ -208,7 +235,7 @@ async function main() { console.error("Unsupported platform for opening browser"); process.exit(1); } - + exec(openCommand, (error) => { if (error) { console.error("Failed to open browser:", error.message); diff --git a/ui/src/App.tsx b/ui/src/App.tsx index ffcb2b4..9a96f86 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from "react"; +import { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { SettingsDialog } from "@/components/SettingsDialog"; @@ -9,30 +9,23 @@ import { JsonEditor } from "@/components/JsonEditor"; import { Button } from "@/components/ui/button"; import { useConfig } from "@/components/ConfigProvider"; import { api } from "@/lib/api"; -import { Settings, Languages, Save, RefreshCw, FileJson, Upload, Download } from "lucide-react"; +import { Settings, Languages, Save, RefreshCw, FileJson } from "lucide-react"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip"; import { Toast } from "@/components/ui/toast"; import "@/styles/animations.css"; function App() { const { t, i18n } = useTranslation(); const navigate = useNavigate(); - const { config, setConfig, error } = useConfig(); + const { config, error } = useConfig(); const [isSettingsOpen, setIsSettingsOpen] = useState(false); const [isJsonEditorOpen, setIsJsonEditorOpen] = useState(false); const [isCheckingAuth, setIsCheckingAuth] = useState(true); const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' | 'warning' } | null>(null); - const fileInputRef = useRef(null); useEffect(() => { const checkAuth = async () => { @@ -160,43 +153,6 @@ function App() { } }; - const exportConfig = () => { - if (!config) return; - - const configString = JSON.stringify(config, null, 2); - const blob = new Blob([configString], { type: "application/json" }); - const url = URL.createObjectURL(blob); - const a = document.createElement("a"); - a.href = url; - a.download = "claude-code-router-config.json"; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(url); - }; - - const importConfig = () => { - fileInputRef.current?.click(); - }; - - const handleFileChange = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; - - const reader = new FileReader(); - reader.onload = (event) => { - try { - const configString = event.target?.result as string; - const importedConfig = JSON.parse(configString); - setConfig(importedConfig); - } catch (error) { - console.error("Failed to parse config file:", error); - setToast({ message: t('settings.import_error'), type: 'error' }); - } - }; - reader.readAsText(file); - }; - if (isCheckingAuth) { return ( @@ -224,93 +180,51 @@ function App() { } return ( - -
-
-

{t('app.title')}

-
- - - + + + + + + +
+ - - -

{t('app.import_config')}

-
- - - - - - -

{t('app.export_config')}

-
-
- - - - - -

{t('app.settings')}

-
-
- - - - - -

{t('json_editor.title')}

-
-
- - - - - - - - -

{t('app.change_language')}

-
-
- -
- - -
-
-
- - -
-
+
+ + + + + +
@@ -324,28 +238,20 @@ function App() {
- - setToast({ message, type })} + + setToast({ message, type })} + /> + {toast && ( + setToast(null)} /> - {toast && ( - setToast(null)} - /> - )} - - -
+ )} + ); } diff --git a/ui/src/components/ui/tooltip.tsx b/ui/src/components/ui/tooltip.tsx deleted file mode 100644 index 61b717e..0000000 --- a/ui/src/components/ui/tooltip.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import * as React from "react" -import * as TooltipPrimitive from "@radix-ui/react-tooltip" - -import { cn } from "@/lib/utils" - -const TooltipProvider = TooltipPrimitive.Provider - -const Tooltip = TooltipPrimitive.Root - -const TooltipTrigger = TooltipPrimitive.Trigger - -const TooltipContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, sideOffset = 4, ...props }, ref) => ( - -)) -TooltipContent.displayName = TooltipPrimitive.Content.displayName - -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } \ No newline at end of file diff --git a/ui/src/locales/en.json b/ui/src/locales/en.json index 6940066..9cf93fa 100644 --- a/ui/src/locales/en.json +++ b/ui/src/locales/en.json @@ -12,10 +12,7 @@ "config_saved_success": "Config saved successfully", "config_saved_failed": "Failed to save config", "config_saved_restart_success": "Config saved and service restarted successfully", - "config_saved_restart_failed": "Failed to save config and restart service", - "import_config": "Import Config", - "export_config": "Export Config", - "change_language": "Change Language" + "config_saved_restart_failed": "Failed to save config and restart service" }, "login": { "title": "Sign in to your account", diff --git a/ui/src/locales/zh.json b/ui/src/locales/zh.json index c0b057c..3a4828e 100644 --- a/ui/src/locales/zh.json +++ b/ui/src/locales/zh.json @@ -12,10 +12,7 @@ "config_saved_success": "配置保存成功", "config_saved_failed": "配置保存失败", "config_saved_restart_success": "配置保存并服务重启成功", - "config_saved_restart_failed": "配置保存并服务重启失败", - "import_config": "导入配置", - "export_config": "导出配置", - "change_language": "切换语言" + "config_saved_restart_failed": "配置保存并服务重启失败" }, "login": { "title": "登录到您的账户", diff --git a/ui/tsconfig.tsbuildinfo b/ui/tsconfig.tsbuildinfo index b42216f..62ad4f2 100644 --- a/ui/tsconfig.tsbuildinfo +++ b/ui/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/i18n.ts","./src/main.tsx","./src/routes.tsx","./src/types.ts","./src/vite-env.d.ts","./src/components/configprovider.tsx","./src/components/jsoneditor.tsx","./src/components/login.tsx","./src/components/protectedroute.tsx","./src/components/providerlist.tsx","./src/components/providers.tsx","./src/components/publicroute.tsx","./src/components/router.tsx","./src/components/settingsdialog.tsx","./src/components/transformerlist.tsx","./src/components/transformers.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/combo-input.tsx","./src/components/ui/combobox.tsx","./src/components/ui/command.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/multi-combobox.tsx","./src/components/ui/popover.tsx","./src/components/ui/switch.tsx","./src/components/ui/toast.tsx","./src/components/ui/tooltip.tsx","./src/lib/api.ts","./src/lib/utils.ts"],"version":"5.8.3"} \ No newline at end of file +{"root":["./src/app.tsx","./src/i18n.ts","./src/main.tsx","./src/routes.tsx","./src/types.ts","./src/vite-env.d.ts","./src/components/configprovider.tsx","./src/components/jsoneditor.tsx","./src/components/login.tsx","./src/components/protectedroute.tsx","./src/components/providerlist.tsx","./src/components/providers.tsx","./src/components/publicroute.tsx","./src/components/router.tsx","./src/components/settingsdialog.tsx","./src/components/transformerlist.tsx","./src/components/transformers.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/combo-input.tsx","./src/components/ui/combobox.tsx","./src/components/ui/command.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/multi-combobox.tsx","./src/components/ui/popover.tsx","./src/components/ui/switch.tsx","./src/components/ui/toast.tsx","./src/lib/api.ts","./src/lib/utils.ts"],"version":"5.8.3"} \ No newline at end of file
Alipay