From b6c18f28aba6566e48a25f927ee7c7c56c69d75f Mon Sep 17 00:00:00 2001 From: Zhang Minghan Date: Tue, 12 Sep 2023 17:03:51 +0800 Subject: [PATCH] add latex support and markdown highlight async build --- app/index.html | 1 + app/package.json | 3 +- app/pnpm-lock.yaml | 218 +++++++++++++++++++++++++++---- app/src/App.tsx | 2 +- app/src/assets/chat.less | 10 ++ app/src/assets/navbar.less | 4 + app/src/components/Markdown.tsx | 6 +- app/src/components/Message.tsx | 4 +- app/src/components/ui/select.tsx | 44 +++---- app/src/conf.ts | 2 +- app/src/conversation/addition.ts | 4 +- app/src/i18n.ts | 49 +++---- app/src/routes/Quota.tsx | 3 +- app/src/routes/Subscription.tsx | 182 ++++++++++++++++---------- app/src/store/chat.ts | 4 +- app/src/store/subscription.ts | 16 ++- app/vite.config.ts | 10 ++ 17 files changed, 411 insertions(+), 151 deletions(-) diff --git a/app/index.html b/app/index.html index 52873ab..4a64d06 100644 --- a/app/index.html +++ b/app/index.html @@ -13,6 +13,7 @@ +
diff --git a/app/package.json b/app/package.json index d40f964..46c2c8d 100644 --- a/app/package.json +++ b/app/package.json @@ -38,12 +38,13 @@ "react-redux": "^8.1.2", "react-router-dom": "^6.15.0", "react-syntax-highlighter": "^15.5.0", + "rehype-katex": "^6.0.3", + "remark-math": "^5.1.1", "sort-by": "^1.2.0", "tailwind-merge": "^1.14.0", "tailwindcss-animate": "^1.0.7" }, "devDependencies": { - "@rollup/plugin-terser": "^0.4.3", "@types/node": "^20.5.9", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index 3a23a62..e2f761a 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -86,6 +86,12 @@ dependencies: react-syntax-highlighter: specifier: ^15.5.0 version: 15.5.0(react@18.2.0) + rehype-katex: + specifier: ^6.0.3 + version: 6.0.3 + remark-math: + specifier: ^5.1.1 + version: 5.1.1 sort-by: specifier: ^1.2.0 version: 1.2.0 @@ -97,9 +103,6 @@ dependencies: version: 1.0.7(tailwindcss@3.3.3) devDependencies: - '@rollup/plugin-terser': - specifier: ^0.4.3 - version: 0.4.3(rollup@2.79.1) '@types/node': specifier: ^20.5.9 version: 20.5.9 @@ -2535,21 +2538,6 @@ packages: rollup: 2.79.1 dev: true - /@rollup/plugin-terser@0.4.3(rollup@2.79.1): - resolution: {integrity: sha512-EF0oejTMtkyhrkwCdg0HJ0IpkcaVg1MMSf2olHb2Jp+1mnLM04OhjpJWGma4HobiDTF0WCyViWuvadyE9ch2XA==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.x || ^3.x - peerDependenciesMeta: - rollup: - optional: true - dependencies: - rollup: 2.79.1 - serialize-javascript: 6.0.1 - smob: 1.4.0 - terser: 5.19.4 - dev: true - /@rollup/pluginutils@3.1.0(rollup@2.79.1): resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} engines: {node: '>= 8.0.0'} @@ -2742,6 +2730,14 @@ packages: resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} dev: true + /@types/katex@0.14.0: + resolution: {integrity: sha512-+2FW2CcT0K3P+JMR8YG846bmDwplKUTsWgT2ENwdQ1UdVfRk3GQrh6Mi4sTopy30gI8Uau5CEqHTDZ6YvWIUPA==} + dev: false + + /@types/katex@0.16.2: + resolution: {integrity: sha512-dHsSjSlU/EWEEbeNADr3FtZZOAXPkFPUO457QCnoNqcZQXNqNEu/svQd0Nritvd3wNff4vvC/f4e6xgX3Llt8A==} + dev: false + /@types/mdast@3.0.12: resolution: {integrity: sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==} dependencies: @@ -3458,7 +3454,6 @@ packages: /commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - dev: true /common-tags@1.8.2: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} @@ -3684,6 +3679,11 @@ packages: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} dev: true + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + dev: false + /errno@0.1.8: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true @@ -4257,10 +4257,70 @@ packages: dependencies: function-bind: 1.1.1 + /hast-util-from-dom@4.2.0: + resolution: {integrity: sha512-t1RJW/OpJbCAJQeKi3Qrj1cAOLA0+av/iPFori112+0X7R3wng+jxLA+kXec8K4szqPRGI8vPxbbpEYvvpwaeQ==} + dependencies: + hastscript: 7.2.0 + web-namespaces: 2.0.1 + dev: false + + /hast-util-from-html-isomorphic@1.0.0: + resolution: {integrity: sha512-Yu480AKeOEN/+l5LA674a+7BmIvtDj24GvOt7MtQWuhzUwlaaRWdEPXAh3Qm5vhuthpAipFb2vTetKXWOjmTvw==} + dependencies: + '@types/hast': 2.3.5 + hast-util-from-dom: 4.2.0 + hast-util-from-html: 1.0.2 + unist-util-remove-position: 4.0.2 + dev: false + + /hast-util-from-html@1.0.2: + resolution: {integrity: sha512-LhrTA2gfCbLOGJq2u/asp4kwuG0y6NhWTXiPKP+n0qNukKy7hc10whqqCFfyvIA1Q5U5d0sp9HhNim9gglEH4A==} + dependencies: + '@types/hast': 2.3.5 + hast-util-from-parse5: 7.1.2 + parse5: 7.1.2 + vfile: 5.3.7 + vfile-message: 3.1.4 + dev: false + + /hast-util-from-parse5@7.1.2: + resolution: {integrity: sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==} + dependencies: + '@types/hast': 2.3.5 + '@types/unist': 2.0.8 + hastscript: 7.2.0 + property-information: 6.3.0 + vfile: 5.3.7 + vfile-location: 4.1.0 + web-namespaces: 2.0.1 + dev: false + + /hast-util-is-element@2.1.3: + resolution: {integrity: sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==} + dependencies: + '@types/hast': 2.3.5 + '@types/unist': 2.0.8 + dev: false + /hast-util-parse-selector@2.2.5: resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} dev: false + /hast-util-parse-selector@3.1.1: + resolution: {integrity: sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==} + dependencies: + '@types/hast': 2.3.5 + dev: false + + /hast-util-to-text@3.1.2: + resolution: {integrity: sha512-tcllLfp23dJJ+ju5wCCZHVpzsQQ43+moJbqVX3jNWPB7z/KFC4FyZD6R7y94cHL6MQ33YtMZL8Z0aIXXI4XFTw==} + dependencies: + '@types/hast': 2.3.5 + '@types/unist': 2.0.8 + hast-util-is-element: 2.1.3 + unist-util-find-after: 4.0.1 + dev: false + /hast-util-whitespace@2.0.1: resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==} dev: false @@ -4275,6 +4335,16 @@ packages: space-separated-tokens: 1.1.5 dev: false + /hastscript@7.2.0: + resolution: {integrity: sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==} + dependencies: + '@types/hast': 2.3.5 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 3.1.1 + property-information: 6.3.0 + space-separated-tokens: 2.0.2 + dev: false + /he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true @@ -4665,6 +4735,13 @@ packages: engines: {node: '>=0.10.0'} dev: true + /katex@0.16.8: + resolution: {integrity: sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==} + hasBin: true + dependencies: + commander: 8.3.0 + dev: false + /keyv@4.5.3: resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} dependencies: @@ -4767,6 +4844,10 @@ packages: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true + /longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + dev: false + /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -4858,6 +4939,21 @@ packages: - supports-color dev: false + /mdast-util-math@2.0.2: + resolution: {integrity: sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==} + dependencies: + '@types/mdast': 3.0.12 + longest-streak: 3.1.0 + mdast-util-to-markdown: 1.5.0 + dev: false + + /mdast-util-phrasing@3.0.1: + resolution: {integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==} + dependencies: + '@types/mdast': 3.0.12 + unist-util-is: 5.2.1 + dev: false + /mdast-util-to-hast@12.3.0: resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==} dependencies: @@ -4871,6 +4967,19 @@ packages: unist-util-visit: 4.1.2 dev: false + /mdast-util-to-markdown@1.5.0: + resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==} + dependencies: + '@types/mdast': 3.0.12 + '@types/unist': 2.0.8 + longest-streak: 3.1.0 + mdast-util-phrasing: 3.0.1 + mdast-util-to-string: 3.2.0 + micromark-util-decode-string: 1.1.0 + unist-util-visit: 4.1.2 + zwitch: 2.0.4 + dev: false + /mdast-util-to-string@3.2.0: resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} dependencies: @@ -4906,6 +5015,18 @@ packages: uvu: 0.5.6 dev: false + /micromark-extension-math@2.1.2: + resolution: {integrity: sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==} + dependencies: + '@types/katex': 0.16.2 + katex: 0.16.8 + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + dev: false + /micromark-factory-destination@1.1.0: resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==} dependencies: @@ -5269,6 +5390,12 @@ packages: engines: {node: '>= 0.10'} dev: true + /parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.5.0 + dev: false + /pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} dependencies: @@ -5735,11 +5862,31 @@ packages: jsesc: 0.5.0 dev: true + /rehype-katex@6.0.3: + resolution: {integrity: sha512-ByZlRwRUcWegNbF70CVRm2h/7xy7jQ3R9LaY4VVSvjnoVWwWVhNL60DiZsBpC5tSzYQOCvDbzncIpIjPZWodZA==} + dependencies: + '@types/hast': 2.3.5 + '@types/katex': 0.14.0 + hast-util-from-html-isomorphic: 1.0.0 + hast-util-to-text: 3.1.2 + katex: 0.16.8 + unist-util-visit: 4.1.2 + dev: false + /relateurl@0.2.7: resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} engines: {node: '>= 0.10'} dev: true + /remark-math@5.1.1: + resolution: {integrity: sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==} + dependencies: + '@types/mdast': 3.0.12 + mdast-util-math: 2.0.2 + micromark-extension-math: 2.1.2 + unified: 10.1.2 + dev: false + /remark-parse@10.0.2: resolution: {integrity: sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==} dependencies: @@ -5943,10 +6090,6 @@ packages: engines: {node: '>=8'} dev: true - /smob@1.4.0: - resolution: {integrity: sha512-MqR3fVulhjWuRNSMydnTlweu38UhQ0HXM4buStD/S3mc/BzX3CuM9OmhyQpmtYCvoYdl5ris6TI0ZqH355Ymqg==} - dev: true - /sort-by@1.2.0: resolution: {integrity: sha512-aRyW65r3xMnf4nxJRluCg0H/woJpksU1dQxRtXYzau30sNBOmf5HACpDd9MZDhKh7ALQ5FgSOfMPwZEtUmMqcg==} dependencies: @@ -6361,6 +6504,13 @@ packages: crypto-random-string: 2.0.0 dev: true + /unist-util-find-after@4.0.1: + resolution: {integrity: sha512-QO/PuPMm2ERxC6vFXEPtmAutOopy5PknD+Oq64gGwxKtk4xwo9Z97t9Av1obPmGU0IyTa6EKYUfTrK2QJS3Ozw==} + dependencies: + '@types/unist': 2.0.8 + unist-util-is: 5.2.1 + dev: false + /unist-util-generated@2.0.1: resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==} dev: false @@ -6377,6 +6527,13 @@ packages: '@types/unist': 2.0.8 dev: false + /unist-util-remove-position@4.0.2: + resolution: {integrity: sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==} + dependencies: + '@types/unist': 2.0.8 + unist-util-visit: 4.1.2 + dev: false + /unist-util-stringify-position@3.0.3: resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} dependencies: @@ -6478,6 +6635,13 @@ packages: sade: 1.8.1 dev: false + /vfile-location@4.1.0: + resolution: {integrity: sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==} + dependencies: + '@types/unist': 2.0.8 + vfile: 5.3.7 + dev: false + /vfile-message@3.1.4: resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} dependencies: @@ -6582,6 +6746,10 @@ packages: graceful-fs: 4.2.11 dev: true + /web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + dev: false + /webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} dev: true @@ -6838,3 +7006,7 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true + + /zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + dev: false diff --git a/app/src/App.tsx b/app/src/App.tsx index 433f20e..7396a4f 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -5,7 +5,7 @@ import { Button } from "./components/ui/button.tsx"; import router from "./router.ts"; import I18nProvider from "./components/I18nProvider.tsx"; import ProjectLink from "./components/ProjectLink.tsx"; -import {BadgeCent, Boxes, CalendarPlus, Cloud, Menu} from "lucide-react"; +import { BadgeCent, Boxes, CalendarPlus, Cloud, Menu } from "lucide-react"; import { Provider, useDispatch, useSelector } from "react-redux"; import { toggleMenu } from "./store/menu.ts"; import store from "./store/index.ts"; diff --git a/app/src/assets/chat.less b/app/src/assets/chat.less index fdc265f..8aede1e 100644 --- a/app/src/assets/chat.less +++ b/app/src/assets/chat.less @@ -13,6 +13,7 @@ .scroll-action { position: absolute; + z-index: 64; bottom: 112px; right: 36px; opacity: 0; @@ -31,6 +32,15 @@ display: flex; gap: 6px; flex-direction: column; + max-width: calc(100vw - 64px); + + pre { + scrollbar-width: thin; + + &::-webkit-scrollbar { + height: 6px; + } + } &:last-child { animation: FlexInAnimationFromBottom 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275) 0s 1 normal forwards running; diff --git a/app/src/assets/navbar.less b/app/src/assets/navbar.less index 7e41515..11aff5a 100644 --- a/app/src/assets/navbar.less +++ b/app/src/assets/navbar.less @@ -26,6 +26,10 @@ height: 40px; cursor: pointer; } + + button { + white-space: nowrap; + } } .avatar { diff --git a/app/src/components/Markdown.tsx b/app/src/components/Markdown.tsx index 4267e6c..3c442b0 100644 --- a/app/src/components/Markdown.tsx +++ b/app/src/components/Markdown.tsx @@ -1,6 +1,8 @@ -import { Light as SyntaxHighlighter } from "react-syntax-highlighter"; +import { LightAsync as SyntaxHighlighter } from "react-syntax-highlighter"; import { atomOneDark as style } from "react-syntax-highlighter/dist/esm/styles/hljs"; import ReactMarkdown from "react-markdown"; +import remarkMath from "remark-math"; +import rehypeKatex from "rehype-katex"; import "../assets/markdown/all.less"; type MarkdownProps = { @@ -11,6 +13,8 @@ type MarkdownProps = { function Markdown({ children, className }: MarkdownProps) { return (
- {(message.quota < 0 ? 0 : message.quota).toFixed(2)} + + {(message.quota < 0 ? 0 : message.quota).toFixed(2)} +
diff --git a/app/src/components/ui/select.tsx b/app/src/components/ui/select.tsx index c55b92a..be6248e 100644 --- a/app/src/components/ui/select.tsx +++ b/app/src/components/ui/select.tsx @@ -1,14 +1,14 @@ -import * as React from "react" -import * as SelectPrimitive from "@radix-ui/react-select" -import { Check, ChevronDown } from "lucide-react" +import * as React from "react"; +import * as SelectPrimitive from "@radix-ui/react-select"; +import { Check, ChevronDown } from "lucide-react"; -import { cn } from "./lib/utils" +import { cn } from "./lib/utils"; -const Select = SelectPrimitive.Root +const Select = SelectPrimitive.Root; -const SelectGroup = SelectPrimitive.Group +const SelectGroup = SelectPrimitive.Group; -const SelectValue = SelectPrimitive.Value +const SelectValue = SelectPrimitive.Value; const SelectTrigger = React.forwardRef< React.ElementRef, @@ -18,7 +18,7 @@ const SelectTrigger = React.forwardRef< ref={ref} className={cn( "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", - className + className, )} {...props} > @@ -27,8 +27,8 @@ const SelectTrigger = React.forwardRef< -)) -SelectTrigger.displayName = SelectPrimitive.Trigger.displayName +)); +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; const SelectContent = React.forwardRef< React.ElementRef, @@ -41,7 +41,7 @@ const SelectContent = React.forwardRef< "relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", - className + className, )} position={position} {...props} @@ -50,15 +50,15 @@ const SelectContent = React.forwardRef< className={cn( "p-1", position === "popper" && - "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]" + "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]", )} > {children} -)) -SelectContent.displayName = SelectPrimitive.Content.displayName +)); +SelectContent.displayName = SelectPrimitive.Content.displayName; const SelectLabel = React.forwardRef< React.ElementRef, @@ -69,8 +69,8 @@ const SelectLabel = React.forwardRef< className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)} {...props} /> -)) -SelectLabel.displayName = SelectPrimitive.Label.displayName +)); +SelectLabel.displayName = SelectPrimitive.Label.displayName; const SelectItem = React.forwardRef< React.ElementRef, @@ -80,7 +80,7 @@ const SelectItem = React.forwardRef< ref={ref} className={cn( "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", - className + className, )} {...props} > @@ -92,8 +92,8 @@ const SelectItem = React.forwardRef< {children} -)) -SelectItem.displayName = SelectPrimitive.Item.displayName +)); +SelectItem.displayName = SelectPrimitive.Item.displayName; const SelectSeparator = React.forwardRef< React.ElementRef, @@ -104,8 +104,8 @@ const SelectSeparator = React.forwardRef< className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} /> -)) -SelectSeparator.displayName = SelectPrimitive.Separator.displayName +)); +SelectSeparator.displayName = SelectPrimitive.Separator.displayName; export { Select, @@ -116,4 +116,4 @@ export { SelectLabel, SelectItem, SelectSeparator, -} +}; diff --git a/app/src/conf.ts b/app/src/conf.ts index c017846..c03d330 100644 --- a/app/src/conf.ts +++ b/app/src/conf.ts @@ -1,6 +1,6 @@ import axios from "axios"; -export const version: string = "2.3.0"; +export const version: string = "2.4.0"; export const deploy: boolean = true; export let rest_api: string = "http://localhost:8094"; export let ws_api: string = "ws://localhost:8094"; diff --git a/app/src/conversation/addition.ts b/app/src/conversation/addition.ts index 971cd27..d0d6446 100644 --- a/app/src/conversation/addition.ts +++ b/app/src/conversation/addition.ts @@ -15,12 +15,12 @@ type SubscriptionResponse = { status: boolean; is_subscribed: boolean; expired: number; -} +}; type BuySubscriptionResponse = { status: boolean; error: string; -} +}; export async function buyQuota(quota: number): Promise { try { diff --git a/app/src/i18n.ts b/app/src/i18n.ts index 71b68d6..19877ea 100644 --- a/app/src/i18n.ts +++ b/app/src/i18n.ts @@ -108,27 +108,29 @@ const resources = { "pro-dalle": "50 quotas per day", "pro-service": "Priority Service Support", "pro-thread": "Concurrency Increase", - "current": "Current Plan", - "upgrade": "Upgrade", - "renew": "Renew", + current: "Current Plan", + upgrade: "Upgrade", + renew: "Renew", "cannot-select": "Cannot Select", "select-time": "Select Subscription Time", - "price": "Price {{price}} CNY", - "expired": "Your Pro subscription will expire in {{expired}} days", + price: "Price {{price}} CNY", + expired: "Your Pro subscription will expire in {{expired}} days", time: { 1: "1 Month", 3: "3 Months", 6: "6 Months", 12: "1 Year", }, - "success": "Subscribe success", - "success-prompt": "You have successfully subscribed to {{month}} months of Pro.", - "failed": "Subscribe failed", - "failed-prompt": "Failed to subscribe, please make sure you have enough balance, you will soon jump to deeptrain wallet to pay balance.", + success: "Subscribe success", + "success-prompt": + "You have successfully subscribed to {{month}} months of Pro.", + failed: "Subscribe failed", + "failed-prompt": + "Failed to subscribe, please make sure you have enough balance, you will soon jump to deeptrain wallet to pay balance.", }, - "cancel": "Cancel", - "confirm": "Confirm", - "percent": "{{cent}}0%", + cancel: "Cancel", + confirm: "Confirm", + percent: "{{cent}}0%", }, }, cn: { @@ -227,27 +229,28 @@ const resources = { "pro-dalle": "每日 50 次绘图", "pro-service": "优先服务支持", "pro-thread": "并发数提升", - "current": "当前计划", - "upgrade": "升级", - "renew": "续费", + current: "当前计划", + upgrade: "升级", + renew: "续费", "cannot-select": "无法选择", "select-time": "选择订阅时间", - "price": "价格 {{price}} 元", - "expired": "您的专业版订阅还有 {{expired}} 天到期", + price: "价格 {{price}} 元", + expired: "您的专业版订阅还有 {{expired}} 天到期", time: { 1: "1个月", 3: "3个月", 6: "半年", 12: "1年", }, - "success": "订阅成功", + success: "订阅成功", "success-prompt": "您已成功订阅 {{month}} 月专业版。", - "failed": "订阅失败", - "failed-prompt": "订阅失败,请确保您有足够的余额,您即将跳转到 deeptrain 钱包支付余额。", + failed: "订阅失败", + "failed-prompt": + "订阅失败,请确保您有足够的余额,您即将跳转到 deeptrain 钱包支付余额。", }, - "cancel": "取消", - "confirm": "确认", - "percent": "{{cent}}折", + cancel: "取消", + confirm: "确认", + percent: "{{cent}}折", }, }, }; diff --git a/app/src/routes/Quota.tsx b/app/src/routes/Quota.tsx index 3d9fb86..287f211 100644 --- a/app/src/routes/Quota.tsx +++ b/app/src/routes/Quota.tsx @@ -20,7 +20,8 @@ import { Cloud, ExternalLink, HardDriveDownload, - HardDriveUpload, Info, + HardDriveUpload, + Info, Plus, } from "lucide-react"; import { Input } from "../components/ui/input.tsx"; diff --git a/app/src/routes/Subscription.tsx b/app/src/routes/Subscription.tsx index 0813c09..cb2e984 100644 --- a/app/src/routes/Subscription.tsx +++ b/app/src/routes/Subscription.tsx @@ -1,15 +1,24 @@ import { dialogSelector, expiredSelector, - isSubscribedSelector, refreshSubscription, + isSubscribedSelector, + refreshSubscription, refreshSubscriptionTask, - setDialog + setDialog, } from "../store/subscription.ts"; -import {Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger} from "../components/ui/dialog.tsx"; -import {useDispatch, useSelector} from "react-redux"; -import {useTranslation} from "react-i18next"; -import {useToast} from "../components/ui/use-toast.ts"; -import React, {useEffect} from "react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "../components/ui/dialog.tsx"; +import { useDispatch, useSelector } from "react-redux"; +import { useTranslation } from "react-i18next"; +import { useToast } from "../components/ui/use-toast.ts"; +import React, { useEffect } from "react"; import "../assets/subscription.less"; import { Calendar, @@ -19,13 +28,21 @@ import { ImagePlus, LifeBuoy, MessageSquare, - MessagesSquare, Plus, - ServerCrash, Webhook + MessagesSquare, + Plus, + ServerCrash, + Webhook, } from "lucide-react"; -import {Button} from "../components/ui/button.tsx"; -import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "../components/ui/select.tsx"; -import {Badge} from "../components/ui/badge.tsx"; -import {buySubscription} from "../conversation/addition.ts"; +import { Button } from "../components/ui/button.tsx"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "../components/ui/select.tsx"; +import { Badge } from "../components/ui/badge.tsx"; +import { buySubscription } from "../conversation/addition.ts"; function calc_prize(month: number): number { if (month >= 12) { @@ -36,29 +53,31 @@ function calc_prize(month: number): number { type UpgradeProps = { children: React.ReactNode; -} +}; -async function callBuyAction(t: any, toast: any, month: number): Promise { - const res = await buySubscription(month); - if (res.status) { - toast({ - title: t("sub.success"), - description: t("sub.success-prompt", { - month, - }), - }); - } else { - toast({ - title: t("sub.failed"), - description: t("sub.failed-prompt"), - }); - setTimeout(() => { - window.open( - "https://deeptrain.lightxi.com/home/wallet", - ); - }, 2000); - } - return res.status; +async function callBuyAction( + t: any, + toast: any, + month: number, +): Promise { + const res = await buySubscription(month); + if (res.status) { + toast({ + title: t("sub.success"), + description: t("sub.success-prompt", { + month, + }), + }); + } else { + toast({ + title: t("sub.failed"), + description: t("sub.failed-prompt"), + }); + setTimeout(() => { + window.open("https://deeptrain.lightxi.com/home/wallet"); + }, 2000); + } + return res.status; } function Upgrade({ children }: UpgradeProps) { const { t } = useTranslation(); @@ -72,12 +91,10 @@ function Upgrade({ children }: UpgradeProps) { {children} - { t('sub.select-time') } + {t("sub.select-time")}
- setMonth(parseInt(value))}> @@ -87,32 +104,36 @@ function Upgrade({ children }: UpgradeProps) { {t(`sub.time.6`)} {t(`sub.time.12`)} - {t(`percent`, { cent: 9 })} + + {t(`percent`, { cent: 9 })} + -

{ t('sub.price', { price: calc_prize(month) }) }

+

+ {t("sub.price", { price: calc_prize(month) })} +

- - +
- ) + ); } function Subscription() { @@ -135,24 +156,37 @@ function Subscription() { {t("sub.dialog-title")}
- { - subscription && ( -
- - {t("sub.expired", { expired })} -
- ) - } + {subscription && ( +
+ + {t("sub.expired", { expired })} +
+ )}
{t("sub.free")}
{t("sub.free-price")}
-
{t("sub.free-gpt3")}
-
{t("sub.free-dalle")}
-
{t("sub.free-web")}
-
{t("sub.free-conversation")}
-
{t("sub.free-api")}
+
+ + {t("sub.free-gpt3")} +
+
+ + {t("sub.free-dalle")} +
+
+ + {t("sub.free-web")} +
+
+ + {t("sub.free-conversation")} +
+
+ + {t("sub.free-api")} +