From 4d6b981a54d676a4d70b767418ec842bb4f3114a Mon Sep 17 00:00:00 2001 From: butterfly Date: Tue, 26 Mar 2024 11:43:55 +0800 Subject: [PATCH 001/352] bugfix: Delete the escapeDollarNumber function, which causes errors in rendering a latex string --- app/components/markdown.tsx | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 7c70fe1a5..c6290d8e0 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -99,23 +99,6 @@ export function PreCode(props: { children: any }) { ); } -function escapeDollarNumber(text: string) { - let escapedText = ""; - - for (let i = 0; i < text.length; i += 1) { - let char = text[i]; - const nextChar = text[i + 1] || " "; - - if (char === "$" && nextChar >= "0" && nextChar <= "9") { - char = "\\$"; - } - - escapedText += char; - } - - return escapedText; -} - function escapeBrackets(text: string) { const pattern = /(```[\s\S]*?```|`.*?`)|\\\[([\s\S]*?[^\\])\\\]|\\\((.*?)\\\)/g; @@ -136,7 +119,7 @@ function escapeBrackets(text: string) { function _MarkDownContent(props: { content: string }) { const escapedContent = useMemo( - () => escapeBrackets(escapeDollarNumber(props.content)), + () => escapeBrackets(props.content), [props.content], ); From 1ef2aa35e910dcc587094909dc5ff114d2252c93 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Tue, 6 Aug 2024 18:03:27 +0800 Subject: [PATCH 002/352] feat: jest --- jest.config.ts | 21 + jest.setup.ts | 2 + package.json | 156 +-- test/sum-module.test.ts | 9 + yarn.lock | 1998 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 2083 insertions(+), 103 deletions(-) create mode 100644 jest.config.ts create mode 100644 jest.setup.ts create mode 100644 test/sum-module.test.ts diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 000000000..cd25e8e7a --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,21 @@ +import type { Config } from "jest"; +import nextJest from "next/jest.js"; + +const createJestConfig = nextJest({ + // Provide the path to your Next.js app to load next.config.js and .env files in your test environment + dir: "./", +}); + +// Add any custom config to be passed to Jest +const config: Config = { + coverageProvider: "v8", + testEnvironment: "jsdom", + testMatch: ["**/*.test.js", "**/*.test.ts", "**/*.test.jsx", "**/*.test.tsx"], + setupFilesAfterEnv: ["/jest.setup.ts"], + moduleNameMapper: { + "^@/(.*)$": "/$1", + }, +}; + +// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async +export default createJestConfig(config); diff --git a/jest.setup.ts b/jest.setup.ts new file mode 100644 index 000000000..ee6ccea1a --- /dev/null +++ b/jest.setup.ts @@ -0,0 +1,2 @@ +// Learn more: https://github.com/testing-library/jest-dom +import "@testing-library/jest-dom"; diff --git a/package.json b/package.json index eb0a5ef67..91be0544e 100644 --- a/package.json +++ b/package.json @@ -1,76 +1,84 @@ { - "name": "nextchat", - "private": false, - "license": "mit", - "scripts": { - "mask": "npx tsx app/masks/build.ts", - "mask:watch": "npx watch \"yarn mask\" app/masks", - "dev": "concurrently -r \"yarn run mask:watch\" \"next dev\"", - "build": "yarn mask && cross-env BUILD_MODE=standalone next build", - "start": "next start", - "lint": "next lint", - "export": "yarn mask && cross-env BUILD_MODE=export BUILD_APP=1 next build", - "export:dev": "concurrently -r \"yarn mask:watch\" \"cross-env BUILD_MODE=export BUILD_APP=1 next dev\"", - "app:dev": "concurrently -r \"yarn mask:watch\" \"yarn tauri dev\"", - "app:build": "yarn mask && yarn tauri build", - "prompts": "node ./scripts/fetch-prompts.mjs", - "prepare": "husky install", - "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev" - }, - "dependencies": { - "@fortaine/fetch-event-source": "^3.0.6", - "@hello-pangea/dnd": "^16.5.0", - "@next/third-parties": "^14.1.0", - "@svgr/webpack": "^6.5.1", - "@vercel/analytics": "^0.1.11", - "@vercel/speed-insights": "^1.0.2", - "emoji-picker-react": "^4.9.2", - "fuse.js": "^7.0.0", - "heic2any": "^0.0.4", - "html-to-image": "^1.11.11", - "lodash-es": "^4.17.21", - "mermaid": "^10.6.1", - "nanoid": "^5.0.3", - "next": "^14.1.1", - "node-fetch": "^3.3.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-markdown": "^8.0.7", - "react-router-dom": "^6.15.0", - "rehype-highlight": "^6.0.0", - "rehype-katex": "^6.0.3", - "remark-breaks": "^3.0.2", - "remark-gfm": "^3.0.1", - "remark-math": "^5.1.1", - "sass": "^1.59.2", - "spark-md5": "^3.0.2", - "use-debounce": "^9.0.4", - "zustand": "^4.3.8" - }, - "devDependencies": { - "@tauri-apps/cli": "1.5.11", - "@types/lodash-es": "^4.17.12", - "@types/node": "^20.11.30", - "@types/react": "^18.2.70", - "@types/react-dom": "^18.2.7", - "@types/react-katex": "^3.0.0", - "@types/spark-md5": "^3.0.4", - "concurrently": "^8.2.2", - "cross-env": "^7.0.3", - "eslint": "^8.49.0", - "eslint-config-next": "13.4.19", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^5.1.3", - "husky": "^8.0.0", - "lint-staged": "^13.2.2", - "prettier": "^3.0.2", - "tsx": "^4.16.0", - "typescript": "5.2.2", - "watch": "^1.0.2", - "webpack": "^5.88.1" - }, - "resolutions": { - "lint-staged/yaml": "^2.2.2" - }, - "packageManager": "yarn@1.22.19" + "name": "nextchat", + "private": false, + "license": "mit", + "scripts": { + "mask": "npx tsx app/masks/build.ts", + "mask:watch": "npx watch \"yarn mask\" app/masks", + "dev": "concurrently -r \"yarn run mask:watch\" \"next dev\"", + "build": "yarn mask && cross-env BUILD_MODE=standalone next build", + "start": "next start", + "lint": "next lint", + "export": "yarn mask && cross-env BUILD_MODE=export BUILD_APP=1 next build", + "export:dev": "concurrently -r \"yarn mask:watch\" \"cross-env BUILD_MODE=export BUILD_APP=1 next dev\"", + "app:dev": "concurrently -r \"yarn mask:watch\" \"yarn tauri dev\"", + "app:build": "yarn mask && yarn tauri build", + "prompts": "node ./scripts/fetch-prompts.mjs", + "prepare": "husky install", + "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev", + "test": "jest --watch", + "test:ci": "jest --ci" + }, + "dependencies": { + "@fortaine/fetch-event-source": "^3.0.6", + "@hello-pangea/dnd": "^16.5.0", + "@next/third-parties": "^14.1.0", + "@svgr/webpack": "^6.5.1", + "@vercel/analytics": "^0.1.11", + "@vercel/speed-insights": "^1.0.2", + "emoji-picker-react": "^4.9.2", + "fuse.js": "^7.0.0", + "heic2any": "^0.0.4", + "html-to-image": "^1.11.11", + "lodash-es": "^4.17.21", + "mermaid": "^10.6.1", + "nanoid": "^5.0.3", + "next": "^14.1.1", + "node-fetch": "^3.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-markdown": "^8.0.7", + "react-router-dom": "^6.15.0", + "rehype-highlight": "^6.0.0", + "rehype-katex": "^6.0.3", + "remark-breaks": "^3.0.2", + "remark-gfm": "^3.0.1", + "remark-math": "^5.1.1", + "sass": "^1.59.2", + "spark-md5": "^3.0.2", + "use-debounce": "^9.0.4", + "zustand": "^4.3.8" + }, + "devDependencies": { + "@tauri-apps/cli": "1.5.11", + "@testing-library/jest-dom": "^6.4.8", + "@testing-library/react": "^16.0.0", + "@types/jest": "^29.5.12", + "@types/lodash-es": "^4.17.12", + "@types/node": "^20.11.30", + "@types/react": "^18.2.70", + "@types/react-dom": "^18.2.7", + "@types/react-katex": "^3.0.0", + "@types/spark-md5": "^3.0.4", + "concurrently": "^8.2.2", + "cross-env": "^7.0.3", + "eslint": "^8.49.0", + "eslint-config-next": "13.4.19", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.1.3", + "husky": "^8.0.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "lint-staged": "^13.2.2", + "prettier": "^3.0.2", + "ts-node": "^10.9.2", + "tsx": "^4.16.0", + "typescript": "5.2.2", + "watch": "^1.0.2", + "webpack": "^5.88.1" + }, + "resolutions": { + "lint-staged/yaml": "^2.2.2" + }, + "packageManager": "yarn@1.22.19" } diff --git a/test/sum-module.test.ts b/test/sum-module.test.ts new file mode 100644 index 000000000..4773d19eb --- /dev/null +++ b/test/sum-module.test.ts @@ -0,0 +1,9 @@ +function sum(a: number, b: number) { + return a + b; +} + +describe("sum module", () => { + test("adds 1 + 2 to equal 3", () => { + expect(sum(1, 2)).toBe(3); + }); +}); diff --git a/yarn.lock b/yarn.lock index 793c845d7..39dbaaeb4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,11 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== +"@adobe/css-tools@^4.4.0": + version "4.4.0" + resolved "https://registry.npmmirror.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63" + integrity sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ== + "@ampproject/remapping@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" @@ -22,6 +27,14 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7": + version "7.24.7" + resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== + dependencies: + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" + "@babel/code-frame@^7.22.13": version "7.22.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" @@ -35,6 +48,32 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== +"@babel/compat-data@^7.25.2": + version "7.25.2" + resolved "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.25.2.tgz#e41928bd33475305c586f6acbbb7e3ade7a6f7f5" + integrity sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": + version "7.25.2" + resolved "https://registry.npmmirror.com/@babel/core/-/core-7.25.2.tgz#ed8eec275118d7613e77a352894cd12ded8eba77" + integrity sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.25.0" + "@babel/helper-compilation-targets" "^7.25.2" + "@babel/helper-module-transforms" "^7.25.2" + "@babel/helpers" "^7.25.0" + "@babel/parser" "^7.25.0" + "@babel/template" "^7.25.0" + "@babel/traverse" "^7.25.2" + "@babel/types" "^7.25.2" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/core@^7.19.6": version "7.21.3" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e" @@ -76,6 +115,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.25.0", "@babel/generator@^7.7.2": + version "7.25.0" + resolved "https://registry.npmmirror.com/@babel/generator/-/generator-7.25.0.tgz#f858ddfa984350bc3d3b7f125073c9af6988f18e" + integrity sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw== + dependencies: + "@babel/types" "^7.25.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -102,6 +151,17 @@ lru-cache "^5.1.1" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.25.2": + version "7.25.2" + resolved "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz#e1d9410a90974a3a5a66e84ff55ef62e3c02d06c" + integrity sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw== + dependencies: + "@babel/compat-data" "^7.25.2" + "@babel/helper-validator-option" "^7.24.8" + browserslist "^4.23.1" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9" @@ -197,6 +257,14 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-module-imports@^7.24.7": + version "7.24.7" + resolved "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b" + integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2": version "7.21.2" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" @@ -211,6 +279,16 @@ "@babel/traverse" "^7.21.2" "@babel/types" "^7.21.2" +"@babel/helper-module-transforms@^7.25.2": + version "7.25.2" + resolved "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz#ee713c29768100f2776edf04d4eb23b8d27a66e6" + integrity sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ== + dependencies: + "@babel/helper-module-imports" "^7.24.7" + "@babel/helper-simple-access" "^7.24.7" + "@babel/helper-validator-identifier" "^7.24.7" + "@babel/traverse" "^7.25.2" + "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" @@ -223,6 +301,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== +"@babel/helper-plugin-utils@^7.24.7": + version "7.24.8" + resolved "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz#94ee67e8ec0e5d44ea7baeb51e571bd26af07878" + integrity sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg== + "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" @@ -252,6 +335,14 @@ dependencies: "@babel/types" "^7.20.2" +"@babel/helper-simple-access@^7.24.7": + version "7.24.7" + resolved "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz#bcade8da3aec8ed16b9c4953b74e506b51b5edb3" + integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers@^7.20.0": version "7.20.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" @@ -283,6 +374,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.24.8": + version "7.24.8" + resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" + integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" @@ -293,11 +389,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== +"@babel/helper-validator-identifier@^7.24.7": + version "7.24.7" + resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" + integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== + "@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== +"@babel/helper-validator-option@^7.24.8": + version "7.24.8" + resolved "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz#3725cdeea8b480e86d34df15304806a06975e33d" + integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q== + "@babel/helper-wrap-function@^7.18.9": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" @@ -317,6 +423,14 @@ "@babel/traverse" "^7.21.0" "@babel/types" "^7.21.0" +"@babel/helpers@^7.25.0": + version "7.25.0" + resolved "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.25.0.tgz#e69beb7841cb93a6505531ede34f34e6a073650a" + integrity sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw== + dependencies: + "@babel/template" "^7.25.0" + "@babel/types" "^7.25.0" + "@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" @@ -335,6 +449,23 @@ chalk "^2.4.2" js-tokens "^4.0.0" +"@babel/highlight@^7.24.7": + version "7.24.7" + resolved "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" + integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.7" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.0", "@babel/parser@^7.25.3": + version "7.25.3" + resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.25.3.tgz#91fb126768d944966263f0657ab222a642b82065" + integrity sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw== + dependencies: + "@babel/types" "^7.25.2" + "@babel/parser@^7.20.7", "@babel/parser@^7.21.3": version "7.21.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" @@ -497,7 +628,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.12.13": +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.npmmirror.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== @@ -532,6 +670,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.npmmirror.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" @@ -546,7 +691,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.24.7" + resolved "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz#39a1fa4a7e3d3d7f34e2acc6be585b718d30e02d" + integrity sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -560,7 +712,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.10.4": +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== @@ -595,7 +747,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-top-level-await@^7.14.5": +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== @@ -609,6 +761,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.24.7" + resolved "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz#58d458271b4d3b6bb27ee6ac9525acbb259bad1c" + integrity sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-arrow-functions@^7.18.6": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" @@ -1035,7 +1194,7 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.21.0": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.21.0": version "7.25.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.0.tgz#3af9a91c1b739c569d5d80cc917280919c544ecb" integrity sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw== @@ -1060,6 +1219,15 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" +"@babel/template@^7.25.0", "@babel/template@^7.3.3": + version "7.25.0" + resolved "https://registry.npmmirror.com/@babel/template/-/template-7.25.0.tgz#e733dc3134b4fede528c15bc95e89cb98c52592a" + integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/parser" "^7.25.0" + "@babel/types" "^7.25.0" + "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3": version "7.23.2" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" @@ -1076,6 +1244,28 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.24.7", "@babel/traverse@^7.25.2": + version "7.25.3" + resolved "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.25.3.tgz#f1b901951c83eda2f3e29450ce92743783373490" + integrity sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.25.0" + "@babel/parser" "^7.25.3" + "@babel/template" "^7.25.0" + "@babel/types" "^7.25.2" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.3.3": + version "7.25.2" + resolved "https://registry.npmmirror.com/@babel/types/-/types-7.25.2.tgz#55fb231f7dc958cd69ea141a4c2997e819646125" + integrity sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q== + dependencies: + "@babel/helper-string-parser" "^7.24.8" + "@babel/helper-validator-identifier" "^7.24.7" + to-fast-properties "^2.0.0" + "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.4.4": version "7.21.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" @@ -1094,11 +1284,23 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.npmmirror.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + "@braintree/sanitize-url@^6.0.1": version "6.0.4" resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz#923ca57e173c6b232bbbb07347b1be982f03e783" integrity sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.npmmirror.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@esbuild/aix-ppc64@0.21.5": version "0.21.5" resolved "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" @@ -1283,6 +1485,214 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.npmmirror.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": + version "0.1.3" + resolved "https://registry.npmmirror.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.npmmirror.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.npmmirror.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== + dependencies: + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.npmmirror.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== + dependencies: + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.npmmirror.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.npmmirror.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== + dependencies: + expect "^29.7.0" + jest-snapshot "^29.7.0" + +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.npmmirror.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== + dependencies: + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.npmmirror.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" + +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.npmmirror.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^6.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.npmmirror.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.npmmirror.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.npmmirror.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== + dependencies: + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.npmmirror.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== + dependencies: + "@jest/test-result" "^29.7.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + slash "^3.0.0" + +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.npmmirror.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.npmmirror.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" @@ -1309,16 +1719,35 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + "@jridgewell/source-map@^0.3.3": version "0.3.3" resolved "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda" @@ -1332,6 +1761,27 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" @@ -1440,6 +1890,25 @@ resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.npmmirror.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.npmmirror.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@svgr/babel-plugin-add-jsx-attribute@^6.5.1": version "6.5.1" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz#74a5d648bd0347bda99d82409d87b8ca80b9a1ba" @@ -1619,11 +2088,90 @@ "@tauri-apps/cli-win32-ia32-msvc" "1.5.11" "@tauri-apps/cli-win32-x64-msvc" "1.5.11" +"@testing-library/jest-dom@^6.4.8": + version "6.4.8" + resolved "https://registry.npmmirror.com/@testing-library/jest-dom/-/jest-dom-6.4.8.tgz#9c435742b20c6183d4e7034f2b329d562c079daa" + integrity sha512-JD0G+Zc38f5MBHA4NgxQMR5XtO5Jx9g86jqturNTt2WUfRmLDIY7iKkWHDCCTiDuFMre6nxAD5wHw9W5kI4rGw== + dependencies: + "@adobe/css-tools" "^4.4.0" + "@babel/runtime" "^7.9.2" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.6.3" + lodash "^4.17.21" + redent "^3.0.0" + +"@testing-library/react@^16.0.0": + version "16.0.0" + resolved "https://registry.npmmirror.com/@testing-library/react/-/react-16.0.0.tgz#0a1e0c7a3de25841c3591b8cb7fb0cf0c0a27321" + integrity sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ== + dependencies: + "@babel/runtime" "^7.12.5" + +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.npmmirror.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.npmmirror.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.npmmirror.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.npmmirror.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/babel__core@^7.1.14": + version "7.20.5" + resolved "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.8" + resolved "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.20.6" + resolved "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" + integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== + dependencies: + "@babel/types" "^7.20.7" + "@types/d3-scale-chromatic@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#103124777e8cdec85b20b51fd3397c682ee1e954" @@ -1669,6 +2217,13 @@ resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== +"@types/graceful-fs@^4.1.3": + version "4.1.9" + resolved "https://registry.npmmirror.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== + dependencies: + "@types/node" "*" + "@types/hast@^2.0.0": version "2.3.4" resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" @@ -1684,6 +2239,42 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.6" + resolved "https://registry.npmmirror.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.npmmirror.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.npmmirror.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^29.5.12": + version "29.5.12" + resolved "https://registry.npmmirror.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544" + integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/jsdom@^20.0.0": + version "20.0.1" + resolved "https://registry.npmmirror.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" + integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^7.0.0" + "@types/json-schema@*", "@types/json-schema@^7.0.8": version "7.0.12" resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" @@ -1778,6 +2369,16 @@ resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.4.tgz#c1221d63c069d95aba0c06a765b80661cacc12bf" integrity sha512-qtOaDz+IXiNndPgYb6t1YoutnGvFRtWSNzpVjkAPCfB2UzTyybuD4Tjgs7VgRawum3JnJNRwNQd4N//SvrHg1Q== +"@types/stack-utils@^2.0.0": + version "2.0.3" + resolved "https://registry.npmmirror.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + +"@types/tough-cookie@*": + version "4.0.5" + resolved "https://registry.npmmirror.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== + "@types/unist@*", "@types/unist@^2.0.0": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" @@ -1788,6 +2389,18 @@ resolved "https://registry.npmmirror.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.npmmirror.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.32" + resolved "https://registry.npmmirror.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== + dependencies: + "@types/yargs-parser" "*" + "@typescript-eslint/parser@^5.4.2 || ^6.0.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.4.0.tgz#47e7c6e22ff1248e8675d95f488890484de67600" @@ -1974,6 +2587,19 @@ resolved "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.npmmirror.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +acorn-globals@^7.0.0: + version "7.0.1" + resolved "https://registry.npmmirror.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" + integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== + dependencies: + acorn "^8.1.0" + acorn-walk "^8.0.2" + acorn-import-assertions@^1.9.0: version "1.9.0" resolved "https://registry.npmmirror.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" @@ -1984,6 +2610,18 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== +acorn-walk@^8.0.2, acorn-walk@^8.1.1: + version "8.3.3" + resolved "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" + integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== + dependencies: + acorn "^8.11.0" + +acorn@^8.1.0, acorn@^8.11.0, acorn@^8.4.1, acorn@^8.8.1: + version "8.12.1" + resolved "https://registry.npmmirror.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + acorn@^8.7.1, acorn@^8.8.2: version "8.9.0" resolved "https://registry.npmmirror.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" @@ -1994,6 +2632,13 @@ acorn@^8.9.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== +agent-base@6: + version "6.0.2" + resolved "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -2017,7 +2662,7 @@ ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-escapes@^4.3.0: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -2048,12 +2693,17 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + ansi-styles@^6.0.0: version "6.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -anymatch@~3.1.2: +anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -2061,11 +2711,30 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-query@^5.0.0: + version "5.3.0" + resolved "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + aria-query@^5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" @@ -2138,6 +2807,11 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + available-typed-arrays@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" @@ -2155,6 +2829,40 @@ axobject-query@^3.1.1: dependencies: deep-equal "^2.0.5" +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== + dependencies: + "@jest/transform" "^29.7.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.6.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.npmmirror.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.npmmirror.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + babel-plugin-polyfill-corejs2@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" @@ -2179,6 +2887,32 @@ babel-plugin-polyfill-regenerator@^0.4.1: dependencies: "@babel/helper-define-polyfill-provider" "^0.3.3" +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.npmmirror.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.npmmirror.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== + dependencies: + babel-plugin-jest-hoist "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + bail@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" @@ -2234,6 +2968,23 @@ browserslist@^4.21.3, browserslist@^4.21.5: node-releases "^2.0.8" update-browserslist-db "^1.0.10" +browserslist@^4.23.1: + version "4.23.3" + resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" + integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== + dependencies: + caniuse-lite "^1.0.30001646" + electron-to-chromium "^1.5.4" + node-releases "^2.0.18" + update-browserslist-db "^1.1.0" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -2259,6 +3010,11 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + camelcase@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" @@ -2269,6 +3025,11 @@ caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001503, caniuse-lite@^1.0.300015 resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz#809bc25f3f5027ceb33142a7d6c40759d7a901eb" integrity sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA== +caniuse-lite@^1.0.30001646: + version "1.0.30001649" + resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001649.tgz#3ec700309ca0da2b0d3d5fb03c411b191761c992" + integrity sha512-fJegqZZ0ZX8HOWr6rcafGr72+xcgJKI9oWfDW5DrD7ExUtgZC7a7R7ZYmZqplh7XDocFdGeIFn7roAxhOeYrPQ== + ccount@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" @@ -2288,6 +3049,14 @@ chalk@^2.0.0, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^4.0.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -2296,6 +3065,11 @@ chalk@^4.0.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + character-entities@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" @@ -2321,6 +3095,16 @@ chrome-trace-event@^1.0.2: resolved "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.npmmirror.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cjs-module-lexer@^1.0.0: + version "1.3.1" + resolved "https://registry.npmmirror.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c" + integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -2363,6 +3147,16 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" +co@^4.6.0: + version "4.6.0" + resolved "https://registry.npmmirror.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.2" + resolved "https://registry.npmmirror.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -2392,6 +3186,13 @@ colorette@^2.0.19: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + comma-separated-tokens@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" @@ -2442,6 +3243,11 @@ convert-source-map@^1.7.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + core-js-compat@^3.25.1: version "3.29.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b" @@ -2474,6 +3280,24 @@ cosmiconfig@^7.0.1: path-type "^4.0.0" yaml "^1.10.0" +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.npmmirror.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-env@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" @@ -2521,6 +3345,11 @@ css-what@^6.0.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.npmmirror.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + csso@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" @@ -2528,6 +3357,23 @@ csso@^4.2.0: dependencies: css-tree "^1.1.2" +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.npmmirror.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.npmmirror.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + csstype@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" @@ -2844,6 +3690,15 @@ data-uri-to-buffer@^4.0.0: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== +data-urls@^3.0.2: + version "3.0.2" + resolved "https://registry.npmmirror.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" + integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + date-fns@^2.30.0: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" @@ -2856,6 +3711,13 @@ dayjs@^1.11.7: resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== +debug@4, debug@^4.3.1: + version "4.3.6" + resolved "https://registry.npmmirror.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + dependencies: + ms "2.1.2" + debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -2870,6 +3732,11 @@ debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" +decimal.js@^10.4.2: + version "10.4.3" + resolved "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + decode-named-character-reference@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" @@ -2877,6 +3744,11 @@ decode-named-character-reference@^1.0.0: dependencies: character-entities "^2.0.0" +dedent@^1.0.0: + version "1.5.3" + resolved "https://registry.npmmirror.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" + integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== + deep-equal@^2.0.5: version "2.2.0" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.0.tgz#5caeace9c781028b9ff459f33b779346637c43e6" @@ -2925,11 +3797,31 @@ delaunator@5: dependencies: robust-predicates "^3.0.0" -dequal@^2.0.0: +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +dequal@^2.0.0, dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.npmmirror.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.npmmirror.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.npmmirror.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + diff@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" @@ -2956,6 +3848,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.npmmirror.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" @@ -2970,6 +3867,13 @@ domelementtype@^2.0.1, domelementtype@^2.2.0: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + domhandler@^4.2.0, domhandler@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" @@ -3006,11 +3910,21 @@ electron-to-chromium@^1.4.431: resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.445.tgz#058d2c5f3a2981ab1a37440f5a5e42d15672aa6d" integrity sha512-++DB+9VK8SBJwC+X1zlMfJ1tMA3F0ipi39GdEp+x3cV2TyBihqAgad8cNMWtLDEkbH39nlDQP7PfGrDr3Dr7HA== +electron-to-chromium@^1.5.4: + version "1.5.5" + resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.5.tgz#03bfdf422bdd2c05ee2657efedde21264a1a566b" + integrity sha512-QR7/A7ZkMS8tZuoftC/jfqNkZLQO779SSW3YuZHP4eXpj3EffGLFcB/Xu9AAZQzLccTiCV+EmUo3ha4mQ9wnlA== + elkjs@^0.8.2: version "0.8.2" resolved "https://registry.npmmirror.com/elkjs/-/elkjs-0.8.2.tgz#c37763c5a3e24e042e318455e0147c912a7c248e" integrity sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.npmmirror.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + emoji-picker-react@^4.9.2: version "4.9.2" resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-4.9.2.tgz#5118c5e1028ce4a96c94eb7c9bef09d30b08742c" @@ -3180,11 +4094,21 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escalade@^3.1.2: + version "3.1.2" + resolved "https://registry.npmmirror.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -3195,6 +4119,17 @@ escape-string-regexp@^5.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== +escodegen@^2.0.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + eslint-config-next@13.4.19: version "13.4.19" resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.4.19.tgz#f46be9d4bd9e52755f846338456132217081d7f8" @@ -3394,6 +4329,11 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esquery@^1.4.2: version "1.5.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" @@ -3435,6 +4375,21 @@ exec-sh@^0.2.0: dependencies: merge "^1.2.0" +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + execa@^7.0.0: version "7.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" @@ -3450,6 +4405,22 @@ execa@^7.0.0: signal-exit "^3.0.7" strip-final-newline "^3.0.0" +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.npmmirror.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expect@^29.0.0, expect@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + extend@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -3476,7 +4447,7 @@ fast-glob@^3.2.11, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -3500,6 +4471,13 @@ fault@^2.0.0: dependencies: format "^0.2.0" +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.npmmirror.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + fetch-blob@^3.1.2, fetch-blob@^3.1.4: version "3.2.0" resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" @@ -3522,6 +4500,14 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -3555,6 +4541,15 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + format@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" @@ -3572,21 +4567,26 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@^2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" @@ -3626,7 +4626,12 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has "^1.0.3" has-symbols "^1.0.3" -get-stream@^6.0.1: +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.npmmirror.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -3682,7 +4687,7 @@ glob@7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.3: +glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -3799,6 +4804,13 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hast-util-from-dom@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/hast-util-from-dom/-/hast-util-from-dom-4.2.0.tgz#25836ddecc3cc0849d32749c2a7aec03e94b59a7" @@ -3904,11 +4916,45 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: dependencies: react-is "^16.7.0" +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + html-to-image@^1.11.11: version "1.11.11" resolved "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.11.tgz#c0f8a34dc9e4b97b93ff7ea286eb8562642ebbea" integrity sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA== +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.1: + version "5.0.1" + resolved "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + human-signals@^4.3.0: version "4.3.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" @@ -3919,7 +4965,7 @@ husky@^8.0.0: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== -iconv-lite@0.6: +iconv-lite@0.6, iconv-lite@0.6.3: version "0.6.3" resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -3944,6 +4990,14 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.npmmirror.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -4052,6 +5106,13 @@ is-core-module@^2.11.0, is-core-module@^2.9.0: dependencies: has "^1.0.3" +is-core-module@^2.13.0: + version "2.15.0" + resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.15.0.tgz#71c72ec5442ace7e76b306e9d48db361f22699ea" + integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA== + dependencies: + hasown "^2.0.2" + is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -4074,6 +5135,11 @@ is-fullwidth-code-point@^4.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -4113,6 +5179,11 @@ is-plain-obj@^4.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -4133,6 +5204,11 @@ is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" @@ -4193,6 +5269,411 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.2" + resolved "https://registry.npmmirror.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-instrument@^5.0.4: + version "5.2.1" + resolved "https://registry.npmmirror.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-instrument@^6.0.0: + version "6.0.3" + resolved "https://registry.npmmirror.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== + dependencies: + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + +istanbul-lib-report@^3.0.0: + version "3.0.1" + resolved "https://registry.npmmirror.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.npmmirror.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.7" + resolved "https://registry.npmmirror.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== + dependencies: + execa "^5.0.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^1.0.0" + is-generator-fn "^2.0.0" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + pretty-format "^29.7.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== + dependencies: + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + chalk "^4.0.0" + create-jest "^29.7.0" + exit "^0.1.2" + import-local "^3.0.2" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + yargs "^17.3.1" + +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== + dependencies: + detect-newline "^3.0.0" + +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + +jest-environment-jsdom@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f" + integrity sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/jsdom" "^20.0.0" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + jsdom "^20.0.0" + +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.npmmirror.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== + dependencies: + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== + dependencies: + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.7.0" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.npmmirror.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.npmmirror.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== + +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== + dependencies: + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" + +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.7.0" + jest-validate "^29.7.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== + dependencies: + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.7.0" + graceful-fs "^4.2.9" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + natural-compare "^1.4.0" + pretty-format "^29.7.0" + semver "^7.5.3" + +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== + dependencies: + "@jest/types" "^29.6.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.6.3" + leven "^3.1.0" + pretty-format "^29.7.0" + +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== + dependencies: + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.7.0" + string-length "^4.0.1" + jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -4202,11 +5683,39 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== + dependencies: + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" + import-local "^3.0.2" + jest-cli "^29.7.0" + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -4214,6 +5723,38 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsdom@^20.0.0: + version "20.0.3" + resolved "https://registry.npmmirror.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" + integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== + dependencies: + abab "^2.0.6" + acorn "^8.8.1" + acorn-globals "^7.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.2" + decimal.js "^10.4.2" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.2" + parse5 "^7.1.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + ws "^8.11.0" + xml-name-validator "^4.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -4246,7 +5787,7 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.2.2: +json5@^2.2.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -4278,6 +5819,11 @@ khroma@^2.0.0: resolved "https://registry.npmmirror.com/khroma/-/khroma-2.0.0.tgz#7577de98aed9f36c7a474c4d453d94c0d6c6588b" integrity sha512-2J8rDNlQWbtiNYThZRvmMv5yt44ZakX+Tz5ZIp/mN1pt4snn+m030Va5Z4v8xA0cQFDXBwO/8i42xL4QPsVk3g== +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + kleur@^4.0.3: version "4.1.5" resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" @@ -4305,6 +5851,11 @@ layout-base@^2.0.0: resolved "https://registry.npmmirror.com/layout-base/-/layout-base-2.0.1.tgz#d0337913586c90f9c2c075292069f5c2da5dd285" integrity sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg== +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.npmmirror.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -4361,6 +5912,13 @@ loader-runner@^4.2.0: resolved "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -4433,6 +5991,25 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.npmmirror.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.npmmirror.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + markdown-table@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd" @@ -4937,7 +6514,7 @@ mime-db@1.52.0: resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.27: +mime-types@^2.1.12, mime-types@^2.1.27: version "2.1.35" resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -4954,6 +6531,11 @@ mimic-fn@^4.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -5038,11 +6620,21 @@ node-fetch@^3.3.1: fetch-blob "^3.1.4" formdata-polyfill "^4.0.10" +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.npmmirror.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + node-releases@^2.0.12: version "2.0.12" resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + node-releases@^2.0.8: version "2.0.10" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" @@ -5058,6 +6650,13 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + npm-run-path@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" @@ -5072,6 +6671,11 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" +nwsapi@^2.2.2: + version "2.2.12" + resolved "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.12.tgz#fb6af5c0ec35b27b4581eb3bbad34ec9e5c696f8" + integrity sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w== + object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -5147,7 +6751,7 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -5173,13 +6777,27 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" -p-limit@^3.0.2: +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + p-locate@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" @@ -5194,6 +6812,11 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -5201,7 +6824,7 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -5211,7 +6834,7 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse5@^7.0.0: +parse5@^7.0.0, parse5@^7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== @@ -5228,7 +6851,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -5253,7 +6876,12 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: +picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -5263,6 +6891,18 @@ pidtree@^0.6.0: resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== +pirates@^4.0.4: + version "4.0.6" + resolved "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + postcss@8.4.31: version "8.4.31" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" @@ -5289,6 +6929,23 @@ prettier@^3.0.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.2.tgz#78fcecd6d870551aa5547437cdae39d4701dca5b" integrity sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ== +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.npmmirror.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +prompts@^2.0.1: + version "2.4.2" + resolved "https://registry.npmmirror.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + prop-types@^15.0.0, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" @@ -5303,11 +6960,31 @@ property-information@^6.0.0: resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.2.0.tgz#b74f522c31c097b5149e3c3cb8d7f3defd986a1d" integrity sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg== +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.npmmirror.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + punycode@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +punycode@^2.1.1: + version "2.3.1" + resolved "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +pure-rand@^6.0.0: + version "6.1.0" + resolved "https://registry.npmmirror.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -5405,6 +7082,14 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + redux@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197" @@ -5540,16 +7225,38 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-pkg-maps@^1.0.0: version "1.0.0" resolved "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.npmmirror.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + resolve@^1.14.2, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -5559,6 +7266,15 @@ resolve@^1.14.2, resolve@^1.22.1: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.4: version "2.0.0-next.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" @@ -5659,6 +7375,13 @@ sass@^1.59.2: immutable "^4.0.0" source-map-js ">=0.6.2 <2.0.0" +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + scheduler@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" @@ -5675,11 +7398,16 @@ schema-utils@^3.1.1, schema-utils@^3.2.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: +semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +semver@^7.5.3: + version "7.6.3" + resolved "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" @@ -5720,11 +7448,16 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.npmmirror.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -5766,6 +7499,14 @@ slice-ansi@^5.0.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -5774,7 +7515,7 @@ source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0, source-map@^0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -5794,11 +7535,23 @@ spawn-command@0.0.2: resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e" integrity sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ== +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + stable@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.npmmirror.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + stop-iteration-iterator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" @@ -5816,6 +7569,14 @@ string-argv@^0.3.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.npmmirror.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -5894,11 +7655,28 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-final-newline@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -5967,6 +7745,11 @@ svgo@^2.8.0: picocolors "^1.0.0" stable "^0.1.8" +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + synckit@^0.8.5, synckit@^0.8.6: version "0.8.8" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.8.tgz#fe7fe446518e3d3d49f5e429f443cf08b6edfcd7" @@ -6001,6 +7784,15 @@ terser@^5.16.8: commander "^2.20.0" source-map-support "~0.5.20" +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.npmmirror.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -6021,6 +7813,11 @@ tiny-invariant@^1.0.6: resolved "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.npmmirror.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -6033,6 +7830,23 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +tough-cookie@^4.1.2: + version "4.1.4" + resolved "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -6058,6 +7872,25 @@ ts-dedent@^2.2.0: resolved "https://registry.npmmirror.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.npmmirror.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + tsconfig-paths@^3.14.1: version "3.14.2" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" @@ -6095,6 +7928,11 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.npmmirror.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -6229,6 +8067,11 @@ unist-util-visit@^4.0.0: unist-util-is "^5.0.0" unist-util-visit-parents "^5.1.1" +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.npmmirror.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + update-browserslist-db@^1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" @@ -6245,6 +8088,14 @@ update-browserslist-db@^1.0.11: escalade "^3.1.1" picocolors "^1.0.0" +update-browserslist-db@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" + integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.1" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -6252,6 +8103,14 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + use-debounce@^9.0.4: version "9.0.4" resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-9.0.4.tgz#51d25d856fbdfeb537553972ce3943b897f1ac85" @@ -6282,6 +8141,20 @@ uvu@^0.5.0: kleur "^4.0.3" sade "^1.7.3" +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +v8-to-istanbul@^9.0.1: + version "9.3.0" + resolved "https://registry.npmmirror.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" + integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^2.0.0" + vfile-location@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-4.1.0.tgz#69df82fb9ef0a38d0d02b90dd84620e120050dd0" @@ -6308,6 +8181,20 @@ vfile@^5.0.0: unist-util-stringify-position "^3.0.0" vfile-message "^3.0.0" +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== + dependencies: + xml-name-validator "^4.0.0" + +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.npmmirror.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + watch@^1.0.2: version "1.0.2" resolved "https://registry.npmmirror.com/watch/-/watch-1.0.2.tgz#340a717bde765726fa0aa07d721e0147a551df0c" @@ -6339,6 +8226,11 @@ web-worker@^1.2.0: resolved "https://registry.npmmirror.com/web-worker/-/web-worker-1.2.0.tgz#5d85a04a7fbc1e7db58f66595d7a3ac7c9c180da" integrity sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + webpack-sources@^3.2.3: version "3.2.3" resolved "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" @@ -6374,6 +8266,26 @@ webpack@^5.88.1: watchpack "^2.4.0" webpack-sources "^3.2.3" +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -6437,6 +8349,29 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.npmmirror.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +ws@^8.11.0: + version "8.18.0" + resolved "https://registry.npmmirror.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -6467,7 +8402,7 @@ yargs-parser@^21.1.1: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs@^17.7.2: +yargs@^17.3.1, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -6480,6 +8415,11 @@ yargs@^17.7.2: y18n "^5.0.5" yargs-parser "^21.1.1" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.npmmirror.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" From 1287e39cc65d1e0339ec39afbccd8d4526bee9d9 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Tue, 6 Aug 2024 19:24:47 +0800 Subject: [PATCH 003/352] feat: run test before build --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 91be0544e..3945a203f 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,13 @@ "mask": "npx tsx app/masks/build.ts", "mask:watch": "npx watch \"yarn mask\" app/masks", "dev": "concurrently -r \"yarn run mask:watch\" \"next dev\"", - "build": "yarn mask && cross-env BUILD_MODE=standalone next build", + "build": "yarn test:ci && yarn mask && cross-env BUILD_MODE=standalone next build", "start": "next start", "lint": "next lint", - "export": "yarn mask && cross-env BUILD_MODE=export BUILD_APP=1 next build", + "export": "yarn test:ci && yarn mask && cross-env BUILD_MODE=export BUILD_APP=1 next build", "export:dev": "concurrently -r \"yarn mask:watch\" \"cross-env BUILD_MODE=export BUILD_APP=1 next dev\"", "app:dev": "concurrently -r \"yarn mask:watch\" \"yarn tauri dev\"", - "app:build": "yarn mask && yarn tauri build", + "app:build": "yarn test:ci && yarn mask && yarn tauri build", "prompts": "node ./scripts/fetch-prompts.mjs", "prepare": "husky install", "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev", @@ -81,4 +81,4 @@ "lint-staged/yaml": "^2.2.2" }, "packageManager": "yarn@1.22.19" -} +} \ No newline at end of file From 56eb9d14308188364652e1dfedf7c71ea2ef277b Mon Sep 17 00:00:00 2001 From: yihang3 Date: Wed, 21 Aug 2024 15:22:31 +0800 Subject: [PATCH 004/352] fix no max_tokens in payload when the vision model name does not contain 'vision'. --- app/client/platforms/openai.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index d4e262c16..2b60a8b6a 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -190,7 +190,7 @@ export class ChatGPTApi implements LLMApi { }; // add max_tokens to vision model - if (visionModel && modelConfig.model.includes("preview")) { + if (visionModel) { requestPayload["max_tokens"] = Math.max(modelConfig.max_tokens, 4000); } } From 2f410fc09f62e67c32ac6142e99937d3e8f29601 Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Tue, 27 Aug 2024 16:21:02 +0800 Subject: [PATCH 005/352] feat: add tts stt --- app/client/api.ts | 22 ++ app/client/platforms/openai.ts | 83 ++++++- app/components/chat.tsx | 150 ++++++++++++- app/components/settings.tsx | 24 ++ app/components/stt-config.tsx | 51 +++++ app/components/stt.module.scss | 119 ++++++++++ app/components/tts-config.tsx | 132 +++++++++++ app/components/tts.module.scss | 119 ++++++++++ app/constant.ts | 20 ++ app/icons/speak-stop.svg | 1 + app/icons/speak.svg | 1 + app/icons/voice-white.svg | 16 ++ app/locales/cn.ts | 34 +++ app/locales/en.ts | 2 + app/locales/index.ts | 31 +++ app/store/access.ts | 9 + app/store/config.ts | 49 +++++ app/utils/audio.ts | 45 ++++ app/utils/ms_edge_tts.ts | 391 +++++++++++++++++++++++++++++++++ app/utils/speech.ts | 126 +++++++++++ package.json | 3 +- yarn.lock | 24 ++ 22 files changed, 1446 insertions(+), 6 deletions(-) create mode 100644 app/components/stt-config.tsx create mode 100644 app/components/stt.module.scss create mode 100644 app/components/tts-config.tsx create mode 100644 app/components/tts.module.scss create mode 100644 app/icons/speak-stop.svg create mode 100644 app/icons/speak.svg create mode 100644 app/icons/voice-white.svg create mode 100644 app/utils/audio.ts create mode 100644 app/utils/ms_edge_tts.ts create mode 100644 app/utils/speech.ts diff --git a/app/client/api.ts b/app/client/api.ts index d7fb023a2..8d0877a0d 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -20,6 +20,7 @@ export const ROLES = ["system", "user", "assistant"] as const; export type MessageRole = (typeof ROLES)[number]; export const Models = ["gpt-3.5-turbo", "gpt-4"] as const; +export const TTSModels = ["tts-1", "tts-1-hd"] as const; export type ChatModel = ModelType; export interface MultimodalContent { @@ -48,6 +49,25 @@ export interface LLMConfig { style?: DalleRequestPayload["style"]; } +export interface SpeechOptions { + model: string; + input: string; + voice: string; + response_format?: string; + speed?: number; + onController?: (controller: AbortController) => void; +} + +export interface TranscriptionOptions { + model?: "whisper-1"; + file: Blob; + language?: string; + prompt?: string; + response_format?: "json" | "text" | "srt" | "verbose_json" | "vtt"; + temperature?: number; + onController?: (controller: AbortController) => void; +} + export interface ChatOptions { messages: RequestMessage[]; config: LLMConfig; @@ -80,6 +100,8 @@ export interface LLMModelProvider { export abstract class LLMApi { abstract chat(options: ChatOptions): Promise; + abstract speech(options: SpeechOptions): Promise; + abstract transcription(options: TranscriptionOptions): Promise; abstract usage(): Promise; abstract models(): Promise; } diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index d4e262c16..02115140b 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -26,6 +26,8 @@ import { LLMModel, LLMUsage, MultimodalContent, + SpeechOptions, + TranscriptionOptions, } from "../api"; import Locale from "../../locales"; import { @@ -77,7 +79,7 @@ export interface DalleRequestPayload { export class ChatGPTApi implements LLMApi { private disableListModels = true; - path(path: string): string { + path(path: string, model?: string): string { const accessStore = useAccessStore.getState(); let baseUrl = ""; @@ -140,6 +142,85 @@ export class ChatGPTApi implements LLMApi { return res.choices?.at(0)?.message?.content ?? res; } + async speech(options: SpeechOptions): Promise { + const requestPayload = { + model: options.model, + input: options.input, + voice: options.voice, + response_format: options.response_format, + speed: options.speed, + }; + + console.log("[Request] openai speech payload: ", requestPayload); + + const controller = new AbortController(); + options.onController?.(controller); + + try { + const speechPath = this.path(OpenaiPath.SpeechPath, options.model); + const speechPayload = { + method: "POST", + body: JSON.stringify(requestPayload), + signal: controller.signal, + headers: getHeaders(), + }; + + // make a fetch request + const requestTimeoutId = setTimeout( + () => controller.abort(), + REQUEST_TIMEOUT_MS, + ); + + const res = await fetch(speechPath, speechPayload); + clearTimeout(requestTimeoutId); + return await res.arrayBuffer(); + } catch (e) { + console.log("[Request] failed to make a speech request", e); + throw e; + } + } + + async transcription(options: TranscriptionOptions): Promise { + const formData = new FormData(); + formData.append("file", options.file, "audio.wav"); + formData.append("model", options.model ?? "whisper-1"); + if (options.language) formData.append("language", options.language); + if (options.prompt) formData.append("prompt", options.prompt); + if (options.response_format) + formData.append("response_format", options.response_format); + if (options.temperature) + formData.append("temperature", options.temperature.toString()); + + console.log("[Request] openai audio transcriptions payload: ", options); + + const controller = new AbortController(); + options.onController?.(controller); + + try { + const path = this.path(OpenaiPath.TranscriptionPath, options.model); + const headers = getHeaders(true); + const payload = { + method: "POST", + body: formData, + signal: controller.signal, + headers: headers, + }; + + // make a fetch request + const requestTimeoutId = setTimeout( + () => controller.abort(), + REQUEST_TIMEOUT_MS, + ); + const res = await fetch(path, payload); + clearTimeout(requestTimeoutId); + const json = await res.json(); + return json.text; + } catch (e) { + console.log("[Request] failed to make a audio transcriptions request", e); + throw e; + } + } + async chat(options: ChatOptions) { const modelConfig = { ...useAppConfig.getState().modelConfig, diff --git a/app/components/chat.tsx b/app/components/chat.tsx index ed5b06799..e5391ad22 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -10,11 +10,14 @@ import React, { } from "react"; import SendWhiteIcon from "../icons/send-white.svg"; +import VoiceWhiteIcon from "../icons/voice-white.svg"; import BrainIcon from "../icons/brain.svg"; import RenameIcon from "../icons/rename.svg"; import ExportIcon from "../icons/share.svg"; import ReturnIcon from "../icons/return.svg"; import CopyIcon from "../icons/copy.svg"; +import SpeakIcon from "../icons/speak.svg"; +import SpeakStopIcon from "../icons/speak-stop.svg"; import LoadingIcon from "../icons/three-dots.svg"; import LoadingButtonIcon from "../icons/loading.svg"; import PromptIcon from "../icons/prompt.svg"; @@ -64,6 +67,7 @@ import { getMessageImages, isVisionModel, isDalle3, + isFirefox, } from "../utils"; import { uploadImage as uploadImageRemote } from "@/app/utils/chat"; @@ -73,7 +77,7 @@ import dynamic from "next/dynamic"; import { ChatControllerPool } from "../client/controller"; import { DalleSize, DalleQuality, DalleStyle } from "../typing"; import { Prompt, usePromptStore } from "../store/prompt"; -import Locale from "../locales"; +import Locale, { getLang, getSTTLang } from "../locales"; import { IconButton } from "./button"; import styles from "./chat.module.scss"; @@ -90,6 +94,10 @@ import { import { useNavigate } from "react-router-dom"; import { CHAT_PAGE_SIZE, + DEFAULT_STT_ENGINE, + DEFAULT_TTS_ENGINE, + FIREFOX_DEFAULT_STT_ENGINE, + ModelProvider, LAST_INPUT_KEY, Path, REQUEST_TIMEOUT_MS, @@ -106,6 +114,16 @@ import { ExportMessageModal } from "./exporter"; import { getClientConfig } from "../config/client"; import { useAllModels } from "../utils/hooks"; import { MultimodalContent } from "../client/api"; +import { ClientApi } from "../client/api"; +import { createTTSPlayer } from "../utils/audio"; +import { + OpenAITranscriptionApi, + SpeechApi, + WebTranscriptionApi, +} from "../utils/speech"; +import { MsEdgeTTS, OUTPUT_FORMAT } from "../utils/ms_edge_tts"; + +const ttsPlayer = createTTSPlayer(); const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { loading: () => , @@ -922,6 +940,33 @@ function _Chat() { } }; + const [isListening, setIsListening] = useState(false); + const [isTranscription, setIsTranscription] = useState(false); + const [speechApi, setSpeechApi] = useState(null); + + const startListening = async () => { + if (speechApi) { + await speechApi.start(); + setIsListening(true); + } + }; + + const stopListening = async () => { + if (speechApi) { + if (config.sttConfig.engine !== DEFAULT_STT_ENGINE) + setIsTranscription(true); + await speechApi.stop(); + setIsListening(false); + } + }; + + const onRecognitionEnd = (finalTranscript: string) => { + console.log(finalTranscript); + if (finalTranscript) setUserInput(finalTranscript); + if (config.sttConfig.engine !== DEFAULT_STT_ENGINE) + setIsTranscription(false); + }; + const doSubmit = (userInput: string) => { if (userInput.trim() === "") return; const matchCommand = chatCommands.match(userInput); @@ -992,6 +1037,16 @@ function _Chat() { } }); // eslint-disable-next-line react-hooks/exhaustive-deps + if (isFirefox()) config.sttConfig.engine = FIREFOX_DEFAULT_STT_ENGINE; + setSpeechApi( + config.sttConfig.engine === DEFAULT_STT_ENGINE + ? new WebTranscriptionApi((transcription) => + onRecognitionEnd(transcription), + ) + : new OpenAITranscriptionApi((transcription) => + onRecognitionEnd(transcription), + ), + ); }, []); // check if should send message @@ -1102,10 +1157,55 @@ function _Chat() { }); }; + const accessStore = useAccessStore(); + const [speechStatus, setSpeechStatus] = useState(false); + const [speechLoading, setSpeechLoading] = useState(false); + async function openaiSpeech(text: string) { + if (speechStatus) { + ttsPlayer.stop(); + setSpeechStatus(false); + } else { + var api: ClientApi; + api = new ClientApi(ModelProvider.GPT); + const config = useAppConfig.getState(); + setSpeechLoading(true); + ttsPlayer.init(); + let audioBuffer: ArrayBuffer; + const { markdownToTxt } = require("markdown-to-txt"); + const textContent = markdownToTxt(text); + if (config.ttsConfig.engine !== DEFAULT_TTS_ENGINE) { + const edgeVoiceName = accessStore.edgeVoiceName(); + const tts = new MsEdgeTTS(); + await tts.setMetadata( + edgeVoiceName, + OUTPUT_FORMAT.AUDIO_24KHZ_96KBITRATE_MONO_MP3, + ); + audioBuffer = await tts.toArrayBuffer(textContent); + } else { + audioBuffer = await api.llm.speech({ + model: config.ttsConfig.model, + input: textContent, + voice: config.ttsConfig.voice, + speed: config.ttsConfig.speed, + }); + } + setSpeechStatus(true); + ttsPlayer + .play(audioBuffer, () => { + setSpeechStatus(false); + }) + .catch((e) => { + console.error("[OpenAI Speech]", e); + showToast(prettyObject(e)); + setSpeechStatus(false); + }) + .finally(() => setSpeechLoading(false)); + } + } + const context: RenderMessage[] = useMemo(() => { return session.mask.hideContext ? [] : session.mask.context.slice(); }, [session.mask.context, session.mask.hideContext]); - const accessStore = useAccessStore(); if ( context.length === 0 && @@ -1567,6 +1667,26 @@ function _Chat() { ) } /> + {config.ttsConfig.enable && ( + + ) : ( + + ) + } + onClick={() => + openaiSpeech(getMessageTextContent(message)) + } + /> + )} )} @@ -1714,13 +1834,35 @@ function _Chat() { })} )} - } + text={ + isListening ? Locale.Chat.StopSpeak : Locale.Chat.StartSpeak + } + className={styles["chat-input-send"]} + type="primary" + onClick={async () => + isListening ? await stopListening() : await startListening() + } + loding={isTranscription} + /> + ) : ( + } + text={Locale.Chat.Send} + className={styles["chat-input-send"]} + type="primary" + onClick={() => doSubmit(userInput)} + /> + )} + {/* } text={Locale.Chat.Send} className={styles["chat-input-send"]} type="primary" onClick={() => doSubmit(userInput)} - /> + /> */} diff --git a/app/components/settings.tsx b/app/components/settings.tsx index ca0a5a187..47a72d79d 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -80,6 +80,8 @@ import { useSyncStore } from "../store/sync"; import { nanoid } from "nanoid"; import { useMaskStore } from "../store/mask"; import { ProviderType } from "../utils/cloud"; +import { TTSConfigList } from "./tts-config"; +import { STTConfigList } from "./stt-config"; function EditPromptModal(props: { id: string; onClose: () => void }) { const promptStore = usePromptStore(); @@ -1646,6 +1648,28 @@ export function Settings() { setShowPromptModal(false)} /> )} + + { + const ttsConfig = { ...config.ttsConfig }; + updater(ttsConfig); + config.update((config) => (config.ttsConfig = ttsConfig)); + }} + /> + + + + { + const sttConfig = { ...config.sttConfig }; + updater(sttConfig); + config.update((config) => (config.sttConfig = sttConfig)); + }} + /> + + diff --git a/app/components/stt-config.tsx b/app/components/stt-config.tsx new file mode 100644 index 000000000..f83d28030 --- /dev/null +++ b/app/components/stt-config.tsx @@ -0,0 +1,51 @@ +import { STTConfig, STTConfigValidator } from "../store"; + +import Locale from "../locales"; +import { ListItem, Select } from "./ui-lib"; +import { DEFAULT_STT_ENGINES } from "../constant"; +import { isFirefox } from "../utils"; + +export function STTConfigList(props: { + sttConfig: STTConfig; + updateConfig: (updater: (config: STTConfig) => void) => void; +}) { + return ( + <> + + + props.updateConfig( + (config) => (config.enable = e.currentTarget.checked), + ) + } + > + + {!isFirefox() && ( + + + + )} + + ); +} diff --git a/app/components/stt.module.scss b/app/components/stt.module.scss new file mode 100644 index 000000000..ba9f382e4 --- /dev/null +++ b/app/components/stt.module.scss @@ -0,0 +1,119 @@ +@import "../styles/animation.scss"; +.plugin-page { + height: 100%; + display: flex; + flex-direction: column; + + .plugin-page-body { + padding: 20px; + overflow-y: auto; + + .plugin-filter { + width: 100%; + max-width: 100%; + margin-bottom: 20px; + animation: slide-in ease 0.3s; + height: 40px; + + display: flex; + + .search-bar { + flex-grow: 1; + max-width: 100%; + min-width: 0; + outline: none; + } + + .search-bar:focus { + border: 1px solid var(--primary); + } + + .plugin-filter-lang { + height: 100%; + margin-left: 10px; + } + + .plugin-create { + height: 100%; + margin-left: 10px; + box-sizing: border-box; + min-width: 80px; + } + } + + .plugin-item { + display: flex; + justify-content: space-between; + padding: 20px; + border: var(--border-in-light); + animation: slide-in ease 0.3s; + + &:not(:last-child) { + border-bottom: 0; + } + + &:first-child { + border-top-left-radius: 10px; + border-top-right-radius: 10px; + } + + &:last-child { + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + } + + .plugin-header { + display: flex; + align-items: center; + + .plugin-icon { + display: flex; + align-items: center; + justify-content: center; + margin-right: 10px; + } + + .plugin-title { + .plugin-name { + font-size: 14px; + font-weight: bold; + } + .plugin-info { + font-size: 12px; + } + .plugin-runtime-warning { + font-size: 12px; + color: #f86c6c; + } + } + } + + .plugin-actions { + display: flex; + flex-wrap: nowrap; + transition: all ease 0.3s; + justify-content: center; + align-items: center; + } + + @media screen and (max-width: 600px) { + display: flex; + flex-direction: column; + padding-bottom: 10px; + border-radius: 10px; + margin-bottom: 20px; + box-shadow: var(--card-shadow); + + &:not(:last-child) { + border-bottom: var(--border-in-light); + } + + .plugin-actions { + width: 100%; + justify-content: space-between; + padding-top: 10px; + } + } + } + } +} diff --git a/app/components/tts-config.tsx b/app/components/tts-config.tsx new file mode 100644 index 000000000..f86e3bc52 --- /dev/null +++ b/app/components/tts-config.tsx @@ -0,0 +1,132 @@ +import { PluginConfig, TTSConfig, TTSConfigValidator } from "../store"; + +import Locale from "../locales"; +import { ListItem, Select } from "./ui-lib"; +import { + DEFAULT_TTS_ENGINE, + DEFAULT_TTS_ENGINES, + DEFAULT_TTS_MODELS, + DEFAULT_TTS_VOICES, +} from "../constant"; +import { InputRange } from "./input-range"; + +export function TTSConfigList(props: { + ttsConfig: TTSConfig; + updateConfig: (updater: (config: TTSConfig) => void) => void; +}) { + return ( + <> + + + props.updateConfig( + (config) => (config.enable = e.currentTarget.checked), + ) + } + > + + {/* + + props.updateConfig( + (config) => (config.autoplay = e.currentTarget.checked), + ) + } + > + */} + + + + {props.ttsConfig.engine === DEFAULT_TTS_ENGINE && ( + <> + + + + + + + + { + props.updateConfig( + (config) => + (config.speed = TTSConfigValidator.speed( + e.currentTarget.valueAsNumber, + )), + ); + }} + > + + + )} + + ); +} diff --git a/app/components/tts.module.scss b/app/components/tts.module.scss new file mode 100644 index 000000000..ba9f382e4 --- /dev/null +++ b/app/components/tts.module.scss @@ -0,0 +1,119 @@ +@import "../styles/animation.scss"; +.plugin-page { + height: 100%; + display: flex; + flex-direction: column; + + .plugin-page-body { + padding: 20px; + overflow-y: auto; + + .plugin-filter { + width: 100%; + max-width: 100%; + margin-bottom: 20px; + animation: slide-in ease 0.3s; + height: 40px; + + display: flex; + + .search-bar { + flex-grow: 1; + max-width: 100%; + min-width: 0; + outline: none; + } + + .search-bar:focus { + border: 1px solid var(--primary); + } + + .plugin-filter-lang { + height: 100%; + margin-left: 10px; + } + + .plugin-create { + height: 100%; + margin-left: 10px; + box-sizing: border-box; + min-width: 80px; + } + } + + .plugin-item { + display: flex; + justify-content: space-between; + padding: 20px; + border: var(--border-in-light); + animation: slide-in ease 0.3s; + + &:not(:last-child) { + border-bottom: 0; + } + + &:first-child { + border-top-left-radius: 10px; + border-top-right-radius: 10px; + } + + &:last-child { + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + } + + .plugin-header { + display: flex; + align-items: center; + + .plugin-icon { + display: flex; + align-items: center; + justify-content: center; + margin-right: 10px; + } + + .plugin-title { + .plugin-name { + font-size: 14px; + font-weight: bold; + } + .plugin-info { + font-size: 12px; + } + .plugin-runtime-warning { + font-size: 12px; + color: #f86c6c; + } + } + } + + .plugin-actions { + display: flex; + flex-wrap: nowrap; + transition: all ease 0.3s; + justify-content: center; + align-items: center; + } + + @media screen and (max-width: 600px) { + display: flex; + flex-direction: column; + padding-bottom: 10px; + border-radius: 10px; + margin-bottom: 20px; + box-shadow: var(--card-shadow); + + &:not(:last-child) { + border-bottom: var(--border-in-light); + } + + .plugin-actions { + width: 100%; + justify-content: space-between; + padding-top: 10px; + } + } + } + } +} diff --git a/app/constant.ts b/app/constant.ts index e88d497ca..ec0445d2e 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -153,6 +153,8 @@ export const Anthropic = { export const OpenaiPath = { ChatPath: "v1/chat/completions", + SpeechPath: "v1/audio/speech", + TranscriptionPath: "v1/audio/transcriptions", ImagePath: "v1/images/generations", UsagePath: "dashboard/billing/usage", SubsPath: "dashboard/billing/subscription", @@ -256,6 +258,24 @@ export const KnowledgeCutOffDate: Record = { "gemini-pro-vision": "2023-12", }; +export const DEFAULT_TTS_ENGINE = "OpenAI-TTS"; +export const DEFAULT_TTS_ENGINES = ["OpenAI-TTS", "Edge-TTS"]; +export const DEFAULT_TTS_MODEL = "tts-1"; +export const DEFAULT_TTS_VOICE = "alloy"; +export const DEFAULT_TTS_MODELS = ["tts-1", "tts-1-hd"]; +export const DEFAULT_TTS_VOICES = [ + "alloy", + "echo", + "fable", + "onyx", + "nova", + "shimmer", +]; + +export const DEFAULT_STT_ENGINE = "WebAPI"; +export const DEFAULT_STT_ENGINES = ["WebAPI", "OpenAI Whisper"]; +export const FIREFOX_DEFAULT_STT_ENGINE = "OpenAI Whisper"; + const openaiModels = [ "gpt-3.5-turbo", "gpt-3.5-turbo-1106", diff --git a/app/icons/speak-stop.svg b/app/icons/speak-stop.svg new file mode 100644 index 000000000..926ae7bb3 --- /dev/null +++ b/app/icons/speak-stop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/speak.svg b/app/icons/speak.svg new file mode 100644 index 000000000..e02212c9a --- /dev/null +++ b/app/icons/speak.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/voice-white.svg b/app/icons/voice-white.svg new file mode 100644 index 000000000..0a4a0ae31 --- /dev/null +++ b/app/icons/voice-white.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 9a3227d68..c6aef5140 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -43,6 +43,8 @@ const cn = { Delete: "删除", Edit: "编辑", FullScreen: "全屏", + Speech: "朗读", + StopSpeech: "停止", }, Commands: { new: "新建聊天", @@ -76,6 +78,8 @@ const cn = { return inputHints + ",/ 触发补全,: 触发命令"; }, Send: "发送", + StartSpeak: "说话", + StopSpeak: "停止", Config: { Reset: "清除记忆", SaveAs: "存为面具", @@ -481,6 +485,36 @@ const cn = { Title: "频率惩罚度 (frequency_penalty)", SubTitle: "值越大,越有可能降低重复字词", }, + TTS: { + Enable: { + Title: "启用文本转语音", + SubTitle: "启用文本生成语音服务", + }, + Autoplay: { + Title: "启用自动朗读", + SubTitle: "自动生成语音并播放,需先开启文本转语音开关", + }, + Model: "模型", + Engine: "转换引擎", + Voice: { + Title: "声音", + SubTitle: "生成语音时使用的声音", + }, + Speed: { + Title: "速度", + SubTitle: "生成语音的速度", + }, + }, + STT: { + Enable: { + Title: "启用语音转文本", + SubTitle: "启用语音转文本", + }, + Engine: { + Title: "转换引擎", + SubTitle: "音频转换引擎", + }, + }, }, Store: { DefaultTopic: "新的聊天", diff --git a/app/locales/en.ts b/app/locales/en.ts index 77f3a700a..1aa2137ec 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -45,6 +45,8 @@ const en: LocaleType = { Delete: "Delete", Edit: "Edit", FullScreen: "FullScreen", + Speech: "Play", + StopSpeech: "Stop", }, Commands: { new: "Start a new chat", diff --git a/app/locales/index.ts b/app/locales/index.ts index acdb3e878..3078afc7b 100644 --- a/app/locales/index.ts +++ b/app/locales/index.ts @@ -137,3 +137,34 @@ export function getISOLang() { const lang = getLang(); return isoLangString[lang] ?? lang; } + +const DEFAULT_STT_LANG = "zh-CN"; +export const STT_LANG_MAP: Record = { + cn: "zh-CN", + en: "en-US", + pt: "pt-BR", + tw: "zh-TW", + jp: "ja-JP", + ko: "ko-KR", + id: "id-ID", + fr: "fr-FR", + es: "es-ES", + it: "it-IT", + tr: "tr-TR", + de: "de-DE", + vi: "vi-VN", + ru: "ru-RU", + cs: "cs-CZ", + no: "no-NO", + ar: "ar-SA", + bn: "bn-BD", + sk: "sk-SK", +}; + +export function getSTTLang(): string { + try { + return STT_LANG_MAP[getLang()]; + } catch { + return DEFAULT_STT_LANG; + } +} diff --git a/app/store/access.ts b/app/store/access.ts index a1014610e..0e392e1e9 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -120,6 +120,9 @@ const DEFAULT_ACCESS_STATE = { disableFastLink: false, customModels: "", defaultModel: "", + + // tts config + edgeTTSVoiceName: "zh-CN-YunxiNeural", }; export const useAccessStore = createPersistStore( @@ -132,6 +135,12 @@ export const useAccessStore = createPersistStore( return get().needCode; }, + edgeVoiceName() { + this.fetch(); + + return get().edgeTTSVoiceName; + }, + isValidOpenAI() { return ensure(get(), ["openaiApiKey"]); }, diff --git a/app/store/config.ts b/app/store/config.ts index e8e3c9863..e2de06c9a 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -5,12 +5,25 @@ import { DEFAULT_INPUT_TEMPLATE, DEFAULT_MODELS, DEFAULT_SIDEBAR_WIDTH, + DEFAULT_STT_ENGINE, + DEFAULT_STT_ENGINES, + DEFAULT_TTS_ENGINE, + DEFAULT_TTS_ENGINES, + DEFAULT_TTS_MODEL, + DEFAULT_TTS_MODELS, + DEFAULT_TTS_VOICE, + DEFAULT_TTS_VOICES, StoreKey, ServiceProvider, } from "../constant"; import { createPersistStore } from "../utils/store"; export type ModelType = (typeof DEFAULT_MODELS)[number]["name"]; +export type TTSModelType = (typeof DEFAULT_TTS_MODELS)[number]; +export type TTSVoiceType = (typeof DEFAULT_TTS_VOICES)[number]; +export type TTSEngineType = (typeof DEFAULT_TTS_ENGINES)[number]; + +export type STTEngineType = (typeof DEFAULT_STT_ENGINES)[number]; export enum SubmitKey { Enter = "Enter", @@ -66,11 +79,26 @@ export const DEFAULT_CONFIG = { quality: "standard" as DalleQuality, style: "vivid" as DalleStyle, }, + + ttsConfig: { + enable: false, + autoplay: false, + engine: DEFAULT_TTS_ENGINE, + model: DEFAULT_TTS_MODEL, + voice: DEFAULT_TTS_VOICE, + speed: 1.0, + }, + sttConfig: { + enable: false, + engine: DEFAULT_STT_ENGINE, + }, }; export type ChatConfig = typeof DEFAULT_CONFIG; export type ModelConfig = ChatConfig["modelConfig"]; +export type TTSConfig = ChatConfig["ttsConfig"]; +export type STTConfig = ChatConfig["sttConfig"]; export function limitNumber( x: number, @@ -85,6 +113,27 @@ export function limitNumber( return Math.min(max, Math.max(min, x)); } +export const TTSConfigValidator = { + engine(x: string) { + return x as TTSEngineType; + }, + model(x: string) { + return x as TTSModelType; + }, + voice(x: string) { + return x as TTSVoiceType; + }, + speed(x: number) { + return limitNumber(x, 0.25, 4.0, 1.0); + }, +}; + +export const STTConfigValidator = { + engine(x: string) { + return x as STTEngineType; + }, +}; + export const ModalConfigValidator = { model(x: string) { return x as ModelType; diff --git a/app/utils/audio.ts b/app/utils/audio.ts new file mode 100644 index 000000000..f6828c7aa --- /dev/null +++ b/app/utils/audio.ts @@ -0,0 +1,45 @@ +type TTSPlayer = { + init: () => void; + play: (audioBuffer: ArrayBuffer, onended: () => void | null) => Promise; + stop: () => void; +}; + +export function createTTSPlayer(): TTSPlayer { + let audioContext: AudioContext | null = null; + let audioBufferSourceNode: AudioBufferSourceNode | null = null; + + const init = () => { + audioContext = new (window.AudioContext || window.webkitAudioContext)(); + audioContext.suspend(); + }; + + const play = async (audioBuffer: ArrayBuffer, onended: () => void | null) => { + if (audioBufferSourceNode) { + audioBufferSourceNode.stop(); + audioBufferSourceNode.disconnect(); + } + + const buffer = await audioContext!.decodeAudioData(audioBuffer); + audioBufferSourceNode = audioContext!.createBufferSource(); + audioBufferSourceNode.buffer = buffer; + audioBufferSourceNode.connect(audioContext!.destination); + audioContext!.resume().then(() => { + audioBufferSourceNode!.start(); + }); + audioBufferSourceNode.onended = onended; + }; + + const stop = () => { + if (audioBufferSourceNode) { + audioBufferSourceNode.stop(); + audioBufferSourceNode.disconnect(); + audioBufferSourceNode = null; + } + if (audioContext) { + audioContext.close(); + audioContext = null; + } + }; + + return { init, play, stop }; +} diff --git a/app/utils/ms_edge_tts.ts b/app/utils/ms_edge_tts.ts new file mode 100644 index 000000000..f291ebada --- /dev/null +++ b/app/utils/ms_edge_tts.ts @@ -0,0 +1,391 @@ +// import axios from "axios"; +import { Buffer } from "buffer"; +import { randomBytes } from "crypto"; +import { Readable } from "stream"; + +// Modified according to https://github.com/Migushthe2nd/MsEdgeTTS + +/** + * https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,volume,-Indicates%20the%20volume + */ +export enum VOLUME { + SILENT = "silent", + X_SOFT = "x-soft", + SOFT = "soft", + MEDIUM = "medium", + LOUD = "loud", + X_LOUD = "x-LOUD", + DEFAULT = "default", +} + +/** + * https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,rate,-Indicates%20the%20speaking + */ +export enum RATE { + X_SLOW = "x-slow", + SLOW = "slow", + MEDIUM = "medium", + FAST = "fast", + X_FAST = "x-fast", + DEFAULT = "default", +} + +/** + * https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,pitch,-Indicates%20the%20baseline + */ +export enum PITCH { + X_LOW = "x-low", + LOW = "low", + MEDIUM = "medium", + HIGH = "high", + X_HIGH = "x-high", + DEFAULT = "default", +} + +/** + * Only a few of the [possible formats](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/rest-text-to-speech#audio-outputs) are accepted. + */ +export enum OUTPUT_FORMAT { + // Streaming ============================= + // AMR_WB_16000HZ = "amr-wb-16000hz", + // AUDIO_16KHZ_16BIT_32KBPS_MONO_OPUS = "audio-16khz-16bit-32kbps-mono-opus", + // AUDIO_16KHZ_32KBITRATE_MONO_MP3 = "audio-16khz-32kbitrate-mono-mp3", + // AUDIO_16KHZ_64KBITRATE_MONO_MP3 = "audio-16khz-64kbitrate-mono-mp3", + // AUDIO_16KHZ_128KBITRATE_MONO_MP3 = "audio-16khz-128kbitrate-mono-mp3", + // AUDIO_24KHZ_16BIT_24KBPS_MONO_OPUS = "audio-24khz-16bit-24kbps-mono-opus", + // AUDIO_24KHZ_16BIT_48KBPS_MONO_OPUS = "audio-24khz-16bit-48kbps-mono-opus", + AUDIO_24KHZ_48KBITRATE_MONO_MP3 = "audio-24khz-48kbitrate-mono-mp3", + AUDIO_24KHZ_96KBITRATE_MONO_MP3 = "audio-24khz-96kbitrate-mono-mp3", + // AUDIO_24KHZ_160KBITRATE_MONO_MP3 = "audio-24khz-160kbitrate-mono-mp3", + // AUDIO_48KHZ_96KBITRATE_MONO_MP3 = "audio-48khz-96kbitrate-mono-mp3", + // AUDIO_48KHZ_192KBITRATE_MONO_MP3 = "audio-48khz-192kbitrate-mono-mp3", + // OGG_16KHZ_16BIT_MONO_OPUS = "ogg-16khz-16bit-mono-opus", + // OGG_24KHZ_16BIT_MONO_OPUS = "ogg-24khz-16bit-mono-opus", + // OGG_48KHZ_16BIT_MONO_OPUS = "ogg-48khz-16bit-mono-opus", + // RAW_8KHZ_8BIT_MONO_ALAW = "raw-8khz-8bit-mono-alaw", + // RAW_8KHZ_8BIT_MONO_MULAW = "raw-8khz-8bit-mono-mulaw", + // RAW_8KHZ_16BIT_MONO_PCM = "raw-8khz-16bit-mono-pcm", + // RAW_16KHZ_16BIT_MONO_PCM = "raw-16khz-16bit-mono-pcm", + // RAW_16KHZ_16BIT_MONO_TRUESILK = "raw-16khz-16bit-mono-truesilk", + // RAW_22050HZ_16BIT_MONO_PCM = "raw-22050hz-16bit-mono-pcm", + // RAW_24KHZ_16BIT_MONO_PCM = "raw-24khz-16bit-mono-pcm", + // RAW_24KHZ_16BIT_MONO_TRUESILK = "raw-24khz-16bit-mono-truesilk", + // RAW_44100HZ_16BIT_MONO_PCM = "raw-44100hz-16bit-mono-pcm", + // RAW_48KHZ_16BIT_MONO_PCM = "raw-48khz-16bit-mono-pcm", + // WEBM_16KHZ_16BIT_MONO_OPUS = "webm-16khz-16bit-mono-opus", + // WEBM_24KHZ_16BIT_24KBPS_MONO_OPUS = "webm-24khz-16bit-24kbps-mono-opus", + WEBM_24KHZ_16BIT_MONO_OPUS = "webm-24khz-16bit-mono-opus", + // Non-streaming ============================= + // RIFF_8KHZ_8BIT_MONO_ALAW = "riff-8khz-8bit-mono-alaw", + // RIFF_8KHZ_8BIT_MONO_MULAW = "riff-8khz-8bit-mono-mulaw", + // RIFF_8KHZ_16BIT_MONO_PCM = "riff-8khz-16bit-mono-pcm", + // RIFF_22050HZ_16BIT_MONO_PCM = "riff-22050hz-16bit-mono-pcm", + // RIFF_24KHZ_16BIT_MONO_PCM = "riff-24khz-16bit-mono-pcm", + // RIFF_44100HZ_16BIT_MONO_PCM = "riff-44100hz-16bit-mono-pcm", + // RIFF_48KHZ_16BIT_MONO_PCM = "riff-48khz-16bit-mono-pcm", +} + +export type Voice = { + Name: string; + ShortName: string; + Gender: string; + Locale: string; + SuggestedCodec: string; + FriendlyName: string; + Status: string; +}; + +export class ProsodyOptions { + /** + * The pitch to use. + * Can be any {@link PITCH}, or a relative frequency in Hz (+50Hz), a relative semitone (+2st), or a relative percentage (+50%). + * [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,pitch,-Indicates%20the%20baseline) + */ + pitch?: PITCH | string = "+0Hz"; + /** + * The rate to use. + * Can be any {@link RATE}, or a relative number (0.5), or string with a relative percentage (+50%). + * [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,rate,-Indicates%20the%20speaking) + */ + rate?: RATE | string | number = 1.0; + /** + * The volume to use. + * Can be any {@link VOLUME}, or an absolute number (0, 100), a string with a relative number (+50), or a relative percentage (+50%). + * [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,volume,-Indicates%20the%20volume) + */ + volume?: VOLUME | string | number = 100.0; +} + +export class MsEdgeTTS { + static OUTPUT_FORMAT = OUTPUT_FORMAT; + private static TRUSTED_CLIENT_TOKEN = "6A5AA1D4EAFF4E9FB37E23D68491D6F4"; + private static VOICES_URL = `https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=${MsEdgeTTS.TRUSTED_CLIENT_TOKEN}`; + private static SYNTH_URL = `wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=${MsEdgeTTS.TRUSTED_CLIENT_TOKEN}`; + private static BINARY_DELIM = "Path:audio\r\n"; + private static VOICE_LANG_REGEX = /\w{2}-\w{2}/; + private readonly _enableLogger; + private _ws: WebSocket | undefined; + private _voice: any; + private _voiceLocale: any; + private _outputFormat: any; + private _streams: { [key: string]: Readable } = {}; + private _startTime = 0; + + private _log(...o: any[]) { + if (this._enableLogger) { + console.log(...o); + } + } + + /** + * Create a new `MsEdgeTTS` instance. + * + * @param agent (optional, **NOT SUPPORTED IN BROWSER**) Use a custom http.Agent implementation like [https-proxy-agent](https://github.com/TooTallNate/proxy-agents) or [socks-proxy-agent](https://github.com/TooTallNate/proxy-agents/tree/main/packages/socks-proxy-agent). + * @param enableLogger=false whether to enable the built-in logger. This logs connections inits, disconnects, and incoming data to the console + */ + public constructor(enableLogger: boolean = false) { + this._enableLogger = enableLogger; + } + + private async _send(message: any) { + for (let i = 1; i <= 3 && this._ws!.readyState !== this._ws!.OPEN; i++) { + if (i == 1) { + this._startTime = Date.now(); + } + this._log("connecting: ", i); + await this._initClient(); + } + this._ws!.send(message); + } + + private _initClient() { + this._ws = new WebSocket(MsEdgeTTS.SYNTH_URL); + + this._ws.binaryType = "arraybuffer"; + return new Promise((resolve, reject) => { + this._ws!.onopen = () => { + this._log( + "Connected in", + (Date.now() - this._startTime) / 1000, + "seconds", + ); + this._send( + `Content-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n + { + "context": { + "synthesis": { + "audio": { + "metadataoptions": { + "sentenceBoundaryEnabled": "false", + "wordBoundaryEnabled": "false" + }, + "outputFormat": "${this._outputFormat}" + } + } + } + } + `, + ).then(resolve); + }; + this._ws!.onmessage = (m: any) => { + const buffer = Buffer.from(m.data as ArrayBuffer); + const message = buffer.toString(); + const requestId = /X-RequestId:(.*?)\r\n/gm.exec(message)![1]; + if (message.includes("Path:turn.start")) { + // start of turn, ignore + } else if (message.includes("Path:turn.end")) { + // end of turn, close stream + this._streams[requestId].push(null); + } else if (message.includes("Path:response")) { + // context response, ignore + } else if ( + message.includes("Path:audio") && + m.data instanceof ArrayBuffer + ) { + this._pushAudioData(buffer, requestId); + } else { + this._log("UNKNOWN MESSAGE", message); + } + }; + this._ws!.onclose = () => { + this._log( + "disconnected after:", + (Date.now() - this._startTime) / 1000, + "seconds", + ); + for (const requestId in this._streams) { + this._streams[requestId].push(null); + } + }; + this._ws!.onerror = function (error: any) { + reject("Connect Error: " + error); + }; + }); + } + + private _pushAudioData(audioBuffer: Buffer, requestId: string) { + const audioStartIndex = + audioBuffer.indexOf(MsEdgeTTS.BINARY_DELIM) + + MsEdgeTTS.BINARY_DELIM.length; + const audioData = audioBuffer.subarray(audioStartIndex); + this._streams[requestId].push(audioData); + this._log("received audio chunk, size: ", audioData?.length); + } + + private _SSMLTemplate(input: string, options: ProsodyOptions = {}): string { + // in case future updates to the edge API block these elements, we'll be concatenating strings. + options = { ...new ProsodyOptions(), ...options }; + return ` + + + ${input} + + + `; + } + + /** + * Fetch the list of voices available in Microsoft Edge. + * These, however, are not all. The complete list of voices supported by this module [can be found here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support) (neural, standard, and preview). + */ + // getVoices(): Promise { + // return new Promise((resolve, reject) => { + // axios + // .get(MsEdgeTTS.VOICES_URL) + // .then((res) => resolve(res.data)) + // .catch(reject); + // }); + // } + getVoices(): Promise { + return fetch(MsEdgeTTS.VOICES_URL) + .then((response) => { + if (!response.ok) { + throw new Error("Network response was not ok"); + } + return response.json(); + }) + .then((data) => data as Voice[]) + .catch((error) => { + throw error; + }); + } + + /** + * Sets the required information for the speech to be synthesised and inits a new WebSocket connection. + * Must be called at least once before text can be synthesised. + * Saved in this instance. Can be called at any time times to update the metadata. + * + * @param voiceName a string with any `ShortName`. A list of all available neural voices can be found [here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support#neural-voices). However, it is not limited to neural voices: standard voices can also be used. A list of standard voices can be found [here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support#standard-voices) + * @param outputFormat any {@link OUTPUT_FORMAT} + * @param voiceLocale (optional) any voice locale that is supported by the voice. See the list of all voices for compatibility. If not provided, the locale will be inferred from the `voiceName` + */ + async setMetadata( + voiceName: string, + outputFormat: OUTPUT_FORMAT, + voiceLocale?: string, + ) { + const oldVoice = this._voice; + const oldVoiceLocale = this._voiceLocale; + const oldOutputFormat = this._outputFormat; + + this._voice = voiceName; + this._voiceLocale = voiceLocale; + if (!this._voiceLocale) { + const voiceLangMatch = MsEdgeTTS.VOICE_LANG_REGEX.exec(this._voice); + if (!voiceLangMatch) + throw new Error("Could not infer voiceLocale from voiceName!"); + this._voiceLocale = voiceLangMatch[0]; + } + this._outputFormat = outputFormat; + + const changed = + oldVoice !== this._voice || + oldVoiceLocale !== this._voiceLocale || + oldOutputFormat !== this._outputFormat; + + // create new client + if (changed || this._ws!.readyState !== this._ws!.OPEN) { + this._startTime = Date.now(); + await this._initClient(); + } + } + + private _metadataCheck() { + if (!this._ws) + throw new Error( + "Speech synthesis not configured yet. Run setMetadata before calling toStream or toFile.", + ); + } + + /** + * Close the WebSocket connection. + */ + close() { + this._ws!.close(); + } + + /** + * Writes raw audio synthesised from text in real-time to a {@link Readable}. Uses a basic {@link _SSMLTemplate SML template}. + * + * @param input the text to synthesise. Can include SSML elements. + * @param options (optional) {@link ProsodyOptions} + * @returns {Readable} - a `stream.Readable` with the audio data + */ + toStream(input: string, options?: ProsodyOptions): Readable { + const { stream } = this._rawSSMLRequest(this._SSMLTemplate(input, options)); + return stream; + } + + toArrayBuffer(input: string, options?: ProsodyOptions): Promise { + return new Promise((resolve, reject) => { + let data: Uint8Array[] = []; + const readable = this.toStream(input, options); + readable.on("data", (chunk) => { + data.push(chunk); + }); + + readable.on("end", () => { + resolve(Buffer.concat(data).buffer); + }); + + readable.on("error", (err) => { + reject(err); + }); + }); + } + + /** + * Writes raw audio synthesised from a request in real-time to a {@link Readable}. Has no SSML template. Basic SSML should be provided in the request. + * + * @param requestSSML the SSML to send. SSML elements required in order to work. + * @returns {Readable} - a `stream.Readable` with the audio data + */ + rawToStream(requestSSML: string): Readable { + const { stream } = this._rawSSMLRequest(requestSSML); + return stream; + } + + private _rawSSMLRequest(requestSSML: string): { + stream: Readable; + requestId: string; + } { + this._metadataCheck(); + + const requestId = randomBytes(16).toString("hex"); + const request = + `X-RequestId:${requestId}\r\nContent-Type:application/ssml+xml\r\nPath:ssml\r\n\r\n + ` + requestSSML.trim(); + // https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/speech-synthesis-markup + const self = this; + const stream = new Readable({ + read() {}, + destroy(error: Error | null, callback: (error: Error | null) => void) { + delete self._streams[requestId]; + callback(error); + }, + }); + this._streams[requestId] = stream; + this._send(request).then(); + return { stream, requestId }; + } +} diff --git a/app/utils/speech.ts b/app/utils/speech.ts new file mode 100644 index 000000000..dc8102879 --- /dev/null +++ b/app/utils/speech.ts @@ -0,0 +1,126 @@ +import { ChatGPTApi } from "../client/platforms/openai"; +import { getSTTLang } from "../locales"; +import { isFirefox } from "../utils"; + +export type TranscriptionCallback = (transcription: string) => void; + +export abstract class SpeechApi { + protected onTranscription: TranscriptionCallback = () => {}; + + abstract isListening(): boolean; + abstract start(): Promise; + abstract stop(): Promise; + + onTranscriptionReceived(callback: TranscriptionCallback) { + this.onTranscription = callback; + } +} + +export class OpenAITranscriptionApi extends SpeechApi { + private listeningStatus = false; + private mediaRecorder: MediaRecorder | null = null; + private stream: MediaStream | null = null; + private audioChunks: Blob[] = []; + + isListening = () => this.listeningStatus; + + constructor(transcriptionCallback?: TranscriptionCallback) { + super(); + if (transcriptionCallback) { + this.onTranscriptionReceived(transcriptionCallback); + } + } + + async start(): Promise { + // @ts-ignore + navigator.getUserMedia = + // @ts-ignore + navigator.getUserMedia || + // @ts-ignore + navigator.webkitGetUserMedia || + // @ts-ignore + navigator.mozGetUserMedia || + // @ts-ignore + navigator.msGetUserMedia; + if (navigator.mediaDevices) { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + this.mediaRecorder = new MediaRecorder(stream); + this.mediaRecorder.ondataavailable = (e) => { + if (e.data && e.data.size > 0) { + this.audioChunks.push(e.data); + } + }; + + this.stream = stream; + } else { + console.warn("Media Decives will work only with SSL"); + return; + } + + this.audioChunks = []; + + // this.recorder.addEventListener("dataavailable", (event) => { + // this.audioChunks.push(event.data); + // }); + + this.mediaRecorder.start(1000); + this.listeningStatus = true; + } + + async stop(): Promise { + if (!this.mediaRecorder || !this.listeningStatus) { + return; + } + + return new Promise((resolve) => { + this.mediaRecorder!.addEventListener("stop", async () => { + const audioBlob = new Blob(this.audioChunks, { type: "audio/wav" }); + const llm = new ChatGPTApi(); + const transcription = await llm.transcription({ file: audioBlob }); + this.onTranscription(transcription); + this.listeningStatus = false; + resolve(); + }); + + this.mediaRecorder!.stop(); + }); + } +} + +export class WebTranscriptionApi extends SpeechApi { + private listeningStatus = false; + private recognitionInstance: any | null = null; + + isListening = () => this.listeningStatus; + + constructor(transcriptionCallback?: TranscriptionCallback) { + super(); + if (isFirefox()) return; + const SpeechRecognition = + (window as any).SpeechRecognition || + (window as any).webkitSpeechRecognition; + this.recognitionInstance = new SpeechRecognition(); + this.recognitionInstance.continuous = true; + this.recognitionInstance.interimResults = true; + this.recognitionInstance.lang = getSTTLang(); + if (transcriptionCallback) { + this.onTranscriptionReceived(transcriptionCallback); + } + this.recognitionInstance.onresult = (event: any) => { + const result = event.results[event.results.length - 1]; + if (result.isFinal) { + this.onTranscription(result[0].transcript); + } + }; + } + + async start(): Promise { + this.listeningStatus = true; + await this.recognitionInstance.start(); + } + + async stop(): Promise { + this.listeningStatus = false; + await this.recognitionInstance.stop(); + } +} diff --git a/package.json b/package.json index eb0a5ef67..02d36ae31 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "html-to-image": "^1.11.11", "lodash-es": "^4.17.21", "mermaid": "^10.6.1", + "markdown-to-txt": "^2.0.1", "nanoid": "^5.0.3", "next": "^14.1.1", "node-fetch": "^3.3.1", @@ -73,4 +74,4 @@ "lint-staged/yaml": "^2.2.2" }, "packageManager": "yarn@1.22.19" -} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 793c845d7..3b76a49e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4378,11 +4378,21 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.escape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" + integrity sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.unescape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" + integrity sha512-DhhGRshNS1aX6s5YdBE3njCCouPgnG29ebyHvImlZzXZf2SHgt+J08DHgytTPnpywNbO1Y8mNUFyQuIDBq2JZg== + lodash@^4.17.21: version "4.17.21" resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -4438,6 +4448,20 @@ markdown-table@^3.0.0: resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd" integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw== +markdown-to-txt@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/markdown-to-txt/-/markdown-to-txt-2.0.1.tgz#bfd6233a2635443cc24900a158b60c6af36ce9c5" + integrity sha512-Hsj7KTN8k1gutlLum3vosHwVZGnv8/cbYKWVkUyo/D1rzOYddbDesILebRfOsaVfjIBJank/AVOySBlHAYqfZw== + dependencies: + lodash.escape "^4.0.1" + lodash.unescape "^4.0.1" + marked "^4.0.14" + +marked@^4.0.14: + version "4.3.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== + mdast-util-definitions@^5.0.0: version "5.1.2" resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz#9910abb60ac5d7115d6819b57ae0bcef07a3f7a7" From 93f1762e6c85e2a71a70534dc8a84b322d3643e7 Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Tue, 27 Aug 2024 17:02:44 +0800 Subject: [PATCH 006/352] chore: wip --- app/client/platforms/alibaba.ts | 7 +++++++ app/client/platforms/anthropic.ts | 7 +++++++ app/client/platforms/baidu.ts | 7 +++++++ app/client/platforms/bytedance.ts | 7 +++++++ app/client/platforms/google.ts | 6 ++++++ app/client/platforms/iflytek.ts | 7 +++++++ app/client/platforms/moonshot.ts | 7 +++++++ app/client/platforms/tencent.ts | 7 +++++++ 8 files changed, 55 insertions(+) diff --git a/app/client/platforms/alibaba.ts b/app/client/platforms/alibaba.ts index d5fa3042f..477ef193f 100644 --- a/app/client/platforms/alibaba.ts +++ b/app/client/platforms/alibaba.ts @@ -83,6 +83,13 @@ export class QwenApi implements LLMApi { return res?.output?.choices?.at(0)?.message?.content ?? ""; } + speech(options: SpeechOptions): Promise { + throw new Error("Method not implemented."); + } + transcription(options: TranscriptionOptions): Promise { + throw new Error("Method not implemented."); + } + async chat(options: ChatOptions) { const messages = options.messages.map((v) => ({ role: v.role, diff --git a/app/client/platforms/anthropic.ts b/app/client/platforms/anthropic.ts index b079ba1ad..df4dc7f38 100644 --- a/app/client/platforms/anthropic.ts +++ b/app/client/platforms/anthropic.ts @@ -73,6 +73,13 @@ const ClaudeMapper = { const keys = ["claude-2, claude-instant-1"]; export class ClaudeApi implements LLMApi { + speech(options: SpeechOptions): Promise { + throw new Error("Method not implemented."); + } + transcription(options: TranscriptionOptions): Promise { + throw new Error("Method not implemented."); + } + extractMessage(res: any) { console.log("[Response] claude response: ", res); diff --git a/app/client/platforms/baidu.ts b/app/client/platforms/baidu.ts index 3be147f49..2b3119c2a 100644 --- a/app/client/platforms/baidu.ts +++ b/app/client/platforms/baidu.ts @@ -75,6 +75,13 @@ export class ErnieApi implements LLMApi { return [baseUrl, path].join("/"); } + speech(options: SpeechOptions): Promise { + throw new Error("Method not implemented."); + } + transcription(options: TranscriptionOptions): Promise { + throw new Error("Method not implemented."); + } + async chat(options: ChatOptions) { const messages = options.messages.map((v) => ({ // "error_code": 336006, "error_msg": "the role of message with even index in the messages must be user or function", diff --git a/app/client/platforms/bytedance.ts b/app/client/platforms/bytedance.ts index 7677cafe1..31c0be3d3 100644 --- a/app/client/platforms/bytedance.ts +++ b/app/client/platforms/bytedance.ts @@ -77,6 +77,13 @@ export class DoubaoApi implements LLMApi { return res.choices?.at(0)?.message?.content ?? ""; } + speech(options: SpeechOptions): Promise { + throw new Error("Method not implemented."); + } + transcription(options: TranscriptionOptions): Promise { + throw new Error("Method not implemented."); + } + async chat(options: ChatOptions) { const messages = options.messages.map((v) => ({ role: v.role, diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index 12d884635..6c6c3b25e 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -56,6 +56,12 @@ export class GeminiProApi implements LLMApi { "" ); } + speech(options: SpeechOptions): Promise { + throw new Error("Method not implemented."); + } + transcription(options: TranscriptionOptions): Promise { + throw new Error("Method not implemented."); + } async chat(options: ChatOptions): Promise { const apiClient = this; let multimodal = false; diff --git a/app/client/platforms/iflytek.ts b/app/client/platforms/iflytek.ts index 73cea5ba0..77a4571e1 100644 --- a/app/client/platforms/iflytek.ts +++ b/app/client/platforms/iflytek.ts @@ -53,6 +53,13 @@ export class SparkApi implements LLMApi { return res.choices?.at(0)?.message?.content ?? ""; } + speech(options: SpeechOptions): Promise { + throw new Error("Method not implemented."); + } + transcription(options: TranscriptionOptions): Promise { + throw new Error("Method not implemented."); + } + async chat(options: ChatOptions) { const messages: ChatOptions["messages"] = []; for (const v of options.messages) { diff --git a/app/client/platforms/moonshot.ts b/app/client/platforms/moonshot.ts index 7d257ccb2..22bbaf01f 100644 --- a/app/client/platforms/moonshot.ts +++ b/app/client/platforms/moonshot.ts @@ -66,6 +66,13 @@ export class MoonshotApi implements LLMApi { return res.choices?.at(0)?.message?.content ?? ""; } + speech(options: SpeechOptions): Promise { + throw new Error("Method not implemented."); + } + transcription(options: TranscriptionOptions): Promise { + throw new Error("Method not implemented."); + } + async chat(options: ChatOptions) { const messages: ChatOptions["messages"] = []; for (const v of options.messages) { diff --git a/app/client/platforms/tencent.ts b/app/client/platforms/tencent.ts index 579008a9b..5eb48791b 100644 --- a/app/client/platforms/tencent.ts +++ b/app/client/platforms/tencent.ts @@ -89,6 +89,13 @@ export class HunyuanApi implements LLMApi { return res.Choices?.at(0)?.Message?.Content ?? ""; } + speech(options: SpeechOptions): Promise { + throw new Error("Method not implemented."); + } + transcription(options: TranscriptionOptions): Promise { + throw new Error("Method not implemented."); + } + async chat(options: ChatOptions) { const visionModel = isVisionModel(options.config.model); const messages = options.messages.map((v, index) => ({ From f86b220c922a9209e99e2a3647e97ab72f47de3d Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Tue, 27 Aug 2024 19:50:16 +0800 Subject: [PATCH 007/352] feat: add voice action --- app/components/chat.tsx | 113 ++++++++++++++++++-------------------- app/icons/voice-white.svg | 6 +- 2 files changed, 55 insertions(+), 64 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index e5391ad22..624b7618e 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -453,6 +453,7 @@ export function ChatActions(props: { showPromptHints: () => void; hitBottom: boolean; uploading: boolean; + setUserInput: (input: string) => void; }) { const config = useAppConfig(); const navigate = useNavigate(); @@ -544,6 +545,44 @@ export function ChatActions(props: { } }, [chatStore, currentModel, models]); + const [isListening, setIsListening] = useState(false); + const [isTranscription, setIsTranscription] = useState(false); + const [speechApi, setSpeechApi] = useState(null); + + useEffect(() => { + if (isFirefox()) config.sttConfig.engine = FIREFOX_DEFAULT_STT_ENGINE; + setSpeechApi( + config.sttConfig.engine === DEFAULT_STT_ENGINE + ? new WebTranscriptionApi((transcription) => + onRecognitionEnd(transcription), + ) + : new OpenAITranscriptionApi((transcription) => + onRecognitionEnd(transcription), + ), + ); + }, []); + + const startListening = async () => { + if (speechApi) { + await speechApi.start(); + setIsListening(true); + } + }; + const stopListening = async () => { + if (speechApi) { + if (config.sttConfig.engine !== DEFAULT_STT_ENGINE) + setIsTranscription(true); + await speechApi.stop(); + setIsListening(false); + } + }; + const onRecognitionEnd = (finalTranscript: string) => { + console.log(finalTranscript); + if (finalTranscript) props.setUserInput(finalTranscript); + if (config.sttConfig.engine !== DEFAULT_STT_ENGINE) + setIsTranscription(false); + }; + return (
{couldStop && ( @@ -768,6 +807,16 @@ export function ChatActions(props: { }} /> )} + + {config.sttConfig.enable && ( + + isListening ? await stopListening() : await startListening() + } + text={isListening ? Locale.Chat.StopSpeak : Locale.Chat.StartSpeak} + icon={} + /> + )}
); } @@ -940,33 +989,6 @@ function _Chat() { } }; - const [isListening, setIsListening] = useState(false); - const [isTranscription, setIsTranscription] = useState(false); - const [speechApi, setSpeechApi] = useState(null); - - const startListening = async () => { - if (speechApi) { - await speechApi.start(); - setIsListening(true); - } - }; - - const stopListening = async () => { - if (speechApi) { - if (config.sttConfig.engine !== DEFAULT_STT_ENGINE) - setIsTranscription(true); - await speechApi.stop(); - setIsListening(false); - } - }; - - const onRecognitionEnd = (finalTranscript: string) => { - console.log(finalTranscript); - if (finalTranscript) setUserInput(finalTranscript); - if (config.sttConfig.engine !== DEFAULT_STT_ENGINE) - setIsTranscription(false); - }; - const doSubmit = (userInput: string) => { if (userInput.trim() === "") return; const matchCommand = chatCommands.match(userInput); @@ -1037,16 +1059,6 @@ function _Chat() { } }); // eslint-disable-next-line react-hooks/exhaustive-deps - if (isFirefox()) config.sttConfig.engine = FIREFOX_DEFAULT_STT_ENGINE; - setSpeechApi( - config.sttConfig.engine === DEFAULT_STT_ENGINE - ? new WebTranscriptionApi((transcription) => - onRecognitionEnd(transcription), - ) - : new OpenAITranscriptionApi((transcription) => - onRecognitionEnd(transcription), - ), - ); }, []); // check if should send message @@ -1784,6 +1796,7 @@ function _Chat() { setUserInput("/"); onSearch(""); }} + setUserInput={setUserInput} /> diff --git a/app/icons/voice-white.svg b/app/icons/voice-white.svg index 0a4a0ae31..e7d5cbcc8 100644 --- a/app/icons/voice-white.svg +++ b/app/icons/voice-white.svg @@ -1,4 +1,4 @@ - + @@ -7,9 +7,9 @@ - + - + From e9f90a4d82edbb446aedaef7ae27984d21b870d4 Mon Sep 17 00:00:00 2001 From: Meaqua Date: Tue, 27 Aug 2024 21:49:00 +0800 Subject: [PATCH 008/352] fix: i18n --- app/locales/en.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/app/locales/en.ts b/app/locales/en.ts index 1aa2137ec..ae20a0d4f 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -490,6 +490,37 @@ const en: LocaleType = { SubTitle: "A larger value decreasing the likelihood to repeat the same line", }, + TTS: { + Enable: { + Title: "Enable TTS", + SubTitle: "Enable text-to-speech service", + }, + Autoplay: { + Title: "Enable Autoplay", + SubTitle: + "Automatically generate speech and play, you need to enable the text-to-speech switch first", + }, + Model: "Model", + Voice: { + Title: "Voice", + SubTitle: "The voice to use when generating the audio", + }, + Speed: { + Title: "Speed", + SubTitle: "The speed of the generated audio", + }, + Engine: "TTS Engine", + }, + STT: { + Enable: { + Title: "Enable STT", + SubTitle: "Enable Speech-to-Text", + }, + Engine: { + Title: "STT Engine", + SubTitle: "Text-to-Speech Engine", + }, + }, }, Store: { DefaultTopic: "New Conversation", From ed5aea0521797841981919fa3c1ebb6340c35168 Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Wed, 28 Aug 2024 12:37:19 +0800 Subject: [PATCH 009/352] fix: bug --- app/client/platforms/alibaba.ts | 2 ++ app/client/platforms/anthropic.ts | 9 ++++++++- app/client/platforms/baidu.ts | 2 ++ app/client/platforms/bytedance.ts | 2 ++ app/client/platforms/google.ts | 10 +++++++++- app/client/platforms/iflytek.ts | 9 ++++++++- app/client/platforms/moonshot.ts | 2 ++ app/client/platforms/tencent.ts | 2 ++ 8 files changed, 35 insertions(+), 3 deletions(-) diff --git a/app/client/platforms/alibaba.ts b/app/client/platforms/alibaba.ts index 477ef193f..e839c69f0 100644 --- a/app/client/platforms/alibaba.ts +++ b/app/client/platforms/alibaba.ts @@ -12,6 +12,8 @@ import { getHeaders, LLMApi, LLMModel, + SpeechOptions, + TranscriptionOptions, MultimodalContent, } from "../api"; import Locale from "../../locales"; diff --git a/app/client/platforms/anthropic.ts b/app/client/platforms/anthropic.ts index df4dc7f38..f0f95f0fd 100644 --- a/app/client/platforms/anthropic.ts +++ b/app/client/platforms/anthropic.ts @@ -1,5 +1,12 @@ import { ACCESS_CODE_PREFIX, Anthropic, ApiPath } from "@/app/constant"; -import { ChatOptions, getHeaders, LLMApi, MultimodalContent } from "../api"; +import { + ChatOptions, + getHeaders, + LLMApi, + MultimodalContent, + SpeechOptions, + TranscriptionOptions, +} from "../api"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { getClientConfig } from "@/app/config/client"; import { DEFAULT_API_HOST } from "@/app/constant"; diff --git a/app/client/platforms/baidu.ts b/app/client/platforms/baidu.ts index 2b3119c2a..0c2be5fb1 100644 --- a/app/client/platforms/baidu.ts +++ b/app/client/platforms/baidu.ts @@ -14,6 +14,8 @@ import { LLMApi, LLMModel, MultimodalContent, + SpeechOptions, + TranscriptionOptions, } from "../api"; import Locale from "../../locales"; import { diff --git a/app/client/platforms/bytedance.ts b/app/client/platforms/bytedance.ts index 31c0be3d3..5a0c9b8b1 100644 --- a/app/client/platforms/bytedance.ts +++ b/app/client/platforms/bytedance.ts @@ -13,6 +13,8 @@ import { LLMApi, LLMModel, MultimodalContent, + SpeechOptions, + TranscriptionOptions, } from "../api"; import Locale from "../../locales"; import { diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index 6c6c3b25e..c8d3658b3 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -1,5 +1,13 @@ import { ApiPath, Google, REQUEST_TIMEOUT_MS } from "@/app/constant"; -import { ChatOptions, getHeaders, LLMApi, LLMModel, LLMUsage } from "../api"; +import { + ChatOptions, + getHeaders, + LLMApi, + LLMModel, + LLMUsage, + SpeechOptions, + TranscriptionOptions, +} from "../api"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { getClientConfig } from "@/app/config/client"; import { DEFAULT_API_HOST } from "@/app/constant"; diff --git a/app/client/platforms/iflytek.ts b/app/client/platforms/iflytek.ts index 77a4571e1..6463e052e 100644 --- a/app/client/platforms/iflytek.ts +++ b/app/client/platforms/iflytek.ts @@ -7,7 +7,14 @@ import { } from "@/app/constant"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; -import { ChatOptions, getHeaders, LLMApi, LLMModel } from "../api"; +import { + ChatOptions, + getHeaders, + LLMApi, + LLMModel, + SpeechOptions, + TranscriptionOptions, +} from "../api"; import Locale from "../../locales"; import { EventStreamContentType, diff --git a/app/client/platforms/moonshot.ts b/app/client/platforms/moonshot.ts index 22bbaf01f..b5a8aa588 100644 --- a/app/client/platforms/moonshot.ts +++ b/app/client/platforms/moonshot.ts @@ -20,6 +20,8 @@ import { LLMModel, LLMUsage, MultimodalContent, + SpeechOptions, + TranscriptionOptions, } from "../api"; import Locale from "../../locales"; import { diff --git a/app/client/platforms/tencent.ts b/app/client/platforms/tencent.ts index 5eb48791b..1739b7a14 100644 --- a/app/client/platforms/tencent.ts +++ b/app/client/platforms/tencent.ts @@ -8,6 +8,8 @@ import { LLMApi, LLMModel, MultimodalContent, + SpeechOptions, + TranscriptionOptions, } from "../api"; import Locale from "../../locales"; import { From 318e0989a2c28ae323d3f00d8256a7e48169e4a6 Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Wed, 28 Aug 2024 13:13:41 +0800 Subject: [PATCH 010/352] fix: transcription headers --- app/client/api.ts | 13 ++++++++----- app/components/chat.tsx | 1 - app/components/tts-config.tsx | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/client/api.ts b/app/client/api.ts index 8d0877a0d..7e1d0135e 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -220,13 +220,16 @@ export function validString(x: string): boolean { return x?.length > 0; } -export function getHeaders() { +export function getHeaders(ignoreHeaders?: boolean) { const accessStore = useAccessStore.getState(); const chatStore = useChatStore.getState(); - const headers: Record = { - "Content-Type": "application/json", - Accept: "application/json", - }; + let headers: Record = {}; + if (!ignoreHeaders) { + headers = { + "Content-Type": "application/json", + Accept: "application/json", + }; + } const clientConfig = getClientConfig(); diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 624b7618e..f4ebd70d8 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -1686,7 +1686,6 @@ function _Chat() { ? Locale.Chat.Actions.StopSpeech : Locale.Chat.Actions.Speech } - loding={speechLoading} icon={ speechStatus ? ( diff --git a/app/components/tts-config.tsx b/app/components/tts-config.tsx index f86e3bc52..39ae85730 100644 --- a/app/components/tts-config.tsx +++ b/app/components/tts-config.tsx @@ -1,4 +1,4 @@ -import { PluginConfig, TTSConfig, TTSConfigValidator } from "../store"; +import { TTSConfig, TTSConfigValidator } from "../store"; import Locale from "../locales"; import { ListItem, Select } from "./ui-lib"; @@ -111,6 +111,7 @@ export function TTSConfigList(props: { subTitle={Locale.Settings.TTS.Speed.SubTitle} > Date: Wed, 28 Aug 2024 13:15:52 +0800 Subject: [PATCH 011/352] fix: i18n --- app/locales/en.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/locales/en.ts b/app/locales/en.ts index ae20a0d4f..dd13ff99c 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -80,6 +80,8 @@ const en: LocaleType = { return inputHints + ", / to search prompts, : to use commands"; }, Send: "Send", + StartSpeak: "Start Speak", + StopSpeak: "Stop Speak", Config: { Reset: "Reset to Default", SaveAs: "Save as Mask", From ebaeb5a0d5cb2fa514b2529b015ce7c99f13de15 Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Wed, 11 Sep 2024 17:54:48 +0800 Subject: [PATCH 012/352] fix: selector css --- app/components/ui-lib.module.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/components/ui-lib.module.scss b/app/components/ui-lib.module.scss index 1cd966f19..28ecb7e68 100644 --- a/app/components/ui-lib.module.scss +++ b/app/components/ui-lib.module.scss @@ -312,8 +312,7 @@ min-width: 300px; .list { max-height: 90vh; - overflow-x: hidden; - overflow-y: auto; + overflow: hidden; .list-item { cursor: pointer; From a3585685df479cdb627442828b17f7cb46f33dca Mon Sep 17 00:00:00 2001 From: wuzhiqing Date: Fri, 26 Jul 2024 11:02:02 +0800 Subject: [PATCH 013/352] chore: add ESLint plugin and rules to remove unused imports - Installed eslint-plugin-unused-imports - Updated .eslintrc.json to include rules for detecting unused imports --- .eslintrc.json | 5 ++++- package.json | 1 + yarn.lock | 12 ++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index d229e86f2..5b5e88e67 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,4 +1,7 @@ { "extends": "next/core-web-vitals", - "plugins": ["prettier"] + "plugins": ["prettier", "unused-imports"], + "rules": { + "unused-imports/no-unused-imports": "warn" + } } diff --git a/package.json b/package.json index ca5fcc0f5..ac4c65f75 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "eslint-config-next": "13.4.19", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-unused-imports": "^3.2.0", "husky": "^8.0.0", "lint-staged": "^13.2.2", "prettier": "^3.0.2", diff --git a/yarn.lock b/yarn.lock index 4979e4d99..8751953ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3367,6 +3367,18 @@ eslint-plugin-react@^7.31.7: semver "^6.3.0" string.prototype.matchall "^4.0.8" +eslint-plugin-unused-imports@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.2.0.tgz#63a98c9ad5f622cd9f830f70bc77739f25ccfe0d" + integrity sha512-6uXyn6xdINEpxE1MtDjxQsyXB37lfyO2yKGVVgtD7WEWQGORSOZjgrD6hBhvGv4/SO+TOlS+UnC6JppRqbuwGQ== + dependencies: + eslint-rule-composer "^0.3.0" + +eslint-rule-composer@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" + integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg== + eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" From 6e79b9a7a2609691f41f1b1663a3bed455ebdc42 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Sat, 14 Sep 2024 14:19:11 +0800 Subject: [PATCH 014/352] add fork --- app/command.ts | 1 + app/components/chat.tsx | 1 + app/locales/cn.ts | 1 + app/store/chat.ts | 22 ++++++++++++++++++++++ 4 files changed, 25 insertions(+) diff --git a/app/command.ts b/app/command.ts index bea4e06f3..aec73ef53 100644 --- a/app/command.ts +++ b/app/command.ts @@ -38,6 +38,7 @@ interface ChatCommands { next?: Command; prev?: Command; clear?: Command; + fork?: Command; del?: Command; } diff --git a/app/components/chat.tsx b/app/components/chat.tsx index dafb98464..1b1c2a045 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -980,6 +980,7 @@ function _Chat() { chatStore.updateCurrentSession( (session) => (session.clearContextIndex = session.messages.length), ), + fork: () => chatStore.forkSession(), del: () => chatStore.deleteSession(chatStore.currentSessionIndex), }); diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 92e81bcb1..a93b2defc 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -51,6 +51,7 @@ const cn = { next: "下一个聊天", prev: "上一个聊天", clear: "清除上下文", + fork: "复制聊天", del: "删除聊天", }, InputActions: { diff --git a/app/store/chat.ts b/app/store/chat.ts index 58c105e7e..2b7fa7e34 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -195,6 +195,28 @@ export const useChatStore = createPersistStore( } const methods = { + forkSession() { + // 获取当前会话 + const currentSession = get().currentSession(); + if (!currentSession) return; + + const newSession = createEmptySession(); + + newSession.topic = currentSession.topic; + newSession.messages = [...currentSession.messages]; + newSession.mask = { + ...currentSession.mask, + modelConfig: { + ...currentSession.mask.modelConfig, + }, + }; + + set((state) => ({ + currentSessionIndex: 0, + sessions: [newSession, ...state.sessions], + })); + }, + clearSessions() { set(() => ({ sessions: [createEmptySession()], From 63ffd473d57385e38ee76acbc6a78cc13be13fcd Mon Sep 17 00:00:00 2001 From: DDDDD12138 Date: Sun, 15 Sep 2024 20:17:02 +0800 Subject: [PATCH 015/352] chore: remove unused imports --- app/api/[provider]/[...path]/route.ts | 2 +- app/api/alibaba.ts | 2 -- app/api/anthropic.ts | 1 - app/api/azure.ts | 1 - app/api/baidu.ts | 1 - app/api/common.ts | 7 +------ app/api/google.ts | 7 +------ app/api/iflytek.ts | 2 -- app/api/moonshot.ts | 2 -- app/api/tencent/route.ts | 9 +-------- app/client/api.ts | 1 - app/client/platforms/anthropic.ts | 11 ++--------- app/client/platforms/iflytek.ts | 2 +- app/client/platforms/moonshot.ts | 25 +++---------------------- app/client/platforms/openai.ts | 6 ------ app/components/artifacts.tsx | 1 - app/components/chat-list.tsx | 3 +-- app/components/chat.tsx | 1 - app/components/exporter.tsx | 2 +- app/components/mask.tsx | 3 +-- app/components/plugin.tsx | 2 +- app/components/sidebar.tsx | 1 - app/constant.ts | 4 +--- app/locales/cn.ts | 1 - app/locales/sk.ts | 1 - app/masks/index.ts | 3 --- app/store/plugin.ts | 1 - app/store/sync.ts | 9 +++++---- app/store/update.ts | 2 -- app/utils.ts | 3 +-- app/utils/cors.ts | 2 +- 31 files changed, 22 insertions(+), 96 deletions(-) diff --git a/app/api/[provider]/[...path]/route.ts b/app/api/[provider]/[...path]/route.ts index 24aa5ec04..dffb3e9da 100644 --- a/app/api/[provider]/[...path]/route.ts +++ b/app/api/[provider]/[...path]/route.ts @@ -1,5 +1,5 @@ import { ApiPath } from "@/app/constant"; -import { NextRequest, NextResponse } from "next/server"; +import { NextRequest } from "next/server"; import { handle as openaiHandler } from "../../openai"; import { handle as azureHandler } from "../../azure"; import { handle as googleHandler } from "../../google"; diff --git a/app/api/alibaba.ts b/app/api/alibaba.ts index 675d9f301..894b1ae4c 100644 --- a/app/api/alibaba.ts +++ b/app/api/alibaba.ts @@ -1,6 +1,5 @@ import { getServerSideConfig } from "@/app/config/server"; import { - Alibaba, ALIBABA_BASE_URL, ApiPath, ModelProvider, @@ -10,7 +9,6 @@ import { prettyObject } from "@/app/utils/format"; import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/app/api/auth"; import { isModelAvailableInServer } from "@/app/utils/model"; -import type { RequestPayload } from "@/app/client/platforms/openai"; const serverConfig = getServerSideConfig(); diff --git a/app/api/anthropic.ts b/app/api/anthropic.ts index 3d49f4c88..3ff747962 100644 --- a/app/api/anthropic.ts +++ b/app/api/anthropic.ts @@ -3,7 +3,6 @@ import { ANTHROPIC_BASE_URL, Anthropic, ApiPath, - DEFAULT_MODELS, ServiceProvider, ModelProvider, } from "@/app/constant"; diff --git a/app/api/azure.ts b/app/api/azure.ts index e2cb0c7e6..39d872e8c 100644 --- a/app/api/azure.ts +++ b/app/api/azure.ts @@ -1,4 +1,3 @@ -import { getServerSideConfig } from "@/app/config/server"; import { ModelProvider } from "@/app/constant"; import { prettyObject } from "@/app/utils/format"; import { NextRequest, NextResponse } from "next/server"; diff --git a/app/api/baidu.ts b/app/api/baidu.ts index f4315d186..0408b43c5 100644 --- a/app/api/baidu.ts +++ b/app/api/baidu.ts @@ -3,7 +3,6 @@ import { BAIDU_BASE_URL, ApiPath, ModelProvider, - BAIDU_OATUH_URL, ServiceProvider, } from "@/app/constant"; import { prettyObject } from "@/app/utils/format"; diff --git a/app/api/common.ts b/app/api/common.ts index 25decbf62..b4c792d6f 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -1,11 +1,6 @@ import { NextRequest, NextResponse } from "next/server"; import { getServerSideConfig } from "../config/server"; -import { - DEFAULT_MODELS, - OPENAI_BASE_URL, - GEMINI_BASE_URL, - ServiceProvider, -} from "../constant"; +import { OPENAI_BASE_URL, ServiceProvider } from "../constant"; import { isModelAvailableInServer } from "../utils/model"; import { cloudflareAIGatewayUrl } from "../utils/cloudflare"; diff --git a/app/api/google.ts b/app/api/google.ts index 98fe469bf..e6ab47256 100644 --- a/app/api/google.ts +++ b/app/api/google.ts @@ -1,12 +1,7 @@ import { NextRequest, NextResponse } from "next/server"; import { auth } from "./auth"; import { getServerSideConfig } from "@/app/config/server"; -import { - ApiPath, - GEMINI_BASE_URL, - Google, - ModelProvider, -} from "@/app/constant"; +import { ApiPath, GEMINI_BASE_URL, ModelProvider } from "@/app/constant"; import { prettyObject } from "@/app/utils/format"; const serverConfig = getServerSideConfig(); diff --git a/app/api/iflytek.ts b/app/api/iflytek.ts index eabdd9f4c..8b8227dce 100644 --- a/app/api/iflytek.ts +++ b/app/api/iflytek.ts @@ -1,6 +1,5 @@ import { getServerSideConfig } from "@/app/config/server"; import { - Iflytek, IFLYTEK_BASE_URL, ApiPath, ModelProvider, @@ -10,7 +9,6 @@ import { prettyObject } from "@/app/utils/format"; import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/app/api/auth"; import { isModelAvailableInServer } from "@/app/utils/model"; -import type { RequestPayload } from "@/app/client/platforms/openai"; // iflytek const serverConfig = getServerSideConfig(); diff --git a/app/api/moonshot.ts b/app/api/moonshot.ts index 247dd6183..5bf4807e3 100644 --- a/app/api/moonshot.ts +++ b/app/api/moonshot.ts @@ -1,6 +1,5 @@ import { getServerSideConfig } from "@/app/config/server"; import { - Moonshot, MOONSHOT_BASE_URL, ApiPath, ModelProvider, @@ -10,7 +9,6 @@ import { prettyObject } from "@/app/utils/format"; import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/app/api/auth"; import { isModelAvailableInServer } from "@/app/utils/model"; -import type { RequestPayload } from "@/app/client/platforms/openai"; const serverConfig = getServerSideConfig(); diff --git a/app/api/tencent/route.ts b/app/api/tencent/route.ts index 885909e7a..fc4f8c79e 100644 --- a/app/api/tencent/route.ts +++ b/app/api/tencent/route.ts @@ -1,15 +1,8 @@ import { getServerSideConfig } from "@/app/config/server"; -import { - TENCENT_BASE_URL, - ApiPath, - ModelProvider, - ServiceProvider, - Tencent, -} from "@/app/constant"; +import { TENCENT_BASE_URL, ModelProvider } from "@/app/constant"; import { prettyObject } from "@/app/utils/format"; import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/app/api/auth"; -import { isModelAvailableInServer } from "@/app/utils/model"; import { getHeader } from "@/app/utils/tencent"; const serverConfig = getServerSideConfig(); diff --git a/app/client/api.ts b/app/client/api.ts index cecc453ba..94296b9aa 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -1,7 +1,6 @@ import { getClientConfig } from "../config/client"; import { ACCESS_CODE_PREFIX, - Azure, ModelProvider, ServiceProvider, } from "../constant"; diff --git a/app/client/platforms/anthropic.ts b/app/client/platforms/anthropic.ts index 7dd39c9cd..df128c704 100644 --- a/app/client/platforms/anthropic.ts +++ b/app/client/platforms/anthropic.ts @@ -1,5 +1,5 @@ -import { ACCESS_CODE_PREFIX, Anthropic, ApiPath } from "@/app/constant"; -import { ChatOptions, getHeaders, LLMApi, MultimodalContent } from "../api"; +import { Anthropic, ApiPath } from "@/app/constant"; +import { ChatOptions, getHeaders, LLMApi } from "../api"; import { useAccessStore, useAppConfig, @@ -9,13 +9,6 @@ import { } from "@/app/store"; import { getClientConfig } from "@/app/config/client"; import { DEFAULT_API_HOST } from "@/app/constant"; -import { - EventStreamContentType, - fetchEventSource, -} from "@fortaine/fetch-event-source"; - -import Locale from "../../locales"; -import { prettyObject } from "@/app/utils/format"; import { getMessageTextContent, isVisionModel } from "@/app/utils"; import { preProcessImageContent, stream } from "@/app/utils/chat"; import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare"; diff --git a/app/client/platforms/iflytek.ts b/app/client/platforms/iflytek.ts index 73cea5ba0..e29b603e2 100644 --- a/app/client/platforms/iflytek.ts +++ b/app/client/platforms/iflytek.ts @@ -17,7 +17,7 @@ import { prettyObject } from "@/app/utils/format"; import { getClientConfig } from "@/app/config/client"; import { getMessageTextContent } from "@/app/utils"; -import { OpenAIListModelResponse, RequestPayload } from "./openai"; +import { RequestPayload } from "./openai"; export class SparkApi implements LLMApi { private disableListModels = true; diff --git a/app/client/platforms/moonshot.ts b/app/client/platforms/moonshot.ts index cd10d2f6c..d09c4619e 100644 --- a/app/client/platforms/moonshot.ts +++ b/app/client/platforms/moonshot.ts @@ -3,10 +3,8 @@ import { ApiPath, DEFAULT_API_HOST, - DEFAULT_MODELS, Moonshot, REQUEST_TIMEOUT_MS, - ServiceProvider, } from "@/app/constant"; import { useAccessStore, @@ -15,28 +13,11 @@ import { ChatMessageTool, usePluginStore, } from "@/app/store"; -import { collectModelsWithDefaultModel } from "@/app/utils/model"; -import { preProcessImageContent, stream } from "@/app/utils/chat"; -import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare"; - -import { - ChatOptions, - getHeaders, - LLMApi, - LLMModel, - LLMUsage, - MultimodalContent, -} from "../api"; -import Locale from "../../locales"; -import { - EventStreamContentType, - fetchEventSource, -} from "@fortaine/fetch-event-source"; -import { prettyObject } from "@/app/utils/format"; +import { stream } from "@/app/utils/chat"; +import { ChatOptions, getHeaders, LLMApi, LLMModel } from "../api"; import { getClientConfig } from "@/app/config/client"; import { getMessageTextContent } from "@/app/utils"; - -import { OpenAIListModelResponse, RequestPayload } from "./openai"; +import { RequestPayload } from "./openai"; export class MoonshotApi implements LLMApi { private disableListModels = true; diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 664ff872b..4f9dcd4d0 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -35,15 +35,9 @@ import { MultimodalContent, } from "../api"; import Locale from "../../locales"; -import { - EventStreamContentType, - fetchEventSource, -} from "@fortaine/fetch-event-source"; -import { prettyObject } from "@/app/utils/format"; import { getClientConfig } from "@/app/config/client"; import { getMessageTextContent, - getMessageImages, isVisionModel, isDalle3 as _isDalle3, } from "@/app/utils"; diff --git a/app/components/artifacts.tsx b/app/components/artifacts.tsx index d725ee659..ce187fbcb 100644 --- a/app/components/artifacts.tsx +++ b/app/components/artifacts.tsx @@ -7,7 +7,6 @@ import { useImperativeHandle, } from "react"; import { useParams } from "react-router"; -import { useWindowSize } from "@/app/utils"; import { IconButton } from "./button"; import { nanoid } from "nanoid"; import ExportIcon from "../icons/share.svg"; diff --git a/app/components/chat-list.tsx b/app/components/chat-list.tsx index 7ef6e7b83..03b1a5c88 100644 --- a/app/components/chat-list.tsx +++ b/app/components/chat-list.tsx @@ -1,5 +1,4 @@ import DeleteIcon from "../icons/delete.svg"; -import BotIcon from "../icons/bot.svg"; import styles from "./home.module.scss"; import { @@ -12,7 +11,7 @@ import { import { useChatStore } from "../store"; import Locale from "../locales"; -import { Link, useLocation, useNavigate } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; import { Path } from "../constant"; import { MaskAvatar } from "./mask"; import { Mask } from "../store/mask"; diff --git a/app/components/chat.tsx b/app/components/chat.tsx index fc7e04aef..e1b76db65 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -95,7 +95,6 @@ import { import { useNavigate } from "react-router-dom"; import { CHAT_PAGE_SIZE, - LAST_INPUT_KEY, Path, REQUEST_TIMEOUT_MS, UNFINISHED_INPUT, diff --git a/app/components/exporter.tsx b/app/components/exporter.tsx index 1771cc9b0..aba8dc544 100644 --- a/app/components/exporter.tsx +++ b/app/components/exporter.tsx @@ -1,5 +1,5 @@ /* eslint-disable @next/next/no-img-element */ -import { ChatMessage, ModelType, useAppConfig, useChatStore } from "../store"; +import { ChatMessage, useAppConfig, useChatStore } from "../store"; import Locale from "../locales"; import styles from "./exporter.module.scss"; import { diff --git a/app/components/mask.tsx b/app/components/mask.tsx index ee6c7da97..e4dd90826 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -37,7 +37,7 @@ import Locale, { AllLangs, ALL_LANG_OPTIONS, Lang } from "../locales"; import { useNavigate } from "react-router-dom"; import chatStyle from "./chat.module.scss"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { copyToClipboard, downloadAs, @@ -48,7 +48,6 @@ import { Updater } from "../typing"; import { ModelConfigList } from "./model-config"; import { FileName, Path } from "../constant"; import { BUILTIN_MASK_STORE } from "../masks"; -import { nanoid } from "nanoid"; import { DragDropContext, Droppable, diff --git a/app/components/plugin.tsx b/app/components/plugin.tsx index 6f0b37107..cf4ae946e 100644 --- a/app/components/plugin.tsx +++ b/app/components/plugin.tsx @@ -28,7 +28,7 @@ import { } from "./ui-lib"; import Locale from "../locales"; import { useNavigate } from "react-router-dom"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { getClientConfig } from "../config/client"; export function PluginPage() { diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 4ec0f8c84..b067d41be 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -7,7 +7,6 @@ import SettingsIcon from "../icons/settings.svg"; import GithubIcon from "../icons/github.svg"; import ChatGptIcon from "../icons/chatgpt.svg"; import AddIcon from "../icons/add.svg"; -import CloseIcon from "../icons/close.svg"; import DeleteIcon from "../icons/delete.svg"; import MaskIcon from "../icons/mask.svg"; import DragIcon from "../icons/drag.svg"; diff --git a/app/constant.ts b/app/constant.ts index 3d33a047e..e281c457d 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -1,5 +1,3 @@ -import path from "path"; - export const OWNER = "ChatGPTNextWeb"; export const REPO = "ChatGPT-Next-Web"; export const REPO_URL = `https://github.com/${OWNER}/${REPO}`; @@ -279,7 +277,7 @@ const openaiModels = [ "gpt-4-1106-preview", "dall-e-3", "o1-mini", - "o1-preview" + "o1-preview", ]; const googleModels = [ diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 92e81bcb1..3dd120a32 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -1,4 +1,3 @@ -import { ShortcutKeyModal } from "../components/chat"; import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; diff --git a/app/locales/sk.ts b/app/locales/sk.ts index 2586aaaa7..4bf9bf443 100644 --- a/app/locales/sk.ts +++ b/app/locales/sk.ts @@ -1,6 +1,5 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; -import { LocaleType } from "./index"; import type { PartialLocaleType } from "./index"; // if you are adding a new translation, please use PartialLocaleType instead of LocaleType diff --git a/app/masks/index.ts b/app/masks/index.ts index 92f21c6ae..bff5c9bbe 100644 --- a/app/masks/index.ts +++ b/app/masks/index.ts @@ -1,7 +1,4 @@ import { Mask } from "../store/mask"; -import { CN_MASKS } from "./cn"; -import { TW_MASKS } from "./tw"; -import { EN_MASKS } from "./en"; import { type BuiltinMask } from "./typing"; export { type BuiltinMask } from "./typing"; diff --git a/app/store/plugin.ts b/app/store/plugin.ts index 2356c6db0..44679cbdc 100644 --- a/app/store/plugin.ts +++ b/app/store/plugin.ts @@ -1,5 +1,4 @@ import OpenAPIClientAxios from "openapi-client-axios"; -import { getLang, Lang } from "../locales"; import { StoreKey } from "../constant"; import { nanoid } from "nanoid"; import { createPersistStore } from "../utils/store"; diff --git a/app/store/sync.ts b/app/store/sync.ts index d3582e3c9..9db60d5f4 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -1,5 +1,4 @@ import { getClientConfig } from "../config/client"; -import { Updater } from "../typing"; import { ApiPath, STORAGE_KEY, StoreKey } from "../constant"; import { createPersistStore } from "../utils/store"; import { @@ -100,15 +99,17 @@ export const useSyncStore = createPersistStore( const remoteState = await client.get(config.username); if (!remoteState || remoteState === "") { await client.set(config.username, JSON.stringify(localState)); - console.log("[Sync] Remote state is empty, using local state instead."); - return + console.log( + "[Sync] Remote state is empty, using local state instead.", + ); + return; } else { const parsedRemoteState = JSON.parse( await client.get(config.username), ) as AppState; mergeAppState(localState, parsedRemoteState); setLocalAppState(localState); - } + } } catch (e) { console.log("[Sync] failed to get remote state", e); throw e; diff --git a/app/store/update.ts b/app/store/update.ts index 7253caffc..e68fde369 100644 --- a/app/store/update.ts +++ b/app/store/update.ts @@ -8,8 +8,6 @@ import { getClientConfig } from "../config/client"; import { createPersistStore } from "../utils/store"; import ChatGptIcon from "../icons/chatgpt.png"; import Locale from "../locales"; -import { use } from "react"; -import { useAppConfig } from "."; import { ClientApi } from "../client/api"; const ONE_MINUTE = 60 * 1000; diff --git a/app/utils.ts b/app/utils.ts index bf7450929..9a8bebf38 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -3,8 +3,7 @@ import { showToast } from "./components/ui-lib"; import Locale from "./locales"; import { RequestMessage } from "./client/api"; import { ServiceProvider, REQUEST_TIMEOUT_MS } from "./constant"; -import isObject from "lodash-es/isObject"; -import { fetch as tauriFetch, Body, ResponseType } from "@tauri-apps/api/http"; +import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; export function trimTopic(topic: string) { // Fix an issue where double quotes still show in the Indonesian language diff --git a/app/utils/cors.ts b/app/utils/cors.ts index fa348f9bf..f5e5ce6f0 100644 --- a/app/utils/cors.ts +++ b/app/utils/cors.ts @@ -1,5 +1,5 @@ import { getClientConfig } from "../config/client"; -import { ApiPath, DEFAULT_API_HOST } from "../constant"; +import { DEFAULT_API_HOST } from "../constant"; export function corsPath(path: string) { const baseUrl = getClientConfig()?.isApp ? `${DEFAULT_API_HOST}` : ""; From 9e1e0a72521cc84ef74499195f3734850b9ccd13 Mon Sep 17 00:00:00 2001 From: skymkmk Date: Mon, 16 Sep 2024 02:06:17 +0800 Subject: [PATCH 016/352] fix: persisted available models ard not be update after source code have been updated --- app/store/config.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/store/config.ts b/app/store/config.ts index 9985b9e76..679135ee7 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -143,6 +143,21 @@ export const useAppConfig = createPersistStore( { name: StoreKey.Config, version: 4, + + merge(persistedState, currentState) { + const state = persistedState as ChatConfig | undefined; + if (!state) return { ...currentState }; + const models = currentState.models.slice(); + state.models.forEach((pModel) => { + const idx = models.findIndex( + (v) => v.name === pModel.name && v.provider === pModel.provider, + ); + if (idx !== -1) models[idx] = pModel; + else models.push(pModel); + }); + return { ...currentState, ...state, models: models }; + }, + migrate(persistedState, version) { const state = persistedState as ChatConfig; From 36a0c7b8a3ab0c0b138940af7ec2efaf94aadcaf Mon Sep 17 00:00:00 2001 From: skymkmk Date: Mon, 16 Sep 2024 02:07:22 +0800 Subject: [PATCH 017/352] fix: default is forced to set gpt-3.5-turbo if no server default model have been set --- app/store/access.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/store/access.ts b/app/store/access.ts index a1014610e..7f8067161 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -204,8 +204,8 @@ export const useAccessStore = createPersistStore( .then((res) => { // Set default model from env request let defaultModel = res.defaultModel ?? ""; - DEFAULT_CONFIG.modelConfig.model = - defaultModel !== "" ? defaultModel : "gpt-3.5-turbo"; + if (defaultModel !== "") + DEFAULT_CONFIG.modelConfig.model = defaultModel; return res; }) .then((res: DangerConfig) => { From 4ddfa9af8d2645c288428e8c70754546271f3fe5 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Tue, 17 Sep 2024 22:28:13 +0800 Subject: [PATCH 018/352] ci: bump `actions/cache` to v4 --- .github/workflows/deploy_preview.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_preview.yml b/.github/workflows/deploy_preview.yml index bdbb78c27..30d9b85b4 100644 --- a/.github/workflows/deploy_preview.yml +++ b/.github/workflows/deploy_preview.yml @@ -49,7 +49,7 @@ jobs: run: npm install --global vercel@latest - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 id: cache-npm with: path: ~/.npm From 3ae8ec1af6011cec2ff57f62e66531c48576a9bf Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Wed, 18 Sep 2024 11:24:25 +0800 Subject: [PATCH 019/352] feat: tts --- app/client/api.ts | 11 --- app/client/platforms/alibaba.ts | 4 - app/client/platforms/anthropic.ts | 4 - app/client/platforms/baidu.ts | 4 - app/client/platforms/bytedance.ts | 4 - app/client/platforms/google.ts | 5 +- app/client/platforms/iflytek.ts | 4 - app/client/platforms/moonshot.ts | 4 - app/client/platforms/openai.ts | 42 ---------- app/client/platforms/tencent.ts | 4 - app/components/chat.tsx | 58 +------------- app/components/settings.tsx | 12 --- app/components/stt-config.tsx | 51 ------------ app/components/stt.module.scss | 119 ---------------------------- app/constant.ts | 5 -- app/locales/cn.ts | 10 --- app/locales/en.ts | 10 --- app/store/config.ts | 15 ---- app/utils/speech.ts | 126 ------------------------------ 19 files changed, 2 insertions(+), 490 deletions(-) delete mode 100644 app/components/stt-config.tsx delete mode 100644 app/components/stt.module.scss delete mode 100644 app/utils/speech.ts diff --git a/app/client/api.ts b/app/client/api.ts index 9f2cb2305..9d9977deb 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -64,16 +64,6 @@ export interface SpeechOptions { onController?: (controller: AbortController) => void; } -export interface TranscriptionOptions { - model?: "whisper-1"; - file: Blob; - language?: string; - prompt?: string; - response_format?: "json" | "text" | "srt" | "verbose_json" | "vtt"; - temperature?: number; - onController?: (controller: AbortController) => void; -} - export interface ChatOptions { messages: RequestMessage[]; config: LLMConfig; @@ -109,7 +99,6 @@ export interface LLMModelProvider { export abstract class LLMApi { abstract chat(options: ChatOptions): Promise; abstract speech(options: SpeechOptions): Promise; - abstract transcription(options: TranscriptionOptions): Promise; abstract usage(): Promise; abstract models(): Promise; } diff --git a/app/client/platforms/alibaba.ts b/app/client/platforms/alibaba.ts index e839c69f0..4ade9ebb9 100644 --- a/app/client/platforms/alibaba.ts +++ b/app/client/platforms/alibaba.ts @@ -13,7 +13,6 @@ import { LLMApi, LLMModel, SpeechOptions, - TranscriptionOptions, MultimodalContent, } from "../api"; import Locale from "../../locales"; @@ -88,9 +87,6 @@ export class QwenApi implements LLMApi { speech(options: SpeechOptions): Promise { throw new Error("Method not implemented."); } - transcription(options: TranscriptionOptions): Promise { - throw new Error("Method not implemented."); - } async chat(options: ChatOptions) { const messages = options.messages.map((v) => ({ diff --git a/app/client/platforms/anthropic.ts b/app/client/platforms/anthropic.ts index 2ab67ed13..e624a2e16 100644 --- a/app/client/platforms/anthropic.ts +++ b/app/client/platforms/anthropic.ts @@ -5,7 +5,6 @@ import { LLMApi, MultimodalContent, SpeechOptions, - TranscriptionOptions, } from "../api"; import { useAccessStore, @@ -90,9 +89,6 @@ export class ClaudeApi implements LLMApi { speech(options: SpeechOptions): Promise { throw new Error("Method not implemented."); } - transcription(options: TranscriptionOptions): Promise { - throw new Error("Method not implemented."); - } extractMessage(res: any) { console.log("[Response] claude response: ", res); diff --git a/app/client/platforms/baidu.ts b/app/client/platforms/baidu.ts index 0c2be5fb1..c360417c6 100644 --- a/app/client/platforms/baidu.ts +++ b/app/client/platforms/baidu.ts @@ -15,7 +15,6 @@ import { LLMModel, MultimodalContent, SpeechOptions, - TranscriptionOptions, } from "../api"; import Locale from "../../locales"; import { @@ -80,9 +79,6 @@ export class ErnieApi implements LLMApi { speech(options: SpeechOptions): Promise { throw new Error("Method not implemented."); } - transcription(options: TranscriptionOptions): Promise { - throw new Error("Method not implemented."); - } async chat(options: ChatOptions) { const messages = options.messages.map((v) => ({ diff --git a/app/client/platforms/bytedance.ts b/app/client/platforms/bytedance.ts index 5a0c9b8b1..a6e2d426e 100644 --- a/app/client/platforms/bytedance.ts +++ b/app/client/platforms/bytedance.ts @@ -14,7 +14,6 @@ import { LLMModel, MultimodalContent, SpeechOptions, - TranscriptionOptions, } from "../api"; import Locale from "../../locales"; import { @@ -82,9 +81,6 @@ export class DoubaoApi implements LLMApi { speech(options: SpeechOptions): Promise { throw new Error("Method not implemented."); } - transcription(options: TranscriptionOptions): Promise { - throw new Error("Method not implemented."); - } async chat(options: ChatOptions) { const messages = options.messages.map((v) => ({ diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index c8d3658b3..ecb5ce44b 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -6,7 +6,6 @@ import { LLMModel, LLMUsage, SpeechOptions, - TranscriptionOptions, } from "../api"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { getClientConfig } from "@/app/config/client"; @@ -67,9 +66,7 @@ export class GeminiProApi implements LLMApi { speech(options: SpeechOptions): Promise { throw new Error("Method not implemented."); } - transcription(options: TranscriptionOptions): Promise { - throw new Error("Method not implemented."); - } + async chat(options: ChatOptions): Promise { const apiClient = this; let multimodal = false; diff --git a/app/client/platforms/iflytek.ts b/app/client/platforms/iflytek.ts index 6463e052e..bd0c60838 100644 --- a/app/client/platforms/iflytek.ts +++ b/app/client/platforms/iflytek.ts @@ -13,7 +13,6 @@ import { LLMApi, LLMModel, SpeechOptions, - TranscriptionOptions, } from "../api"; import Locale from "../../locales"; import { @@ -63,9 +62,6 @@ export class SparkApi implements LLMApi { speech(options: SpeechOptions): Promise { throw new Error("Method not implemented."); } - transcription(options: TranscriptionOptions): Promise { - throw new Error("Method not implemented."); - } async chat(options: ChatOptions) { const messages: ChatOptions["messages"] = []; diff --git a/app/client/platforms/moonshot.ts b/app/client/platforms/moonshot.ts index 173ecd14c..bf8f18751 100644 --- a/app/client/platforms/moonshot.ts +++ b/app/client/platforms/moonshot.ts @@ -27,7 +27,6 @@ import { LLMUsage, MultimodalContent, SpeechOptions, - TranscriptionOptions, } from "../api"; import Locale from "../../locales"; import { @@ -77,9 +76,6 @@ export class MoonshotApi implements LLMApi { speech(options: SpeechOptions): Promise { throw new Error("Method not implemented."); } - transcription(options: TranscriptionOptions): Promise { - throw new Error("Method not implemented."); - } async chat(options: ChatOptions) { const messages: ChatOptions["messages"] = []; diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 71b7731fa..70b191f5c 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -34,7 +34,6 @@ import { LLMUsage, MultimodalContent, SpeechOptions, - TranscriptionOptions, } from "../api"; import Locale from "../../locales"; import { @@ -187,47 +186,6 @@ export class ChatGPTApi implements LLMApi { } } - async transcription(options: TranscriptionOptions): Promise { - const formData = new FormData(); - formData.append("file", options.file, "audio.wav"); - formData.append("model", options.model ?? "whisper-1"); - if (options.language) formData.append("language", options.language); - if (options.prompt) formData.append("prompt", options.prompt); - if (options.response_format) - formData.append("response_format", options.response_format); - if (options.temperature) - formData.append("temperature", options.temperature.toString()); - - console.log("[Request] openai audio transcriptions payload: ", options); - - const controller = new AbortController(); - options.onController?.(controller); - - try { - const path = this.path(OpenaiPath.TranscriptionPath, options.model); - const headers = getHeaders(true); - const payload = { - method: "POST", - body: formData, - signal: controller.signal, - headers: headers, - }; - - // make a fetch request - const requestTimeoutId = setTimeout( - () => controller.abort(), - REQUEST_TIMEOUT_MS, - ); - const res = await fetch(path, payload); - clearTimeout(requestTimeoutId); - const json = await res.json(); - return json.text; - } catch (e) { - console.log("[Request] failed to make a audio transcriptions request", e); - throw e; - } - } - async chat(options: ChatOptions) { const modelConfig = { ...useAppConfig.getState().modelConfig, diff --git a/app/client/platforms/tencent.ts b/app/client/platforms/tencent.ts index 1739b7a14..3e8f1a459 100644 --- a/app/client/platforms/tencent.ts +++ b/app/client/platforms/tencent.ts @@ -9,7 +9,6 @@ import { LLMModel, MultimodalContent, SpeechOptions, - TranscriptionOptions, } from "../api"; import Locale from "../../locales"; import { @@ -94,9 +93,6 @@ export class HunyuanApi implements LLMApi { speech(options: SpeechOptions): Promise { throw new Error("Method not implemented."); } - transcription(options: TranscriptionOptions): Promise { - throw new Error("Method not implemented."); - } async chat(options: ChatOptions) { const visionModel = isVisionModel(options.config.model); diff --git a/app/components/chat.tsx b/app/components/chat.tsx index cb0344077..59ffa01c1 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -10,7 +10,6 @@ import React, { } from "react"; import SendWhiteIcon from "../icons/send-white.svg"; -import VoiceWhiteIcon from "../icons/voice-white.svg"; import BrainIcon from "../icons/brain.svg"; import RenameIcon from "../icons/rename.svg"; import ExportIcon from "../icons/share.svg"; @@ -83,7 +82,7 @@ import dynamic from "next/dynamic"; import { ChatControllerPool } from "../client/controller"; import { DalleSize, DalleQuality, DalleStyle } from "../typing"; import { Prompt, usePromptStore } from "../store/prompt"; -import Locale, { getLang, getSTTLang } from "../locales"; +import Locale from "../locales"; import { IconButton } from "./button"; import styles from "./chat.module.scss"; @@ -100,9 +99,7 @@ import { import { useNavigate } from "react-router-dom"; import { CHAT_PAGE_SIZE, - DEFAULT_STT_ENGINE, DEFAULT_TTS_ENGINE, - FIREFOX_DEFAULT_STT_ENGINE, ModelProvider, LAST_INPUT_KEY, Path, @@ -123,11 +120,6 @@ import { MultimodalContent } from "../client/api"; const localStorage = safeLocalStorage(); import { ClientApi } from "../client/api"; import { createTTSPlayer } from "../utils/audio"; -import { - OpenAITranscriptionApi, - SpeechApi, - WebTranscriptionApi, -} from "../utils/speech"; import { MsEdgeTTS, OUTPUT_FORMAT } from "../utils/ms_edge_tts"; const ttsPlayer = createTTSPlayer(); @@ -556,44 +548,6 @@ export function ChatActions(props: { } }, [chatStore, currentModel, models]); - const [isListening, setIsListening] = useState(false); - const [isTranscription, setIsTranscription] = useState(false); - const [speechApi, setSpeechApi] = useState(null); - - useEffect(() => { - if (isFirefox()) config.sttConfig.engine = FIREFOX_DEFAULT_STT_ENGINE; - setSpeechApi( - config.sttConfig.engine === DEFAULT_STT_ENGINE - ? new WebTranscriptionApi((transcription) => - onRecognitionEnd(transcription), - ) - : new OpenAITranscriptionApi((transcription) => - onRecognitionEnd(transcription), - ), - ); - }, []); - - const startListening = async () => { - if (speechApi) { - await speechApi.start(); - setIsListening(true); - } - }; - const stopListening = async () => { - if (speechApi) { - if (config.sttConfig.engine !== DEFAULT_STT_ENGINE) - setIsTranscription(true); - await speechApi.stop(); - setIsListening(false); - } - }; - const onRecognitionEnd = (finalTranscript: string) => { - console.log(finalTranscript); - if (finalTranscript) props.setUserInput(finalTranscript); - if (config.sttConfig.engine !== DEFAULT_STT_ENGINE) - setIsTranscription(false); - }; - return (
{couldStop && ( @@ -828,16 +782,6 @@ export function ChatActions(props: { icon={} /> )} - - {config.sttConfig.enable && ( - - isListening ? await stopListening() : await startListening() - } - text={isListening ? Locale.Chat.StopSpeak : Locale.Chat.StartSpeak} - icon={} - /> - )}
); } diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 47a72d79d..33bf8b2e7 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -81,7 +81,6 @@ import { nanoid } from "nanoid"; import { useMaskStore } from "../store/mask"; import { ProviderType } from "../utils/cloud"; import { TTSConfigList } from "./tts-config"; -import { STTConfigList } from "./stt-config"; function EditPromptModal(props: { id: string; onClose: () => void }) { const promptStore = usePromptStore(); @@ -1659,17 +1658,6 @@ export function Settings() { /> - - { - const sttConfig = { ...config.sttConfig }; - updater(sttConfig); - config.update((config) => (config.sttConfig = sttConfig)); - }} - /> - - diff --git a/app/components/stt-config.tsx b/app/components/stt-config.tsx deleted file mode 100644 index f83d28030..000000000 --- a/app/components/stt-config.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { STTConfig, STTConfigValidator } from "../store"; - -import Locale from "../locales"; -import { ListItem, Select } from "./ui-lib"; -import { DEFAULT_STT_ENGINES } from "../constant"; -import { isFirefox } from "../utils"; - -export function STTConfigList(props: { - sttConfig: STTConfig; - updateConfig: (updater: (config: STTConfig) => void) => void; -}) { - return ( - <> - - - props.updateConfig( - (config) => (config.enable = e.currentTarget.checked), - ) - } - > - - {!isFirefox() && ( - - - - )} - - ); -} diff --git a/app/components/stt.module.scss b/app/components/stt.module.scss deleted file mode 100644 index ba9f382e4..000000000 --- a/app/components/stt.module.scss +++ /dev/null @@ -1,119 +0,0 @@ -@import "../styles/animation.scss"; -.plugin-page { - height: 100%; - display: flex; - flex-direction: column; - - .plugin-page-body { - padding: 20px; - overflow-y: auto; - - .plugin-filter { - width: 100%; - max-width: 100%; - margin-bottom: 20px; - animation: slide-in ease 0.3s; - height: 40px; - - display: flex; - - .search-bar { - flex-grow: 1; - max-width: 100%; - min-width: 0; - outline: none; - } - - .search-bar:focus { - border: 1px solid var(--primary); - } - - .plugin-filter-lang { - height: 100%; - margin-left: 10px; - } - - .plugin-create { - height: 100%; - margin-left: 10px; - box-sizing: border-box; - min-width: 80px; - } - } - - .plugin-item { - display: flex; - justify-content: space-between; - padding: 20px; - border: var(--border-in-light); - animation: slide-in ease 0.3s; - - &:not(:last-child) { - border-bottom: 0; - } - - &:first-child { - border-top-left-radius: 10px; - border-top-right-radius: 10px; - } - - &:last-child { - border-bottom-left-radius: 10px; - border-bottom-right-radius: 10px; - } - - .plugin-header { - display: flex; - align-items: center; - - .plugin-icon { - display: flex; - align-items: center; - justify-content: center; - margin-right: 10px; - } - - .plugin-title { - .plugin-name { - font-size: 14px; - font-weight: bold; - } - .plugin-info { - font-size: 12px; - } - .plugin-runtime-warning { - font-size: 12px; - color: #f86c6c; - } - } - } - - .plugin-actions { - display: flex; - flex-wrap: nowrap; - transition: all ease 0.3s; - justify-content: center; - align-items: center; - } - - @media screen and (max-width: 600px) { - display: flex; - flex-direction: column; - padding-bottom: 10px; - border-radius: 10px; - margin-bottom: 20px; - box-shadow: var(--card-shadow); - - &:not(:last-child) { - border-bottom: var(--border-in-light); - } - - .plugin-actions { - width: 100%; - justify-content: space-between; - padding-top: 10px; - } - } - } - } -} diff --git a/app/constant.ts b/app/constant.ts index d349268ba..7d7fcf78b 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -153,7 +153,6 @@ export const Anthropic = { export const OpenaiPath = { ChatPath: "v1/chat/completions", SpeechPath: "v1/audio/speech", - TranscriptionPath: "v1/audio/transcriptions", ImagePath: "v1/images/generations", UsagePath: "dashboard/billing/usage", SubsPath: "dashboard/billing/subscription", @@ -274,10 +273,6 @@ export const DEFAULT_TTS_VOICES = [ "shimmer", ]; -export const DEFAULT_STT_ENGINE = "WebAPI"; -export const DEFAULT_STT_ENGINES = ["WebAPI", "OpenAI Whisper"]; -export const FIREFOX_DEFAULT_STT_ENGINE = "OpenAI Whisper"; - const openaiModels = [ "gpt-3.5-turbo", "gpt-3.5-turbo-1106", diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 05e330491..979485a00 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -520,16 +520,6 @@ const cn = { SubTitle: "生成语音的速度", }, }, - STT: { - Enable: { - Title: "启用语音转文本", - SubTitle: "启用语音转文本", - }, - Engine: { - Title: "转换引擎", - SubTitle: "音频转换引擎", - }, - }, }, Store: { DefaultTopic: "新的聊天", diff --git a/app/locales/en.ts b/app/locales/en.ts index 0c2d2d27d..4bf065033 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -527,16 +527,6 @@ const en: LocaleType = { }, Engine: "TTS Engine", }, - STT: { - Enable: { - Title: "Enable STT", - SubTitle: "Enable Speech-to-Text", - }, - Engine: { - Title: "STT Engine", - SubTitle: "Text-to-Speech Engine", - }, - }, }, Store: { DefaultTopic: "New Conversation", diff --git a/app/store/config.ts b/app/store/config.ts index e2a2f8747..39268c69b 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -5,8 +5,6 @@ import { DEFAULT_INPUT_TEMPLATE, DEFAULT_MODELS, DEFAULT_SIDEBAR_WIDTH, - DEFAULT_STT_ENGINE, - DEFAULT_STT_ENGINES, DEFAULT_TTS_ENGINE, DEFAULT_TTS_ENGINES, DEFAULT_TTS_MODEL, @@ -23,8 +21,6 @@ export type TTSModelType = (typeof DEFAULT_TTS_MODELS)[number]; export type TTSVoiceType = (typeof DEFAULT_TTS_VOICES)[number]; export type TTSEngineType = (typeof DEFAULT_TTS_ENGINES)[number]; -export type STTEngineType = (typeof DEFAULT_STT_ENGINES)[number]; - export enum SubmitKey { Enter = "Enter", CtrlEnter = "Ctrl + Enter", @@ -90,17 +86,12 @@ export const DEFAULT_CONFIG = { voice: DEFAULT_TTS_VOICE, speed: 1.0, }, - sttConfig: { - enable: false, - engine: DEFAULT_STT_ENGINE, - }, }; export type ChatConfig = typeof DEFAULT_CONFIG; export type ModelConfig = ChatConfig["modelConfig"]; export type TTSConfig = ChatConfig["ttsConfig"]; -export type STTConfig = ChatConfig["sttConfig"]; export function limitNumber( x: number, @@ -130,12 +121,6 @@ export const TTSConfigValidator = { }, }; -export const STTConfigValidator = { - engine(x: string) { - return x as STTEngineType; - }, -}; - export const ModalConfigValidator = { model(x: string) { return x as ModelType; diff --git a/app/utils/speech.ts b/app/utils/speech.ts deleted file mode 100644 index dc8102879..000000000 --- a/app/utils/speech.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { ChatGPTApi } from "../client/platforms/openai"; -import { getSTTLang } from "../locales"; -import { isFirefox } from "../utils"; - -export type TranscriptionCallback = (transcription: string) => void; - -export abstract class SpeechApi { - protected onTranscription: TranscriptionCallback = () => {}; - - abstract isListening(): boolean; - abstract start(): Promise; - abstract stop(): Promise; - - onTranscriptionReceived(callback: TranscriptionCallback) { - this.onTranscription = callback; - } -} - -export class OpenAITranscriptionApi extends SpeechApi { - private listeningStatus = false; - private mediaRecorder: MediaRecorder | null = null; - private stream: MediaStream | null = null; - private audioChunks: Blob[] = []; - - isListening = () => this.listeningStatus; - - constructor(transcriptionCallback?: TranscriptionCallback) { - super(); - if (transcriptionCallback) { - this.onTranscriptionReceived(transcriptionCallback); - } - } - - async start(): Promise { - // @ts-ignore - navigator.getUserMedia = - // @ts-ignore - navigator.getUserMedia || - // @ts-ignore - navigator.webkitGetUserMedia || - // @ts-ignore - navigator.mozGetUserMedia || - // @ts-ignore - navigator.msGetUserMedia; - if (navigator.mediaDevices) { - const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); - this.mediaRecorder = new MediaRecorder(stream); - this.mediaRecorder.ondataavailable = (e) => { - if (e.data && e.data.size > 0) { - this.audioChunks.push(e.data); - } - }; - - this.stream = stream; - } else { - console.warn("Media Decives will work only with SSL"); - return; - } - - this.audioChunks = []; - - // this.recorder.addEventListener("dataavailable", (event) => { - // this.audioChunks.push(event.data); - // }); - - this.mediaRecorder.start(1000); - this.listeningStatus = true; - } - - async stop(): Promise { - if (!this.mediaRecorder || !this.listeningStatus) { - return; - } - - return new Promise((resolve) => { - this.mediaRecorder!.addEventListener("stop", async () => { - const audioBlob = new Blob(this.audioChunks, { type: "audio/wav" }); - const llm = new ChatGPTApi(); - const transcription = await llm.transcription({ file: audioBlob }); - this.onTranscription(transcription); - this.listeningStatus = false; - resolve(); - }); - - this.mediaRecorder!.stop(); - }); - } -} - -export class WebTranscriptionApi extends SpeechApi { - private listeningStatus = false; - private recognitionInstance: any | null = null; - - isListening = () => this.listeningStatus; - - constructor(transcriptionCallback?: TranscriptionCallback) { - super(); - if (isFirefox()) return; - const SpeechRecognition = - (window as any).SpeechRecognition || - (window as any).webkitSpeechRecognition; - this.recognitionInstance = new SpeechRecognition(); - this.recognitionInstance.continuous = true; - this.recognitionInstance.interimResults = true; - this.recognitionInstance.lang = getSTTLang(); - if (transcriptionCallback) { - this.onTranscriptionReceived(transcriptionCallback); - } - this.recognitionInstance.onresult = (event: any) => { - const result = event.results[event.results.length - 1]; - if (result.isFinal) { - this.onTranscription(result[0].transcript); - } - }; - } - - async start(): Promise { - this.listeningStatus = true; - await this.recognitionInstance.start(); - } - - async stop(): Promise { - this.listeningStatus = false; - await this.recognitionInstance.stop(); - } -} From fd47bc1dc3e969d98410f0ea384b71c5ec26de34 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Wed, 18 Sep 2024 13:56:44 +0800 Subject: [PATCH 020/352] Add English copy --- app/locales/en.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/locales/en.ts b/app/locales/en.ts index 09b76f1fa..80859c543 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -52,6 +52,7 @@ const en: LocaleType = { next: "Next Chat", prev: "Previous Chat", clear: "Clear Context", + fork: "Copy Chat", del: "Delete Chat", }, InputActions: { From a3b664763e8438196d29a074df366abef814b4c6 Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Wed, 18 Sep 2024 14:57:43 +0800 Subject: [PATCH 021/352] chore: default header --- app/client/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/api.ts b/app/client/api.ts index 97a0d061a..8285b4d9f 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -216,7 +216,7 @@ export function validString(x: string): boolean { return x?.length > 0; } -export function getHeaders(ignoreHeaders?: boolean) { +export function getHeaders(ignoreHeaders: boolean = false) { const accessStore = useAccessStore.getState(); const chatStore = useChatStore.getState(); let headers: Record = {}; From 7f1b44befe8449f767968f545742049ff90a089b Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Wed, 18 Sep 2024 15:04:41 +0800 Subject: [PATCH 022/352] fix: css --- app/components/sidebar.tsx | 5 ----- app/components/ui-lib.module.scss | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 4ec0f8c84..7d5ca2a68 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -254,11 +254,6 @@ export function SideBar(props: { className?: string }) { {showPluginSelector && ( { return { title: item.name, diff --git a/app/components/ui-lib.module.scss b/app/components/ui-lib.module.scss index 28ecb7e68..1cd966f19 100644 --- a/app/components/ui-lib.module.scss +++ b/app/components/ui-lib.module.scss @@ -312,7 +312,8 @@ min-width: 300px; .list { max-height: 90vh; - overflow: hidden; + overflow-x: hidden; + overflow-y: auto; .list-item { cursor: pointer; From 10d7a64f8869e1b35cc2e296d111431f2a00945d Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Wed, 18 Sep 2024 15:37:21 +0800 Subject: [PATCH 023/352] fix: error --- app/client/platforms/openai.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index a1b83a922..d86be718b 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -79,7 +79,7 @@ export interface DalleRequestPayload { export class ChatGPTApi implements LLMApi { private disableListModels = true; - path(path: string, model?: string): string { + path(path: string): string { const accessStore = useAccessStore.getState(); let baseUrl = ""; @@ -157,7 +157,7 @@ export class ChatGPTApi implements LLMApi { options.onController?.(controller); try { - const speechPath = this.path(OpenaiPath.SpeechPath, options.model); + const speechPath = this.path(OpenaiPath.SpeechPath); const speechPayload = { method: "POST", body: JSON.stringify(requestPayload), From 8dc24403d8892d5b0f84c31b57405c1a93bba1c1 Mon Sep 17 00:00:00 2001 From: river Date: Wed, 18 Sep 2024 22:05:51 +0800 Subject: [PATCH 024/352] chore: Update Chinese translation for API key placeholder --- app/locales/cn.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 0017e8e42..2dee13515 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -360,7 +360,7 @@ const cn = { ApiKey: { Title: "API 密钥", SubTitle: "从 Google AI 获取您的 API 密钥", - Placeholder: "输入您的 Google AI Studio API 密钥", + Placeholder: "Google AI API KEY", }, Endpoint: { From 2f0d94a46b6fbee2621bed9c1ba0670490b470d5 Mon Sep 17 00:00:00 2001 From: river Date: Thu, 19 Sep 2024 00:05:06 +0800 Subject: [PATCH 025/352] chore: add auth tip --- app/components/auth.module.scss | 4 ++++ app/components/auth.tsx | 10 +++++----- app/constant.ts | 2 ++ app/locales/cn.ts | 1 + 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/components/auth.module.scss b/app/components/auth.module.scss index 6630c0613..c9c66f1a5 100644 --- a/app/components/auth.module.scss +++ b/app/components/auth.module.scss @@ -24,6 +24,10 @@ margin: 3vh 0; } + .auth-input-second { + margin: 0 0 3vh 0; + } + .auth-actions { display: flex; justify-content: center; diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 57118349b..9d10ae475 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -2,7 +2,7 @@ import styles from "./auth.module.scss"; import { IconButton } from "./button"; import { useNavigate } from "react-router-dom"; -import { Path } from "../constant"; +import { Path, SAAS_CHAT_URL } from "../constant"; import { useAccessStore } from "../store"; import Locale from "../locales"; @@ -16,6 +16,7 @@ export function AuthPage() { const goHome = () => navigate(Path.Home); const goChat = () => navigate(Path.Chat); + const goSaas = () => window.open(SAAS_CHAT_URL); const resetAccessCode = () => { accessStore.update((access) => { access.openaiApiKey = ""; @@ -65,7 +66,7 @@ export function AuthPage() { }} /> { - resetAccessCode(); - goHome(); + goSaas(); }} /> diff --git a/app/constant.ts b/app/constant.ts index acb744cad..d97f3b209 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -501,3 +501,5 @@ export const PLUGINS = [ { name: "Stable Diffusion", path: Path.Sd }, { name: "Search Chat", path: Path.SearchChat }, ]; + +export const SAAS_CHAT_URL = "https://nextchat.dev/chat"; diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 2dee13515..8bf2cf3a3 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -17,6 +17,7 @@ const cn = { Input: "在此处填写访问码", Confirm: "确认", Later: "稍后再说", + SaasTips: "配置太麻烦,想要立即使用", }, ChatItem: { ChatItemCount: (count: number) => `${count} 条对话`, From accb526cd649fe505f1bf3e4a5bcc1b01d1bdf40 Mon Sep 17 00:00:00 2001 From: JuliusMoehring <44407680+JuliusMoehring@users.noreply.github.com> Date: Wed, 18 Sep 2024 18:07:10 +0200 Subject: [PATCH 026/352] Avoid fetching prompts.json serverside --- app/store/prompt.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/store/prompt.ts b/app/store/prompt.ts index a25cda581..f746edecd 100644 --- a/app/store/prompt.ts +++ b/app/store/prompt.ts @@ -1,7 +1,7 @@ import Fuse from "fuse.js"; -import { getLang } from "../locales"; -import { StoreKey } from "../constant"; import { nanoid } from "nanoid"; +import { StoreKey } from "../constant"; +import { getLang } from "../locales"; import { createPersistStore } from "../utils/store"; export interface Prompt { @@ -147,6 +147,11 @@ export const usePromptStore = createPersistStore( }, onRehydrateStorage(state) { + // Skip store rehydration on server side + if (typeof window === "undefined") { + return; + } + const PROMPT_URL = "./prompts.json"; type PromptList = Array<[string, string]>; From e4fda6cacfbaf863dcd54dcbabcb0d93088019f7 Mon Sep 17 00:00:00 2001 From: river Date: Thu, 19 Sep 2024 08:41:09 +0800 Subject: [PATCH 027/352] feat: add auth tip --- app/components/auth.module.scss | 13 ++++++++++++- app/components/auth.tsx | 12 +++++++++++- app/constant.ts | 2 +- app/locales/cn.ts | 3 ++- app/locales/en.ts | 2 ++ 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/components/auth.module.scss b/app/components/auth.module.scss index c9c66f1a5..02c1f4292 100644 --- a/app/components/auth.module.scss +++ b/app/components/auth.module.scss @@ -1,12 +1,22 @@ .auth-page { display: flex; - justify-content: center; + justify-content: flex-start; align-items: center; height: 100%; width: 100%; flex-direction: column; + .auth-header { + display: flex; + justify-content: space-between; + width: 100%; + padding: 10px; + box-sizing: border-box; + animation: slide-in-from-top ease 0.3s; + } + .auth-logo { + margin-top: 10vh; transform: scale(1.4); } @@ -14,6 +24,7 @@ font-size: 24px; font-weight: bold; line-height: 2; + margin-bottom: 1vh; } .auth-tips { diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 9d10ae475..c044fb0ac 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -9,6 +9,7 @@ import Locale from "../locales"; import BotIcon from "../icons/bot.svg"; import { useEffect } from "react"; import { getClientConfig } from "../config/client"; +import LeftIcon from "@/app/icons/left.svg"; export function AuthPage() { const navigate = useNavigate(); @@ -16,7 +17,9 @@ export function AuthPage() { const goHome = () => navigate(Path.Home); const goChat = () => navigate(Path.Chat); - const goSaas = () => window.open(SAAS_CHAT_URL); + const goSaas = () => { + window.location.href = SAAS_CHAT_URL; + }; const resetAccessCode = () => { accessStore.update((access) => { access.openaiApiKey = ""; @@ -33,6 +36,13 @@ export function AuthPage() { return (
+
+ } + text={Locale.Auth.Return} + onClick={() => navigate(Path.Home)} + > +
diff --git a/app/constant.ts b/app/constant.ts index d97f3b209..1ba3f94c2 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -502,4 +502,4 @@ export const PLUGINS = [ { name: "Search Chat", path: Path.SearchChat }, ]; -export const SAAS_CHAT_URL = "https://nextchat.dev/chat"; +export const SAAS_CHAT_URL = "https://nextchat.dev"; diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 8bf2cf3a3..ae45cbf80 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -11,9 +11,10 @@ const cn = { : "访问密码不正确或为空,请前往[登录](/#/auth)页输入正确的访问密码,或者在[设置](/#/settings)页填入你自己的 OpenAI API Key。", }, Auth: { + Return: "返回", Title: "需要密码", Tips: "管理员开启了密码验证,请在下方填入访问码", - SubTips: "或者输入你的 OpenAI 或 Google API 密钥", + SubTips: "或者输入你的 OpenAI 或 Google AI 密钥", Input: "在此处填写访问码", Confirm: "确认", Later: "稍后再说", diff --git a/app/locales/en.ts b/app/locales/en.ts index 63e244b9a..ba94f5825 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -13,12 +13,14 @@ const en: LocaleType = { : "Unauthorized access, please enter access code in [auth](/#/auth) page, or enter your OpenAI API Key.", }, Auth: { + Return: "Return", Title: "Need Access Code", Tips: "Please enter access code below", SubTips: "Or enter your OpenAI or Google API Key", Input: "access code", Confirm: "Confirm", Later: "Later", + SaasTips: "Too Complex, Use Immediately Now", }, ChatItem: { ChatItemCount: (count: number) => `${count} messages`, From 065f015f7b87b65f522c913f95958c4f3392b97d Mon Sep 17 00:00:00 2001 From: river Date: Thu, 19 Sep 2024 09:53:00 +0800 Subject: [PATCH 028/352] feat: add error tip --- app/locales/cn.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/locales/cn.ts b/app/locales/cn.ts index ae45cbf80..2c3eb7c27 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -1,5 +1,6 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; @@ -7,8 +8,14 @@ const cn = { WIP: "该功能仍在开发中……", Error: { Unauthorized: isApp - ? "检测到无效 API Key,请前往[设置](/#/settings)页检查 API Key 是否配置正确。" - : "访问密码不正确或为空,请前往[登录](/#/auth)页输入正确的访问密码,或者在[设置](/#/settings)页填入你自己的 OpenAI API Key。", + ? `😆 对话遇到了一些问题,不用慌,立刻带你排查: + \\ 1️⃣ 如果你是小白,想要开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️` + : `😆 对话遇到了一些问题,不用慌,立刻带你排查: + \ 1️⃣ 如果你是小白,想要开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ 如果你在使用私有部署的版本,点击[这里](/#/auth)输入访问秘钥 🔑 + \ 3️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️ + `, }, Auth: { Return: "返回", From 212d15fdd0adcdd8df8eec85b3131242831275bb Mon Sep 17 00:00:00 2001 From: Yudong Date: Thu, 19 Sep 2024 11:20:18 +0800 Subject: [PATCH 029/352] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BA=86typo?= =?UTF-8?q?=EF=BC=8CWHITE=5FWEBDEV=5FENDPOINTS=20->=20WHITE=5FWEBDAV=5FEND?= =?UTF-8?q?POINTS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.template | 2 +- README.md | 2 +- README_CN.md | 2 +- README_JA.md | 2 +- app/config/server.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.env.template b/.env.template index 25addf2b3..82f44216a 100644 --- a/.env.template +++ b/.env.template @@ -66,4 +66,4 @@ ANTHROPIC_API_VERSION= ANTHROPIC_URL= ### (optional) -WHITE_WEBDEV_ENDPOINTS= \ No newline at end of file +WHITE_WEBDAV_ENDPOINTS= \ No newline at end of file diff --git a/README.md b/README.md index c8b158956..2001d4d88 100644 --- a/README.md +++ b/README.md @@ -340,7 +340,7 @@ For ByteDance: use `modelName@bytedance=deploymentName` to customize model name Change default model -### `WHITE_WEBDEV_ENDPOINTS` (optional) +### `WHITE_WEBDAV_ENDPOINTS` (optional) You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format: - Each address must be a complete endpoint diff --git a/README_CN.md b/README_CN.md index beed396c5..7831e2ee9 100644 --- a/README_CN.md +++ b/README_CN.md @@ -202,7 +202,7 @@ ByteDance Api Url. 如果你想禁用从链接解析预制设置,将此环境变量设置为 1 即可。 -### `WHITE_WEBDEV_ENDPOINTS` (可选) +### `WHITE_WEBDAV_ENDPOINTS` (可选) 如果你想增加允许访问的webdav服务地址,可以使用该选项,格式要求: - 每一个地址必须是一个完整的 endpoint diff --git a/README_JA.md b/README_JA.md index 6b8caadae..1716089af 100644 --- a/README_JA.md +++ b/README_JA.md @@ -193,7 +193,7 @@ ByteDance API の URL。 リンクからのプリセット設定解析を無効にしたい場合は、この環境変数を 1 に設定します。 -### `WHITE_WEBDEV_ENDPOINTS` (オプション) +### `WHITE_WEBDAV_ENDPOINTS` (オプション) アクセス許可を与える WebDAV サービスのアドレスを追加したい場合、このオプションを使用します。フォーマット要件: - 各アドレスは完全なエンドポイントでなければなりません。 diff --git a/app/config/server.ts b/app/config/server.ts index 676b0174f..d98488129 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -155,7 +155,7 @@ export const getServerSideConfig = () => { // ); const allowedWebDevEndpoints = ( - process.env.WHITE_WEBDEV_ENDPOINTS ?? "" + process.env.WHITE_WEBDAV_ENDPOINTS ?? "" ).split(","); return { From 03268ce4d82734091444adc66d76e35c28d45628 Mon Sep 17 00:00:00 2001 From: endless-learner <35006844+endless-learner@users.noreply.github.com> Date: Wed, 18 Sep 2024 20:38:20 -0700 Subject: [PATCH 030/352] Added 1-click deployment link for Alibaba Cloud. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c8b158956..9ea61bb08 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4 [MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple [Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu -[Deploy on Zeabur](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) +[Deploy on Zeabur](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [Deploy to Alibaba Cloud](https://computenest.console.aliyun.com/service/detail/cn-hangzhou/service-f1c9b75e59814dc49d52?type=user&isRecommend=true) [](https://monica.im/?utm=nxcrp) From 775794e0e4d770ff745f342744dd5d85e2bd2e25 Mon Sep 17 00:00:00 2001 From: river Date: Thu, 19 Sep 2024 12:38:46 +0800 Subject: [PATCH 031/352] chore: add setting --- app/components/settings.tsx | 25 +++++++++++++++++++++++++ app/icons/fire.svg | 1 + app/locales/cn.ts | 15 +++++++++++---- app/locales/en.ts | 21 +++++++++++++++++---- 4 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 app/icons/fire.svg diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 33bf8b2e7..8fbeb6605 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -9,6 +9,7 @@ import CopyIcon from "../icons/copy.svg"; import ClearIcon from "../icons/clear.svg"; import LoadingIcon from "../icons/three-dots.svg"; import EditIcon from "../icons/edit.svg"; +import FireIcon from "../icons/fire.svg"; import EyeIcon from "../icons/eye.svg"; import DownloadIcon from "../icons/download.svg"; import UploadIcon from "../icons/upload.svg"; @@ -69,6 +70,7 @@ import { UPDATE_URL, Stability, Iflytek, + SAAS_CHAT_URL, } from "../constant"; import { Prompt, SearchService, usePromptStore } from "../store/prompt"; import { ErrorBoundary } from "./error"; @@ -686,6 +688,28 @@ export function Settings() { ); + const saasStartComponent = ( + + } + text={Locale.Settings.Access.SaasStart.ChatNow} + onClick={() => { + window.location.href = SAAS_CHAT_URL; + }} + /> + + ); + const useCustomConfigComponent = // Conditionally render the following ListItem based on clientConfig.isApp !clientConfig?.isApp && ( // only show if isApp is false + {saasStartComponent} {accessCodeComponent} {!accessStore.hideUserApiKey && ( diff --git a/app/icons/fire.svg b/app/icons/fire.svg new file mode 100644 index 000000000..446d532aa --- /dev/null +++ b/app/icons/fire.svg @@ -0,0 +1 @@ + diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 2c3eb7c27..001ee1d8f 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -8,12 +8,12 @@ const cn = { WIP: "该功能仍在开发中……", Error: { Unauthorized: isApp - ? `😆 对话遇到了一些问题,不用慌,立刻带你排查: - \\ 1️⃣ 如果你是小白,想要开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) + ? `😆 对话遇到了一些问题,不用慌: + \\ 1️⃣ 如果你是小白,想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) \\ 2️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️` : `😆 对话遇到了一些问题,不用慌,立刻带你排查: - \ 1️⃣ 如果你是小白,想要开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) - \ 2️⃣ 如果你在使用私有部署的版本,点击[这里](/#/auth)输入访问秘钥 🔑 + \ 1️⃣ 如果你是小白,想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ 如果你正在使用私有部署版本,点击[这里](/#/auth)输入访问秘钥 🔑 \ 3️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️ `, }, @@ -306,6 +306,13 @@ const cn = { }, Access: { + SaasStart: { + Title: "使用 NextChat AI", + Label: "(性价比最高的方案)", + SubTitle: + "由 NextChat 官方维护, 零配置开箱即用,支持最新 GPT-4o、Gemini、Claude3 等最新大模型", + ChatNow: "立刻对话", + }, AccessCode: { Title: "访问密码", SubTitle: "管理员已开启加密访问", diff --git a/app/locales/en.ts b/app/locales/en.ts index ba94f5825..610a5bc09 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -1,7 +1,7 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; import { LocaleType } from "./index"; - +import { SAAS_CHAT_URL } from "@/app/constant"; // if you are adding a new translation, please use PartialLocaleType instead of LocaleType const isApp = !!getClientConfig()?.isApp; @@ -9,8 +9,14 @@ const en: LocaleType = { WIP: "Coming Soon...", Error: { Unauthorized: isApp - ? "Invalid API Key, please check it in [Settings](/#/settings) page." - : "Unauthorized access, please enter access code in [auth](/#/auth) page, or enter your OpenAI API Key.", + ? `😆 Oops, there's an issue. No worries: + \\ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️` + : `😆 Oops, there's an issue. Let's fix it: + \ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Using a private setup? [Click here](/#/auth) to enter your key 🔑 + \ 3️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️ + `, }, Auth: { Return: "Return", @@ -303,6 +309,13 @@ const en: LocaleType = { NoAccess: "Enter API Key to check balance", }, Access: { + SaasStart: { + Title: "Use NextChat AI", + Label: " (Most Cost-Effective Option)", + SubTitle: + "Maintained by NextChat, zero setup needed, supports the latest models like GPT-4o, Gemini, Claude3, and more.", + ChatNow: "Start Now", + }, AccessCode: { Title: "Access Code", SubTitle: "Access control Enabled", @@ -463,7 +476,7 @@ const en: LocaleType = { ApiKey: { Title: "API Key", SubTitle: "Obtain your API Key from Google AI", - Placeholder: "Enter your Google AI Studio API Key", + Placeholder: "Google AI API Key", }, Endpoint: { From f4f3c6ad5a1be5ac86e712fafea537ede4cb4aaa Mon Sep 17 00:00:00 2001 From: river Date: Thu, 19 Sep 2024 12:47:09 +0800 Subject: [PATCH 032/352] chore: change placeholder --- app/locales/cn.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 001ee1d8f..3ef634bee 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -11,7 +11,7 @@ const cn = { ? `😆 对话遇到了一些问题,不用慌: \\ 1️⃣ 如果你是小白,想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) \\ 2️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️` - : `😆 对话遇到了一些问题,不用慌,立刻带你排查: + : `😆 对话遇到了一些问题,不用慌: \ 1️⃣ 如果你是小白,想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) \ 2️⃣ 如果你正在使用私有部署版本,点击[这里](/#/auth)输入访问秘钥 🔑 \ 3️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️ From 23793e834d0751ddae8f6513ff101e9274cfd388 Mon Sep 17 00:00:00 2001 From: river Date: Thu, 19 Sep 2024 12:53:43 +0800 Subject: [PATCH 033/352] chore: change placeholder --- app/locales/cn.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 3ef634bee..d7a206961 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -9,10 +9,10 @@ const cn = { Error: { Unauthorized: isApp ? `😆 对话遇到了一些问题,不用慌: - \\ 1️⃣ 如果你是小白,想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) \\ 2️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️` : `😆 对话遇到了一些问题,不用慌: - \ 1️⃣ 如果你是小白,想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) \ 2️⃣ 如果你正在使用私有部署版本,点击[这里](/#/auth)输入访问秘钥 🔑 \ 3️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️ `, From 7dc0f81d3ffe462d97cc1990a1b127dd554ee556 Mon Sep 17 00:00:00 2001 From: river Date: Thu, 19 Sep 2024 13:48:59 +0800 Subject: [PATCH 034/352] chore: change placeholder --- app/components/settings.tsx | 1 + app/locales/cn.ts | 2 +- app/locales/en.ts | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 8fbeb6605..7f8d0cb71 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -702,6 +702,7 @@ export function Settings() { Locale.Settings.Access.SaasStart.ChatNow } icon={} + type={"primary"} text={Locale.Settings.Access.SaasStart.ChatNow} onClick={() => { window.location.href = SAAS_CHAT_URL; diff --git a/app/locales/cn.ts b/app/locales/cn.ts index d7a206961..772feac47 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -310,7 +310,7 @@ const cn = { Title: "使用 NextChat AI", Label: "(性价比最高的方案)", SubTitle: - "由 NextChat 官方维护, 零配置开箱即用,支持最新 GPT-4o、Gemini、Claude3 等最新大模型", + "由 NextChat 官方维护, 零配置开箱即用,支持 OpenAI o1, GPT-4o, Claude-3.5 等最新大模型", ChatNow: "立刻对话", }, AccessCode: { diff --git a/app/locales/en.ts b/app/locales/en.ts index 610a5bc09..61d1fc428 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -313,7 +313,8 @@ const en: LocaleType = { Title: "Use NextChat AI", Label: " (Most Cost-Effective Option)", SubTitle: - "Maintained by NextChat, zero setup needed, supports the latest models like GPT-4o, Gemini, Claude3, and more.", + "Maintained by NextChat, zero setup needed, unlock OpenAI o1, GPT-4o," + + " Claude-3.5 and more", ChatNow: "Start Now", }, AccessCode: { From df222ded12b9a501b8a5edd87297e089d9881907 Mon Sep 17 00:00:00 2001 From: Yudong Date: Thu, 19 Sep 2024 14:15:31 +0800 Subject: [PATCH 035/352] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BA=86typo,=20WebD?= =?UTF-8?q?ev=20->=20WebDav?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/webdav/[...path]/route.ts | 2 +- app/config/server.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/api/webdav/[...path]/route.ts b/app/api/webdav/[...path]/route.ts index 9f96cbfcf..bb7743bda 100644 --- a/app/api/webdav/[...path]/route.ts +++ b/app/api/webdav/[...path]/route.ts @@ -6,7 +6,7 @@ const config = getServerSideConfig(); const mergedAllowedWebDavEndpoints = [ ...internalAllowedWebDavEndpoints, - ...config.allowedWebDevEndpoints, + ...config.allowedWebDavEndpoints, ].filter((domain) => Boolean(domain.trim())); const normalizeUrl = (url: string) => { diff --git a/app/config/server.ts b/app/config/server.ts index d98488129..6544fe564 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -154,7 +154,7 @@ export const getServerSideConfig = () => { // `[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`, // ); - const allowedWebDevEndpoints = ( + const allowedWebDavEndpoints = ( process.env.WHITE_WEBDAV_ENDPOINTS ?? "" ).split(","); @@ -229,6 +229,6 @@ export const getServerSideConfig = () => { disableFastLink: !!process.env.DISABLE_FAST_LINK, customModels, defaultModel, - allowedWebDevEndpoints, + allowedWebDavEndpoints, }; }; From 4c63ee23cdf2760a4b88510081b2d630c583050e Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Thu, 19 Sep 2024 15:13:33 +0800 Subject: [PATCH 036/352] =?UTF-8?q?feat:=20#5422=20=E5=BF=AB=E6=8D=B7?= =?UTF-8?q?=E9=94=AE=E6=B8=85=E9=99=A4=E4=B8=8A=E4=B8=8B=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/chat.tsx | 20 ++++++++++++++++++++ app/locales/cn.ts | 1 + app/locales/en.ts | 1 + app/locales/tw.ts | 1 + 4 files changed, 23 insertions(+) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 3d519dee7..08c931f9e 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -874,6 +874,10 @@ export function ShortcutKeyModal(props: { onClose: () => void }) { title: Locale.Chat.ShortcutKey.showShortcutKey, keys: isMac ? ["⌘", "/"] : ["Ctrl", "/"], }, + { + title: Locale.Chat.ShortcutKey.clearContext, + keys: isMac ? ["⌘", "Shift", "Delete"] : ["Ctrl", "Shift", "Delete"], + }, ]; return (
@@ -1560,6 +1564,22 @@ function _Chat() { event.preventDefault(); setShowShortcutKeyModal(true); } + // 清除上下文 command + shift + delete + else if ( + (event.metaKey || event.ctrlKey) && + event.shiftKey && + event.key.toLowerCase() === "delete" + ) { + event.preventDefault(); + chatStore.updateCurrentSession((session) => { + if (session.clearContextIndex === session.messages.length) { + session.clearContextIndex = undefined; + } else { + session.clearContextIndex = session.messages.length; + session.memoryPrompt = ""; // will clear memory + } + }); + } }; window.addEventListener("keydown", handleKeyDown); diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 0017e8e42..0acf8c545 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -95,6 +95,7 @@ const cn = { copyLastMessage: "复制最后一个回复", copyLastCode: "复制最后一个代码块", showShortcutKey: "显示快捷方式", + clearContext: "清除上下文", }, }, Export: { diff --git a/app/locales/en.ts b/app/locales/en.ts index 63e244b9a..559b93abd 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -97,6 +97,7 @@ const en: LocaleType = { copyLastMessage: "Copy Last Reply", copyLastCode: "Copy Last Code Block", showShortcutKey: "Show Shortcuts", + clearContext: "Clear Context", }, }, Export: { diff --git a/app/locales/tw.ts b/app/locales/tw.ts index b0602a081..b84d3bf1f 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -90,6 +90,7 @@ const tw = { copyLastMessage: "複製最後一個回覆", copyLastCode: "複製最後一個代碼塊", showShortcutKey: "顯示快捷方式", + clearContext: "清除上下文", }, }, Export: { From 62efab987bbf1b2e06d969243fe92b4a70335c8a Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Thu, 19 Sep 2024 16:58:51 +0800 Subject: [PATCH 037/352] add fanyi add top tip --- app/components/auth.module.scss | 37 ++++++++++++++++++ app/components/auth.tsx | 66 +++++++++++++++++++++++++++++++-- app/icons/arrow.svg | 1 + app/icons/logo.svg | 19 ++++++++++ app/icons/www-delete.svg | 1 + app/locales/ar.ts | 23 ++++++++++-- app/locales/bn.ts | 24 ++++++++++-- app/locales/cn.ts | 2 + app/locales/cs.ts | 24 ++++++++++-- app/locales/de.ts | 25 +++++++++++-- app/locales/es.ts | 25 +++++++++++-- app/locales/fr.ts | 25 +++++++++++-- app/locales/id.ts | 24 ++++++++++-- app/locales/it.ts | 25 +++++++++++-- app/locales/jp.ts | 24 ++++++++++-- app/locales/ko.ts | 24 ++++++++++-- app/locales/no.ts | 25 +++++++++++-- app/locales/pt.ts | 24 ++++++++++-- app/locales/ru.ts | 24 ++++++++++-- app/locales/sk.ts | 24 ++++++++++-- app/locales/tr.ts | 24 ++++++++++-- app/locales/tw.ts | 24 ++++++++++-- app/locales/vi.ts | 24 ++++++++++-- 23 files changed, 484 insertions(+), 54 deletions(-) create mode 100644 app/icons/arrow.svg create mode 100644 app/icons/logo.svg create mode 100644 app/icons/www-delete.svg diff --git a/app/components/auth.module.scss b/app/components/auth.module.scss index 02c1f4292..c26320b7d 100644 --- a/app/components/auth.module.scss +++ b/app/components/auth.module.scss @@ -5,6 +5,43 @@ height: 100%; width: 100%; flex-direction: column; + .top-banner { + position: relative; + width: 100%; + display: flex; + justify-content: center; + align-items: center; + + padding: 12px 64px; + box-sizing: border-box; + background: var(--second); + .top-banner-inner { + display: flex; + justify-content: center; + align-items: center; + font-size: 14px; + line-height: 150%; + span { + display: inline-flex; + align-items: center; + gap: 8px; + a { + display: inline-flex; + align-items: center; + text-decoration: none; + margin-left: 8px; + color: var(--primary); + } + } + } + .top-banner-close { + cursor: pointer; + position: absolute; + top: 50%; + right: 48px; + transform: translateY(-50%); + } + } .auth-header { display: flex; diff --git a/app/components/auth.tsx b/app/components/auth.tsx index c044fb0ac..8b6e22607 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -1,13 +1,14 @@ import styles from "./auth.module.scss"; import { IconButton } from "./button"; - +import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { Path, SAAS_CHAT_URL } from "../constant"; import { useAccessStore } from "../store"; import Locale from "../locales"; - +import Delete from "../icons/www-delete.svg"; +import Arrow from "../icons/arrow.svg"; +import Logo from "../icons/logo.svg"; import BotIcon from "../icons/bot.svg"; -import { useEffect } from "react"; import { getClientConfig } from "../config/client"; import LeftIcon from "@/app/icons/left.svg"; @@ -20,6 +21,7 @@ export function AuthPage() { const goSaas = () => { window.location.href = SAAS_CHAT_URL; }; + const resetAccessCode = () => { accessStore.update((access) => { access.openaiApiKey = ""; @@ -36,6 +38,7 @@ export function AuthPage() { return (
+
} @@ -105,3 +108,60 @@ export function AuthPage() {
); } + +function TopBanner() { + const [isHovered, setIsHovered] = useState(false); + const [isVisible, setIsVisible] = useState(true); + + useEffect(() => { + // 检查 localStorage 中是否有标记 + const bannerDismissed = localStorage.getItem("bannerDismissed"); + + // 如果标记不存在,存储默认值并显示横幅 + if (!bannerDismissed) { + localStorage.setItem("bannerDismissed", "false"); + setIsVisible(true); // 显示横幅 + } else if (bannerDismissed === "true") { + // 如果标记为 "true",则隐藏横幅 + setIsVisible(false); + } + }, []); + + const handleMouseEnter = () => { + setIsHovered(true); + }; + + const handleMouseLeave = () => { + setIsHovered(false); + }; + + const handleClose = () => { + setIsVisible(false); + localStorage.setItem("bannerDismissed", "true"); + }; + + if (!isVisible) { + return null; + } + return ( +
+
+ + + {Locale.Auth.TopTips} + + {Locale.Settings.Access.SaasStart.ChatNow} + + + +
+ {isHovered && ( + + )} +
+ ); +} diff --git a/app/icons/arrow.svg b/app/icons/arrow.svg new file mode 100644 index 000000000..ddd69e614 --- /dev/null +++ b/app/icons/arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/logo.svg b/app/icons/logo.svg new file mode 100644 index 000000000..8f3c1de08 --- /dev/null +++ b/app/icons/logo.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/icons/www-delete.svg b/app/icons/www-delete.svg new file mode 100644 index 000000000..17952c3b3 --- /dev/null +++ b/app/icons/www-delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/locales/ar.ts b/app/locales/ar.ts index 464519e20..0399d7f7c 100644 --- a/app/locales/ar.ts +++ b/app/locales/ar.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const ar: PartialLocaleType = { WIP: "قريبًا...", Error: { Unauthorized: isApp - ? "تم اكتشاف مفتاح API غير صالح، يرجى الذهاب إلى [الإعدادات](/#/settings) للتحقق من صحة مفتاح API." - : "كلمة المرور غير صحيحة أو فارغة، يرجى الذهاب إلى [تسجيل الدخول](/#/auth) لإدخال كلمة مرور صحيحة، أو أدخل مفتاح OpenAI API الخاص بك في [الإعدادات](/#/settings).", + ? `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق: + \\ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️` + : `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق: + \ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ إذا كنت تستخدم إصدار النشر الخاص، انقر [هنا](/#/auth) لإدخال مفتاح الوصول 🔑 + \ 3️⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️ + `, }, Auth: { Title: "تحتاج إلى كلمة مرور", @@ -18,6 +24,10 @@ const ar: PartialLocaleType = { Input: "أدخل رمز الوصول هنا", Confirm: "تأكيد", Later: "في وقت لاحق", + Return: "عودة", + SaasTips: "الإعدادات معقدة، أريد استخدامه على الفور", + TopTips: + "🥳 عرض NextChat AI الأول، افتح الآن OpenAI o1, GPT-4o, Claude-3.5 وأحدث النماذج الكبيرة", }, ChatItem: { ChatItemCount: (count: number) => `${count} محادثة`, @@ -281,6 +291,13 @@ const ar: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "استخدام NextChat AI", + Label: "(أفضل حل من حيث التكلفة)", + SubTitle: + "مدعوم رسميًا من NextChat، جاهز للاستخدام بدون إعداد، يدعم أحدث النماذج الكبيرة مثل OpenAI o1 و GPT-4o و Claude-3.5", + ChatNow: "الدردشة الآن", + }, AccessCode: { Title: "كلمة المرور للوصول", SubTitle: "قام المشرف بتمكين الوصول المشفر", diff --git a/app/locales/bn.ts b/app/locales/bn.ts index 945c442df..78c0f76f0 100644 --- a/app/locales/bn.ts +++ b/app/locales/bn.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const bn: PartialLocaleType = { WIP: "শীঘ্রই আসছে...", Error: { Unauthorized: isApp - ? "অবৈধ API কী সনাক্ত করা হয়েছে, অনুগ্রহ করে [সেটিংস](/#/settings) পৃষ্ঠায় যান এবং নিশ্চিত করুন যে API কী সঠিকভাবে কনফিগার করা হয়েছে।" - : "অ্যাক্সেস পাসওয়ার্ড সঠিক নয় বা খালি, অনুগ্রহ করে [লগইন](/#/auth) পৃষ্ঠায় যান এবং সঠিক অ্যাক্সেস পাসওয়ার্ড প্রবেশ করান, অথবা [সেটিংস](/#/settings) পৃষ্ঠায় আপনার OpenAI API কী প্রবেশ করান।", + ? `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই: + \\ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️` + : `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই: + \ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ যদি আপনি একটি প্রাইভেট ডেপ্লয়মেন্ট সংস্করণ ব্যবহার করেন, তাহলে [এখানে ক্লিক করুন](/#/auth) প্রবেশাধিকার কীগুলি প্রবেশ করতে 🔑 + \ 3️⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️ + `, }, Auth: { Title: "পাসওয়ার্ড প্রয়োজন", @@ -18,6 +24,10 @@ const bn: PartialLocaleType = { Input: "এখানে অ্যাক্সেস কোড লিখুন", Confirm: "নিশ্চিত করুন", Later: "পরে বলুন", + Return: "ফিরে আসা", + SaasTips: "কনফিগারেশন খুব কঠিন, আমি অবিলম্বে ব্যবহার করতে চাই", + TopTips: + "🥳 NextChat AI প্রথম প্রকাশের অফার, এখনই OpenAI o1, GPT-4o, Claude-3.5 এবং সর্বশেষ বড় মডেলগুলি আনলক করুন", }, ChatItem: { ChatItemCount: (count: number) => `${count} টি চ্যাট`, @@ -284,6 +294,14 @@ const bn: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "NextChat AI ব্যবহার করুন", + Label: "(সেরা মূল্যসাশ্রয়ী সমাধান)", + SubTitle: + "NextChat কর্তৃক অফিসিয়াল রক্ষণাবেক্ষণ, শূন্য কনফিগারেশন ব্যবহার শুরু করুন, OpenAI o1, GPT-4o, Claude-3.5 সহ সর্বশেষ বড় মডেলগুলি সমর্থন করে", + ChatNow: "এখনই চ্যাট করুন", + }, + AccessCode: { Title: "অ্যাক্সেস পাসওয়ার্ড", SubTitle: "অ্যাডমিন এনক্রিপ্টেড অ্যাক্সেস সক্রিয় করেছেন", diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 772feac47..3181d1703 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -26,6 +26,8 @@ const cn = { Confirm: "确认", Later: "稍后再说", SaasTips: "配置太麻烦,想要立即使用", + TopTips: + "🥳 NextChat AI 首发优惠,立刻解锁 OpenAI o1, GPT-4o, Claude-3.5 等最新大模型", }, ChatItem: { ChatItemCount: (count: number) => `${count} 条对话`, diff --git a/app/locales/cs.ts b/app/locales/cs.ts index 5a132b3ce..972c46eda 100644 --- a/app/locales/cs.ts +++ b/app/locales/cs.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const cs: PartialLocaleType = { WIP: "V přípravě...", Error: { Unauthorized: isApp - ? "Byl zjištěn neplatný API Key, prosím přejděte na stránku [Nastavení](/#/settings) a zkontrolujte, zda je API Key správně nakonfigurován." - : "Heslo je nesprávné nebo prázdné, prosím přejděte na stránku [Přihlášení](/#/auth) a zadejte správné heslo, nebo na stránku [Nastavení](/#/settings) a zadejte svůj vlastní OpenAI API Key.", + ? `😆 Rozhovor narazil na nějaké problémy, nebojte se: + \\ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️` + : `😆 Rozhovor narazil na nějaké problémy, nebojte se: + \ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Pokud používáte verzi soukromého nasazení, klikněte [sem](/#/auth) a zadejte přístupový klíč 🔑 + \ 3️⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️ + `, }, Auth: { Title: "Potřebné heslo", @@ -18,6 +24,10 @@ const cs: PartialLocaleType = { Input: "Zadejte přístupový kód zde", Confirm: "Potvrdit", Later: "Později", + Return: "Návrat", + SaasTips: "Konfigurace je příliš složitá, chci okamžitě začít používat", + TopTips: + "🥳 Uvítací nabídka NextChat AI, okamžitě odemkněte OpenAI o1, GPT-4o, Claude-3.5 a nejnovější velké modely", }, ChatItem: { ChatItemCount: (count: number) => `${count} konverzací`, @@ -284,6 +294,14 @@ const cs: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "Použití NextChat AI", + Label: "(Nejlepší nákladově efektivní řešení)", + SubTitle: + "Oficiálně udržováno NextChat, připraveno k použití bez konfigurace, podporuje nejnovější velké modely jako OpenAI o1, GPT-4o, Claude-3.5", + ChatNow: "Začněte chatovat nyní", + }, + AccessCode: { Title: "Přístupový kód", SubTitle: "Administrátor aktivoval šifrovaný přístup", diff --git a/app/locales/de.ts b/app/locales/de.ts index ebe7aff2d..4b878b170 100644 --- a/app/locales/de.ts +++ b/app/locales/de.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const de: PartialLocaleType = { WIP: "In Bearbeitung...", Error: { Unauthorized: isApp - ? "Ungültiger API-Schlüssel erkannt. Bitte gehen Sie zur [Einstellungen](/#/settings) Seite, um zu überprüfen, ob der API-Schlüssel korrekt konfiguriert ist." - : "Das Passwort ist falsch oder leer. Bitte gehen Sie zur [Login](/#/auth) Seite, um das richtige Passwort einzugeben, oder fügen Sie Ihren OpenAI API-Schlüssel auf der [Einstellungen](/#/settings) Seite hinzu.", + ? `😆 Das Gespräch hatte einige Probleme, keine Sorge: + \\ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️` + : `😆 Das Gespräch hatte einige Probleme, keine Sorge: + \ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Wenn du eine private Bereitstellung verwendest, klicke [hier](/#/auth), um den Zugriffsschlüssel einzugeben 🔑 + \ 3️⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️ + `, }, Auth: { Title: "Passwort erforderlich", @@ -18,6 +24,11 @@ const de: PartialLocaleType = { Input: "Geben Sie hier den Zugangscode ein", Confirm: "Bestätigen", Later: "Später", + Return: "Zurück", + SaasTips: + "Die Konfiguration ist zu kompliziert, ich möchte es sofort nutzen", + TopTips: + "🥳 NextChat AI Einführungsangebot, schalte jetzt OpenAI o1, GPT-4o, Claude-3.5 und die neuesten großen Modelle frei", }, ChatItem: { ChatItemCount: (count: number) => `${count} Gespräche`, @@ -291,6 +302,14 @@ const de: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "NextChat AI verwenden", + Label: "(Die kosteneffektivste Lösung)", + SubTitle: + "Offiziell von NextChat verwaltet, sofort einsatzbereit ohne Konfiguration, unterstützt die neuesten großen Modelle wie OpenAI o1, GPT-4o und Claude-3.5", + ChatNow: "Jetzt chatten", + }, + AccessCode: { Title: "Zugangscode", SubTitle: diff --git a/app/locales/es.ts b/app/locales/es.ts index c1caae2d3..3719e7e4c 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const es: PartialLocaleType = { WIP: "En construcción...", Error: { Unauthorized: isApp - ? "Se detectó una clave API inválida. Por favor, ve a la página de [Configuración](/#/settings) para verificar si la clave API está configurada correctamente." - : "La contraseña de acceso es incorrecta o está vacía. Por favor, ve a la página de [Iniciar sesión](/#/auth) para ingresar la contraseña correcta, o en la página de [Configuración](/#/settings) para introducir tu propia clave API de OpenAI.", + ? `😆 La conversación encontró algunos problemas, no te preocupes: + \\ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️` + : `😆 La conversación encontró algunos problemas, no te preocupes: + \ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Si estás utilizando una versión de implementación privada, haz clic [aquí](/#/auth) para ingresar la clave de acceso 🔑 + \ 3️⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️ + `, }, Auth: { Title: "Se requiere contraseña", @@ -18,6 +24,11 @@ const es: PartialLocaleType = { Input: "Introduce el código de acceso aquí", Confirm: "Confirmar", Later: "Más tarde", + Return: "Regresar", + SaasTips: + "La configuración es demasiado complicada, quiero usarlo de inmediato", + TopTips: + "🥳 Oferta de lanzamiento de NextChat AI, desbloquea OpenAI o1, GPT-4o, Claude-3.5 y los últimos grandes modelos", }, ChatItem: { ChatItemCount: (count: number) => `${count} conversaciones`, @@ -294,6 +305,14 @@ const es: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "Use NextChat AI", + Label: "(The most cost-effective solution)", + SubTitle: + "Officially maintained by NextChat, zero configuration ready to use, supports the latest large models like OpenAI o1, GPT-4o, and Claude-3.5", + ChatNow: "Chat Now", + }, + AccessCode: { Title: "Contraseña de acceso", SubTitle: "El administrador ha habilitado el acceso encriptado", diff --git a/app/locales/fr.ts b/app/locales/fr.ts index 97fb79e32..dd611cbf9 100644 --- a/app/locales/fr.ts +++ b/app/locales/fr.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const fr: PartialLocaleType = { WIP: "Prochainement...", Error: { Unauthorized: isApp - ? "Clé API invalide détectée. Veuillez vérifier si la clé API est correctement configurée dans la page [Paramètres](/#/settings)." - : "Le mot de passe d'accès est incorrect ou manquant. Veuillez entrer le mot de passe d'accès correct sur la page [Connexion](/#/auth) ou entrer votre propre clé API OpenAI sur la page [Paramètres](/#/settings).", + ? `😆 La conversation a rencontré quelques problèmes, pas de panique : + \\ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️` + : `😆 La conversation a rencontré quelques problèmes, pas de panique : + \ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Si vous utilisez une version déployée privée, cliquez [ici](/#/auth) pour entrer la clé d'accès 🔑 + \ 3️⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️ + `, }, Auth: { Title: "Mot de passe requis", @@ -18,6 +24,11 @@ const fr: PartialLocaleType = { Input: "Entrez le code d'accès ici", Confirm: "Confirmer", Later: "Plus tard", + Return: "Retour", + SaasTips: + "La configuration est trop compliquée, je veux l'utiliser immédiatement", + TopTips: + "🥳 Offre de lancement NextChat AI, débloquez OpenAI o1, GPT-4o, Claude-3.5 et les derniers grands modèles", }, ChatItem: { ChatItemCount: (count: number) => `${count} conversations`, @@ -294,6 +305,14 @@ const fr: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "Utiliser NextChat AI", + Label: "(La solution la plus rentable)", + SubTitle: + "Officiellement maintenu par NextChat, prêt à l'emploi sans configuration, prend en charge les derniers grands modèles comme OpenAI o1, GPT-4o et Claude-3.5", + ChatNow: "Discuter maintenant", + }, + AccessCode: { Title: "Mot de passe d'accès", SubTitle: "L'administrateur a activé l'accès sécurisé", diff --git a/app/locales/id.ts b/app/locales/id.ts index 1eff667ed..c9c6428f1 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const id: PartialLocaleType = { WIP: "Coming Soon...", Error: { Unauthorized: isApp - ? "API Key tidak valid terdeteksi, silakan periksa apakah API Key telah dikonfigurasi dengan benar di halaman [Pengaturan](/#/settings)." - : "Kata sandi akses tidak benar atau kosong, silakan masukkan kata sandi akses yang benar di halaman [Masuk](/#/auth), atau masukkan OpenAI API Key Anda di halaman [Pengaturan](/#/settings).", + ? `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir: + \\ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️` + : `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir: + \ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Jika Anda menggunakan versi penyebaran pribadi, klik [di sini](/#/auth) untuk memasukkan kunci akses 🔑 + \ 3️⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️ +`, }, Auth: { Title: "Kebutuhan Kata Sandi", @@ -18,6 +24,10 @@ const id: PartialLocaleType = { Input: "Masukkan kode akses di sini", Confirm: "Konfirmasi", Later: "Nanti", + Return: "Kembali", + SaasTips: "Konfigurasi terlalu rumit, saya ingin menggunakannya segera", + TopTips: + "🥳 Penawaran Peluncuran NextChat AI, buka OpenAI o1, GPT-4o, Claude-3.5 dan model besar terbaru sekarang", }, ChatItem: { ChatItemCount: (count: number) => `${count} percakapan`, @@ -285,6 +295,14 @@ const id: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "Gunakan NextChat AI", + Label: "(Solusi paling hemat biaya)", + SubTitle: + "Dikelola secara resmi oleh NextChat, siap digunakan tanpa konfigurasi, mendukung model besar terbaru seperti OpenAI o1, GPT-4o, dan Claude-3.5", + ChatNow: "Chat Sekarang", + }, + AccessCode: { Title: "Kata Sandi Akses", SubTitle: "Administrator telah mengaktifkan akses terenkripsi", diff --git a/app/locales/it.ts b/app/locales/it.ts index 9b0d965d3..83b80a1b6 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const it: PartialLocaleType = { WIP: "Work in progress...", Error: { Unauthorized: isApp - ? "API Key non valido rilevato. Vai alla pagina [Impostazioni](/#/settings) per controllare se l'API Key è configurata correttamente." - : "La password di accesso è errata o mancante. Vai alla pagina [Login](/#/auth) per inserire la password corretta o inserisci la tua API Key OpenAI nella pagina [Impostazioni](/#/settings).", + ? `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti: + \\ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️` + : `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti: + \ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Se stai utilizzando una versione di distribuzione privata, clicca [qui](/#/auth) per inserire la chiave di accesso 🔑 + \ 3️⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️ + `, }, Auth: { Title: "Password richiesta", @@ -18,6 +24,11 @@ const it: PartialLocaleType = { Input: "Inserisci il codice di accesso qui", Confirm: "Conferma", Later: "Più tardi", + Return: "Ritorna", + SaasTips: + "La configurazione è troppo complicata, voglio usarlo immediatamente", + TopTips: + "🥳 Offerta di lancio NextChat AI, sblocca OpenAI o1, GPT-4o, Claude-3.5 e i più recenti modelli di grandi dimensioni", }, ChatItem: { ChatItemCount: (count: number) => `${count} conversazioni`, @@ -295,6 +306,14 @@ const it: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "Usa NextChat AI", + Label: "(La soluzione più conveniente)", + SubTitle: + "Mantenuto ufficialmente da NextChat, pronto all'uso senza configurazione, supporta i modelli più recenti come OpenAI o1, GPT-4o e Claude-3.5", + ChatNow: "Chatta ora", + }, + AccessCode: { Title: "Password di accesso", SubTitle: "L'amministratore ha abilitato l'accesso criptato", diff --git a/app/locales/jp.ts b/app/locales/jp.ts index 1511afc2c..a4f849c77 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const jp: PartialLocaleType = { WIP: "この機能は開発中です", Error: { Unauthorized: isApp - ? "無効なAPIキーが検出されました。[設定](/#/settings)ページでAPIキーが正しく設定されているか確認してください。" - : "アクセスパスワードが正しくないか空です。[ログイン](/#/auth)ページで正しいアクセスパスワードを入力するか、[設定](/#/settings)ページで自分のOpenAI APIキーを入力してください。", + ? `😆 会話中に問題が発生しましたが、心配しないでください: + \\ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️` + : `😆 会話中に問題が発生しましたが、心配しないでください: + \ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ プライベートデプロイ版を使用している場合は、[ここをクリックして](/#/auth)アクセストークンを入力してください 🔑 + \ 3️⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️ + `, }, Auth: { Title: "パスワードが必要です", @@ -18,6 +24,10 @@ const jp: PartialLocaleType = { Input: "ここにアクセスコードを入力", Confirm: "確認", Later: "後で", + Return: "戻る", + SaasTips: "設定が面倒すぎる、すぐに使いたい", + TopTips: + "🥳 NextChat AIの発売特典で、OpenAI o1、GPT-4o、Claude-3.5などの最新の大規模モデルを今すぐアンロック", }, ChatItem: { ChatItemCount: (count: number) => `${count}件の会話`, @@ -282,6 +292,14 @@ const jp: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "NextChat AIを使用する", + Label: "(コストパフォーマンスの最も高いソリューション)", + SubTitle: + "NextChatによって公式に管理されており、設定なしですぐに使用でき、OpenAI o1、GPT-4o、Claude-3.5などの最新の大規模モデルをサポートしています", + ChatNow: "今すぐチャット", + }, + AccessCode: { Title: "アクセスパスワード", SubTitle: "管理者が暗号化アクセスを有効にしました", diff --git a/app/locales/ko.ts b/app/locales/ko.ts index 11e6aa4eb..32eb2be73 100644 --- a/app/locales/ko.ts +++ b/app/locales/ko.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const ko: PartialLocaleType = { WIP: "곧 출시 예정...", Error: { Unauthorized: isApp - ? "유효하지 않은 API 키가 감지되었습니다. [설정](/#/settings) 페이지에서 API 키가 올바르게 구성되었는지 확인하십시오." - : "잘못된 접근 비밀번호이거나 비밀번호가 비어 있습니다. [로그인](/#/auth) 페이지에서 올바른 접근 비밀번호를 입력하거나 [설정](/#/settings) 페이지에서 OpenAI API 키를 입력하십시오.", + ? `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요: + \\ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️` + : `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요: + \ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ 개인 배포 버전을 사용하고 있다면, [여기를 클릭하여](/#/auth) 접근 키를 입력하세요 🔑 + \ 3️⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️ + `, }, Auth: { Title: "비밀번호 필요", @@ -18,6 +24,10 @@ const ko: PartialLocaleType = { Input: "여기에 접근 코드를 입력하십시오.", Confirm: "확인", Later: "나중에 하기", + Return: "돌아가기", + SaasTips: "설정이 너무 복잡합니다. 즉시 사용하고 싶습니다.", + TopTips: + "🥳 NextChat AI 출시 기념 할인, 지금 OpenAI o1, GPT-4o, Claude-3.5 및 최신 대형 모델을 해제하세요", }, ChatItem: { ChatItemCount: (count: number) => `${count} 개의 대화`, @@ -281,6 +291,14 @@ const ko: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "NextChat AI 사용하기", + Label: "(가장 비용 효율적인 솔루션)", + SubTitle: + "NextChat에 의해 공식적으로 유지 관리되며, 제로 구성으로 즉시 사용할 수 있으며, OpenAI o1, GPT-4o, Claude-3.5와 같은 최신 대형 모델을 지원합니다", + ChatNow: "지금 채팅하기", + }, + AccessCode: { Title: "접근 비밀번호", SubTitle: "관리자가 암호화된 접근을 활성화했습니다.", diff --git a/app/locales/no.ts b/app/locales/no.ts index 6fc8e86c7..d02b5ce66 100644 --- a/app/locales/no.ts +++ b/app/locales/no.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const no: PartialLocaleType = { WIP: "Arbeid pågår ...", Error: { Unauthorized: isApp - ? "Ugyldig API-nøkkel oppdaget. Gå til [innstillinger](/#/settings) for å sjekke om API-nøkkelen er riktig konfigurert." - : "Adgangskoden er feil eller tom. Gå til [innlogging](/#/auth) for å oppgi riktig adgangskode, eller fyll inn din egen OpenAI API-nøkkel på [innstillinger](/#/settings) siden.", + ? `😆 Samtalen har støtt på noen problemer, ikke bekymre deg: + \\ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️` + : `😆 Samtalen har støtt på noen problemer, ikke bekymre deg: + \ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Hvis du bruker en privat distribusjonsversjon, klikk [her](/#/auth) for å skrive inn tilgangsnøkkelen 🔑 + \ 3️⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️ + `, }, Auth: { Title: "Passord påkrevd", @@ -18,6 +24,11 @@ const no: PartialLocaleType = { Input: "Skriv tilgangskoden her", Confirm: "Bekreft", Later: "Kom tilbake senere", + Return: "Tilbake", + SaasTips: + "Konfigurasjonen er for komplisert, jeg vil bruke det med en gang", + TopTips: + "🥳 NextChat AI lanseringstilbud, lås opp OpenAI o1, GPT-4o, Claude-3.5 og de nyeste store modellene nå", }, ChatItem: { ChatItemCount: (count: number) => `${count} samtaler`, @@ -288,6 +299,14 @@ const no: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "Bruk NextChat AI", + Label: "(Den mest kostnadseffektive løsningen)", + SubTitle: + "Offisielt vedlikeholdt av NextChat, klar til bruk uten konfigurasjon, støtter de nyeste store modellene som OpenAI o1, GPT-4o og Claude-3.5", + ChatNow: "Chat nå", + }, + AccessCode: { Title: "Adgangskode", SubTitle: "Administrator har aktivert kryptert tilgang", diff --git a/app/locales/pt.ts b/app/locales/pt.ts index c04081a8b..c1de67384 100644 --- a/app/locales/pt.ts +++ b/app/locales/pt.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import { PartialLocaleType } from "../locales/index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const pt: PartialLocaleType = { WIP: "Em breve...", Error: { Unauthorized: isApp - ? "Chave API inválida, por favor verifique em [Configurações](/#/settings)." - : "Acesso não autorizado, por favor insira o código de acesso em [auth](/#/auth) ou insira sua Chave API OpenAI.", + ? `😆 A conversa encontrou alguns problemas, não se preocupe: + \\ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️` + : `😆 A conversa encontrou alguns problemas, não se preocupe: + \ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Se você estiver usando uma versão de implantação privada, clique [aqui](/#/auth) para inserir a chave de acesso 🔑 + \ 3️⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️ +`, }, Auth: { Title: "Necessário Código de Acesso", @@ -18,6 +24,10 @@ const pt: PartialLocaleType = { Input: "código de acesso", Confirm: "Confirmar", Later: "Depois", + Return: "Voltar", + SaasTips: "A configuração é muito complicada, quero usá-la imediatamente", + TopTips: + "🥳 Oferta de Lançamento do NextChat AI, desbloqueie o OpenAI o1, GPT-4o, Claude-3.5 e os mais recentes grandes modelos agora", }, ChatItem: { ChatItemCount: (count: number) => `${count} mensagens`, @@ -281,6 +291,14 @@ const pt: PartialLocaleType = { NoAccess: "Insira a Chave API para verificar o saldo", }, Access: { + SaasStart: { + Title: "Usar NextChat AI", + Label: "(A solução mais econômica)", + SubTitle: + "Mantido oficialmente pelo NextChat, pronto para uso sem configuração, suporta os mais recentes grandes modelos como OpenAI o1, GPT-4o e Claude-3.5", + ChatNow: "Conversar agora", + }, + AccessCode: { Title: "Código de Acesso", SubTitle: "Controle de Acesso Habilitado", diff --git a/app/locales/ru.ts b/app/locales/ru.ts index 0fcf73a24..2793ab7ec 100644 --- a/app/locales/ru.ts +++ b/app/locales/ru.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import { PartialLocaleType } from "../locales/index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const ru: PartialLocaleType = { WIP: "Скоро...", Error: { Unauthorized: isApp - ? "Обнаружен недействительный API-ключ. Пожалуйста, перейдите на страницу [Настройки](/#/settings), чтобы проверить правильность конфигурации API-ключа." - : "Неверный или пустой пароль доступа. Пожалуйста, перейдите на страницу [Вход](/#/auth), чтобы ввести правильный пароль доступа, или на страницу [Настройки](/#/settings), чтобы ввести ваш собственный API-ключ OpenAI.", + ? `😆 В разговоре возникли некоторые проблемы, не переживайте: + \\ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️` + : `😆 В разговоре возникли некоторые проблемы, не переживайте: + \ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Если вы используете частную версию развертывания, нажмите [здесь](/#/auth), чтобы ввести ключ доступа 🔑 + \ 3️⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️ + `, }, Auth: { Title: "Требуется пароль", @@ -18,6 +24,10 @@ const ru: PartialLocaleType = { Input: "Введите код доступа здесь", Confirm: "Подтвердить", Later: "Позже", + Return: "Назад", + SaasTips: "Настройка слишком сложна, я хочу использовать это немедленно", + TopTips: + "🥳 Предложение по запуску NextChat AI: разблокируйте OpenAI o1, GPT-4o, Claude-3.5 и новейшие большие модели прямо сейчас", }, ChatItem: { ChatItemCount: (count: number) => `${count} бесед`, @@ -286,6 +296,14 @@ const ru: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "Используйте NextChat AI", + Label: "(Самое экономичное решение)", + SubTitle: + "Официально поддерживается NextChat, готов к использованию без настройки, поддерживает последние крупные модели, такие как OpenAI o1, GPT-4o и Claude-3.5", + ChatNow: "Начать чат", + }, + AccessCode: { Title: "Пароль доступа", SubTitle: "Администратор включил защиту паролем", diff --git a/app/locales/sk.ts b/app/locales/sk.ts index 3649b763b..62cfab0ab 100644 --- a/app/locales/sk.ts +++ b/app/locales/sk.ts @@ -1,7 +1,7 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; - +import { SAAS_CHAT_URL } from "@/app/constant"; // if you are adding a new translation, please use PartialLocaleType instead of LocaleType const isApp = !!getClientConfig()?.isApp; @@ -9,8 +9,14 @@ const sk: PartialLocaleType = { WIP: "Už čoskoro...", Error: { Unauthorized: isApp - ? "Neplatný API kľúč, prosím skontrolujte ho na stránke [Nastavenia](/#/settings)." - : "Neoprávnený prístup, prosím zadajte prístupový kód na stránke [auth](/#/auth), alebo zadajte váš OpenAI API kľúč.", + ? `😆 Rozhovor narazil na nejaké problémy, nebojte sa: + \\ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️` + : `😆 Rozhovor narazil na nejaké problémy, nebojte sa: + \ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Ak používate verziu súkromného nasadenia, kliknite [sem](/#/auth), aby ste zadali prístupový kľúč 🔑 + \ 3️⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️ + `, }, Auth: { Title: "Potrebný prístupový kód", @@ -19,6 +25,10 @@ const sk: PartialLocaleType = { Input: "prístupový kód", Confirm: "Potvrdiť", Later: "Neskôr", + Return: "Návrat", + SaasTips: "Nastavenie je príliš zložité, chcem to okamžite použiť", + TopTips: + "🥳 Uvítacia ponuka NextChat AI, okamžite odomknite OpenAI o1, GPT-4o, Claude-3.5 a najnovšie veľké modely", }, ChatItem: { ChatItemCount: (count: number) => `${count} správ`, @@ -281,6 +291,14 @@ const sk: PartialLocaleType = { NoAccess: "Zadajte API kľúč na skontrolovanie zostatku", }, Access: { + SaasStart: { + Title: "Použite NextChat AI", + Label: "(Najvýhodnejšie riešenie)", + SubTitle: + "Oficiálne udržiavané NextChat, pripravené na použitie bez konfigurácie, podporuje najnovšie veľké modely ako OpenAI o1, GPT-4o a Claude-3.5", + ChatNow: "Chatovať teraz", + }, + AccessCode: { Title: "Prístupový kód", SubTitle: "Povolený prístupový kód", diff --git a/app/locales/tr.ts b/app/locales/tr.ts index b7f141047..7fe6170ca 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const tr: PartialLocaleType = { WIP: "Çalışma devam ediyor...", Error: { Unauthorized: isApp - ? "Geçersiz API Anahtarı tespit edildi, lütfen API Anahtarını doğru şekilde yapılandırmak için [Ayarlar](/#/settings) sayfasına gidin." - : "Erişim şifresi yanlış veya boş, lütfen doğru erişim şifresini girmek için [Giriş](/#/auth) sayfasına gidin veya kendi OpenAI API Anahtarınızı [Ayarlar](/#/settings) sayfasına girin.", + ? `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin: + \\ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️` + : `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin: + \ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Eğer özel dağıtım sürümü kullanıyorsanız, [buraya tıklayarak](/#/auth) erişim anahtarını girin 🔑 + \ 3️⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️ + `, }, Auth: { Title: "Şifre Gerekli", @@ -18,6 +24,10 @@ const tr: PartialLocaleType = { Input: "Erişim kodunu buraya girin", Confirm: "Onayla", Later: "Sonra", + Return: "Geri", + SaasTips: "Ayarlar çok karmaşık, hemen kullanmak istiyorum", + TopTips: + "🥳 NextChat AI lansman teklifi, OpenAI o1, GPT-4o, Claude-3.5 ve en son büyük modelleri şimdi açın", }, ChatItem: { ChatItemCount: (count: number) => `${count} konuşma`, @@ -286,6 +296,14 @@ const tr: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "NextChat AI kullanın", + Label: "(En maliyet etkin çözüm)", + SubTitle: + "NextChat tarafından resmi olarak yönetilmektedir, yapılandırma olmadan hemen kullanıma hazırdır, OpenAI o1, GPT-4o, Claude-3.5 gibi en son büyük modelleri destekler", + ChatNow: "Şimdi sohbet et", + }, + AccessCode: { Title: "Erişim Şifresi", SubTitle: "Yönetici şifreli erişimi etkinleştirdi", diff --git a/app/locales/tw.ts b/app/locales/tw.ts index b0602a081..39e2bc968 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -1,14 +1,20 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const tw = { WIP: "此功能仍在開發中……", Error: { Unauthorized: isApp - ? "偵測到無效的 API Key,請前往[設定](/#/settings)頁面檢查 API Key 是否設定正確。" - : "存取密碼不正確或尚未填寫,請前往[登入](/#/auth)頁面輸入正確的存取密碼,或者在[設定](/#/settings)頁面填入你自己的 OpenAI API Key。", + ? `😆 對話遇到了一些問題,不用慌: + \\ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️` + : `😆 對話遇到了一些問題,不用慌: + \ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ 如果你正在使用私有部署版本,點擊[這裡](/#/auth)輸入訪問秘鑰 🔑 + \ 3️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️ + `, }, Auth: { @@ -18,6 +24,10 @@ const tw = { Input: "在此處填寫存取密碼", Confirm: "確認", Later: "稍候再說", + Return: "返回", + SaasTips: "配置太麻煩,想要立即使用", + TopTips: + "🥳 NextChat AI 首發優惠,立刻解鎖 OpenAI o1, GPT-4o, Claude-3.5 等最新大模型", }, ChatItem: { ChatItemCount: (count: number) => `${count} 則對話`, @@ -287,6 +297,14 @@ const tw = { }, Access: { + SaasStart: { + Title: "使用 NextChat AI", + Label: "(性價比最高的方案)", + SubTitle: + "由 NextChat 官方維護,零配置開箱即用,支持 OpenAI o1、GPT-4o、Claude-3.5 等最新大模型", + ChatNow: "立刻對話", + }, + AccessCode: { Title: "存取密碼", SubTitle: "管理員已開啟加密存取", diff --git a/app/locales/vi.ts b/app/locales/vi.ts index 1f20e15a0..c4b6ffcd5 100644 --- a/app/locales/vi.ts +++ b/app/locales/vi.ts @@ -1,15 +1,21 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; - +import { SAAS_CHAT_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const vi: PartialLocaleType = { WIP: "Sắp ra mắt...", Error: { Unauthorized: isApp - ? "Phát hiện khóa API không hợp lệ, vui lòng truy cập trang [Cài đặt](/#/settings) để kiểm tra xem khóa API có được cấu hình chính xác không." - : "Mật khẩu truy cập không đúng hoặc để trống, vui lòng truy cập trang [Đăng nhập](/#/auth) để nhập mật khẩu truy cập chính xác, hoặc điền khóa API OpenAI của bạn vào trang [Cài đặt](/#/settings).", + ? `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng: + \\ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_URL}) + \\ 2️⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️` + : `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng: + \ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_URL}) + \ 2️⃣ Nếu bạn đang sử dụng phiên bản triển khai riêng, hãy nhấp [vào đây](/#/auth) để nhập khóa truy cập 🔑 + \ 3️⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️ + `, }, Auth: { Title: "Cần mật khẩu", @@ -18,6 +24,10 @@ const vi: PartialLocaleType = { Input: "Nhập mã truy cập tại đây", Confirm: "Xác nhận", Later: "Để sau", + Return: "Trở lại", + SaasTips: "Cấu hình quá phức tạp, tôi muốn sử dụng ngay lập tức", + TopTips: + "🥳 Ưu đãi ra mắt NextChat AI, mở khóa OpenAI o1, GPT-4o, Claude-3.5 và các mô hình lớn mới nhất ngay bây giờ", }, ChatItem: { ChatItemCount: (count: number) => `${count} cuộc trò chuyện`, @@ -283,6 +293,14 @@ const vi: PartialLocaleType = { }, Access: { + SaasStart: { + Title: "Sử dụng NextChat AI", + Label: "(Giải pháp tiết kiệm chi phí nhất)", + SubTitle: + "Được NextChat chính thức duy trì, sẵn sàng sử dụng mà không cần cấu hình, hỗ trợ các mô hình lớn mới nhất như OpenAI o1, GPT-4o và Claude-3.5", + ChatNow: "Chat ngay", + }, + AccessCode: { Title: "Mật khẩu truy cập", SubTitle: "Quản trị viên đã bật truy cập mã hóa", From 9858d1f9585b9d096c2e98cd424b5cc9e0d7bdc6 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Thu, 19 Sep 2024 18:21:26 +0800 Subject: [PATCH 038/352] add top tip --- app/components/auth.tsx | 12 +++++++----- app/icons/logo.svg | 36 ++++++++++++++++++------------------ app/icons/www-delete.svg | 1 - 3 files changed, 25 insertions(+), 24 deletions(-) delete mode 100644 app/icons/www-delete.svg diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 8b6e22607..3bd28aa1b 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -5,12 +5,13 @@ import { useNavigate } from "react-router-dom"; import { Path, SAAS_CHAT_URL } from "../constant"; import { useAccessStore } from "../store"; import Locale from "../locales"; -import Delete from "../icons/www-delete.svg"; +import Delete from "../icons/close.svg"; import Arrow from "../icons/arrow.svg"; import Logo from "../icons/logo.svg"; import BotIcon from "../icons/bot.svg"; import { getClientConfig } from "../config/client"; import LeftIcon from "@/app/icons/left.svg"; +import { safeLocalStorage } from "@/app/utils"; export function AuthPage() { const navigate = useNavigate(); @@ -112,20 +113,21 @@ export function AuthPage() { function TopBanner() { const [isHovered, setIsHovered] = useState(false); const [isVisible, setIsVisible] = useState(true); + const storage = safeLocalStorage(); useEffect(() => { // 检查 localStorage 中是否有标记 - const bannerDismissed = localStorage.getItem("bannerDismissed"); + const bannerDismissed = storage.getItem("bannerDismissed"); // 如果标记不存在,存储默认值并显示横幅 if (!bannerDismissed) { - localStorage.setItem("bannerDismissed", "false"); + storage.setItem("bannerDismissed", "false"); setIsVisible(true); // 显示横幅 } else if (bannerDismissed === "true") { // 如果标记为 "true",则隐藏横幅 setIsVisible(false); } - }, []); + }, [storage]); const handleMouseEnter = () => { setIsHovered(true); @@ -137,7 +139,7 @@ function TopBanner() { const handleClose = () => { setIsVisible(false); - localStorage.setItem("bannerDismissed", "true"); + storage.setItem("bannerDismissed", "true"); }; if (!isVisible) { diff --git a/app/icons/logo.svg b/app/icons/logo.svg index 8f3c1de08..b80263b86 100644 --- a/app/icons/logo.svg +++ b/app/icons/logo.svg @@ -1,19 +1,19 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/app/icons/www-delete.svg b/app/icons/www-delete.svg deleted file mode 100644 index 17952c3b3..000000000 --- a/app/icons/www-delete.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 702f5bd3624b565519eeaa0d758bd632e0813e96 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Fri, 20 Sep 2024 10:22:22 +0800 Subject: [PATCH 039/352] fex setCookie --- app/components/auth.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 3bd28aa1b..c89103a2e 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -13,10 +13,10 @@ import { getClientConfig } from "../config/client"; import LeftIcon from "@/app/icons/left.svg"; import { safeLocalStorage } from "@/app/utils"; +const storage = safeLocalStorage(); export function AuthPage() { const navigate = useNavigate(); const accessStore = useAccessStore(); - const goHome = () => navigate(Path.Home); const goChat = () => navigate(Path.Chat); const goSaas = () => { @@ -113,7 +113,6 @@ export function AuthPage() { function TopBanner() { const [isHovered, setIsHovered] = useState(false); const [isVisible, setIsVisible] = useState(true); - const storage = safeLocalStorage(); useEffect(() => { // 检查 localStorage 中是否有标记 @@ -127,7 +126,7 @@ function TopBanner() { // 如果标记为 "true",则隐藏横幅 setIsVisible(false); } - }, [storage]); + }, []); const handleMouseEnter = () => { setIsHovered(true); From 4d1f9e49d46eed55ff5ad970092be8d1d464416c Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Sun, 22 Sep 2024 18:53:51 +0800 Subject: [PATCH 040/352] hotfix openai function call tool_calls no index --- app/client/platforms/openai.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index d86be718b..c59a0519c 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -277,6 +277,7 @@ export class ChatGPTApi implements LLMApi { ); } if (shouldStream) { + let index = -1; const [tools, funcs] = usePluginStore .getState() .getAsTools( @@ -302,7 +303,7 @@ export class ChatGPTApi implements LLMApi { }>; const tool_calls = choices[0]?.delta?.tool_calls; if (tool_calls?.length > 0) { - const index = tool_calls[0]?.index; + index += 1; const id = tool_calls[0]?.id; const args = tool_calls[0]?.function?.arguments; if (id) { @@ -327,6 +328,8 @@ export class ChatGPTApi implements LLMApi { toolCallMessage: any, toolCallResult: any[], ) => { + // reset index value + index = -1; // @ts-ignore requestPayload?.messages?.splice( // @ts-ignore From 3a969054e309797153a01ec3283e926dee75008c Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Sun, 22 Sep 2024 18:59:49 +0800 Subject: [PATCH 041/352] hotfix openai function call tool_calls no index --- app/client/platforms/openai.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index c59a0519c..0a8d6203a 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -303,10 +303,10 @@ export class ChatGPTApi implements LLMApi { }>; const tool_calls = choices[0]?.delta?.tool_calls; if (tool_calls?.length > 0) { - index += 1; const id = tool_calls[0]?.id; const args = tool_calls[0]?.function?.arguments; if (id) { + index += 1; runTools.push({ id, type: tool_calls[0]?.type, From 51f7b02b272f2cda70a7c16892f8d63342463c08 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Mon, 23 Sep 2024 10:56:43 +0800 Subject: [PATCH 042/352] fex en --- app/locales/en.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/locales/en.ts b/app/locales/en.ts index 61d1fc428..71d02d6b3 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -27,6 +27,8 @@ const en: LocaleType = { Confirm: "Confirm", Later: "Later", SaasTips: "Too Complex, Use Immediately Now", + TopTips: + "🥳 NextChat AI launch promotion: Instantly unlock the latest models like OpenAI o1, GPT-4o, Claude-3.5!", }, ChatItem: { ChatItemCount: (count: number) => `${count} messages`, From 518e0d90a5f337805db20876b0006bd3a5f63fe8 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Mon, 23 Sep 2024 11:11:36 +0800 Subject: [PATCH 043/352] fex url --- app/constant.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/constant.ts b/app/constant.ts index 1ba3f94c2..d97f3b209 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -502,4 +502,4 @@ export const PLUGINS = [ { name: "Search Chat", path: Path.SearchChat }, ]; -export const SAAS_CHAT_URL = "https://nextchat.dev"; +export const SAAS_CHAT_URL = "https://nextchat.dev/chat"; From 35aa2c7270042acfbbd4049fc0e48fefd1aafb10 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Mon, 23 Sep 2024 11:34:20 +0800 Subject: [PATCH 044/352] Fix code duplication --- app/components/markdown.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index b57fd7490..178233000 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -128,8 +128,10 @@ export function PreCode(props: { children: any }) { className="copy-code-button" onClick={() => { if (ref.current) { - const code = ref.current.innerText; - copyToClipboard(code); + // const code = ref.current.innerText; + copyToClipboard( + ref.current.querySelector("code")?.innerText ?? "", + ); } }} > From 0e210cf8de4b4a5c75acd8684b706a840ca947ba Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Mon, 23 Sep 2024 14:13:09 +0800 Subject: [PATCH 045/352] =?UTF-8?q?fix:=20#5486=20plugin=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/plugin.module.scss | 25 ++++++++++++++++++++++++- app/components/plugin.tsx | 4 ++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/app/components/plugin.module.scss b/app/components/plugin.module.scss index a179e0a07..4b0e990e9 100644 --- a/app/components/plugin.module.scss +++ b/app/components/plugin.module.scss @@ -10,7 +10,30 @@ max-height: 240px; overflow-y: auto; white-space: pre-wrap; - min-width: 300px; + min-width: 280px; } } +.plugin-schema { + display: flex; + justify-content: flex-end; + flex-wrap: wrap; + flex-direction: row; + + input { + margin-right: 20px; + + @media screen and (max-width: 600px) { + margin-right: 0px; + } + } + + @media screen and (max-width: 600px) { + flex-direction: column; + gap: 5px; + + button { + padding: 10px; + } + } +} diff --git a/app/components/plugin.tsx b/app/components/plugin.tsx index cf4ae946e..a768c78a8 100644 --- a/app/components/plugin.tsx +++ b/app/components/plugin.tsx @@ -345,10 +345,10 @@ export function PluginPage() { -
+
setLoadUrl(e.currentTarget.value)} > Date: Mon, 23 Sep 2024 14:18:32 +0800 Subject: [PATCH 046/352] chore: css --- app/components/plugin.module.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/app/components/plugin.module.scss b/app/components/plugin.module.scss index 4b0e990e9..99a089896 100644 --- a/app/components/plugin.module.scss +++ b/app/components/plugin.module.scss @@ -17,7 +17,6 @@ .plugin-schema { display: flex; justify-content: flex-end; - flex-wrap: wrap; flex-direction: row; input { From c15c8526684e5606e16425f3fe9f5be1b809aebc Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Mon, 23 Sep 2024 15:12:45 +0800 Subject: [PATCH 047/352] fex media --- app/components/auth.module.scss | 17 ++++++++++++++--- app/components/auth.tsx | 18 +++++++++++++----- app/components/button.module.scss | 3 +-- app/components/ui-lib.module.scss | 5 +++-- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/app/components/auth.module.scss b/app/components/auth.module.scss index c26320b7d..fe143b428 100644 --- a/app/components/auth.module.scss +++ b/app/components/auth.module.scss @@ -11,7 +11,6 @@ display: flex; justify-content: center; align-items: center; - padding: 12px 64px; box-sizing: border-box; background: var(--second); @@ -22,8 +21,6 @@ font-size: 14px; line-height: 150%; span { - display: inline-flex; - align-items: center; gap: 8px; a { display: inline-flex; @@ -43,6 +40,20 @@ } } + @media (max-width: 600px) { + .top-banner { + padding: 12px 24px 12px 12px; + .top-banner-close { + right: 10px; + } + .top-banner-inner { + .top-banner-logo { + margin-right: 8px; + } + } + } + } + .auth-header { display: flex; justify-content: space-between; diff --git a/app/components/auth.tsx b/app/components/auth.tsx index c89103a2e..52fd860dd 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -113,11 +113,10 @@ export function AuthPage() { function TopBanner() { const [isHovered, setIsHovered] = useState(false); const [isVisible, setIsVisible] = useState(true); - + const [isMobile, setIsMobile] = useState(window.innerWidth < 600); useEffect(() => { // 检查 localStorage 中是否有标记 const bannerDismissed = storage.getItem("bannerDismissed"); - // 如果标记不存在,存储默认值并显示横幅 if (!bannerDismissed) { storage.setItem("bannerDismissed", "false"); @@ -128,6 +127,15 @@ function TopBanner() { } }, []); + useEffect(() => { + const handleResize = () => { + setIsMobile(window.innerWidth < 600); + }; + + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, []); + const handleMouseEnter = () => { setIsHovered(true); }; @@ -151,16 +159,16 @@ function TopBanner() { onMouseLeave={handleMouseLeave} > - {isHovered && ( + {(isHovered || isMobile) && ( )}
diff --git a/app/components/button.module.scss b/app/components/button.module.scss index e332df2d2..c65385cfc 100644 --- a/app/components/button.module.scss +++ b/app/components/button.module.scss @@ -5,10 +5,9 @@ align-items: center; justify-content: center; padding: 10px; - cursor: pointer; transition: all 0.3s ease; - overflow: hidden; + // overflow: hidden; user-select: none; outline: none; border: none; diff --git a/app/components/ui-lib.module.scss b/app/components/ui-lib.module.scss index 20bf62a18..16ef106e1 100644 --- a/app/components/ui-lib.module.scss +++ b/app/components/ui-lib.module.scss @@ -250,8 +250,7 @@ .select-with-icon { position: relative; - max-width: fit-content; - + max-width: 60%; &.left-align-option { option { text-align: left; @@ -260,9 +259,11 @@ .select-with-icon-select { height: 100%; + max-width: 100%; border: var(--border-in-light); padding: 10px 35px 10px 10px; border-radius: 10px; + white-space: normal; appearance: none; cursor: pointer; background-color: var(--white); From d95d509046392996941b2e757f795e53ae7f4b38 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Mon, 23 Sep 2024 15:43:36 +0800 Subject: [PATCH 048/352] fex --- app/components/markdown.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 178233000..4f1b0ed24 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -128,7 +128,6 @@ export function PreCode(props: { children: any }) { className="copy-code-button" onClick={() => { if (ref.current) { - // const code = ref.current.innerText; copyToClipboard( ref.current.querySelector("code")?.innerText ?? "", ); From ed20fd296292bf4787145086b9d23a5920ae237d Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 23 Sep 2024 20:00:07 +0800 Subject: [PATCH 049/352] 1. add buildin plugin; 2. remove `usingProxy` --- app/components/plugin.tsx | 37 ++++------------------------ app/store/plugin.ts | 52 +++++++++++++++++++++++++++++++++++---- public/plugins.json | 17 +++++++++++++ 3 files changed, 69 insertions(+), 37 deletions(-) create mode 100644 public/plugins.json diff --git a/app/components/plugin.tsx b/app/components/plugin.tsx index cf4ae946e..73f0db64e 100644 --- a/app/components/plugin.tsx +++ b/app/components/plugin.tsx @@ -12,7 +12,6 @@ import EditIcon from "../icons/edit.svg"; import AddIcon from "../icons/add.svg"; import CloseIcon from "../icons/close.svg"; import DeleteIcon from "../icons/delete.svg"; -import EyeIcon from "../icons/eye.svg"; import ConfirmIcon from "../icons/confirm.svg"; import ReloadIcon from "../icons/reload.svg"; import GithubIcon from "../icons/github.svg"; @@ -29,7 +28,6 @@ import { import Locale from "../locales"; import { useNavigate } from "react-router-dom"; import { useState } from "react"; -import { getClientConfig } from "../config/client"; export function PluginPage() { const navigate = useNavigate(); @@ -209,19 +207,11 @@ export function PluginPage() {
- {m.builtin ? ( - } - text={Locale.Plugin.Item.View} - onClick={() => setEditingPluginId(m.id)} - /> - ) : ( - } - text={Locale.Plugin.Item.Edit} - onClick={() => setEditingPluginId(m.id)} - /> - )} + } + text={Locale.Plugin.Item.Edit} + onClick={() => setEditingPluginId(m.id)} + /> {!m.builtin && ( } @@ -325,23 +315,6 @@ export function PluginPage() { > )} - {!getClientConfig()?.isApp && ( - - { - pluginStore.updatePlugin(editingPlugin.id, (plugin) => { - plugin.usingProxy = e.currentTarget.checked; - }); - }} - > - - )} diff --git a/app/store/plugin.ts b/app/store/plugin.ts index 44679cbdc..48930384d 100644 --- a/app/store/plugin.ts +++ b/app/store/plugin.ts @@ -2,8 +2,12 @@ import OpenAPIClientAxios from "openapi-client-axios"; import { StoreKey } from "../constant"; import { nanoid } from "nanoid"; import { createPersistStore } from "../utils/store"; +import { getClientConfig } from "../config/client"; import yaml from "js-yaml"; import { adapter } from "../utils"; +import { useAccessStore } from "./access"; + +const isApp = getClientConfig()?.buildMode === "export"; export type Plugin = { id: string; @@ -16,7 +20,6 @@ export type Plugin = { authLocation?: string; authHeader?: string; authToken?: string; - usingProxy?: boolean; }; export type FunctionToolItem = { @@ -46,17 +49,24 @@ export const FunctionToolService = { plugin?.authType == "basic" ? `Basic ${plugin?.authToken}` : plugin?.authType == "bearer" - ? ` Bearer ${plugin?.authToken}` + ? `Bearer ${plugin?.authToken}` : plugin?.authToken; const authLocation = plugin?.authLocation || "header"; const definition = yaml.load(plugin.content) as any; const serverURL = definition?.servers?.[0]?.url; - const baseURL = !!plugin?.usingProxy ? "/api/proxy" : serverURL; + const baseURL = !isApp ? "/api/proxy" : serverURL; const headers: Record = { - "X-Base-URL": !!plugin?.usingProxy ? serverURL : undefined, + "X-Base-URL": !isApp ? serverURL : undefined, }; if (authLocation == "header") { headers[headerName] = tokenValue; + // try using openaiApiKey for Dalle3 Plugin. + if (!tokenValue && plugin.id === "dalle3") { + const openaiApiKey = useAccessStore.getState().openaiApiKey; + if (openaiApiKey) { + headers[headerName] = `Bearer ${openaiApiKey}`; + } + } } const api = new OpenAPIClientAxios({ definition: yaml.load(plugin.content) as any, @@ -165,7 +175,7 @@ export const usePluginStore = createPersistStore( (set, get) => ({ create(plugin?: Partial) { const plugins = get().plugins; - const id = nanoid(); + const id = plugin?.id || nanoid(); plugins[id] = { ...createEmptyPlugin(), ...plugin, @@ -220,5 +230,37 @@ export const usePluginStore = createPersistStore( { name: StoreKey.Plugin, version: 1, + onRehydrateStorage(state) { + console.log("onRehydrateStorage", state); + // Skip store rehydration on server side + if (typeof window === "undefined") { + return; + } + + fetch("./plugins.json") + .then((res) => res.json()) + .then((res) => { + Promise.all( + res.map((item: any) => + fetch(item.schema) + .then((res) => res.text()) + .then((content) => ({ + ...item, + content, + })), + ), + ).then((builtinPlugins: any) => { + builtinPlugins.forEach((item: any) => { + const plugin = state.create(item); + state.updatePlugin(plugin.id, (plugin) => { + const tool = FunctionToolService.add(plugin, true); + plugin.title = tool.api.definition.info.title; + plugin.version = tool.api.definition.info.version; + plugin.builtin = true; + }); + }); + }); + }); + }, }, ); diff --git a/public/plugins.json b/public/plugins.json new file mode 100644 index 000000000..c4d7ec46a --- /dev/null +++ b/public/plugins.json @@ -0,0 +1,17 @@ +[ + { + "id": "dalle3", + "name": "Dalle3", + "schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/dalle/openapi.json" + }, + { + "id": "arxivsearch", + "name": "ArxivSearch", + "schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/arxivsearch/openapi.json" + }, + { + "id": "duckduckgolite", + "name": "DuckDuckGoLiteSearch", + "schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/duckduckgolite/openapi.json" + } +] From 90e7b5aecf7bb4e85cd848bb9f24ffa2688874fb Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 23 Sep 2024 20:20:15 +0800 Subject: [PATCH 050/352] try using openai api key for dalle-3 plugin --- app/store/plugin.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/store/plugin.ts b/app/store/plugin.ts index 48930384d..ae31dde4e 100644 --- a/app/store/plugin.ts +++ b/app/store/plugin.ts @@ -60,12 +60,12 @@ export const FunctionToolService = { }; if (authLocation == "header") { headers[headerName] = tokenValue; - // try using openaiApiKey for Dalle3 Plugin. - if (!tokenValue && plugin.id === "dalle3") { - const openaiApiKey = useAccessStore.getState().openaiApiKey; - if (openaiApiKey) { - headers[headerName] = `Bearer ${openaiApiKey}`; - } + } + // try using openaiApiKey for Dalle3 Plugin. + if (!tokenValue && plugin.id === "dalle3") { + const openaiApiKey = useAccessStore.getState().openaiApiKey; + if (openaiApiKey) { + headers[headerName] = `Bearer ${openaiApiKey}`; } } const api = new OpenAPIClientAxios({ From 6d5bf490ab1042f216adffaac8b176a108c52058 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Tue, 24 Sep 2024 11:36:26 +0800 Subject: [PATCH 051/352] fix media --- app/components/auth.tsx | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 52fd860dd..39af25661 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -8,12 +8,14 @@ import Locale from "../locales"; import Delete from "../icons/close.svg"; import Arrow from "../icons/arrow.svg"; import Logo from "../icons/logo.svg"; +import { useMobileScreen } from "@/app/utils"; import BotIcon from "../icons/bot.svg"; import { getClientConfig } from "../config/client"; import LeftIcon from "@/app/icons/left.svg"; import { safeLocalStorage } from "@/app/utils"; const storage = safeLocalStorage(); + export function AuthPage() { const navigate = useNavigate(); const accessStore = useAccessStore(); @@ -113,7 +115,7 @@ export function AuthPage() { function TopBanner() { const [isHovered, setIsHovered] = useState(false); const [isVisible, setIsVisible] = useState(true); - const [isMobile, setIsMobile] = useState(window.innerWidth < 600); + const isMobile = useMobileScreen(); useEffect(() => { // 检查 localStorage 中是否有标记 const bannerDismissed = storage.getItem("bannerDismissed"); @@ -127,15 +129,6 @@ function TopBanner() { } }, []); - useEffect(() => { - const handleResize = () => { - setIsMobile(window.innerWidth < 600); - }; - - window.addEventListener("resize", handleResize); - return () => window.removeEventListener("resize", handleResize); - }, []); - const handleMouseEnter = () => { setIsHovered(true); }; From f9f99639db6a759ad108d27a7fb18641673e55d9 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 24 Sep 2024 12:59:21 +0800 Subject: [PATCH 052/352] update --- app/store/plugin.ts | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/app/store/plugin.ts b/app/store/plugin.ts index ae31dde4e..84ae0816e 100644 --- a/app/store/plugin.ts +++ b/app/store/plugin.ts @@ -7,7 +7,7 @@ import yaml from "js-yaml"; import { adapter } from "../utils"; import { useAccessStore } from "./access"; -const isApp = getClientConfig()?.buildMode === "export"; +const isApp = getClientConfig()?.isApp; export type Plugin = { id: string; @@ -231,7 +231,6 @@ export const usePluginStore = createPersistStore( name: StoreKey.Plugin, version: 1, onRehydrateStorage(state) { - console.log("onRehydrateStorage", state); // Skip store rehydration on server side if (typeof window === "undefined") { return; @@ -242,23 +241,29 @@ export const usePluginStore = createPersistStore( .then((res) => { Promise.all( res.map((item: any) => - fetch(item.schema) - .then((res) => res.text()) - .then((content) => ({ - ...item, - content, - })), + // skip get schema + state.get(item.id) + ? item + : fetch(item.schema) + .then((res) => res.text()) + .then((content) => ({ + ...item, + content, + })) + .catch((e) => item), ), ).then((builtinPlugins: any) => { - builtinPlugins.forEach((item: any) => { - const plugin = state.create(item); - state.updatePlugin(plugin.id, (plugin) => { - const tool = FunctionToolService.add(plugin, true); - plugin.title = tool.api.definition.info.title; - plugin.version = tool.api.definition.info.version; - plugin.builtin = true; + builtinPlugins + .filter((item: any) => item?.content) + .forEach((item: any) => { + const plugin = state.create(item); + state.updatePlugin(plugin.id, (plugin) => { + const tool = FunctionToolService.add(plugin, true); + plugin.title = tool.api.definition.info.title; + plugin.version = tool.api.definition.info.version; + plugin.builtin = true; + }); }); - }); }); }); }, From 6c8143b7de54724ce8e7e3d1d40bd2052cce25e3 Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Tue, 24 Sep 2024 15:15:08 +0800 Subject: [PATCH 053/352] =?UTF-8?q?feat:=20=E5=85=A8=E5=B1=80=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E6=98=AF=E5=90=A6=E5=90=AF=E7=94=A8artifacts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/markdown.tsx | 5 ++++- app/components/mask.tsx | 32 +++++++++++++++++--------------- app/components/settings.tsx | 14 ++++++++++++++ app/store/config.ts | 2 ++ 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 4f1b0ed24..c0833caf7 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -21,6 +21,7 @@ import { } from "./artifacts"; import { useChatStore } from "../store"; import { IconButton } from "./button"; +import { useAppConfig } from "../store/config"; export function Mermaid(props: { code: string }) { const ref = useRef(null); @@ -92,7 +93,9 @@ export function PreCode(props: { children: any }) { } }, 600); - const enableArtifacts = session.mask?.enableArtifacts !== false; + const config = useAppConfig(); + const enableArtifacts = + session.mask?.enableArtifacts !== false && config.enableArtifacts; //Wrap the paragraph for plain-text useEffect(() => { diff --git a/app/components/mask.tsx b/app/components/mask.tsx index e4dd90826..c60e7a528 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -166,21 +166,23 @@ export function MaskConfig(props: { > - - { - props.updateMask((mask) => { - mask.enableArtifacts = e.currentTarget.checked; - }); - }} - > - + {globalConfig.enableArtifacts && ( + + { + props.updateMask((mask) => { + mask.enableArtifacts = e.currentTarget.checked; + }); + }} + > + + )} {!props.shouldSyncFromGlobal ? ( + + + + updateConfig( + (config) => + (config.enableArtifacts = e.currentTarget.checked), + ) + } + > + diff --git a/app/store/config.ts b/app/store/config.ts index 615cc8e82..3dcd4d86b 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -50,6 +50,8 @@ export const DEFAULT_CONFIG = { enableAutoGenerateTitle: true, sidebarWidth: DEFAULT_SIDEBAR_WIDTH, + enableArtifacts: true, // show artifacts config + disablePromptHint: false, dontShowMaskSplashScreen: false, // dont show splash screen when create chat From 269d064e0a7b7b3690cc9aa0f3204960f1bee912 Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Tue, 24 Sep 2024 15:21:27 +0800 Subject: [PATCH 054/352] fix: #5450 --- app/components/settings.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 8f90c4c36..fcb106385 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -1466,9 +1466,12 @@ export function Settings() { > - + From 6c37d04591d0fdfef130425346f69bd5d7ce3843 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 24 Sep 2024 18:45:20 +0800 Subject: [PATCH 055/352] auto play video/audio --- app/components/markdown.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 4f1b0ed24..d6765f263 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -279,6 +279,20 @@ function _MarkDownContent(props: { content: string }) { p: (pProps) =>

, a: (aProps) => { const href = aProps.href || ""; + if (/\.(aac|mp3|opus|wav)$/.test(href)) { + return ( +

+ +
+ ); + } + if (/\.(3gp|3g2|webm|ogv|mpeg|mp4|avi)$/.test(href)) { + return ( + + ); + } const isInternal = /^\/#/i.test(href); const target = isInternal ? "_self" : aProps.target ?? "_blank"; return ; From dbabb2c4030a96bb01aee5da35decef6f3328d6b Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 24 Sep 2024 18:48:55 +0800 Subject: [PATCH 056/352] auto play video/audio --- app/components/markdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index d6765f263..6c73d9ade 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -288,7 +288,7 @@ function _MarkDownContent(props: { content: string }) { } if (/\.(3gp|3g2|webm|ogv|mpeg|mp4|avi)$/.test(href)) { return ( -
{ diff --git a/app/components/ui-lib.module.scss b/app/components/ui-lib.module.scss index 8c23142ac..56aeac311 100644 --- a/app/components/ui-lib.module.scss +++ b/app/components/ui-lib.module.scss @@ -260,11 +260,9 @@ .select-with-icon-select { height: 100%; - max-width: 100%; border: var(--border-in-light); padding: 10px 35px 10px 10px; border-radius: 10px; - white-space: normal; appearance: none; cursor: pointer; background-color: var(--white); diff --git a/app/components/ui-lib.tsx b/app/components/ui-lib.tsx index ff317cc75..10c91b989 100644 --- a/app/components/ui-lib.tsx +++ b/app/components/ui-lib.tsx @@ -294,19 +294,32 @@ export function Select( props: React.DetailedHTMLProps< React.SelectHTMLAttributes & { align?: "left" | "center"; + withiconstyle?: CSSProperties; + iconselectstyles?: CSSProperties; }, HTMLSelectElement >, ) { - const { className, children, align, style, ...otherProps } = props; + const { + className, + children, + align, + withiconstyle, + iconselectstyles, + ...otherProps + } = props; return (
- {children} From 10d472e79e2069046a3066db72785f99a127e047 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Wed, 25 Sep 2024 14:41:41 +0800 Subject: [PATCH 065/352] fix --- app/components/model-config.module.scss | 7 +++++++ app/components/model-config.tsx | 4 ++-- app/components/ui-lib.tsx | 18 ++---------------- 3 files changed, 11 insertions(+), 18 deletions(-) create mode 100644 app/components/model-config.module.scss diff --git a/app/components/model-config.module.scss b/app/components/model-config.module.scss new file mode 100644 index 000000000..40ba03f86 --- /dev/null +++ b/app/components/model-config.module.scss @@ -0,0 +1,7 @@ +.select-compress-model { + width: 60%; + select { + max-width: 100%; + white-space: normal; + } +} diff --git a/app/components/model-config.tsx b/app/components/model-config.tsx index 008dda6d4..f2297e10b 100644 --- a/app/components/model-config.tsx +++ b/app/components/model-config.tsx @@ -6,6 +6,7 @@ import { InputRange } from "./input-range"; import { ListItem, Select } from "./ui-lib"; import { useAllModels } from "../utils/hooks"; import { groupBy } from "lodash-es"; +import styles from "./model-config.module.scss"; export function ModelConfigList(props: { modelConfig: ModelConfig; @@ -242,8 +243,7 @@ export function ModelConfigList(props: { subTitle={Locale.Settings.CompressModel.SubTitle} > + From e83f61e74dfc036608eea8a6b9af8d8bb5259191 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Wed, 25 Sep 2024 15:21:17 +0800 Subject: [PATCH 066/352] fix --- app/components/markdown.tsx | 10 +--------- app/locales/cn.ts | 5 +++-- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 86adb4928..9e1d17a30 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -21,8 +21,6 @@ import { } from "./artifacts"; import { useChatStore } from "../store"; import { IconButton } from "./button"; -import { SAAS_CHAT_URL } from "@/app/constant"; -import { trackConversationGuideToCPaymentClick } from "../utils/auth-settings-events"; export function Mermaid(props: { code: string }) { const ref = useRef(null); const [hasError, setHasError] = useState(false); @@ -281,13 +279,7 @@ function _MarkDownContent(props: { content: string }) { const href = aProps.href || ""; const isInternal = /^\/#/i.test(href); const target = isInternal ? "_self" : aProps.target ?? "_blank"; - const handleClick = () => { - if (href === SAAS_CHAT_URL) { - trackConversationGuideToCPaymentClick(); - } - }; - - return ; + return ; }, }} > diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 3181d1703..727f784b4 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -2,6 +2,7 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=title`; const isApp = !!getClientConfig()?.isApp; const cn = { @@ -9,10 +10,10 @@ const cn = { Error: { Unauthorized: isApp ? `😆 对话遇到了一些问题,不用慌: - \\ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️` : `😆 对话遇到了一些问题,不用慌: - \ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ 如果你正在使用私有部署版本,点击[这里](/#/auth)输入访问秘钥 🔑 \ 3️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️ `, From 68702bfb1f57c51e2d7498809797930c1bfa2798 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Wed, 25 Sep 2024 15:49:20 +0800 Subject: [PATCH 067/352] update version --- src-tauri/tauri.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 2a19c9332..eb0d411cb 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "NextChat", - "version": "2.15.2" + "version": "2.15.3" }, "tauri": { "allowlist": { From 1d2f44fba861e16ea5cd2c3fc25752795ae8dc75 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Wed, 25 Sep 2024 16:00:48 +0800 Subject: [PATCH 068/352] fix url --- app/locales/ar.ts | 5 +++-- app/locales/bn.ts | 5 +++-- app/locales/cn.ts | 2 +- app/locales/cs.ts | 5 +++-- app/locales/de.ts | 5 +++-- app/locales/en.ts | 5 +++-- app/locales/es.ts | 5 +++-- app/locales/fr.ts | 5 +++-- app/locales/id.ts | 5 +++-- app/locales/it.ts | 5 +++-- app/locales/jp.ts | 5 +++-- app/locales/ko.ts | 5 +++-- app/locales/no.ts | 5 +++-- app/locales/pt.ts | 5 +++-- app/locales/ru.ts | 5 +++-- app/locales/sk.ts | 5 +++-- app/locales/tr.ts | 5 +++-- app/locales/tw.ts | 5 +++-- app/locales/vi.ts | 5 +++-- 19 files changed, 55 insertions(+), 37 deletions(-) diff --git a/app/locales/ar.ts b/app/locales/ar.ts index 0399d7f7c..fb5757001 100644 --- a/app/locales/ar.ts +++ b/app/locales/ar.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const ar: PartialLocaleType = { @@ -9,10 +10,10 @@ const ar: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق: - \\ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️` : `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق: - \ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ إذا كنت تستخدم إصدار النشر الخاص، انقر [هنا](/#/auth) لإدخال مفتاح الوصول 🔑 \ 3️⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️ `, diff --git a/app/locales/bn.ts b/app/locales/bn.ts index 78c0f76f0..94729525b 100644 --- a/app/locales/bn.ts +++ b/app/locales/bn.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const bn: PartialLocaleType = { @@ -9,10 +10,10 @@ const bn: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই: - \\ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️` : `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই: - \ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ যদি আপনি একটি প্রাইভেট ডেপ্লয়মেন্ট সংস্করণ ব্যবহার করেন, তাহলে [এখানে ক্লিক করুন](/#/auth) প্রবেশাধিকার কীগুলি প্রবেশ করতে 🔑 \ 3️⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️ `, diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 727f784b4..d5fd6d413 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -2,7 +2,7 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=title`; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const cn = { diff --git a/app/locales/cs.ts b/app/locales/cs.ts index 972c46eda..334062843 100644 --- a/app/locales/cs.ts +++ b/app/locales/cs.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const cs: PartialLocaleType = { @@ -9,10 +10,10 @@ const cs: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Rozhovor narazil na nějaké problémy, nebojte se: - \\ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️` : `😆 Rozhovor narazil na nějaké problémy, nebojte se: - \ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Pokud používáte verzi soukromého nasazení, klikněte [sem](/#/auth) a zadejte přístupový klíč 🔑 \ 3️⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️ `, diff --git a/app/locales/de.ts b/app/locales/de.ts index 4b878b170..ac151956e 100644 --- a/app/locales/de.ts +++ b/app/locales/de.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const de: PartialLocaleType = { @@ -9,10 +10,10 @@ const de: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Das Gespräch hatte einige Probleme, keine Sorge: - \\ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️` : `😆 Das Gespräch hatte einige Probleme, keine Sorge: - \ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Wenn du eine private Bereitstellung verwendest, klicke [hier](/#/auth), um den Zugriffsschlüssel einzugeben 🔑 \ 3️⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️ `, diff --git a/app/locales/en.ts b/app/locales/en.ts index 71d02d6b3..a291e7712 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -2,6 +2,7 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; import { LocaleType } from "./index"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; // if you are adding a new translation, please use PartialLocaleType instead of LocaleType const isApp = !!getClientConfig()?.isApp; @@ -10,10 +11,10 @@ const en: LocaleType = { Error: { Unauthorized: isApp ? `😆 Oops, there's an issue. No worries: - \\ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️` : `😆 Oops, there's an issue. Let's fix it: - \ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Using a private setup? [Click here](/#/auth) to enter your key 🔑 \ 3️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️ `, diff --git a/app/locales/es.ts b/app/locales/es.ts index 3719e7e4c..186a7230a 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const es: PartialLocaleType = { @@ -9,10 +10,10 @@ const es: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 La conversación encontró algunos problemas, no te preocupes: - \\ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️` : `😆 La conversación encontró algunos problemas, no te preocupes: - \ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Si estás utilizando una versión de implementación privada, haz clic [aquí](/#/auth) para ingresar la clave de acceso 🔑 \ 3️⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️ `, diff --git a/app/locales/fr.ts b/app/locales/fr.ts index dd611cbf9..1e222566c 100644 --- a/app/locales/fr.ts +++ b/app/locales/fr.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const fr: PartialLocaleType = { @@ -9,10 +10,10 @@ const fr: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 La conversation a rencontré quelques problèmes, pas de panique : - \\ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️` : `😆 La conversation a rencontré quelques problèmes, pas de panique : - \ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Si vous utilisez une version déployée privée, cliquez [ici](/#/auth) pour entrer la clé d'accès 🔑 \ 3️⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️ `, diff --git a/app/locales/id.ts b/app/locales/id.ts index c9c6428f1..d5e1e76ae 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const id: PartialLocaleType = { @@ -9,10 +10,10 @@ const id: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir: - \\ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️` : `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir: - \ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Jika Anda menggunakan versi penyebaran pribadi, klik [di sini](/#/auth) untuk memasukkan kunci akses 🔑 \ 3️⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️ `, diff --git a/app/locales/it.ts b/app/locales/it.ts index 83b80a1b6..126660394 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const it: PartialLocaleType = { @@ -9,10 +10,10 @@ const it: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti: - \\ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️` : `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti: - \ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Se stai utilizzando una versione di distribuzione privata, clicca [qui](/#/auth) per inserire la chiave di accesso 🔑 \ 3️⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️ `, diff --git a/app/locales/jp.ts b/app/locales/jp.ts index a4f849c77..c20a8b3a2 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const jp: PartialLocaleType = { @@ -9,10 +10,10 @@ const jp: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 会話中に問題が発生しましたが、心配しないでください: - \\ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️` : `😆 会話中に問題が発生しましたが、心配しないでください: - \ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ プライベートデプロイ版を使用している場合は、[ここをクリックして](/#/auth)アクセストークンを入力してください 🔑 \ 3️⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️ `, diff --git a/app/locales/ko.ts b/app/locales/ko.ts index 32eb2be73..b34863b51 100644 --- a/app/locales/ko.ts +++ b/app/locales/ko.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const ko: PartialLocaleType = { @@ -9,10 +10,10 @@ const ko: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요: - \\ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️` : `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요: - \ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ 개인 배포 버전을 사용하고 있다면, [여기를 클릭하여](/#/auth) 접근 키를 입력하세요 🔑 \ 3️⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️ `, diff --git a/app/locales/no.ts b/app/locales/no.ts index d02b5ce66..0c3561ec0 100644 --- a/app/locales/no.ts +++ b/app/locales/no.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const no: PartialLocaleType = { @@ -9,10 +10,10 @@ const no: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Samtalen har støtt på noen problemer, ikke bekymre deg: - \\ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️` : `😆 Samtalen har støtt på noen problemer, ikke bekymre deg: - \ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Hvis du bruker en privat distribusjonsversjon, klikk [her](/#/auth) for å skrive inn tilgangsnøkkelen 🔑 \ 3️⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️ `, diff --git a/app/locales/pt.ts b/app/locales/pt.ts index c1de67384..b6de1d330 100644 --- a/app/locales/pt.ts +++ b/app/locales/pt.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import { PartialLocaleType } from "../locales/index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const pt: PartialLocaleType = { @@ -9,10 +10,10 @@ const pt: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 A conversa encontrou alguns problemas, não se preocupe: - \\ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️` : `😆 A conversa encontrou alguns problemas, não se preocupe: - \ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Se você estiver usando uma versão de implantação privada, clique [aqui](/#/auth) para inserir a chave de acesso 🔑 \ 3️⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️ `, diff --git a/app/locales/ru.ts b/app/locales/ru.ts index 2793ab7ec..2410e0894 100644 --- a/app/locales/ru.ts +++ b/app/locales/ru.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import { PartialLocaleType } from "../locales/index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const ru: PartialLocaleType = { @@ -9,10 +10,10 @@ const ru: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 В разговоре возникли некоторые проблемы, не переживайте: - \\ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️` : `😆 В разговоре возникли некоторые проблемы, не переживайте: - \ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Если вы используете частную версию развертывания, нажмите [здесь](/#/auth), чтобы ввести ключ доступа 🔑 \ 3️⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️ `, diff --git a/app/locales/sk.ts b/app/locales/sk.ts index 62cfab0ab..3edd6532f 100644 --- a/app/locales/sk.ts +++ b/app/locales/sk.ts @@ -2,6 +2,7 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; // if you are adding a new translation, please use PartialLocaleType instead of LocaleType const isApp = !!getClientConfig()?.isApp; @@ -10,10 +11,10 @@ const sk: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Rozhovor narazil na nejaké problémy, nebojte sa: - \\ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️` : `😆 Rozhovor narazil na nejaké problémy, nebojte sa: - \ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Ak používate verziu súkromného nasadenia, kliknite [sem](/#/auth), aby ste zadali prístupový kľúč 🔑 \ 3️⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️ `, diff --git a/app/locales/tr.ts b/app/locales/tr.ts index 7fe6170ca..9f8682e84 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const tr: PartialLocaleType = { @@ -9,10 +10,10 @@ const tr: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin: - \\ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️` : `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin: - \ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Eğer özel dağıtım sürümü kullanıyorsanız, [buraya tıklayarak](/#/auth) erişim anahtarını girin 🔑 \ 3️⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️ `, diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 39e2bc968..ce5c7874f 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -1,6 +1,7 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const tw = { @@ -8,10 +9,10 @@ const tw = { Error: { Unauthorized: isApp ? `😆 對話遇到了一些問題,不用慌: - \\ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️` : `😆 對話遇到了一些問題,不用慌: - \ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ 如果你正在使用私有部署版本,點擊[這裡](/#/auth)輸入訪問秘鑰 🔑 \ 3️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️ `, diff --git a/app/locales/vi.ts b/app/locales/vi.ts index c4b6ffcd5..bdee2db15 100644 --- a/app/locales/vi.ts +++ b/app/locales/vi.ts @@ -2,6 +2,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; import { SAAS_CHAT_URL } from "@/app/constant"; +const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const vi: PartialLocaleType = { @@ -9,10 +10,10 @@ const vi: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng: - \\ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_URL}) + \\ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \\ 2️⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️` : `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng: - \ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_URL}) + \ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_URL_WITH_PARAM}) \ 2️⃣ Nếu bạn đang sử dụng phiên bản triển khai riêng, hãy nhấp [vào đây](/#/auth) để nhập khóa truy cập 🔑 \ 3️⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️ `, From 13c68bd81053bde55081a811e0a4bedd7eb429fc Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Wed, 25 Sep 2024 16:11:57 +0800 Subject: [PATCH 069/352] fix url utm --- app/constant.ts | 1 + app/locales/ar.ts | 7 +++---- app/locales/bn.ts | 7 +++---- app/locales/cn.ts | 7 +++---- app/locales/cs.ts | 7 +++---- app/locales/de.ts | 7 +++---- app/locales/en.ts | 7 +++---- app/locales/es.ts | 7 +++---- app/locales/fr.ts | 7 +++---- app/locales/id.ts | 7 +++---- app/locales/it.ts | 7 +++---- app/locales/jp.ts | 7 +++---- app/locales/ko.ts | 7 +++---- app/locales/no.ts | 7 +++---- app/locales/pt.ts | 7 +++---- app/locales/ru.ts | 7 +++---- app/locales/sk.ts | 7 +++---- app/locales/tr.ts | 7 +++---- app/locales/tw.ts | 7 +++---- app/locales/vi.ts | 7 +++---- 20 files changed, 58 insertions(+), 76 deletions(-) diff --git a/app/constant.ts b/app/constant.ts index d97f3b209..a54a973da 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -503,3 +503,4 @@ export const PLUGINS = [ ]; export const SAAS_CHAT_URL = "https://nextchat.dev/chat"; +export const SAAS_CHAT_UTM_URL = "https://nextchat.dev/chat?utm=github"; diff --git a/app/locales/ar.ts b/app/locales/ar.ts index fb5757001..e2fad3494 100644 --- a/app/locales/ar.ts +++ b/app/locales/ar.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const ar: PartialLocaleType = { @@ -10,10 +9,10 @@ const ar: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق: - \\ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️` : `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق: - \ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ إذا كنت تستخدم إصدار النشر الخاص، انقر [هنا](/#/auth) لإدخال مفتاح الوصول 🔑 \ 3️⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️ `, diff --git a/app/locales/bn.ts b/app/locales/bn.ts index 94729525b..f52f101ce 100644 --- a/app/locales/bn.ts +++ b/app/locales/bn.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const bn: PartialLocaleType = { @@ -10,10 +9,10 @@ const bn: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই: - \\ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️` : `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই: - \ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ যদি আপনি একটি প্রাইভেট ডেপ্লয়মেন্ট সংস্করণ ব্যবহার করেন, তাহলে [এখানে ক্লিক করুন](/#/auth) প্রবেশাধিকার কীগুলি প্রবেশ করতে 🔑 \ 3️⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️ `, diff --git a/app/locales/cn.ts b/app/locales/cn.ts index d5fd6d413..e5bcca0ed 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -1,8 +1,7 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; -import { SAAS_CHAT_URL } from "@/app/constant"; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; const isApp = !!getClientConfig()?.isApp; const cn = { @@ -10,10 +9,10 @@ const cn = { Error: { Unauthorized: isApp ? `😆 对话遇到了一些问题,不用慌: - \\ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️` : `😆 对话遇到了一些问题,不用慌: - \ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ 如果你正在使用私有部署版本,点击[这里](/#/auth)输入访问秘钥 🔑 \ 3️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️ `, diff --git a/app/locales/cs.ts b/app/locales/cs.ts index 334062843..d62a20367 100644 --- a/app/locales/cs.ts +++ b/app/locales/cs.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const cs: PartialLocaleType = { @@ -10,10 +9,10 @@ const cs: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Rozhovor narazil na nějaké problémy, nebojte se: - \\ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️` : `😆 Rozhovor narazil na nějaké problémy, nebojte se: - \ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Pokud používáte verzi soukromého nasazení, klikněte [sem](/#/auth) a zadejte přístupový klíč 🔑 \ 3️⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️ `, diff --git a/app/locales/de.ts b/app/locales/de.ts index ac151956e..3490190a8 100644 --- a/app/locales/de.ts +++ b/app/locales/de.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const de: PartialLocaleType = { @@ -10,10 +9,10 @@ const de: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Das Gespräch hatte einige Probleme, keine Sorge: - \\ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️` : `😆 Das Gespräch hatte einige Probleme, keine Sorge: - \ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Wenn du eine private Bereitstellung verwendest, klicke [hier](/#/auth), um den Zugriffsschlüssel einzugeben 🔑 \ 3️⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️ `, diff --git a/app/locales/en.ts b/app/locales/en.ts index a291e7712..120457522 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -1,8 +1,7 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; import { LocaleType } from "./index"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; // if you are adding a new translation, please use PartialLocaleType instead of LocaleType const isApp = !!getClientConfig()?.isApp; @@ -11,10 +10,10 @@ const en: LocaleType = { Error: { Unauthorized: isApp ? `😆 Oops, there's an issue. No worries: - \\ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️` : `😆 Oops, there's an issue. Let's fix it: - \ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Using a private setup? [Click here](/#/auth) to enter your key 🔑 \ 3️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️ `, diff --git a/app/locales/es.ts b/app/locales/es.ts index 186a7230a..03af9b439 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const es: PartialLocaleType = { @@ -10,10 +9,10 @@ const es: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 La conversación encontró algunos problemas, no te preocupes: - \\ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️` : `😆 La conversación encontró algunos problemas, no te preocupes: - \ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Si estás utilizando una versión de implementación privada, haz clic [aquí](/#/auth) para ingresar la clave de acceso 🔑 \ 3️⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️ `, diff --git a/app/locales/fr.ts b/app/locales/fr.ts index 1e222566c..d25c60eb6 100644 --- a/app/locales/fr.ts +++ b/app/locales/fr.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const fr: PartialLocaleType = { @@ -10,10 +9,10 @@ const fr: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 La conversation a rencontré quelques problèmes, pas de panique : - \\ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️` : `😆 La conversation a rencontré quelques problèmes, pas de panique : - \ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Si vous utilisez une version déployée privée, cliquez [ici](/#/auth) pour entrer la clé d'accès 🔑 \ 3️⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️ `, diff --git a/app/locales/id.ts b/app/locales/id.ts index d5e1e76ae..af96fd272 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const id: PartialLocaleType = { @@ -10,10 +9,10 @@ const id: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir: - \\ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️` : `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir: - \ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Jika Anda menggunakan versi penyebaran pribadi, klik [di sini](/#/auth) untuk memasukkan kunci akses 🔑 \ 3️⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️ `, diff --git a/app/locales/it.ts b/app/locales/it.ts index 126660394..59bc1eb15 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const it: PartialLocaleType = { @@ -10,10 +9,10 @@ const it: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti: - \\ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️` : `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti: - \ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Se stai utilizzando una versione di distribuzione privata, clicca [qui](/#/auth) per inserire la chiave di accesso 🔑 \ 3️⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️ `, diff --git a/app/locales/jp.ts b/app/locales/jp.ts index c20a8b3a2..e7c81e186 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const jp: PartialLocaleType = { @@ -10,10 +9,10 @@ const jp: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 会話中に問題が発生しましたが、心配しないでください: - \\ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️` : `😆 会話中に問題が発生しましたが、心配しないでください: - \ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ プライベートデプロイ版を使用している場合は、[ここをクリックして](/#/auth)アクセストークンを入力してください 🔑 \ 3️⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️ `, diff --git a/app/locales/ko.ts b/app/locales/ko.ts index b34863b51..f2c433b76 100644 --- a/app/locales/ko.ts +++ b/app/locales/ko.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const ko: PartialLocaleType = { @@ -10,10 +9,10 @@ const ko: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요: - \\ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️` : `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요: - \ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ 개인 배포 버전을 사용하고 있다면, [여기를 클릭하여](/#/auth) 접근 키를 입력하세요 🔑 \ 3️⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️ `, diff --git a/app/locales/no.ts b/app/locales/no.ts index 0c3561ec0..f056ef12f 100644 --- a/app/locales/no.ts +++ b/app/locales/no.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const no: PartialLocaleType = { @@ -10,10 +9,10 @@ const no: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Samtalen har støtt på noen problemer, ikke bekymre deg: - \\ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️` : `😆 Samtalen har støtt på noen problemer, ikke bekymre deg: - \ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Hvis du bruker en privat distribusjonsversjon, klikk [her](/#/auth) for å skrive inn tilgangsnøkkelen 🔑 \ 3️⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️ `, diff --git a/app/locales/pt.ts b/app/locales/pt.ts index b6de1d330..152f50228 100644 --- a/app/locales/pt.ts +++ b/app/locales/pt.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import { PartialLocaleType } from "../locales/index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const pt: PartialLocaleType = { @@ -10,10 +9,10 @@ const pt: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 A conversa encontrou alguns problemas, não se preocupe: - \\ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️` : `😆 A conversa encontrou alguns problemas, não se preocupe: - \ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Se você estiver usando uma versão de implantação privada, clique [aqui](/#/auth) para inserir a chave de acesso 🔑 \ 3️⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️ `, diff --git a/app/locales/ru.ts b/app/locales/ru.ts index 2410e0894..4294a3b34 100644 --- a/app/locales/ru.ts +++ b/app/locales/ru.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import { PartialLocaleType } from "../locales/index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const ru: PartialLocaleType = { @@ -10,10 +9,10 @@ const ru: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 В разговоре возникли некоторые проблемы, не переживайте: - \\ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️` : `😆 В разговоре возникли некоторые проблемы, не переживайте: - \ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Если вы используете частную версию развертывания, нажмите [здесь](/#/auth), чтобы ввести ключ доступа 🔑 \ 3️⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️ `, diff --git a/app/locales/sk.ts b/app/locales/sk.ts index 3edd6532f..36454de75 100644 --- a/app/locales/sk.ts +++ b/app/locales/sk.ts @@ -1,8 +1,7 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; // if you are adding a new translation, please use PartialLocaleType instead of LocaleType const isApp = !!getClientConfig()?.isApp; @@ -11,10 +10,10 @@ const sk: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Rozhovor narazil na nejaké problémy, nebojte sa: - \\ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️` : `😆 Rozhovor narazil na nejaké problémy, nebojte sa: - \ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Ak používate verziu súkromného nasadenia, kliknite [sem](/#/auth), aby ste zadali prístupový kľúč 🔑 \ 3️⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️ `, diff --git a/app/locales/tr.ts b/app/locales/tr.ts index 9f8682e84..2082488a5 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const tr: PartialLocaleType = { @@ -10,10 +9,10 @@ const tr: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin: - \\ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️` : `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin: - \ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Eğer özel dağıtım sürümü kullanıyorsanız, [buraya tıklayarak](/#/auth) erişim anahtarını girin 🔑 \ 3️⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️ `, diff --git a/app/locales/tw.ts b/app/locales/tw.ts index ce5c7874f..7cb846708 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -1,7 +1,6 @@ import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const tw = { @@ -9,10 +8,10 @@ const tw = { Error: { Unauthorized: isApp ? `😆 對話遇到了一些問題,不用慌: - \\ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️` : `😆 對話遇到了一些問題,不用慌: - \ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ 如果你正在使用私有部署版本,點擊[這裡](/#/auth)輸入訪問秘鑰 🔑 \ 3️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️ `, diff --git a/app/locales/vi.ts b/app/locales/vi.ts index bdee2db15..c53baf35d 100644 --- a/app/locales/vi.ts +++ b/app/locales/vi.ts @@ -1,8 +1,7 @@ import { SubmitKey } from "../store/config"; import type { PartialLocaleType } from "./index"; import { getClientConfig } from "../config/client"; -import { SAAS_CHAT_URL } from "@/app/constant"; -const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=chat`; +import { SAAS_CHAT_UTM_URL } from "@/app/constant"; const isApp = !!getClientConfig()?.isApp; const vi: PartialLocaleType = { @@ -10,10 +9,10 @@ const vi: PartialLocaleType = { Error: { Unauthorized: isApp ? `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng: - \\ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \\ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_UTM_URL}) \\ 2️⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️` : `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng: - \ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_URL_WITH_PARAM}) + \ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_UTM_URL}) \ 2️⃣ Nếu bạn đang sử dụng phiên bản triển khai riêng, hãy nhấp [vào đây](/#/auth) để nhập khóa truy cập 🔑 \ 3️⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️ `, From 6655c64e5502d3ad845758e35c028f8c1df5bc25 Mon Sep 17 00:00:00 2001 From: river Date: Wed, 25 Sep 2024 16:29:59 +0800 Subject: [PATCH 070/352] chore: cn --- README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index 7831e2ee9..c5d02477c 100644 --- a/README_CN.md +++ b/README_CN.md @@ -8,7 +8,7 @@ 一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型。 -[企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) /[演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) +[NextChatAI](https://nextchat.dev/chat) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) [Deploy on Zeabur](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) From 13777786c45b37eb2b2081bebaa3a406e55d1959 Mon Sep 17 00:00:00 2001 From: river Date: Wed, 25 Sep 2024 16:30:26 +0800 Subject: [PATCH 071/352] chore: ja --- README_JA.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_JA.md b/README_JA.md index 1716089af..2b0a3ab78 100644 --- a/README_JA.md +++ b/README_JA.md @@ -5,7 +5,7 @@ ワンクリックで無料であなた専用の ChatGPT ウェブアプリをデプロイ。GPT3、GPT4 & Gemini Pro モデルをサポート。 -[企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N) +[NextChatAI](https://nextchat.dev/chat) / [企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N) [Zeaburでデプロイ](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Zeaburでデプロイ](https://zeabur.com/templates/ZBUEFA) [Gitpodで開く](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) From 702e17c96b2c533cdb9e0589d19601d57e9abb8b Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 26 Sep 2024 23:21:42 +0800 Subject: [PATCH 072/352] google api using `x-google-api-key` header --- app/api/google.ts | 5 ++++- app/client/api.ts | 15 +++++++++++---- app/client/platforms/google.ts | 4 ---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/api/google.ts b/app/api/google.ts index e6ab47256..396237eea 100644 --- a/app/api/google.ts +++ b/app/api/google.ts @@ -91,7 +91,7 @@ async function request(req: NextRequest, apiKey: string) { }, 10 * 60 * 1000, ); - const fetchUrl = `${baseUrl}${path}?key=${apiKey}${ + const fetchUrl = `${baseUrl}${path}${ req?.nextUrl?.searchParams?.get("alt") === "sse" ? "&alt=sse" : "" }`; @@ -100,6 +100,9 @@ async function request(req: NextRequest, apiKey: string) { headers: { "Content-Type": "application/json", "Cache-Control": "no-store", + "x-google-api-key": + req.headers.get("x-google-api-key") || + (req.headers.get("Authorization") ?? "").replace("Bearer "), }, method: req.method, body: req.body, diff --git a/app/client/api.ts b/app/client/api.ts index 8285b4d9f..48bbde6bc 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -272,7 +272,13 @@ export function getHeaders(ignoreHeaders: boolean = false) { } function getAuthHeader(): string { - return isAzure ? "api-key" : isAnthropic ? "x-api-key" : "Authorization"; + return isAzure + ? "api-key" + : isAnthropic + ? "x-api-key" + : isGoogle + ? "x-goog-api-key" + : "Authorization"; } const { @@ -283,14 +289,15 @@ export function getHeaders(ignoreHeaders: boolean = false) { apiKey, isEnabledAccessControl, } = getConfig(); - // when using google api in app, not set auth header - if (isGoogle && clientConfig?.isApp) return headers; // when using baidu api in app, not set auth header if (isBaidu && clientConfig?.isApp) return headers; const authHeader = getAuthHeader(); - const bearerToken = getBearerToken(apiKey, isAzure || isAnthropic); + const bearerToken = getBearerToken( + apiKey, + isAzure || isAnthropic || isGoogle, + ); if (bearerToken) { headers[authHeader] = bearerToken; diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index ecb5ce44b..3c2607271 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -48,10 +48,6 @@ export class GeminiProApi implements LLMApi { let chatPath = [baseUrl, path].join("/"); chatPath += chatPath.includes("?") ? "&alt=sse" : "?alt=sse"; - // if chatPath.startsWith('http') then add key in query string - if (chatPath.startsWith("http") && accessStore.googleApiKey) { - chatPath += `&key=${accessStore.googleApiKey}`; - } return chatPath; } extractMessage(res: any) { From 3fb389551ba5284be77734be47b7595c9c425967 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Fri, 27 Sep 2024 11:42:16 +0800 Subject: [PATCH 073/352] fix: build error --- app/api/google.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/google.ts b/app/api/google.ts index 396237eea..7d3f08be4 100644 --- a/app/api/google.ts +++ b/app/api/google.ts @@ -102,7 +102,7 @@ async function request(req: NextRequest, apiKey: string) { "Cache-Control": "no-store", "x-google-api-key": req.headers.get("x-google-api-key") || - (req.headers.get("Authorization") ?? "").replace("Bearer "), + (req.headers.get("Authorization") ?? "").replace("Bearer ", ""), }, method: req.method, body: req.body, From 07d089a2bd41ea74ea9edcd7e1395e6d21e14645 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 27 Sep 2024 13:31:07 +0800 Subject: [PATCH 074/352] try using method and path when operationId is undefined #5525 --- app/store/plugin.ts | 11 ++++++----- app/utils.ts | 12 ++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/store/plugin.ts b/app/store/plugin.ts index 84ae0816e..a79a1ae4b 100644 --- a/app/store/plugin.ts +++ b/app/store/plugin.ts @@ -4,7 +4,7 @@ import { nanoid } from "nanoid"; import { createPersistStore } from "../utils/store"; import { getClientConfig } from "../config/client"; import yaml from "js-yaml"; -import { adapter } from "../utils"; +import { adapter, getOperationId } from "../utils"; import { useAccessStore } from "./access"; const isApp = getClientConfig()?.isApp; @@ -116,7 +116,7 @@ export const FunctionToolService = { return { type: "function", function: { - name: o.operationId, + name: getOperationId(o), description: o.description || o.summary, parameters: parameters, }, @@ -124,7 +124,7 @@ export const FunctionToolService = { }), funcs: operations.reduce((s, o) => { // @ts-ignore - s[o.operationId] = function (args) { + s[getOperationId(o)] = function (args) { const parameters: Record = {}; if (o.parameters instanceof Array) { o.parameters.forEach((p) => { @@ -139,8 +139,8 @@ export const FunctionToolService = { } else if (authLocation == "body") { args[headerName] = tokenValue; } - // @ts-ignore - return api.client[o.operationId]( + // @ts-ignore if o.operationId is null, then using o.path and o.method + return api.client.paths[o.path][o.method]( parameters, args, api.axiosConfigDefaults, @@ -253,6 +253,7 @@ export const usePluginStore = createPersistStore( .catch((e) => item), ), ).then((builtinPlugins: any) => { + return; builtinPlugins .filter((item: any) => item?.content) .forEach((item: any) => { diff --git a/app/utils.ts b/app/utils.ts index 9a8bebf38..6b2f65952 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -377,3 +377,15 @@ export function safeLocalStorage(): { }, }; } + +export function getOperationId(operation: { + operationId?: string; + method: string; + path: string; +}) { + // pattern '^[a-zA-Z0-9_-]+$' + return ( + operation?.operationId || + `${operation.method.toUpperCase()}${operation.path.replaceAll("/", "_")}` + ); +} From 22aa1698b407322c2cb980aed971ad3d06e6f87c Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 27 Sep 2024 13:31:49 +0800 Subject: [PATCH 075/352] try using method and path when operationId is undefined #5525 --- app/store/plugin.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/store/plugin.ts b/app/store/plugin.ts index a79a1ae4b..40abdc8d9 100644 --- a/app/store/plugin.ts +++ b/app/store/plugin.ts @@ -253,7 +253,6 @@ export const usePluginStore = createPersistStore( .catch((e) => item), ), ).then((builtinPlugins: any) => { - return; builtinPlugins .filter((item: any) => item?.content) .forEach((item: any) => { From 19c4ed4463288fbf3066656197088d88fd35c6ef Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 27 Sep 2024 16:43:50 +0800 Subject: [PATCH 076/352] docs links updated sync.yml https://github.com/Yidadaa/ChatGPT-Next-Web is renamed to https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/ --- .github/workflows/sync.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index e04e30adb..68f5fabec 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -35,6 +35,6 @@ jobs: - name: Sync check if: failure() run: | - echo "[Error] 由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次,详细教程请查看:https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/README_CN.md#%E6%89%93%E5%BC%80%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0" - echo "[Error] Due to a change in the workflow file of the upstream repository, GitHub has automatically suspended the scheduled automatic update. You need to manually sync your fork. Please refer to the detailed tutorial for instructions: https://github.com/Yidadaa/ChatGPT-Next-Web#enable-automatic-updates" + echo "[Error] 由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次,详细教程请查看:https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/blob/main/README_CN.md#%E6%89%93%E5%BC%80%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0" + echo "[Error] Due to a change in the workflow file of the upstream repository, GitHub has automatically suspended the scheduled automatic update. You need to manually sync your fork. Please refer to the detailed tutorial for instructions: https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web#enable-automatic-updates" exit 1 From b35895b551d49cd7448f4311ebb799c0158abcc7 Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 27 Sep 2024 16:49:08 +0800 Subject: [PATCH 077/352] Update correct links to manualy code update section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c72c791b2..be5e91d65 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ We recommend that you follow the steps below to re-deploy: ### Enable Automatic Updates -> If you encounter a failure of Upstream Sync execution, please manually sync fork once. +> If you encounter a failure of Upstream Sync execution, please [manually update code](./README.md#manually-updating-code). After forking the project, due to the limitations imposed by GitHub, you need to manually enable Workflows and Upstream Sync Action on the Actions page of the forked project. Once enabled, automatic updates will be scheduled every hour: From 2333a47c55f7c840b3f6eacd425deac94d5aceb7 Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 27 Sep 2024 16:50:51 +0800 Subject: [PATCH 078/352] Update links in doc to manual code update section (CN) --- README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index c5d02477c..640fe3933 100644 --- a/README_CN.md +++ b/README_CN.md @@ -54,7 +54,7 @@ ### 打开自动更新 -> 如果你遇到了 Upstream Sync 执行错误,请手动 Sync Fork 一次! +> 如果你遇到了 Upstream Sync 执行错误,请[手动 Sync Fork 一次](./README_CN.md#手动更新代码)! 当你 fork 项目之后,由于 Github 的限制,需要手动去你 fork 后的项目的 Actions 页面启用 Workflows,并启用 Upstream Sync Action,启用之后即可开启每小时定时自动更新: From c6ebd6e73cbc58bbd752eeab22a3b029985d2e57 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Fri, 27 Sep 2024 17:00:24 +0800 Subject: [PATCH 079/352] fix: default model --- app/store/access.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/store/access.ts b/app/store/access.ts index d74cb9d02..9fcd227e7 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -211,10 +211,13 @@ export const useAccessStore = createPersistStore( }) .then((res) => res.json()) .then((res) => { - // Set default model from env request - let defaultModel = res.defaultModel ?? ""; - if (defaultModel !== "") - DEFAULT_CONFIG.modelConfig.model = defaultModel; + const defaultModel = res.defaultModel ?? ""; + if (defaultModel !== "") { + const [model, providerName] = defaultModel.split("@"); + DEFAULT_CONFIG.modelConfig.model = model; + DEFAULT_CONFIG.modelConfig.providerName = providerName; + } + return res; }) .then((res: DangerConfig) => { From 2f3457e73d879be322a0ac6cd7d2aa0b44c6542f Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 27 Sep 2024 17:33:02 +0800 Subject: [PATCH 080/352] Update correct links to manualy code update section (JP) --- README_JA.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_JA.md b/README_JA.md index 2b0a3ab78..ba3c514dc 100644 --- a/README_JA.md +++ b/README_JA.md @@ -54,7 +54,7 @@ ### 自動更新を開く -> Upstream Sync の実行エラーが発生した場合は、手動で Sync Fork してください! +> Upstream Sync の実行エラーが発生した場合は、[手動で Sync Fork](./README_JA.md#手動でコードを更新する) してください! プロジェクトを fork した後、GitHub の制限により、fork 後のプロジェクトの Actions ページで Workflows を手動で有効にし、Upstream Sync Action を有効にする必要があります。有効化後、毎時の定期自動更新が可能になります: From 8fb019b2e2a6395ecda0f6d1dd1f2bf47d8ec505 Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 27 Sep 2024 17:34:38 +0800 Subject: [PATCH 081/352] revert, leave sync.yml untouched revert commit 19c4ed4463288fbf3066656197088d88fd35c6ef --- .github/workflows/sync.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 68f5fabec..e04e30adb 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -35,6 +35,6 @@ jobs: - name: Sync check if: failure() run: | - echo "[Error] 由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次,详细教程请查看:https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/blob/main/README_CN.md#%E6%89%93%E5%BC%80%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0" - echo "[Error] Due to a change in the workflow file of the upstream repository, GitHub has automatically suspended the scheduled automatic update. You need to manually sync your fork. Please refer to the detailed tutorial for instructions: https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web#enable-automatic-updates" + echo "[Error] 由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次,详细教程请查看:https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/README_CN.md#%E6%89%93%E5%BC%80%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0" + echo "[Error] Due to a change in the workflow file of the upstream repository, GitHub has automatically suspended the scheduled automatic update. You need to manually sync your fork. Please refer to the detailed tutorial for instructions: https://github.com/Yidadaa/ChatGPT-Next-Web#enable-automatic-updates" exit 1 From d84d51b475a1a6ad18efed46121af9adbc7b98f3 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Sat, 28 Sep 2024 01:19:39 +0800 Subject: [PATCH 082/352] using sse: schema to fetch in App --- app/utils.ts | 51 +++++++++++++++++-------- src-tauri/Cargo.lock | 86 +++++++++++++++++++++++++++++++------------ src-tauri/Cargo.toml | 4 ++ src-tauri/src/main.rs | 48 ++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 40 deletions(-) diff --git a/app/utils.ts b/app/utils.ts index 9a8bebf38..5be7bb2d9 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -2,8 +2,7 @@ import { useEffect, useState } from "react"; import { showToast } from "./components/ui-lib"; import Locale from "./locales"; import { RequestMessage } from "./client/api"; -import { ServiceProvider, REQUEST_TIMEOUT_MS } from "./constant"; -import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; +import { ServiceProvider } from "./constant"; export function trimTopic(topic: string) { // Fix an issue where double quotes still show in the Indonesian language @@ -292,30 +291,50 @@ export function fetch( options?: Record, ): Promise { if (window.__TAURI__) { - const payload = options?.body || options?.data; - return tauriFetch(url, { - ...options, - body: - payload && - ({ - type: "Text", - payload, - } as any), - timeout: ((options?.timeout as number) || REQUEST_TIMEOUT_MS) / 1000, - responseType: - options?.responseType == "text" ? ResponseType.Text : ResponseType.JSON, - } as any); + const tauriUri = window.__TAURI__.convertFileSrc(url, "sse"); + return window.fetch(tauriUri, options).then((r) => { + // 1. create response, + // TODO using event to get status and statusText and headers + const { status, statusText } = r; + const { readable, writable } = new TransformStream(); + const res = new Response(readable, { status, statusText }); + // 2. call fetch_read_body multi times, and write to Response.body + const writer = writable.getWriter(); + let unlisten; + window.__TAURI__.event + .listen("sse-response", (e) => { + const { id, payload } = e; + console.log("event", id, payload); + writer.ready.then(() => { + if (payload !== 0) { + writer.write(new Uint8Array(payload)); + } else { + writer.releaseLock(); + writable.close(); + unlisten && unlisten(); + } + }); + }) + .then((u) => (unlisten = u)); + return res; + }); } return window.fetch(url, options); } +if (undefined !== window) { + window.tauriFetch = fetch; +} + export function adapter(config: Record) { const { baseURL, url, params, ...rest } = config; const path = baseURL ? `${baseURL}${url}` : url; const fetchUrl = params ? `${path}?${new URLSearchParams(params as any).toString()}` : path; - return fetch(fetchUrl as string, { ...rest, responseType: "text" }); + return fetch(fetchUrl as string, rest) + .then((res) => res.text()) + .then((data) => ({ data })); } export function safeLocalStorage(): { diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 47d12e119..fcc06d163 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -348,9 +348,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" dependencies = [ "serde", ] @@ -942,9 +942,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -970,9 +970,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" @@ -987,9 +987,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" @@ -1008,9 +1008,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", @@ -1019,21 +1019,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-io", @@ -1555,9 +1555,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1986,6 +1986,9 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" name = "nextchat" version = "0.1.0" dependencies = [ + "futures-util", + "percent-encoding", + "reqwest", "serde", "serde_json", "tauri", @@ -2213,6 +2216,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_info" +version = "3.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "overload" version = "0.1.1" @@ -2281,9 +2295,9 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phf" @@ -2545,9 +2559,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -3237,6 +3251,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sys-locale" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a11bd9c338fdba09f7881ab41551932ad42e405f61d01e8406baea71c07aee" +dependencies = [ + "js-sys", + "libc", + "wasm-bindgen", + "web-sys", + "windows-sys 0.45.0", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -3385,6 +3412,7 @@ dependencies = [ "objc", "once_cell", "open", + "os_info", "percent-encoding", "rand 0.8.5", "raw-window-handle", @@ -3397,6 +3425,7 @@ dependencies = [ "serde_repr", "serialize-to-javascript", "state", + "sys-locale", "tar", "tauri-macros", "tauri-runtime", @@ -3889,9 +3918,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "url" -version = "2.3.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -4316,6 +4345,15 @@ dependencies = [ "windows-targets 0.48.0", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-targets" version = "0.42.2" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 387584491..31ecfd83e 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -35,8 +35,12 @@ tauri = { version = "1.5.4", features = [ "http-all", "window-start-dragging", "window-unmaximize", "window-unminimize", + "linux-protocol-headers", ] } tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } +percent-encoding = "2.3.1" +reqwest = "0.11.18" +futures-util = "0.3.30" [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ed3ec32f3..792c656cf 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,9 +1,57 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use futures_util::{StreamExt}; +use reqwest::Client; +use tauri::{ Manager}; +use tauri::http::{ResponseBuilder}; + fn main() { tauri::Builder::default() .plugin(tauri_plugin_window_state::Builder::default().build()) + .register_uri_scheme_protocol("sse", |app_handle, request| { + let path = request.uri().strip_prefix("sse://localhost/").unwrap(); + let path = percent_encoding::percent_decode(path.as_bytes()) + .decode_utf8_lossy() + .to_string(); + // println!("path : {}", path); + let client = Client::new(); + let window = app_handle.get_window("main").unwrap(); + // send http request + let body = reqwest::Body::from(request.body().clone()); + let response_future = client.request(request.method().clone(), path) + .headers(request.headers().clone()) + .body(body).send(); + + // get response and emit to client + tauri::async_runtime::spawn(async move { + let res = response_future.await; + + match res { + Ok(res) => { + let mut stream = res.bytes_stream(); + + while let Some(chunk) = stream.next().await { + match chunk { + Ok(bytes) => { + window.emit("sse-response", bytes).unwrap(); + } + Err(err) => { + println!("Error: {:?}", err); + } + } + } + window.emit("sse-response", 0).unwrap(); + } + Err(err) => { + println!("Error: {:?}", err); + } + } + }); + ResponseBuilder::new() + .header("Access-Control-Allow-Origin", "*") + .status(200).body("OK".into()) + }) .run(tauri::generate_context!()) .expect("error while running tauri application"); } From 2d920f7ccc7bed1ed06cdb52e0ef50f96f8100ac Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Sat, 28 Sep 2024 15:05:41 +0800 Subject: [PATCH 083/352] using stream: schema to fetch in App --- app/global.d.ts | 1 + app/utils.ts | 41 +--------------- app/utils/stream.ts | 100 ++++++++++++++++++++++++++++++++++++++++ src-tauri/Cargo.lock | 36 +-------------- src-tauri/Cargo.toml | 1 + src-tauri/src/main.rs | 51 ++------------------ src-tauri/src/stream.rs | 96 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 204 insertions(+), 122 deletions(-) create mode 100644 app/utils/stream.ts create mode 100644 src-tauri/src/stream.rs diff --git a/app/global.d.ts b/app/global.d.ts index 8ee636bcd..a1453dc33 100644 --- a/app/global.d.ts +++ b/app/global.d.ts @@ -12,6 +12,7 @@ declare module "*.svg"; declare interface Window { __TAURI__?: { + convertFileSrc(url: string, protocol?: string): string; writeText(text: string): Promise; invoke(command: string, payload?: Record): Promise; dialog: { diff --git a/app/utils.ts b/app/utils.ts index 5be7bb2d9..fbe77c114 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -3,6 +3,7 @@ import { showToast } from "./components/ui-lib"; import Locale from "./locales"; import { RequestMessage } from "./client/api"; import { ServiceProvider } from "./constant"; +import { fetch } from "./utils/stream"; export function trimTopic(topic: string) { // Fix an issue where double quotes still show in the Indonesian language @@ -286,46 +287,6 @@ export function showPlugins(provider: ServiceProvider, model: string) { return false; } -export function fetch( - url: string, - options?: Record, -): Promise { - if (window.__TAURI__) { - const tauriUri = window.__TAURI__.convertFileSrc(url, "sse"); - return window.fetch(tauriUri, options).then((r) => { - // 1. create response, - // TODO using event to get status and statusText and headers - const { status, statusText } = r; - const { readable, writable } = new TransformStream(); - const res = new Response(readable, { status, statusText }); - // 2. call fetch_read_body multi times, and write to Response.body - const writer = writable.getWriter(); - let unlisten; - window.__TAURI__.event - .listen("sse-response", (e) => { - const { id, payload } = e; - console.log("event", id, payload); - writer.ready.then(() => { - if (payload !== 0) { - writer.write(new Uint8Array(payload)); - } else { - writer.releaseLock(); - writable.close(); - unlisten && unlisten(); - } - }); - }) - .then((u) => (unlisten = u)); - return res; - }); - } - return window.fetch(url, options); -} - -if (undefined !== window) { - window.tauriFetch = fetch; -} - export function adapter(config: Record) { const { baseURL, url, params, ...rest } = config; const path = baseURL ? `${baseURL}${url}` : url; diff --git a/app/utils/stream.ts b/app/utils/stream.ts new file mode 100644 index 000000000..8f9ccfbaa --- /dev/null +++ b/app/utils/stream.ts @@ -0,0 +1,100 @@ +// using tauri register_uri_scheme_protocol, register `stream:` protocol +// see src-tauri/src/stream.rs, and src-tauri/src/main.rs +// 1. window.fetch(`stream://localhost/${fetchUrl}`), get request_id +// 2. listen event: `stream-response` multi times to get response headers and body + +type ResponseEvent = { + id: number; + payload: { + request_id: number; + status?: number; + error?: string; + name?: string; + value?: string; + chunk?: number[]; + }; +}; + +export function fetch(url: string, options?: RequestInit): Promise { + if (window.__TAURI__) { + const tauriUri = window.__TAURI__.convertFileSrc(url, "stream"); + const { signal, ...rest } = options || {}; + return window + .fetch(tauriUri, rest) + .then((r) => r.text()) + .then((rid) => parseInt(rid)) + .then((request_id: number) => { + // 1. using event to get status and statusText and headers, and resolve it + let resolve: Function | undefined; + let reject: Function | undefined; + let status: number; + let writable: WritableStream | undefined; + let writer: WritableStreamDefaultWriter | undefined; + const headers = new Headers(); + let unlisten: Function | undefined; + + if (signal) { + signal.addEventListener("abort", () => { + // Reject the promise with the abort reason. + unlisten && unlisten(); + reject && reject(signal.reason); + }); + } + // @ts-ignore 2. listen response multi times, and write to Response.body + window.__TAURI__.event + .listen("stream-response", (e: ResponseEvent) => { + const { id, payload } = e; + const { + request_id: rid, + status: _status, + name, + value, + error, + chunk, + } = payload; + if (request_id != rid) { + return; + } + /** + * 1. get status code + * 2. get headers + * 3. start get body, then resolve response + * 4. get body chunk + */ + if (error) { + unlisten && unlisten(); + return reject && reject(error); + } else if (_status) { + status = _status; + } else if (name && value) { + headers.append(name, value); + } else if (chunk) { + if (resolve) { + const ts = new TransformStream(); + writable = ts.writable; + writer = writable.getWriter(); + resolve(new Response(ts.readable, { status, headers })); + resolve = undefined; + } + writer && + writer.ready.then(() => { + writer && writer.write(new Uint8Array(chunk)); + }); + } else if (_status === 0) { + // end of body + unlisten && unlisten(); + writer && + writer.ready.then(() => { + writer && writer.releaseLock(); + writable && writable.close(); + }); + } + }) + .then((u: Function) => (unlisten = u)); + return new Promise( + (_resolve, _reject) => ([resolve, reject] = [_resolve, _reject]), + ); + }); + } + return window.fetch(url, options); +} diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index fcc06d163..c9baffc0a 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1986,6 +1986,7 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" name = "nextchat" version = "0.1.0" dependencies = [ + "bytes", "futures-util", "percent-encoding", "reqwest", @@ -2216,17 +2217,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "os_info" -version = "3.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" -dependencies = [ - "log", - "serde", - "windows-sys 0.52.0", -] - [[package]] name = "overload" version = "0.1.1" @@ -3251,19 +3241,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sys-locale" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a11bd9c338fdba09f7881ab41551932ad42e405f61d01e8406baea71c07aee" -dependencies = [ - "js-sys", - "libc", - "wasm-bindgen", - "web-sys", - "windows-sys 0.45.0", -] - [[package]] name = "system-configuration" version = "0.5.1" @@ -3412,7 +3389,6 @@ dependencies = [ "objc", "once_cell", "open", - "os_info", "percent-encoding", "rand 0.8.5", "raw-window-handle", @@ -3425,7 +3401,6 @@ dependencies = [ "serde_repr", "serialize-to-javascript", "state", - "sys-locale", "tar", "tauri-macros", "tauri-runtime", @@ -4345,15 +4320,6 @@ dependencies = [ "windows-targets 0.48.0", ] -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.0", -] - [[package]] name = "windows-targets" version = "0.42.2" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 31ecfd83e..c954deb72 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -41,6 +41,7 @@ tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-works percent-encoding = "2.3.1" reqwest = "0.11.18" futures-util = "0.3.30" +bytes = "1.7.2" [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 792c656cf..e38208257 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,57 +1,14 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use futures_util::{StreamExt}; -use reqwest::Client; -use tauri::{ Manager}; -use tauri::http::{ResponseBuilder}; +mod stream; fn main() { tauri::Builder::default() .plugin(tauri_plugin_window_state::Builder::default().build()) - .register_uri_scheme_protocol("sse", |app_handle, request| { - let path = request.uri().strip_prefix("sse://localhost/").unwrap(); - let path = percent_encoding::percent_decode(path.as_bytes()) - .decode_utf8_lossy() - .to_string(); - // println!("path : {}", path); - let client = Client::new(); - let window = app_handle.get_window("main").unwrap(); - // send http request - let body = reqwest::Body::from(request.body().clone()); - let response_future = client.request(request.method().clone(), path) - .headers(request.headers().clone()) - .body(body).send(); - - // get response and emit to client - tauri::async_runtime::spawn(async move { - let res = response_future.await; - - match res { - Ok(res) => { - let mut stream = res.bytes_stream(); - - while let Some(chunk) = stream.next().await { - match chunk { - Ok(bytes) => { - window.emit("sse-response", bytes).unwrap(); - } - Err(err) => { - println!("Error: {:?}", err); - } - } - } - window.emit("sse-response", 0).unwrap(); - } - Err(err) => { - println!("Error: {:?}", err); - } - } - }); - ResponseBuilder::new() - .header("Access-Control-Allow-Origin", "*") - .status(200).body("OK".into()) - }) + .register_uri_scheme_protocol("stream", move |app_handle, request| { + stream::stream(app_handle, request) + }) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs new file mode 100644 index 000000000..5e84e0f00 --- /dev/null +++ b/src-tauri/src/stream.rs @@ -0,0 +1,96 @@ + +use std::error::Error; +use futures_util::{StreamExt}; +use reqwest::Client; +use tauri::{ Manager, AppHandle }; +use tauri::http::{Request, ResponseBuilder}; +use tauri::http::Response; + +static mut REQUEST_COUNTER: u32 = 0; + +#[derive(Clone, serde::Serialize)] +pub struct ErrorPayload { + request_id: u32, + error: String, +} + +#[derive(Clone, serde::Serialize)] +pub struct StatusPayload { + request_id: u32, + status: u16, +} + +#[derive(Clone, serde::Serialize)] +pub struct HeaderPayload { + request_id: u32, + name: String, + value: String, +} + +#[derive(Clone, serde::Serialize)] +pub struct ChunkPayload { + request_id: u32, + chunk: bytes::Bytes, +} + +pub fn stream(app_handle: &AppHandle, request: &Request) -> Result> { + let mut request_id = 0; + let event_name = "stream-response"; + unsafe { + REQUEST_COUNTER += 1; + request_id = REQUEST_COUNTER; + } + let path = request.uri().to_string().replace("stream://localhost/", "").replace("http://stream.localhost/", ""); + let path = percent_encoding::percent_decode(path.as_bytes()) + .decode_utf8_lossy() + .to_string(); + // println!("path : {}", path); + let client = Client::new(); + let handle = app_handle.app_handle(); + // send http request + let body = reqwest::Body::from(request.body().clone()); + let response_future = client.request(request.method().clone(), path) + .headers(request.headers().clone()) + .body(body).send(); + + // get response and emit to client + tauri::async_runtime::spawn(async move { + let res = response_future.await; + + match res { + Ok(res) => { + handle.emit_all(event_name, StatusPayload{ request_id, status: res.status().as_u16() }).unwrap(); + for (name, value) in res.headers() { + handle.emit_all(event_name, HeaderPayload { + request_id, + name: name.to_string(), + value: std::str::from_utf8(value.as_bytes()).unwrap().to_string() + }).unwrap(); + } + let mut stream = res.bytes_stream(); + + while let Some(chunk) = stream.next().await { + match chunk { + Ok(bytes) => { + handle.emit_all(event_name, ChunkPayload{ request_id, chunk: bytes }).unwrap(); + } + Err(err) => { + println!("Error: {:?}", err); + } + } + } + handle.emit_all(event_name, StatusPayload { request_id, status: 0 }).unwrap(); + } + Err(err) => { + println!("Error: {:?}", err.source().expect("REASON").to_string()); + handle.emit_all(event_name, ErrorPayload { + request_id, + error: err.source().expect("REASON").to_string() + }).unwrap(); + } + } + }); + return ResponseBuilder::new() + .header("Access-Control-Allow-Origin", "*") + .status(200).body(request_id.to_string().into()) +} From 5bdf41139917f815143de11a9e04b261666c7707 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Sun, 29 Sep 2024 15:51:28 +0800 Subject: [PATCH 084/352] hotfix for `x-goog-api-key` --- app/api/google.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/api/google.ts b/app/api/google.ts index 7d3f08be4..707892c33 100644 --- a/app/api/google.ts +++ b/app/api/google.ts @@ -23,7 +23,8 @@ export async function handle( }); } - const bearToken = req.headers.get("Authorization") ?? ""; + const bearToken = + req.headers.get("x-goog-api-key") || req.headers.get("Authorization") || ""; const token = bearToken.trim().replaceAll("Bearer ", "").trim(); const apiKey = token ? token : serverConfig.googleApiKey; @@ -92,7 +93,7 @@ async function request(req: NextRequest, apiKey: string) { 10 * 60 * 1000, ); const fetchUrl = `${baseUrl}${path}${ - req?.nextUrl?.searchParams?.get("alt") === "sse" ? "&alt=sse" : "" + req?.nextUrl?.searchParams?.get("alt") === "sse" ? "?alt=sse" : "" }`; console.log("[Fetch Url] ", fetchUrl); @@ -100,8 +101,8 @@ async function request(req: NextRequest, apiKey: string) { headers: { "Content-Type": "application/json", "Cache-Control": "no-store", - "x-google-api-key": - req.headers.get("x-google-api-key") || + "x-goog-api-key": + req.headers.get("x-goog-api-key") || (req.headers.get("Authorization") ?? "").replace("Bearer ", ""), }, method: req.method, From 3898c507c466c13537330fd6feb7b960e330c774 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Sun, 29 Sep 2024 19:44:09 +0800 Subject: [PATCH 085/352] using stream_fetch in App --- app/utils.ts | 8 ++- app/utils/chat.ts | 2 + app/utils/stream.ts | 154 +++++++++++++++++++--------------------- src-tauri/src/main.rs | 4 +- src-tauri/src/stream.rs | 125 ++++++++++++++++++-------------- 5 files changed, 156 insertions(+), 137 deletions(-) diff --git a/app/utils.ts b/app/utils.ts index fbe77c114..baf45abe5 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -288,12 +288,16 @@ export function showPlugins(provider: ServiceProvider, model: string) { } export function adapter(config: Record) { - const { baseURL, url, params, ...rest } = config; + const { baseURL, url, params, method, data, ...rest } = config; const path = baseURL ? `${baseURL}${url}` : url; const fetchUrl = params ? `${path}?${new URLSearchParams(params as any).toString()}` : path; - return fetch(fetchUrl as string, rest) + return fetch(fetchUrl as string, { + ...rest, + method, + body: method.toUpperCase() == "GET" ? undefined : data, + }) .then((res) => res.text()) .then((data) => ({ data })); } diff --git a/app/utils/chat.ts b/app/utils/chat.ts index 7f3bb23c5..359b2c53e 100644 --- a/app/utils/chat.ts +++ b/app/utils/chat.ts @@ -10,6 +10,7 @@ import { fetchEventSource, } from "@fortaine/fetch-event-source"; import { prettyObject } from "./format"; +import { fetch as tauriFetch } from "./stream"; export function compressImage(file: Blob, maxSize: number): Promise { return new Promise((resolve, reject) => { @@ -287,6 +288,7 @@ export function stream( REQUEST_TIMEOUT_MS, ); fetchEventSource(chatPath, { + fetch: tauriFetch, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/utils/stream.ts b/app/utils/stream.ts index 8f9ccfbaa..09b898431 100644 --- a/app/utils/stream.ts +++ b/app/utils/stream.ts @@ -1,100 +1,94 @@ -// using tauri register_uri_scheme_protocol, register `stream:` protocol +// using tauri command to send request // see src-tauri/src/stream.rs, and src-tauri/src/main.rs -// 1. window.fetch(`stream://localhost/${fetchUrl}`), get request_id -// 2. listen event: `stream-response` multi times to get response headers and body +// 1. invoke('stream_fetch', {url, method, headers, body}), get response with headers. +// 2. listen event: `stream-response` multi times to get body type ResponseEvent = { id: number; payload: { request_id: number; status?: number; - error?: string; - name?: string; - value?: string; chunk?: number[]; }; }; export function fetch(url: string, options?: RequestInit): Promise { if (window.__TAURI__) { - const tauriUri = window.__TAURI__.convertFileSrc(url, "stream"); - const { signal, ...rest } = options || {}; - return window - .fetch(tauriUri, rest) - .then((r) => r.text()) - .then((rid) => parseInt(rid)) - .then((request_id: number) => { - // 1. using event to get status and statusText and headers, and resolve it - let resolve: Function | undefined; - let reject: Function | undefined; - let status: number; - let writable: WritableStream | undefined; - let writer: WritableStreamDefaultWriter | undefined; - const headers = new Headers(); - let unlisten: Function | undefined; + const { signal, method = "GET", headers = {}, body = [] } = options || {}; + return window.__TAURI__ + .invoke("stream_fetch", { + method, + url, + headers, + // TODO FormData + body: + typeof body === "string" + ? Array.from(new TextEncoder().encode(body)) + : [], + }) + .then( + (res: { + request_id: number; + status: number; + status_text: string; + headers: Record; + }) => { + const { request_id, status, status_text: statusText, headers } = res; + console.log("send request_id", request_id, status, statusText); + let unlisten: Function | undefined; + const ts = new TransformStream(); + const writer = ts.writable.getWriter(); - if (signal) { - signal.addEventListener("abort", () => { - // Reject the promise with the abort reason. + const close = () => { unlisten && unlisten(); - reject && reject(signal.reason); - }); - } - // @ts-ignore 2. listen response multi times, and write to Response.body - window.__TAURI__.event - .listen("stream-response", (e: ResponseEvent) => { - const { id, payload } = e; - const { - request_id: rid, - status: _status, - name, - value, - error, - chunk, - } = payload; - if (request_id != rid) { - return; - } - /** - * 1. get status code - * 2. get headers - * 3. start get body, then resolve response - * 4. get body chunk - */ - if (error) { - unlisten && unlisten(); - return reject && reject(error); - } else if (_status) { - status = _status; - } else if (name && value) { - headers.append(name, value); - } else if (chunk) { - if (resolve) { - const ts = new TransformStream(); - writable = ts.writable; - writer = writable.getWriter(); - resolve(new Response(ts.readable, { status, headers })); - resolve = undefined; + writer.ready.then(() => { + try { + writer.releaseLock(); + } catch (e) { + console.error(e); } - writer && - writer.ready.then(() => { - writer && writer.write(new Uint8Array(chunk)); - }); - } else if (_status === 0) { - // end of body - unlisten && unlisten(); - writer && - writer.ready.then(() => { - writer && writer.releaseLock(); - writable && writable.close(); - }); - } - }) - .then((u: Function) => (unlisten = u)); - return new Promise( - (_resolve, _reject) => ([resolve, reject] = [_resolve, _reject]), - ); + ts.writable.close(); + }); + }; + + const response = new Response(ts.readable, { + status, + statusText, + headers, + }); + if (signal) { + signal.addEventListener("abort", () => close()); + } + // @ts-ignore 2. listen response multi times, and write to Response.body + window.__TAURI__.event + .listen("stream-response", (e: ResponseEvent) => { + const { id, payload } = e; + const { request_id: rid, chunk, status } = payload; + if (request_id != rid) { + return; + } + if (chunk) { + writer && + writer.ready.then(() => { + writer && writer.write(new Uint8Array(chunk)); + }); + } else if (status === 0) { + // end of body + close(); + } + }) + .then((u: Function) => (unlisten = u)); + return response; + }, + ) + .catch((e) => { + console.error("stream error", e); + throw e; }); } return window.fetch(url, options); } + +if (undefined !== window) { + window.tauriFetch = fetch; +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index e38208257..d04969c04 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -5,10 +5,8 @@ mod stream; fn main() { tauri::Builder::default() + .invoke_handler(tauri::generate_handler![stream::stream_fetch]) .plugin(tauri_plugin_window_state::Builder::default().build()) - .register_uri_scheme_protocol("stream", move |app_handle, request| { - stream::stream(app_handle, request) - }) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index 5e84e0f00..514e62298 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -1,30 +1,25 @@ +// +// use std::error::Error; use futures_util::{StreamExt}; use reqwest::Client; -use tauri::{ Manager, AppHandle }; -use tauri::http::{Request, ResponseBuilder}; -use tauri::http::Response; +use reqwest::header::{HeaderName, HeaderMap}; static mut REQUEST_COUNTER: u32 = 0; #[derive(Clone, serde::Serialize)] -pub struct ErrorPayload { - request_id: u32, - error: String, -} - -#[derive(Clone, serde::Serialize)] -pub struct StatusPayload { +pub struct StreamResponse { request_id: u32, status: u16, + status_text: String, + headers: HashMap } #[derive(Clone, serde::Serialize)] -pub struct HeaderPayload { +pub struct EndPayload { request_id: u32, - name: String, - value: String, + status: u16, } #[derive(Clone, serde::Serialize)] @@ -33,64 +28,90 @@ pub struct ChunkPayload { chunk: bytes::Bytes, } -pub fn stream(app_handle: &AppHandle, request: &Request) -> Result> { +use std::collections::HashMap; + +#[derive(serde::Serialize)] +pub struct CustomResponse { + message: String, + other_val: usize, +} + +#[tauri::command] +pub async fn stream_fetch( + window: tauri::Window, + method: String, + url: String, + headers: HashMap, + body: Vec, +) -> Result { + let mut request_id = 0; let event_name = "stream-response"; unsafe { REQUEST_COUNTER += 1; request_id = REQUEST_COUNTER; } - let path = request.uri().to_string().replace("stream://localhost/", "").replace("http://stream.localhost/", ""); - let path = percent_encoding::percent_decode(path.as_bytes()) - .decode_utf8_lossy() - .to_string(); - // println!("path : {}", path); - let client = Client::new(); - let handle = app_handle.app_handle(); - // send http request - let body = reqwest::Body::from(request.body().clone()); - let response_future = client.request(request.method().clone(), path) - .headers(request.headers().clone()) - .body(body).send(); - // get response and emit to client - tauri::async_runtime::spawn(async move { - let res = response_future.await; + let mut _headers = HeaderMap::new(); + for (key, value) in headers { + _headers.insert(key.parse::().unwrap(), value.parse().unwrap()); + } + let body = bytes::Bytes::from(body); - match res { - Ok(res) => { - handle.emit_all(event_name, StatusPayload{ request_id, status: res.status().as_u16() }).unwrap(); - for (name, value) in res.headers() { - handle.emit_all(event_name, HeaderPayload { - request_id, - name: name.to_string(), - value: std::str::from_utf8(value.as_bytes()).unwrap().to_string() - }).unwrap(); - } + let response_future = Client::new().request( + method.parse::().map_err(|err| format!("failed to parse method: {}", err))?, + url.parse::().map_err(|err| format!("failed to parse url: {}", err))? + ).headers(_headers).body(body).send(); + + let res = response_future.await; + let response = match res { + Ok(res) => { + println!("Error: {:?}", res); + // get response and emit to client + // .register_uri_scheme_protocol("stream", move |app_handle, request| { + let mut headers = HashMap::new(); + for (name, value) in res.headers() { + headers.insert( + name.as_str().to_string(), + std::str::from_utf8(value.as_bytes()).unwrap().to_string() + ); + } + let status = res.status().as_u16(); + + tauri::async_runtime::spawn(async move { let mut stream = res.bytes_stream(); while let Some(chunk) = stream.next().await { match chunk { Ok(bytes) => { - handle.emit_all(event_name, ChunkPayload{ request_id, chunk: bytes }).unwrap(); + println!("chunk: {:?}", bytes); + window.emit(event_name, ChunkPayload{ request_id, chunk: bytes }).unwrap(); } Err(err) => { println!("Error: {:?}", err); } } } - handle.emit_all(event_name, StatusPayload { request_id, status: 0 }).unwrap(); - } - Err(err) => { - println!("Error: {:?}", err.source().expect("REASON").to_string()); - handle.emit_all(event_name, ErrorPayload { - request_id, - error: err.source().expect("REASON").to_string() - }).unwrap(); + window.emit(event_name, EndPayload { request_id, status: 0 }).unwrap(); + }); + + StreamResponse { + request_id, + status, + status_text: "OK".to_string(), + headers, } } - }); - return ResponseBuilder::new() - .header("Access-Control-Allow-Origin", "*") - .status(200).body(request_id.to_string().into()) + Err(err) => { + println!("Error: {:?}", err.source().expect("REASON").to_string()); + StreamResponse { + request_id, + status: 599, + status_text: err.source().expect("REASON").to_string(), + headers: HashMap::new(), + } + } + }; + Ok(response) } + From 9e6ee50fa6335fe9c405b69e09bb7b6046aff42c Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Sun, 29 Sep 2024 20:32:36 +0800 Subject: [PATCH 086/352] using stream_fetch in App --- app/utils/stream.ts | 110 +++++++++++++++++++--------------------- src-tauri/src/stream.rs | 4 +- 2 files changed, 52 insertions(+), 62 deletions(-) diff --git a/app/utils/stream.ts b/app/utils/stream.ts index 09b898431..f8c272e42 100644 --- a/app/utils/stream.ts +++ b/app/utils/stream.ts @@ -12,9 +12,55 @@ type ResponseEvent = { }; }; +type StreamResponse = { + request_id: number; + status: number; + status_text: string; + headers: Record; +}; + export function fetch(url: string, options?: RequestInit): Promise { if (window.__TAURI__) { const { signal, method = "GET", headers = {}, body = [] } = options || {}; + let unlisten: Function | undefined; + let request_id = 0; + const ts = new TransformStream(); + const writer = ts.writable.getWriter(); + + const close = () => { + unlisten && unlisten(); + writer.ready.then(() => { + try { + writer.releaseLock(); + } catch (e) { + console.error(e); + } + ts.writable.close(); + }); + }; + + if (signal) { + signal.addEventListener("abort", () => close()); + } + // @ts-ignore 2. listen response multi times, and write to Response.body + window.__TAURI__.event + .listen("stream-response", (e: ResponseEvent) => { + const { request_id: rid, chunk, status } = e?.payload || {}; + if (request_id != rid) { + return; + } + if (chunk) { + writer && + writer.ready.then(() => { + writer && writer.write(new Uint8Array(chunk)); + }); + } else if (status === 0) { + // end of body + close(); + } + }) + .then((u: Function) => (unlisten = u)); + return window.__TAURI__ .invoke("stream_fetch", { method, @@ -26,61 +72,11 @@ export function fetch(url: string, options?: RequestInit): Promise { ? Array.from(new TextEncoder().encode(body)) : [], }) - .then( - (res: { - request_id: number; - status: number; - status_text: string; - headers: Record; - }) => { - const { request_id, status, status_text: statusText, headers } = res; - console.log("send request_id", request_id, status, statusText); - let unlisten: Function | undefined; - const ts = new TransformStream(); - const writer = ts.writable.getWriter(); - - const close = () => { - unlisten && unlisten(); - writer.ready.then(() => { - try { - writer.releaseLock(); - } catch (e) { - console.error(e); - } - ts.writable.close(); - }); - }; - - const response = new Response(ts.readable, { - status, - statusText, - headers, - }); - if (signal) { - signal.addEventListener("abort", () => close()); - } - // @ts-ignore 2. listen response multi times, and write to Response.body - window.__TAURI__.event - .listen("stream-response", (e: ResponseEvent) => { - const { id, payload } = e; - const { request_id: rid, chunk, status } = payload; - if (request_id != rid) { - return; - } - if (chunk) { - writer && - writer.ready.then(() => { - writer && writer.write(new Uint8Array(chunk)); - }); - } else if (status === 0) { - // end of body - close(); - } - }) - .then((u: Function) => (unlisten = u)); - return response; - }, - ) + .then((res: StreamResponse) => { + request_id = res.request_id; + const { status, status_text: statusText, headers } = res; + return new Response(ts.readable, { status, statusText, headers }); + }) .catch((e) => { console.error("stream error", e); throw e; @@ -88,7 +84,3 @@ export function fetch(url: string, options?: RequestInit): Promise { } return window.fetch(url, options); } - -if (undefined !== window) { - window.tauriFetch = fetch; -} diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index 514e62298..81710c733 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -66,9 +66,7 @@ pub async fn stream_fetch( let res = response_future.await; let response = match res { Ok(res) => { - println!("Error: {:?}", res); // get response and emit to client - // .register_uri_scheme_protocol("stream", move |app_handle, request| { let mut headers = HashMap::new(); for (name, value) in res.headers() { headers.insert( @@ -84,7 +82,7 @@ pub async fn stream_fetch( while let Some(chunk) = stream.next().await { match chunk { Ok(bytes) => { - println!("chunk: {:?}", bytes); + // println!("chunk: {:?}", bytes); window.emit(event_name, ChunkPayload{ request_id, chunk: bytes }).unwrap(); } Err(err) => { From f9d410517030259434c26f7443bacdc32568682b Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Sun, 29 Sep 2024 21:47:38 +0800 Subject: [PATCH 087/352] stash code --- app/utils/stream.ts | 22 ++++++++++++++++++++-- src-tauri/src/stream.rs | 30 ++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/app/utils/stream.ts b/app/utils/stream.ts index f8c272e42..dd665e71c 100644 --- a/app/utils/stream.ts +++ b/app/utils/stream.ts @@ -21,7 +21,12 @@ type StreamResponse = { export function fetch(url: string, options?: RequestInit): Promise { if (window.__TAURI__) { - const { signal, method = "GET", headers = {}, body = [] } = options || {}; + const { + signal, + method = "GET", + headers: _headers = {}, + body = [], + } = options || {}; let unlisten: Function | undefined; let request_id = 0; const ts = new TransformStream(); @@ -32,10 +37,10 @@ export function fetch(url: string, options?: RequestInit): Promise { writer.ready.then(() => { try { writer.releaseLock(); + ts.writable.close(); } catch (e) { console.error(e); } - ts.writable.close(); }); }; @@ -61,6 +66,19 @@ export function fetch(url: string, options?: RequestInit): Promise { }) .then((u: Function) => (unlisten = u)); + const headers = { + Accept: "*", + Connection: "close", + Origin: "http://localhost:3000", + Referer: "http://localhost:3000/", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "cross-site", + "User-Agent": navigator.userAgent, + }; + for (const item of new Headers(_headers || {})) { + headers[item[0]] = item[1]; + } return window.__TAURI__ .invoke("stream_fetch", { method, diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index 81710c733..97989ba7e 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -53,15 +53,33 @@ pub async fn stream_fetch( } let mut _headers = HeaderMap::new(); - for (key, value) in headers { - _headers.insert(key.parse::().unwrap(), value.parse().unwrap()); + for (key, value) in &headers { + _headers.insert(key.parse::().unwrap(), value.parse().unwrap()); } - let body = bytes::Bytes::from(body); - let response_future = Client::new().request( - method.parse::().map_err(|err| format!("failed to parse method: {}", err))?, + println!("method: {:?}", method); + println!("url: {:?}", url); + println!("headers: {:?}", headers); + println!("headers: {:?}", _headers); + + let method = method.parse::().map_err(|err| format!("failed to parse method: {}", err))?; + let client = Client::builder() + .user_agent("Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15") + .default_headers(_headers) + .build() + .map_err(|err| format!("failed to generate client: {}", err))?; + + let mut request = client.request( + method.clone(), url.parse::().map_err(|err| format!("failed to parse url: {}", err))? - ).headers(_headers).body(body).send(); + ); + + if method == reqwest::Method::POST { + let body = bytes::Bytes::from(body); + println!("body: {:?}", body); + request = request.body(body); + } + let response_future = request.send(); let res = response_future.await; let response = match res { From f5ad51a35e5b21122ca40b7dd6723bff33d51edb Mon Sep 17 00:00:00 2001 From: code-october <148516338+code-october@users.noreply.github.com> Date: Sun, 29 Sep 2024 14:29:42 +0000 Subject: [PATCH 088/352] fix quoteEnd extract regex --- app/components/markdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 6e340d065..7d27f1d60 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -252,7 +252,7 @@ function tryWrapHtmlCode(text: string) { }, ) .replace( - /(<\/body>)([\r\n\s]*?)(<\/html>)([\n\r]*?)([`]*?)([\n\r]*?)/g, + /(<\/body>)([\r\n\s]*?)(<\/html>)([\n\r]*)([`]*)([\n\r]*?)/g, (match, bodyEnd, space, htmlEnd, newLine, quoteEnd) => { return !quoteEnd ? bodyEnd + space + htmlEnd + "\n```\n" : match; }, From b5f6e5a5989a36ecb1b5f2b37209f2d4bffc607b Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 00:38:30 +0800 Subject: [PATCH 089/352] update --- app/store/plugin.ts | 2 +- app/utils/stream.ts | 28 ++++++++++------------------ src-tauri/src/stream.rs | 8 +++++--- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/app/store/plugin.ts b/app/store/plugin.ts index 40abdc8d9..b3d9f6d8c 100644 --- a/app/store/plugin.ts +++ b/app/store/plugin.ts @@ -7,7 +7,7 @@ import yaml from "js-yaml"; import { adapter, getOperationId } from "../utils"; import { useAccessStore } from "./access"; -const isApp = getClientConfig()?.isApp; +const isApp = getClientConfig()?.isApp !== false; export type Plugin = { id: string; diff --git a/app/utils/stream.ts b/app/utils/stream.ts index dd665e71c..625ecea1f 100644 --- a/app/utils/stream.ts +++ b/app/utils/stream.ts @@ -32,15 +32,13 @@ export function fetch(url: string, options?: RequestInit): Promise { const ts = new TransformStream(); const writer = ts.writable.getWriter(); + let closed = false; const close = () => { + if (closed) return; + closed = true; unlisten && unlisten(); writer.ready.then(() => { - try { - writer.releaseLock(); - ts.writable.close(); - } catch (e) { - console.error(e); - } + writer.close().catch((e) => console.error(e)); }); }; @@ -55,10 +53,9 @@ export function fetch(url: string, options?: RequestInit): Promise { return; } if (chunk) { - writer && - writer.ready.then(() => { - writer && writer.write(new Uint8Array(chunk)); - }); + writer.ready.then(() => { + writer.write(new Uint8Array(chunk)); + }); } else if (status === 0) { // end of body close(); @@ -67,13 +64,8 @@ export function fetch(url: string, options?: RequestInit): Promise { .then((u: Function) => (unlisten = u)); const headers = { - Accept: "*", - Connection: "close", - Origin: "http://localhost:3000", - Referer: "http://localhost:3000/", - "Sec-Fetch-Dest": "empty", - "Sec-Fetch-Mode": "cors", - "Sec-Fetch-Site": "cross-site", + Accept: "application/json, text/plain, */*", + "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", "User-Agent": navigator.userAgent, }; for (const item of new Headers(_headers || {})) { @@ -81,7 +73,7 @@ export function fetch(url: string, options?: RequestInit): Promise { } return window.__TAURI__ .invoke("stream_fetch", { - method, + method: method.toUpperCase(), url, headers, // TODO FormData diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index 97989ba7e..a35e2a001 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -8,7 +8,7 @@ use reqwest::header::{HeaderName, HeaderMap}; static mut REQUEST_COUNTER: u32 = 0; -#[derive(Clone, serde::Serialize)] +#[derive(Debug, Clone, serde::Serialize)] pub struct StreamResponse { request_id: u32, status: u16, @@ -66,6 +66,7 @@ pub async fn stream_fetch( let client = Client::builder() .user_agent("Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15") .default_headers(_headers) + .redirect(reqwest::redirect::Policy::limited(3)) .build() .map_err(|err| format!("failed to generate client: {}", err))?; @@ -104,7 +105,7 @@ pub async fn stream_fetch( window.emit(event_name, ChunkPayload{ request_id, chunk: bytes }).unwrap(); } Err(err) => { - println!("Error: {:?}", err); + println!("Error chunk: {:?}", err); } } } @@ -119,7 +120,7 @@ pub async fn stream_fetch( } } Err(err) => { - println!("Error: {:?}", err.source().expect("REASON").to_string()); + println!("Error response: {:?}", err.source().expect("REASON").to_string()); StreamResponse { request_id, status: 599, @@ -128,6 +129,7 @@ pub async fn stream_fetch( } } }; + println!("Response: {:?}", response); Ok(response) } From 5141145e4df26bbb732e2536bdb6ac2739f2a6bd Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 00:58:50 +0800 Subject: [PATCH 090/352] revert plugin runtime using tarui/api/http, not using fetch_stream --- app/utils.ts | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/app/utils.ts b/app/utils.ts index 315695163..6b2f65952 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -2,8 +2,8 @@ import { useEffect, useState } from "react"; import { showToast } from "./components/ui-lib"; import Locale from "./locales"; import { RequestMessage } from "./client/api"; -import { ServiceProvider } from "./constant"; -import { fetch } from "./utils/stream"; +import { ServiceProvider, REQUEST_TIMEOUT_MS } from "./constant"; +import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; export function trimTopic(topic: string) { // Fix an issue where double quotes still show in the Indonesian language @@ -287,19 +287,35 @@ export function showPlugins(provider: ServiceProvider, model: string) { return false; } +export function fetch( + url: string, + options?: Record, +): Promise { + if (window.__TAURI__) { + const payload = options?.body || options?.data; + return tauriFetch(url, { + ...options, + body: + payload && + ({ + type: "Text", + payload, + } as any), + timeout: ((options?.timeout as number) || REQUEST_TIMEOUT_MS) / 1000, + responseType: + options?.responseType == "text" ? ResponseType.Text : ResponseType.JSON, + } as any); + } + return window.fetch(url, options); +} + export function adapter(config: Record) { - const { baseURL, url, params, method, data, ...rest } = config; + const { baseURL, url, params, ...rest } = config; const path = baseURL ? `${baseURL}${url}` : url; const fetchUrl = params ? `${path}?${new URLSearchParams(params as any).toString()}` : path; - return fetch(fetchUrl as string, { - ...rest, - method, - body: method.toUpperCase() == "GET" ? undefined : data, - }) - .then((res) => res.text()) - .then((data) => ({ data })); + return fetch(fetchUrl as string, { ...rest, responseType: "text" }); } export function safeLocalStorage(): { From a50c282d01c24a66c603d57ce47c978f725f8c7d Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 01:19:20 +0800 Subject: [PATCH 091/352] remove DEFAULT_API_HOST --- app/client/platforms/alibaba.ts | 2 + app/client/platforms/anthropic.ts | 6 +-- app/client/platforms/baidu.ts | 2 + app/client/platforms/bytedance.ts | 2 + app/client/platforms/google.ts | 6 ++- app/client/platforms/iflytek.ts | 6 ++- app/client/platforms/moonshot.ts | 4 +- app/client/platforms/openai.ts | 4 +- app/client/platforms/tencent.ts | 8 ++-- app/constant.ts | 1 - app/store/access.ts | 72 +++++++++---------------------- app/store/sync.ts | 3 +- app/utils/cors.ts | 19 -------- 13 files changed, 45 insertions(+), 90 deletions(-) delete mode 100644 app/utils/cors.ts diff --git a/app/client/platforms/alibaba.ts b/app/client/platforms/alibaba.ts index 4ade9ebb9..727e3aebf 100644 --- a/app/client/platforms/alibaba.ts +++ b/app/client/platforms/alibaba.ts @@ -23,6 +23,7 @@ import { import { prettyObject } from "@/app/utils/format"; import { getClientConfig } from "@/app/config/client"; import { getMessageTextContent } from "@/app/utils"; +import { fetch } from "@/app/utils/stream"; export interface OpenAIListModelResponse { object: string; @@ -178,6 +179,7 @@ export class QwenApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { + fetch, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/client/platforms/anthropic.ts b/app/client/platforms/anthropic.ts index 7826838a6..1a83bd53a 100644 --- a/app/client/platforms/anthropic.ts +++ b/app/client/platforms/anthropic.ts @@ -8,7 +8,7 @@ import { ChatMessageTool, } from "@/app/store"; import { getClientConfig } from "@/app/config/client"; -import { DEFAULT_API_HOST } from "@/app/constant"; +import { ANTHROPIC_BASE_URL } from "@/app/constant"; import { getMessageTextContent, isVisionModel } from "@/app/utils"; import { preProcessImageContent, stream } from "@/app/utils/chat"; import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare"; @@ -388,9 +388,7 @@ export class ClaudeApi implements LLMApi { if (baseUrl.trim().length === 0) { const isApp = !!getClientConfig()?.isApp; - baseUrl = isApp - ? DEFAULT_API_HOST + "/api/proxy/anthropic" - : ApiPath.Anthropic; + baseUrl = isApp ? ANTHROPIC_BASE_URL : ApiPath.Anthropic; } if (!baseUrl.startsWith("http") && !baseUrl.startsWith("/api")) { diff --git a/app/client/platforms/baidu.ts b/app/client/platforms/baidu.ts index c360417c6..4f3294d5e 100644 --- a/app/client/platforms/baidu.ts +++ b/app/client/platforms/baidu.ts @@ -24,6 +24,7 @@ import { import { prettyObject } from "@/app/utils/format"; import { getClientConfig } from "@/app/config/client"; import { getMessageTextContent } from "@/app/utils"; +import { fetch } from "@/app/utils/stream"; export interface OpenAIListModelResponse { object: string; @@ -197,6 +198,7 @@ export class ErnieApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { + fetch, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/client/platforms/bytedance.ts b/app/client/platforms/bytedance.ts index a6e2d426e..279be815f 100644 --- a/app/client/platforms/bytedance.ts +++ b/app/client/platforms/bytedance.ts @@ -23,6 +23,7 @@ import { import { prettyObject } from "@/app/utils/format"; import { getClientConfig } from "@/app/config/client"; import { getMessageTextContent } from "@/app/utils"; +import { fetch } from "@/app/utils/stream"; export interface OpenAIListModelResponse { object: string; @@ -165,6 +166,7 @@ export class DoubaoApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { + fetch, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index 3c2607271..7a74dd4f3 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -9,7 +9,7 @@ import { } from "../api"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { getClientConfig } from "@/app/config/client"; -import { DEFAULT_API_HOST } from "@/app/constant"; +import { GEMINI_BASE_URL } from "@/app/constant"; import Locale from "../../locales"; import { EventStreamContentType, @@ -22,6 +22,7 @@ import { isVisionModel, } from "@/app/utils"; import { preProcessImageContent } from "@/app/utils/chat"; +import { fetch } from "@/app/utils/stream"; export class GeminiProApi implements LLMApi { path(path: string): string { @@ -34,7 +35,7 @@ export class GeminiProApi implements LLMApi { const isApp = !!getClientConfig()?.isApp; if (baseUrl.length === 0) { - baseUrl = isApp ? DEFAULT_API_HOST + `/api/proxy/google` : ApiPath.Google; + baseUrl = isApp ? GEMINI_BASE_URL : ApiPath.Google; } if (baseUrl.endsWith("/")) { baseUrl = baseUrl.slice(0, baseUrl.length - 1); @@ -213,6 +214,7 @@ export class GeminiProApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { + fetch, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/client/platforms/iflytek.ts b/app/client/platforms/iflytek.ts index 3931672e6..07bfeda58 100644 --- a/app/client/platforms/iflytek.ts +++ b/app/client/platforms/iflytek.ts @@ -1,7 +1,7 @@ "use client"; import { ApiPath, - DEFAULT_API_HOST, + IFLYTEK_BASE_URL, Iflytek, REQUEST_TIMEOUT_MS, } from "@/app/constant"; @@ -22,6 +22,7 @@ import { import { prettyObject } from "@/app/utils/format"; import { getClientConfig } from "@/app/config/client"; import { getMessageTextContent } from "@/app/utils"; +import { fetch } from "@/app/utils/stream"; import { RequestPayload } from "./openai"; @@ -40,7 +41,7 @@ export class SparkApi implements LLMApi { if (baseUrl.length === 0) { const isApp = !!getClientConfig()?.isApp; const apiPath = ApiPath.Iflytek; - baseUrl = isApp ? DEFAULT_API_HOST + "/proxy" + apiPath : apiPath; + baseUrl = isApp ? IFLYTEK_BASE_URL + apiPath : apiPath; } if (baseUrl.endsWith("/")) { @@ -149,6 +150,7 @@ export class SparkApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { + fetch, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/client/platforms/moonshot.ts b/app/client/platforms/moonshot.ts index 6b1979745..4570dca97 100644 --- a/app/client/platforms/moonshot.ts +++ b/app/client/platforms/moonshot.ts @@ -2,7 +2,7 @@ // azure and openai, using same models. so using same LLMApi. import { ApiPath, - DEFAULT_API_HOST, + MOONSHOT_BASE_URL, Moonshot, REQUEST_TIMEOUT_MS, } from "@/app/constant"; @@ -40,7 +40,7 @@ export class MoonshotApi implements LLMApi { if (baseUrl.length === 0) { const isApp = !!getClientConfig()?.isApp; const apiPath = ApiPath.Moonshot; - baseUrl = isApp ? DEFAULT_API_HOST + "/proxy" + apiPath : apiPath; + baseUrl = isApp ? MOONSHOT_BASE_URL + apiPath : apiPath; } if (baseUrl.endsWith("/")) { diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 0a8d6203a..0b2d91c99 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -2,7 +2,7 @@ // azure and openai, using same models. so using same LLMApi. import { ApiPath, - DEFAULT_API_HOST, + OPENAI_BASE_URL, DEFAULT_MODELS, OpenaiPath, Azure, @@ -98,7 +98,7 @@ export class ChatGPTApi implements LLMApi { if (baseUrl.length === 0) { const isApp = !!getClientConfig()?.isApp; const apiPath = isAzure ? ApiPath.Azure : ApiPath.OpenAI; - baseUrl = isApp ? DEFAULT_API_HOST + "/proxy" + apiPath : apiPath; + baseUrl = isApp ? OPENAI_BASE_URL + apiPath : apiPath; } if (baseUrl.endsWith("/")) { diff --git a/app/client/platforms/tencent.ts b/app/client/platforms/tencent.ts index 3e8f1a459..28f5b56f2 100644 --- a/app/client/platforms/tencent.ts +++ b/app/client/platforms/tencent.ts @@ -1,5 +1,5 @@ "use client"; -import { ApiPath, DEFAULT_API_HOST, REQUEST_TIMEOUT_MS } from "@/app/constant"; +import { ApiPath, TENCENT_BASE_URL, REQUEST_TIMEOUT_MS } from "@/app/constant"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { @@ -22,6 +22,7 @@ import mapKeys from "lodash-es/mapKeys"; import mapValues from "lodash-es/mapValues"; import isArray from "lodash-es/isArray"; import isObject from "lodash-es/isObject"; +import { fetch } from "@/app/utils/stream"; export interface OpenAIListModelResponse { object: string; @@ -70,9 +71,7 @@ export class HunyuanApi implements LLMApi { if (baseUrl.length === 0) { const isApp = !!getClientConfig()?.isApp; - baseUrl = isApp - ? DEFAULT_API_HOST + "/api/proxy/tencent" - : ApiPath.Tencent; + baseUrl = isApp ? TENCENT_BASE_URL : ApiPath.Tencent; } if (baseUrl.endsWith("/")) { @@ -179,6 +178,7 @@ export class HunyuanApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { + fetch, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/constant.ts b/app/constant.ts index a54a973da..a06b8f050 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -11,7 +11,6 @@ export const RUNTIME_CONFIG_DOM = "danger-runtime-config"; export const STABILITY_BASE_URL = "https://api.stability.ai"; -export const DEFAULT_API_HOST = "https://api.nextchat.dev"; export const OPENAI_BASE_URL = "https://api.openai.com"; export const ANTHROPIC_BASE_URL = "https://api.anthropic.com"; diff --git a/app/store/access.ts b/app/store/access.ts index 9fcd227e7..74050733c 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -1,9 +1,17 @@ import { - ApiPath, - DEFAULT_API_HOST, GoogleSafetySettingsThreshold, ServiceProvider, StoreKey, + OPENAI_BASE_URL, + ANTHROPIC_BASE_URL, + GEMINI_BASE_URL, + BAIDU_BASE_URL, + BYTEDANCE_BASE_URL, + ALIBABA_BASE_URL, + TENCENT_BASE_URL, + MOONSHOT_BASE_URL, + STABILITY_BASE_URL, + IFLYTEK_BASE_URL, } from "../constant"; import { getHeaders } from "../client/api"; import { getClientConfig } from "../config/client"; @@ -15,46 +23,6 @@ let fetchState = 0; // 0 not fetch, 1 fetching, 2 done const isApp = getClientConfig()?.buildMode === "export"; -const DEFAULT_OPENAI_URL = isApp - ? DEFAULT_API_HOST + "/api/proxy/openai" - : ApiPath.OpenAI; - -const DEFAULT_GOOGLE_URL = isApp - ? DEFAULT_API_HOST + "/api/proxy/google" - : ApiPath.Google; - -const DEFAULT_ANTHROPIC_URL = isApp - ? DEFAULT_API_HOST + "/api/proxy/anthropic" - : ApiPath.Anthropic; - -const DEFAULT_BAIDU_URL = isApp - ? DEFAULT_API_HOST + "/api/proxy/baidu" - : ApiPath.Baidu; - -const DEFAULT_BYTEDANCE_URL = isApp - ? DEFAULT_API_HOST + "/api/proxy/bytedance" - : ApiPath.ByteDance; - -const DEFAULT_ALIBABA_URL = isApp - ? DEFAULT_API_HOST + "/api/proxy/alibaba" - : ApiPath.Alibaba; - -const DEFAULT_TENCENT_URL = isApp - ? DEFAULT_API_HOST + "/api/proxy/tencent" - : ApiPath.Tencent; - -const DEFAULT_MOONSHOT_URL = isApp - ? DEFAULT_API_HOST + "/api/proxy/moonshot" - : ApiPath.Moonshot; - -const DEFAULT_STABILITY_URL = isApp - ? DEFAULT_API_HOST + "/api/proxy/stability" - : ApiPath.Stability; - -const DEFAULT_IFLYTEK_URL = isApp - ? DEFAULT_API_HOST + "/api/proxy/iflytek" - : ApiPath.Iflytek; - const DEFAULT_ACCESS_STATE = { accessCode: "", useCustomConfig: false, @@ -62,7 +30,7 @@ const DEFAULT_ACCESS_STATE = { provider: ServiceProvider.OpenAI, // openai - openaiUrl: DEFAULT_OPENAI_URL, + openaiUrl: OPENAI_BASE_URL, openaiApiKey: "", // azure @@ -71,44 +39,44 @@ const DEFAULT_ACCESS_STATE = { azureApiVersion: "2023-08-01-preview", // google ai studio - googleUrl: DEFAULT_GOOGLE_URL, + googleUrl: GEMINI_BASE_URL, googleApiKey: "", googleApiVersion: "v1", googleSafetySettings: GoogleSafetySettingsThreshold.BLOCK_ONLY_HIGH, // anthropic - anthropicUrl: DEFAULT_ANTHROPIC_URL, + anthropicUrl: ANTHROPIC_BASE_URL, anthropicApiKey: "", anthropicApiVersion: "2023-06-01", // baidu - baiduUrl: DEFAULT_BAIDU_URL, + baiduUrl: BAIDU_BASE_URL, baiduApiKey: "", baiduSecretKey: "", // bytedance - bytedanceUrl: DEFAULT_BYTEDANCE_URL, + bytedanceUrl: BYTEDANCE_BASE_URL, bytedanceApiKey: "", // alibaba - alibabaUrl: DEFAULT_ALIBABA_URL, + alibabaUrl: ALIBABA_BASE_URL, alibabaApiKey: "", // moonshot - moonshotUrl: DEFAULT_MOONSHOT_URL, + moonshotUrl: MOONSHOT_BASE_URL, moonshotApiKey: "", //stability - stabilityUrl: DEFAULT_STABILITY_URL, + stabilityUrl: STABILITY_BASE_URL, stabilityApiKey: "", // tencent - tencentUrl: DEFAULT_TENCENT_URL, + tencentUrl: TENCENT_BASE_URL, tencentSecretKey: "", tencentSecretId: "", // iflytek - iflytekUrl: DEFAULT_IFLYTEK_URL, + iflytekUrl: IFLYTEK_BASE_URL, iflytekApiKey: "", iflytekApiSecret: "", diff --git a/app/store/sync.ts b/app/store/sync.ts index 9db60d5f4..c53a7a82a 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -12,7 +12,6 @@ import { downloadAs, readFromFile } from "../utils"; import { showToast } from "../components/ui-lib"; import Locale from "../locales"; import { createSyncClient, ProviderType } from "../utils/cloud"; -import { corsPath } from "../utils/cors"; export interface WebDavConfig { server: string; @@ -26,7 +25,7 @@ export type SyncStore = GetStoreState; const DEFAULT_SYNC_STATE = { provider: ProviderType.WebDAV, useProxy: true, - proxyUrl: corsPath(ApiPath.Cors), + proxyUrl: ApiPath.Cors, webdav: { endpoint: "", diff --git a/app/utils/cors.ts b/app/utils/cors.ts deleted file mode 100644 index f5e5ce6f0..000000000 --- a/app/utils/cors.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { getClientConfig } from "../config/client"; -import { DEFAULT_API_HOST } from "../constant"; - -export function corsPath(path: string) { - const baseUrl = getClientConfig()?.isApp ? `${DEFAULT_API_HOST}` : ""; - - if (baseUrl === "" && path === "") { - return ""; - } - if (!path.startsWith("/")) { - path = "/" + path; - } - - if (!path.endsWith("/")) { - path += "/"; - } - - return `${baseUrl}${path}`; -} From 9be58f3eb4217b00073a954262ac4e5b970806f3 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 01:30:20 +0800 Subject: [PATCH 092/352] fix ts error --- app/client/platforms/alibaba.ts | 2 +- app/client/platforms/baidu.ts | 2 +- app/client/platforms/bytedance.ts | 2 +- app/client/platforms/google.ts | 2 +- app/client/platforms/iflytek.ts | 2 +- app/client/platforms/tencent.ts | 2 +- app/store/sync.ts | 2 +- app/utils/chat.ts | 2 +- app/utils/stream.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/client/platforms/alibaba.ts b/app/client/platforms/alibaba.ts index 727e3aebf..86229a147 100644 --- a/app/client/platforms/alibaba.ts +++ b/app/client/platforms/alibaba.ts @@ -179,7 +179,7 @@ export class QwenApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { - fetch, + fetch: fetch as any, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/client/platforms/baidu.ts b/app/client/platforms/baidu.ts index 4f3294d5e..2511a696b 100644 --- a/app/client/platforms/baidu.ts +++ b/app/client/platforms/baidu.ts @@ -198,7 +198,7 @@ export class ErnieApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { - fetch, + fetch: fetch as any, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/client/platforms/bytedance.ts b/app/client/platforms/bytedance.ts index 279be815f..000a9e278 100644 --- a/app/client/platforms/bytedance.ts +++ b/app/client/platforms/bytedance.ts @@ -166,7 +166,7 @@ export class DoubaoApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { - fetch, + fetch: fetch as any, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index 7a74dd4f3..dcf300a0f 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -214,7 +214,7 @@ export class GeminiProApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { - fetch, + fetch: fetch as any, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/client/platforms/iflytek.ts b/app/client/platforms/iflytek.ts index 07bfeda58..de638829e 100644 --- a/app/client/platforms/iflytek.ts +++ b/app/client/platforms/iflytek.ts @@ -150,7 +150,7 @@ export class SparkApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { - fetch, + fetch: fetch as any, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/client/platforms/tencent.ts b/app/client/platforms/tencent.ts index 28f5b56f2..3610fac0a 100644 --- a/app/client/platforms/tencent.ts +++ b/app/client/platforms/tencent.ts @@ -178,7 +178,7 @@ export class HunyuanApi implements LLMApi { controller.signal.onabort = finish; fetchEventSource(chatPath, { - fetch, + fetch: fetch as any, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/store/sync.ts b/app/store/sync.ts index c53a7a82a..8477c1e4b 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -25,7 +25,7 @@ export type SyncStore = GetStoreState; const DEFAULT_SYNC_STATE = { provider: ProviderType.WebDAV, useProxy: true, - proxyUrl: ApiPath.Cors, + proxyUrl: ApiPath.Cors as string, webdav: { endpoint: "", diff --git a/app/utils/chat.ts b/app/utils/chat.ts index 359b2c53e..254cef401 100644 --- a/app/utils/chat.ts +++ b/app/utils/chat.ts @@ -288,7 +288,7 @@ export function stream( REQUEST_TIMEOUT_MS, ); fetchEventSource(chatPath, { - fetch: tauriFetch, + fetch: tauriFetch as any, ...chatPayload, async onopen(res) { clearTimeout(requestTimeoutId); diff --git a/app/utils/stream.ts b/app/utils/stream.ts index 625ecea1f..e8850fcba 100644 --- a/app/utils/stream.ts +++ b/app/utils/stream.ts @@ -63,7 +63,7 @@ export function fetch(url: string, options?: RequestInit): Promise { }) .then((u: Function) => (unlisten = u)); - const headers = { + const headers: Record = { Accept: "application/json, text/plain, */*", "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", "User-Agent": navigator.userAgent, From 3c01738c29c64c215f0d118a2db0e65c3f51f4de Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 01:37:16 +0800 Subject: [PATCH 093/352] update --- src-tauri/src/stream.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index a35e2a001..9aae3d164 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -57,14 +57,13 @@ pub async fn stream_fetch( _headers.insert(key.parse::().unwrap(), value.parse().unwrap()); } - println!("method: {:?}", method); - println!("url: {:?}", url); - println!("headers: {:?}", headers); - println!("headers: {:?}", _headers); + // println!("method: {:?}", method); + // println!("url: {:?}", url); + // println!("headers: {:?}", headers); + // println!("headers: {:?}", _headers); let method = method.parse::().map_err(|err| format!("failed to parse method: {}", err))?; let client = Client::builder() - .user_agent("Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15") .default_headers(_headers) .redirect(reqwest::redirect::Policy::limited(3)) .build() @@ -77,7 +76,7 @@ pub async fn stream_fetch( if method == reqwest::Method::POST { let body = bytes::Bytes::from(body); - println!("body: {:?}", body); + // println!("body: {:?}", body); request = request.body(body); } let response_future = request.send(); @@ -129,7 +128,7 @@ pub async fn stream_fetch( } } }; - println!("Response: {:?}", response); + // println!("Response: {:?}", response); Ok(response) } From b174a40634d7c2ab809a2ed89ff6fa2fbbe1beb1 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 01:44:27 +0800 Subject: [PATCH 094/352] update --- app/client/platforms/iflytek.ts | 2 +- app/client/platforms/moonshot.ts | 2 +- app/client/platforms/openai.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/client/platforms/iflytek.ts b/app/client/platforms/iflytek.ts index de638829e..55a39d0cc 100644 --- a/app/client/platforms/iflytek.ts +++ b/app/client/platforms/iflytek.ts @@ -41,7 +41,7 @@ export class SparkApi implements LLMApi { if (baseUrl.length === 0) { const isApp = !!getClientConfig()?.isApp; const apiPath = ApiPath.Iflytek; - baseUrl = isApp ? IFLYTEK_BASE_URL + apiPath : apiPath; + baseUrl = isApp ? IFLYTEK_BASE_URL : apiPath; } if (baseUrl.endsWith("/")) { diff --git a/app/client/platforms/moonshot.ts b/app/client/platforms/moonshot.ts index 4570dca97..e0ef3494f 100644 --- a/app/client/platforms/moonshot.ts +++ b/app/client/platforms/moonshot.ts @@ -40,7 +40,7 @@ export class MoonshotApi implements LLMApi { if (baseUrl.length === 0) { const isApp = !!getClientConfig()?.isApp; const apiPath = ApiPath.Moonshot; - baseUrl = isApp ? MOONSHOT_BASE_URL + apiPath : apiPath; + baseUrl = isApp ? MOONSHOT_BASE_URL : apiPath; } if (baseUrl.endsWith("/")) { diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 0b2d91c99..a22633611 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -98,7 +98,7 @@ export class ChatGPTApi implements LLMApi { if (baseUrl.length === 0) { const isApp = !!getClientConfig()?.isApp; const apiPath = isAzure ? ApiPath.Azure : ApiPath.OpenAI; - baseUrl = isApp ? OPENAI_BASE_URL + apiPath : apiPath; + baseUrl = isApp ? OPENAI_BASE_URL : apiPath; } if (baseUrl.endsWith("/")) { From af49ed4fdcd81b05c6bc0f11a35af346180134f8 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 01:51:14 +0800 Subject: [PATCH 095/352] update --- app/global.d.ts | 1 - src-tauri/src/stream.rs | 9 +-------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/app/global.d.ts b/app/global.d.ts index a1453dc33..8ee636bcd 100644 --- a/app/global.d.ts +++ b/app/global.d.ts @@ -12,7 +12,6 @@ declare module "*.svg"; declare interface Window { __TAURI__?: { - convertFileSrc(url: string, protocol?: string): string; writeText(text: string): Promise; invoke(command: string, payload?: Record): Promise; dialog: { diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index 9aae3d164..f7d5a7693 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -2,6 +2,7 @@ // use std::error::Error; +use std::collections::HashMap; use futures_util::{StreamExt}; use reqwest::Client; use reqwest::header::{HeaderName, HeaderMap}; @@ -28,14 +29,6 @@ pub struct ChunkPayload { chunk: bytes::Bytes, } -use std::collections::HashMap; - -#[derive(serde::Serialize)] -pub struct CustomResponse { - message: String, - other_val: usize, -} - #[tauri::command] pub async fn stream_fetch( window: tauri::Window, From f42488d4cb1aba73b4632b49b6606efd0b5a378d Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 02:28:19 +0800 Subject: [PATCH 096/352] using stream fetch replace old tauri http fetch --- app/utils.ts | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/app/utils.ts b/app/utils.ts index 6b2f65952..4887a1021 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -2,8 +2,9 @@ import { useEffect, useState } from "react"; import { showToast } from "./components/ui-lib"; import Locale from "./locales"; import { RequestMessage } from "./client/api"; -import { ServiceProvider, REQUEST_TIMEOUT_MS } from "./constant"; -import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; +import { ServiceProvider } from "./constant"; +// import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; +import { fetch as tauriStreamFetch } from "./utils/stream"; export function trimTopic(topic: string) { // Fix an issue where double quotes still show in the Indonesian language @@ -292,19 +293,22 @@ export function fetch( options?: Record, ): Promise { if (window.__TAURI__) { - const payload = options?.body || options?.data; - return tauriFetch(url, { - ...options, - body: - payload && - ({ - type: "Text", - payload, - } as any), - timeout: ((options?.timeout as number) || REQUEST_TIMEOUT_MS) / 1000, - responseType: - options?.responseType == "text" ? ResponseType.Text : ResponseType.JSON, - } as any); + return tauriStreamFetch(url, options) + .then((res) => res.text()) + .then((data) => ({ data })); + // const payload = options?.body || options?.data; + // return tauriFetch(url, { + // ...options, + // body: + // payload && + // ({ + // type: "Text", + // payload, + // } as any), + // timeout: ((options?.timeout as number) || REQUEST_TIMEOUT_MS) / 1000, + // responseType: + // options?.responseType == "text" ? ResponseType.Text : ResponseType.JSON, + // } as any); } return window.fetch(url, options); } From 8030e71a5aefe551d23b19867fd8738791c0d712 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 02:33:02 +0800 Subject: [PATCH 097/352] update --- src-tauri/src/stream.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index f7d5a7693..51d844305 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -2,12 +2,13 @@ // use std::error::Error; +use std::sync::atomic::{AtomicU32, Ordering}; use std::collections::HashMap; use futures_util::{StreamExt}; use reqwest::Client; use reqwest::header::{HeaderName, HeaderMap}; -static mut REQUEST_COUNTER: u32 = 0; +static REQUEST_COUNTER: AtomicU32 = AtomicU32::new(0); #[derive(Debug, Clone, serde::Serialize)] pub struct StreamResponse { @@ -38,12 +39,8 @@ pub async fn stream_fetch( body: Vec, ) -> Result { - let mut request_id = 0; let event_name = "stream-response"; - unsafe { - REQUEST_COUNTER += 1; - request_id = REQUEST_COUNTER; - } + let request_id = REQUEST_COUNTER.fetch_add(1, Ordering::SeqCst); let mut _headers = HeaderMap::new(); for (key, value) in &headers { @@ -72,6 +69,10 @@ pub async fn stream_fetch( // println!("body: {:?}", body); request = request.body(body); } + + // println!("client: {:?}", client); + // println!("request: {:?}", request); + let response_future = request.send(); let res = response_future.await; From ef4665cd8b590531af31c56562afdbeb7fa7d0bd Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 02:57:51 +0800 Subject: [PATCH 098/352] update --- app/utils.ts | 8 ++++---- app/utils/stream.ts | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/utils.ts b/app/utils.ts index 4887a1021..9f5dd3150 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -293,9 +293,7 @@ export function fetch( options?: Record, ): Promise { if (window.__TAURI__) { - return tauriStreamFetch(url, options) - .then((res) => res.text()) - .then((data) => ({ data })); + return tauriStreamFetch(url, options); // const payload = options?.body || options?.data; // return tauriFetch(url, { // ...options, @@ -319,7 +317,9 @@ export function adapter(config: Record) { const fetchUrl = params ? `${path}?${new URLSearchParams(params as any).toString()}` : path; - return fetch(fetchUrl as string, { ...rest, responseType: "text" }); + return fetch(fetchUrl as string, { ...rest, responseType: "text" }) + .then((res) => res.text()) + .then((data) => ({ data })); } export function safeLocalStorage(): { diff --git a/app/utils/stream.ts b/app/utils/stream.ts index e8850fcba..2a8c13777 100644 --- a/app/utils/stream.ts +++ b/app/utils/stream.ts @@ -85,7 +85,15 @@ export function fetch(url: string, options?: RequestInit): Promise { .then((res: StreamResponse) => { request_id = res.request_id; const { status, status_text: statusText, headers } = res; - return new Response(ts.readable, { status, statusText, headers }); + const response = new Response(ts.readable, { + status, + statusText, + headers, + }); + if (status >= 300) { + setTimeout(close, 50); + } + return response; }) .catch((e) => { console.error("stream error", e); From 6293b95a3b7f6722d5e8971a4937331119cb174f Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 10:13:11 +0800 Subject: [PATCH 099/352] update default api base url --- app/store/access.ts | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/app/store/access.ts b/app/store/access.ts index 74050733c..dec3a7258 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -2,6 +2,7 @@ import { GoogleSafetySettingsThreshold, ServiceProvider, StoreKey, + ApiPath, OPENAI_BASE_URL, ANTHROPIC_BASE_URL, GEMINI_BASE_URL, @@ -23,6 +24,26 @@ let fetchState = 0; // 0 not fetch, 1 fetching, 2 done const isApp = getClientConfig()?.buildMode === "export"; +const DEFAULT_OPENAI_URL = isApp ? OPENAI_BASE_URL : ApiPath.OpenAI; + +const DEFAULT_GOOGLE_URL = isApp ? GEMINI_BASE_URL : ApiPath.Google; + +const DEFAULT_ANTHROPIC_URL = isApp ? ANTHROPIC_BASE_URL : ApiPath.Anthropic; + +const DEFAULT_BAIDU_URL = isApp ? BAIDU_BASE_URL : ApiPath.Baidu; + +const DEFAULT_BYTEDANCE_URL = isApp ? BYTEDANCE_BASE_URL : ApiPath.ByteDance; + +const DEFAULT_ALIBABA_URL = isApp ? ALIBABA_BASE_URL : ApiPath.Alibaba; + +const DEFAULT_TENCENT_URL = isApp ? TENCENT_BASE_URL : ApiPath.Tencent; + +const DEFAULT_MOONSHOT_URL = isApp ? MOONSHOT_BASE_URL : ApiPath.Moonshot; + +const DEFAULT_STABILITY_URL = isApp ? STABILITY_BASE_URL : ApiPath.Stability; + +const DEFAULT_IFLYTEK_URL = isApp ? IFLYTEK_BASE_URL : ApiPath.Iflytek; + const DEFAULT_ACCESS_STATE = { accessCode: "", useCustomConfig: false, @@ -30,7 +51,7 @@ const DEFAULT_ACCESS_STATE = { provider: ServiceProvider.OpenAI, // openai - openaiUrl: OPENAI_BASE_URL, + openaiUrl: DEFAULT_OPENAI_URL, openaiApiKey: "", // azure @@ -39,44 +60,44 @@ const DEFAULT_ACCESS_STATE = { azureApiVersion: "2023-08-01-preview", // google ai studio - googleUrl: GEMINI_BASE_URL, + googleUrl: DEFAULT_GOOGLE_URL, googleApiKey: "", googleApiVersion: "v1", googleSafetySettings: GoogleSafetySettingsThreshold.BLOCK_ONLY_HIGH, // anthropic - anthropicUrl: ANTHROPIC_BASE_URL, + anthropicUrl: DEFAULT_ANTHROPIC_URL, anthropicApiKey: "", anthropicApiVersion: "2023-06-01", // baidu - baiduUrl: BAIDU_BASE_URL, + baiduUrl: DEFAULT_BAIDU_URL, baiduApiKey: "", baiduSecretKey: "", // bytedance - bytedanceUrl: BYTEDANCE_BASE_URL, + bytedanceUrl: DEFAULT_BYTEDANCE_URL, bytedanceApiKey: "", // alibaba - alibabaUrl: ALIBABA_BASE_URL, + alibabaUrl: DEFAULT_ALIBABA_URL, alibabaApiKey: "", // moonshot - moonshotUrl: MOONSHOT_BASE_URL, + moonshotUrl: DEFAULT_MOONSHOT_URL, moonshotApiKey: "", //stability - stabilityUrl: STABILITY_BASE_URL, + stabilityUrl: DEFAULT_STABILITY_URL, stabilityApiKey: "", // tencent - tencentUrl: TENCENT_BASE_URL, + tencentUrl: DEFAULT_TENCENT_URL, tencentSecretKey: "", tencentSecretId: "", // iflytek - iflytekUrl: IFLYTEK_BASE_URL, + iflytekUrl: DEFAULT_IFLYTEK_URL, iflytekApiKey: "", iflytekApiSecret: "", From b6d9ba93fa101deeb9920f29bee5f675591dacd5 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 10:18:30 +0800 Subject: [PATCH 100/352] update --- src-tauri/src/stream.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index 51d844305..938c663e1 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -95,14 +95,18 @@ pub async fn stream_fetch( match chunk { Ok(bytes) => { // println!("chunk: {:?}", bytes); - window.emit(event_name, ChunkPayload{ request_id, chunk: bytes }).unwrap(); + if let Err(e) = window.emit(event_name, ChunkPayload{ request_id, chunk: bytes }) { + println!("Failed to emit chunk payload: {:?}", e); + } } Err(err) => { println!("Error chunk: {:?}", err); } } } - window.emit(event_name, EndPayload { request_id, status: 0 }).unwrap(); + if let Err(e) = window.emit(event_name, EndPayload{ request_id, status: 0 }) { + println!("Failed to emit end payload: {:?}", e); + } }); StreamResponse { From edfa6d14eefb339781c839750fb68bbfeb632011 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 10:23:24 +0800 Subject: [PATCH 101/352] update --- src-tauri/src/stream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index 938c663e1..3d21623e6 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -64,7 +64,7 @@ pub async fn stream_fetch( url.parse::().map_err(|err| format!("failed to parse url: {}", err))? ); - if method == reqwest::Method::POST { + if method == reqwest::Method::POST || method == reqwest::Method::PUT || method == reqwest::Method::PATCH { let body = bytes::Bytes::from(body); // println!("body: {:?}", body); request = request.body(body); From 7173cf21846475455502cf2d6363fff30e3c4600 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 13:07:06 +0800 Subject: [PATCH 102/352] update --- README.md | 2 ++ src-tauri/tauri.conf.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index be5e91d65..5887369ff 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ For enterprise inquiries, please contact: **business@nextchat.dev** ## What's New +- 🚀 v2.15.4 The Application supports using Tauri fetch LLM API, MORE SECURITY! [#5379](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5379) - 🚀 v2.15.0 Now supports Plugins! Read this: [NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins) - 🚀 v2.14.0 Now supports Artifacts & SD - 🚀 v2.10.1 support Google Gemini Pro model. @@ -137,6 +138,7 @@ For enterprise inquiries, please contact: **business@nextchat.dev** ## 最新动态 +- 🚀 v2.15.4 客户端支持Tauri本地直接调用大模型API,更安全![#5379](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5379) - 🚀 v2.15.0 现在支持插件功能了!了解更多:[NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins) - 🚀 v2.14.0 现在支持 Artifacts & SD 了。 - 🚀 v2.10.1 现在支持 Gemini Pro 模型。 diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index eb0d411cb..cc137ee8a 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "NextChat", - "version": "2.15.3" + "version": "2.15.4" }, "tauri": { "allowlist": { From deb215ccd1adaf14bb55d1f26de4ef4713fd4780 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Mon, 30 Sep 2024 13:23:24 +0800 Subject: [PATCH 103/352] fix readme --- README.md | 21 +++++++++++++-------- README_CN.md | 2 +- README_JA.md | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index be5e91d65..e10d3bce8 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,11 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4 [![MacOS][MacOS-image]][download-url] [![Linux][Linux-image]][download-url] -[NextChatAI](https://nextchat.dev/chat) / [Web App](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev) +[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [Web App](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev) [NextChatAI](https://nextchat.dev/chat) / [网页版](https://app.nextchat.dev) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) -[saas-url]: https://nextchat.dev/chat +[saas-url]: https://nextchat.dev/chat?utm_source=readme [saas-image]: https://img.shields.io/badge/NextChat-Saas-green?logo=microsoftedge [web-url]: https://app.nextchat.dev/ [download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases @@ -31,7 +31,7 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4 [MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple [Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu -[Deploy on Zeabur](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) +[Deploy on Zeabur](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [](https://monica.im/?utm=nxcrp) @@ -40,6 +40,7 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4 ## Enterprise Edition Meeting Your Company's Privatization and Customization Deployment Requirements: + - **Brand Customization**: Tailored VI/UI to seamlessly align with your corporate brand image. - **Resource Integration**: Unified configuration and management of dozens of AI resources by company administrators, ready for use by team members. - **Permission Control**: Clearly defined member permissions, resource permissions, and knowledge base permissions, all controlled via a corporate-grade Admin Panel. @@ -53,6 +54,7 @@ For enterprise inquiries, please contact: **business@nextchat.dev** ## 企业版 满足企业用户私有化部署和个性化定制需求: + - **品牌定制**:企业量身定制 VI/UI,与企业品牌形象无缝契合 - **资源集成**:由企业管理人员统一配置和管理数十种 AI 资源,团队成员开箱即用 - **权限管理**:成员权限、资源权限、知识库权限层级分明,企业级 Admin Panel 统一控制 @@ -101,7 +103,7 @@ For enterprise inquiries, please contact: **business@nextchat.dev** ## What's New - 🚀 v2.15.0 Now supports Plugins! Read this: [NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins) -- 🚀 v2.14.0 Now supports Artifacts & SD +- 🚀 v2.14.0 Now supports Artifacts & SD - 🚀 v2.10.1 support Google Gemini Pro model. - 🚀 v2.9.11 you can use azure endpoint now. - 🚀 v2.8 now we have a client that runs across all platforms! @@ -132,8 +134,8 @@ For enterprise inquiries, please contact: **business@nextchat.dev** - [x] 支持自部署的大语言模型:开箱即用 [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) ,服务端部署 [LocalAI 项目](https://github.com/go-skynet/LocalAI) llama / gpt4all / rwkv / vicuna / koala / gpt4all-j / cerebras / falcon / dolly 等等,或者使用 [api-for-open-llm](https://github.com/xusenlinzy/api-for-open-llm) - [x] Artifacts: 通过独立窗口,轻松预览、复制和分享生成的内容/可交互网页 [#5092](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/pull/5092) - [x] 插件机制,支持`联网搜索`、`计算器`、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) [#5353](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5353) - - [x] 支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) [#5353](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5353) - - [ ] 本地知识库 + - [x] 支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) [#5353](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5353) +- [ ] 本地知识库 ## 最新动态 @@ -333,10 +335,12 @@ To control custom models, use `+` to add a custom model, use `-` to hide a model User `-all` to disable all default models, `+all` to enable all default models. For Azure: use `modelName@azure=deploymentName` to customize model name and deployment name. + > Example: `+gpt-3.5-turbo@azure=gpt35` will show option `gpt35(Azure)` in model list. > If you only can use Azure model, `-all,+gpt-3.5-turbo@azure=gpt35` will `gpt35(Azure)` the only option in model list. For ByteDance: use `modelName@bytedance=deploymentName` to customize model name and deployment name. + > Example: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` will show option `Doubao-lite-4k(ByteDance)` in model list. ### `DEFAULT_MODEL` (optional) @@ -346,8 +350,9 @@ Change default model ### `WHITE_WEBDAV_ENDPOINTS` (optional) You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format: -- Each address must be a complete endpoint -> `https://xxxx/yyy` + +- Each address must be a complete endpoint + > `https://xxxx/yyy` - Multiple addresses are connected by ', ' ### `DEFAULT_INPUT_TEMPLATE` (optional) diff --git a/README_CN.md b/README_CN.md index 640fe3933..73fbc3f51 100644 --- a/README_CN.md +++ b/README_CN.md @@ -8,7 +8,7 @@ 一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型。 -[NextChatAI](https://nextchat.dev/chat) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) +[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) [Deploy on Zeabur](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) diff --git a/README_JA.md b/README_JA.md index ba3c514dc..416928c26 100644 --- a/README_JA.md +++ b/README_JA.md @@ -5,7 +5,7 @@ ワンクリックで無料であなた専用の ChatGPT ウェブアプリをデプロイ。GPT3、GPT4 & Gemini Pro モデルをサポート。 -[NextChatAI](https://nextchat.dev/chat) / [企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N) +[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N) [Zeaburでデプロイ](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Zeaburでデプロイ](https://zeabur.com/templates/ZBUEFA) [Gitpodで開く](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) From d2984db6e7deb0fc51ea76abde2b6748067baa28 Mon Sep 17 00:00:00 2001 From: lyf <1910527151@qq.com> Date: Mon, 30 Sep 2024 13:28:14 +0800 Subject: [PATCH 104/352] fix readme --- README.md | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e10d3bce8..bf7c30594 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4 [MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple [Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu -[Deploy on Zeabur](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) +[Deploy on Zeabur](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [](https://monica.im/?utm=nxcrp) @@ -40,7 +40,6 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4 ## Enterprise Edition Meeting Your Company's Privatization and Customization Deployment Requirements: - - **Brand Customization**: Tailored VI/UI to seamlessly align with your corporate brand image. - **Resource Integration**: Unified configuration and management of dozens of AI resources by company administrators, ready for use by team members. - **Permission Control**: Clearly defined member permissions, resource permissions, and knowledge base permissions, all controlled via a corporate-grade Admin Panel. @@ -54,7 +53,6 @@ For enterprise inquiries, please contact: **business@nextchat.dev** ## 企业版 满足企业用户私有化部署和个性化定制需求: - - **品牌定制**:企业量身定制 VI/UI,与企业品牌形象无缝契合 - **资源集成**:由企业管理人员统一配置和管理数十种 AI 资源,团队成员开箱即用 - **权限管理**:成员权限、资源权限、知识库权限层级分明,企业级 Admin Panel 统一控制 @@ -103,7 +101,7 @@ For enterprise inquiries, please contact: **business@nextchat.dev** ## What's New - 🚀 v2.15.0 Now supports Plugins! Read this: [NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins) -- 🚀 v2.14.0 Now supports Artifacts & SD +- 🚀 v2.14.0 Now supports Artifacts & SD - 🚀 v2.10.1 support Google Gemini Pro model. - 🚀 v2.9.11 you can use azure endpoint now. - 🚀 v2.8 now we have a client that runs across all platforms! @@ -134,8 +132,8 @@ For enterprise inquiries, please contact: **business@nextchat.dev** - [x] 支持自部署的大语言模型:开箱即用 [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) ,服务端部署 [LocalAI 项目](https://github.com/go-skynet/LocalAI) llama / gpt4all / rwkv / vicuna / koala / gpt4all-j / cerebras / falcon / dolly 等等,或者使用 [api-for-open-llm](https://github.com/xusenlinzy/api-for-open-llm) - [x] Artifacts: 通过独立窗口,轻松预览、复制和分享生成的内容/可交互网页 [#5092](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/pull/5092) - [x] 插件机制,支持`联网搜索`、`计算器`、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) [#5353](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5353) - - [x] 支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) [#5353](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5353) -- [ ] 本地知识库 + - [x] 支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) [#5353](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5353) + - [ ] 本地知识库 ## 最新动态 @@ -335,12 +333,10 @@ To control custom models, use `+` to add a custom model, use `-` to hide a model User `-all` to disable all default models, `+all` to enable all default models. For Azure: use `modelName@azure=deploymentName` to customize model name and deployment name. - > Example: `+gpt-3.5-turbo@azure=gpt35` will show option `gpt35(Azure)` in model list. > If you only can use Azure model, `-all,+gpt-3.5-turbo@azure=gpt35` will `gpt35(Azure)` the only option in model list. For ByteDance: use `modelName@bytedance=deploymentName` to customize model name and deployment name. - > Example: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` will show option `Doubao-lite-4k(ByteDance)` in model list. ### `DEFAULT_MODEL` (optional) @@ -350,9 +346,8 @@ Change default model ### `WHITE_WEBDAV_ENDPOINTS` (optional) You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format: - -- Each address must be a complete endpoint - > `https://xxxx/yyy` +- Each address must be a complete endpoint +> `https://xxxx/yyy` - Multiple addresses are connected by ', ' ### `DEFAULT_INPUT_TEMPLATE` (optional) From 35e03e1bcaf228d685e6b2a1ec9168f7892dab98 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 13:44:01 +0800 Subject: [PATCH 105/352] remove code --- src-tauri/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c954deb72..8a11c3b6f 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -35,7 +35,6 @@ tauri = { version = "1.5.4", features = [ "http-all", "window-start-dragging", "window-unmaximize", "window-unminimize", - "linux-protocol-headers", ] } tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } percent-encoding = "2.3.1" From 3029dcb2f6edbf2fcc805188ac3883a09715fd3f Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 15:32:47 +0800 Subject: [PATCH 106/352] hotfix for run plugin call post api --- app/utils.ts | 5 ++++- src-tauri/src/stream.rs | 18 +++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/app/utils.ts b/app/utils.ts index 9f5dd3150..e542e256d 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -293,7 +293,10 @@ export function fetch( options?: Record, ): Promise { if (window.__TAURI__) { - return tauriStreamFetch(url, options); + return tauriStreamFetch(url, { + ...options, + body: options?.body || options?.data, + }); // const payload = options?.body || options?.data; // return tauriFetch(url, { // ...options, diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index 3d21623e6..0fcc02dfc 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -47,10 +47,10 @@ pub async fn stream_fetch( _headers.insert(key.parse::().unwrap(), value.parse().unwrap()); } - // println!("method: {:?}", method); - // println!("url: {:?}", url); - // println!("headers: {:?}", headers); - // println!("headers: {:?}", _headers); + println!("method: {:?}", method); + println!("url: {:?}", url); + println!("headers: {:?}", headers); + println!("headers: {:?}", _headers); let method = method.parse::().map_err(|err| format!("failed to parse method: {}", err))?; let client = Client::builder() @@ -66,12 +66,12 @@ pub async fn stream_fetch( if method == reqwest::Method::POST || method == reqwest::Method::PUT || method == reqwest::Method::PATCH { let body = bytes::Bytes::from(body); - // println!("body: {:?}", body); + println!("body: {:?}", body); request = request.body(body); } - // println!("client: {:?}", client); - // println!("request: {:?}", request); + println!("client: {:?}", client); + println!("request: {:?}", request); let response_future = request.send(); @@ -94,7 +94,7 @@ pub async fn stream_fetch( while let Some(chunk) = stream.next().await { match chunk { Ok(bytes) => { - // println!("chunk: {:?}", bytes); + println!("chunk: {:?}", bytes); if let Err(e) = window.emit(event_name, ChunkPayload{ request_id, chunk: bytes }) { println!("Failed to emit chunk payload: {:?}", e); } @@ -126,7 +126,7 @@ pub async fn stream_fetch( } } }; - // println!("Response: {:?}", response); + println!("Response: {:?}", response); Ok(response) } From fd3568c459d2339817838b824b6ee5f8d4b59d24 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 15:33:40 +0800 Subject: [PATCH 107/352] hotfix for run plugin call post api --- src-tauri/src/stream.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index 0fcc02dfc..3d21623e6 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -47,10 +47,10 @@ pub async fn stream_fetch( _headers.insert(key.parse::().unwrap(), value.parse().unwrap()); } - println!("method: {:?}", method); - println!("url: {:?}", url); - println!("headers: {:?}", headers); - println!("headers: {:?}", _headers); + // println!("method: {:?}", method); + // println!("url: {:?}", url); + // println!("headers: {:?}", headers); + // println!("headers: {:?}", _headers); let method = method.parse::().map_err(|err| format!("failed to parse method: {}", err))?; let client = Client::builder() @@ -66,12 +66,12 @@ pub async fn stream_fetch( if method == reqwest::Method::POST || method == reqwest::Method::PUT || method == reqwest::Method::PATCH { let body = bytes::Bytes::from(body); - println!("body: {:?}", body); + // println!("body: {:?}", body); request = request.body(body); } - println!("client: {:?}", client); - println!("request: {:?}", request); + // println!("client: {:?}", client); + // println!("request: {:?}", request); let response_future = request.send(); @@ -94,7 +94,7 @@ pub async fn stream_fetch( while let Some(chunk) = stream.next().await { match chunk { Ok(bytes) => { - println!("chunk: {:?}", bytes); + // println!("chunk: {:?}", bytes); if let Err(e) = window.emit(event_name, ChunkPayload{ request_id, chunk: bytes }) { println!("Failed to emit chunk payload: {:?}", e); } @@ -126,7 +126,7 @@ pub async fn stream_fetch( } } }; - println!("Response: {:?}", response); + // println!("Response: {:?}", response); Ok(response) } From d830c23daba6e80452286aadfec8eeb9cc8652bb Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 30 Sep 2024 15:38:13 +0800 Subject: [PATCH 108/352] hotfix for run plugin call post api --- app/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/utils.ts b/app/utils.ts index e542e256d..c1476152a 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -295,7 +295,7 @@ export function fetch( if (window.__TAURI__) { return tauriStreamFetch(url, { ...options, - body: options?.body || options?.data, + body: (options?.body || options?.data) as any, }); // const payload = options?.body || options?.data; // return tauriFetch(url, { From 953114041bb8381314c7f880d83c5157d0b6b3ad Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 1 Oct 2024 12:02:29 +0800 Subject: [PATCH 109/352] add connect timeout --- src-tauri/src/stream.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index 3d21623e6..d2c0726b0 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -1,6 +1,7 @@ // // +use std::time::Duration; use std::error::Error; use std::sync::atomic::{AtomicU32, Ordering}; use std::collections::HashMap; @@ -56,6 +57,7 @@ pub async fn stream_fetch( let client = Client::builder() .default_headers(_headers) .redirect(reqwest::redirect::Policy::limited(3)) + .connect_timeout(Duration::new(3, 0)) .build() .map_err(|err| format!("failed to generate client: {}", err))?; From 9c577ad9d57d47ad5831ca15f15988ba0381ee2c Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 1 Oct 2024 12:55:57 +0800 Subject: [PATCH 110/352] hotfix for plugin runtime --- app/utils.ts | 9 ++++++--- app/utils/chat.ts | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/utils.ts b/app/utils.ts index c1476152a..95880115a 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -320,9 +320,12 @@ export function adapter(config: Record) { const fetchUrl = params ? `${path}?${new URLSearchParams(params as any).toString()}` : path; - return fetch(fetchUrl as string, { ...rest, responseType: "text" }) - .then((res) => res.text()) - .then((data) => ({ data })); + return fetch(fetchUrl as string, { ...rest, responseType: "text" }).then( + (res) => { + const { status, headers } = res; + return res.text().then((data) => ({ status, headers, data })); + }, + ); } export function safeLocalStorage(): { diff --git a/app/utils/chat.ts b/app/utils/chat.ts index 254cef401..3d7960480 100644 --- a/app/utils/chat.ts +++ b/app/utils/chat.ts @@ -222,7 +222,10 @@ export function stream( ), ) .then((res) => { - const content = JSON.stringify(res.data); + let content = res.data; + try { + content = JSON.stringify(res.data); + } catch (e) {} if (res.status >= 300) { return Promise.reject(content); } From 919ee51dca25ba03f2d627eaabbe17b578dec45d Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 1 Oct 2024 13:58:50 +0800 Subject: [PATCH 111/352] hover show errorMsg when plugin run error --- app/components/chat.tsx | 1 + app/store/chat.ts | 1 + app/utils.ts | 6 ++++-- app/utils/chat.ts | 11 ++++++----- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 3d519dee7..b45d36f95 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -1815,6 +1815,7 @@ function _Chat() { {message?.tools?.map((tool) => (
{tool.isError === false ? ( diff --git a/app/store/chat.ts b/app/store/chat.ts index 968d8cb64..931cad768 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -37,6 +37,7 @@ export type ChatMessageTool = { }; content?: string; isError?: boolean; + errorMsg?: string; }; export type ChatMessage = RequestMessage & { diff --git a/app/utils.ts b/app/utils.ts index 95880115a..83bcea5c0 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -322,8 +322,10 @@ export function adapter(config: Record) { : path; return fetch(fetchUrl as string, { ...rest, responseType: "text" }).then( (res) => { - const { status, headers } = res; - return res.text().then((data) => ({ status, headers, data })); + const { status, headers, statusText } = res; + return res + .text() + .then((data: string) => ({ status, statusText, headers, data })); }, ); } diff --git a/app/utils/chat.ts b/app/utils/chat.ts index 3d7960480..46f232638 100644 --- a/app/utils/chat.ts +++ b/app/utils/chat.ts @@ -222,10 +222,7 @@ export function stream( ), ) .then((res) => { - let content = res.data; - try { - content = JSON.stringify(res.data); - } catch (e) {} + let content = res.data || res?.statusText; if (res.status >= 300) { return Promise.reject(content); } @@ -240,7 +237,11 @@ export function stream( return content; }) .catch((e) => { - options?.onAfterTool?.({ ...tool, isError: true }); + options?.onAfterTool?.({ + ...tool, + isError: true, + errorMsg: e.toString(), + }); return e.toString(); }) .then((content) => ({ From d51d31a55959c00279f9d84b302d3ac4de77f559 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 1 Oct 2024 14:40:23 +0800 Subject: [PATCH 112/352] update --- app/utils.ts | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/app/utils.ts b/app/utils.ts index 83bcea5c0..b3d27cbce 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -293,41 +293,23 @@ export function fetch( options?: Record, ): Promise { if (window.__TAURI__) { - return tauriStreamFetch(url, { - ...options, - body: (options?.body || options?.data) as any, - }); - // const payload = options?.body || options?.data; - // return tauriFetch(url, { - // ...options, - // body: - // payload && - // ({ - // type: "Text", - // payload, - // } as any), - // timeout: ((options?.timeout as number) || REQUEST_TIMEOUT_MS) / 1000, - // responseType: - // options?.responseType == "text" ? ResponseType.Text : ResponseType.JSON, - // } as any); + return tauriStreamFetch(url, options); } return window.fetch(url, options); } export function adapter(config: Record) { - const { baseURL, url, params, ...rest } = config; + const { baseURL, url, params, data: body, ...rest } = config; const path = baseURL ? `${baseURL}${url}` : url; const fetchUrl = params ? `${path}?${new URLSearchParams(params as any).toString()}` : path; - return fetch(fetchUrl as string, { ...rest, responseType: "text" }).then( - (res) => { - const { status, headers, statusText } = res; - return res - .text() - .then((data: string) => ({ status, statusText, headers, data })); - }, - ); + return fetch(fetchUrl as string, { ...rest, body }).then((res) => { + const { status, headers, statusText } = res; + return res + .text() + .then((data: string) => ({ status, statusText, headers, data })); + }); } export function safeLocalStorage(): { From fbb66a4a5d4da9e143c62b482ff27e8b3037d2db Mon Sep 17 00:00:00 2001 From: code-october <148516338+code-october@users.noreply.github.com> Date: Thu, 3 Oct 2024 02:08:10 +0000 Subject: [PATCH 113/352] use safe equal operation --- app/client/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/api.ts b/app/client/api.ts index 48bbde6bc..7a242ea99 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -231,7 +231,7 @@ export function getHeaders(ignoreHeaders: boolean = false) { function getConfig() { const modelConfig = chatStore.currentSession().mask.modelConfig; - const isGoogle = modelConfig.providerName == ServiceProvider.Google; + const isGoogle = modelConfig.providerName === ServiceProvider.Google; const isAzure = modelConfig.providerName === ServiceProvider.Azure; const isAnthropic = modelConfig.providerName === ServiceProvider.Anthropic; const isBaidu = modelConfig.providerName == ServiceProvider.Baidu; From 450766a44bf6868a716de7c012c7e42347c62898 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 3 Oct 2024 20:28:15 +0800 Subject: [PATCH 114/352] google gemini support function call --- app/client/platforms/google.ts | 195 ++++++++++++++------------------- app/utils.ts | 3 + app/utils/chat.ts | 1 + 3 files changed, 87 insertions(+), 112 deletions(-) diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index 3c2607271..3b82a569b 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -7,21 +7,25 @@ import { LLMUsage, SpeechOptions, } from "../api"; -import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; +import { + useAccessStore, + useAppConfig, + useChatStore, + usePluginStore, + ChatMessageTool, +} from "@/app/store"; +import { stream } from "@/app/utils/chat"; import { getClientConfig } from "@/app/config/client"; import { DEFAULT_API_HOST } from "@/app/constant"; -import Locale from "../../locales"; -import { - EventStreamContentType, - fetchEventSource, -} from "@fortaine/fetch-event-source"; -import { prettyObject } from "@/app/utils/format"; + import { getMessageTextContent, getMessageImages, isVisionModel, } from "@/app/utils"; import { preProcessImageContent } from "@/app/utils/chat"; +import { nanoid } from "nanoid"; +import { RequestPayload } from "./openai"; export class GeminiProApi implements LLMApi { path(path: string): string { @@ -177,114 +181,81 @@ export class GeminiProApi implements LLMApi { ); if (shouldStream) { - let responseText = ""; - let remainText = ""; - let finished = false; + const [tools, funcs] = usePluginStore + .getState() + .getAsTools( + useChatStore.getState().currentSession().mask?.plugin || [], + ); + return stream( + chatPath, + requestPayload, + getHeaders(), + // @ts-ignore + [{ functionDeclarations: tools.map((tool) => tool.function) }], + funcs, + controller, + // parseSSE + (text: string, runTools: ChatMessageTool[]) => { + // console.log("parseSSE", text, runTools); + const chunkJson = JSON.parse(text); - const finish = () => { - if (!finished) { - finished = true; - options.onFinish(responseText + remainText); - } - }; - - // animate response to make it looks smooth - function animateResponseText() { - if (finished || controller.signal.aborted) { - responseText += remainText; - finish(); - return; - } - - if (remainText.length > 0) { - const fetchCount = Math.max(1, Math.round(remainText.length / 60)); - const fetchText = remainText.slice(0, fetchCount); - responseText += fetchText; - remainText = remainText.slice(fetchCount); - options.onUpdate?.(responseText, fetchText); - } - - requestAnimationFrame(animateResponseText); - } - - // start animaion - animateResponseText(); - - controller.signal.onabort = finish; - - fetchEventSource(chatPath, { - ...chatPayload, - async onopen(res) { - clearTimeout(requestTimeoutId); - const contentType = res.headers.get("content-type"); - console.log( - "[Gemini] request response content type: ", - contentType, + const functionCall = chunkJson?.candidates + ?.at(0) + ?.content.parts.at(0)?.functionCall; + if (functionCall) { + const { name, args } = functionCall; + runTools.push({ + id: nanoid(), + type: "function", + function: { + name, + arguments: JSON.stringify(args), // utils.chat call function, using JSON.parse + }, + }); + } + return chunkJson?.candidates?.at(0)?.content.parts.at(0)?.text; + }, + // processToolMessage, include tool_calls message and tool call results + ( + requestPayload: RequestPayload, + toolCallMessage: any, + toolCallResult: any[], + ) => { + // @ts-ignore + requestPayload?.contents?.splice( + // @ts-ignore + requestPayload?.contents?.length, + 0, + { + role: "model", + parts: toolCallMessage.tool_calls.map( + (tool: ChatMessageTool) => ({ + functionCall: { + name: tool?.function?.name, + args: JSON.parse(tool?.function?.arguments as string), + }, + }), + ), + }, + // @ts-ignore + ...toolCallResult.map((result) => ({ + role: "function", + parts: [ + { + functionResponse: { + name: result.name, + response: { + name: result.name, + content: result.content, // TODO just text content... + }, + }, + }, + ], + })), ); - - if (contentType?.startsWith("text/plain")) { - responseText = await res.clone().text(); - return finish(); - } - - if ( - !res.ok || - !res.headers - .get("content-type") - ?.startsWith(EventStreamContentType) || - res.status !== 200 - ) { - const responseTexts = [responseText]; - let extraInfo = await res.clone().text(); - try { - const resJson = await res.clone().json(); - extraInfo = prettyObject(resJson); - } catch {} - - if (res.status === 401) { - responseTexts.push(Locale.Error.Unauthorized); - } - - if (extraInfo) { - responseTexts.push(extraInfo); - } - - responseText = responseTexts.join("\n\n"); - - return finish(); - } }, - onmessage(msg) { - if (msg.data === "[DONE]" || finished) { - return finish(); - } - const text = msg.data; - try { - const json = JSON.parse(text); - const delta = apiClient.extractMessage(json); - - if (delta) { - remainText += delta; - } - - const blockReason = json?.promptFeedback?.blockReason; - if (blockReason) { - // being blocked - console.log(`[Google] [Safety Ratings] result:`, blockReason); - } - } catch (e) { - console.error("[Request] parse error", text, msg); - } - }, - onclose() { - finish(); - }, - onerror(e) { - options.onError?.(e); - throw e; - }, - openWhenHidden: true, - }); + options, + ); } else { const res = await fetch(chatPath, chatPayload); clearTimeout(requestTimeoutId); diff --git a/app/utils.ts b/app/utils.ts index 6b2f65952..a7c817767 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -284,6 +284,9 @@ export function showPlugins(provider: ServiceProvider, model: string) { if (provider == ServiceProvider.Anthropic && !model.includes("claude-2")) { return true; } + if (provider == ServiceProvider.Google && !model.includes("vision")) { + return true; + } return false; } diff --git a/app/utils/chat.ts b/app/utils/chat.ts index 7f3bb23c5..c93334bb1 100644 --- a/app/utils/chat.ts +++ b/app/utils/chat.ts @@ -240,6 +240,7 @@ export function stream( return e.toString(); }) .then((content) => ({ + name: tool.function.name, role: "tool", content, tool_call_id: tool.id, From cd75461f9e215f6e3140e36359d138dc096abe99 Mon Sep 17 00:00:00 2001 From: little_huang <53588889+little-huang@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:30:25 +0800 Subject: [PATCH 115/352] fix: correct typo in variable name from ALLOWD_PATH to ALLOWED_PATH --- app/api/openai.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/api/openai.ts b/app/api/openai.ts index 7dfd84e17..bbba69e56 100644 --- a/app/api/openai.ts +++ b/app/api/openai.ts @@ -6,7 +6,7 @@ import { NextRequest, NextResponse } from "next/server"; import { auth } from "./auth"; import { requestOpenai } from "./common"; -const ALLOWD_PATH = new Set(Object.values(OpenaiPath)); +const ALLOWED_PATH = new Set(Object.values(OpenaiPath)); function getModels(remoteModelRes: OpenAIListModelResponse) { const config = getServerSideConfig(); @@ -34,7 +34,7 @@ export async function handle( const subpath = params.path.join("/"); - if (!ALLOWD_PATH.has(subpath)) { + if (!ALLOWED_PATH.has(subpath)) { console.log("[OpenAI Route] forbidden path ", subpath); return NextResponse.json( { From 461154bb039793b3086a4697d031f9a85a1c3b26 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Tue, 8 Oct 2024 10:29:42 +0800 Subject: [PATCH 116/352] fix: format package --- package.json | 166 +++++++++++++++++++++++++-------------------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/package.json b/package.json index 3945a203f..eb7683992 100644 --- a/package.json +++ b/package.json @@ -1,84 +1,84 @@ { - "name": "nextchat", - "private": false, - "license": "mit", - "scripts": { - "mask": "npx tsx app/masks/build.ts", - "mask:watch": "npx watch \"yarn mask\" app/masks", - "dev": "concurrently -r \"yarn run mask:watch\" \"next dev\"", - "build": "yarn test:ci && yarn mask && cross-env BUILD_MODE=standalone next build", - "start": "next start", - "lint": "next lint", - "export": "yarn test:ci && yarn mask && cross-env BUILD_MODE=export BUILD_APP=1 next build", - "export:dev": "concurrently -r \"yarn mask:watch\" \"cross-env BUILD_MODE=export BUILD_APP=1 next dev\"", - "app:dev": "concurrently -r \"yarn mask:watch\" \"yarn tauri dev\"", - "app:build": "yarn test:ci && yarn mask && yarn tauri build", - "prompts": "node ./scripts/fetch-prompts.mjs", - "prepare": "husky install", - "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev", - "test": "jest --watch", - "test:ci": "jest --ci" - }, - "dependencies": { - "@fortaine/fetch-event-source": "^3.0.6", - "@hello-pangea/dnd": "^16.5.0", - "@next/third-parties": "^14.1.0", - "@svgr/webpack": "^6.5.1", - "@vercel/analytics": "^0.1.11", - "@vercel/speed-insights": "^1.0.2", - "emoji-picker-react": "^4.9.2", - "fuse.js": "^7.0.0", - "heic2any": "^0.0.4", - "html-to-image": "^1.11.11", - "lodash-es": "^4.17.21", - "mermaid": "^10.6.1", - "nanoid": "^5.0.3", - "next": "^14.1.1", - "node-fetch": "^3.3.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-markdown": "^8.0.7", - "react-router-dom": "^6.15.0", - "rehype-highlight": "^6.0.0", - "rehype-katex": "^6.0.3", - "remark-breaks": "^3.0.2", - "remark-gfm": "^3.0.1", - "remark-math": "^5.1.1", - "sass": "^1.59.2", - "spark-md5": "^3.0.2", - "use-debounce": "^9.0.4", - "zustand": "^4.3.8" - }, - "devDependencies": { - "@tauri-apps/cli": "1.5.11", - "@testing-library/jest-dom": "^6.4.8", - "@testing-library/react": "^16.0.0", - "@types/jest": "^29.5.12", - "@types/lodash-es": "^4.17.12", - "@types/node": "^20.11.30", - "@types/react": "^18.2.70", - "@types/react-dom": "^18.2.7", - "@types/react-katex": "^3.0.0", - "@types/spark-md5": "^3.0.4", - "concurrently": "^8.2.2", - "cross-env": "^7.0.3", - "eslint": "^8.49.0", - "eslint-config-next": "13.4.19", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^5.1.3", - "husky": "^8.0.0", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", - "lint-staged": "^13.2.2", - "prettier": "^3.0.2", - "ts-node": "^10.9.2", - "tsx": "^4.16.0", - "typescript": "5.2.2", - "watch": "^1.0.2", - "webpack": "^5.88.1" - }, - "resolutions": { - "lint-staged/yaml": "^2.2.2" - }, - "packageManager": "yarn@1.22.19" -} \ No newline at end of file + "name": "nextchat", + "private": false, + "license": "mit", + "scripts": { + "mask": "npx tsx app/masks/build.ts", + "mask:watch": "npx watch \"yarn mask\" app/masks", + "dev": "concurrently -r \"yarn run mask:watch\" \"next dev\"", + "build": "yarn test:ci && yarn mask && cross-env BUILD_MODE=standalone next build", + "start": "next start", + "lint": "next lint", + "export": "yarn test:ci && yarn mask && cross-env BUILD_MODE=export BUILD_APP=1 next build", + "export:dev": "concurrently -r \"yarn mask:watch\" \"cross-env BUILD_MODE=export BUILD_APP=1 next dev\"", + "app:dev": "concurrently -r \"yarn mask:watch\" \"yarn tauri dev\"", + "app:build": "yarn test:ci && yarn mask && yarn tauri build", + "prompts": "node ./scripts/fetch-prompts.mjs", + "prepare": "husky install", + "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev", + "test": "jest --watch", + "test:ci": "jest --ci" + }, + "dependencies": { + "@fortaine/fetch-event-source": "^3.0.6", + "@hello-pangea/dnd": "^16.5.0", + "@next/third-parties": "^14.1.0", + "@svgr/webpack": "^6.5.1", + "@vercel/analytics": "^0.1.11", + "@vercel/speed-insights": "^1.0.2", + "emoji-picker-react": "^4.9.2", + "fuse.js": "^7.0.0", + "heic2any": "^0.0.4", + "html-to-image": "^1.11.11", + "lodash-es": "^4.17.21", + "mermaid": "^10.6.1", + "nanoid": "^5.0.3", + "next": "^14.1.1", + "node-fetch": "^3.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-markdown": "^8.0.7", + "react-router-dom": "^6.15.0", + "rehype-highlight": "^6.0.0", + "rehype-katex": "^6.0.3", + "remark-breaks": "^3.0.2", + "remark-gfm": "^3.0.1", + "remark-math": "^5.1.1", + "sass": "^1.59.2", + "spark-md5": "^3.0.2", + "use-debounce": "^9.0.4", + "zustand": "^4.3.8" + }, + "devDependencies": { + "@tauri-apps/cli": "1.5.11", + "@testing-library/jest-dom": "^6.4.8", + "@testing-library/react": "^16.0.0", + "@types/jest": "^29.5.12", + "@types/lodash-es": "^4.17.12", + "@types/node": "^20.11.30", + "@types/react": "^18.2.70", + "@types/react-dom": "^18.2.7", + "@types/react-katex": "^3.0.0", + "@types/spark-md5": "^3.0.4", + "concurrently": "^8.2.2", + "cross-env": "^7.0.3", + "eslint": "^8.49.0", + "eslint-config-next": "13.4.19", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.1.3", + "husky": "^8.0.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "lint-staged": "^13.2.2", + "prettier": "^3.0.2", + "ts-node": "^10.9.2", + "tsx": "^4.16.0", + "typescript": "5.2.2", + "watch": "^1.0.2", + "webpack": "^5.88.1" + }, + "resolutions": { + "lint-staged/yaml": "^2.2.2" + }, + "packageManager": "yarn@1.22.19" +} From 7d55a6d0e441bddaf9870c9adfa88f1f72c600a5 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Tue, 8 Oct 2024 21:31:01 +0800 Subject: [PATCH 117/352] feat: allow send image only --- app/components/chat.tsx | 7 +++++-- app/store/chat.ts | 22 ++++++++-------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 3d519dee7..77b48abee 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -115,11 +115,14 @@ import { getClientConfig } from "../config/client"; import { useAllModels } from "../utils/hooks"; import { MultimodalContent } from "../client/api"; -const localStorage = safeLocalStorage(); import { ClientApi } from "../client/api"; import { createTTSPlayer } from "../utils/audio"; import { MsEdgeTTS, OUTPUT_FORMAT } from "../utils/ms_edge_tts"; +import { isEmpty } from "lodash-es"; + +const localStorage = safeLocalStorage(); + const ttsPlayer = createTTSPlayer(); const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { @@ -1015,7 +1018,7 @@ function _Chat() { }; const doSubmit = (userInput: string) => { - if (userInput.trim() === "") return; + if (userInput.trim() === "" && isEmpty(attachImages)) return; const matchCommand = chatCommands.match(userInput); if (matchCommand.matched) { setUserInput(""); diff --git a/app/store/chat.ts b/app/store/chat.ts index 968d8cb64..fc1fb23ba 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -337,22 +337,16 @@ export const useChatStore = createPersistStore( if (attachImages && attachImages.length > 0) { mContent = [ - { - type: "text", - text: userContent, - }, + ...(userContent + ? [{ type: "text" as const, text: userContent }] + : []), + ...attachImages.map((url) => ({ + type: "image_url" as const, + image_url: { url }, + })), ]; - mContent = mContent.concat( - attachImages.map((url) => { - return { - type: "image_url", - image_url: { - url: url, - }, - }; - }), - ); } + let userMessage: ChatMessage = createMessage({ role: "user", content: mContent, From 77a58bc4b058ced13a99ddc593f544ec8f1603bb Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Wed, 9 Oct 2024 01:46:52 +0800 Subject: [PATCH 118/352] i18n: improve tw Traditional Chinese locale --- app/locales/tw.ts | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 7cb846708..c800ad15d 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -8,12 +8,12 @@ const tw = { Error: { Unauthorized: isApp ? `😆 對話遇到了一些問題,不用慌: - \\ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL}) - \\ 2️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️` + \\ 1️⃣ 想要無須設定開箱即用,[點選這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL}) + \\ 2️⃣ 如果你想消耗自己的 OpenAI 資源,點選[這裡](/#/settings)修改設定 ⚙️` : `😆 對話遇到了一些問題,不用慌: - \ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL}) - \ 2️⃣ 如果你正在使用私有部署版本,點擊[這裡](/#/auth)輸入訪問秘鑰 🔑 - \ 3️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️ + \ 1️⃣ 想要無須設定開箱即用,[點選這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL}) + \ 2️⃣ 如果你正在使用私有部署版本,點選[這裡](/#/auth)輸入存取金鑰 🔑 + \ 3️⃣ 如果你想消耗自己的 OpenAI 資源,點選[這裡](/#/settings)修改設定 ⚙️ `, }, @@ -25,9 +25,9 @@ const tw = { Confirm: "確認", Later: "稍候再說", Return: "返回", - SaasTips: "配置太麻煩,想要立即使用", + SaasTips: "設定太麻煩,想要立即使用", TopTips: - "🥳 NextChat AI 首發優惠,立刻解鎖 OpenAI o1, GPT-4o, Claude-3.5 等最新大模型", + "🥳 NextChat AI 首發優惠,立刻解鎖 OpenAI o1, GPT-4o, Claude-3.5 等最新的大型語言模型", }, ChatItem: { ChatItemCount: (count: number) => `${count} 則對話`, @@ -53,8 +53,8 @@ const tw = { PinToastAction: "檢視", Delete: "刪除", Edit: "編輯", - RefreshTitle: "刷新標題", - RefreshToast: "已發送刷新標題請求", + RefreshTitle: "重新整理標題", + RefreshToast: "已傳送重新整理標題請求", }, Commands: { new: "新建聊天", @@ -95,10 +95,10 @@ const tw = { IsContext: "預設提示詞", ShortcutKey: { Title: "鍵盤快捷方式", - newChat: "打開新聊天", + newChat: "開啟新聊天", focusInput: "聚焦輸入框", copyLastMessage: "複製最後一個回覆", - copyLastCode: "複製最後一個代碼塊", + copyLastCode: "複製最後一個程式碼區塊", showShortcutKey: "顯示快捷方式", }, }, @@ -174,9 +174,9 @@ const tw = { SubTitle: "聊天內容的字型大小", }, FontFamily: { - Title: "聊天字體", - SubTitle: "聊天內容的字體,若置空則應用全局默認字體", - Placeholder: "字體名稱", + Title: "聊天字型", + SubTitle: "聊天內容的字型,若留空則套用全域預設字型", + Placeholder: "字型名稱", }, InjectSystemPrompts: { Title: "匯入系統提示", @@ -301,8 +301,8 @@ const tw = { Title: "使用 NextChat AI", Label: "(性價比最高的方案)", SubTitle: - "由 NextChat 官方維護,零配置開箱即用,支持 OpenAI o1、GPT-4o、Claude-3.5 等最新大模型", - ChatNow: "立刻對話", + "由 NextChat 官方維護,無須設定開箱即用,支援 OpenAI o1、GPT-4o、Claude-3.5 等最新的大型語言模型", + ChatNow: "立刻開始對話", }, AccessCode: { @@ -485,18 +485,18 @@ const tw = { }, }, SearchChat: { - Name: "搜索", + Name: "搜尋", Page: { - Title: "搜索聊天記錄", - Search: "輸入搜索關鍵詞", + Title: "搜尋聊天記錄", + Search: "輸入搜尋關鍵詞", NoResult: "沒有找到結果", - NoData: "沒有數據", - Loading: "加載中", + NoData: "沒有資料", + Loading: "載入中", SubTitle: (count: number) => `找到 ${count} 條結果`, }, Item: { - View: "查看", + View: "檢視", }, }, NewChat: { From 6c1cbe120cb5f018bfc618c7c4382a696a1aa339 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Wed, 9 Oct 2024 11:46:49 +0800 Subject: [PATCH 119/352] update --- app/utils/stream.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/utils/stream.ts b/app/utils/stream.ts index 2a8c13777..313ec4dd5 100644 --- a/app/utils/stream.ts +++ b/app/utils/stream.ts @@ -91,7 +91,7 @@ export function fetch(url: string, options?: RequestInit): Promise { headers, }); if (status >= 300) { - setTimeout(close, 50); + setTimeout(close, 100); } return response; }) From a925b424a8b02399d22ed05b3dc28631dbc03bc5 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Wed, 9 Oct 2024 13:42:25 +0800 Subject: [PATCH 120/352] fix compressModel, related #5426, fix #5606 #5603 #5575 --- app/store/chat.ts | 50 +++++++++++++++++++++++++++++++++++++++++---- app/store/config.ts | 8 ++++---- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/app/store/chat.ts b/app/store/chat.ts index 931cad768..98163981c 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -16,6 +16,8 @@ import { DEFAULT_SYSTEM_TEMPLATE, KnowledgeCutOffDate, StoreKey, + SUMMARIZE_MODEL, + GEMINI_SUMMARIZE_MODEL, } from "../constant"; import Locale, { getLang } from "../locales"; import { isDalle3, safeLocalStorage } from "../utils"; @@ -23,6 +25,8 @@ import { prettyObject } from "../utils/format"; import { createPersistStore } from "../utils/store"; import { estimateTokenLength } from "../utils/token"; import { ModelConfig, ModelType, useAppConfig } from "./config"; +import { useAccessStore } from "./access"; +import { collectModelsWithDefaultModel } from "../utils/model"; import { createEmptyMask, Mask } from "./mask"; const localStorage = safeLocalStorage(); @@ -103,6 +107,29 @@ function createEmptySession(): ChatSession { }; } +function getSummarizeModel(currentModel: string, providerName: string) { + // if it is using gpt-* models, force to use 4o-mini to summarize + if (currentModel.startsWith("gpt") || currentModel.startsWith("chatgpt")) { + const configStore = useAppConfig.getState(); + const accessStore = useAccessStore.getState(); + const allModel = collectModelsWithDefaultModel( + configStore.models, + [configStore.customModels, accessStore.customModels].join(","), + accessStore.defaultModel, + ); + const summarizeModel = allModel.find( + (m) => m.name === SUMMARIZE_MODEL && m.available, + ); + if (summarizeModel) { + return [summarizeModel.name, summarizeModel.providerName]; + } + } + if (currentModel.startsWith("gemini")) { + return [GEMINI_SUMMARIZE_MODEL, ServiceProvider.Google]; + } + return [currentModel, providerName]; +} + function countMessages(msgs: ChatMessage[]) { return msgs.reduce( (pre, cur) => pre + estimateTokenLength(getMessageTextContent(cur)), @@ -579,7 +606,13 @@ export const useChatStore = createPersistStore( return; } - const providerName = modelConfig.compressProviderName; + // if not config compressModel, then using getSummarizeModel + const [model, providerName] = modelConfig.compressModel + ? [modelConfig.compressModel, modelConfig.compressProviderName] + : getSummarizeModel( + session.mask.modelConfig.model, + session.mask.modelConfig.providerName, + ); const api: ClientApi = getClientApi(providerName); // remove error messages if any @@ -611,7 +644,7 @@ export const useChatStore = createPersistStore( api.llm.chat({ messages: topicMessages, config: { - model: modelConfig.compressModel, + model, stream: false, providerName, }, @@ -675,7 +708,8 @@ export const useChatStore = createPersistStore( config: { ...modelcfg, stream: true, - model: modelConfig.compressModel, + model, + providerName, }, onUpdate(message) { session.memoryPrompt = message; @@ -728,7 +762,7 @@ export const useChatStore = createPersistStore( }, { name: StoreKey.Chat, - version: 3.2, + version: 3.3, migrate(persistedState, version) { const state = persistedState as any; const newState = JSON.parse( @@ -784,6 +818,14 @@ export const useChatStore = createPersistStore( config.modelConfig.compressProviderName; }); } + // revert default summarize model for every session + if (version < 3.3) { + newState.sessions.forEach((s) => { + const config = useAppConfig.getState(); + s.mask.modelConfig.compressModel = undefined; + s.mask.modelConfig.compressProviderName = undefined; + }); + } return newState as any; }, diff --git a/app/store/config.ts b/app/store/config.ts index 3dcd4d86b..c52ee3915 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -71,8 +71,8 @@ export const DEFAULT_CONFIG = { sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, - compressModel: "gpt-4o-mini" as ModelType, - compressProviderName: "OpenAI" as ServiceProvider, + compressModel: undefined, + compressProviderName: undefined, enableInjectSystemPrompts: true, template: config?.template ?? DEFAULT_INPUT_TEMPLATE, size: "1024x1024" as DalleSize, @@ -178,7 +178,7 @@ export const useAppConfig = createPersistStore( }), { name: StoreKey.Config, - version: 4, + version: 4.1, merge(persistedState, currentState) { const state = persistedState as ChatConfig | undefined; @@ -231,7 +231,7 @@ export const useAppConfig = createPersistStore( : config?.template ?? DEFAULT_INPUT_TEMPLATE; } - if (version < 4) { + if (version < 4.1) { state.modelConfig.compressModel = DEFAULT_CONFIG.modelConfig.compressModel; state.modelConfig.compressProviderName = From 93ca303b6c981fd910a7320d8a3d78b335cea1d4 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Wed, 9 Oct 2024 13:49:33 +0800 Subject: [PATCH 121/352] fix ts error --- app/store/chat.ts | 17 ++++++++++++----- app/store/config.ts | 4 ++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/store/chat.ts b/app/store/chat.ts index 98163981c..02adb26b3 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -18,6 +18,7 @@ import { StoreKey, SUMMARIZE_MODEL, GEMINI_SUMMARIZE_MODEL, + ServiceProvider, } from "../constant"; import Locale, { getLang } from "../locales"; import { isDalle3, safeLocalStorage } from "../utils"; @@ -107,7 +108,10 @@ function createEmptySession(): ChatSession { }; } -function getSummarizeModel(currentModel: string, providerName: string) { +function getSummarizeModel( + currentModel: string, + providerName: string, +): string[] { // if it is using gpt-* models, force to use 4o-mini to summarize if (currentModel.startsWith("gpt") || currentModel.startsWith("chatgpt")) { const configStore = useAppConfig.getState(); @@ -121,7 +125,10 @@ function getSummarizeModel(currentModel: string, providerName: string) { (m) => m.name === SUMMARIZE_MODEL && m.available, ); if (summarizeModel) { - return [summarizeModel.name, summarizeModel.providerName]; + return [ + summarizeModel.name, + summarizeModel.provider?.providerName as string, + ]; } } if (currentModel.startsWith("gemini")) { @@ -613,7 +620,7 @@ export const useChatStore = createPersistStore( session.mask.modelConfig.model, session.mask.modelConfig.providerName, ); - const api: ClientApi = getClientApi(providerName); + const api: ClientApi = getClientApi(providerName as ServiceProvider); // remove error messages if any const messages = session.messages; @@ -822,8 +829,8 @@ export const useChatStore = createPersistStore( if (version < 3.3) { newState.sessions.forEach((s) => { const config = useAppConfig.getState(); - s.mask.modelConfig.compressModel = undefined; - s.mask.modelConfig.compressProviderName = undefined; + s.mask.modelConfig.compressModel = ""; + s.mask.modelConfig.compressProviderName = ""; }); } diff --git a/app/store/config.ts b/app/store/config.ts index c52ee3915..f9ddce4a8 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -71,8 +71,8 @@ export const DEFAULT_CONFIG = { sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, - compressModel: undefined, - compressProviderName: undefined, + compressModel: "", + compressProviderName: "", enableInjectSystemPrompts: true, template: config?.template ?? DEFAULT_INPUT_TEMPLATE, size: "1024x1024" as DalleSize, From c0c8cdbbf37fdde5df0fba4adf6fce477dded75b Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Wed, 9 Oct 2024 14:36:58 +0800 Subject: [PATCH 122/352] =?UTF-8?q?fix:=20[#5574]=20=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- README_CN.md | 6 +++--- README_JA.md | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c72c791b2..4d19bb325 100644 --- a/README.md +++ b/README.md @@ -332,9 +332,9 @@ To control custom models, use `+` to add a custom model, use `-` to hide a model User `-all` to disable all default models, `+all` to enable all default models. -For Azure: use `modelName@azure=deploymentName` to customize model name and deployment name. -> Example: `+gpt-3.5-turbo@azure=gpt35` will show option `gpt35(Azure)` in model list. -> If you only can use Azure model, `-all,+gpt-3.5-turbo@azure=gpt35` will `gpt35(Azure)` the only option in model list. +For Azure: use `modelName@Azure=deploymentName` to customize model name and deployment name. +> Example: `+gpt-3.5-turbo@Azure=gpt35` will show option `gpt35(Azure)` in model list. +> If you only can use Azure model, `-all,+gpt-3.5-turbo@Azure=gpt35` will `gpt35(Azure)` the only option in model list. For ByteDance: use `modelName@bytedance=deploymentName` to customize model name and deployment name. > Example: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` will show option `Doubao-lite-4k(ByteDance)` in model list. diff --git a/README_CN.md b/README_CN.md index c5d02477c..fa92f5f07 100644 --- a/README_CN.md +++ b/README_CN.md @@ -216,9 +216,9 @@ ByteDance Api Url. 用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。 -在Azure的模式下,支持使用`modelName@azure=deploymentName`的方式配置模型名称和部署名称(deploy-name) -> 示例:`+gpt-3.5-turbo@azure=gpt35`这个配置会在模型列表显示一个`gpt35(Azure)`的选项。 -> 如果你只能使用Azure模式,那么设置 `-all,+gpt-3.5-turbo@azure=gpt35` 则可以让对话的默认使用 `gpt35(Azure)` +在Azure的模式下,支持使用`modelName@Azure=deploymentName`的方式配置模型名称和部署名称(deploy-name) +> 示例:`+gpt-3.5-turbo@Azure=gpt35`这个配置会在模型列表显示一个`gpt35(Azure)`的选项。 +> 如果你只能使用Azure模式,那么设置 `-all,+gpt-3.5-turbo@Azure=gpt35` 则可以让对话的默认使用 `gpt35(Azure)` 在ByteDance的模式下,支持使用`modelName@bytedance=deploymentName`的方式配置模型名称和部署名称(deploy-name) > 示例: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx`这个配置会在模型列表显示一个`Doubao-lite-4k(ByteDance)`的选项 diff --git a/README_JA.md b/README_JA.md index 2b0a3ab78..24a28686f 100644 --- a/README_JA.md +++ b/README_JA.md @@ -207,8 +207,8 @@ ByteDance API の URL。 モデルリストを管理します。`+` でモデルを追加し、`-` でモデルを非表示にし、`モデル名=表示名` でモデルの表示名をカスタマイズし、カンマで区切ります。 -Azure モードでは、`modelName@azure=deploymentName` 形式でモデル名とデプロイ名(deploy-name)を設定できます。 -> 例:`+gpt-3.5-turbo@azure=gpt35` この設定でモデルリストに `gpt35(Azure)` のオプションが表示されます。 +Azure モードでは、`modelName@Azure=deploymentName` 形式でモデル名とデプロイ名(deploy-name)を設定できます。 +> 例:`+gpt-3.5-turbo@Azure=gpt35` この設定でモデルリストに `gpt35(Azure)` のオプションが表示されます。 ByteDance モードでは、`modelName@bytedance=deploymentName` 形式でモデル名とデプロイ名(deploy-name)を設定できます。 > 例: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` この設定でモデルリストに `Doubao-lite-4k(ByteDance)` のオプションが表示されます。 From 4e9bb51d2fe648b429861a20c4c1048fb59ee283 Mon Sep 17 00:00:00 2001 From: ElricLiu <20209191+ElricLiu@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:43:49 +0800 Subject: [PATCH 123/352] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c284e987..b3472291d 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ For enterprise inquiries, please contact: **business@nextchat.dev** 企业版咨询: **business@nextchat.dev** - + ## Features From 1dac02e4d63206213913f4abfe934f9c28727fb6 Mon Sep 17 00:00:00 2001 From: Lloyd Zhou Date: Wed, 9 Oct 2024 14:48:43 +0800 Subject: [PATCH 124/352] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b3472291d..aba21a510 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ For enterprise inquiries, please contact: **business@nextchat.dev** 企业版咨询: **business@nextchat.dev** - + ## Features From 3e63d405c1b85b62ad7762af64318f92b05ebeb7 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Wed, 9 Oct 2024 16:12:01 +0800 Subject: [PATCH 125/352] update --- app/utils/stream.ts | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/app/utils/stream.ts b/app/utils/stream.ts index 313ec4dd5..2eda768f3 100644 --- a/app/utils/stream.ts +++ b/app/utils/stream.ts @@ -28,7 +28,8 @@ export function fetch(url: string, options?: RequestInit): Promise { body = [], } = options || {}; let unlisten: Function | undefined; - let request_id = 0; + let setRequestId: Function | undefined; + const requestIdPromise = new Promise((resolve) => (setRequestId = resolve)); const ts = new TransformStream(); const writer = ts.writable.getWriter(); @@ -47,20 +48,22 @@ export function fetch(url: string, options?: RequestInit): Promise { } // @ts-ignore 2. listen response multi times, and write to Response.body window.__TAURI__.event - .listen("stream-response", (e: ResponseEvent) => { - const { request_id: rid, chunk, status } = e?.payload || {}; - if (request_id != rid) { - return; - } - if (chunk) { - writer.ready.then(() => { - writer.write(new Uint8Array(chunk)); - }); - } else if (status === 0) { - // end of body - close(); - } - }) + .listen("stream-response", (e: ResponseEvent) => + requestIdPromise.then((request_id) => { + const { request_id: rid, chunk, status } = e?.payload || {}; + if (request_id != rid) { + return; + } + if (chunk) { + writer.ready.then(() => { + writer.write(new Uint8Array(chunk)); + }); + } else if (status === 0) { + // end of body + close(); + } + }), + ) .then((u: Function) => (unlisten = u)); const headers: Record = { @@ -83,8 +86,8 @@ export function fetch(url: string, options?: RequestInit): Promise { : [], }) .then((res: StreamResponse) => { - request_id = res.request_id; - const { status, status_text: statusText, headers } = res; + const { request_id, status, status_text: statusText, headers } = res; + setRequestId?.(request_id); const response = new Response(ts.readable, { status, statusText, From 268cf3b6066d35f3f6d0acfbb066e07f2139c306 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 10 Oct 2024 12:47:25 +0800 Subject: [PATCH 126/352] hotfix plugin result is not string #5614 --- app/utils/chat.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/utils/chat.ts b/app/utils/chat.ts index 46f232638..cf9b7af41 100644 --- a/app/utils/chat.ts +++ b/app/utils/chat.ts @@ -223,6 +223,11 @@ export function stream( ) .then((res) => { let content = res.data || res?.statusText; + // hotfix #5614 + content = + typeof content === "string" + ? content + : JSON.stringify(content); if (res.status >= 300) { return Promise.reject(content); } From 2eebfcf6fe873595a69ac0de805811fa7ed94f12 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 11 Oct 2024 11:29:22 +0800 Subject: [PATCH 127/352] client app tauri updater #2966 --- app/components/settings.tsx | 16 ++++++++++++---- app/global.d.ts | 7 +++++++ app/locales/cn.ts | 2 ++ app/locales/en.ts | 2 ++ app/store/update.ts | 2 ++ app/utils.ts | 23 +++++++++++++++++++++++ 6 files changed, 48 insertions(+), 4 deletions(-) diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 9f338718e..9ee919b8d 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -49,7 +49,7 @@ import Locale, { changeLang, getLang, } from "../locales"; -import { copyToClipboard } from "../utils"; +import { copyToClipboard, clientUpdate } from "../utils"; import Link from "next/link"; import { Anthropic, @@ -1357,9 +1357,17 @@ export function Settings() { {checkingUpdate ? ( ) : hasNewVersion ? ( - - {Locale.Settings.Update.GoToUpdate} - + clientConfig?.isApp ? ( + } + text={Locale.Settings.Update.GoToUpdate} + onClick={() => clientUpdate()} + /> + ) : ( + + {Locale.Settings.Update.GoToUpdate} + + ) ) : ( } diff --git a/app/global.d.ts b/app/global.d.ts index 8ee636bcd..897871fec 100644 --- a/app/global.d.ts +++ b/app/global.d.ts @@ -26,6 +26,13 @@ declare interface Window { isPermissionGranted(): Promise; sendNotification(options: string | Options): void; }; + updater: { + checkUpdate(): Promise; + installUpdate(): Promise; + onUpdaterEvent( + handler: (status: UpdateStatusResult) => void, + ): Promise; + }; http: { fetch( url: string, diff --git a/app/locales/cn.ts b/app/locales/cn.ts index e5bcca0ed..3086cc63e 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -205,6 +205,8 @@ const cn = { IsChecking: "正在检查更新...", FoundUpdate: (x: string) => `发现新版本:${x}`, GoToUpdate: "前往更新", + Success: "Update Succesfull.", + Failed: "Update Failed.", }, SendKey: "发送键", Theme: "主题", diff --git a/app/locales/en.ts b/app/locales/en.ts index 120457522..a025a522f 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -207,6 +207,8 @@ const en: LocaleType = { IsChecking: "Checking update...", FoundUpdate: (x: string) => `Found new version: ${x}`, GoToUpdate: "Update", + Success: "Update Succesfull.", + Failed: "Update Failed.", }, SendKey: "Send Key", Theme: "Theme", diff --git a/app/store/update.ts b/app/store/update.ts index e68fde369..327dc5e88 100644 --- a/app/store/update.ts +++ b/app/store/update.ts @@ -6,6 +6,7 @@ import { } from "../constant"; import { getClientConfig } from "../config/client"; import { createPersistStore } from "../utils/store"; +import { clientUpdate } from "../utils"; import ChatGptIcon from "../icons/chatgpt.png"; import Locale from "../locales"; import { ClientApi } from "../client/api"; @@ -119,6 +120,7 @@ export const useUpdateStore = createPersistStore( icon: `${ChatGptIcon.src}`, sound: "Default", }); + clientUpdate(); } } }); diff --git a/app/utils.ts b/app/utils.ts index 5d4501710..9e75ffc7f 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -386,3 +386,26 @@ export function getOperationId(operation: { `${operation.method.toUpperCase()}${operation.path.replaceAll("/", "_")}` ); } + +export function clientUpdate() { + // this a wild for updating client app + return window.__TAURI__?.updater + .checkUpdate() + .then((updateResult) => { + if (updateResult.shouldUpdate) { + window.__TAURI__?.updater + .installUpdate() + .then((result) => { + showToast(Locale.Settings.Update.Success); + }) + .catch((e) => { + console.error("[Install Update Error]", e); + showToast(Locale.Settings.Update.Failed); + }); + } + }) + .catch((e) => { + console.error("[Check Update Error]", e); + showToast(Locale.Settings.Update.Failed); + }); +} From bd9de4dc4db95a6d9aca4567d6147cb5b55b38ab Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 11 Oct 2024 11:42:36 +0800 Subject: [PATCH 128/352] fix version compare --- app/components/settings.tsx | 4 ++-- app/utils.ts | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 9ee919b8d..c5653543c 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -49,7 +49,7 @@ import Locale, { changeLang, getLang, } from "../locales"; -import { copyToClipboard, clientUpdate } from "../utils"; +import { copyToClipboard, clientUpdate, semverCompare } from "../utils"; import Link from "next/link"; import { Anthropic, @@ -585,7 +585,7 @@ export function Settings() { const [checkingUpdate, setCheckingUpdate] = useState(false); const currentVersion = updateStore.formatVersion(updateStore.version); const remoteId = updateStore.formatVersion(updateStore.remoteVersion); - const hasNewVersion = currentVersion !== remoteId; + const hasNewVersion = semverCompare(currentVersion, remoteId) === -1; const updateUrl = getClientConfig()?.isApp ? RELEASE_URL : UPDATE_URL; function checkUpdate(force = false) { diff --git a/app/utils.ts b/app/utils.ts index 9e75ffc7f..9e9e90f66 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -409,3 +409,14 @@ export function clientUpdate() { showToast(Locale.Settings.Update.Failed); }); } + +// https://gist.github.com/iwill/a83038623ba4fef6abb9efca87ae9ccb +export function semverCompare(a, b) { + if (a.startsWith(b + "-")) return -1; + if (b.startsWith(a + "-")) return 1; + return a.localeCompare(b, undefined, { + numeric: true, + sensitivity: "case", + caseFirst: "upper", + }); +} From a0d4a04192e2221f0ca969cab3236d4090a85955 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 11 Oct 2024 11:52:24 +0800 Subject: [PATCH 129/352] update --- app/locales/en.ts | 2 +- app/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/locales/en.ts b/app/locales/en.ts index a025a522f..40471536f 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -207,7 +207,7 @@ const en: LocaleType = { IsChecking: "Checking update...", FoundUpdate: (x: string) => `Found new version: ${x}`, GoToUpdate: "Update", - Success: "Update Succesfull.", + Success: "Update Successful.", Failed: "Update Failed.", }, SendKey: "Send Key", diff --git a/app/utils.ts b/app/utils.ts index 9e9e90f66..d8fc46330 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -411,7 +411,7 @@ export function clientUpdate() { } // https://gist.github.com/iwill/a83038623ba4fef6abb9efca87ae9ccb -export function semverCompare(a, b) { +export function semverCompare(a: string, b: string) { if (a.startsWith(b + "-")) return -1; if (b.startsWith(a + "-")) return 1; return a.localeCompare(b, undefined, { From be98aa20785c852ec8338090ed12798d34a50fba Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Fri, 11 Oct 2024 15:17:38 +0800 Subject: [PATCH 130/352] chore: improve test --- .github/workflows/test.yml | 23 +++++++++++++++++++++++ package.json | 8 ++++---- 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..b2f37cfb4 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Run Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + cache: "yarn" + + - name: Install dependencies + run: yarn install + + - name: Run Jest tests + run: yarn test:ci diff --git a/package.json b/package.json index 6db49241f..e43344534 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,13 @@ "mask": "npx tsx app/masks/build.ts", "mask:watch": "npx watch \"yarn mask\" app/masks", "dev": "concurrently -r \"yarn run mask:watch\" \"next dev\"", - "build": "yarn test:ci && yarn mask && cross-env BUILD_MODE=standalone next build", + "build": "yarn mask && cross-env BUILD_MODE=standalone next build", "start": "next start", "lint": "next lint", - "export": "yarn test:ci && yarn mask && cross-env BUILD_MODE=export BUILD_APP=1 next build", + "export": "yarn mask && cross-env BUILD_MODE=export BUILD_APP=1 next build", "export:dev": "concurrently -r \"yarn mask:watch\" \"cross-env BUILD_MODE=export BUILD_APP=1 next dev\"", "app:dev": "concurrently -r \"yarn mask:watch\" \"yarn tauri dev\"", - "app:build": "yarn test:ci && yarn mask && yarn tauri build", + "app:build": "yarn mask && yarn tauri build", "prompts": "node ./scripts/fetch-prompts.mjs", "prepare": "husky install", "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev", @@ -88,4 +88,4 @@ "lint-staged/yaml": "^2.2.2" }, "packageManager": "yarn@1.22.19" -} \ No newline at end of file +} From bd43af3a8d57d06fd74ca6556641a7397e383684 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Fri, 11 Oct 2024 15:41:46 +0800 Subject: [PATCH 131/352] chore: cache node_modules --- .github/workflows/test.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b2f37cfb4..fc885b293 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,12 @@ name: Run Tests -on: [push, pull_request] +on: + push: + branches: + - main + tags: + - "!*" + pull_request: jobs: test: @@ -16,6 +22,14 @@ jobs: node-version: 18 cache: "yarn" + - name: Cache node_modules + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-node_modules- + - name: Install dependencies run: yarn install From c98dc31cdfd8bb92d11cc19cb577cb4d99356a72 Mon Sep 17 00:00:00 2001 From: code-october <148516338+code-october@users.noreply.github.com> Date: Fri, 11 Oct 2024 09:03:20 +0000 Subject: [PATCH 132/352] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AE=BF=E9=97=AE?= =?UTF-8?q?=E7=A0=81=E8=BE=93=E5=85=A5=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/auth.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/components/auth.tsx b/app/components/auth.tsx index e19512d87..6c5e75d79 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -11,6 +11,7 @@ import Logo from "../icons/logo.svg"; import { useMobileScreen } from "@/app/utils"; import BotIcon from "../icons/bot.svg"; import { getClientConfig } from "../config/client"; +import { PasswordInput } from "./ui-lib"; import LeftIcon from "@/app/icons/left.svg"; import { safeLocalStorage } from "@/app/utils"; import { @@ -60,17 +61,20 @@ export function AuthPage() {
{Locale.Auth.Title}
{Locale.Auth.Tips}
- { accessStore.update( (access) => (access.accessCode = e.currentTarget.value), ); }} /> + {!accessStore.hideUserApiKey ? ( <>
{Locale.Auth.SubTips}
From 4a7fd3a3803b229f6ecaeea03a0589017130470c Mon Sep 17 00:00:00 2001 From: code-october <148516338+code-october@users.noreply.github.com> Date: Fri, 11 Oct 2024 10:36:11 +0000 Subject: [PATCH 133/352] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=A6=96=E9=A1=B5=20?= =?UTF-8?q?api=20=E8=BE=93=E5=85=A5=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/auth.tsx | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 6c5e75d79..539a52eec 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -78,22 +78,26 @@ export function AuthPage() { {!accessStore.hideUserApiKey ? ( <>
{Locale.Auth.SubTips}
- { accessStore.update( (access) => (access.openaiApiKey = e.currentTarget.value), ); }} /> - { accessStore.update( (access) => (access.googleApiKey = e.currentTarget.value), From 6792d6e475756b188f90c1f56d19188eabb7b55f Mon Sep 17 00:00:00 2001 From: code-october <148516338+code-october@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:19:36 +0000 Subject: [PATCH 134/352] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E4=BD=BF=E8=83=BD/=E7=A6=81=E7=94=A8=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=8A=98=E5=8F=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/markdown.tsx | 10 ++++++++-- app/components/mask.tsx | 17 +++++++++++++++++ app/components/settings.tsx | 15 +++++++++++++++ app/locales/cn.ts | 4 ++++ app/locales/en.ts | 5 +++++ app/store/config.ts | 2 ++ app/store/mask.ts | 1 + 7 files changed, 52 insertions(+), 2 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index a25b8537b..22664df8a 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -169,6 +169,12 @@ export function PreCode(props: { children: any }) { } function CustomCode(props: { children: any; className?: string }) { + const chatStore = useChatStore(); + const session = chatStore.currentSession(); + const config = useAppConfig(); + const enableCodeFold = + session.mask?.enableCodeFold != false && config.enableCodeFold; + const ref = useRef(null); const [collapsed, setCollapsed] = useState(true); const [showToggle, setShowToggle] = useState(false); @@ -190,13 +196,13 @@ function CustomCode(props: { children: any; className?: string }) { className={props?.className} ref={ref} style={{ - maxHeight: collapsed ? "400px" : "none", + maxHeight: enableCodeFold && collapsed ? "400px" : "none", overflowY: "hidden", }} > {props.children} - {showToggle && collapsed && ( + {showToggle && enableCodeFold && collapsed && (
diff --git a/app/components/mask.tsx b/app/components/mask.tsx index c60e7a528..12b19e335 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -183,6 +183,23 @@ export function MaskConfig(props: { > )} + {globalConfig.enableCodeFold && ( + + { + props.updateMask((mask) => { + mask.enableCodeFold = e.currentTarget.checked; + }); + }} + > + + )} {!props.shouldSyncFromGlobal ? ( + + + updateConfig( + (config) => (config.enableCodeFold = e.currentTarget.checked), + ) + } + > + diff --git a/app/locales/cn.ts b/app/locales/cn.ts index e5bcca0ed..3ba8dd80b 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -665,6 +665,10 @@ const cn = { Title: "启用Artifacts", SubTitle: "启用之后可以直接渲染HTML页面", }, + CodeFold: { + Title: "启用CodeFold", + SubTitle: "启用之后可以折叠/展开过长的代码块", + }, Share: { Title: "分享此面具", SubTitle: "生成此面具的直达链接", diff --git a/app/locales/en.ts b/app/locales/en.ts index 120457522..40782be7a 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -675,6 +675,11 @@ const en: LocaleType = { Title: "Enable Artifacts", SubTitle: "Can render HTML page when enable artifacts.", }, + CodeFold: { + Title: "Enable CodeFold", + SubTitle: + "Automatically collapse/expand overly long code block when enable CodeFold", + }, Share: { Title: "Share This Mask", SubTitle: "Generate a link to this mask", diff --git a/app/store/config.ts b/app/store/config.ts index f9ddce4a8..f14793c28 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -52,6 +52,8 @@ export const DEFAULT_CONFIG = { enableArtifacts: true, // show artifacts config + enableCodeFold: true, // code fold config + disablePromptHint: false, dontShowMaskSplashScreen: false, // dont show splash screen when create chat diff --git a/app/store/mask.ts b/app/store/mask.ts index 0c74a892e..850abeef6 100644 --- a/app/store/mask.ts +++ b/app/store/mask.ts @@ -19,6 +19,7 @@ export type Mask = { builtin: boolean; plugin?: string[]; enableArtifacts?: boolean; + enableCodeFold?: boolean; }; export const DEFAULT_MASK_STATE = { From 8fd843d228e75068c1233738d4ce6507e2bddc4c Mon Sep 17 00:00:00 2001 From: code-october <148516338+code-october@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:38:52 +0000 Subject: [PATCH 135/352] =?UTF-8?q?=E5=8F=82=E8=80=83coderabbitai=E5=BB=BA?= =?UTF-8?q?=E8=AE=AE=E8=A7=84=E8=8C=83=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/markdown.tsx | 19 ++++++++++++------- app/components/settings.tsx | 1 + app/locales/cn.ts | 4 ++-- app/locales/en.ts | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 22664df8a..8e744b441 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -190,6 +190,16 @@ function CustomCode(props: { children: any; className?: string }) { const toggleCollapsed = () => { setCollapsed((collapsed) => !collapsed); }; + const renderShowMoreButton = () => { + if (showToggle && enableCodeFold && collapsed) { + return ( +
+ +
+ ); + } + return null; + }; return ( <> {props.children} - {showToggle && enableCodeFold && collapsed && ( -
- -
- )} + + {renderShowMoreButton()} ); } diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 5f478374e..e24644813 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -1517,6 +1517,7 @@ export function Settings() { aria-label={Locale.Mask.Config.CodeFold.Title} type="checkbox" checked={config.enableCodeFold} + data-testid="enable-code-fold-checkbox" onChange={(e) => updateConfig( (config) => (config.enableCodeFold = e.currentTarget.checked), diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 3ba8dd80b..09eafe492 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -666,8 +666,8 @@ const cn = { SubTitle: "启用之后可以直接渲染HTML页面", }, CodeFold: { - Title: "启用CodeFold", - SubTitle: "启用之后可以折叠/展开过长的代码块", + Title: "启用代码折叠", + SubTitle: "启用之后可以自动折叠/展开过长的代码块", }, Share: { Title: "分享此面具", diff --git a/app/locales/en.ts b/app/locales/en.ts index 40782be7a..8dfe8ed93 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -678,7 +678,7 @@ const en: LocaleType = { CodeFold: { Title: "Enable CodeFold", SubTitle: - "Automatically collapse/expand overly long code block when enable CodeFold", + "Automatically collapse/expand overly long code blocks when CodeFold is enabled", }, Share: { Title: "Share This Mask", From 4a1319f2c0db25e5609553e2938761a4455ff6b8 Mon Sep 17 00:00:00 2001 From: code-october <148516338+code-october@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:57:23 +0000 Subject: [PATCH 136/352] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/markdown.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 8e744b441..9841a196d 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -173,7 +173,7 @@ function CustomCode(props: { children: any; className?: string }) { const session = chatStore.currentSession(); const config = useAppConfig(); const enableCodeFold = - session.mask?.enableCodeFold != false && config.enableCodeFold; + session.mask?.enableCodeFold !== false && config.enableCodeFold; const ref = useRef(null); const [collapsed, setCollapsed] = useState(true); @@ -212,7 +212,7 @@ function CustomCode(props: { children: any; className?: string }) { > {props.children} - + {renderShowMoreButton()} ); From 819238acaf5114329168f2c95da74d747795daa1 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Fri, 11 Oct 2024 20:49:43 +0800 Subject: [PATCH 137/352] fix: i18n --- app/locales/cn.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 3086cc63e..1e0116ec9 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -205,8 +205,8 @@ const cn = { IsChecking: "正在检查更新...", FoundUpdate: (x: string) => `发现新版本:${x}`, GoToUpdate: "前往更新", - Success: "Update Succesfull.", - Failed: "Update Failed.", + Success: "更新成功!", + Failed: "更新失败", }, SendKey: "发送键", Theme: "主题", From 9961b513cc0bfa1db8e1865af4099fdd9b78c15d Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Sat, 12 Oct 2024 14:54:22 +0800 Subject: [PATCH 138/352] fix: sidebar style --- app/components/home.module.scss | 3 +++ app/components/sidebar.tsx | 11 +++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/components/home.module.scss b/app/components/home.module.scss index b31334568..381b6a9b9 100644 --- a/app/components/home.module.scss +++ b/app/components/home.module.scss @@ -140,6 +140,9 @@ display: flex; justify-content: space-between; align-items: center; + &-narrow { + justify-content: center; + } } .sidebar-logo { diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 493b1103b..2a5c308b7 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -165,11 +165,17 @@ export function SideBarHeader(props: { subTitle?: string | React.ReactNode; logo?: React.ReactNode; children?: React.ReactNode; + shouldNarrow?: boolean; }) { - const { title, subTitle, logo, children } = props; + const { title, subTitle, logo, children, shouldNarrow } = props; return ( -
+
{title} @@ -227,6 +233,7 @@ export function SideBar(props: { className?: string }) { title="NextChat" subTitle="Build your own AI assistant." logo={} + shouldNarrow={shouldNarrow} >
Date: Sat, 12 Oct 2024 16:49:24 +0000 Subject: [PATCH 139/352] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E2=80=9C=E5=8E=8B?= =?UTF-8?q?=E7=BC=A9=E6=A8=A1=E5=9E=8B=E2=80=9D=E5=90=8D=E7=A7=B0=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E2=80=9C=E7=94=9F=E6=88=90=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E6=A0=87=E9=A2=98=E2=80=9D=E7=9A=84=E5=8A=9F=E8=83=BD=E6=8F=90?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/locales/cn.ts | 4 ++-- app/locales/en.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 09eafe492..b7debe805 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -495,8 +495,8 @@ const cn = { Model: "模型 (model)", CompressModel: { - Title: "压缩模型", - SubTitle: "用于压缩历史记录的模型", + Title: "对话摘要模型", + SubTitle: "用于压缩历史记录、生成对话标题的模型", }, Temperature: { Title: "随机性 (temperature)", diff --git a/app/locales/en.ts b/app/locales/en.ts index 8dfe8ed93..5cc296f1e 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -500,8 +500,8 @@ const en: LocaleType = { Model: "Model", CompressModel: { - Title: "Compression Model", - SubTitle: "Model used to compress history", + Title: "Summary Model", + SubTitle: "Model used to compress history and generate title", }, Temperature: { Title: "Temperature", From 12e7caa20977d8fbee2d16ec3d33ae3d94472603 Mon Sep 17 00:00:00 2001 From: ccq18 <348578429@qq.com> Date: Mon, 14 Oct 2024 16:03:01 +0800 Subject: [PATCH 140/352] =?UTF-8?q?fix=20=E9=BB=98=E8=AE=A4=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E6=97=B6=E9=97=B4=E6=94=B9=E4=B8=BA3=E5=88=86?= =?UTF-8?q?=E9=92=9F=EF=BC=8C=E6=94=AF=E6=8C=81o1-mini?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/constant.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/constant.ts b/app/constant.ts index a06b8f050..856370511 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -95,7 +95,7 @@ export const UNFINISHED_INPUT = (id: string) => "unfinished-input-" + id; export const STORAGE_KEY = "chatgpt-next-web"; -export const REQUEST_TIMEOUT_MS = 60000; +export const REQUEST_TIMEOUT_MS = 180000; export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown"; From 592f62005b474b78042a373cbfd7361f3de9b323 Mon Sep 17 00:00:00 2001 From: ccq18 <348578429@qq.com> Date: Mon, 14 Oct 2024 16:31:17 +0800 Subject: [PATCH 141/352] =?UTF-8?q?=E4=BB=85=E4=BF=AE=E6=94=B9o1=E7=9A=84?= =?UTF-8?q?=E8=B6=85=E6=97=B6=E6=97=B6=E9=97=B4=E4=B8=BA4=E5=88=86?= =?UTF-8?q?=E9=92=9F=EF=BC=8C=E5=87=8F=E5=B0=91o1=E7=B3=BB=E5=88=97?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E8=AF=B7=E6=B1=82=E5=A4=B1=E8=B4=A5=E7=9A=84?= =?UTF-8?q?=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/client/platforms/openai.ts | 2 +- app/constant.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index a22633611..76bac59e8 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -352,7 +352,7 @@ export class ChatGPTApi implements LLMApi { // make a fetch request const requestTimeoutId = setTimeout( () => controller.abort(), - isDalle3 || isO1 ? REQUEST_TIMEOUT_MS * 2 : REQUEST_TIMEOUT_MS, // dalle3 using b64_json is slow. + isDalle3 || isO1 ? REQUEST_TIMEOUT_MS * 4 : REQUEST_TIMEOUT_MS, // dalle3 using b64_json is slow. ); const res = await fetch(chatPath, chatPayload); diff --git a/app/constant.ts b/app/constant.ts index 856370511..a06b8f050 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -95,7 +95,7 @@ export const UNFINISHED_INPUT = (id: string) => "unfinished-input-" + id; export const STORAGE_KEY = "chatgpt-next-web"; -export const REQUEST_TIMEOUT_MS = 180000; +export const REQUEST_TIMEOUT_MS = 60000; export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown"; From 8c39a687b5d8bb44dfcc103ac69910d97220bea7 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 14 Oct 2024 16:53:46 +0800 Subject: [PATCH 142/352] update deploy_preview run target --- .github/workflows/deploy_preview.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/deploy_preview.yml b/.github/workflows/deploy_preview.yml index 30d9b85b4..b98845243 100644 --- a/.github/workflows/deploy_preview.yml +++ b/.github/workflows/deploy_preview.yml @@ -3,9 +3,7 @@ name: VercelPreviewDeployment on: pull_request_target: types: - - opened - - synchronize - - reopened + - review_requested env: VERCEL_TEAM: ${{ secrets.VERCEL_TEAM }} From 103106bb9371748b3f1ab3f304846546297657cc Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 14 Oct 2024 17:09:56 +0800 Subject: [PATCH 143/352] update test run target --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fc885b293..faf7205d9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,6 +7,8 @@ on: tags: - "!*" pull_request: + types: + - review_requested jobs: test: From 7f454cbcec0d5c735726808ccbe75ba897142a31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 10:49:46 +0000 Subject: [PATCH 144/352] Bump @types/jest from 29.5.12 to 29.5.13 Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.12 to 29.5.13. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) --- updated-dependencies: - dependency-name: "@types/jest" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e43344534..803c0d1a4 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "@tauri-apps/cli": "1.5.11", "@testing-library/jest-dom": "^6.4.8", "@testing-library/react": "^16.0.0", - "@types/jest": "^29.5.12", + "@types/jest": "^29.5.13", "@types/js-yaml": "4.0.9", "@types/lodash-es": "^4.17.12", "@types/node": "^20.11.30", diff --git a/yarn.lock b/yarn.lock index 0f6a29d6d..ef296924e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2263,10 +2263,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.5.12": - version "29.5.12" - resolved "https://registry.npmmirror.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544" - integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== +"@types/jest@^29.5.13": + version "29.5.13" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.13.tgz#8bc571659f401e6a719a7bf0dbcb8b78c71a8adc" + integrity sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg== dependencies: expect "^29.0.0" pretty-format "^29.0.0" From 87d85c10c3b3dcd7a62e174dabb6338f005ce9b7 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 14 Oct 2024 21:48:36 +0800 Subject: [PATCH 145/352] update --- src-tauri/tauri.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index cc137ee8a..56a5b46a9 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -99,7 +99,7 @@ "endpoints": [ "https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/releases/latest/download/latest.json" ], - "dialog": false, + "dialog": true, "windows": { "installMode": "passive" }, From 463fa743e987624ab357c4335de0b133ccfa8fd0 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 15 Oct 2024 16:10:44 +0800 Subject: [PATCH 146/352] update version --- src-tauri/tauri.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 56a5b46a9..b2c3e04b0 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "NextChat", - "version": "2.15.4" + "version": "2.15.5" }, "tauri": { "allowlist": { From deb1e76c41ec156450db10872f88f84d2865d450 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Wed, 16 Oct 2024 21:57:07 +0800 Subject: [PATCH 147/352] fix: use tauri fetch --- app/client/platforms/anthropic.ts | 1 + app/client/platforms/moonshot.ts | 1 + app/client/platforms/openai.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/app/client/platforms/anthropic.ts b/app/client/platforms/anthropic.ts index 1a83bd53a..3645cbe6e 100644 --- a/app/client/platforms/anthropic.ts +++ b/app/client/platforms/anthropic.ts @@ -13,6 +13,7 @@ import { getMessageTextContent, isVisionModel } from "@/app/utils"; import { preProcessImageContent, stream } from "@/app/utils/chat"; import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare"; import { RequestPayload } from "./openai"; +import { fetch } from "@/app/utils/stream"; export type MultiBlockContent = { type: "image" | "text"; diff --git a/app/client/platforms/moonshot.ts b/app/client/platforms/moonshot.ts index e0ef3494f..22a34b2e2 100644 --- a/app/client/platforms/moonshot.ts +++ b/app/client/platforms/moonshot.ts @@ -24,6 +24,7 @@ import { import { getClientConfig } from "@/app/config/client"; import { getMessageTextContent } from "@/app/utils"; import { RequestPayload } from "./openai"; +import { fetch } from "@/app/utils/stream"; export class MoonshotApi implements LLMApi { private disableListModels = true; diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 76bac59e8..30f7415c1 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -42,6 +42,7 @@ import { isVisionModel, isDalle3 as _isDalle3, } from "@/app/utils"; +import { fetch } from "@/app/utils/stream"; export interface OpenAIListModelResponse { object: string; From 8455fefc8aeada7e4204099f1548908320c4c1b7 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Wed, 23 Oct 2024 11:40:06 +0800 Subject: [PATCH 148/352] add xai --- app/api/[provider]/[...path]/route.ts | 3 + app/api/auth.ts | 5 +- app/api/xai.ts | 128 +++++++++++++++++ app/client/api.ts | 10 ++ app/client/platforms/xai.ts | 195 ++++++++++++++++++++++++++ app/components/settings.tsx | 41 ++++++ app/config/server.ts | 9 ++ app/constant.ts | 23 +++ app/locales/cn.ts | 11 ++ app/locales/en.ts | 11 ++ app/store/access.ts | 12 ++ 11 files changed, 447 insertions(+), 1 deletion(-) create mode 100644 app/api/xai.ts create mode 100644 app/client/platforms/xai.ts diff --git a/app/api/[provider]/[...path]/route.ts b/app/api/[provider]/[...path]/route.ts index dffb3e9da..5ac248d0c 100644 --- a/app/api/[provider]/[...path]/route.ts +++ b/app/api/[provider]/[...path]/route.ts @@ -10,6 +10,7 @@ import { handle as alibabaHandler } from "../../alibaba"; import { handle as moonshotHandler } from "../../moonshot"; import { handle as stabilityHandler } from "../../stability"; import { handle as iflytekHandler } from "../../iflytek"; +import { handle as xaiHandler } from "../../xai"; import { handle as proxyHandler } from "../../proxy"; async function handle( @@ -38,6 +39,8 @@ async function handle( return stabilityHandler(req, { params }); case ApiPath.Iflytek: return iflytekHandler(req, { params }); + case ApiPath.XAI: + return xaiHandler(req, { params }); case ApiPath.OpenAI: return openaiHandler(req, { params }); default: diff --git a/app/api/auth.ts b/app/api/auth.ts index 95965ceec..fb147cf51 100644 --- a/app/api/auth.ts +++ b/app/api/auth.ts @@ -92,6 +92,9 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) { systemApiKey = serverConfig.iflytekApiKey + ":" + serverConfig.iflytekApiSecret; break; + case ModelProvider.XAI: + systemApiKey = serverConfig.xaiApiKey; + break; case ModelProvider.GPT: default: if (req.nextUrl.pathname.includes("azure/deployments")) { @@ -102,7 +105,7 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) { } if (systemApiKey) { - console.log("[Auth] use system api key"); + console.log("[Auth] use system api key", systemApiKey); req.headers.set("Authorization", `Bearer ${systemApiKey}`); } else { console.log("[Auth] admin did not provide an api key"); diff --git a/app/api/xai.ts b/app/api/xai.ts new file mode 100644 index 000000000..a4ee8b397 --- /dev/null +++ b/app/api/xai.ts @@ -0,0 +1,128 @@ +import { getServerSideConfig } from "@/app/config/server"; +import { + XAI_BASE_URL, + ApiPath, + ModelProvider, + ServiceProvider, +} from "@/app/constant"; +import { prettyObject } from "@/app/utils/format"; +import { NextRequest, NextResponse } from "next/server"; +import { auth } from "@/app/api/auth"; +import { isModelAvailableInServer } from "@/app/utils/model"; + +const serverConfig = getServerSideConfig(); + +export async function handle( + req: NextRequest, + { params }: { params: { path: string[] } }, +) { + console.log("[XAI Route] params ", params); + + if (req.method === "OPTIONS") { + return NextResponse.json({ body: "OK" }, { status: 200 }); + } + + const authResult = auth(req, ModelProvider.XAI); + if (authResult.error) { + return NextResponse.json(authResult, { + status: 401, + }); + } + + try { + const response = await request(req); + return response; + } catch (e) { + console.error("[XAI] ", e); + return NextResponse.json(prettyObject(e)); + } +} + +async function request(req: NextRequest) { + const controller = new AbortController(); + + // alibaba use base url or just remove the path + let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.XAI, ""); + + let baseUrl = serverConfig.xaiUrl || XAI_BASE_URL; + + if (!baseUrl.startsWith("http")) { + baseUrl = `https://${baseUrl}`; + } + + if (baseUrl.endsWith("/")) { + baseUrl = baseUrl.slice(0, -1); + } + + console.log("[Proxy] ", path); + console.log("[Base Url]", baseUrl); + + const timeoutId = setTimeout( + () => { + controller.abort(); + }, + 10 * 60 * 1000, + ); + + const fetchUrl = `${baseUrl}${path}`; + const fetchOptions: RequestInit = { + headers: { + "Content-Type": "application/json", + Authorization: req.headers.get("Authorization") ?? "", + }, + method: req.method, + body: req.body, + redirect: "manual", + // @ts-ignore + duplex: "half", + signal: controller.signal, + }; + + // #1815 try to refuse some request to some models + if (serverConfig.customModels && req.body) { + try { + const clonedBody = await req.text(); + fetchOptions.body = clonedBody; + + const jsonBody = JSON.parse(clonedBody) as { model?: string }; + + // not undefined and is false + if ( + isModelAvailableInServer( + serverConfig.customModels, + jsonBody?.model as string, + ServiceProvider.XAI as string, + ) + ) { + return NextResponse.json( + { + error: true, + message: `you are not allowed to use ${jsonBody?.model} model`, + }, + { + status: 403, + }, + ); + } + } catch (e) { + console.error(`[XAI] filter`, e); + } + } + try { + const res = await fetch(fetchUrl, fetchOptions); + + // to prevent browser prompt for credentials + const newHeaders = new Headers(res.headers); + newHeaders.delete("www-authenticate"); + // to disable nginx buffering + newHeaders.set("X-Accel-Buffering", "no"); + + return new Response(res.body, { + status: res.status, + statusText: res.statusText, + headers: newHeaders, + }); + } finally { + clearTimeout(timeoutId); + } +} diff --git a/app/client/api.ts b/app/client/api.ts index 7a242ea99..4238c2a26 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -20,6 +20,7 @@ import { QwenApi } from "./platforms/alibaba"; import { HunyuanApi } from "./platforms/tencent"; import { MoonshotApi } from "./platforms/moonshot"; import { SparkApi } from "./platforms/iflytek"; +import { XAIApi } from "./platforms/xai"; export const ROLES = ["system", "user", "assistant"] as const; export type MessageRole = (typeof ROLES)[number]; @@ -152,6 +153,9 @@ export class ClientApi { case ModelProvider.Iflytek: this.llm = new SparkApi(); break; + case ModelProvider.XAI: + this.llm = new XAIApi(); + break; default: this.llm = new ChatGPTApi(); } @@ -239,6 +243,7 @@ export function getHeaders(ignoreHeaders: boolean = false) { const isAlibaba = modelConfig.providerName === ServiceProvider.Alibaba; const isMoonshot = modelConfig.providerName === ServiceProvider.Moonshot; const isIflytek = modelConfig.providerName === ServiceProvider.Iflytek; + const isXAI = modelConfig.providerName === ServiceProvider.XAI; const isEnabledAccessControl = accessStore.enabledAccessControl(); const apiKey = isGoogle ? accessStore.googleApiKey @@ -252,6 +257,8 @@ export function getHeaders(ignoreHeaders: boolean = false) { ? accessStore.alibabaApiKey : isMoonshot ? accessStore.moonshotApiKey + : isXAI + ? accessStore.xaiApiKey : isIflytek ? accessStore.iflytekApiKey && accessStore.iflytekApiSecret ? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret @@ -266,6 +273,7 @@ export function getHeaders(ignoreHeaders: boolean = false) { isAlibaba, isMoonshot, isIflytek, + isXAI, apiKey, isEnabledAccessControl, }; @@ -328,6 +336,8 @@ export function getClientApi(provider: ServiceProvider): ClientApi { return new ClientApi(ModelProvider.Moonshot); case ServiceProvider.Iflytek: return new ClientApi(ModelProvider.Iflytek); + case ServiceProvider.XAI: + return new ClientApi(ModelProvider.XAI); default: return new ClientApi(ModelProvider.GPT); } diff --git a/app/client/platforms/xai.ts b/app/client/platforms/xai.ts new file mode 100644 index 000000000..69f80e9fc --- /dev/null +++ b/app/client/platforms/xai.ts @@ -0,0 +1,195 @@ +"use client"; +// azure and openai, using same models. so using same LLMApi. +import { ApiPath, XAI_BASE_URL, XAI, REQUEST_TIMEOUT_MS } from "@/app/constant"; +import { + useAccessStore, + useAppConfig, + useChatStore, + ChatMessageTool, + usePluginStore, +} from "@/app/store"; +import { stream } from "@/app/utils/chat"; +import { + ChatOptions, + getHeaders, + LLMApi, + LLMModel, + SpeechOptions, +} from "../api"; +import { getClientConfig } from "@/app/config/client"; +import { getMessageTextContent } from "@/app/utils"; +import { RequestPayload } from "./openai"; +import { fetch } from "@/app/utils/stream"; + +export class XAIApi implements LLMApi { + private disableListModels = true; + + path(path: string): string { + const accessStore = useAccessStore.getState(); + + let baseUrl = ""; + + if (accessStore.useCustomConfig) { + baseUrl = accessStore.xaiUrl; + } + + if (baseUrl.length === 0) { + const isApp = !!getClientConfig()?.isApp; + const apiPath = ApiPath.XAI; + baseUrl = isApp ? XAI_BASE_URL : apiPath; + } + + if (baseUrl.endsWith("/")) { + baseUrl = baseUrl.slice(0, baseUrl.length - 1); + } + if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.XAI)) { + baseUrl = "https://" + baseUrl; + } + + console.log("[Proxy Endpoint] ", baseUrl, path); + + return [baseUrl, path].join("/"); + } + + extractMessage(res: any) { + return res.choices?.at(0)?.message?.content ?? ""; + } + + speech(options: SpeechOptions): Promise { + throw new Error("Method not implemented."); + } + + async chat(options: ChatOptions) { + const messages: ChatOptions["messages"] = []; + for (const v of options.messages) { + const content = getMessageTextContent(v); + messages.push({ role: v.role, content }); + } + + const modelConfig = { + ...useAppConfig.getState().modelConfig, + ...useChatStore.getState().currentSession().mask.modelConfig, + ...{ + model: options.config.model, + providerName: options.config.providerName, + }, + }; + + const requestPayload: RequestPayload = { + messages, + stream: options.config.stream, + model: modelConfig.model, + temperature: modelConfig.temperature, + presence_penalty: modelConfig.presence_penalty, + frequency_penalty: modelConfig.frequency_penalty, + top_p: modelConfig.top_p, + // max_tokens: Math.max(modelConfig.max_tokens, 1024), + // Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore. + }; + + console.log("[Request] openai payload: ", requestPayload); + + const shouldStream = !!options.config.stream; + const controller = new AbortController(); + options.onController?.(controller); + + try { + const chatPath = this.path(XAI.ChatPath); + const chatPayload = { + method: "POST", + body: JSON.stringify(requestPayload), + signal: controller.signal, + headers: getHeaders(), + }; + + // make a fetch request + const requestTimeoutId = setTimeout( + () => controller.abort(), + REQUEST_TIMEOUT_MS, + ); + + if (shouldStream) { + const [tools, funcs] = usePluginStore + .getState() + .getAsTools( + useChatStore.getState().currentSession().mask?.plugin || [], + ); + return stream( + chatPath, + requestPayload, + getHeaders(), + tools as any, + funcs, + controller, + // parseSSE + (text: string, runTools: ChatMessageTool[]) => { + // console.log("parseSSE", text, runTools); + const json = JSON.parse(text); + const choices = json.choices as Array<{ + delta: { + content: string; + tool_calls: ChatMessageTool[]; + }; + }>; + const tool_calls = choices[0]?.delta?.tool_calls; + if (tool_calls?.length > 0) { + const index = tool_calls[0]?.index; + const id = tool_calls[0]?.id; + const args = tool_calls[0]?.function?.arguments; + if (id) { + runTools.push({ + id, + type: tool_calls[0]?.type, + function: { + name: tool_calls[0]?.function?.name as string, + arguments: args, + }, + }); + } else { + // @ts-ignore + runTools[index]["function"]["arguments"] += args; + } + } + return choices[0]?.delta?.content; + }, + // processToolMessage, include tool_calls message and tool call results + ( + requestPayload: RequestPayload, + toolCallMessage: any, + toolCallResult: any[], + ) => { + // @ts-ignore + requestPayload?.messages?.splice( + // @ts-ignore + requestPayload?.messages?.length, + 0, + toolCallMessage, + ...toolCallResult, + ); + }, + options, + ); + } else { + const res = await fetch(chatPath, chatPayload); + clearTimeout(requestTimeoutId); + + const resJson = await res.json(); + const message = this.extractMessage(resJson); + options.onFinish(message); + } + } catch (e) { + console.log("[Request] failed to make a chat request", e); + options.onError?.(e as Error); + } + } + async usage() { + return { + used: 0, + total: 0, + }; + } + + async models(): Promise { + return []; + } +} diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 82ce70e5a..6ce71b5ef 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -59,6 +59,7 @@ import { ByteDance, Alibaba, Moonshot, + XAI, Google, GoogleSafetySettingsThreshold, OPENAI_BASE_URL, @@ -1194,6 +1195,45 @@ export function Settings() { ); + const XAIConfigComponent = accessStore.provider === ServiceProvider.XAI && ( + <> + + + accessStore.update( + (access) => (access.moonshotUrl = e.currentTarget.value), + ) + } + > + + + { + accessStore.update( + (access) => (access.moonshotApiKey = e.currentTarget.value), + ); + }} + /> + + + ); + const stabilityConfigComponent = accessStore.provider === ServiceProvider.Stability && ( <> @@ -1652,6 +1692,7 @@ export function Settings() { {moonshotConfigComponent} {stabilityConfigComponent} {lflytekConfigComponent} + {XAIConfigComponent} )} diff --git a/app/config/server.ts b/app/config/server.ts index 6544fe564..eac4ba0cf 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -71,6 +71,10 @@ declare global { IFLYTEK_API_KEY?: string; IFLYTEK_API_SECRET?: string; + // xai only + XAI_URL?: string; + XAI_API_KEY?: string; + // custom template for preprocessing user input DEFAULT_INPUT_TEMPLATE?: string; } @@ -146,6 +150,7 @@ export const getServerSideConfig = () => { const isAlibaba = !!process.env.ALIBABA_API_KEY; const isMoonshot = !!process.env.MOONSHOT_API_KEY; const isIflytek = !!process.env.IFLYTEK_API_KEY; + const isXAI = !!process.env.XAI_API_KEY; // const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? ""; // const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim()); // const randomIndex = Math.floor(Math.random() * apiKeys.length); @@ -208,6 +213,10 @@ export const getServerSideConfig = () => { iflytekApiKey: process.env.IFLYTEK_API_KEY, iflytekApiSecret: process.env.IFLYTEK_API_SECRET, + isXAI, + xaiUrl: process.env.XAI_URL, + xaiApiKey: getApiKey(process.env.XAI_API_KEY), + cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID, cloudflareKVNamespaceId: process.env.CLOUDFLARE_KV_NAMESPACE_ID, cloudflareKVApiKey: getApiKey(process.env.CLOUDFLARE_KV_API_KEY), diff --git a/app/constant.ts b/app/constant.ts index a06b8f050..9774bb594 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -28,6 +28,8 @@ export const TENCENT_BASE_URL = "https://hunyuan.tencentcloudapi.com"; export const MOONSHOT_BASE_URL = "https://api.moonshot.cn"; export const IFLYTEK_BASE_URL = "https://spark-api-open.xf-yun.com"; +export const XAI_BASE_URL = "https://api.x.ai"; + export const CACHE_URL_PREFIX = "/api/cache"; export const UPLOAD_URL = `${CACHE_URL_PREFIX}/upload`; @@ -59,6 +61,7 @@ export enum ApiPath { Iflytek = "/api/iflytek", Stability = "/api/stability", Artifacts = "/api/artifacts", + XAI = "/api/xai", } export enum SlotID { @@ -111,6 +114,7 @@ export enum ServiceProvider { Moonshot = "Moonshot", Stability = "Stability", Iflytek = "Iflytek", + XAI = "XAI", } // Google API safety settings, see https://ai.google.dev/gemini-api/docs/safety-settings @@ -133,6 +137,7 @@ export enum ModelProvider { Hunyuan = "Hunyuan", Moonshot = "Moonshot", Iflytek = "Iflytek", + XAI = "XAI", } export const Stability = { @@ -215,6 +220,11 @@ export const Iflytek = { ChatPath: "v1/chat/completions", }; +export const XAI = { + ExampleEndpoint: XAI_BASE_URL, + ChatPath: "v1/chat/completions", +}; + export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang // export const DEFAULT_SYSTEM_TEMPLATE = ` // You are ChatGPT, a large language model trained by {{ServiceProvider}}. @@ -364,6 +374,8 @@ const iflytekModels = [ "4.0Ultra", ]; +const xAIModes = ["grok-beta"]; + let seq = 1000; // 内置的模型序号生成器从1000开始 export const DEFAULT_MODELS = [ ...openaiModels.map((name) => ({ @@ -476,6 +488,17 @@ export const DEFAULT_MODELS = [ sorted: 10, }, })), + ...xAIModes.map((name) => ({ + name, + available: true, + sorted: seq++, + provider: { + id: "xai", + providerName: "XAI", + providerType: "xai", + sorted: 11, + }, + })), ] as const; export const CHAT_PAGE_SIZE = 15; diff --git a/app/locales/cn.ts b/app/locales/cn.ts index e514eb4fe..006fc8162 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -462,6 +462,17 @@ const cn = { SubTitle: "样例:", }, }, + XAI: { + ApiKey: { + Title: "接口密钥", + SubTitle: "使用自定义XAI API Key", + Placeholder: "XAI API Key", + }, + Endpoint: { + Title: "接口地址", + SubTitle: "样例:", + }, + }, Stability: { ApiKey: { Title: "接口密钥", diff --git a/app/locales/en.ts b/app/locales/en.ts index c86cc08f0..7204bd946 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -446,6 +446,17 @@ const en: LocaleType = { SubTitle: "Example: ", }, }, + XAI: { + ApiKey: { + Title: "XAI API Key", + SubTitle: "Use a custom XAI API Key", + Placeholder: "XAI API Key", + }, + Endpoint: { + Title: "Endpoint Address", + SubTitle: "Example: ", + }, + }, Stability: { ApiKey: { Title: "Stability API Key", diff --git a/app/store/access.ts b/app/store/access.ts index dec3a7258..1a27deb1c 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -13,6 +13,7 @@ import { MOONSHOT_BASE_URL, STABILITY_BASE_URL, IFLYTEK_BASE_URL, + XAI_BASE_URL, } from "../constant"; import { getHeaders } from "../client/api"; import { getClientConfig } from "../config/client"; @@ -44,6 +45,8 @@ const DEFAULT_STABILITY_URL = isApp ? STABILITY_BASE_URL : ApiPath.Stability; const DEFAULT_IFLYTEK_URL = isApp ? IFLYTEK_BASE_URL : ApiPath.Iflytek; +const DEFAULT_XAI_URL = isApp ? XAI_BASE_URL : ApiPath.XAI; + const DEFAULT_ACCESS_STATE = { accessCode: "", useCustomConfig: false, @@ -101,6 +104,10 @@ const DEFAULT_ACCESS_STATE = { iflytekApiKey: "", iflytekApiSecret: "", + // moonshot + xaiUrl: DEFAULT_XAI_URL, + xaiApiKey: "", + // server config needCode: true, hideUserApiKey: false, @@ -169,6 +176,10 @@ export const useAccessStore = createPersistStore( return ensure(get(), ["iflytekApiKey"]); }, + isValidXAI() { + return ensure(get(), ["xaiApiKey"]); + }, + isAuthorized() { this.fetch(); @@ -184,6 +195,7 @@ export const useAccessStore = createPersistStore( this.isValidTencent() || this.isValidMoonshot() || this.isValidIflytek() || + this.isValidXAI() || !this.enabledAccessControl() || (this.enabledAccessControl() && ensure(get(), ["accessCode"])) ); From e791cd441d544a18126ddb825651d0e6274020e9 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Wed, 23 Oct 2024 11:55:25 +0800 Subject: [PATCH 149/352] add xai --- app/api/auth.ts | 2 +- app/client/platforms/xai.ts | 4 +--- app/store/access.ts | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/api/auth.ts b/app/api/auth.ts index fb147cf51..d4ac66a11 100644 --- a/app/api/auth.ts +++ b/app/api/auth.ts @@ -105,7 +105,7 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) { } if (systemApiKey) { - console.log("[Auth] use system api key", systemApiKey); + console.log("[Auth] use system api key"); req.headers.set("Authorization", `Bearer ${systemApiKey}`); } else { console.log("[Auth] admin did not provide an api key"); diff --git a/app/client/platforms/xai.ts b/app/client/platforms/xai.ts index 69f80e9fc..deb74e66c 100644 --- a/app/client/platforms/xai.ts +++ b/app/client/platforms/xai.ts @@ -83,11 +83,9 @@ export class XAIApi implements LLMApi { presence_penalty: modelConfig.presence_penalty, frequency_penalty: modelConfig.frequency_penalty, top_p: modelConfig.top_p, - // max_tokens: Math.max(modelConfig.max_tokens, 1024), - // Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore. }; - console.log("[Request] openai payload: ", requestPayload); + console.log("[Request] xai payload: ", requestPayload); const shouldStream = !!options.config.stream; const controller = new AbortController(); diff --git a/app/store/access.ts b/app/store/access.ts index 1a27deb1c..b3d412a2d 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -104,7 +104,7 @@ const DEFAULT_ACCESS_STATE = { iflytekApiKey: "", iflytekApiSecret: "", - // moonshot + // xai xaiUrl: DEFAULT_XAI_URL, xaiApiKey: "", From 65bb962fc0b6eaa0cb1e15451d954df216b1956f Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Wed, 23 Oct 2024 12:00:59 +0800 Subject: [PATCH 150/352] hotfix --- app/components/settings.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 6ce71b5ef..666caece8 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -1206,11 +1206,11 @@ export function Settings() { accessStore.update( - (access) => (access.moonshotUrl = e.currentTarget.value), + (access) => (access.xaiUrl = e.currentTarget.value), ) } > @@ -1221,12 +1221,12 @@ export function Settings() { > { accessStore.update( - (access) => (access.moonshotApiKey = e.currentTarget.value), + (access) => (access.xaiApiKey = e.currentTarget.value), ); }} /> From 801dc412f99937dfd64a895309d9304429d94cac Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 24 Oct 2024 15:28:05 +0800 Subject: [PATCH 151/352] add claude-3.5-haiku --- app/constant.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/constant.ts b/app/constant.ts index 9774bb594..00b8d9dae 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -319,6 +319,7 @@ const anthropicModels = [ "claude-3-opus-20240229", "claude-3-haiku-20240307", "claude-3-5-sonnet-20240620", + "claude-3-5-haiku-latest", ]; const baiduModels = [ From 4745706c42a390117e5e0f700af3d5f06e18f312 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 24 Oct 2024 15:32:27 +0800 Subject: [PATCH 152/352] update version to v2.15.6 --- src-tauri/tauri.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b2c3e04b0..415825b13 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "NextChat", - "version": "2.15.5" + "version": "2.15.6" }, "tauri": { "allowlist": { From e3ca7e8b4433bea43376035b9417fe233fe5f6f0 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 25 Oct 2024 17:52:08 +0800 Subject: [PATCH 153/352] hotfix for statusText is non ISO-8859-1 #5717 --- src-tauri/src/stream.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index d2c0726b0..d31dd67c3 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -119,11 +119,20 @@ pub async fn stream_fetch( } } Err(err) => { - println!("Error response: {:?}", err.source().expect("REASON").to_string()); + let error: String = err.source().expect("REASON").to_string(); + println!("Error response: {:?}", error); + tauri::async_runtime::spawn( async move { + if let Err(e) = window.emit(event_name, ChunkPayload{ request_id, chunk: error.into() }) { + println!("Failed to emit chunk payload: {:?}", e); + } + if let Err(e) = window.emit(event_name, EndPayload{ request_id, status: 0 }) { + println!("Failed to emit end payload: {:?}", e); + } + }); StreamResponse { request_id, status: 599, - status_text: err.source().expect("REASON").to_string(), + status_text: "Error".to_string(), headers: HashMap::new(), } } From 2c745590101b5201c677243f151616cb7023186e Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 25 Oct 2024 18:02:51 +0800 Subject: [PATCH 154/352] hitfix --- app/utils/stream.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/utils/stream.ts b/app/utils/stream.ts index 2eda768f3..782634595 100644 --- a/app/utils/stream.ts +++ b/app/utils/stream.ts @@ -100,7 +100,8 @@ export function fetch(url: string, options?: RequestInit): Promise { }) .catch((e) => { console.error("stream error", e); - throw e; + // throw e; + return new Response("", { status: 599 }); }); } return window.fetch(url, options); From 90ced9287626492898f2eb9bfd3b079171faf6ea Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 25 Oct 2024 18:05:29 +0800 Subject: [PATCH 155/352] update --- src-tauri/src/stream.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index d31dd67c3..8320db3e4 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -119,7 +119,9 @@ pub async fn stream_fetch( } } Err(err) => { - let error: String = err.source().expect("REASON").to_string(); + let error: String = err.source() + .map(|e| e.to_string()) + .unwrap_or_else(|| "Unknown error occurred".to_string()); println!("Error response: {:?}", error); tauri::async_runtime::spawn( async move { if let Err(e) = window.emit(event_name, ChunkPayload{ request_id, chunk: error.into() }) { From f89872b833d27c48b33281e60157640037e17a99 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 25 Oct 2024 18:12:09 +0800 Subject: [PATCH 156/352] hotfix for gemini invald argument #5715 --- app/client/platforms/google.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index 7265a500b..14fecb8f2 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -192,7 +192,9 @@ export class GeminiProApi implements LLMApi { requestPayload, getHeaders(), // @ts-ignore - [{ functionDeclarations: tools.map((tool) => tool.function) }], + tools.length > 0 + ? [{ functionDeclarations: tools.map((tool) => tool.function) }] + : [], funcs, controller, // parseSSE From f0b3e10a6caf55bf91325183b5ad84de2a05db04 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 25 Oct 2024 18:19:22 +0800 Subject: [PATCH 157/352] hotfix for gemini invald argument #5715 --- app/client/platforms/google.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index 14fecb8f2..a4b594ddf 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -193,7 +193,8 @@ export class GeminiProApi implements LLMApi { getHeaders(), // @ts-ignore tools.length > 0 - ? [{ functionDeclarations: tools.map((tool) => tool.function) }] + ? // @ts-ignore + [{ functionDeclarations: tools.map((tool) => tool.function) }] : [], funcs, controller, From 45db20c1c37279ebfe610d75a80dc09a21a14c54 Mon Sep 17 00:00:00 2001 From: ElricLiu <20209191+ElricLiu@users.noreply.github.com> Date: Sat, 26 Oct 2024 11:16:43 +0800 Subject: [PATCH 158/352] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d370000fa..b9e994e50 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4 [MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple [Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu -[Deploy on Zeabur](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) +[Deploy on Zeabur](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [Open in Gitpod](https://www.bt.cn/new/download.html) [](https://monica.im/?utm=nxcrp) From 49d42bb45d50141c6f7321ea650b0c5d58697591 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Mon, 28 Oct 2024 16:47:05 +0800 Subject: [PATCH 159/352] chore: improve jest --- jest.setup.ts | 22 +++++++++++++++++++ package.json | 3 ++- yarn.lock | 61 +++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/jest.setup.ts b/jest.setup.ts index ee6ccea1a..bc515f9a1 100644 --- a/jest.setup.ts +++ b/jest.setup.ts @@ -1,2 +1,24 @@ // Learn more: https://github.com/testing-library/jest-dom import "@testing-library/jest-dom"; + +global.fetch = jest.fn(() => + Promise.resolve({ + ok: true, + status: 200, + json: () => Promise.resolve({}), + headers: new Headers(), + redirected: false, + statusText: "OK", + type: "basic", + url: "", + clone: function () { + return this; + }, + body: null, + bodyUsed: false, + arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)), + blob: () => Promise.resolve(new Blob()), + formData: () => Promise.resolve(new FormData()), + text: () => Promise.resolve(""), + }), +); diff --git a/package.json b/package.json index 803c0d1a4..a984af688 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "html-to-image": "^1.11.11", "idb-keyval": "^6.2.1", "lodash-es": "^4.17.21", - "mermaid": "^10.6.1", "markdown-to-txt": "^2.0.1", + "mermaid": "^10.6.1", "nanoid": "^5.0.3", "next": "^14.1.1", "node-fetch": "^3.3.1", @@ -56,6 +56,7 @@ "devDependencies": { "@tauri-apps/api": "^1.6.0", "@tauri-apps/cli": "1.5.11", + "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.4.8", "@testing-library/react": "^16.0.0", "@types/jest": "^29.5.13", diff --git a/yarn.lock b/yarn.lock index ef296924e..3f32ae7d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27,6 +27,15 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.10.4": + version "7.26.0" + resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.26.0.tgz#9374b5cd068d128dac0b94ff482594273b1c2815" + integrity sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7": version "7.24.7" resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" @@ -394,6 +403,11 @@ resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + "@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" @@ -2093,6 +2107,20 @@ "@tauri-apps/cli-win32-ia32-msvc" "1.5.11" "@tauri-apps/cli-win32-x64-msvc" "1.5.11" +"@testing-library/dom@^10.4.0": + version "10.4.0" + resolved "https://registry.npmmirror.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8" + integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.3.0" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + "@testing-library/jest-dom@^6.4.8": version "6.4.8" resolved "https://registry.npmmirror.com/@testing-library/jest-dom/-/jest-dom-6.4.8.tgz#9c435742b20c6183d4e7034f2b329d562c079daa" @@ -2144,6 +2172,11 @@ resolved "https://registry.npmmirror.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.npmmirror.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + "@types/babel__core@^7.1.14": version "7.20.5" resolved "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" @@ -2738,7 +2771,7 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.0.0: +aria-query@5.3.0, aria-query@^5.0.0: version "5.3.0" resolved "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== @@ -3081,7 +3114,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3877,6 +3910,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.npmmirror.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + dom-accessibility-api@^0.6.3: version "0.6.3" resolved "https://registry.npmmirror.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" @@ -6052,6 +6090,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.npmmirror.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + make-dir@^4.0.0: version "4.0.0" resolved "https://registry.npmmirror.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" @@ -7018,6 +7061,15 @@ prettier@^3.0.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.2.tgz#78fcecd6d870551aa5547437cdae39d4701dca5b" integrity sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ== +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.npmmirror.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.npmmirror.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -7109,6 +7161,11 @@ react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" From a4d7a2c6e3ef4d325a8039b5dd5bb9445d496c02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 10:31:27 +0000 Subject: [PATCH 160/352] Bump @testing-library/jest-dom from 6.4.8 to 6.6.2 Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 6.4.8 to 6.6.2. - [Release notes](https://github.com/testing-library/jest-dom/releases) - [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/jest-dom/compare/v6.4.8...v6.6.2) --- updated-dependencies: - dependency-name: "@testing-library/jest-dom" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 18 +++++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 803c0d1a4..108bf7083 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "devDependencies": { "@tauri-apps/api": "^1.6.0", "@tauri-apps/cli": "1.5.11", - "@testing-library/jest-dom": "^6.4.8", + "@testing-library/jest-dom": "^6.6.2", "@testing-library/react": "^16.0.0", "@types/jest": "^29.5.13", "@types/js-yaml": "4.0.9", diff --git a/yarn.lock b/yarn.lock index ef296924e..b03c3c991 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2093,13 +2093,12 @@ "@tauri-apps/cli-win32-ia32-msvc" "1.5.11" "@tauri-apps/cli-win32-x64-msvc" "1.5.11" -"@testing-library/jest-dom@^6.4.8": - version "6.4.8" - resolved "https://registry.npmmirror.com/@testing-library/jest-dom/-/jest-dom-6.4.8.tgz#9c435742b20c6183d4e7034f2b329d562c079daa" - integrity sha512-JD0G+Zc38f5MBHA4NgxQMR5XtO5Jx9g86jqturNTt2WUfRmLDIY7iKkWHDCCTiDuFMre6nxAD5wHw9W5kI4rGw== +"@testing-library/jest-dom@^6.6.2": + version "6.6.2" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.2.tgz#8186aa9a07263adef9cc5a59a4772db8c31f4a5b" + integrity sha512-P6GJD4yqc9jZLbe98j/EkyQDTPgqftohZF5FBkHY5BUERZmcf4HeO2k0XaefEg329ux2p21i1A1DmyQ1kKw2Jw== dependencies: "@adobe/css-tools" "^4.4.0" - "@babel/runtime" "^7.9.2" aria-query "^5.0.0" chalk "^3.0.0" css.escape "^1.5.1" @@ -2738,20 +2737,13 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.0.0: +aria-query@^5.0.0, aria-query@^5.1.3: version "5.3.0" resolved "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== dependencies: dequal "^2.0.3" -aria-query@^5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" - integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== - dependencies: - deep-equal "^2.0.5" - array-buffer-byte-length@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" From 24df85cf9d3ab2a307baa1539922c9463949ffa9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 10:31:34 +0000 Subject: [PATCH 161/352] Bump @types/jest from 29.5.13 to 29.5.14 Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.13 to 29.5.14. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) --- updated-dependencies: - dependency-name: "@types/jest" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 803c0d1a4..bee434a63 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "@tauri-apps/cli": "1.5.11", "@testing-library/jest-dom": "^6.4.8", "@testing-library/react": "^16.0.0", - "@types/jest": "^29.5.13", + "@types/jest": "^29.5.14", "@types/js-yaml": "4.0.9", "@types/lodash-es": "^4.17.12", "@types/node": "^20.11.30", diff --git a/yarn.lock b/yarn.lock index ef296924e..7625b280e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2263,10 +2263,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.5.13": - version "29.5.13" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.13.tgz#8bc571659f401e6a719a7bf0dbcb8b78c71a8adc" - integrity sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg== +"@types/jest@^29.5.14": + version "29.5.14" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== dependencies: expect "^29.0.0" pretty-format "^29.0.0" From 736cbdbdd12d340e9b08b69724f9a1321befd645 Mon Sep 17 00:00:00 2001 From: hyiip Date: Wed, 30 Oct 2024 02:18:41 +0800 Subject: [PATCH 162/352] add constant to claude 3.5 sonnet 20241022 --- app/constant.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/constant.ts b/app/constant.ts index 9774bb594..f2021736a 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -319,6 +319,7 @@ const anthropicModels = [ "claude-3-opus-20240229", "claude-3-haiku-20240307", "claude-3-5-sonnet-20240620", + "claude-3-5-sonnet-20241022", ]; const baiduModels = [ From 86ffa1e6430b0a34893665bb284130c1f144e399 Mon Sep 17 00:00:00 2001 From: yuxuan-ctrl <714180720@qq.com> Date: Wed, 30 Oct 2024 16:30:01 +0800 Subject: [PATCH 163/352] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E9=98=BF?= =?UTF-8?q?=E9=87=8C=E7=B3=BB=E6=A8=A1=E5=9E=8B=E4=BB=A3=E7=A0=81=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.mjs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/next.config.mjs b/next.config.mjs index 26dadca4c..2bb6bc4f4 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -94,8 +94,12 @@ if (mode !== "export") { source: "/sharegpt", destination: "https://sharegpt.com/api/conversations", }, + { + source: "/api/proxy/alibaba/:path*", + destination: "https://dashscope.aliyuncs.com/api/:path*", + }, ]; - + return { beforeFiles: ret, }; From d357b45e84eb773c2e0c142d0d849c4f20be2975 Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Wed, 30 Oct 2024 19:24:03 +0800 Subject: [PATCH 164/352] =?UTF-8?q?feat:=20[#5714]=20=E6=94=AF=E6=8C=81GLM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/[provider]/[...path]/route.ts | 3 + app/api/auth.ts | 3 + app/api/glm.ts | 129 +++++++++++++++++ app/client/api.ts | 10 ++ app/client/platforms/glm.ts | 192 ++++++++++++++++++++++++++ app/components/settings.tsx | 41 ++++++ app/config/server.ts | 9 ++ app/constant.ts | 32 +++++ app/locales/cn.ts | 11 ++ app/locales/en.ts | 11 ++ app/store/access.ts | 12 ++ 11 files changed, 453 insertions(+) create mode 100644 app/api/glm.ts create mode 100644 app/client/platforms/glm.ts diff --git a/app/api/[provider]/[...path]/route.ts b/app/api/[provider]/[...path]/route.ts index 5ac248d0c..78836cc52 100644 --- a/app/api/[provider]/[...path]/route.ts +++ b/app/api/[provider]/[...path]/route.ts @@ -11,6 +11,7 @@ import { handle as moonshotHandler } from "../../moonshot"; import { handle as stabilityHandler } from "../../stability"; import { handle as iflytekHandler } from "../../iflytek"; import { handle as xaiHandler } from "../../xai"; +import { handle as glmHandler } from "../../glm"; import { handle as proxyHandler } from "../../proxy"; async function handle( @@ -41,6 +42,8 @@ async function handle( return iflytekHandler(req, { params }); case ApiPath.XAI: return xaiHandler(req, { params }); + case ApiPath.GLM: + return glmHandler(req, { params }); case ApiPath.OpenAI: return openaiHandler(req, { params }); default: diff --git a/app/api/auth.ts b/app/api/auth.ts index d4ac66a11..db920fc28 100644 --- a/app/api/auth.ts +++ b/app/api/auth.ts @@ -95,6 +95,9 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) { case ModelProvider.XAI: systemApiKey = serverConfig.xaiApiKey; break; + case ModelProvider.GLM: + systemApiKey = serverConfig.glmApiKey; + break; case ModelProvider.GPT: default: if (req.nextUrl.pathname.includes("azure/deployments")) { diff --git a/app/api/glm.ts b/app/api/glm.ts new file mode 100644 index 000000000..d40c4b6a8 --- /dev/null +++ b/app/api/glm.ts @@ -0,0 +1,129 @@ +import { getServerSideConfig } from "@/app/config/server"; +import { + GLM_BASE_URL, + ApiPath, + ModelProvider, + ServiceProvider, +} from "@/app/constant"; +import { prettyObject } from "@/app/utils/format"; +import { NextRequest, NextResponse } from "next/server"; +import { auth } from "@/app/api/auth"; +import { isModelAvailableInServer } from "@/app/utils/model"; + +const serverConfig = getServerSideConfig(); + +export async function handle( + req: NextRequest, + { params }: { params: { path: string[] } }, +) { + console.log("[GLM Route] params ", params); + + if (req.method === "OPTIONS") { + return NextResponse.json({ body: "OK" }, { status: 200 }); + } + + const authResult = auth(req, ModelProvider.GLM); + if (authResult.error) { + return NextResponse.json(authResult, { + status: 401, + }); + } + + try { + const response = await request(req); + return response; + } catch (e) { + console.error("[GLM] ", e); + return NextResponse.json(prettyObject(e)); + } +} + +async function request(req: NextRequest) { + const controller = new AbortController(); + + // alibaba use base url or just remove the path + let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.GLM, ""); + + let baseUrl = serverConfig.glmUrl || GLM_BASE_URL; + + if (!baseUrl.startsWith("http")) { + baseUrl = `https://${baseUrl}`; + } + + if (baseUrl.endsWith("/")) { + baseUrl = baseUrl.slice(0, -1); + } + + console.log("[Proxy] ", path); + console.log("[Base Url]", baseUrl); + + const timeoutId = setTimeout( + () => { + controller.abort(); + }, + 10 * 60 * 1000, + ); + + const fetchUrl = `${baseUrl}${path}`; + console.log("[Fetch Url] ", fetchUrl); + const fetchOptions: RequestInit = { + headers: { + "Content-Type": "application/json", + Authorization: req.headers.get("Authorization") ?? "", + }, + method: req.method, + body: req.body, + redirect: "manual", + // @ts-ignore + duplex: "half", + signal: controller.signal, + }; + + // #1815 try to refuse some request to some models + if (serverConfig.customModels && req.body) { + try { + const clonedBody = await req.text(); + fetchOptions.body = clonedBody; + + const jsonBody = JSON.parse(clonedBody) as { model?: string }; + + // not undefined and is false + if ( + isModelAvailableInServer( + serverConfig.customModels, + jsonBody?.model as string, + ServiceProvider.GLM as string, + ) + ) { + return NextResponse.json( + { + error: true, + message: `you are not allowed to use ${jsonBody?.model} model`, + }, + { + status: 403, + }, + ); + } + } catch (e) { + console.error(`[GLM] filter`, e); + } + } + try { + const res = await fetch(fetchUrl, fetchOptions); + + // to prevent browser prompt for credentials + const newHeaders = new Headers(res.headers); + newHeaders.delete("www-authenticate"); + // to disable nginx buffering + newHeaders.set("X-Accel-Buffering", "no"); + + return new Response(res.body, { + status: res.status, + statusText: res.statusText, + headers: newHeaders, + }); + } finally { + clearTimeout(timeoutId); + } +} diff --git a/app/client/api.ts b/app/client/api.ts index 4238c2a26..4082d085c 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -21,6 +21,7 @@ import { HunyuanApi } from "./platforms/tencent"; import { MoonshotApi } from "./platforms/moonshot"; import { SparkApi } from "./platforms/iflytek"; import { XAIApi } from "./platforms/xai"; +import { GLMApi } from "./platforms/glm"; export const ROLES = ["system", "user", "assistant"] as const; export type MessageRole = (typeof ROLES)[number]; @@ -156,6 +157,9 @@ export class ClientApi { case ModelProvider.XAI: this.llm = new XAIApi(); break; + case ModelProvider.GLM: + this.llm = new GLMApi(); + break; default: this.llm = new ChatGPTApi(); } @@ -244,6 +248,7 @@ export function getHeaders(ignoreHeaders: boolean = false) { const isMoonshot = modelConfig.providerName === ServiceProvider.Moonshot; const isIflytek = modelConfig.providerName === ServiceProvider.Iflytek; const isXAI = modelConfig.providerName === ServiceProvider.XAI; + const isGLM = modelConfig.providerName === ServiceProvider.GLM; const isEnabledAccessControl = accessStore.enabledAccessControl(); const apiKey = isGoogle ? accessStore.googleApiKey @@ -259,6 +264,8 @@ export function getHeaders(ignoreHeaders: boolean = false) { ? accessStore.moonshotApiKey : isXAI ? accessStore.xaiApiKey + : isGLM + ? accessStore.glmApiKey : isIflytek ? accessStore.iflytekApiKey && accessStore.iflytekApiSecret ? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret @@ -274,6 +281,7 @@ export function getHeaders(ignoreHeaders: boolean = false) { isMoonshot, isIflytek, isXAI, + isGLM, apiKey, isEnabledAccessControl, }; @@ -338,6 +346,8 @@ export function getClientApi(provider: ServiceProvider): ClientApi { return new ClientApi(ModelProvider.Iflytek); case ServiceProvider.XAI: return new ClientApi(ModelProvider.XAI); + case ServiceProvider.GLM: + return new ClientApi(ModelProvider.GLM); default: return new ClientApi(ModelProvider.GPT); } diff --git a/app/client/platforms/glm.ts b/app/client/platforms/glm.ts new file mode 100644 index 000000000..b88272ae1 --- /dev/null +++ b/app/client/platforms/glm.ts @@ -0,0 +1,192 @@ +"use client"; +import { ApiPath, GLM_BASE_URL, GLM, REQUEST_TIMEOUT_MS } from "@/app/constant"; +import { + useAccessStore, + useAppConfig, + useChatStore, + ChatMessageTool, + usePluginStore, +} from "@/app/store"; +import { stream } from "@/app/utils/chat"; +import { + ChatOptions, + getHeaders, + LLMApi, + LLMModel, + SpeechOptions, +} from "../api"; +import { getClientConfig } from "@/app/config/client"; +import { getMessageTextContent } from "@/app/utils"; +import { RequestPayload } from "./openai"; +import { fetch } from "@/app/utils/stream"; + +export class GLMApi implements LLMApi { + private disableListModels = true; + + path(path: string): string { + const accessStore = useAccessStore.getState(); + + let baseUrl = ""; + + if (accessStore.useCustomConfig) { + baseUrl = accessStore.glmUrl; + } + + if (baseUrl.length === 0) { + const isApp = !!getClientConfig()?.isApp; + const apiPath = ApiPath.GLM; + baseUrl = isApp ? GLM_BASE_URL : apiPath; + } + + if (baseUrl.endsWith("/")) { + baseUrl = baseUrl.slice(0, baseUrl.length - 1); + } + if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.GLM)) { + baseUrl = "https://" + baseUrl; + } + + console.log("[Proxy Endpoint] ", baseUrl, path); + + return [baseUrl, path].join("/"); + } + + extractMessage(res: any) { + return res.choices?.at(0)?.message?.content ?? ""; + } + + speech(options: SpeechOptions): Promise { + throw new Error("Method not implemented."); + } + + async chat(options: ChatOptions) { + const messages: ChatOptions["messages"] = []; + for (const v of options.messages) { + const content = getMessageTextContent(v); + messages.push({ role: v.role, content }); + } + + const modelConfig = { + ...useAppConfig.getState().modelConfig, + ...useChatStore.getState().currentSession().mask.modelConfig, + ...{ + model: options.config.model, + providerName: options.config.providerName, + }, + }; + + const requestPayload: RequestPayload = { + messages, + stream: options.config.stream, + model: modelConfig.model, + temperature: modelConfig.temperature, + presence_penalty: modelConfig.presence_penalty, + frequency_penalty: modelConfig.frequency_penalty, + top_p: modelConfig.top_p, + }; + + console.log("[Request] glm payload: ", requestPayload); + + const shouldStream = !!options.config.stream; + const controller = new AbortController(); + options.onController?.(controller); + + try { + const chatPath = this.path(GLM.ChatPath); + const chatPayload = { + method: "POST", + body: JSON.stringify(requestPayload), + signal: controller.signal, + headers: getHeaders(), + }; + + // make a fetch request + const requestTimeoutId = setTimeout( + () => controller.abort(), + REQUEST_TIMEOUT_MS, + ); + + if (shouldStream) { + const [tools, funcs] = usePluginStore + .getState() + .getAsTools( + useChatStore.getState().currentSession().mask?.plugin || [], + ); + return stream( + chatPath, + requestPayload, + getHeaders(), + tools as any, + funcs, + controller, + // parseSSE + (text: string, runTools: ChatMessageTool[]) => { + // console.log("parseSSE", text, runTools); + const json = JSON.parse(text); + const choices = json.choices as Array<{ + delta: { + content: string; + tool_calls: ChatMessageTool[]; + }; + }>; + const tool_calls = choices[0]?.delta?.tool_calls; + if (tool_calls?.length > 0) { + const index = tool_calls[0]?.index; + const id = tool_calls[0]?.id; + const args = tool_calls[0]?.function?.arguments; + if (id) { + runTools.push({ + id, + type: tool_calls[0]?.type, + function: { + name: tool_calls[0]?.function?.name as string, + arguments: args, + }, + }); + } else { + // @ts-ignore + runTools[index]["function"]["arguments"] += args; + } + } + return choices[0]?.delta?.content; + }, + // processToolMessage, include tool_calls message and tool call results + ( + requestPayload: RequestPayload, + toolCallMessage: any, + toolCallResult: any[], + ) => { + // @ts-ignore + requestPayload?.messages?.splice( + // @ts-ignore + requestPayload?.messages?.length, + 0, + toolCallMessage, + ...toolCallResult, + ); + }, + options, + ); + } else { + const res = await fetch(chatPath, chatPayload); + clearTimeout(requestTimeoutId); + + const resJson = await res.json(); + const message = this.extractMessage(resJson); + options.onFinish(message); + } + } catch (e) { + console.log("[Request] failed to make a chat request", e); + options.onError?.(e as Error); + } + } + async usage() { + return { + used: 0, + total: 0, + }; + } + + async models(): Promise { + return []; + } +} diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 666caece8..e5859e716 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -72,6 +72,7 @@ import { Stability, Iflytek, SAAS_CHAT_URL, + GLM, } from "../constant"; import { Prompt, SearchService, usePromptStore } from "../store/prompt"; import { ErrorBoundary } from "./error"; @@ -1234,6 +1235,45 @@ export function Settings() { ); + const glmConfigComponent = accessStore.provider === ServiceProvider.GLM && ( + <> + + + accessStore.update( + (access) => (access.glmUrl = e.currentTarget.value), + ) + } + > + + + { + accessStore.update( + (access) => (access.glmApiKey = e.currentTarget.value), + ); + }} + /> + + + ); + const stabilityConfigComponent = accessStore.provider === ServiceProvider.Stability && ( <> @@ -1693,6 +1733,7 @@ export function Settings() { {stabilityConfigComponent} {lflytekConfigComponent} {XAIConfigComponent} + {glmConfigComponent} )} diff --git a/app/config/server.ts b/app/config/server.ts index eac4ba0cf..b9a68ce4d 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -75,6 +75,10 @@ declare global { XAI_URL?: string; XAI_API_KEY?: string; + // glm only + GLM_URL?: string; + GLM_API_KEY?: string; + // custom template for preprocessing user input DEFAULT_INPUT_TEMPLATE?: string; } @@ -151,6 +155,7 @@ export const getServerSideConfig = () => { const isMoonshot = !!process.env.MOONSHOT_API_KEY; const isIflytek = !!process.env.IFLYTEK_API_KEY; const isXAI = !!process.env.XAI_API_KEY; + const isGLM = !!process.env.GLM_API_KEY; // const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? ""; // const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim()); // const randomIndex = Math.floor(Math.random() * apiKeys.length); @@ -217,6 +222,10 @@ export const getServerSideConfig = () => { xaiUrl: process.env.XAI_URL, xaiApiKey: getApiKey(process.env.XAI_API_KEY), + isGLM, + glmUrl: process.env.GLM_URL, + glmApiKey: getApiKey(process.env.GLM_API_KEY), + cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID, cloudflareKVNamespaceId: process.env.CLOUDFLARE_KV_NAMESPACE_ID, cloudflareKVApiKey: getApiKey(process.env.CLOUDFLARE_KV_API_KEY), diff --git a/app/constant.ts b/app/constant.ts index 9774bb594..d58bc3935 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -30,6 +30,8 @@ export const IFLYTEK_BASE_URL = "https://spark-api-open.xf-yun.com"; export const XAI_BASE_URL = "https://api.x.ai"; +export const GLM_BASE_URL = "https://open.bigmodel.cn"; + export const CACHE_URL_PREFIX = "/api/cache"; export const UPLOAD_URL = `${CACHE_URL_PREFIX}/upload`; @@ -62,6 +64,7 @@ export enum ApiPath { Stability = "/api/stability", Artifacts = "/api/artifacts", XAI = "/api/xai", + GLM = "/api/glm", } export enum SlotID { @@ -115,6 +118,7 @@ export enum ServiceProvider { Stability = "Stability", Iflytek = "Iflytek", XAI = "XAI", + GLM = "GLM", } // Google API safety settings, see https://ai.google.dev/gemini-api/docs/safety-settings @@ -138,6 +142,7 @@ export enum ModelProvider { Moonshot = "Moonshot", Iflytek = "Iflytek", XAI = "XAI", + GLM = "GLM", } export const Stability = { @@ -225,6 +230,11 @@ export const XAI = { ChatPath: "v1/chat/completions", }; +export const GLM = { + ExampleEndpoint: GLM_BASE_URL, + ChatPath: "/api/paas/v4/chat/completions", +}; + export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang // export const DEFAULT_SYSTEM_TEMPLATE = ` // You are ChatGPT, a large language model trained by {{ServiceProvider}}. @@ -376,6 +386,17 @@ const iflytekModels = [ const xAIModes = ["grok-beta"]; +const glmModels = [ + "glm-4-plus", + "glm-4-0520", + "glm-4", + "glm-4-air", + "glm-4-airx", + "glm-4-long", + "glm-4-flashx", + "glm-4-flash", +]; + let seq = 1000; // 内置的模型序号生成器从1000开始 export const DEFAULT_MODELS = [ ...openaiModels.map((name) => ({ @@ -499,6 +520,17 @@ export const DEFAULT_MODELS = [ sorted: 11, }, })), + ...glmModels.map((name) => ({ + name, + available: true, + sorted: seq++, + provider: { + id: "glm", + providerName: "GLM", + providerType: "glm", + sorted: 12, + }, + })), ] as const; export const CHAT_PAGE_SIZE = 15; diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 006fc8162..92aaf6228 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -473,6 +473,17 @@ const cn = { SubTitle: "样例:", }, }, + GLM: { + ApiKey: { + Title: "接口密钥", + SubTitle: "使用自定义 GLM API Key", + Placeholder: "GLM API Key", + }, + Endpoint: { + Title: "接口地址", + SubTitle: "样例:", + }, + }, Stability: { ApiKey: { Title: "接口密钥", diff --git a/app/locales/en.ts b/app/locales/en.ts index 7204bd946..d691925c4 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -457,6 +457,17 @@ const en: LocaleType = { SubTitle: "Example: ", }, }, + GLM: { + ApiKey: { + Title: "GLM API Key", + SubTitle: "Use a custom GLM API Key", + Placeholder: "GLM API Key", + }, + Endpoint: { + Title: "Endpoint Address", + SubTitle: "Example: ", + }, + }, Stability: { ApiKey: { Title: "Stability API Key", diff --git a/app/store/access.ts b/app/store/access.ts index b3d412a2d..9cc420fdf 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -14,6 +14,7 @@ import { STABILITY_BASE_URL, IFLYTEK_BASE_URL, XAI_BASE_URL, + GLM_BASE_URL, } from "../constant"; import { getHeaders } from "../client/api"; import { getClientConfig } from "../config/client"; @@ -47,6 +48,8 @@ const DEFAULT_IFLYTEK_URL = isApp ? IFLYTEK_BASE_URL : ApiPath.Iflytek; const DEFAULT_XAI_URL = isApp ? XAI_BASE_URL : ApiPath.XAI; +const DEFAULT_GLM_URL = isApp ? GLM_BASE_URL : ApiPath.GLM; + const DEFAULT_ACCESS_STATE = { accessCode: "", useCustomConfig: false, @@ -108,6 +111,10 @@ const DEFAULT_ACCESS_STATE = { xaiUrl: DEFAULT_XAI_URL, xaiApiKey: "", + // glm + glmUrl: DEFAULT_GLM_URL, + glmApiKey: "", + // server config needCode: true, hideUserApiKey: false, @@ -180,6 +187,10 @@ export const useAccessStore = createPersistStore( return ensure(get(), ["xaiApiKey"]); }, + isValidGLM() { + return ensure(get(), ["glmApiKey"]); + }, + isAuthorized() { this.fetch(); @@ -196,6 +207,7 @@ export const useAccessStore = createPersistStore( this.isValidMoonshot() || this.isValidIflytek() || this.isValidXAI() || + this.isValidGLM() || !this.enabledAccessControl() || (this.enabledAccessControl() && ensure(get(), ["accessCode"])) ); From 44383a8b331ed283f06213c5176bf60fe98bbcc0 Mon Sep 17 00:00:00 2001 From: Core Date: Thu, 31 Oct 2024 11:00:45 +0800 Subject: [PATCH 165/352] add claude-3-5-sonnet-latest and claude-3-opus-latest add claude-3-5-sonnet-latest and claude-3-opus-latest --- app/constant.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/constant.ts b/app/constant.ts index f2021736a..23c164298 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -320,6 +320,8 @@ const anthropicModels = [ "claude-3-haiku-20240307", "claude-3-5-sonnet-20240620", "claude-3-5-sonnet-20241022", + "claude-3-5-sonnet-latest", + "claude-3-opus-latest", ]; const baiduModels = [ From d3f0a77830073684dd8da25e34d5d8eb0a94ecdb Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Thu, 31 Oct 2024 11:23:06 +0800 Subject: [PATCH 166/352] chore: update Provider --- app/constant.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/constant.ts b/app/constant.ts index d58bc3935..b8b25b7ab 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -118,7 +118,7 @@ export enum ServiceProvider { Stability = "Stability", Iflytek = "Iflytek", XAI = "XAI", - GLM = "GLM", + GLM = "ChatGLM", } // Google API safety settings, see https://ai.google.dev/gemini-api/docs/safety-settings @@ -142,7 +142,7 @@ export enum ModelProvider { Moonshot = "Moonshot", Iflytek = "Iflytek", XAI = "XAI", - GLM = "GLM", + GLM = "ChatGLM", } export const Stability = { @@ -525,9 +525,9 @@ export const DEFAULT_MODELS = [ available: true, sorted: seq++, provider: { - id: "glm", - providerName: "GLM", - providerType: "glm", + id: "chatglm", + providerName: "ChatGLM", + providerType: "chatglm", sorted: 12, }, })), From 7a8d557ea37e9b02fc26d8416fc631f4b7adda56 Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Thu, 31 Oct 2024 11:37:19 +0800 Subject: [PATCH 167/352] =?UTF-8?q?chore:=20=E5=BC=80=E5=90=AF=E6=8F=92?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/utils.ts b/app/utils.ts index d8fc46330..91f11c0c2 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -278,7 +278,8 @@ export function showPlugins(provider: ServiceProvider, model: string) { if ( provider == ServiceProvider.OpenAI || provider == ServiceProvider.Azure || - provider == ServiceProvider.Moonshot + provider == ServiceProvider.Moonshot || + provider == ServiceProvider.GLM ) { return true; } From afe12c212e51bd2d27c5db5700f881c32a0bd3ba Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Fri, 1 Nov 2024 13:53:43 +0800 Subject: [PATCH 168/352] chore: update --- app/api/[provider]/[...path]/route.ts | 6 ++--- app/api/auth.ts | 4 ++-- app/api/glm.ts | 8 +++---- app/client/api.ts | 18 +++++++-------- app/client/platforms/glm.ts | 19 ++++++++++------ app/components/settings.tsx | 32 ++++++++++++++------------- app/config/server.ts | 14 ++++++------ app/constant.ts | 16 +++++++------- app/locales/cn.ts | 6 ++--- app/locales/en.ts | 8 +++---- app/store/access.ts | 16 +++++++------- app/utils.ts | 2 +- 12 files changed, 78 insertions(+), 71 deletions(-) diff --git a/app/api/[provider]/[...path]/route.ts b/app/api/[provider]/[...path]/route.ts index 78836cc52..3017fd371 100644 --- a/app/api/[provider]/[...path]/route.ts +++ b/app/api/[provider]/[...path]/route.ts @@ -11,7 +11,7 @@ import { handle as moonshotHandler } from "../../moonshot"; import { handle as stabilityHandler } from "../../stability"; import { handle as iflytekHandler } from "../../iflytek"; import { handle as xaiHandler } from "../../xai"; -import { handle as glmHandler } from "../../glm"; +import { handle as chatglmHandler } from "../../glm"; import { handle as proxyHandler } from "../../proxy"; async function handle( @@ -42,8 +42,8 @@ async function handle( return iflytekHandler(req, { params }); case ApiPath.XAI: return xaiHandler(req, { params }); - case ApiPath.GLM: - return glmHandler(req, { params }); + case ApiPath.ChatGLM: + return chatglmHandler(req, { params }); case ApiPath.OpenAI: return openaiHandler(req, { params }); default: diff --git a/app/api/auth.ts b/app/api/auth.ts index db920fc28..6703b64bd 100644 --- a/app/api/auth.ts +++ b/app/api/auth.ts @@ -95,8 +95,8 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) { case ModelProvider.XAI: systemApiKey = serverConfig.xaiApiKey; break; - case ModelProvider.GLM: - systemApiKey = serverConfig.glmApiKey; + case ModelProvider.ChatGLM: + systemApiKey = serverConfig.chatglmApiKey; break; case ModelProvider.GPT: default: diff --git a/app/api/glm.ts b/app/api/glm.ts index d40c4b6a8..ea7a766bd 100644 --- a/app/api/glm.ts +++ b/app/api/glm.ts @@ -1,6 +1,6 @@ import { getServerSideConfig } from "@/app/config/server"; import { - GLM_BASE_URL, + CHATGLM_BASE_URL, ApiPath, ModelProvider, ServiceProvider, @@ -42,9 +42,9 @@ async function request(req: NextRequest) { const controller = new AbortController(); // alibaba use base url or just remove the path - let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.GLM, ""); + let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.ChatGLM, ""); - let baseUrl = serverConfig.glmUrl || GLM_BASE_URL; + let baseUrl = serverConfig.chatglmUrl || CHATGLM_BASE_URL; if (!baseUrl.startsWith("http")) { baseUrl = `https://${baseUrl}`; @@ -92,7 +92,7 @@ async function request(req: NextRequest) { isModelAvailableInServer( serverConfig.customModels, jsonBody?.model as string, - ServiceProvider.GLM as string, + ServiceProvider.ChatGLM as string, ) ) { return NextResponse.json( diff --git a/app/client/api.ts b/app/client/api.ts index 4082d085c..8fecf841f 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -21,7 +21,7 @@ import { HunyuanApi } from "./platforms/tencent"; import { MoonshotApi } from "./platforms/moonshot"; import { SparkApi } from "./platforms/iflytek"; import { XAIApi } from "./platforms/xai"; -import { GLMApi } from "./platforms/glm"; +import { ChatGLMApi } from "./platforms/glm"; export const ROLES = ["system", "user", "assistant"] as const; export type MessageRole = (typeof ROLES)[number]; @@ -157,8 +157,8 @@ export class ClientApi { case ModelProvider.XAI: this.llm = new XAIApi(); break; - case ModelProvider.GLM: - this.llm = new GLMApi(); + case ModelProvider.ChatGLM: + this.llm = new ChatGLMApi(); break; default: this.llm = new ChatGPTApi(); @@ -248,7 +248,7 @@ export function getHeaders(ignoreHeaders: boolean = false) { const isMoonshot = modelConfig.providerName === ServiceProvider.Moonshot; const isIflytek = modelConfig.providerName === ServiceProvider.Iflytek; const isXAI = modelConfig.providerName === ServiceProvider.XAI; - const isGLM = modelConfig.providerName === ServiceProvider.GLM; + const isChatGLM = modelConfig.providerName === ServiceProvider.ChatGLM; const isEnabledAccessControl = accessStore.enabledAccessControl(); const apiKey = isGoogle ? accessStore.googleApiKey @@ -264,8 +264,8 @@ export function getHeaders(ignoreHeaders: boolean = false) { ? accessStore.moonshotApiKey : isXAI ? accessStore.xaiApiKey - : isGLM - ? accessStore.glmApiKey + : isChatGLM + ? accessStore.chatglmApiKey : isIflytek ? accessStore.iflytekApiKey && accessStore.iflytekApiSecret ? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret @@ -281,7 +281,7 @@ export function getHeaders(ignoreHeaders: boolean = false) { isMoonshot, isIflytek, isXAI, - isGLM, + isChatGLM, apiKey, isEnabledAccessControl, }; @@ -346,8 +346,8 @@ export function getClientApi(provider: ServiceProvider): ClientApi { return new ClientApi(ModelProvider.Iflytek); case ServiceProvider.XAI: return new ClientApi(ModelProvider.XAI); - case ServiceProvider.GLM: - return new ClientApi(ModelProvider.GLM); + case ServiceProvider.ChatGLM: + return new ClientApi(ModelProvider.ChatGLM); default: return new ClientApi(ModelProvider.GPT); } diff --git a/app/client/platforms/glm.ts b/app/client/platforms/glm.ts index b88272ae1..10696ee82 100644 --- a/app/client/platforms/glm.ts +++ b/app/client/platforms/glm.ts @@ -1,5 +1,10 @@ "use client"; -import { ApiPath, GLM_BASE_URL, GLM, REQUEST_TIMEOUT_MS } from "@/app/constant"; +import { + ApiPath, + CHATGLM_BASE_URL, + ChatGLM, + REQUEST_TIMEOUT_MS, +} from "@/app/constant"; import { useAccessStore, useAppConfig, @@ -20,7 +25,7 @@ import { getMessageTextContent } from "@/app/utils"; import { RequestPayload } from "./openai"; import { fetch } from "@/app/utils/stream"; -export class GLMApi implements LLMApi { +export class ChatGLMApi implements LLMApi { private disableListModels = true; path(path: string): string { @@ -29,19 +34,19 @@ export class GLMApi implements LLMApi { let baseUrl = ""; if (accessStore.useCustomConfig) { - baseUrl = accessStore.glmUrl; + baseUrl = accessStore.chatglmUrl; } if (baseUrl.length === 0) { const isApp = !!getClientConfig()?.isApp; - const apiPath = ApiPath.GLM; - baseUrl = isApp ? GLM_BASE_URL : apiPath; + const apiPath = ApiPath.ChatGLM; + baseUrl = isApp ? CHATGLM_BASE_URL : apiPath; } if (baseUrl.endsWith("/")) { baseUrl = baseUrl.slice(0, baseUrl.length - 1); } - if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.GLM)) { + if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.ChatGLM)) { baseUrl = "https://" + baseUrl; } @@ -91,7 +96,7 @@ export class GLMApi implements LLMApi { options.onController?.(controller); try { - const chatPath = this.path(GLM.ChatPath); + const chatPath = this.path(ChatGLM.ChatPath); const chatPayload = { method: "POST", body: JSON.stringify(requestPayload), diff --git a/app/components/settings.tsx b/app/components/settings.tsx index e5859e716..e2666b551 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -72,7 +72,7 @@ import { Stability, Iflytek, SAAS_CHAT_URL, - GLM, + ChatGLM, } from "../constant"; import { Prompt, SearchService, usePromptStore } from "../store/prompt"; import { ErrorBoundary } from "./error"; @@ -1235,38 +1235,40 @@ export function Settings() { ); - const glmConfigComponent = accessStore.provider === ServiceProvider.GLM && ( + const chatglmConfigComponent = accessStore.provider === + ServiceProvider.ChatGLM && ( <> accessStore.update( - (access) => (access.glmUrl = e.currentTarget.value), + (access) => (access.chatglmUrl = e.currentTarget.value), ) } > { accessStore.update( - (access) => (access.glmApiKey = e.currentTarget.value), + (access) => (access.chatglmApiKey = e.currentTarget.value), ); }} /> @@ -1733,7 +1735,7 @@ export function Settings() { {stabilityConfigComponent} {lflytekConfigComponent} {XAIConfigComponent} - {glmConfigComponent} + {chatglmConfigComponent} )} diff --git a/app/config/server.ts b/app/config/server.ts index b9a68ce4d..485f950da 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -75,9 +75,9 @@ declare global { XAI_URL?: string; XAI_API_KEY?: string; - // glm only - GLM_URL?: string; - GLM_API_KEY?: string; + // chatglm only + CHATGLM_URL?: string; + CHATGLM_API_KEY?: string; // custom template for preprocessing user input DEFAULT_INPUT_TEMPLATE?: string; @@ -155,7 +155,7 @@ export const getServerSideConfig = () => { const isMoonshot = !!process.env.MOONSHOT_API_KEY; const isIflytek = !!process.env.IFLYTEK_API_KEY; const isXAI = !!process.env.XAI_API_KEY; - const isGLM = !!process.env.GLM_API_KEY; + const isChatGLM = !!process.env.CHATGLM_API_KEY; // const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? ""; // const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim()); // const randomIndex = Math.floor(Math.random() * apiKeys.length); @@ -222,9 +222,9 @@ export const getServerSideConfig = () => { xaiUrl: process.env.XAI_URL, xaiApiKey: getApiKey(process.env.XAI_API_KEY), - isGLM, - glmUrl: process.env.GLM_URL, - glmApiKey: getApiKey(process.env.GLM_API_KEY), + isChatGLM, + chatglmUrl: process.env.CHATGLM_URL, + chatglmApiKey: getApiKey(process.env.CHATGLM_API_KEY), cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID, cloudflareKVNamespaceId: process.env.CLOUDFLARE_KV_NAMESPACE_ID, diff --git a/app/constant.ts b/app/constant.ts index b8b25b7ab..1a84e5c84 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -30,7 +30,7 @@ export const IFLYTEK_BASE_URL = "https://spark-api-open.xf-yun.com"; export const XAI_BASE_URL = "https://api.x.ai"; -export const GLM_BASE_URL = "https://open.bigmodel.cn"; +export const CHATGLM_BASE_URL = "https://open.bigmodel.cn"; export const CACHE_URL_PREFIX = "/api/cache"; export const UPLOAD_URL = `${CACHE_URL_PREFIX}/upload`; @@ -64,7 +64,7 @@ export enum ApiPath { Stability = "/api/stability", Artifacts = "/api/artifacts", XAI = "/api/xai", - GLM = "/api/glm", + ChatGLM = "/api/chatglm", } export enum SlotID { @@ -118,7 +118,7 @@ export enum ServiceProvider { Stability = "Stability", Iflytek = "Iflytek", XAI = "XAI", - GLM = "ChatGLM", + ChatGLM = "ChatGLM", } // Google API safety settings, see https://ai.google.dev/gemini-api/docs/safety-settings @@ -142,7 +142,7 @@ export enum ModelProvider { Moonshot = "Moonshot", Iflytek = "Iflytek", XAI = "XAI", - GLM = "ChatGLM", + ChatGLM = "ChatGLM", } export const Stability = { @@ -230,8 +230,8 @@ export const XAI = { ChatPath: "v1/chat/completions", }; -export const GLM = { - ExampleEndpoint: GLM_BASE_URL, +export const ChatGLM = { + ExampleEndpoint: CHATGLM_BASE_URL, ChatPath: "/api/paas/v4/chat/completions", }; @@ -386,7 +386,7 @@ const iflytekModels = [ const xAIModes = ["grok-beta"]; -const glmModels = [ +const chatglmModels = [ "glm-4-plus", "glm-4-0520", "glm-4", @@ -520,7 +520,7 @@ export const DEFAULT_MODELS = [ sorted: 11, }, })), - ...glmModels.map((name) => ({ + ...chatglmModels.map((name) => ({ name, available: true, sorted: seq++, diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 92aaf6228..9712593c6 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -473,11 +473,11 @@ const cn = { SubTitle: "样例:", }, }, - GLM: { + ChatGLM: { ApiKey: { Title: "接口密钥", - SubTitle: "使用自定义 GLM API Key", - Placeholder: "GLM API Key", + SubTitle: "使用自定义 ChatGLM API Key", + Placeholder: "ChatGLM API Key", }, Endpoint: { Title: "接口地址", diff --git a/app/locales/en.ts b/app/locales/en.ts index d691925c4..ac8d3aed2 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -457,11 +457,11 @@ const en: LocaleType = { SubTitle: "Example: ", }, }, - GLM: { + ChatGLM: { ApiKey: { - Title: "GLM API Key", - SubTitle: "Use a custom GLM API Key", - Placeholder: "GLM API Key", + Title: "ChatGLM API Key", + SubTitle: "Use a custom ChatGLM API Key", + Placeholder: "ChatGLM API Key", }, Endpoint: { Title: "Endpoint Address", diff --git a/app/store/access.ts b/app/store/access.ts index 9cc420fdf..3b0e6357b 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -14,7 +14,7 @@ import { STABILITY_BASE_URL, IFLYTEK_BASE_URL, XAI_BASE_URL, - GLM_BASE_URL, + CHATGLM_BASE_URL, } from "../constant"; import { getHeaders } from "../client/api"; import { getClientConfig } from "../config/client"; @@ -48,7 +48,7 @@ const DEFAULT_IFLYTEK_URL = isApp ? IFLYTEK_BASE_URL : ApiPath.Iflytek; const DEFAULT_XAI_URL = isApp ? XAI_BASE_URL : ApiPath.XAI; -const DEFAULT_GLM_URL = isApp ? GLM_BASE_URL : ApiPath.GLM; +const DEFAULT_CHATGLM_URL = isApp ? CHATGLM_BASE_URL : ApiPath.ChatGLM; const DEFAULT_ACCESS_STATE = { accessCode: "", @@ -111,9 +111,9 @@ const DEFAULT_ACCESS_STATE = { xaiUrl: DEFAULT_XAI_URL, xaiApiKey: "", - // glm - glmUrl: DEFAULT_GLM_URL, - glmApiKey: "", + // chatglm + chatglmUrl: DEFAULT_CHATGLM_URL, + chatglmApiKey: "", // server config needCode: true, @@ -187,8 +187,8 @@ export const useAccessStore = createPersistStore( return ensure(get(), ["xaiApiKey"]); }, - isValidGLM() { - return ensure(get(), ["glmApiKey"]); + isValidChatGLM() { + return ensure(get(), ["chatglmApiKey"]); }, isAuthorized() { @@ -207,7 +207,7 @@ export const useAccessStore = createPersistStore( this.isValidMoonshot() || this.isValidIflytek() || this.isValidXAI() || - this.isValidGLM() || + this.isValidChatGLM() || !this.enabledAccessControl() || (this.enabledAccessControl() && ensure(get(), ["accessCode"])) ); diff --git a/app/utils.ts b/app/utils.ts index 91f11c0c2..c444f8ef4 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -279,7 +279,7 @@ export function showPlugins(provider: ServiceProvider, model: string) { provider == ServiceProvider.OpenAI || provider == ServiceProvider.Azure || provider == ServiceProvider.Moonshot || - provider == ServiceProvider.GLM + provider == ServiceProvider.ChatGLM ) { return true; } From 4d75b23ed1b41a042e28805e46ad2b5c8111cc3d Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Fri, 1 Nov 2024 14:15:12 +0800 Subject: [PATCH 169/352] fix: ts error --- app/api/glm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/glm.ts b/app/api/glm.ts index ea7a766bd..3625b9f7b 100644 --- a/app/api/glm.ts +++ b/app/api/glm.ts @@ -22,7 +22,7 @@ export async function handle( return NextResponse.json({ body: "OK" }, { status: 200 }); } - const authResult = auth(req, ModelProvider.GLM); + const authResult = auth(req, ModelProvider.ChatGLM); if (authResult.error) { return NextResponse.json(authResult, { status: 401, From 17d5209738a114db34484838c18786924430cc5c Mon Sep 17 00:00:00 2001 From: weige <772752726@qq.com> Date: Fri, 1 Nov 2024 17:28:20 +0800 Subject: [PATCH 170/352] add bt install doc --- docs/bt-cn.md | 29 +++++++++++++++++++++++++++++ docs/images/bt/bt-install-1.jpeg | Bin 0 -> 164721 bytes docs/images/bt/bt-install-2.jpeg | Bin 0 -> 200257 bytes docs/images/bt/bt-install-3.jpeg | Bin 0 -> 119602 bytes docs/images/bt/bt-install-4.jpeg | Bin 0 -> 162832 bytes docs/images/bt/bt-install-5.jpeg | Bin 0 -> 75420 bytes docs/images/bt/bt-install-6.jpeg | Bin 0 -> 149223 bytes 7 files changed, 29 insertions(+) create mode 100644 docs/bt-cn.md create mode 100644 docs/images/bt/bt-install-1.jpeg create mode 100644 docs/images/bt/bt-install-2.jpeg create mode 100644 docs/images/bt/bt-install-3.jpeg create mode 100644 docs/images/bt/bt-install-4.jpeg create mode 100644 docs/images/bt/bt-install-5.jpeg create mode 100644 docs/images/bt/bt-install-6.jpeg diff --git a/docs/bt-cn.md b/docs/bt-cn.md new file mode 100644 index 000000000..7fe946db0 --- /dev/null +++ b/docs/bt-cn.md @@ -0,0 +1,29 @@ +# 宝塔面板 的部署说明 + +## 拥有自己的宝塔 +当你需要通过 宝塔面板 部署本项目之前,需要在服务器上先安装好 宝塔面板工具。 接下来的 部署流程 都建立在已有宝塔面板的前提下。宝塔安装请参考 ([宝塔官网](https://www.bt.cn/new/download.html)) + +> 注意:本项目需要宝塔面板版本 9.2.0 以上 + +## 一键安装 +![bt-install-1](./images/bt/bt-install-1.jpeg) +1. 在 宝塔面板 -> Docker -> 应用商店 页面,搜索 ChatGPT-Next-Web 找到本项目的docker应用; +2. 点击 安装 开始部署本项目 + +![bt-install-2](./images/bt/bt-install-2.jpeg) +1. 在项目配置页,根据要求开始配置环境变量; +2. 如勾选 允许外部访问 配置,请注意为配置的 web端口 开放安全组端口访问权限; +3. 请确保你添加了正确的 Open Api Key,否则无法使用;当配置 OpenAI官方 提供的key(国内无法访问),请配置代理地址; +4. 建议配置 访问权限密码,否则部署后所有人均可使用已配置的 Open Api Key(当允许外部访问时); +5. 点击 确认 开始自动部署。 + +## 如何访问 +![bt-install-3](./images/bt/bt-install-3.jpeg) +通过根据服务器IP地址和配置的web端口(http://host:port),在浏览器中打开 ChatGPT-Next-Web。 + +![bt-install-4](./images/bt/bt-install-4.jpeg) +若配置了 访问权限密码,访问大模型前需要登录,请点击 登录,获取访问权限。 + +![bt-install-5](./images/bt/bt-install-5.jpeg) + +![bt-install-6](./images/bt/bt-install-6.jpeg) diff --git a/docs/images/bt/bt-install-1.jpeg b/docs/images/bt/bt-install-1.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..fff3406d6564350f89d0b72b60408731f7f3404f GIT binary patch literal 164721 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�Pa3N1$JCWa+3>G$R*zjp>BA~x zIw%!h#d_v&<3ADYHa)*u48S+S&_2i$EPvCt9~)EB21Yrld9hXXRx)Lpy=CL)&J7*o zcy)B1#Tmce^yZ}OW#J+pU%2Ec+cI$9KH|`n( zhTr&2L}`U)+|E>JllOvIpl@>T2_N36VHh3~&dKkoZ+p~2iYy^;2$B`F45D^Xr=_91%jLw}dz&Bt8q^#cO?C$~7#upuy?k3=DElsqUU@*Jf5tEAD?^}+D zo__dEn^~q&z?4Z8{=2VlrdvW?n9KpH+f!ijR&d%!$NKhL82!GsG}LY{9e$hUusYyK z-L<#xnVQ^Ot`84g;g&pliYZVTb)vf$>KkTe8wIAf$(h*|k_rD+&h7?rf_rbjO-5dV z0oFdjLa&dF0>W@zrMB}OaMI)scdWX`%xhk$ma%N*8hrK)L}CgO@{48Mm_TSM~yj`IpPJ zDOqzAWX`%c$K>#s4b&)-iEXr0?8!Qu^K-6pyj_HMg&*zX@I};*aiv!hy|WCVB|HN# zD`M8unrFR}E3$dz1{FhgOhOVF(LZOuQZc>$5f|djqTUr24B?QO%^KZEV|PeM3j!tA zy6p`0jQ!lJ%<3LAFJLX^601ej#x8&nG0obu@8Sy@r!=IZ8e}u)Cfp6EtoGh{59Qez za|JK(0P85D?`pT_#FYy6!AEmoTwTL>g3jSmu=ZHXKC5C8eU>9Tk&ecpXdb@ums4#5 z1vdIB$rCxB&N?A&2q6FzxzJ z8NSk!bwMAP8*&svu@pYISFm>6Kf*~$Gus>h zVCl|s5vfX8j|SyOpO)H$W_%O4Yw8(HNP=lA-?YrEbaoXxin_9f&c#v)_+7dE9P4~x0M`bZt=nrVrIa*L*Q6^sBD667 z?Bt!y032fA@a1dygi{@D(!tEf;pOF$O2MUFxliCtq?0ZCEhunFXdDB!MdV4kyU90$*?UNt_tbuJ;zit0O1$gs z?}HXqm5&ismL2W__PFIO6Ty`f7mOQ8k23`z)dPFM6Rf|%iy1~*4SgK3N~s)g$qyN# z;>-ITAufQ(lNmU_>3_08S(+|oKF3uA4G#8a=3HnO`audb#^K z$`!Eew$sv>c>4UvTJu zfyA&z_k^YaJ|;v)x|$;RTv21o?jogp2UXp|i=b3*6jh1iVM0oVMWKgR>Vj~x)^_le zJutEoyEx~OoKWy;snFVU0etjaefo*#B=|E9L}TL6U?ftT-p`wUIxfE&CW)8BGJXGR zT#fzO>L=Mxlz)dQgB*jDZkdV=lvDPh#c2Q-vC3iTsxtT>FT0*qYPPXxr2q1~8bQc& zixb4-REgUbb>C;+Sb3^$m0dkvd9{iGF+;9*CI%7Y^KZ82A$>$*WHOksUJuM-MridQ z2ef4fAhoqgO?^n8S%O&uhF0lqnN-!$m+hHZ0vaug@o}ik+mIlU(>R!YW^HDf^%rVO@JOC>NV&hL-VG=zxUa{K-`IM! z`XCP3SgjNtAhqqWWF%A%Eer{yfbx0&nn%c~c7!7_q(N$C?+ktmA$oS3b+_(;q!8=V zMi)l3nyx~aSNZsM>XpF!>TL%7sMfJBV6(CWop{7HgSbZ#SjjOxr!h$L7F_yfhWwEn(7@k1bqu7fMTey61lPp`)rfx5#YhiLPDG$3Fk0A}sUNM` zZ}Jb53bWr*T$ced(j!ogk%!bMqUNiXp?SqrjI2mXAn1^Az{WTsYp> z#56K*weyoo4Yy?%m9YV!ztt%X=m>GK)-qertI?LOWzBTwC?a z?RGJb%baX4W_r?@X;%5w`i@{gsm0En#q2}c1pC=ai~0839Q5%9S#(8!l^a;TUr#F3 zWJgPOA0&j9NV`8Q0cp!lYFfEQrbA1H-cSKIZYG|_tn-dmS~hpGws%WNG079LADRgY zT~ji(+hSyBq1VINzG*zUh&LeCy(rZKDXOC^ue8DRnukuDNe~Chl@I<%v=N^|d{6f~ zY&4Ord(%{_68P@4{>VdnJ(owM49vl2D&+kH`1LcmBMjh z4PPjrq`>v|B;4?CMedu72Y)CgZr=0tdL&8QUS}ox)EiF_=5>whZi8~Y=lH7xh?!(r z51<@$D+x7We9IPk~Mo01Rx4^sBhzA{LS^+kEGj7}oG9p!& zb@r%sPFSMhN_m4NZIhNEJYX^0qEW3cBvJ9rtC6_MG=@IAaxS3`dPISt8{OGb!pTyN zl;h{3VmG{?IoGVlka=jxw{L$Tl#KVfQJJoxPzK;szMI+m{xtR%d0%{Daf~e-VlSpR zS7+?L`b5~g%_lvq&q<9!^;8wh_uWb>D5b2M8Bfo|#F|{P9E=ZyUJoH!48&M_PbKw! zMy99shZ*cb8|qPePG5`AHbqM%m-`26E%ouP^)FHb zUVZtattYJN4xI z|2$O=tM|(ZVV<{Ib*IB=SNim2aBbA9cSG}+W3D_Sj6xqO{%4EE;S2vtJFe~8Wo0wV zRVw6pVCuh@cRG?Tt5gW2h+$W*LzRumh*Zh^gDRT9H~+zEk5CZbwI6za_OD6EV?y{m z7GWwTlI6#&mzd3cFB$u6S!`yY!W&+>Rq2h{!#ioW_CKF84vQ0eWm%MhElVHU7!~*5 zxvswH0<`_!D~x?`FKO!+Mew?1Q%D&~A}}ACT{NM-y`ay^!O#IW3x0{(7hYiTp=B&b zKt8js^t(Q1!dx4LUrzH|=KK4RsEi!>zG;pxVM;)~dhIw>R(<-%Hl<0|_|xmHyzQn1Ul~8}=s#RH-Rjcp zwr>`ht+PA)p@3EJ|3D%C?pF&pnK`%@l8$!l^=8vi+h3LLg}M(sxCjZ0gwHjtXmF%` zc6(o?mm2SAuMnc z@d>&gK9Uq3N?GZbmISiodSG!ZIo*x7CbF?PX8dWEAAH6PkovMPXHHI{28%)>wB1uP z{)Lg10MRrBDBzX$fXqK@Gg*-L%nLA7uvfluY8ZAfJagpyeIYC)l&voA?Pkc&%3z(e z2|9LQD*w*OQr=2bFHI;^q4!APp)dy*q%Nk%a?q0LDN)&|u}8Pr9Vuw%I!FGv+oTvK zIzHN^F03zUeg41Ld+)HO(rtg7IWy|ma6|#=Djfu*H-WJr1_Y#wp-3l?LN7wV0TJoa zr7I=$00|^?loALC89E_Agb)xyfPjSV{BrKS<2~p6&YkC;=lMOq@AtWP{>s{$y>~Wm z*1Oib-nG`}^MO5nQ8ssP9dCxTC=dPd>z%+Dh{e9_z2s%PBpYd=@QC&j{CV|@f&NB- z=TT9CTF#ihqH5il9k{B0nJcHq`jEqd$Rp1+8YUZ(r$RCeAZnqTgIUOrk{fxd#+oa+ z6!l3k0&(C52~o^bCEJltfhjs~&x1biJ{Hh`E*yJiyrSjY=;^Ow;yIrprmyr(*-kCl zJwe42g!B!~G6zDWV=P-R5$RzgntlO^#Qw?#EAU$FKQ|J8c=qp7ZU2~g`=O(1^sBJ& z*U`&K*u%=Guf}7nrdGccCL0g_4sX&rjj`+5At*vWeev z`cA-OG4_O`lLaQW&W84rkA@1cXGWsm5|qj<`L^SEX>C=Z^hTk+li@bv&=bSrJdD2# zb0)0;I^@m&4Q?37^bPU7`hIFkQ%@Gq5*brG=_qLdJZm*_?(MYI)Vn(w5z)3?!7(9m zhfjR)z;|!WJ!lFk*mCtTht1ghzr^o!0v34s>wbL&x|K1Jr=K%oTU2ehsiOJm(|0*E%C~Uad$O(F%8DqhO&^7daS4O+z3#nK&(mK z*Eq{Jy4uHZi#`i3aQk9MA2=`x9kUzMicRBDyI-!I^~$m3rQuJ(Vjn7oiZcfuFX7b! zQl!*g`zKr;aDA}S-mhVQdL!-8xUM{cIQfCl_&vM){6T_Frsy7`ZSTgX)x0~9OFeKU zrPO9tw0rcC0K63<_%qVyd_vBbtig}>S`F@b)*jiPxN&HpkZ?sOCC21d)hY>MvALeJ zK!%3M8p1j}*C5OQvAMqX3fq=;(aWtM?9|cO0Ti7Mk|C~4vF{#yn1`mUjLdLbFGsg& zdmSSxKn8im7&CiqSXYUGQNe&c5j+h`8dLgZ@Zd29Nu~c8zx(25$I>sf*v7tyuUl z!|%Z@-9B>Ot<*^S&T+$EXg#o0PKC3OoR7lPbeLy^wy{{!u^x9yTaGsFG~} zFJ?lN`)};%o#+`FzIE+?9O8K}uWA*FS=m`CI7abM)s$B!bMcL8e?G)<($X1*5gNd*5cUB~}yG8m2yJ#`55Q9YJe zh+zD^&yN&7vC=hL~k{Gyy7=D36Rc;&2`3q&gbtg2D{ISOTQg_nOF}+ewILO zsCXg=L@|Cq^ZMbOHq5xPJSqg%QODds>`mIx5(1}f%n!rNu6=EJ7(SGA>NA<}74gfp z4d8}|(t$OM@$)z7c6sjZpo;9x;Ky#Jk%_exks!Oj@p-dgasl17n1P_`^jb5KawU18qdnEF;NFodq@4Xz$w3QwGRnQFLn?EE zfRfbLh^os!xr=IBPi`rypBf&H5y9!Xm7TFQyMQX+}TDATm(e?z75 z($BLfOq zW!OMm^l-=Eyw@sO{i<%7MF`(`7u#f$I2&+w5aC5>1R{_H+o-Y9K80(d(c+r|Gm7`Q z6F|EbV@dbnDO>D359BL7<(?+)Dj0U?B}F;5?K#tw%e5nn$qz4E9vvSLIi)eyrb06{~3J zf8avf*_{oRj5+s~Op+`eQMdvnv3o^$vjMM2Fky>isBIk9nM4$A%?Xj6n_W+vX$jb( z&;Q0|3dAsD4FcwRlletiM6d0uVlP=%Ln8JGvI67=b}$^#^UwK=$_9}zG0Cq zNFS_bk_w!tB=l1LpcqDzW0C#d=YHRES*7J>fsTq;C3wcL8&%NBu@!!uis$$2m(a2s zew<1H`h8ps^{1vK(ERM0eWAm}t)X|p_?8fHIpJ0%dpG-Ww|O0(x%se_j?VAr@>x8! z9kW(5rHCQsZg@-kXh=a&=Ck9s>l+xILt#dZC%lnKI2!0}HPC+lj2J;QJMG1Q?uDq% zLwv~%Jslid#n{H4s^oF2Eq~@ZKPhi$oghyz9}b#9d9^3ZO0_t9+&mWp zPGN|&;<$Dpdvl-I%)iZn?`dA4$%N_;+~pw`Izx$)h(py6NApRkN6GesgjKnS=x@@E zv=e`akSWLU-v)L6_iy9WCa3j~d5nu*UWf~3ck~QcFzR2tjqq*=MvGZniUnqCm$&kQAefKCb=uO3O0G5L; zK3ryA`I>GgaUdgpPPBg7Di}`$SVto>b&<_tl~<1W=r7d+R@?6)21G1ioHuOwo?KtM znX84vOkSXH{SsWm0JU1ei@0Q)He^=nNR61w-&4_bPrF?!YLNcvi2sLG=c_5y?X^00`_O7d; z_>Fe>T%sQ9U99xRN;bT97%&G2%+?yav3p*r@$^9G#2By(Qy1UJ$4Axm+$ah#mGL+7 z&elk?yb`5ySOhiLANdv`9~tbFWU ze1zJ5qPJo1JjdBCFI)1q^sEoXLz%tw;u{3J^hK@*!FtIv?-wq}@eJw38-u|WY?YHUn-xvP!_fQG{2%T`#;CCGC2mb?bu#?}PdgSxzpAXKz zRAm(j4;6&f91>5cU#QUg!YN4R>=7BT4pjSex7m%T?C zJ+marAJ1q!ZOLG_a^T%i1BnVU#FOcqfVuKjl=OVNLV6fR)=0%n`|?7L#jI@#0FTq$ z?4h>++JQxSCPv&MA(OaHhRaB?7RKg5rNvZ2b@3yk7=3H1bf4HWO zfg{L)dVSbNZ3 z<$4DSxsi*|u$xydr0HCIwG3yBcxrb%HT z)>ih(d6v3wG99h%iy7){#4Pk%5~S7u$P;m?DQTXG!ju3tm9>b7j8X5@=jS6H{`0lN z|0QStApDQ$SvYfKh|N&LmEVd$I@~ylG(rwwb^Up(>iuS4YRE5u8ucI+ci}nBX1o;{ z(`EUr016C=cjFw+tDGvX;PRlO`212cD~Gxt7p7U?x{;XjD20U#w_agAbhF|~GdQDP zJeR^pi>KvMcqB$aCHVT0Fmdel&-EqTqRpm;)vR{9LC8Gylgg@Ak}z~mr{!2{)6c^3 z_TpJvNAg6SlO;$qa&PMMiL?eAu;Nj~c*_no8>>sV8#sZ;0*tJ9yrhqtUG7zR|Gpd6 zYdl{u?6;iZaWA(zmi^3~-fJi4e1ub*xDqjpoF`P*rRid{exz=$lD|m%Pr)q-rsJ0n zIY!3eQbV7R;BncvGv4&?w5~JX6akl)cN@G9UHO78S~S;-gmLW}MMrOj8ogoi1q@|j z4N;gSrEc%m!Zaaq;C!{|uT>Qvh3=0^S|F6BBZDEY=Rn4m$(Gcq8#x)n>ddJ z%~DMZ=}Q}!DF5P-%`XyH>|w_>_l2yUX@xS7eKHj?$)DZVM@V~?3!-mXJMs&UZd$!L z6f)Y)*4-#qx4QB4W{3F4;Z@~CIl!?UK=b;#X#V4%Va-4hL_Q4``P_X!iP- z=5bfX>h4l@=)PF4bIvZ)JA6A1psc}_h&cHS*dk5TbaHcw;cFTH6& z4a>&4F1QJme;^-0-0ISQs~q=?NXEiq8L!%;m{FM z6pJ~JVW{o{^!)_AXzY(NrPCF{<%zWL5`p{y=oPh|_1&+>l6V9*q-Tp*YVMyiQoyqb z69ATYwh8aRafwbcYqUo;)%}6bay$4mj4$aIt*zpEOp6L?<|GhC=+p6l`f+Y{;EjsP z`5X1VV;^8!%Qs2;8YKVZVDFZG3oy6Ai4?Juc!$)`NEOtj3uN8T^^PbQ1=#95JXaPZ zgSA#U47m=OoRuA9mYv%vmnac$p_2<@e0B!WcKG|IbJ8vt5`=R;^w`5-2)p(CyWpqa z%;LY23<+06svI9ViGN`W+R9+@bIwo(Ssf|8@dX42<2|))&mF->-z_jNv)GD~R5~90 zE|^f+1m*tYf(w6M_~-EaRXhHFQ;I#NmQG|O^P9f^-zxlbG|7K!ZT&A^84kUWqQ293 zeMin=&p1a^iR`+klPkX!o+7>9bgaAi=$oXQ^8CMe#Q)IKj~E3hqWg`tb7VXJ-l`;vCLJ)M+*}~#jX0oM zj`%~(DfxEw?ZbY%^gDOBhH23w%yz8`5hgMXt|@6rNsisEFz4`L1sU^YGdl$fdI`K`uQuehZKCLVUE9s!}-ZFrx1uFnL!3S)GjG z2oa~X+A&Gnz#b+Nx9@8cS7Ni!n7z}kbEIhni;@j((ck0Gc<^?fa(_LqRB${{;yU%9 zT(*-g@hE~#IL@f-^FQ>S0)f3^Tt2H*%yxeq#e6Q`&Sfk81E$yHorVhQ# zH1)Do^nvTWdiD$1WAd8!;5)Q5_~EgjO6{>*%wy%y5rGFZphS7D9;BG6UYwEGknHJ) zc!yav4nrEW3Br?9Xt6hyo>$|wI&tvZ9=Z<07OVp|giDQPD`C+Gorir+U#7JNuU z=2@B^=uy4~Jg2rB+Tn7b4i1ms&Aj9D8khgEQS*oDc4!#3sf)Y4^St4^R0L9x0b@fb;Hbe^p3fKmc z`9nlIzE~36S~Eynv#jmi>Jd-)C_FcRu91xeNTOA8lB#Ix6j}_y^UTA42>F6ff81oGkpUMy7V zc;7sJVdGl1B!3Ww+qz>CF#K`YyZu15pI~#HWK)^sVrHwDoAy8^EvQJM9k1K-HNEcf zz)O=%*ts+eV{KQq9LkCW^Z^h1j2PDm)M7!_UhnK-g!fvFP@~Ccx~3vtedv~QI*V3q zS^X$HN&R>`W`IvM9@cv~W9#OKhp9UVo#ktn6J2+=(y@+Ml_`1dnxcJ(@Y$>Lk zs$MId5@Lnfhd=mr1hW8UH~q)bDDBn3A&E_MrmzgjnRJu+M4p($CB&@SmaXlLTEfjK zm}Y;c)dHjrw3>F0uF}xDxccf&kDLIDl3FfBZ?gV!u0~HM$z-zxvjNE_?x}3~hAGcC zw`gFyGqyHW=I}v*u_X9Wjmv`O^U_C98_`K_Lz`v0Rpc*&e%f!F-xZ3=53~k`!M=B` z*E94PlpP3uMV-^KUWDgoEa|pJ@<>Mo=%QCC0zyhj$;q3!Zwc_)dAlYGd8-4M4!m&A zFTTY{VWq3m7PMRLp_yJigPJZ?nGX`wY$giLU55FW%_;fJgAQ_qAGCpaJ?C*Q8QB{o zTK-u9DT7I^xqi)(F1*&H2$Pk@jtNRxlS^2an?PjH8@i`MiVHko*a8)DlF!`II&d@_ zc{rQ8uzqdf9PE-1XLj_DWBev7R|d|pHt_wvMEJ_9x2F)i+U2j(tQv=LKiyT+)v70H zj$P$R0f@A45JUPpNf7K{`>$*#zRk?q&1Cv|Ox-g2{>aTT#{l|zzzCiy*) zO(Y2NrUnh!IK)B@pS7EOfG0Gbo4W$j?zk~VE-dqO^*!dHj_Rprk%OCoY;dESq1-e)1KbVu`U78#nN$Hr@d+ z<=LW{^3E&EJ9ZuC4(wJ&?ay^hJS9HR^5oxoq95*DJnPa5tqj7^G{N~cVyxwe7a5Lj zzt*N}VNyoEf-BOB`535C_0rF}l~4VF^V1^#ir$_vu} zuj0IgeZb26ivq!sln@+Hw1?m(7}g{^cyytQ@Bw3uyTz_AqqLB-)8cCz=hm{dMa8y1 z>UH%Ikn{`3%>;k2)b?AoPhYkC3Z7rxl~+cN?kPt#OG)u|Nvi)8dFx|2JkBKWN3G{! z`Cs{z0%o;L?WaaNr;Lrz7hHBx;V-l@jq@&vkH{1yKHb}erb_S;FpXL(-u1EA8wAo$ zPzzlBL-PbY%k|RZ%U`Ay!9O2rc+Q`P*LG+4`lCFfK@uCX5WFrp6^^`Q+kU=@2{nCF z?!Cb+Q;5-L6_u(X_RQaPh#k80trlcIlR}IJcSJUJB?Yfyv!icq$|&YZ&7=<|E7*9S z=MxCHGsfg)hm<(5-${mDKQ0L_3gEqpAEG@Rcy|T)>T|vwtS)>k&*?Dlc$n2QqG#N; zx9dJAPC_!tZM~Z1GoeNca=phrht%~fdWUCgJ+r1oy)r~ELU|kFvTOy^;Tf`@~W?90*ipjp~nAo8+A4^o+`0z^YFkaXEhTPSTl2f zl#eFQAHQb%QoH(^YCZj>{0nn4{cP!L$d+c1RsEr3ZVt(fmQ4y1oXFU4ArAIJXJ3t; zl03j22Mim)ne%aL3)I22oDe9}I83kHN|w>lB!1QCSH<9WJ{qspoeG&lN~u|yrEOkY zT9wpkz4n6VRr+O1l2)3!{l!Z>HF5e%L8OP(g&Ngu9?%PRCSQu1OBM&X`F%`n>U0;~ zZXT69!InF?Ib{A zICr#App0H>m3BdIf!ioX1|WcJk65PGK5W2^;J*;EO=tT{$=h#6c(VzKczrj<-*{^< zx6zfwSj}x+iRfoRipZqTo;p8i5DHD{yTRJ2Pbr5XTM&gIp6xjlK2MBdyz}Ps)6%58 zPC&&+XGnncNh4((lBX>%&2Jp;PqN(^ccnF(i26^${mECzhII)IEU;JXl_BAuF9T$| zGg~oyEVAR1x<}ou72ak2`Va7;=LK^g-Z3Vt2j!ZZ8Z`Q)IYSdU%`D|tngdq)!P)a- z)3Vgg>Cz}JGf3V0G6e;Nt&x4FED(U_Ax=;ERl>)(NjYC3j$Xdiewn2BiketG#Yy<) zc#0s7*se_$)fNfYeRSR!lDsDw2TjQpe6s=D^>s_`T?=`i*0uAsGz9(?=|C3?fU!i_}imD&@MKX!-21(aFbidYHdP6OSA_PvwBaI zpgoc0+-LSsKt$o8!^v2udky4=8}0tRQAowymm`Ml-Po`#sZ@yQAST7mYD)J*UDGE{ zhCo()Iessu>jvJ{?3RE}cYtthbb`xQJL+VdLk3@Xz5-3^@Z?Xwv8gbPoxV0Akp9Te z*3Pu4uv|pE!l-lcC1{bYz}1~N(8<+3RLgoHpUWu##ScRcU1?XD>?E;BL%?o4TB#xX zW_EJCc1o6y(k$uV0-0UoJr&&#ROoD==iBj$v+6!nXA%ZHt2JTUNrP3mzm1Tjp<=5l zay+s@|HeczO=GPx)IPKYz&?~uaO#&k)Eh9mWrsiwld61eymBdx6K<&>e=Y`{D({5y zUbLv2ElK5k%yLnj`syEBY%f`Xu1q|kAp`VT`wXV>foroR9c>-$9R>!S5%DD*=U9mt zRx#+(7gE-{PvkS$Ph+$*6pQERJKo*0cEC!AY}%?wrl@z7syGRhNNj|cAkHRItK;di z*EeS|q|d3ZmPV3p76SG3v_`=j$a=DxPD;>7LQig-PsGUenvYdK5kYZV(FxDB685D?rHou~IIkXf>i60b~A%EDgRK}ajt#%L@uG4gIa%9z2uXI2PN(#gB{{=Pq}s;E8ZGZ^$f|V zjLGB~p11awKY!u^_!lNd^5|A^l(%>)L};x?cXyVXAk2fLKJd>(t{r|z^csI?Y0k`f z%&dD>k*(O6tEXRT^k}h~6N*tkLr;IA;l7U{O}kxYo{Hx$W~3Hx7SS8VyHwq;uAmxw zUq-$#wbp7d(NgJ#+C789E#rm;@DGtaDc&C^)1J*A(5J4WXoZ8Kc@h=P7+ovr^%OPI zdjnzs?#-m#N2kl4X>L6ovY}X5j?K|&NJnH})0TpMU!z*tHbiS0Se;_qO8$ij!8goP zUzZ>vCV8`E!A%Gi%fY|o#J#-eMNz(|n(kSFu~Buekj?za5j<`7BgR_%%wEm&*z$5r zi_`Rd%7@T6e8!wj&T47w=Z437g5v0pKeN(!c^lab?qC`OBq4rQGraiyjL5VP`vl_L zuQkTq^XJ5dH2LnluQNLDPD~xYN~Ipsvp{_4$>2mo-1?|PrnE~&V%n(rm~WBI#u{(q ztd^3oe}nNE2!d6@Kx8QaCkm^Qop}~ey%!idV=q=B#5Jfb(YD`1N0(SujHSk99BN^S zP%Pchpm(~_8ABCqqO12F$@GV!SV(MYQ0s*!LS{B%Ia+1!|GiuZbcm*M`ql1PX@~>If*YS_CLcvglESx&nBIe*D?Z+m;jh)D z)xT+-zUt^ zP=GB8P_>5;{qh!eez|`))P`(@Uj(A~nqu-ILtDXBKC3Dg0sRugH2>Gc`?RWvtkEuAi%TS1X2Nr~-D91Y znvG}Se`|#Pl_u-Iz3+d%{AUvA|GVG#50XGU74!foNu38+jca<7m*rn1(TICSYso~W z;d~1vXqaiwJaj?;0ZL+Ral|jH3FajCK#bO>cTufz}06H?w ziwG9O#9lA0yOE!>cc!#CWKWy@0D|;ILQ*{xJRxI1AQPHFgE!GTKz^n^u_0@VsU)l` zGXypOZ#k{ast^!v4^dj+vm_2_dMB7UV0;q?Rp>{?zI&~lN+j%Vi|VLO8%ERf>@K&@ z1+F#+fj(tx{Yv8f3auoVMD`OXy+WUQg3Can0@*aFTbHMT3&2eg+@P9@@yq^?R*ee; zCqV2gM;}ZgTH0TwT6Uch&p+`?g8uoqS9^Og@kWF@R#SHY17fk~LLys;k}ux8f2Wzp zqub;>*!JB}Zh9cqBa?%qe$rQkkBg3E^gp1+KMZ^^9DfJ_3cuW}dUJD#19!-jE($;Y z5W+%~`FewL-nR|!ADhoL9m*dHD3NJ0$UHkAqBTcDZeXi5yWtt9*Pqx-yBupx@aiku_t3%q zlaTz0Os0-g9thdu(rq`CjB6S?&f1kg`AhO$!#A8t*tg!;zgM#^mt&85Bx}Vx?`18+8;vsqH2j!w~l&UB6`K2VaMP*^| zR2$6zIk~7}Cf&9oZT#%zTq&k(7E`UJnozy#qre0$0(OWP}-8<$~3kz-1p|2T#PV1B@qEr{w*{LEdBmFGT9 z%+@gf+-w+_Q>D;Pa!!s9LgwjA7J((tBNqDl`U$2yeUFu9s zU5}j%rFg2^%=}P)o^)Bzh=l?##Sk2x(fK$=t9hslRoT6;_&7{F{gc6tmVW5*HQiv2 zYpEGuS_B-wJF<t*H3_EmgwCB^Tm z&frZOGXlBF+$-zlo>>!vM(UiSGgA{6`F>*)vnJt-PRsF}L)1W^eq9eTo)r)2@UD$( zw=3S_1ik9VfoPV_wgX1x9kc)tzB|4aCT zfLGyHGt^F2z@=Y}(-Sh|fw(K}cTZC-SNHhD^Xe*^B{KGiTD!*Z%f^0Vqwsn%ivdS% zNQ8ZEm^cO0UcXha6@%Svu=tJbVzRWw(s;m}PRK3HR#2>N00X~Q$z@0^roygxSI87i z*Taxjz&hPP=8ZDM6f1gFo3wEhJ8OP5e^uTXSD4BO;(pcCZwRJ}6?j0_j7rq(UR) zhJ9isOOZJdNO@S`<>^Hh;$VTtGKF|TQqP|UAdUv9U_I;F3L@V_wF+wT%hrw(E`DTj ztvWoWmQDNh2<(bJDmSkX#SNd=ph$ICPD)!okNY@=c;${y3ym!oYt@$@w9(zm%}y$4 zO}FSc?e=*5io_L5{;E~$o-k8OTWPI+<*2QBu`NsDy=F)~HDxJn?FBO4EoiB(HH;sY zN2&$a+Z|SXFyt$F_a0kWlk(~1K%G}LD>J^fwzSVlyJ6U_4n2hXE)z+F{6m?e5MLQ6 zc*zs1vvrFgv32G(5jCh#b6+Yd;9?Z?q_%9iO>D)__wRntEspyUckx%RI{HO0QOl2R z;G)QKCT_|PPT)mS44hN`y8R{6aoZDuAMny%j(S6`8FxH}{(NSNV;t&G7QYNq-A6j1EGyKv+z$-zT14u(8H_etx;)eLQH$ z-;n%_4DmGM>8y4%vF%Pd2+#eEjTii@$GpY%Z){D>VEHKi22o_$U=qqx7&T}t-NnkcQp<{IYYMs^ zH#K>v{5mO&*br#YP;+6Oz8$0wCOd}jGlTmZxeVs6Ee3rBnnl=7@Rtspw*Rs~Uzu)= zvnGd|=a1Cpg#dmKk~~+jYP3-tmN=3!rwV6wPPYz#59P%c6LZwbuc!-egU`l6UUQ(m z1A@ykrt4VXf(*YVP>?}LH~$A|7W*uX<0sRgyh-W#2N?HvV%ztegl-0{<{)h*m7ABE zYIqritqR0;Zd8aWny5Wk5VP?~lFhE!Z;)`-=g{bT2o6n9kNR>~aw1KPuU99_PU=od zrm&Jf!n)ly(<7~6B$s%ur7O*pB*U$%E)WPpnsOIwS5}2_{R&syUMzejQn;!H>Q>$1 z6-fa=F{?MWr_U)H*ng+hdaex9KF|@762{xIf*6uGbZ3fBw+K1y7W}fOziEtq3EaTU z$TdKk*XTf`ow;YNOuOi(u&2v`0T}+Uq5}zNhY39DM@46$VyoE1EyvZrJutnxq0Eza60{h0)me)}|k%xKL2d$iU);VTz z*7g4CS9(s#)<$^9WPC1fZUnF@kMd9EKwi?|Sl}KOxE~8iL zMH!UF?}4PU-%N&DEGCvX&XqePNbw`E<^C~|{ zWlRy3ZwVZ#;1rP{(PmGq-vs$>)f3Y=cr2hJSP?Rg*1hjAEhAXl|HzO>?`Ee>BBk!W zYTk_Q6!6``?Cs`BNe}#Xhv0a|#!jb!Bpvf>0y^`_cn!jZ1xrHaVCa?Vb<|WFLgT-aO0qv~H zfM^DbLscKoSXbLly2ev}+AeH2$v*))(c?zaW1su@1aSrTdFYO<|B-D*VsSu3LJV`l z%4*X3l&S!hC<&`C5gZ?Yay4yGZLo&)=AhN=Xs)l^Ty0>N_7TI4=0yzo%Nlc5IP@TP z)fXYFdUO@zc}9TiUbXMSP`S(qlQtLJmFesZQ+c2nN#?*RmWeSWemu>k;`dXdN=XH zia>23Wg?h)Ei-(ReD6OH@E?j99&3)TAKE=U7ya|RVlT@$>KTv1X*1p#iUy~ypl$H~ZOF5W?xMWehg1f0>^Ec9uZF0wA) zz98VeD_)jXbiT=Gt|-oh<8Y|q2{r8SlwPTiZcS2YWliq=vCE5~{ga7~wI90k>5jYx zt8^q-kE#sHq6{?TwIDlfye~C1TXe+vt2qVAoKDr@U||vxW&d4f)^!m zccz9e4wHO?q3mvBu=%r2PyPK6+q|=!-i!H1c#a;)m;Bb9=bPaxTP@zNE4mbu6SITE z{I5Z~+x>6f8~5h3bP~EG_rv_!OBbn1<=YHpiysdEm|a z;Es8vEbrUBTpPpDcX%00Omv`g1%Pqtn)sQyg#*qu|G{@HGQn|EDY=#xiyjXaD@~r= zD{?m7Y`wgL6L@$G6L0p%4qbp=>CtLTYt^jAk}{6=6_9&c`@Jx`3oE@s#Y`b3Fm6DmW2>(jxlb`6|hL5VUmxvL#R5zQ@q$8tI(Q)A>UVj!G ze12x^R$r@Qr%M2^s*;P*(rqdfW#tYvU%_{X`q)HkjQmO|pD;I4D49@qdLE2^PTfCE zv)W4vkyN%tK|Wppn<-z(0+a@X-PY~;{PJ-qKF)7;pvhBiREDoQuHdK5%68Ft*;^EC z@w(}&E;O6;q4Cc#sz2!+F0NxoMY4{48uaYR~1`2a=hfux3>w7{;4)!*0_MX_3qm_)n} z*?H7w4ge$ePJ^b{NJ{Vj-HH6^TfaHee-Qq_xBkc2{lg7xf5s#KI3m9v|3N%*Dyt=s zeNwm5oXdyWnSDJnU&C51)%XJER zOB;QyyvwP|6(T|z(0LYWG%;L>y&l z6Z!UC#&OtvQ~mezah{30Ij3A2jVJ?C1qXfCvhay2 z41ivy7L(O9E4I7T6aYal6T-SgD(B-u_aW_vK9CZu*e;#yKky|@UJlkfIqA#!WLsai zZR~iZ*!SQb-?Op)QEo1UbsNQ)5Q})j3<6k2R8YxpLjB{N3m2bJ(J%O*6=mg!Mm4Kv zdLO$xOeA*dUBo`jeI8I?_RHAvX#gN8DT!;OUdk9QYnUQ;3dRdb|7neY3J_f4ALu_i z3wSjTMFbUAj}oMRem{95)agT0F(cG-ARDvCb4K_3D4Kbt)0suPAX>sxxm{`3+~~_` zhxaH?3@5LH>b5GIa@?+BCV>$qK9mK6_h}h}VoZ4Jk!^f)MS85^psrrD|NK16byDY4 z+w^4X@y!{G4}Rb!j1M_X(H#e`RHmif9&nS8L>XYcBAP!}NEae=5Ec5K6*1*V7L`nk zXRf0uR?wdGamq7p82RI44|&+KjF+^fYid^2ogNHeEw|^TT)>m|sJKMeW_EHP;3~05 zWk94QDivU;N`^7=F!zE=7&4dh_nOyOBQoG+ax=EYzq2(x1Q>H9(qG6Ez^KgG_Y|GCYB` zYH^d?<}5gG{wbi=KjdQwPRUC7Oo>>s<>F};b@Wx5McuHBm|_ z`pRva1HZWzaJkWg9<{Dm-&uLQ1y?yuUd|yok?g)J>EeYixjX{E;##E?Y-0ginZLA= zMTma#YK1wusgUYsEQv?0ohbf^oE>M-ApC2)f`Yz>zBVtf)|dKL^ip!NiAe%?xTv_u zvEOuEQj+7ivaMF=&Pch6`!GshcHaMz?&V!GxIckuDr>}Ube{vNest@j=5DeNgCl-M z58s3DZ8Ry`Tj63K)g9$tv%OyF7C%#*`f`ZFgfn&6P-RkJ%`3g2(p@{@nQzL9Y`Ty; zi>{d(h6uhsai9|uG(p#1sMD)s)XR$Btj3^4(xgl*NoIb0uEO10DM^nE1^fZ+*}@od zRQrIcexdK^3F*8$b+KstgmkiHjCMh@Iq4T_0YUnN^o__c3m8w0xf4{_>eXP%d6kjdOrVm^Zl<1?6)#4rVg7YSi|!@B z#GteAEUs}DL-Ryw-;6a@wHDyST{5}1!@^yj1aDN`yX@&Vx(2FIH)n#7W24)A-=FQX z@wG$tjXlzc7vj1wgGE5)I@ z@9wzgc)<2Ey-DR93q4=b&8}V}*Ev-$MD{hI`L`+O>pe1>VJK1=a#eEPJ^#|A=H&Z^ z+u|KsyW|{lAR4%z-!lZMZh~NDGDtF`q#>+2!xVPSMc7q1ZCLWh%(ca-ii-2IVmUbW zOMXOM-e!O+(r?(Wp5(`0Sn1z@GqV=GM1m;JDv8f0HfbX^4?;WaQ9gsqv0oNxXBdY7 ztDwVJvAENq}>YI+U4mf=>Dk((L1^8K6OjOXh; zLwOoLvqoK^)X&!UA38VL$4LeT0ZRfJpE&d ztbw9k@kt`LdP3-$X8A`QmoWmnzc}tuGM%gd7`NS3Yww!M0_bv%O0=5#gd|BtMRI^zWez5 z$n0k|*o2b2FJ!|N%5Q94g1aDy+kW8lvZ>c)wpF+Ay;Xav9DYCUx7(w=#}DJEi@_$q z7vyUi8Ii%Zi#(^E!Jc$06@lI?oo$>p$*^$rdwE7bdy$kD=o5l%F|N~VP{vA>3=B|Y z6DICP-3LlAWm0XCcASJ9*x-WZ(@%s>d0mROPMNn$N0fN=E$@dbzNvf0oDT%=`kTG3 zw+{}?@;pipYHn%0nK*R&9p1M8{pPyuZ>& z`Oa>CL zLG7_3WLv)Ln0+h1^vzJA?v$1Q@%m7z!n0L%lH12j+e%EFSWDpfOk327RQ?pncEpQ# z&qlaiF{!&y3W4nCKpfDCxq^pUEunl&7*z^03nMm@=Tze!9LxoV@I895@S@TTbt_uH zs=6bl2@lu;!?mT&eds%FR!O;IxplYCd;hQ{sQ(>LoQ99WR<+ZDP(4!yOwC@q=eu`S z*`YqfqiyJ$@%T$hOcA0<|F&Q23v5|b!R{EbfDHg?>_#ER+{PZY-~#aG-93@&;P zd*}2u`Bh3xxWfv0pT2M{A;2-9^<6R=8padezeAFlp!r_`1EpqEjAYgu1As~x;MUh2 zXhHSjE~o^vm1ZQR>mKuf=(?ilwIFk-e)*?l*XjFOE?H`Z{Q&KxpzJ{*w3%PS@Te+4 zia5dtjaXUFIP@$CI78<3_63|x1?bZb}SUm{X?)(%bK8twd|ZL&s;iTV|_)plQi{FQnK}jRTEET6QF{V zx|&x{TN;)KBLpe8(1FEFBJ;3k3jHEzZ!u$Cpau#whG)7SZf<<79Cp%EBI_$ zaQe}Pvg5!|+kL(M2IQk4xk$vk=z-bdlJZ%Z3TQww&P=B2i;yGIT8v?s0^?_Pk}k^k z4O)qknb_%;K!Aq2}bazSk))ofC_z0lM*nic3Z=6uZPaV zYuGu17Wk}CjpP`|pY$oizD8laMuL}3TbK8A_u#-~1IxMWwIj6c3#5;Z>GsTGC*$#R zC-;feu|Um2ACKB@S<$UI#fzr>9e3ngHDGf^H!7<@FxcLs10zbD&#lVd zTK-|b7H{fqg>*F+0#&7Ukk_ZeUKyM+`LqG=qm&J;V&6p$fBwn*;aFkAb8AXglO?Ic zF?A2i@#xL7mLLaIrl@EaR@*F-{qSJCc^+6lG1tWO`B+t=4{U7r4h6Ok{V3jr*$UBC zMR{h=Z@%#ldWjVnEfJm1vUAiO?gg}6VB1o@NLkIyO)IjJR#K(E} z{Lek_KJOXhj`4l(jqiSDWbCo`&f073%r)1XbFDS!{B2(V+{OTeyShLOMZOeM4ikqr zYXMmpJDTKbZt|M-Txd7fl-3onvG=URTcZ#vuhD%vW}eky#l2pPtg69@Uec1|hN?P8 z;rL080gbDgrI)R#FJt*k)z@c6vwaSvyJc74Stj;WyR>g7 z@lh*(xB3!rf!mSQ>|9o|KYA5%7tb|yq^s-fAhBjJykDj3sNBJ=3?ImQIOaR2W^xT! zQtfl9VSKG9>EAEr`K@+%4tVke@&@1k^ z)Pt#!X!?pdx&!~doK)g-u?bBVE|(N*6&G6@ypr_W>KaS9`If>BphURja&h})4WSbv za|!m2HKY-_RIT-++S^hF|K0(0IrQu*}mqa=|kme#|Z1?-SGX3Ru^q*T5|HAL+=MVjgxA-|-$jJUx zyv6UW^W8_s8q8oRuv(_O z+U0xN(Y8HQWEQNer#u3r7*{iO%?)DSHk;+zO)>Ij(CFgnDf)89o_9k8dJJZ}dJ5jb z`t(UDqi`Rw0Gjy3C*C^#tXusd zL*%xC|GRfKmIS)Y^yo-uv?yLfO(@o-D5i6JcoqJA1fYZ83#)zZ)R7D2g%9RRnbDG- zcdnH~9aoV@2k!GBkte|!C{IGxA%MwEr<%KsS=fwsJngI~l6z|d8S-o)UW3rXoh#sj z%aDvze&L25E!?KDsNEUdDR*%dcl@)aSEf_du%dbrgGf6&GjYSwOiqO`AN#nR$%$F>4jR-KahZ?KW}|_H4kxE8 zvR-^*YV+~ARZAh(_Mk6@%*BS%p>#b7DayT6+38RxZdnViF>0rGjmS+{i zI}<>)&Zd#@dQBC~rx{0kL7FtxTYyYUZVodPaO3@#2qu11hMtpVf9|T-oZ4|ezspp} zWz2iB>nZ=>)nA)g{~*PGIQbWwS*OfP>xiOnB)*6!I zn&h;R1vW*iVkg=H=k7F$`F6c3!FGgP7ehH7>6v?01qsKdyBLSqHw1hhU9zTi>eI3> z!Q|5f#qaR6TcpoYFhUE|nnF@cZWN-IxsangPIx{`Un6BvQC!<#PU?5!rksDOE73{A zcVLcyYkk5ND+wsPYCu0rG3*MwJx;I#M9+m!8>m<=jN{=+u{%vXHs)`+D?G zvzpg{N5w`+J=;u(#|tcs6&8FIhP^YJ6eEt5qya6qdMv& zjoe|!i^P(02Vig=eY!ySAcQbGUHbjGL~hS%v4S^eNQ);($dBbpLuf;nH@cRT7cu8&acGSjazo1~46=;* z{2JxNsxmU)sj{S>G+0iq-cGuCzsN_7NWSn(C%xs`YA_2y~!lZ5J*=)BnM` zzp|$MgB1Vaz;+5Y$6uKXEf(e(XZ z!m0B4MY49kA7n_Dzds*E#&l~t2w z|0?wEw^mlV&CqB6A8PBQpOwptxb@VjOrX#fH*xl_AW-i8yQ29I-SOY^^5>>r{^u(% z{!_39SnE!teC40=73Wl2E&n=~MC7kr62fGxT<*V3GS&Z_fBqnD1?3OHTB%^6VP}H> z3asva&*Pt0@!$3CPZwHynaF%DFe!ijIcfZ#16_$Ydtua)@7k1Pt%C97o$f$O02c z7h6r-iKNcZK)DaUIQGBZ`!8nE5Oh)D`y&Q;2^v-nwU+|i(Od5%g5x}LkmqQyD8|&s zXY;~+NO~=MGj{H-#`imf?{>aF$oS&PzS})?mKyYsju*-!1p<+Q3Ohchw(+y#3S$bn zV}N6S)&k@YGWy#ue$%fbWQbCa%Npz97zl{EDL~cf?6F+^(3!t%*6>5!QJMR)!&6Q5 zowq$$V4}OR=jWc)Zmi%~!r^x8{XBAo7#Zh;u9#99R4$(_%jp*LMvscSR> zy3e!FpmBhXX*QT??8K;g_cX(((fGF)!LaM{_x<(_cKAxOIt)qmDq7st0({!cN}uXrymZ$tky!7K%p@#(zi#B=9^cu+ z%jd4;7OVVe$uvDKZB-SR#RI`l?-q1V7T+DVMm0u#tRK9oyX&U!`j*(lU#MA1XF52t zS59xgKs++GBJNWFpuD*`)BZf!k$`&aok0Pf80OkV!#O%Iq%GLU+j=ZiXxmNy*`CM% z4MZX@Xi#mUPqEi;U-3h^8|(eh@D$jGf*tvh&r`fp{PZ$_$j}Pm3MXg!T8?lLf3>3u znIR&Wd8JZ=?xVoovHfJNH1+S_PrtrZ%#P-{BX&(F<*!R1-7x$Nac z{$dF?svB%qJD%;-p?De-1rp0aX}2d}wC$fRhg1|D?n5?ZNhlXQHml!gHUVqUeisahM*l z;l7mGCxCKk9pd!|+fswX$J?`ay!2&Bu8Q`FY||~)N?x5`pEE^pSXSbS3+mBf3&yw# zY6*1E4SWXqc=f(g(=!=gPSRq;xE{YVooi?jLz-(=g60-1E^XG$t5cBnEUi3`Jaz=y zn}4q6;h7*NHqLM-sM+zIgZwAUaJGeGOlq{;a0)&mT+2R3$u_p#H2&RGql$vR97BJX zDiqB=E9p86#1Y|90w``+jyOmY?8Y3pCnmnSz=+x#BHytAHb-zcm6{%hCi00nGsUbK zAD3hKZuaoh(@Epale!~?wtqRjV*3Bl^y<%l zQW43C;Vr{j3KRCBJKf)|NllbK@}j%aut1&){XSZpIk*%_)4eAS>Jm;hUA5MTvf4)t zVP51@*IVNBtGLCWeC#f_wx-~PwV>_nd$}e#sX64kaZV!kvWWpU6}&2E$-J2BJxlV? zY*qGWHD$yWRxmG{pS^?8G+vtux+6WIx3OSHdemH*p&@|pWnmQ>34(2JI}+~$lk|MG z#J{j~chU?(fpB^;!G3%FXzL?e$yMh z)&MC;WQXN*3|mp)4a-8X-Y7FV8iE)pFBn3-Uf{0^ zy-44{st(aNb39gElUe39r8setkS*2F)v~iNVHU`G{_+j9lAGo}*UZC3xr=-pHO>xi zP;%?0VD@jp=pRKF(0Ka~yt z%4m9;^PJ1gx2@7qjLylKpna{HsqS22_Ni7w@EZ z2a)-33jdS_WMseQr9UOit$&w^^A}#%|7gL z@0h%7B0tf6Bu#_t(o6m%Rp$1&g4dA?I^HELf3JnDU;se_!M0DJ*-I##cUHQpGs}JU z)3;Fsb9v=+WCMtPutx`&SW>S}94l|YnEKUus;O1nXws&-eiq~sHC@>YKP`)p%FY^X#X9qbbPdk6Vn0d{mNi$?0v7g@^4!gR zrK;4GFyrBFGd;bOTMllzAEsC|V9d(dDF`=PgnXP2ocq>RPO{hpm-0x$ALV#?7J&9shuEkeJXe>xyCJK6TWnxcfTz5gTSZGGL1;uE!32(C z_FucCM9oXNuwD9YqmWnP*0DwngmVs`=mY8CP%QNp^w^MbLF5d2C>mr4L#L!8rD!6FL>WDaX(Cg z5jn28`SEh+W&Zk9EwpRIpn*p#5STKqPXh$CUNuQhRWOrYh9svBU?CG)8qvBbb$ez4 z0}o@P`Fh?w1^0kPuS1(Ng`5rF*YA{kv{_olb3!H$mTmBMPs_Up`g^+T0>RZif3q0_ zRA2s_`oC<(pV8%C@Z^WI_{R(WQ6XNIdQH1x{!YQmVPnQ0F!3KLDr@TlQ!DEaO7A$v za0n(r(?jHgP2=7*OcZ|o*jOuzRP3!Cz+R0i8DMn7mfdcW^fu ze&h3yL7xjb9TTB54}aESuV! z9q~tnTWXGGQ`P6KcK-V*~8~rU|F6L6uvK82SyOrMd zlK4}G#8Fj^Gv{`(?gq$_MKp0bx2>xO{=BADx9qJk{o~2aw8Q5&-qp0L%s+W}GGa4d z(5T!P`3Vt^x!EvkK>8kLR>z&Mldh8U)GH_9feyTf*Gt&TC6Y#rlOCUL=37?AErk&f zqi+zCD{j5MCqf78SUJA|$r5%MLS;LXjQkt?u{1J|w)LT?exg$kbj5^`c~1+(WlqYh zbC!lSD}DQ8g#14l7_6YMMap3FtHCB`taBm%JB)-XN(1{&Pc!5kGGw2%|AWjWqto2l z9V@!z3rrrX!6^G~+mja%z#ehI8T?rfkyCTpr=%J^Gp`ctVEy1xr}z|83g?HY!}-cZ z{$@)~qIC;RC^vZbnt~o#+V7mR(Jd*+HX&m1p*RsD+~zc}RAkohG;ehJlI zTIQE-{O@Nhg#XjR?zZn=hLoN7g8&|L3^H+}ngG5x!;KWikH}?O?%6dAIfviw3}rbX zAm|4}S>5dIL?-_{A`W5(&zf6gA@pxP>FsgFOO@;U?V!D~gHlpu;)w^>j;FOe`)1_S zR0TFD?@pJPD;F?D~1XOBWg@mdkQys19=i;S>pTHv*=H>-mzbs2F*s>%pqaaG+O zUrf|^t9F9o#Qj&Y=mpet&@-nY?Kte+31DJug^}N(UUHoBzFoZ;;}x2j*}Y@Xlxm_M zJeB!oX+UY|*vx*1)^Y*69d-ij$_Y_3e-@r=R6yq656sUXG`>`kHGng|&^Wj0p?vH3k{6lQ!&MX~`=;Z^o7Cb*GA=gm^o@bRJW^q}&_( z*);CrUa&b;iqyW}{e8Es(1^cq9zXjXC|@M=WSMf#KK-5-yYo9!isU&@Wb4{e*O)Aj zDhxA_@Nt2RQXx8jE8w_lpNdB5+y1UKQHt!3g!zv_EX^*#N*&LS6g^_B^-Y#l>gg$(`&W6B(4ci1xNY03sPea zt8$QER+@I!ZPFAzbc&NZw4B<2Om@hf)OlDwJ63^9GBhySkZkM_a}@$7lU~ zVv2MDG;wl~;H()9zQ_Oo$9}%Fa2+H(aX#B&3A%gO1b;hZf1c;;gm7rO&^m)@}H{1TKl~yx_)Me)}i4d1cg>R>E5D@ty!iR&hRwe%PkNx6sJ0cJK@JxFS2u8jdx*b&A)vqhAfw)k zv~X2Qim)P=`NyTq-yi)(^OkR8Q|U~5ggLl=V^5*|`+W@V=bMG2z6-d%$;dX|b3!RD{;vkN ze?x5x&h90Q{2=okUPbSK7yt2^|Nf_5;{DP$PgGY!(JOoSWAUAXzuuDYSBtWbUH|sC z+4frSdjYOzS^j~4e@TzOTFkvCVa{=Sp7{SBU4ngv-W~6M``h|kh*~K4BF2e$22Mh` zA%a-HZ;{=Pdi-cibmr>Hl89*IkfLnfggSCc+2l{YcIRtDoclSWtmLRX z>t55^E9wx#h3g7*{e_}!!F>5X3;JB~#40dSode9CCD4~tR(*R^2QBluyh~l+e1PNT z-~(HSE%6|R#rE%J-ot}@Io%#^qQhYR3=7un=-fY%RlN1hpVDf-*ddrkyq2jL!1Z1=`JH#ZHq6mz2%QBFg^Y|>%Rgo#}Fc7I?oQ;ipU^2A$*taQMz%Nz#!l2 z(V%75aVu&?N;UgQKQObuNPHcVq^OpI!YGEr_=eXmQA3#E2<9X;XqxqzJx^)e%gL8c zMJ*fPUOsC1L z`?$kgm|XT;G)8^Xq~33wXA!OiF26IgR_#FA@rw;vYqJ{04fSLRKwp^Mi;`;5F;X9W zk!$7z;FO|MyCiu+KM>D+&1A$wh;fp`x1@__SJfw&?Vg^ltCGW03#VQ-z(V$-xbZqt zDqxz&1PA=E>Ier1C^kkkT)>8UWSQucH6)oc*X9^6G22;6??(9yTD{A}=duBlT+w%? z*H<}ik6pVddAqvGUB^?z%Tx1ye*PpnB|SaWZ$@4~Kqw5ttdN)(LV9W}WYq&zZ}1`Y zmPxqaIcSXl)T7`kor#l_)rrL}k?B_5WjH-zTMVSCZY(ALNAjxfS+EHp+Od3F?KJsX z?b_F#+9&!T3x9grsiN2270vERWt*Sq47qKk6IbU1xVmIugVVBk*T8jEYkj?*Kp8C& zteXphe15VKED}9o7U%{4I>Q~s*Kjav+8IcfV9iC%H3J~{XI_>vXMN{*yV2x59WvWf z6AZt!e?{zVyQx%C)nNx>-+5+Owa$U$J90jGYCcR0r*WT?y9ok6&I+R)bfs0HsYsGH z=ii6y8B@EIW^{`}uJ6!{0xXc}&Rnl?RUWi+@h5VMPiPJuV1Rx1OdK0hVlc7t==l$_ zD>BY5W+EZ=(x4d?^A-skFi)hcM@(kYjc+#h3e6v-G}IwrpvX_s#%%-b9IggN*^6)F zve#1>uU!`FStPuiC3A{9g~Q{H29s}f`)ri@NfoR2P}3^_lM@zt!#6Nj>0?;NxmHnL zTxyP2ChBe19i^kE44P+@Ukezj&G}w)zWamB?m(IR<4kUYfmc8m-EP;79pzMUN{%~~ z&l?mI-Lpr83f?{-s`_Q_EyR2`6bFvlWXtpgNhqswITU2BSNlzpzh?-kG?x>cYe(Bj zIGe>hx>2Aa`K%SshlX4#*NWV(8Nu|sa(6bp{8q+rBWWSOAZ!_b%mw`Yzv9bk3@8 zmdn~gM0<=k&8qvbJzz)gBDX zdWg|mWvUEtop*L}ga`U{XJ+rkWn|TH_2?T54+m;UhR+CL2mfFV|FRWCvhUC~M+7)e zDLI|Y5Dq;&V5XiEJXerUb)zkRFd!)t`6j>Fb@cwRjX= zDV&rTGz`Vc^DWNEE@dY9x=K`W%3UE0z5~1w4t9;th)*k@7wboPy&D`( zkMm+0<}fhBaeAN|l!?X;ubVO)QP^3q_^2lMLfg@QdtNhG>i6)N%04e*;n^FZJI%5s zJBwA{Yp?O}I#BYpjCuvbJY`=WsyydfeHv<_r~@SRe5?o%Fm7;#t9Msd>GKVH=o1v$ zKu7X<#-)zkkm6C=>e@-;h4rvHS$vn~T8wB7s!B8te9OhxN(CV&fK_jQjrjB~sd8}* zVD=gPNhA(z`_kYk1^#j|PMWwXkY4NNAL3FWABSw6D6c)`S5A$2$L;2P$g5fVu7#@ylNWZdZOS5h2KEyz{#|eJSv#Vih`b;%CcGiOH%>9kJ}c1LSI;*vAvTZD ztX@zisQu!clAjb!5r|i;8SxY$sqZaW1-ZgOCj~Kt>xzax|vuQp`9J zCyzua0i7T5U_an0cx8{ne94;!gtlEwdwQDfnHhR0rW@adEM9go z7T318rb=m6j~Bi9L@pGL2kH;YLCylfu^}Sw$;e*UEJTM5s%6bTT>z?|{sE};{Ntm& zCaV|#uhJ)ES@|hFGSLj>fiM=$DDVE11EwX7_7dSQ4zpcb899QD?r}bz>uy#QklV|u zFmsuZ%1R%E*mUd_;@>&MZ_thxxmf#p>Izh623n%;9>nL#fTSfH@U~+Bxw0tA;(Bp$ z&bZR_g=fHgfAEebSCR{LDEpu`g#B(CboHd1zM;cYK)#jtmQhApwbeuM>%&6SrWxLC zPRfNe?qeeAyTAW_yILUbE6GF=} zzCU1Vs}nyi2a&`(8w@h)R|^ZR_=G+&3l?kez+U3*zZpo!KH{V&{n>E&T~yM*gSqZg zhgI~nm`b{$zlk+C+l#FbOvqxpxrF}>8kg>!wandw_W_&U8`z2aBr4!JFaJC-kh%+V zLaJ7mino~+dFxxfCT1Bi3{H1is}8Wf2($6l9mKaB5}o6RI#~>aYoG6Mfg0R-%*J7+ zM{M@jD`OI3Uc2W};~SB( zuDh+Z*E#`Uc6|Bu_}#%qqs~#a&v+8U=f)|ToH$={Wq@>4{Oe%bgpY+nV!~}w)B1OG z^+sYD&QW`zB5E!YwaS?CBp2N{!ssa7&{WCVqa} z(;0Ovl*Ik540KmOfUp#ZT^wG>lt8#of`&K3v?A z8qV&w0-6TvJSSRfJ(Yy-!loW_Cuj7j4#=wakQJzDs1m5M#QUlRK{T4p=I9s$!rWfc6WgvnGmor*ar)^{$p7r-|1&9%`E9735a#D{=Pc#totZMV&;uXlgw=`$ERFHf2C>!<4}sP2D+yRR_7P^M7@grEG=36>H0#{42<@) z%|vCW=sRQO$2I9E1t2_7<;)cU{2b}6BZW70EsWlu80Qt*xzBjNgHBo?ItU0*wR@s$ z9+2@3E|#A3674DalxbSAYDG=?;dzXSC)?Onp|%bA~_h~njiL2 zvAME5jn-V=)OqbiIV@h^$STsrM6WUmE-A*f$@1c+mfV8zEay|rf$_0`!n=&K4?M=+ zScvwCL8?81X95!{)QWO(o2f>eR&X_kjv0|yS$n8J?WiVioLHxYe|qdur$N;<|Js^Y zsrBWH47)T8YRzU#3{r7c=;H0WJsVYtQ#}KUO3vK82(EE^&inUoLmJB)y(A=X5d9~( z;M!4RkC{@WaCMWLef^v6bl{jOg=comvrPUaVJEfk=C|hu(sV+Z7TalF z?MI7C=_jQ)^aal>l5~UVJd6p#9_JXY!il#WD3dbAB;s<(q)Wqy^?S!EUPN;}0i?OG z5TtxUTasp5wy_6{Ng0I{QEO293Jq4^IHC)IjMDh_5l5tj&3syKpQ4i z5vmTSv5t!mqJnuw=MGJ{)+49mgV|P2I$>gKDCsXPXc_max@-v9Zk;HoW+X+YI1EX5cX@`>>@`e&SbL46)YFSYgoW$ZC1rD= z+&uv5qBsr|9PFm5aM=f_FXF)o-iGRfm@;i`hf$4LWvS2gJgddWBQwBS*VTKsJ?&`; z%5dR#`V#`)^ULJP(Z*g4WmUG#(qUlc1eFbtRpK z3-fu?%$+UvMs7T-1uLtW3-a2<+cw^^ITI>Rl4$Aoc(woqygq?0HI1k5dyG;F**-vT z?jqcn?06{6m4fnFQO{~$%mGuh&4jXXc)x534V1@`N9p*22E6i3j8v$E*Zfr3$;t_K zs}Cb!Ivhtt8Nabj(ESWto!DoHNDyIGy1e}5Y|sXuEje*=bWuFK$=lz{WT;hN#~%@Y z%n)EuX&7ob(m{T+Gem`U_{qZcJ4x=En-LbFUnzsopNJ*3?9Pv;CHiY$rFDO)tOzNu2sf$QYR(QZahlXa6nex z)ZJ!Q^tdz4J`fj|KlrKkn4v}!vC2@}MyE0&p;qIZnl{BDLe~1Fv1dT?$(uViG2uoV z)lLdJP)18#!8(J+8ztLu1b)2((9Sl1^X`|CqrOx@kyB-P_k9>A+g z?E)Nzo(e+4PKZaSP7q_=VSzr80iAq+$;b z@Dj>V*|OO;#(9_TM5xah<#&mu*Y}LD%aEe?OxC5bHL=Ok1zuFcHU)eZDC{7^dNiN) zY>SCEw#pkNYMhq6fnc_ulZ{R(29xu|qv~4$g9g9}kJcw3>*N&OXgGMikRN__|Eo;F znf&)vW=tHpeRA9^pZrQ!#I0uEL1Ms0^~d2GE>Wh-ccF#KBYD^1V6(><)nK!|CVK1i zvi=Kf>f4~DIcsNX%EYQ!UdTjzp}?aPL*B~#qNSp{ora9h6a@xB*bn&bhuD;Gs|}=* z8m*CqmG`W&_wduh@uy=Lvr#=jRdd#Avk)?C71=O|c1AXI{>}L!t}5G}eo)x<0`8g{ z$K+BT6s5;WUZ^~d<0&bYlr)B=>2|(4HVmZ z3ps|tK1P~r4SG_jWpYPknM#nb0nmnFK0N9B@8lW_>WjyV+i{`3=*lj-(rI7YFP#nG zk~GO9p8crKSmfa85spvQXnwb_+)~yB*;o9{dNPQ=FsO3q0y>oY3xCgq1aQp$>nEug z;PnnBGBGQ0_=7 z<~pP6&iOec%pD_pE1s>1xM?H1#T-SUO39Z9m4TAu90tK8 zrcF*7&Km=Je4hHN-BJp!51(7!LPXX3fmy*)T%BTm6_GH?Jg41FX!Z2l*``~48g;&= zF_kzMYE;GV1d4|>iVuJP9dO#>G)*cH^l`ukGj0_4>A6?}M3C2!t3j45+=N3 z(nlDuUaG8_Rm8jG_|cfa+gHCE4AFa5c4G6BvJ?f3I1KD^;;ZaRH0MOK`EW9_6{ZjF zm3!3dc}wUZ*$Xmlae%Ku7;<56Y+JK*G3s~Hoq4C4Q{h_`e4y(E8{$r+67QURT*=Br znx7hxVq(^CP0q($LDiTOp*kftzIJK4${pK_zJ)47je+4>Q;QIFk=UGCRhTZaDT@G? z1Dg!WbU@zWPmVU(%(dj2w1L6mz5pstW{k_j%B}=p(_VcK%+V~E5l6`tDhGm}d&>0u zR_SF1+M6>=5ByE0zs8~L{i^Dx1UBpIw@ymnd6@_dXV&|ue1M-V_5x=1TI#mIjEv+ckEE_lhpFw7sCE^F z?kspW&ST2CLRH(f9q|cQ=H_H1Dq%byD^bOL($OKQiUF^c-aY8yWI(A7ugZ);@bry_ z4GYE47j;)QyHUu?OKK2k z%1Kh7;Rl(bvmonhbEL?A^_qA{wR_&f?=A#^o`>54DSKA>m?V^HzHDF-0r>rJ^aRGi zu!ejdku%)NtpanZ)&cZBs-|{S@o=+wb^XH*vr_U0pX!lf+SIpNx(2r(rtST$vW`NR z_n)o$VTQ%8u9W!*vdt90e6Pw{z0Tw=2^7lqu@}*EMe`zWBFox(*P~PT;5yly^!#J_ zl6CaTaL1BS)6s`QJ6wGQ<=G~yJ)O6W_aSs3u|_`V)FRJ%c(RsAc(zSL$yYME zjFL)2L5i?6V-WRW9vU1!EB9byyGXc=S;?&3dRF=3(`1~pTr0}z2N_|!YxcuTfdcWjG95}ok+fKUUPe;C zi4m=qO@XY3!zS49-Xe#&%IOQ7S+>yj(#c0m*G-IVR-hoF816f2Y9CO$6ffjIZX1&% z>AGI3*64a3_B!uY!pr5JwyY}3cw#p>aNkH=lzIet z<`NLL*JD!`=xUW|Zi~JGe6JN$UFS7%l}8632A7qd&RP5*(_XcGn3UEBMw(@botBFIAnQ1xdqGB* z_D}tLSvl5qsLl<}(pkHmI-$fU@0SI>)yuWg4D`s^&a-@C>bCw2mTeED!+38W?f2Z< z@XX_OoSh|i+q$8zYs`)21CtT0+x*r{OYoqm#RD;?v`L#Z6ZR5_PKj5#lS{lf$YvY^ z6dUJrM%uBk&mKXuk4Z6Ov9ww`NoqA02@1z{_Xk2lT`ZCj9dmg4{JXVilY=4jhDteSBImQGsJsZjN(SK2N^^aCii^mL9dplACopo>_9{wUi9MQ4gF*GB^b%H? zGIWh_Mwgtvd`nn8smXTbIeWik)ygT=eN%fw z(CTnX(P+SxoBq;AzURLB5K)Z$8s5sWLw7pIu+r1lK-L6U(`)y#XozWNKk1Kz`gvXT zm$=jhbgm>@0@P#Q{29Sq+YzE2yWw7Eh#`HrKL<~}I@t+9Yy0=37*`6!)}**L5(119 z663w`dw~aL3LYY$a`PGxaH`R6^?H&s(i;mFHA08q8MXnA0;y-s_B%a-OdUjBm8{wX zz5`%pg%66ev^zU-FCLU@-qjL*f-~?W(f8H*VXr-`$dh}57#kUHyC&^X;N%QN6JE&q z9KHTQR(ID!_4dFnNF1|vrv2pyS$JEiiBaXfP7gYk(Ien`Ddmnm2LKVfXdEiHYirQc zxdUi$%8m#&abp|6!n_8`Z&9sR(xW0PC+fn=1j_1{lkJX>1EYe^O@n)MZq`sD#4 z;9aUSmXqT`x!%9;y?ynLsZ^@5Qq;hA+nEN&B&qR3#YQYg-&rep9~1X)BSWluF5Kpl z8fjIz)EOzu6Q1RJh^IOwN6i%`K_^4Jp$WMzgAM%n1Z-JkULP1?>(rRzyI5qp zRrxHAh-A@v7t9@u!jeM6CBpm&=~#=E+lL!M3GeC#RMe)AevqZq4;qiE#J;kd@9GjL z=Dz77F(r67QY1^PgK;RP>%dI?fA1RYnv;c9eDOgJKc99J-zn<}4Li9K2}^es0;T$+ zGOnqIfjtliw>xoz(>Qc;+QpIvZGYDLlcJ1T zi$hi-In3-{_&s0vOs9keSc48w7KuwmvgRw zuv`dTPkM-E0j7b@+dtQ!u0K6MGh*OcymmyDt`w!T_*&&vHPP1_J9VGW*t9n@#W-dcA*VOVXy-JaQ7X>CV2m01iD=8Cb z?K1s)&tdw+ePV|tbcE6v!@E`W^(!D-Lk*poV$}H5%WnOafr{lR7KARDHOqO{=(+E* zF$d9~qztEBp1JXjQknqws@8$k9?M8@kKH>_ja!b-r$-Q;o%?uvMu;_oma_7mQ+uB3 zxrjO)vPcaGjP^xIG|8gzytKAQ6+g(>Cf&wyV5Hs`gZP{i=LO1)V~NC*q`46FZ)m~G z5Hvg>-`26LcyuBleet~T=rpt8bs`|e`9*H&=r&jx-zyToiW)?vn^$&vrMfg_=BQ|> z;`j0*r~zg{G~UAi4`dbrD2564K$#GsCI=U(bK3v_Q9S@!#(UPx7bA%?wI}oD?%2B| zc~E-9dCVgc!LXb$cIcv5XBtdTqq*R_N6XBKggt=lZZT36NuHdglE%5qh*x9Rq-{O1)K$!rvl)sz8ZOq#+Xa0 zIj`Cb_foD;&DfDJyxdgb#x_n;T5g0u1?x08l@or_-*LXt68!BCI3;8;P@hiwY|-)T zUeIstqVSB>vw(^cY_2HLfYgg5x5*YuLM{CsTkNCss-6O>3BE0s!5XY09(=(mB!NXECT+8|S?ciSg1I6){s;h%S7jx$>SCJz`?D>Jmrp+Ii z6T(;>fXSJA@H`s)hD*E5jDZU-}t*TWM1^Fy8`rnq%c_>I(K z&`cBj;{Nk_%7mD-vo1}Xb@>!HK#$V)-MVMAyF9=$L zp%X9le%+x!XHO$>>(G}H{4+o4sbxFU?tsGLVO?{IQ6O`61n~)qAltf}dc*mczqkXS& zC1rwhd@zzCkC`p@iP|(thiK;sidC+f;;KVvx^%J^2o;hIKR@E`!CwR zV^;P)T1qE3urM)WjV)YQSpUA#WHt-H5N{`m(iuoAQ+(wZ-0wM~{?aHoeuc+iS#GlB z$`3LMPY!3X!FvlKY)h0HWz&8m*|qb*>AF1-sqPUbnD<8xj<+Ee=9FO_pxhnohsxDQ zZ(dd@tz39F#-y`6KfZmr!=PCY2D-k8;xA_|q3t4PVJ7#8eTa}%ruEY<03EA7svLz`&jljdQ6HYvy5A7k}l*2|a} zw``PgLS$mjyRt#U_C@MnLk~F&vL5{)lX)2sj+b^U@uXQRIp?Yc>_1V0bOb&7AMJft zRFhlRu5Q~_M7Kz<8v*Gc9RkP}1f)wzVyGK|Pymy{#%e+SDKL*S zh<*}%cyhaZ3UM#TkKD1J?3f<^wg?=|sGi3yR7`Gkokf4nFhL_vCiZ+($LJryF*%*a zQA1D#rl-awpD zH|G&(4bz5>a4!gZcm8Sa&YNBNb^Vhz-@E5#vR10yh+N;rGriHIb>o)uVGq6R58Uz? zZG|Gt^DO8J094!b1jkyW-XZ*njyEf=7l=7Fu~U5l{ZZDCyLsa;&Fi}+dmiUQ8kgnz zM>p6W8?Q&cp!VwC(lVQBK?XoKSHIJ}wdY5f$Vnh?TB|Ho@|_x23=b3Mwp^p!SeUCu z;E@MxbN`m_sN#O%J-Cu1-KfH5wzju@9lr8Yb+HvGGFe5cp1R`OtVedIQahEJM{OP5 z6oA~a5B4<7g1<5vt9pVvFOr>Su{H@omaU*F885$|pQTkf-o1GF2E?n=>o9R(PRN|VY|CglYlSFYuWV6&)}pQ$?tv2X*T zRe@TvHw}4u%`h5-wJ1~!pn^=18W^2kg?9mSgPL$R8ryVb?p71|8vKG)eSW8o5WoHq z_k{kFA6ccde?!JkfXPA_LXw5c1ktzI!HJY7-IRI(SFE`(FSrHEZP+vR?8+@eNq0;Zsk0fH?eT$!**(09;r;ewq6EWrYqEevTw?&~BfHxG zo4s<9p5sf}hvxr(TaqxxxnV%&oMMAFGcqgz?Yv z#jMUdwkwS7G|BsJH6|9DZD3WW#6O$sEH@E#Bi+DU3km#q2w-W$*s-xkaxN2*s>5kO zcWERledNb+cr3Iy=;q&^#h%6Y!6iBh6X&gqTH4;KMx(4}v9mNYzrl1><<}mrxe_YK z+pF9y7RsTx2@bI~3wh)p=e>qMT+rk0OtWW}jrK*ViCVd;)F^_FA1QwaNqNP=M+mDLA6p4{`)ZV3?I=)-#Ol`#^FZoz1+Su60vQKV- zOxc$qpUKPfI{g>O@;2#_f)1qyIeqO*hM?Nh@+%hL(HjfN!N-;ST;)Iih*#8HtsKml zsSQ|-&o9iE7(FFa^bRc)spejPV&mG>E6u@o_bLF6+@n`TodDbmKjqQ9~BwnmJn`h<;gzta4tPOjD1ZJN+N_l zm?ManWah4;2#cM~ufR+T81`Gh)i~87x+(s}MDvUUJ~V-ME3nVa=66Qp!IC0URBo>! zo-xPA5qv^mROebh#8NAgu3jli>Jq9Z&OI5_`qw*x&GOe!&-M9`VeJowxM@ZMoB{{@ zpd%wO$zIP%cRUYb=EdJFxrTRNlMFyc#-}xzzg#T2m)+PB>6Rx@l-rJbT(;Gi2w1Xz zpv@RBjm=%<^gEQ#A^MUZ-fNDLxtUF-qjfz#uQ=tjTE>QWv?^Ux@?2F=dWOEH*~d3D zz0~+-D48VogQ@?y*tl%9pC#+*5RMwDTl3rWqA3eN$5V+bb@JNG_-a$&983fwChdvy zDHA#eCvFvH6Kz&m?$lMhRY$aJ^GUqD$0}iKp%KwW7JEh{f1NIWW??8dE0uW2I#X|C zI6q2T=!gr;j({vrc=kzwkMsu7nI8ipo8IWv&Kq zo3vBOiZ1 zK2{mpn`ULpc#h)1_<;oG`r&7QZ< zh5#_c0Klry9*c2ur$Z6rm3fe4>b*9f_OLLe3?wTQN^LL)0zKZx_x)akj*}?Jt?Hyg z4S!ZZ((A!BV~@;}q}h~l37(*9Ga~KnLXz^n>(dau&XLYXl)U+RIY^pE!S^SO6yE`C z0i)iN@K@-{z9peg=vy;b>9IH?D>pP|k)4lsG9)RpB4gFYB%_aeztOII{(Cg=@HLot zQCUj`U>LX|AaZbC+7cJRCjs>BQv6-HA$aQbCqmBZYni0=qjKQlNLd+WH}duFIB$@q ze~o@-SvAM4yC&^*gw*HZsm&^C5sDZ7yi?CpfcfG)+^jU5*eQR$GqO_%`ggkrOKxCB zi9tE-2Y0CGCUO&9a6NaOR`tO(3ib1j9L=X0sv&)}MfDyETov7b!Kpl+c)}KTX4S5V z$~mGur8=U3ogVd8!20IVRWGgys|%Obwu?SxsUI?R@v0Ng9pLJ2>wT{uZRAq#`=I^# z@DB`63N)AaV?7VAliKH)|MvG-o=M`JK2hGz0H@lU0#fq^L#(}q%F&ocMkexZx#x0Q zwwlT}uLZ9$^ov#jpHKZG_uB`(y2Mq68Ui=oJd;aKT%jmUG3OXMU|FfsB?8>EhgB<8 z0iK*mb-~2Spv%~cP@CX>(NHZ@CQ3x5ssFGG|3x!!UxeTPn`XF(z}0VGG0bp8lz4BB zTo-TZ>}PhsjzTP;i8#M+N-Cfm+@#zum>VdWHB;()b zOI($)6;NPf74ia{3nTQnD`j{%FCB>C=Lj zYkA^tGrhx!>j^EVB%8d5p*exBzWwrFmyooFCszW0OwLVVIG(z(Z3sb4YE0(Lj10CM zeaaD8e1uK|$Yy2$47VkBuQ~XHsE|NcCHh{F{9I?jBMLvhk7zX~WWaFmS}e{cFC#Xh zzvI^iDhJ(WaO$oC6PqwkUn+_so&A?ec1&=Bx^$}Zux4(b8|5~NgwHVp6lE&@Yf>>D z&Q$M%8kq9$nuZv!!dO0os7V_jJHvjuND}XWEiDO#RkJ`?7Pp#wJC|C^pZjxY4ClL3 zf;}dFcQ#jBqT3;ve0pf;8hvg{+;4I{)&)wL-S;1yfGbT|N4Z+9;YngUZ-qiEqrKBT zS2aY9Fahbz;(Q!(4%UFn~hW< z`2lfbrEjG(MkT`~7O)u?R);b&!MhRs)RIVew6<*r@$Iir;m^&R%i&aXS~iaiD|q}+ zitfVZaSz*jYu9QczdmnS+_b6_ZJ^C_@uS1a6oa6TF(^9S&~$cA^zIe&Ph)!(FizFL zDQ^)#51OFlMAIAC`HaZnpsKs!fs52n4z-F&_pPggexLf1Cru2qEDn}8=V*A;p*rq# z;*;6(&fYraZQd%O;+}D@d|cJuyhK~$8$;+%&5)bU){qq^$(tfC(c=L)KxqLTvaMew z*v;}sAR5P?7aD9)YE#zl7az|wFzX=yV&x0$N}ufuir)S0?8)b2Pc{XFv>y_=m{TBa zda05yR9I&;9nBm%?$rLo0{!J!{WT)(!ajdHaZPU;C=ef!#dFem+yx(`y}4mqmR51A z0yRxM-9XeQdAgHF(i-!s(uUY;Lh@X8q^NV^g_(AeTncYm(K`6RL_a8HkA* zTqj60TLuJ;i*M!o3*3nu;5-3>v!04(sSZ$V5E|u8_Xq?)g=7`Oismfh<-)X5VP}y+ z;m{7@@UK7QZ*tE~WQ8ZZu&J`M-D%hYK-wSmMCq#5_clN{YuW7V&O&Z6{IQXTFR2=N z)U{?I1tv*nmTF_UM(apEpP_Wa}UguHvc!Hs zTe?mNqXQ9_f_t@n?4UO$1srJ;qrj*VZ%@}d1E$mlj6|Ck&ujt%^OX^(4q5KtE%L_J z`y&+Zg}6{977@4F-Du^SBITOlWfkm+KbfrXh_-P>9a#Pq!8h*u_D6==0;hn?cC*E5N0NcrL;4NHx(4>ZpTjr0}mKC@Ku za1Y6)$35-se;*|>4O#Y-V^Q#OX+j{P$IR2?iScUg5Av)uZ2f$YmB=!RoY$NZ+(5&_ zaq0l66z+jWOS<{XTas*K$U? zJ+r2MwCR~>*>#5`pXU~{lcyf3)jF!CwWrpJS%DvH@^$@0n=I}=Uss|ktCr7tdST8c zaHxh9Y*XFdm6#LsN&|OScTu|!KN%P_n;-N(*rQZ&rPpflwQBXKMuv|D!~TOIR_C*< z207u(`aW7M3E_=}HYPMdoFeewI_l;h2SPHp1HDeV3|(AWniDEe^i5nI{Ao^o&om2O z*KE+rb5FrHvrLFqV&2d<(4GX@SN}~8ICJ*WQaa02SM|FX)LO0Ejc8DO`HfEgp%VhXDFH~2nM&fDSQU`<{d>>5 zn>}u)%(hK;C1wO!t=P&u=j*>d@A@?}<9O_B!=KUVH+{O$pJ9ml+gD^ziLg`y0`NO| z^k!f!?!HNq6QM@+6a z{Qj^(tkXMf0z~3)f3Vyo-eP74B8xJWBX&#Bo{flnZu$V(52TZT+vS(y_3XyNsJs>M zX1n-`#pNf5IW|Nk7d7ex5xk%}&A>V+Oq?vc93Uk-!MYovwg&@aiO3|i>TC})iL>f_ zExk{}6;X3Wp%~&|Mk^D$td{JwrKzm;yzen+XcfW)WIU0)Mvd?!`312Z>k@>(_VkAF42wh{}CPXg4m zVe@EKJPA@&rfjh4& z@~vM?x>}cCtAac(?%ZOO-xB(LW({(GCh!pGp`!ci()2>Q)`{&Q=GUcFyFp_0&p&=| z)oV{PEOovA?FpEO6!?R^{{}9 z5MXF-nOzf}vNW??)tcMdG#vZyPsZz+y{ z&Yf8~t?clG@kH$iQCf9P*u64OeKr8$O~y=ef`BA8V-luS@T;1(e2=N*q2RDRzfOHoGJIl5t`9y}o(=Kx^ zpS^osNeF>ysbp;2C8t21#b0Uswk#6(nj{KnQePj7HzfVs%e9i^Ho&Nk4V6XJ$%S^z zf9?_p-M@K0=ZNK)aY?@wr`?z~auPZtZZiDk4TJX z_wP5iPoGfp>odR$UV9kl<0e_07QEP_CgHw|u33 zt~a~BVtAXM)jVj5XrVx5r=l%iHKI<3N2{Xo?vvjheJ@l6yI`*&r>;!n*tKy)=Ox7SZQ-~P=FyH|Ahv0bogbIL|T9MCSW zWrQK8%Y4)|utEL0gI7p5Lww(5`vb@=qQ*znHkm{&OAN^e>$SPeD0s56gsu(R!K3qM z*oEYGTZmkrf4t};l5CjGzde*7$HeF?=Nd#C1_o;4^&_d5$t(kxS1iJP^d3Oj7rCXZ_z)@qga6Ypg23YMWdB~7%$ zyXsy!p$L+_6HPCc1E$13Pn`a`bkBc*ZNlxi(==h(-WUC0kz=Qry5M}8S7ODtJ7K2z z0nyp^WCK}V!@=t~6d2)JVOz2XVD~HgkywDQsl&QoYv3L1#&=qLHsM@M&V1|Z$4pQ;aOche@EUvE)VdyKu=bi(C4 zH*Ke5p1rt^u74X7|K1sQA`Z;ydF0Mbb~mWHA&+A*Y=)+ai~Y1Vr?-lWBGYSOXWx z*^zjt)-ZFSUXCzqdxZ;#S!W9>K{h6|-*U|N#$1c{l9xdR=xb>KTIkNC2)R!^W0L7! zc&;NTJa+62->iH1vHA`9rqh(aou0hML5geOrdPvi@VTeZ{=4>w`O!Y*A9+0hn3pt4 zc&2fHG%wdC8bV|>0ESC1GmL&+;_wdtb;;e^^4F!J@iKfyZ=Nf0V7Rs~FU+8%78;PY z!F_nEQN%Z76|uJp-G+ig=z`Jfjn=0j{I6nB%|a5HgT8tt9a0=cGa@N|NRb_MucEE%SANC(F#N2!bAsE1RK z4Q$4&#Go*ZO$}^p^PJ}YwIo}eclqPRiYmfP=Twy-TUsLraX~IGFKuZI?gM>0rzAEI zQ{W_avd5h%J$^orgCvXr=pPvr43|5PZYe#lar{E>2K~cGoM&;(V@mDFgt}^WO?G1!ANFTlyyHy{9wt_bvbpbV zu3jR}NPYQX0;h^esdE3aN%Mku(VXzuxcUzMWXd?$239b;(vJK(f4^a%BftLJG5d>$ zOBr2z3JT#778x0@I~B((Sv_#uGNTm3XaCzoyHO{9?Y{v$pmS)1xZ z>A^3)0g`Eb$wxPMd-h<5laF5y1L?nS7$+tMwup<|<^1p`ke08HdP{=cJCgYqZA1`V zOILa=Ne`tKayuUr{}elnfu9Acz^xL4HtR{S^xeus4v3zqhOCPxVPG?AeG_#u$}e;g zAzxva()QKo?G5Ubp5UvM5{FMxaM~a2#AhiKGMp0r!9-K-g+P_0)<+F-b+HC1sR;9~(cK!Vs8=1KCDUZTvZB1+AR;4^2 zLBRE>s{qjqV@xDkCYL6z?Uz35B(ao_^Pnhg?A2P@X^*5Z2)z`}KYAuS$#i=Sd7KoE zeORBJ;n5@-Yb<4D=Y<6l;i`s%ywYj*A4=3QQ!ow&i&OmJQua@Q_Q{a#J@f#8p%(dY z(6;H|>QCTYY_Ji}(_ppX&>*DMt9I6rZS}RLXgS~H8jGstwq7LKFZDg4iQv}Vz+!z&G;YbLRB7K>t~IxF;qu9ua^5|2B+}m%KId#xHNza352IU)#2C%ullRS z@LaWhFFgD7Ew`GZ_&o^{l*N1$DV~6$Ay+)BDFe5h01|O+6?g?Kzo;TjXnTu|Rex{?3u|MWG-PmRlL=(VwVo^gCOP21?Tb3%CT##)6QQrql!6Z}fkr*me(YHK92W#C3w`ndn*fk+?m^;`prA5Nc z9z1F=xBQ$yGl{Dd6&e*~*knO-8~YhAOp%9&wab*-t~`RU0k*mL#OnKv{U zwB`$$r{E_M3oVcO zbqPKCxP&zJKAVY}l~j9$s6xmKR>g)B{Pst_10fBlR?v8sA4FFfyEcEiwdKBcRL`W?-Ubid z8X?KaeS1{4(N;N3%(v2cQGg6XBpFXDd@17?EcHg3D zqtin+rA4}I_?Aq^UZDJ`FG)WI8YMWLv+E{A*3^qN#pXkXr3;K=EtfKMBafl$)hz({2!`@2e zapaRrfLNObf*#f*pn6ha>%GplVsD16>vnxT-%siXkX=}U>x0D2MI|=#9;6xSVarLw zVZiZFg9_)Bb)dc-x_=6)tkfRh(tU-AgU!TMUyFYT0(ocKkiP3xPj*SEi0ZwG%{^7G z@QdczZ?ZsW_3dUQ`H0Ky%k66vm4g%8qHPyX0M6G^R{5+{39F+@H$9y_rm`u4WBWD1 zr!V1}nNdk~;Fl@8tWAqw7n1+3uII?L%m!?By!|w28{e0uCKp)t3O}gtSRt_ z|6|kZ)}H2e7yNVHA;{f*^QIN;F>mq62DH@IjCX7kK-7#nVw4(T zL1oRcslNPM2Yw?oryPkkeVG!cEc0vl1v3|D;whbLN^L}N$29zk2tp~DW-lFqiXl@O zX#*ne1FD_|(6(Fe%p9#%0vbjd`Uj)x682FxdL{T%B?ApuT49!sJcy4o16tX~eHJfw zIv6_W9y!P|YJ;3LAIRv4;85~UhoRZ0I4Kg04CQZJ{AJt;jkeN~_kksmkw|1(;wBz! zlXN?09sG%0rEiP@4AKMd7=mq@CctUN9t0~oB`M7);OcqasPQ54s#iB;ZIG`mZM9V7 zTVq;P>$CO8dN`Q@`;~3zRA8?4E?_}?)H*dAXK0{j*k5~lJt95v8-8}5>7Gq3n4D{W z{T4r47%w4{={J&GH9bAA0FuT@hL#_@#i>6VzzA^h_CjvtKxPn}3O)piK?7htSKUsX z{lyE;VE=ScDPs@cyp4onCO|IT0m5l$0mrZgpJLSx!|kjaJS6;%?5>#N^ht^}5>!EW zuJ?7B+t?b@N3DzF+@Whz&J}=+fmOuvaA23*6+WgV1q!6*bXAAm%;*sn`^CSk+1Ume0L$Q;i# zs1B~c<=4Q*dC&TN>CM|E&?yg$-o6RfGri^%Gb`)jPTmJZYh8TK$jQ{rHt>IDmO|Vg zzg3CVl%(1Vr`kL7z_zJA#QB}$e_gitKhz(WS9QlTPt2ToWup3h2(eyq1ET2@3&x^^ z*j1CJ5#$bvu-1SkGsbE^2f`<&NPW~k?EOZBpd0Y7ZxR_P7|LV51{$vxoTYGs=;Bm_wE&BUv zDdER&0-%cntG+uRMHCmH-?j1-{^~l85}+3(ZfxENcKoTfw%z$LTIBYZM;-tD!R2|* zH;JL@wRM+zr`h31PxFef)sb!;t<@u zEpCMtN{tG`d++MX*P6BFd(K+PIeVW+&a?OP?9bl&Ip+)K?*TWVUEZ;OF9S3$?*7+~e=zx9<8VQJ{szEs?MesLvn!YQ0hbvrU17L%-V5OUhniQf z{6pV=9h6rosIFbQPJQL_MZND$0D$r`;1VSzEyWG0i*gpgRuDJcXaCibRm-aLYd1K8vUC}m#$vELP1G&?V{F%0dV=! z+5Cg#gjNf`E}y(0R*Z>vjR~SC#CIckQ%n!z@Z-+iSDY)IUl(765x&%N zT+*;23}JEjR>ezVkX9(XYi*@i_-NTXXZf+o+p7IBe2_~TAA7%A71;;_$2f2Yh=FC| zp)&DEI7AQ&F&D%xBo_=ztRdNaND0;Db^a#inm|&FLXf^c^Id&(#^To#2N&hBrz^>( z5kud<`~~V0Vu8v)w8bun1@Vj!Nk%=bv^dV1DSWXB+#8O=6pJZri76^vNMD|9t_ZG6 z-PweF;@W4E@eKOG6iRs(HS(~0|JSqBoM-R9c1}o69nnk)8!a;LCe0}ygjWnk6evJq$(6g*IUx-Yy)CA~%o#3my+sS<}_5$o=Yq4VzAh9QEwLp#<+L@cv?Nh~VDX zofG~?nJLi^IM}GmW)vBtW^x9j#lFLx`SZsj)!G(MVMG@9+; z>Q*TZqX-mTrt$FSRkmhLUKx;0H5K&vRQ|rC9eR*3Cd-e%s?eb+ln@jBS7u7h;^*6M z2y()l2$$Jpjg|n&I5=ObEq+S`D2TRGSXH^`N}mJ0sz1EkM*tB&p$V?Qj#I`u%m&Zk zFrSzWso&15Bwk2_fmesFNP>2W*DL{7@r8NDn9{FqpWlE&?XIv)FWkMO)Fhfei`e?1 zy4Y;0XA(`YELTCSiH&)g?=+yxv~_yyYOD&ct?xq9q>$okzJ41fM!jW*4Ayo&f#!&u z$K_J?Q9yzRlIW71b5_-j=i>eq$Wgb)xXJhO=wqp61;dBP zJqw&rr?6pP{1h^~qzbcWpSeimN0fMejAdUreDa1#Bwpg4 za9I~nkJ5y#dHQ3jUuHJJXLtB#DL|?L3eEM6WyQkFk>blD67P zan@~>>H1qfvy*jBLk)>pB2Uzxg*9r|RO;#t^t|b9tG<(D&2Ay&-iB)GFnzz~@9kA&JV_0_M zHZm|gDJS!%#?3=rr&LoR{k~u1eQ~Z<*Epwq`QOFpQ7^H~enoz%Gg=`@)R)&(Xx z&;fHXn{_kZxziji%Vc5otgUqF156H4BTX_gIU7YZ>9xVB&6qB&k&jvUfMT8 zOs!3`OXaVwWm6wSruExE6(I>2b?s$}rOGxE{q^wqb(b{v+fN~x%v`UwIM{EY)o8)_ZDly7w6dO_#r1MZx!88-j@sUgIbx| z{PL%?7GuTzowQt!EDngH`U~7@^|C4h71J{gW>2gLwGZl|AjA0OmJESkJLdr5di@ug zO`e46*5D_+-QG*b`?_YHtL5BdceQh%H=OKMvdxn{3E^wD&Owx@S@SRUDSJ5kNPM;s z-1Au5O4i1!XT~brygp;;)8=3i23Bqr}D~9&~$lKhZ3n~1!1Aa2brFr zJ?OAqVfNL+BC~w%Ro4%GV^)wxFH6UHC+5EA#bLrM%RwA^Y!4gnHVrNwC0oooSPtJj zVc|ThsjXtaKUfyfna{sA-3oU?i_enF0ptHU*30XJk}cN{83Lml&2O{ocF8*TH5l&} zuzXk(a->^h2Tof-EI%s5RN`G%l}s=mHAbUTj|GIhA278+n&xS)v^2_i*EJl_2w9X% z-eXZ5lG_h{@H_OQhuhqt=kkEKI%VJKA{U=5X!Mw)QFo}tni+M#b6M^5uP#I~ja_wP zmO}{Zo4*A8RyY$TycFCIvabM-xfzxRL{s}2Vi#DH<)CZrdltkqS@|nvg)HKr9ebpy zmFM)X*G{>Hc++MHqP9>n3m?K|WMpS(K-+2b}exFVE(yn&m%YmrwwZ&O#-3s-rw@o6m&n!YHZkv1^bf$RJ;aiZK+1|2| zY4s%v?mMdA3rx%8VRBDeFiD^VN-;9>yT4!ZsiUb5a2?V%-P78oAX%z<^jD?8*xkAcW{7iTk$4@Gm{N>Q4+jnmWx2958bNYA zVhb=y^9>%S25a4Mo^qeJB{Uko|6tpJh2dCl=+;+tmzYW^?;3DmX5xVoPHmZI*@#36 zV?!^~Y<@+j*BOOaIAjJav;Y^k5t3vBshh&@0oq)Q-Gi(`zgzB;`4tr@% zEbX*4&2wCH#iHmL{z_T&PKaRc*z8ldTCyJ3k_YGSOX1+M?^`NfMkxwDlTK;;u_ak0 zpz<6w_SUix=S4cV`6rJ_5B;tO+>|1`guqq{i}-@~mQ}mnLDgM&Qyn<9*ke^69^JsN zyk9f~Ht{iAr4DaW9yF!mC~LDC8$z|GYTX{K^_48n^9UH%)#lkRZusrF-(ED`N`_Dc zY4?OHfJhunN?kW_hVNs;FX8PhLnuYnrtB!7CFs_HiqPsbV_{?s{fcJ#GvP4H?W#Woao00VYWwc*)$gUc#0YmCM*?PS>5mRSq8=N!>EF$a zPpQ=#AJ=Y=b%JKa7Yx22*w^#6enY6k4n2>tBX2Ft6d_e43YSIi5A_}$DJcUkUm!Zh zgy5Cw&1L?tHkWyg*7e7uI0g4LlRM)SGCD&K^w3jp2cts~C1?D|xe=Jv;7}&@geFh(4GHX^{BvONT>znq(u{_R0_Z$oM@IwsC zXfA4;_D4%L&%)YAD_z3~2_xoGlYWCu{=Ki1yMvvW2=_l{la>4-*S~s70aNsvRj*Fr z9=Ryk4?ftS@EO%~3Glr6y+#A7!3l=n##W}=2Z`zg+DVNhUB{0#gsc=bS+t4C5(VFv zZ)w%%1_uE%#D-sDiE6UYX-%7|d#LJJkKWp%brAmsA1{&9uprg5Pi2W6X54sCO(x-} zPZzC^5Jo+XrshsGkM4z-*ni2hbkFumc0(G~3 zZrzEDs2tLHVfG|GM>vV7Fzm9>HP*Y#WLh?pOmXXG&NCk~GdX|9tZrYU{kjQy$D&6t zF1k@=F;T})>o)UjGW=_}n5V&fg(bBlr%Arg#H83Lo3{PD8!ioVq^UI4CS}*bc?m0$ zi|1@oSk}X&&4tcUUpA)WaW*<%9vB!=y!)GWV{%$l3xjZzWrH@m&7o2#1f8-a8*(RB|C#XnULLwg^oN$t4g_v z#QgksqR0ha9{PgE$RJLw2II0lMTY7fM_x^+?aSdAHxL|+8D~DKxO_TpIht=(+FEbQ zRq|UjDzAgkTd=J|kTbs&{gp7bEUp-slJQj0EHWr0dj8`sW^i`4+c}TTsruj5zNMtA8-uQ6GyU>q&fS4Q+v`!l`?JmBVhq)zX_LCP)mvNCo@Y zfulAe9;8aSCK}um;EWh0^p)vl}bji>1DC1A7?%>kweG zwR765Qd8(qFBcOi2|FC;$`(mxRZRBS6^K4^6mJro8&>$H&QP?|2yIPKU6_lkDU?re zylifuwTMXL;D9<533GxUDEdlru*vYT-Q7}(#_*<>6>^WZyUw)&Jm3GrKLNBx??7qa z+qG=?s!Vh#8Sm&X)_O6YiAIGeLCmkTfT}gn%mcT6e$Z%xnNms+i%DnI#&aZ$%4L-_6igRFvb?| zGn3N0n9=+DqJ7Yj)&&A`o=fhGD`@{={9&sYLBe&|xtO3GBTkm z5aHqyU!K-nVvm{-7u#0lfRQ zdyYj<-l-kenO^tO@3lFhY;0H^OGf!zD`sFPhB7`juZqlN%kT9L&?d|X-7QzOdwRtx zrKBOvv8$@l^~Vnf>|m-xWqtk7fpyCFsU{8+gPld40oicZF)0m>RyLm!I_k>Hc`Q zm7um}KiIK&%Ng7uaCzTd|pQwPO?Tuz{4^RVzU zMeejXr<#Hm!lu%8d}=%E%>?zNoopqiCaUd`25~aBpfZ&ea%EPHyvz99vquleKnCV( zZA0z2F;$+qU`PC5`Wx5mO6QmGloQ^ULQ-E3jc|e;orKdmLfO(6W1BaFdVQ)Pwmx5U zq)o~o&52Rofinebj_d=%-)Jp3N`w?fS)}=#@%~JbYWw^yDrI9fdaEuuV>0r#uF|eG z>vY=7gm>K~OSBqQ)VlMq*vLGwES9>J3P;+c>gv4}8r4#=3Uaw@JIRXY)I6VeOM8;P zM0^lkR=&Z*lsDqjyU%?{5Svo2v){DQZWERl4V?3JZA}ft2|~lYi;HaZAX4xh<*v7o zd&nortmD$hX{fq*F7|$q=gA|h`xicWRn=YvHEjDfx6Y?sCsAzf35=%^2|luDeSGZ) zf?LsAfB|8u4QJ3I=x-UxQyZuB$@miWwVd}DkAibNBkU&q_p(M#_3mc;fblj9u6W*R6^u<<7(2au*G7ux&d|B z41KljH;M?+``vW%?2h^NZT%M*Q;(Q5hLQi$lAKK-g}YHV+&jYS2hP^{F#uf`z0h|Cb`Jys)4q09|*F;AtitLSxkik z#>SElF=gVtQlZoR05&pzY!h;MtMW?A2yYc#KW9*tFJ%(z1F)EoWCV+IS*wv}`Pjqu zM7miM85z&??JZ~D5sNCUi?9fEwNAHuM8U@@qvOOSo0!fqrYuWzSONS;4*^i54IW+~ zl8qs;`nd(gxD@1Slsrd4B|pUlm<5JoC39e!* z<+LDFh!B2RCvLG(?~KbpCVBWyE42Q|7y|DXvj2dva3~P?Gwn0ExGOR?ySKa0DHPnR z5iUaI<+~#q@0jrm?Y#LcQaMRoR)U@G6@1Q-j^L8?>X|)ljK7QW(m=EG5d%cGcWcu$ zm_#izjPU6nT>2)O@%zawTEF!88b0?n(bdH8ce-Jn{;pzrt0FnBsrd4H{?c5G$8;t* zW5()hLwYhoKfYv@b>)-T*R-z2DL0GfWJ9N8!3f~5NWZ;zCY7{Otj$Ja z0?U2SA;~(ik9k_q+aHc9r5{#6`{kTO=Zv!Zy#)px9hQPWJAr>d%LjEw4WKoD1uR*uRM?m7F9C!$H=5E5xr6+)Kr4 zfnNUTClv-#1u(%4*Re&_)R#h%i`bnjfv$&G<*3!_- zUG9{BJ{^-vkUhAn9cMPjva(S0g1`LuPH;gq#slxkBx&laECM`xcq{?qu8)b6s-pue zzqp8L008?(XY(qg#d4+!!K>rv=EJb9=}eBRW<76el9T*?_f}#95p{5FG0Qd9T3^W8 zlWGKvCAXgs*J;RqgC|Q2vcDQl%c2`ze+M-Qkt(5aoP=vfL^~oi(!clfJ6lrSz46O4 zougm<)qToru$lKAapS`pqipRE?IW-Q&QNE`)Q1F@ ziJI!^M-!cSkKAPEm;;D7w2e2z5x*enD=KA4+<5V^iN&&qm7D;6Gt)uW;1>a*{}P!5rh5O*>wA#T+*lB^s9IJC~U(YM6YWT>0i-k!oyIOV6KS zC3|0pLX_>q6Z3nzX)zc(?xQI<@o@yao1^^esH)by3d=kJ6VnVkxfq8w z`&GXrVIwcE&?R8UE24iUVh~fF649pI)4`%HOq2z|$)85Uh-?00n*wc(m4zu1FF zuj$d%RC0pN3O>0#TI|k{FxEUdq3bG;x#4pB9opeqCovKk7U~RJE=}+f{g1{3+p}ejWCn>MId*8c!1q+Sa!9dMkr;`M?Pge3_;%T`5_M>lCX4UjEth?; zo+5KhHKK(%i}GB9h>TjB#OB`wEewFoA2&%RnnK)WSOT6rmVn=+KJ3gEUQ`16Lb`^u z(^O!^bqrImWZ|eN;X20nwh)!vYja_D71`a#9TXb5JUt_KqrK=6Q6?>WLFGbRo*~@m zksLeW{96VET@L;7cl8DfbM+sUbQuy^jEk%?9hY~MdZL(s-0P>r zmbCVB!00*P`@288h1~l`vzs4sh}O9St^+zVzn#doryfhc&8uX75TyKT9dH%mm$yxj zeyu0dMZG!0G{p9v1g5ALH6-*^AAG^kfC@w&B9V3-bMr&Cf?(!G(3i?Ng>eBlXl-hR zBHG9G#M^8#3MMq`MR&YZ(fg<}%_zYQ#UU6iUmp$GGjxL&7l`*(7b7$|hv_5_QD0V;P zGg|8j({=nz_h(1f)pm=6xG$j0>luL!7NT2)_FE<=aJr-~`e>$PV5`o71#}q^@Y{3D zC`7K4@ZqbCSi5*J{eHXZZoTcSBR1rlc$Mv(hqW~H*z_f7g3&|(?U}%_4&k~cfvqE) z5-WAfO@~@wRJ8Nvt#Hs&C&5)Em_e%F#f}PtgtmQFXp&WG-S4C&c`!KW_8RK}ur^m! zyYO!HtpAjVlYdmul=O^|+Nr3L_d=^Bl`=>oGNM%{;Z<-o(xI^2w47KIz(N`YSEIA=f2rwB4Fe{o6Fe`n7uLXo$*QPvz;zIe^UH z=4qws+D2Cx|KzF)M7I`YlTw|Ty8RG>nCGYCTGMnQ3w1x+y2rBjEjh=Zie0u*Kid@e za+Lq7^UN&yXUC2b>~cw6Qqd;2V;q{Cf6YnX%I_KjXcZBgYNHIkF)ZkvG0dXP6z7>q zvxp!YM9_|q;_!XOMW!YFL5oAjm~`$D7r9#b0bh38Ya=;UO8kKj$vSe~nX;}k4(2XE zrfgy=36^4feTRFN4tas$D@)L+aaWwsIo4Tv)vYz*gA(tLmiPdNj%`I&7Gv;jUh4u{ zM0XM4$%HCQr<1dcjkdL=8{!$zXj)n`KezR8Rn~?2goGHZ4%Us)*?L&+IK-lVazZR; zAHuJ+2&1eV1gIRWMR3*8P6lQJ*Biwa`TJ-@jceV9D9-^~D>;h@eR4fyI9xOhRSk!6 zXJwE~?6;^5SX205PC%nJu9fme$fNw? z{^-X8+oCu7X>*M?C~v8sHmBBh)W&p8zj55zU6*Z+HFJp=EH)@T>wc-pVkB>pDAqU8 zW8yE?-^AL$!mYn`%XH8xKW6i6A#S}MjTCeGbC8StY+=+W+uaa+YHxJp2z%(d)lGjrQ*qo=YFR#DwgQpGu!xmyqEszJRfa0~Y_YIz>4|P} zZyu8!z8REEKc@TL& zI8yH+l$UC{jr9vpb{L5mfO5xJjX}-o*tdKq@d=M&g!K)qnES??lUQuQt;TyR$rc!y z8}t!T(#7TL1r`0?l_V20V1|AeWfC>x$lg}h`%j;UjAzQrC}3_qeRgH6vu{d8D3speuMtEDD&^(O+&F#7Ix?@N{DO3KMiy6XbU8VXtYo?1*L zrQkNG@``|R&@WndcKt9m1|Tkn6BOm9)Q{NSEZ-)Gu!sbHZY~xsPAgVeCKqk+xR$hf zWHbA+^~Ph^LbJSeBim!R>*(iTlv1X{nzh?Z)f{RqMxIsEY~Ne?J}227)p-h~rPMYg z_C9yPV20x|X5S4u(cC|g^}2t)IyP(E{nvF+r{-Mc7wlyheX#ehY4Q zNgruf4yf=G3|^b^_UZB?);=)S%~_9a@@%s@DVR7UhjK3%=M3f=|B`%po7)8_5k$c& z@ujD9N;%MeQ1``d2`q5HKo{;(kI*UfO50`OCbR|dyYQv?_=kDvOn6%s51j+Mp%)t&{!VfS#%CIl`RZ0b+K8J)HP!OB%aIYkJaTKQp3x(oT%fXM?^q3l^1_CsR? zVna9BjYn6>NOM53KV)Jl~> zv%KgOfxGVbSD{I?2#YO~hWBj64AD&mh%1GZL9yN-ypvKX(BJFvOG6ccwlSzz3e@JC z4Qua~fvj;75A2mb#?JPeMu}H9MkdQ2fZGtRQ2mAzjZuA4C^eN&Q%_ zUz#RlXt&^6hZ_t3CdgL9q4iyE88%y->IDSRo zglh#2IRpFQCH|Il2B*g;6$-)j=EZ#6IA&>A@zrZwq4u3TtS zjbwbZ+NLMkiqTThV8a0&7k*0LPYU9%>=}YI7AGcoxG`HRK3a>rfqNh2mGLz()|0?O z6Rp2n9kk~nG$w;NaTuRHl}>X%K}yivITf3=%IDT1&Gt?Zi(?-4f=Q&Z>7qi5&@EnP zxgDDvw<*+131RG0Xjm%hshZ!hRHONWH69jfKT;RE8ey%-raaWNel0T-cf!t@}v6h zur^25yqyePE&cRPoyyw7Zg&Rof^TfcGZtfR5{G{26n-e3LO}-ien#e0)YU&rIo4O4 zdy9fewJ{|u|L3Eqy9R#64{fQNe6&GMvSh3%kL)dF-QH~+#qK?=R_*mxkl&ipnoo<0 zlBc7`Go%iin3Q*pd&~7U?iYn{QX#m0$Co{5ZN-sCp#RIe>?zOe2pezB8hEB2@-6L%u`s8>f~Vs*8vY+Ez*o z&ZQ|R>Ee;GW^z%!3w454>xDafk17d)CiqnNXR6@CRh_Fd+3tAbTp}6+Ka8hqYm}|_ zE0+YIUnV(UCvyK7Z-GB*UTBH^?mX1=Xi*LdKD6Yqx|w6w#c>-vIu%b|ZX4En@1#Py{u{#sh4*@EtI^LM=cfFGoAcINxx0W_O|i$rfHonnnUOWOI4C zbGF#~G?SD1PW(NAQP&KlWJl~194!{i18YGxU=qQP6!+q;pQc%{^e$?#71hs8GjZmz9l_e85t9YL)&Y18ek@eZh11_DRG8L+xTOMRn%{@aM4OwJi(8SWBr069dU*FM9T|#lxBC3x zKr&gFjSDkqs3qxx1IP4tU(&3#kvY)KuIUeQQ$b9EXZ=mjh4|{K7qu9AvikCetlHGQ z6kWn_KNQ}AQs1ABYoDU=MO52{2~*| zH42_zE1;cFed5i8NFYTNpMm$vvSN2|6ve;21`)N52bw%X#`$;k``27QhvIA^XCWt5 z*B`Cf$hC4tx@+VKPV`LR;3LI`ENLWv`vWsGUgKO&g0kIlZ3VYCXQ%%6eWU|c37EhD zMCFMheetzlfV0p!cfWh>?u>{Xy{=x^LWYdb5~onMmAgrp4>Yxy?M9e@ zicj0vSoB4^ejDJUDUwy7|{x;bopYvg=pZzgA?^_|uU%`d%WC>p6e%*6iG|>P{%~v~A=R&f3c?m>s&}zG5E`Agz4IfGvBpuexYUFV^ zuQqWu?Jj2*a^>$)6&&|EknF|!J3NMYDBx=8F}((rw}elg(x{sau&DUvt04Ij3&Z0h z_htyejlMHKUb%~3N7+iMD47{&mjd-@R(KVv*t;s7g{yQGHDq@XYZ7Y5ig*|HsmMFi zD73fR6`MGG1;}VK#8iyHO3%Y9BHRA<-)hOS>$|`{POShK;O*rEbgCILD-ew7{|)9tXT$l8}}-=AAKg&zLeWt|qjl z>$Qw3M}Wk%_c3;C7B>Cls!?{vLL`H@shGOnHF>wIC=UPJ+bbVUgP*kc#hYsu7*dyo zE>@YO&B^gk87LoU+fNK5X3-K58ep_RLFUl3gafaR@(_-Xh#no&ZWG(O>x(LJN2LRU zWo#!Kqk9mMKMa&;XoXHOLcxJ?^F8z7IDgT}G#*6M^>rRGHN<+MG9152LX?WL>)(=q zZ#}VSaN*|aU3d9C8!TH?Zc?-al`SK+Kr7u`l4UuR!LmjM4A^0dD;IOLXW!`00p8$# zTJ*%NGs|;8%d?*-g*#8j&!o=*Wh*h~fZlV!uL03pvZv#J>Q2&skDmi-y8i<3em~h{ z$lKKW#J103pZd>>NAyCSc8_oU#(vYRl-#(;khIzT{@3p6$58WuyQ}}L!un&V*?%kk z@jgaI-#(>ZOu6Ipqt+qF*f?L%V!!f@vGE2QtBv%#bIPcDd`~)$AN}-?>Uj25ZdwX;U*DYn@mJX}g5;Q>P&)wXE z8f4x@eGFOeIzG4&co9U?+j#y{KW)p5AZL_#PkTbDhs3OA!M5Zuo2YJ+xf&V|34j)v zik4+QtuHCimXSHkW`FbhEBE|vkYV=f@7RC5P5la)_dnIipJP2f2P{hav3akNGPTJ! z6wO-t^|51L^L5Y!WO(ZYb^6n~$vF({U#&n2w?U5crk+IJN0Q_5AGg)&Nf+ZQBP z{Ttjya5G8@p1lC3Q{!;oNbj!_Q*PF0grlMRG|zrk?!W%+bZ`zRL5_Dsd^4YYb~ZeJ z4$!a23!=5v>wea-b`IDu{vLl0m}95^`r+2E(Obz?z97JVp^UtkQYJ4og>?--skCX< zURF$Gg$HfL*TcQmBSh4yaSWo@`221*(SmTY9g?`_2!0#g$-nUfa6@}~18oWRK1oU+?KEd0+ z{l@%`H4ctl6mI9T%|G7X{hoKo)xl$Zi|KdalbD68XuoqnPfWnwt2=p6>z(z`;I_LW zKfxcVOm!{-mh0-;SHSk(IUt7aLnvj?Y4O`Ik%4~-@b?Qf{F?ao)+0Layzf3o18-dZ zX7K-(4a}Y23+$}tYQ6%txPFgIHNIUcW4|SY^+}ylCg(%_=Coxuh??oj(tPpuJ4$1B zes`1Z6?r}r$NYTZoWgBmeCm**Y~QG+BX(lK#9>1a(#Wx>3EF@d__N4ui2g>Z;IIZ? z(S%piuXU(~)yjuXxAdrzUdEIvx79@o){zS_kG7``50ttY9?uoB?1NeVjr~0zQt}?x zE%L_QU5+9u#h(QhpAIn?3vJ@Y86I>gPPm+vILd zl3ZZ0g&{89=%hbIg%VZ&B^7D4*2LN^D1Cpm&WTUOzy60>w({Me{;@k&_VxgEVX7+; znNwvA8MboBUXLOy;@g zgogPat=E^r@=e6D*o3q?UXLdKVQ0BbFZdpyLd8v8BFP+fTrX%4?yh1kr(0M&?YXE{ z=E+uM-Q5q=Ey$I{m6eZ%r(_J(AEBrp0-6p;U#ikU_DxQ1sKDn@5S#J%07n;}*U+Ka zB<(ih0A0FD=)6&{eXVTPTSggc&9W?15gC=Bq#((*nWlDDN0Odn%=MhItDPN}Kjv!p zjGKZQT>+BH1_=@+PGe{%#f*noT9kA3)hn2&EMUK_1J*wR5fPf#3d)+` z{Y*PmGi{#ujP3Ni(SG#MFcJO)R-tO%DZlvnSEIsEWtGi0w?0p!Y3pwg3zcQd#)HoM zx*wif?bwp=e(yHDH4)L&9w!tw%J_QA1AQm?iMx8ENER8aXs*7dgOGxcYI;PChtksB z=1;jI7LC4=u&Q^98m*?VnGKKnuex79re{{Y85iz45V@`b(~wUc;$g625=1`HYnH+7 z#XfFO7q5CG(uMy`*o-w4zKuQ3C)KLup}z|+YGXRDgU5V;*mAn_k@{^)uELcfk;Lb$mg#wmKK#R|_wiDd z8)H_%=x>oWk8Kejs;8ehRL*~($9jX~)Uu-!2?02*8Kk`etmo@+C3w@^f0ke9E)y^T z7|KTZ@Q2*|1+%~Qu5UbYQ1RQ>k%amXy>rd`L5Xt%Zo<`-Rc-xkc-Ofk=!_dOYI+Dx z6yd5$mcW_XlzJ3$gl)84U`f-4X9YgDp2QrfR-OaCgy@`!T>2I`)XAsjNu_JZb;WSv zQ{IP`$KRP>@fN>%`P=$p&H4ko^bd<$-=FF|$GrAkbvXxAEZv(_qHphAD>7%7_w0)K z6_+5q+u%G};!vNPRb;Lvq+jupkYbJSj^?7*9s1&+zkC|8Cfr-LiHOX_<{W)Cuad70JfN;NsMDNXMnC<CVS6ljcx_U*vJkKk5NuKVEkH{z#tt81qlw$437U z`Hy-)>A%Eq&%X`(w&n4pPv>7MUH)6#-^TpAtMtG5+Y$bbDyx4x!rzYY_wafFO#XI+ zza8QK$r0|8GNr7P%)`8c^r8qxHr`KPKW0}>#JDW~F)(!f4`6!FOa^`A!f_600QAdX z9N53zQ3e2lL;#m2l-~dQMCiXBi2*A7w$;(nWz5e7%x$I}~ z+vig6%K;RMaF^}G&db(s_Vs^FHov&oe7t|@Z-#%{@b7N(x2OCa9R7}ne~&5ue;r1d zj~pz4nKfCEf@&FJiC;qG589zzeXgrrKK45+W3q=QE*D>$1%ae)1EToDQbE~g=YT%~ z`(hmb+;ah(7yz!k@O=a5H02Ko2oSzi1kz3oTdgCdQ^-u+-*+fQg7SpKW?k-q z4N)6@7daI#4BtpoD(Yv_%@A-gHG5zn++ZwJ=&*I|&~ale-%1MLm z3&;VytakQ+qP4l<24AifqWbXhsQTF0GH++L|GOqXqJ445M-%t?Kc>@`>RDb*3ILw5 z<^6}(RBEvl{mn7tukn4CNt6A#Rh@~|)|-z)Z0o$nWf|9|%PJTjd+2J7)>_v(2!6QU zh?+c}f)$iXG`)V=9Bfnc?eLenqegDXfrsTx?(#>ECp&FB`u9~WUm-+Ms>T{R3YGnB z!Y+zvS&jxjbqK`mw_m`Ge*1@w-@#ti4T2-$(UJ_ZSNID}GmH!_Qtl4??qp3V*sRLd zdYtXac`VawjE|TVeD+Ip#U;F*NE+FIdOEbCn&GtM;|f1reMO9f<>lAF67NZc#Rs}$ zRUzu>UmiDh{8>axwu=Q%EqoJsDqRoyoIjgFliVb}NqQ^f@rqLYMN0x$U9qwJhMsCd zbP~^<$G5fH{Ug9$+``9&{bRaVL1WrarC=viD0}3<6?xLDbHLq-YRZzzrPb1xKZ?G; z008d)8}j9CqQd$fJTiUxi+8@ws{83q?MLS1d;E|8diO9VR$zQE`@Q0fh<&Ti1<4g1 zg}bAjowh!G3X8O5AAs}{Irt@d8`bRk3frOw9znwr)8d@Xf$U?QoQ->}MD3jLQZ)x1 z9kMreZ%Wv+O#kgK`34i0$29>3$o-UQID5?eU0MZ!><8Pv)LF(r&l?PL0SSRrhPg5y zKnlZ6Zl zptjH2Smj~bdd<^}2w|=1ku+u<4%(;54VJsAUm{QFS!zuJ5LH3us<^X5=dK%NkVqagl0!Q^qC8qGAT4@LQ=&I%wi$38$$1%&<$6?U2(%?=bBu3 zf8ha-ysYR1NagQW6O%)R=26bu_lTXv6a~DUXq&jlQI-uU-ffAN@_ImK+P9ZpEZN!} z?ZnL`%i3$419-^+jZ7#HD+9XTBat2m6aPF2U2|yFD05Ktwvf*rg255^%;d(c$+Sps zQu_(uhOBUL;ADIAjZT&7RM=Rs;Olb(UA~p z_#1gKS*l-rb~1F`Lc~knb|l&2!!w)-PsTD?H}p{aAb&BjqbO!<>Q&oqg$kieJwJ8o ztRn8v!iDRq`W;`Q)jtf2mPJcv^vQXSG2XSf9>gK0^D!AK+R!76;T&j1YN#U-me>4V z^}jZDD)&hXUiP>e`K$4jYC&P$j#8a`OstDIgAjGU`x`9F2AVB^IIO%^dY#{Op2~xt zb{hZc_4^;|&^`kdmYeyTFf_>><@rt@In)AmPPZ*&$N4ori-r$3~Z zn3Ce><9y$^kg)@-PZ?Rl)}^}ILvSVI4{iQ8_TD>yYG9xv(J0(`Mz_D&GORjAswi;WfYM~8(ubD+m4LfAZRPKHvoWkH;rDzd7X;``X-u=$+ zbZBzlG^-@LL20kFysT{JJrSm#?*puw-#8>yynffjHncn%qG=0?D?u7=gg{d~AD*}2 zdYTp{Ta#S?nCG7-zH&c*dCGK^X2N2yQDq$@^5vzFnWyzr#OrzMe@_+TD^oppK@mr+ z(6QvqhMoHt=6uHArX1JaiEP!`29u_hI&6y0lI}d%uQopF5VdMP zgo7ag0^kbj?hr@S1*_-ahZk{0X4kpd5xi8)K!_jNB#59+pgCMV8ChzFNm;d+UB&!< znMh|hj6D7(LaO?UHMjqyYa)`(OV;to-MS=PsbXk?(bOJl&rS?Aj?93sE@Hdu7k)EY{&#|)e?d^}+S$@j$EYiAG2Yebu0u&{ z3%4j=gql;R;mxu67XjCG>(bqX7BYh6tOOhv7#6B} z=J5CVxmFf+D%DKPhj5C8#@-77yEGf-A6+^eDsYwj9V&N4R=O2VP#vpZ8xU>5KQgbH z{j#lJ=2mP|+LAhNaeKkB(GQtyOh~J)QTZ%4W@%b26;%0cSkk<|2UXP0mC`}5)u85@cjtZ z9K~fjfIuDBQS1+rYPD7g>jsgj%sYBx=H9KAQ1rQpMOa6xtwHD36O>dea*T?vOi8l-X`1ig_gyipbYVqw) zf1&ovg;3)dd;9<50seQF zemzJ6g11hm-z0m2>r)Cqa3y`$SZz_U^wrz%BLcvztunl_ZR4`p{GW{Ma;xmexRm_R zrwx8te~`TRgXG-7={qE=n8mi*e{N5ClBey%Oyktd(vc;N{!GI*?R_~=$h^OMaBuV& z+PYcT2&b1-`lcG`EXXhRy`frePFa{*iMitmaRZ0-n>8dwnYLSyaTRF=Y*Q_)^?2fE zp^a_UPhMq=J81=0riaIT0Q+yntMj-_ajMPLbcVz*h)#H6dNpl&6Rx8Jat zxqYzvW9X&py!`9k$P5NkZpZ|CSOWT9a*zSGJ zo1UnBgulI#wWc>aJ(DK(K`mzPnK>ZP2c~K(&E+< z8}xOKf$^)vs^D91s~bxGOT5Ww`Yb2U*`#Dd$zcr_TaJgB_5IvLh3*6C=@ zus|^=m;+!vbd7YeO+~(MBO7>tevodsBxIYiM(4S<*!qNndoeUP^wg(L4q`qM*2KA(eH-4IH+F^nql3{ru7M}qjS^16GKX|@F7?bLH z5Mrkh`CM~@r!SQ}gYE;b&nK%Aw)`fcV7{I5p^zhChTZf~ceU2iaV*c88z6>Yo~s1W z$v*SHY^A`Jk4coHFfZCiJ=jdeL%N&l=h&1oV&nA0Pt>VcCpMFZx2=p5Yyh6H1$m(! zCX<8P10~nopSkH+O}Qth7*v=DRx|eKPwq8K@9Ka+?ZZo&ADeetz}-qheozr+ym@sn z_Vxv^sETX9W?Dmn7qi6_yckNFf)`5P4kW13 zxCbig%kf%mGCTGM!25g1j9AdWQ4L+GGL*5Z9@dySW68Y%x0-ybZSIcr+7(GR%L}BB z7QG;C5O0y?!ArP}ln%A!qxEPNYS>yW8(ZO&kh-?ipCq zINretwI%dn8mpJR`x0HVnzOR{AG8m+oQ*@~sABS8#pw0-_WLkpE4BR8y^{9Ktt%<4 znHxGJW%;b&x6wl05&>z->#7=+=6Dh!!`O5pEUf`s|NEy}E5K#p6pWjR2S2#qrb2() z%r|eyj8n_Y-o6@6`W63B?-`UPT#I}~s1&sRvA9x4F2ynY#5OhQiHl2S+`#LH8F%6$ zs!{=g5x))pCYEZoXZ@6)@(PP-r}lhFdX~mArD@R5w>A9MXnQ7LJpWv_Mns4YK^y1o zM5Y+Duwf#s&xj{lOK9{1e@xWj&qz_(HiGG$cM*DXhVeZ3r(*ebcN-4C3AUh}ObZHy zr1_DKxiK=_TU}0enR;QyDBZN+W!p#6BcwPT>yNb+)4`0vCV)wX(Ogk;NJyk!+ZCQO zQg-H#m@RI0SqR~9%$zpApOLiXggK0%Vu|IZ@ zU!J9cHUgFxG?+QXYnYh?Uwia$&L)jVTt)jN@M3VVo&WUt(HF zfhlZRKl~^i$Ev`Z=jz@-YS~-_cNmA(8e@Gld=7yBW$6-;~n` zF)4ML_|RwfFrv_i#!%0oEPY6YS0=~jIWDaz50m=T%q7%nK~AA0sbnZf;Op+EDrov` zk1;w|Jp)42eTcbQT_bB2DK(`)4X zG6KoohMg7LOYfP#`7~RuOZ8cgYUD_bjGN~XCc(V`bg;}^F-93>B&gL;HTPoOLP)oa zTbyf_4>eze)^t7c@LT8@xX^dDIb%)Lj5UAH!_-HNUlKShHkW`AkQ!D?zE+UgrI;8sw z7Qll3$OiD+s;+Z8{Sp8w4bFf|Cl*pCB{VHR%tcc!`V~?Xc~4C2@w=(CZ#|w8Nd!-* zwz1qVcSpk8U3j>;It=|C%a&f?x?<;2l3av{oxU!XCbzK<5l{zpzzO-X=4ta7Vyy_0Am~dc8&$ zJ^c2XCb=r>Os-+CUP-0E+jM^4U?!F8ZuZl>ryr zLi0IU#83|k3cT4h4dpbR3K=JFQo(OK0Zo+UxrgowiCWs6an%%A%MH0DrMk`}_NN5i zxdJksO!0XuovCZf-hjm95=S3$q@`-U6&vmy+tQS~vZ%vkW<2+pH|%5Xqibq)nBV5t zIn2ta>UUONWVA23DMIyxXY3xkDF*L~8^i-EhZ{q%R`Hb5-r67h6Sa3hNZmEf$Rrgs zXp4WPMl6`kdzBVU0jbI3tsOMiX@)ad_-0UTO=Y-{_8Ur)&9=b3*j?JPBTe*Fp&ERI zMi4dfbdh+?{1QW&drk|WYk?4>QT*T(+us^jgHQ6{UhIoa4&|AAd@+bU?Y)kd zc1dr~^4__|6IOE7#+uO;T4M<62YS9p&74KYEv};>E6tN2rS53{qlDYwH#7a~ z58-|lvizD6_Hs!W%%dEmJX!90LX&CRjQ#u_t3&$+R8mtN%69Yn$)BfuvvY@|%tnf3 zu(fZ*A%Rlo)Lyd;K096IhGcYPLb4fjYjO9z=?BtD zT)$xD`UKDB=c%TM;tcuFTVZ3hLMY$X(@CowA1>ReF=q7|W;yqR5w-O4T9fX_YjMXTx#NP=A z#rqT{7Y(_#;oL-M%8+r4_Tl1jfxvRSZ0pS7BqIOGr2CRtV0Fb&MzyNWod2TF_+|0z z%u6cf1Z~{i-C;R1_hQ;n7hRcTf)_yk+J2GEnc)i+rJ<2=ozQbh_x<<7xE~S|c)m-YDbBb&#pg8XN3Lc!BlMLwF-l) zH`s8l$27RQ!i=YU7gX#y*0rlr>oT(6H@M%V_#1`gf*MVJN+Z!uE{Sc=x66sMy7_mQ zn>@iIjvb9wdwLBH);Ch0uXCpcYNDM)Cj>JNoYsv&Oxj#aj8TDtv6|JhPOK|a;k=mS zNX1~gPj4eCLP$|pcbU#d zPa;9i&viQ|-*{)Jbj?Mv{z~GQgIpG6r7{&lTaeY>Px5h24i# zX~7!FHbgCkYja7;2Qa8<-8sgwQEpgHRZ?G?^mqGqHw*?M2Uwc5;R)|R26}z%dRv@ zzwmlbW!2=~G2%D`?xL=w=;sxzD+Z7KmMoM8dABFJyC!Y zxWk&>(KM%`cyg{9sjJq*4VRLDJx7+Fw}I2r9MU#&Xr&Bv*nCpZ!A^=YR3x^}ndc z{?}@)`GuR}&|BDrg6o0oJ2w+X?A~v7kIo2SyCcRMiyXzhVRvOd8}s~2NfO^P($T2y zGMr1T0ii!Z^TE8=^-KgUZqlG4K)zTOp!+Tdmcc3`j)XS!G@9|fJ|~LjIkcJS>igj{ zW&nYs^W5~TvBs)$KoQ4~>oC8y-Q8Nh!4&$S&wDMRYy!rEmgOz#`C@S(dzuG~<=DfH zdnS2@AL~rQh)^g<-pup#?5m(~j`Op39dpc3a(7`*Z<>LM*As6GPDGB0cYADRa|-z_IX@tGQS5q}O@k>8tDS55gM@Nt;k_5E zELJW8a~55%6zb75yhpb1u^DRJXko{{feh! z660L$`BoZ#hZ$;MZ`Gonp(-q@KAo=4ntSZ-1GW*h!-|!27PO};`xYZTuSAmN@3@l=yF|p)g!vE{bHX;hMdvMk5-nv zW|!7UNXY&{rLN7sKGIWkr#StCBz*CUPL_V>nd17t;BA4g5sC7N{ zw$jooIfbELuRESLS5I7IrCFk9MtEbv<*&F{79MyCg`?tLc=P!P*e3`M7RO{-0w=ChjHV za?`_j^%)6?#ro6T^6?+}!+(%uXj|UMPA$SaFBC_8)UGl%a`#M#A5d4K)~#4m6_{zS z*sQc?lorRlpC3b7ZY&Z$bFN0+dIqXjuGt;P9RszLolowTthIOI{o`V)^%} zeW@h`8Ul7j%%wW3DSsMs+082@g>>5%)xbG6&3I#EhRUhY~ob;)>7$Ndm9Se~rmRHpvz9m3e1)KExEYVvuh$#i(* zQZVpN4&t%4&=;j!R$kUm{d;GFUouZ;A{GW3RTN#Z!ig-hI71!#eKk{$3^%}3I_a4E z=*ElcJbF}$2x1wZVdiJYiIEW$K^-Ikq*FPgq=U4YB?NCcS5MjBYv@;Ww1?4;2Bh|r%M(kcvnf`Wn7w%koP4C7s&y+Uc3*Fb?Yi_`HMM?jGJK3*{8yfI3N~3URRW~0jp$V#ZA-;ot+(e{yF}cW zkJ51B<_VePdmqD{Z(p3#*Qimf&hjD$gjKWc@LQr$E6t`QltP3ab#6S$B90;5HyGbs zHnR{@RxDi_8Vs8f+*Mih=)^>i0$ySnLJLSmTI!VB*VQ1Y@??cw8@@Dc#De;$I1g!!r(o;A*lSX!CU2}_C+}+bTF44hqjF7$SB{!@~@)B@$ zPGMNK$Eo%@4E3ibH5(XWVb=BygJA)6Oo52loB)RCUJF5QbD#amgPXtDVs;qD*uL8N zb1$|IzRF@N)GTOx9Go`af|H2}N`L(QZtx%vL)I`S3rkz1eV(F9b%aK&^9i~EKCN;c z3kw#x%;<&sa}}nR>qifM@s6L}VxL`@-R7l~@ToN@-~S+P6f2hKxpTfnOVy6+b_1T{o<5U+b6rdkmGN)JYp`@L|;zxr;i%No9vX* zZC@ueLx4zbub0p&89a3Ap?hB|6G;8{FA8^m>rMTQGxkrbpiMA%Ye+AbB>W^&|?aZlzC7HtRS6AF4=Cg!MR z<-W$wjz%t5JCxPxou}ffZZTS48o6iv%rrJ~ZP+_357zW0jkJp+Uc>_iqlbBns3!U8 zc!Ce4SM}7kvTdNZr#v<{m0Z$KAK;h2iu-p3|CqZ=F(GyRhXoz!-AqTgESob(stRH; zlOCCBt)~Z2y?48m>yAT)E3whoxD(IUU%kOt5wTJfSeqv@RpOH$`RbQ(%KvGv&&Pe= z_syPef3ehgM)~xYdi;geWRfua{6)|&8YvaW!|z$l6|DG(kO=?yCC6V3{kz6nl30BE zbJf4{60IaH|5Q)CuXz1h^)EO>`~K>?U#tFwLUF&o@qcgsUtRP6fzm(zHz7XOAqB{= zbu1m%?aMHso~PYr?00qc7Awo+#J43x0T5aLcc;uu`;oCDq5U(AtDeaW0nOa^ht;K^rE*w8vOVKh!MaVKNXxC685AvnacS zWdw{{TZ_r+#$E3?Eg>W z@>=%d*RYb#NfI@zQaIl)(MXOFP@UHdQ4p|9CghorVe={SsZzHYrS&}ZTqx|F7-<__ zFI*$@w=b<2`X(*)B6lwsp zx6p#MapEKIl#zZ8-qxf3sB9OVUf(DxXxpsS+ydAYk?Gk5Spo)rZh!fDRaKG8S}aeG z=iw|zcB5l6ZTOQ($f7`XoB`D`4QYoo36)y2cN>*|CfDZ4W$U)Rg-^vXyI7!G47VwA zXA$?2i`zgA5a1S;4;b=Qd>ZBRd6&l6J{wz9=fLH-?{_kSa8Kte^V>ZpJyRX2;5V#! z2!Cp;sYEsR2#86F1If-#eLlmf40Wc&5g@BFkq_r3qU4`Ty4AlRnc1DKU94fpc2vYr zjQ=zU$LT1c@Vd*(3qr}LJ+W+64JLsE#Wg_yb;|7B*0{&=@=>XGg$mRIkSW45_vN2b zeLBvdr2k#IhR1TOu4WlAnxUtssT=>s-DK*^opUveP^$A6*v(sy9&Ri;BuuGFNn25> z6HLGdd?Zp`5RhC(jdh~7t8Q-D?2yYo0C|4>#h&i!h&B* z0}+;vunwv0+En-L`+HCBdrapV0lzf;@aI4?@X}&GY*vCq*=HuaV*wMItTO64wwrK( zQ*}yI!%Q(eAdYXXgS1kv`w}F9$~NQtQIG_OHO8C z@vQCH&=&Az>@K}Ek|t3FTb+)6lw=4aXpTB~mb}t1VSWwFj%X}f!wPpBx|rY~T-;&Q zuHAaf{L`0DYZ#FC@KZ`Nd0C~T$t7|I1ZPJ8(Ob|9XOoatYVFh{&V06XG)JcbUAjW; z@c1n~dH>9r(F3ogqy6{Tb{?`9EA7(H2kMbOxhGTIWW-seYpLf0bEiBw6^=%c4TDzI z-lVzio$m4>r?ZEr3wND&j^09#k)DVmw6~_|lh2w>a2gL&jkUef-t3e&aql}gNT>p% zrzQ6NevF#|lkJ?K-jfE@qQ<#+-R@I~-t=nM{EqJql$9`GmGSOdc?t%`dm|+I_tE}@+?Yn?0_av zS1m;%`8R^ua*QZ-AGFj|L~&pa_!D1;9 z385@jWqs4OdRu+Ko5ImF6Q4clIC&aIADC-!g!c@CLIJo6-#dvzQ?k*VhGu}p~_Ak5gvN`=kq)nT)qNl+B*V2h{M}r2*X|3KnC0a)`A*+JNK>|$(1^k zKg+qPqM1>L26WZ23G$V;U z&qZJ+u8ssn_lciYQs9Y(UBDNkx6MJ?qE4xsk&B$gtTN@lC87_F?$T257`5MKZpu=-PhZ*fk$LENNa!HN~Y<` z_p@fxaEkHAv;{In@yU1_rv&*AsuHP&Yq-wTIXsHW?#2eP+;bC@CiLTiP#6?3c$rh( zvLi%`JHgpFUmnsEHDVH?3*^jjYxJIwC;*VmSSKiG@K03cModBt(_Wmof+{phbI|%8 zdDT;pG82~YLIR_CtAZ{mfh)YtEgO3%x~4S|LG_NGZo@?#sc zte52@g55yJ&RPc;dX3_lxevCfQ10lGN54{+*))Ks>u5R-niuj7Hgs`b@6;6L zC>kx2m%UoGxh#mX+u&uKhSl&pLaTz>jL|M-0&&vm)#x+~9XHRI)0uB7n=I6C7mN|C zHj3DFO3>BVX-{|6^g$!5E6S%04dbA>CCt95DYepyeAt&Q!Vu?0Y~!Xzq)gMfL`p9+ zg_dG-FGRn?;#PK>Fw)fyNX~F1U}8=zwBmCT=hMV~#4m}}j6 zZF>)hkl_NREnoIZ>D(cZYeMFgwSvXx3S+iv1n54cZLl^vl(F6-Do9iSC(~UB6yV%U zxe_@86wjjGg&Xe+VG-ntWAbIWz@2;UzNRLIC*jZqZl_Uaf4L)wChX{?Az9@9btgLC zHnZ>H(tTt1qGU9Un|aOPPA)S@6Q_4#SwRFn7*bziFSfOw0k(_3UFyiJK3j17k^Y9*?h|Ji(tSrl57fU?CYQaP z`s4oQPu<>O`bb#1i;*9e2o({@9SV}|r-A-KQiMGwoJyz+EAGzu`{M!)m!yr2Ia=Dl zRW6$DWoB(k{I_hz5cuw7kyrigHT9tX~-m1X(GLG|xWFG#epLGi- zxpXzLX3V8)Q&OF)FxZ2@fH6v7g+PsGH8{B}bt|%rW<9+>i+?_@t$Hhp zHGrJP-rJS*XJZZ5uo^s(MLG~Vsw+uu`C3Hb8P5Hi#>Jx&5d7qZ)o+?n*@0p^!*Y#X zhw76!D?3^?KB7&ThkK6C=rp>=x6x-jq);~{fq9vER^PU0fo}lR04qUmu5_S4ui}gt z?oEuQRtgb=rV~42W&zyDj$XPQC_mj${lr89`qu093P#_FP^Jcs@Kz9LjiW*q-QrPqu<@y1Zm0sW>d1Lae6}5EK$rSJEF*a!23e4@bduVb~doqCC(uAS6$#Jm@$sF>qY6*OMo_=?hH&zO2{zO{5 zsu7VHTMgg}UX<{?6&a;!4(c9=^vvOsVcs)akg6^Puet&j?udm;<35&5p_A(0NXOV! z8hrMj0qlClSBf_cI618I-pT8f+$<_H|SW8?Z`efdoJaoZ15g;3b} zVj@i^FGa%W9>2fH8@Q@uJOrLI&_BFXVy@&Cs{!|%9Kdl(10UvToIvO@LD66dc5vDX%YLIooh%X0>W&gv0kGb8$L zc|jQw(J88i%c^_}i9_M1n-W3fh2h1~xfS~Isn5%TM~8R$rcm$>K&20OaiQ@r%^3Lx zsdV?6%?_Paph?z+)b5P`JwW;m3KyxO()v#Z!%mJHynOvR&x|@a)0x{KIXhB8#r`of zgwdk|hC|)#xaYe~0tYA}7i@^m@Fi;tp-;r9d&gnrp(}}n>rTyu^I{mhs>#)#x!q-T zRC0eYWTzM+`9*Td!brETMk<3D=ur)q!sNN=>4ulc?G&M?KoPz>U2L^WR0;g*~3 zHt7(p%1hR2_f&vBS6f+kbA^@W0DWF$)b0GScOq%o7wu=m^83}NbIzIyD)B<;#eSR* z3nxR`!9*07j&$t%F7aHUhTyG{_X3^lOnrTm<`uh3UXtr;0B~lw!Tc5WyyUVvOj1I+g{R!Gp52_zJp=R1dC{}0 z$ss?jZoAETxHJQAPEzGFE&8}Gn<;QfL3*+R-Ehy71i=ey`!qiNzI=F-gUnP>c z04r(1?y_KlDFIMHF_mYYk`LmCRgh1m{p@zx%r%U2#V|u7I0aiEA;6(F!AT4+JcFmn z9TMUXEMv~fnRZ>2M_Egqy!%(w`UuS5SE8vw@)gZOv(%d;+K9{!YxE)N$B! zT@RNx6PGzUnrTH-Wejdj?{vmZtBuaSE-Bda`!Bx+!Bg~F-`w=U!LStC_%7Tl?%x+t z-X93^Px_@rWN69hP^gYD*28$w7jHLrrvz#@8wS;SrrgrN3oIaDYD#j_v&Vn~mxZ1h z8rpCsCS7G`gnjlFh|UBwnT>4%^Fvupg{vH!sVl|O4g|6LJi zYwy4o6qI{At0snMm+qv+pQxD-k1F3;7_`~IBv_=F+0rV?$G~ z?-j>~=G}2xgWcLde$I$CBy6y<`mNs%hfr*qI-$k0vSLjym~u=+;&*}3s{Qs?h>>an z0PDc{X%t*pZ6Z}3J$yD>rtfe!Hp=s$sH$^6YER0ly$tYChbg8}FE%a}DpH|8)*m1{ zcm|dTrrzQ8L2I+AB5t=j#U5Yfe=rf>6!SX{{ece;y-#KJASX^1CdJC1wSke7 zfR|~x&3=lbG{q%yl{qcFf^`-{2k?6_j`tL4M?%=!vvpFFcLg!L=zA$fc2!}C)&UZp z%iy4D&XJrXvg$Yj+wNXoPfxiEgYFpg(KaU>t5fnYM^eLIj+as(Uo7#RjrF^+&o(t@ zAqn_qtGI8lns&+=-qO{oj)W@C2AjzR&Q8(w6uBl6KmZ2C0&$EV`}d&XSw4 zTL9$uAy`p5H&}GIw|#O?xkpy*elL}0$jG2fni93mxb1Xf?6UKSedfxT_yKCDqq%#D z*KVmg{hUQ!pz&e^ImTePmdhl`(mT*##cMc%wb7!k1iIo{A>6YM2a%ZXag3WHdS4Qn zA3Od*@&^gS{M?V4J751Dj4U^3oN%h@Oc&X!O4>H{`4=`8%K)_{c z?{qtz=wzdz)f#-C5e}$i<`FjAY72&e9A~_7R5ny*r3?K?`vr*+D4CzrZxsgc^Z2~L-aBByzU$1 zCxa}q{kkc`m<`sVRT2rwf9rAns;5lfuc}(I<0GE0RHx+gwg2yC+_x*~Hj3Rgw+XPW zAoU|(1c3jZg8`ApH{VvT(8GmZEv++4oPZ9J_DQt`*efg}+w?QM6;ehX17MsdiN3j8 zJMEcUZJl45YCHoNUM_XG%d)oZh;m;jp4ja1&n8U)51q1$1zIkYCB_`_W**~HO=o)^ z(XQ|1N`HL+=C*p+eqJ>EwN?44(&Cqn+Lz9_pCYUU2bmmCV`Kjy$;?d3RjNAwgXARr z!@$?pcjv(G*6tT|i-Wf-Uj4%fcixRRcOCB4Jze#B73iA7eB&zb>bI{%5kw^^IF064)m&odD3R;1gO&xJ$Fo_M+}?0EROyiyr_g1#)Wu>t?A$#Z#pCu_TH7f|Ab z_3d1>Xvz6Ls|*#P^MQ!^FqS=;^~(dPj^&Aqbbkd%V> zFn5|VBfq6`C8<*=;8kxm5vub~m@Zi?-Yxjf9I5*8gq<%Ok{7LVBdGeVsnzfav zlHoIaut8pj+P*(Ll;4oA`v*x`szSkNO7d?cjNhb(Zd)Eb7-l4%HKfgr`6O);$4v)B zo=y!V+HAf4MZ@Kt*ngzt3X2!jd{rI&C&9llT_;uggwq_3(2C3XLaRB!$_DAr$V zVbnjhgiA~R&n)4;OS687_2201{67<`rV7b_Ce~jr?tjW>{i5ODsMdd=wExr;{ts|) zdkjNr_N3j0U*9x@+cT;3`=Xp!Tl;Xf= z3S6_SOHDeLFR;2pK@7t@9a_HlVCH30@a+5j|M7f2rzVyERBJLc@I7z5HaZgdQcrpF z@UfpzOWpYECwGoMA3M#2#az8UInIS|E-%kzPY8s(RTifwgwZ2nGLS}06T{*6ZW0Ih zsZiUj*#phIFXlG3YkQxOQ2*Pl(f^Ns6i#kL9H}w3J9-<7Rv&6>l^bJ#v~!EMQH_eg zjI+e+jT}mdW0aVURN)sDSG+zDeGg7sk1o+b6fv9M3qEWI6atqE#z#a&J4Odu*;woH z2foj5f3%ghnA?7@_5KeM(fiVmO)fJ-PLMA{?;qP2bcGkEAJ6|mlFRyIpW$zlly9B~ zo#S`AF0(uSKJx2AkN%P2hnEQ|f3EQ812ODOd22v9^hU<%`PVydep)MCZk;Z3OnwdA z)DqloxJ11Hew+VChKF9J$^3mJEEBGg0|uHo>e2kx26LEn?m)=V+G8pP3s>7kbX6Y> zyt$Px7M7Vi&MPWU>FIWS38eJ&a@Q7My-RYH{J|xt4u{RNR|ZvdzmEPtc1Qe=cCxco z%_q2i^v~J!niWWE(J%w~^mk%uymi%CA>$mvl4tua!+dt*LV8|?_)8n?8+D_Q&$Ju2 zZyCLCIrXxe=i#+}J?a*m6JJW5h4Ianzl3^)F;rC&JA>0vH5NRMDPBBlsJL4N70hq} zLDo}|0*WGs{_ODzoy@21=-hVr?)fW=mVL_Z(avEfQK^GCs+S!}^#Ju)0VfTY(CJU2 zoKs3h(&k0PN#MhV=LMX5kQM`&@aHG!=Dy)^iD<}GNoRju;(-=?HYL7y30@|`YMYn1 zBjJ!7ueAhooDy#nRn%gAL7~ZNC-OtiJS%C!{kvomRPFlHURB|=$`cI=E^QoKPqTD>yZ;aQNtD z%&mZ#iKQuE2!jVp^+a#@aeX(_Z$}jNq;FDt3D25aMX;?40q)h1{JSin_HECb`lX@3 z(dVjQjS;ZO-!gg+$9X`&W&c@2ml+2-zim>ObRwN@nZXOk9k${})pLB!-@0U*VweWp zWV#q9%aZ+TsZ+Y!szDG0--$)O0)f7Y#lE^cj=pSsQbzXzSk zVxwuXvtgcdZ+6HuNUmcG$K|Dj+2no#effTez{#pb;|6X{9N6#) zWa4`HJ$Kf0(I1D+11npM>djUOCC}xgEK#OEci}y-(k5aepF~>Zy1}Eg7zKoLmrHET zbA+l*0I4z34%xIglfvSsJSz$?JCSAMdSx|5sBVVqbJyy5>gV6VZOBzTTJ?q;6P@hkmTC;C7B_98x| zB=bY~50c%#9n)Vn8F)c*b8I{zRZi(;^S+s7ukku8#f&4xWDI2|oe=Nt>(Cm=|DY{q zA?JRdvR34sDWLeRc=b!f219ARg0-WuRsQ8J?bLuY+u$Kn=JpC**k-bvgZMQ1nXNbq zh&M?XQKAhSj2SM1B3!`V?R~MhWH!4fcw>v{cG9pWS=|F-b>xqzX2H%<-Da1lX01b( zblw_kd6iwrlC6^t$I2V9D(93?{`g@w*)SU%NESUav!V%oaa4@{On(#L+(wpKyQM1I zR6J-e-R4;qna#QO`O zy51?;nV+r*n`~IY#X4XQi3izg2ithvSLoc8I4yQ=EI1$s*je1;)DaguC&NW7!`@TQ>`rMzO+3`fi<9F25}wv8IL zFYylfezl?AEESj#T`uNsd~W0kqiY~!u|s;^+d?CpD~}_X(N!=IL864bpROz>-vtav z+d6MELfHd-V+j~a)`p?)=@FY3(4K1L$ucmlZ$ij9UbTe67sWT-JB_T6+%-i_xB~+<;cv--&P{=@|Dotjp|89>C$W=6J%%DkVqY# z;3g86lX3Df{|66Q(GQ8*2#%5EV+=hs+t>t#TW$l=%W1yBky>>faPJI_+2--|J(3hg zL`DYFvBNnY(LfQs9egp{w0BcK*JU8rPE=!oV0Ho9VZRjs!2<&fTBw}Sb~gi5;(=X{ z5midu)}DjUr;Zw5zVXSr+go&`a9rOUi`{_99SZ|idOP4`l`}G)K0{IQrMfKms@8V# z!jYG=T@`q07kb5qFnF}^LrS5zQJW@&aehbkvOCpdvKGCubHW|oRYXU#w!kj;u|jEh z<%)mZ!kgaZVy>p#d8(8Lkr_s{Z+hoUB7E=hoWzR%UTWsB1cDhj4yD*FFmt?J$t5n+ z?03Wjk_)VbT?refbvKUl5*f>TaGpEt#yo{sS}f=KZKo}eamHWH2_DtwecFbl8p_&N zu3_!f-d!|sEgH*pw>0`WonXs7V}gi2pbmi7!jrt)Z+K_JI9%)KF2LgUk4pPP;D9^K z1x#;JyelNaoqnUzSU|^{3AsZ_K`F2OI#9`&Uthv|kXD9+=hHtI5Bu2Cm_&(!& zxAhpVDw??-`cjX%kdwyPsfFwi`2}W92DdcU?#9?{wi;Vzg3|ZAXsSix_wzcYog}~>u?XOT??TS;?^h>xv zd;8Gt6tizJyP!G~twt*+P?R6$TN2;>M#xZAT-b-K+Z)>O4~nFGq6(gt`W`G>i<6^^v2z^!8g>oNEqG01KC$NUL@;|=+*;)iUhu~XPU&*T*qJ?e_I6JxQgj-{uXf(j*9_LW#M+n;Lr*)D;MI z#tuV{0s(s5@+xX^g|bg7#HWIbX%1^*pDX`?llanx@7pcTMXN2V5uQ=gs7=!vH~*kE zCwHR&Cx~&LVKMrJ_K6i}yJOFoeRBVRl?OKjjC_&^Rppu@l>5H0!>aaVF!yD%vRNjV zdCWuSfdEhVZ68!w-aW6HkF#?b8QdGE;&Xoe2mA~uHJjA7dRZVs;cf7pBLsJOaiU$pa)kU)S0cL?qTZ5$Gy z@rK|u?(PnOkOTVB&r3oz2>)U!@fFH|Dyz>#^8xn_P7(|RsWQ^3WvwER`XMJnP8tXmPbw~9KAA` z=^P`(d;xTX%PqudsX^0{xj#GSsVhdQ3hIK~ug(-$1LU5z*Dj1091zu4Fvsp%&F@k$Ms!isq<* zk+%~d2pZwJ$Q((%O);uVrm~K>`?NU%gsZ;+{+n+ zNt&HVR8#e)mn~3XFATJr(-fhEOEjsxO*0-FX8;_=%Sg228g*L5X0#4Hnv?yO*rgGA zE!ymOw`3f=MJd&6LrpO4L9}%~NLph@^y}RgD~GC59wPwNJM(YG=DE^h{0VNfxt>%% zqIlu4fF2Q-HF`|MhwOu(DXB%q7P=X3w`OrXaC(^K^E$r>`)Wv2)MC9s;4XUfOZRf{V&(I> z+Tdjdcw_Pqi++kTluLH!hXOM~Y$(wrYQ%B1vy6dQo-sxjz5{Eu&yEbQ!*ebo=(=Rh zLw0y%&zzVz=9;@Y**Ds5Ys<44eWqh%F3^=Rlsn`2c7TVKB90&JSiCD_d7Wk9LJ?k= zl?dpMsh8GcODxldW3I&7fSA}f;A0%Qy7F?;@(59f(6pyzcPf)< z(hEK^&ilG|e(QvbJG}dP3@SPw^E*Y2kvNe!Z`U3Y2a#83?QV`SJ1O5>Qd>r^r36DY_#4)CHgyT|*Lxf`5 zkBS&awMxn232~&{K`gBzsWv_M4R1~K!JhS-Mg=lD?9%JD)*ugVTXs5GXM!+vtoQ_Z z{mypPuJq^Ht#D$BIYNzL2v>ztcJCmlu;K{B$X?rYb6nY@&}-=H`VyJK%U}wv@?s&% z_l=(pO~e;2QDs*xhivYe=NQ6;ol1OpDCT<#{xjRmx#-uZ!FcN$PGS%FJA~oZILC1{JH}r0P8k%$mwmmYIWv}r9 z!#w@#qk{G9Wuxh6%`D2kqnp%F@;Hb(A5or*j8P_t< zb9%$QncjfWGkQUs7891b&#q;VT|$GL72G9ll`9yCbq_Br`V9NR$@g|Uq+G*&SsbtH zSwv9;5w{>%ph>&3h|r@E%%84i(PfkD0=Kl#lO#hd5w7Bc(HiY=F_HWSrY-4N#b1R2 zDp+OwUml^q&80ARkCf`SX`tis>xsu+tbZHzF*u%A1ECbxoJ8m;`?yauhT=DXROwT! zj5UeA-mrpuJEVUwQ2Jbz7Xw`^(t)Nl-=uiFPRoFt0 z)MbKOkxgYBTvSqKJ}Ic%WYu>FRC*ol^QA-umgCykIZZ<$-c*}*KLHU*NsY^w>JTsB zE0eAeKydoj^e7uJ;W78sL?>GN^!E)7lHxJftGP-8etkcoAs686Wsb68r95nndQ&8I zuv64%G3~nxS5pFYWnnGZ$eEHN35L2;uINihCr4L+$^*$NJP^>IGr<8XwU^pYyyRt~D6xuZs^j9>@lIw|9|S!%LjP0)vy_Qr1P zBO;Ue&4@78=dwMl2j#@pZU}-Q_tbYGj_SC)%u;4T9lUq%9wWMZV@t<39r31ROATa7 z$ML>5#F?&kMT*6lk$rCP!dQ@B`J#FGV*FcRfe$Mq4Oxa%zPfaYUev~PHD>ukzW4B` zXBMiJ9Wp)%zgMP$-nB5}bC}LseO4&CQiJ`4MZcNxwG<1%Hb;bu0^~?{UWTk)=4;nn znsq}$7vYJfjB$mOW&tmhD)=MvV?oqd`Yp+(HGi(l4S6-R*mHA>9yR(Z@{KGnhyZ* z2#QXO3TvDJdU=~V9nu3MgB-~xu_vPz=K3ZKT7y_-3#^Cpv!Fkin;PYtSgA2+q2Wa% zTZ~d)1vwpQyKDnFGONtWC=rn4E;Md*t6;_FX;~IIi(EtTSxI=Eb5b}w<#>ZdCQ1ou zNf<+j?Q*H*ekm(*C3f%-f^jOk)5JObVAwtuK%mR$ z;UD=kwsar$Ck@Q5k*>gvV8BF__Uu&mK~c@{zWSNVZ5}qCyV{ zKMuNVxkGRDPiJ|eN+lW<2plq5-J_Q}YZ8jcp?gj#m|oA!Z7x$_=~JWeZc8|rMz4Xj zfhH_kr-YN%bdd!By$I`$0PZ#Em-hs1>|vA6=U8p8$$8Ki8@X){=vwHe#a2*1Ykn)D z0CLQ!F{5Zw6Vu9)V=@tQUBW7DVxIPVe+{olTOhz#T;!Ze$Ns>KOe?-adoLQT8srJz zSb&epST=&!9(M*So#Woj4=1U}ml0dlzsVRH#2*N?-kWB7T;4B$Ea_=YbxTP^0sQ#T z@kojd*2%^*(|d^-XgFuvE6ifoTJJKAkc&w@Pa<|0cMUV(ckdZcGvF!Cq~P(5@QNbp z07tUU%|B~=bGMNq&yQ(p`t2R4#LlE z1xCzZ*;6eV-0Pgo3)|*j0gRwMj~oMZuKa;tr8`H@k`Sj!f3^SzPXyZjEO7jR`%I}( zhDRnKbJd;8?M-i!7gxHwQcj8@4;b!F1W{u&rgPED%;DAi+Q2PoeUh}>JLVXtnr?2? zH2nF5J}lYzb4(@l7>>=jo@Yi?*u>~YQKz}{tP$r2o=A zgikc+GRrYoRRm-DG&fe6Rq}MvW zEz*jQ&8V7Ej%C6^hUep8YRr^pdHDrMI6{_j39IlvsK!VJ^gL*aizw&klC_???X^Qv zPL*!kjfqX>f>The=dz4gg7t9LFajs1#z4d)*tf$q4Pu^waajJBdR_4Sz2{)!jVd;N}06j8EV^dDlCGcj8G=cEh*ls z@jV;7j#xkD*fWEPeo4IIO_c35bSe$P*yBE)o0K>auVwX=QNJp(YOQ5%us92Ab+FQ* z70mx2gSpe<(ZQE%jU>@9Ne-;0W6%;F;){thUybr9O~31(jZaGm^LAelGQJclwxZpa zjI+8%qfzr}y6jUS6VEHt6Cu;}oOwQ=vx=Cb_!byA4Ad!FufR>zgZ1(|-!rn6|KPD< zp`)aZ{HCqd6o40}RWNI9Edy4_P9ne^BIi*nPcmF}W#CU!m>TEa+(~^s8W*pQgPjOr zqYhFx&~9Y&vn#UE78XlZR$I{S#+%^Vr=eR!?#~%$tQjT2>MaV(TT52Lbj>KFxKudI zg@;lYd4h_!R44Yc6(~gO;l7E|VEJ=>%$n3^XyO)9%a|o(Yax8;d%k59^0xIc6RA?Q zmIfYovZC@9_+2Tl$@*UnalTpH^9bpjk20K|7Ln*wIOn^1w@+zTJ_v@SLkU#jV9lS`zkNf0XKYeEdv z$LBZ;no5YVJaj*n#r&pJ($DY>YmBP_HH$u?t!=vUJ2odhs}6DuFM&nHLM8*_@JOX_ z(aRDvyON?lZ)3drtiWD%sxhIo&8_&gvcP+l(ScG#C!#PyYrjDgSVqzAo-Idjp+RTK z9_M_rw?*Tsq08skoWg2e_$|B`7Q{tDe&zMqHx;(vWq6qFr~Klw!$Fichfl+V zavD;VEyddqH1j;smOm(QKH+@OuX0ol(=^w*Zzol&-OA6EE)6`>nn`t-PVLW67|_&I ze>rB|&p{Dvi3a3jP%~UQd9g1`a=_P(#{T2JTnKO z&&M|NBRbS7O`DTe$(&!(?_7K-TiZ| z4d#Zu2Pf+ilK{)>KT3!|u_8ylm?pWOOH9&mO{;g>RrzA#Y1etMa=5hSJZ#j%F!y=K z)RR7cH7>i@t?uFJRuSC@{4l-`?M{C}-f2PVKX=X?pE7li#)$9 ztLtp8l`>fCe`ps+Dzogduf{8IfLS4{SNu_N(_bZ|dBJ7l^q@_^?W$Oa z+#&6}xCZu)O0(N|%f0ENuuhNT>;~!pu^49Us7Ro_0l*2A0hzh3EQ_gul$^ zbP12M`l#$cB3EM{P12GHihnlP5(SplY4MHYf{v2EDpK&=@QA;NtV3XF^}XxI1<9pp zimm)TSt46{DWfS@j`UkPs~MreB?y$8dbW91a28*>nxpqEKH^oT68vgpav%}H^D>`% z*xtAi4>zLzvHBd;);<-aG{UOq@MLYgcDpeW>f9^*6YwC*J>ebyW!Nq;wa&TPm9m2P z3q+-K^HzyzG8W7BM;OshwiCQ47PVppkus_-CY(J`307T)j`}jthG1u>A}onvx~ZH0 z%cx}cQKCAD0b{-Z7rcaXEv#HutISkrxG7SU*p53j@HGQ{uj$gOddr@yXB`Rkr>=BjnYS{}g{pXI-=#h6&|uF%8m2n(3fn;a3#RHB!?M9d187h5TuXz_ zf<5ho)@iqJB6dGNsMH@(ezI ze8G_>MvtUp>Dht7ZV-Pe4bS2|?RYCU-lj_a&W(nz9AEi~11UZ(eUhn~rXI!G$o`?} z6xdI!RG?kEu&0`R)wGVzO@XSrQwH}FW*b%9f$wt>Tg@3)B)3*z%M9P59epWq<_yW~ z4K39W4PqykR5w(Spf^1WJ}BNW@06cOZF1pRIdfWO3YvA}j?T~s_ciD$-=OZd3vFtZ z8Rf->1j>h7YaTEotH^rEu8J+Vrn|0)e2ZC&Yh4?;B6&YFH0=!MQ8Uj0mFW^vqyF&G zU&40KvTo1`U!Uw%5Z$v;Z1h`dt`SPiCzi#5b*?fHfzvfCve$@>%C^&4UQP4F4t)I@ zq3WfvkiwH2{jJe`0@z{p2|Ocy+^#F;`a$85m!KLkWTbbcoI<9!3eSo8jG5pU$SrQr zjy@AOrAh858maVo+DGv745PZC#d&zKrS$Z+u4x|GL&LHJrNz`jNS)3s(^Kc^&H*L{ zU2YBc>RnQJ>e+~*$avp;F7vn@19PNiu|I=tqWms_1-qBc0^vYZRB0aY!#?|F5-R!3 z0Og-4ByIkpm_%V7)R~IrW((&m)$F(Ft9tQ@Qh^(^c0BP!C&nyMn>Kl3|boCWHU- zr%oE<^E$^8=}2jUw{-X{DNG+xdFORh`Wb-bFA=~emO!X;<54}stV7SP>_^QHW;|He zm+&G>))I@KHq^8jUXA~;L6Uv&OC{Cmx_4{+RiuEZq7oy*aFS3CR@W=~t?v!497H&f zvy6G7>osk*6NWkbtspC@{UO?F7w~7*pc!#mLJ9*w0czhQ6>7a(U&q{Z3lXR=jgv%N zG2J*YgvN4}z;VbnzN53dIB}*~R0&ex6id%vd{U~dN ztcCSP><>vW>?pG(?W!{y=f+~lS+GuNHtfx7dVI>M3vXWTLxmPBpacnw!k>GQqZ%PX zu66C~V&WaPt~J%1Us?RJkQ5p@aoZ+DZ0&q$*xE4BVwb4+{lxgfY6nffa8fkdf(Fy* zX^crNjv`h4CQ!3~P>D4UG7&TcvrCm9!JSd-|btr4$Kny6a&8^Bvr zb|HZOq=Lq}yiEKR!0S#y>kjW&wOGD~$|r|M2|8sHY^>T#-c@4omM=eApN&=vV|8~u z$s014lECefm_Q%r9ap8V-JW!S3jtafv%i}~xuBx*hR`ELMnMaRJ{q)a={YB10Wqnl6lHeSSi7y8)p z$6ZcOB%FC5#AgtDFgsVi#Vtu&2s7+?U83)INSl>Kk465q<$FejQz`{nc;g9tfQV7#id#H53BG(zC8+QCf23BBT&48w` z@aVf4j(K5S$KL?0I%KkzDY`Cw(uj?6x}#_1;|R|<#9B%PvPzrCpM7pGS~r=sa%EiB zKIqe8GjE<-!sVi_r#up8b(LcR&H<6Gr}Lf{hnycQiPH56*#yA5*vR!iz|3Zz$a~j9 z_Od^ z6!@<#z7F7)bk(pk7##F6DQZXALR&(MSjK2V;wNw@*taybeu>ZzYmMj@|12q?y=D6- z?$0CkLT6WfM_4?)U^^vi$EW2XC3kbn-EVaL?0Rw8>7>y|tm>8}+%&NVh^20Z&JqDL zM};J)8ZN=jUKf}ZX|GGB()z;pAaA@Md3$31PLF*J>sL}FmGh8|cSl7?pL?c27^*-) zHZX%&V_#y!*Rk(6pnuKf=>12`eCqRGsH&@PU8vG2X^YiRoq);p-+;@&7Y`1&DM2?W zcK5XZ9B1yoQ4~g5F)E&i;a=Q$_>*g=SM3J@}Zi^WukOW7Rm!bGLWv_C#rmYEm^Pp8|tkI;Pj{ z6oYUPn1?NYFJ<=BYe9AB>DLPw5=0||v$D5MjNV1%5}B7G+5;=v2XPj;y^jSpI#hhIXKm!e1VTy_8!e;OFYb$L#R%qf zvg0hC2?2Tz6bCfF1g_0yFh+~YeFBRvwx%bnBgd0C4xEdO(-Qc!UKgkV4dU^TV zvX;?bSU1v&F1<5rOfrS=Fi<%{ZhQ3G)tC>diCoqYq3&jwpeAvZ zPL1bSt>n6J;~&O7_Kulb-tT!D_~?Yq#l9>aE^dBVBFXVQ?n_OqkfV?A9mshPUJphr zMd)ZiM3chN_$7~sCSUJW6-m=t9H_f+D>R$&z=9Mwte8Fb7tLXMr&Vv|-L)*V<%`3L zCwL!=rYJ*82n%Gko(ps@$SKlc?uS1K%r}^t6!;>#DOy}-NGYzK^mrn}plNZ_*_0*a zHOVT^I=XCWQD(%`smYl-F-|IU{@X2U#SXcex6EnhSy}r@4*b>3hY{R%3N&&=Y{H2F zL<2;jEruFF?h(<5ohjMeZqPIlDJ1=qM$xdhmtm@P9>0ds(#xr`?3cxT{cVw=iw6hN zQxixQ42bXKu61B>l?5%$hH&I#NoBQn*c)ZT(5@$>LaA5W-ifxdVO8F6)N0UM{dA7u z>?%~^LqIu1W+fFg(K0p7Lb%1QiO-;4f3L}eYKpe}8*n-p>R4);=s<@12`>}WB|Rf4 zT_=NMX5q%3o}|d=K~}zbs^e(YpvRsR6PpkLDUd96hXtxL9@-@S<8OpTPtUi%L#=GT z`F9I2jo4HlXJ$l(Y9dH;uLsm1NX0<+XNmcm7S8GPQG(k(Th^U0B}u!u!Enc=H2F8|0B47Nbpa7O7q9?XPX6-GW5rrUxW zp!v!~>(5{Ej=K#0B^Ey_Ot2nZ*)GXC>ea}i^OS3Ynn0{}>}Jf>!AhyD9Fq_aZ~{3= zr!{?X-zbfm()NI2<}|%G4^lRwtO94=_~#Gs%;bl0NLUU#Oet~NU~!B3TMe3aI{tOTAM=5^H(#7+)h}5U){ulzt;r^zm>*E0pN7kCVFcf{{fFz z?hCzH;;_f>$_L-fXMdIX&eQ1mhsVPhh(-H|-l#lbmXSw!p{GuGgqwMHU&}6Oz>Sv^ z|H3XlTV`Mg+?G^lA9`uwV43+b(u!LB>xU&c9{aZ5D7%&FNxuj25&a_H+q(pYq(Be8 z@dYo^$RTjk^~H`t9+QW6iHPkjM|LxE-v()nyQs3{D}Flcxu9})sviB}KV674D49Axo5TqYS$i| zGo__ZL&~leu04;1rypH;d}(ms#!^}tu3WX}<;`YH&*AUtQ*RWp0c`79?A|2ahitIhfo?z2eapgtxV#evICc@y>5L6e<*N@0}tFB>#==`C=e zmg)(a=99vMsX+)=HBwD1eKRJY+d1Irs&s-z18Y&7THDZG#@5H8-Q^v({a7-Y^)Y??!4jlxp5g+|B| zNVb5kW+Zb6FbpLjx412Tx(%|H`g3tRT;-7Q4{_cxHi^Xm6t2r&vw=TGng1+x z!$aP0z_e%64ZiMeWttRP1PT8nJb&-zoapK%dU3RI zF}!bMUY7jO{Mv2DD%(3oMxwOw&VWLDvMnSfEogP*o_rSqxANV(k*uw=@LI?xew2Jh zNsW4k#zUw`-3?YNg?gJu+d@0{EgK?0kzrE5q!)0i)GKuQN6EK;JH{D_K0`NRkUIEI z5{s^PF#D6s;#5O?`0v*POy7_?!OdDaQU>1La{c}MqcDI)+uv^O!X5vG;QfMC1w(cF z?b6_eSy64dWX8-R0E9WHyocqp-!$!8WUV!D*i5zpvrsJyQS4_swjjRQVsEs3o=0Zcf_q#2i z0MOy~eTY4E=XY$LM~&^R0-+Tqg&tIA5l1&%Qi+=&F0MYWyD(A>r$UYc&4e=QqG);puTDn$kx{r=Q?8wwu>Z z5(gs24@57P)4R3L4VMFEJv;8$0h6!D({Cre!yh6RMsIOuC4vL5@=plwiR-^!QdR~$ z2k`Y>><_0wq0=|;fi(S4+SOxGK1 zhDVKat0JK-sCV`JcJeQ3zH=Ce()lYl*ss@z+jKGiRTBQ^r9iQc68s6wiUK8gt3O=} zO7JLef@+7o(q0P#aP@btd$yxo8h*fdbLfi$GxHB*1U~;BQ?IbO|CK`g&%cZRND!po zEbW~$x(!jR1mExeWlHgRcA#|feOj*{@g@8dn*cL%y5li4`8ST9-pXsQ zQC6t);T(b5h`%i?VvKSz{)^k<&u@MD*Z-wD{6{4G&r2ah@K3>`oIFaKP>sr6VAh9$ z|70dm+GGX5;^Asos#tFC{&DWOhIUIbNBt$3>fhxFP3g^lcT)WKrSQd?G@XLUg&4)j zYJjhq;3y|=6lJB$U+nVNC-(gK&c#x3{9! z>QjUe(X*&G_ti@XWnz|H1^+i#hkqdm6NDg2n^499rA??zkiHd}i19iqCL3NyX%pl- z8dfByfZbKaiNHO1!^ekSpzLav?7zzsVDjaEWT5^93IEDQQu9s7Q;-xfLUn}0S^XF# z6VNr)H$fUGI1J-KW;c>OYSDb5vjp-#!>T5Ue{RzM%QKh;if-A5u?Rx(ENHZxir^iJ zT$i<{Kq+q{%&H^6PLS|xYd~20&QkZ&OYUk3Yye-_KV(YpqlQK?M5R#E&s%R9Mb8jy zTgLvcKCi>cNr>XZc_s}C%5M4iq4Vc&K=2pkk$SI^ww^V~CV87|MJFq5r<8o?$Hj`} zgl>s*y=CfIgWrJT%8b2b%qwt#l^wLF;LCl_Q%KeF-b*c^A~6B)u&B__DV4IQnlU+r zrm}hml3sDKt`Nr!iiWr7a`ZV#$?|-|uD$6$o~-fjt)CvAo{#E_9Oq>7X~hJsqeTNp z^Rtc^SZ|m@b~dLRUZe8q{c-E$Pd6WV_Sr)o2dsSRP04#%JiN)UE9!a?8b1 zvEkb4+++eW&bI~D;P(FBX*!ml)U4^WsGUo|i5I51d*jN^8kLe(>}+v52%qbXQNadA zp-Y`hir|WfTTKR0lME^F8;#mS9^5MDWnu0xI~VoSFSb3jad5XX4zAU50wvf5omqBz z>e6$WUAkJ24@5Nu)&6J(3%+m5yuKW!X{S{n8^I{2|(;uOOUdvnsf|lax%=-TQ*3-Sx3G)ffsJ-(_)HUK=_vu zJAa;K!xsPOBV4#cTtfkjeOyS9f6zh;Yb&D<=-v_Vlw@F)Fg;lk)MwQNcOljPxPZfi zRP?%~pdfR4@8(YbGjT{WLPjFZH{Q`mL8?}HB2g{IfL)6HT1|F8wU3o^Wsoh56hpId zOLILKwKV|w&$F?Ta{Z{DkN7dB&>$d|R9yCBA?(Q0l^$;Oj&OUc1(b`iBE5RzoMH#@ zXkBnvXaM}r?caUwm+wPne0>?7(6BiraEeP$PgYuWcGm-@rPvxhC1^zWmp=daPyYr} zNH5`$w`qN6<_exRM_x}!5ZI9W?$cYR@@=?l_G|qUIX;2#hDTxp+%_!2$aZcX z(4X#~YSVgbNM~3s!TIC$pV;(&zZGX7{GOEhl<|ipnUf!J=uAn>Q2o#Z3jTeL+bD3YBf5^*1H?-4ajkK72BC;u?SVhdLbW z02W8@rmS{6Lq02Yt2z6ejst&DQ0k2k@`nqiXr4ch_Nu@8FckwfHCxamxR!olCeP)m zdqxza>fK@{u-me?CtbXR*63%U3c~+hZS_pjU8m#bS(m6|h(uaS2{mH}cPf^K{k_rW z`t1@Pw2l58u<_NQq!jxI&ua-r zA_HxXNszc}hyiU1D{T zpKY)TlOA*2H`bniX=5>!qqwO%PN%2N!$CQ4_+#+~c2eF8Z7xMJzV)7H3rUYHLF!2I zM#{*rtQ}}5#gGYFz0yn)kYAyrophDgtNLMEZ?*5n4=HK3U0SYih@W)qO+emjiLk>e zRc(+4Te-^}20AmI4)~-xo)6&5*_1c6$k2nHCaqI=(ZGlnahgSHS4o=2LS71v>NaB* z{#%GELQdA?qUXv=Q zxj?Wa2u`S7Sj|R2an+NWgQ2TdB=*JqE8)yPaELpZfa;xtXFgl`08;3ie@IIG|Az(V z^3_cOU6wBJecCVH3xJr>EfMxy!lyFP6F91POF?4pXd}rnMaJi8S>4Dor2qnz&e?Ao zcGp91e77~q-7IrB*=hYMT>607`u@z9Y?FqKG}GpQ<3L z?aA3!>0x3b?nM-}sLRp=p$V8i!F;jzE&W#{dK!kw?9*M#bbAIBCrT3({mN5Clws!Drtw0^NKNIl_ zmxx2p5m;wC^m9ygcYZl#+8@G_l6b1=$O;|GNkX4}U{_|{_}Vj#`75H5ToXTC&nId$ zo~5kS2og4)5g+rcpG0*vwn5{o^UQ17FO%8c_g>64!gz&rKT> zo`uYi!lGA$7-1i5rwwr691$Xqk>E|}P`$gQGVNj?hZS8n{X1`|8c2L$+@P|8 zIZm1;r{YJT5<11qqV(~7-4FSmy|Uc`pgoM;CZEx)_6U(@KrP*kYrc@F2F`PIl<#`B z)&QZ$x}LBBb`ItzYi`5x5Tbn)Fyf7t0~d>~XvGofy9384ulO^Hbm-z@+sjioeez$0 z>5Cf66FxxyZ|IYNFx8(^{^@!at}_CdJ*Ro}B^z`H=hw4C^k~!T)Pj+5pwb$t6nkyW zpa@wdD#rLN5v!;f$GT_5vWyJh{*@a1HwVzpr8aqEe_hF$ex=56i$tx*MnByEP+8rl zE>keElCQSEJRlL((>p{aUAj#w>0V@OSG?rr_y&`0lFA1WfVmArqm#pjLO3v3UM24P zQSw%+T5vNCUP=>WckP3o#OYbgOUJ4Jw2f9~(1Kc_C+-Ysd6tpd%90%P>F!dyi zvQ)tkkZ7nEFwg^ez|X6^$Z)a@9+rgi9uN16gvk_uWhT~2=Vx6I!kU}Az!hTsYAfu1vUS&64upVjPw6WFeSVI5m=g27LJE!#yz zgwiP7iDX>Yzj(z@C^a=EhGoW|KZvW5qLBw%)pg~Y2X&A5G*aJ4pI@`@ovq>%#ZbEk zqt2tj#OKl!Q{@6Zu(x$3v(_BYW-t1d;51o$pGs3uGqJx3^HFUWInsPRjhg-rJ41d-lxU)smSJ0cY-wKCr_=@@EbpU*`d?ojZ9%B^c=QxZxNPv z&7)yr9S)bVDp3sX91W;ZUgC6fI`B+qj|j6FXyo^`ihlu1hh-OAk+9n2ORf~w*?+d- zx18hC*)9WL(z)c+V`JDm{87%zD#YRM=5PrBV&rJGT+MmcTF(w?VB4wQmY5xo(s8(ZKta zm0V9xUAN~qFTYdUw>rhR{|0DZMGN0LQSUvRobN^X&9!H#G*PM+qzjz0T)m+dZ(HcQ zX>a*|FA)8|7yLy~|5pzGq%i*{%fZ>|uu?sM`kMFd$H)ULrb5GJYvdl&f4n2DW^4OF z;m6|Wg9+yj8dA1O$F=CYW0Vl@pt|4!McOX`$Ns)kSCp}RYC=~LVA>GG^k`l%h(%5x zbvBsAS#3SV)86kd6=u=;+d`LBN85>>F99>lZ1n^MFBXB0Ba=_?=3Tw??V2f}I2_m7 zqHaa`_R%_-({!48jiQoxn00RKGDo`Q)>8(AOyI-;ZIv=dp7n;$S;s}~C3>=94gt-x zOM*7ZHGH2^Kjyy!(tE7^F()Y_sc6ulx<;p`>pnhKnO0LWaIrMI=ZZOvtJ@*2N7iSy zm=xWN=2W3Pb;yl-i8%tLQfN2%oNSvg+gK?7#ZGpxM-HxE0GcF4=Ong1Y|7)YdsQ3; zYh;0cDn1cKKW-*OvoB|nuGBFrcek8xgV^OG7wXgUbw?5-NG)|wE6nQ+^X)a7%pfHF zGdABf7-&eCTil-L(^t*a^BCJ^EO@y*?*di0jRR+JFW5||oZHEpiu70)igZBIw)@LK zKgZ&<^4^JyNUI=$LQ)&M42b`Gqd46UOEka;-^HMzyEfi!;nv#v6kJa+vp!(T*9DO# z2}0!;L~EE8_H#FO8hZ{$ zf!lkDFRe_Cxr_{+1~hD(jkC$%*ATN3J6(RNdR#=sO%HjpvTE|Oji&`A1``Wex zc6GDW2N8#RIFEv&Jnp?wT?l-n!t)Erd2Aycd`Eznpduo;tjKno9;Cr&?R7*ZyFHn3 zVgWAKO;;BV!O`vDxiXk{$yC&qhkQi~Q>MHp37Dk|HrN3Zb-SgY#}`XW<{)p zom&L4N4h-yjC)CBFS+dl+a{7;rf2P`)B-bJ%g#`(A#rfYh>U5EMl1_dhsN~KsI4ua zqwmzkPS|q7>1F(Ngn_AOx7%<|5YAIW2k*SKK508DYJvQTx#RbPO#aSQ^;ZOK&FY3) z)(5-5dzY?=jC&m?&5Rwan`!idho+O3bvc3r7 zq$dj(44iEK1H9bT=6qG^(c>j^;C*9emn1n_7`@b$eQ@s+S$;z+N%m{`gQ`Y+`3Q@d zbx_Gx;pGQM%9_Er`EW{K5!pCl)_OXX>G5j3gb8-Gi!@)nre0;zec6ffm$iKUAc*cQ zOdmr$yRgv9Qo=g@HE+mD!yl`T&BVq*W(swJA(ij)Js@VA^^ChvnX=LYJ02I4EW26; z&*$}I$O>Wa%c5bKcQX}C^gXoF%{*0)!Jh#8A6|!*{RZ@mV(7aptJHntS2?2mR^|@Z zy08<3)N7Z31N0RQI7E7aMv5C0DbTAI<94>5gM~i1FL~Bh>y&~mwBWoE0r(3)v5%47 zl*A=DM*>CQ7zwC7d7oBesP7&5m9<0Z7B1(>N#32kQRch80l_^8KQby` z^$>UTwi_?cgAJz}bOW7lmD#$^BbaA+#<}pI_M1X4vlOP+HXz76TH0~VNu4L=wEF!s zq;d+}pWc%pwB(=a%!yIAdYrrS(}hIMlwEA)m-gh4Ly8?Zq-woCHprx~33?t1=m`(? z@H;x{@|9;WCsLTro-yf%$R|E_y-%X(X?Ert3Rh_aHfn8zVpZ*ttb zEJOBquzAvnKV#k4e(xr3KqgJd6eM@NUeCIb9`ASrPLB%IV2uxL;uBghv-?R!UVZKC zltF1xMOWnIc^p1X7CtIDtyz}j7gS~&g$b@`JkV&`m{Sjh>v9Sx;_I^eqJR~qZ%W16 zY87hB8)J7XMGU`;PhTMqJ?bwLSo79HvEQpytEC>?+<$OQpbWLM%cW4RjmQ&j(kH93 zKLkd_Db{Y;yW;**)5dkR*8>mK8j?mo_qLT)nmosHY#hd^o=FCk>)wbrI<|b**+E`x zZWX++D*CZjTIMvSo{?jkj-V%*i`QePU+N-f4AvRHMk|iASEj#QUSH*jv94DduVdg1 z=Qxonb!4lnEYfn~X}VDfPC=dxA#A5I+Vi>&(Z|)!YOT*>@WVTg)XL_ki^Pmiy6swW z;JSzY?G;{`?Q^@fjS9>TDTT=SC3dJWg8sjA0#+OHnkAA_n~c zT>R(6j~M94U%U#U@XPX1X)X7Dx$3`USf;5PA`)biVuv$a0U>wbsO~cX&Uq&9@@LG( z0QH`C*`4>ytzj?FCq-NKJ}8JW+Q&s1K(V0okx>;`FEvIy{XKGkI6Y%U3Wn51oB|5z z^GDxjX9m*p_vuW%92%XVTMKb%X)tj*Ef*{Q5Fju&Ff_`Hewnbsk^<-BIS1d>1%EAP zUS4YixX*tLDrIu8k0q-0)lU2@cwSGT+bqp?DQ#LlnS3mD>}y-`{wy;;uIOUMjm>(G z`9{W?q45GsZ+VkTF@Jt^FD{Ljny~bw zc~7ztAdvQjp?dl>aZ=3|<(P`C z{X^-1w!_N^#PBC_up5PmjU046~A`b zw`y9t<2(zqQ1toWxUZ+QHaK$54u+PXdbr8U-?HGQRJE7AGlR3IyT78!UMmlO7lf80 zPO6SP^^>*T-%Ni4+HbxuFnzPHwzJWIKQ^XhlkDKd@w!H1#8p?tQLEfzp;VJ|64#>I z{&K!r{JU+lt=@1a+ZT^>18FO#pWl8p)cizGf7?6~h1Lh2krT-kUM9#-?$Vq+ae5aR zsx2J0)Y{slqSJ5F&A@N>r7|f5vB25GhL+mBl2&G8@#97mf85N858V2w^0G$jw17{c zU8spDy+UcpGY`X#yVUiZGIv6^d@IW|@O>~gG0{boDb;J1c#eDjRI9Gn~1qm ze$DlcW>HxC@?pJ3?N`V`y%+!03D}Ku82L#M<%*?6+AwNMr z)C+Wj%^dJ=jb>C151aMk3@(knbu`V@h8HHv?6+y}6DU$f=q1BhS_PFq$G#`b0kU^9 zX3>uGR1s*bn-$`{i#a5i)gx+)m8?K@@27dn};ky`H6L>#Qy;t`3 zb?$k@EiahNZ4WbXg6}f=$IQIcZfNKN2v#*E+-<5(PLnj1A)o%4eZayeXR068bVG;Uw8JQk)i=wMb9_^cO*HfIqPs&;&&#H8JK+SwF z9b-vDW>>=4KD?~eEKODn4kT9=Ld>})jrZV5$`DzZ2&?s2m@k`#Rb0&DhswHkwaoo?|`fatxOGGGg+yh_()NWva<)KPRnzC*e>r8CR;kKA%5V z`kcvKn4hi&`m$RjP^^lHpT{Ff2+Wj-38f$X_Mb@A{8ul*Y4cA+;VMdbf2b<@4RC<2 zcwz}(U#kBGaAU9D^cLLL*?oMP(0QBsi~9mc_Q@6ZvDqHQ$N%a>{y$v*Z&y`NCH6D z9FNz0=HFME=hQ3gzH?jVzx2tKb(bo?2ZP{6%C>6Qq_>9Ibf=2GNmX@Y>|4ov5SBfo zlk!r6XKR9|o{{Ro^F3emy%|FtAovr(%D45BSWjju*ArJOIE@Gm$9f5tMA`l;uq1dGTGA`9| zLX4P`m{;xpVec)Y+UnYUZ+h1#R@|kyLvbt6;(_4qk^~JD5}X#=Qrr`q0L3A}-AaoE z2v*!_ai;|eE!O+w*>;|N&O64t$2jABIb-u7%r&wW%#k(cwKC`RzkdI36*+kuyk`r2 zX8Z7u%_da?Pfxs49nE2}MGk#Er1d%A zLEv7AnJJT=w2s+o476Uy`U>y00odGlY1Osq_+XG>D*Xt6NIf0pDbakC0S-p~nPe6*!0}Db8|7RTK8?)6(lH(^ANESwz0C_=L~KSfV6JR7)cV-6`m8Y-U^kYI$rSb zfK6bwbv{5^oHF%_Wo(Xt97<`X(PWOw1<}7Mn{ucpTc2G^@O7$Vae(k@)M7mmf zqiiST#h24Wb(WqO26%;ZP<#@!N1Lr}ZV3tN*BlDI0B|`&Ct#J4HWV~mh+(dzR7Rd= z8|qdyoHJ`=&4O0}_u;7D%HF8g9halBN6^Qem{uf@nu%lntl&;qBmrNs(&9i_=7^v^bM(4XNHtzf$B?Mq_c>aSioKUWK1J-u$Km zst0W;DZDvaUp*d{C{8=C#(G#ujn4eFznG`tbNaBU#xz%>(Ts9jjE(m96#A)9`SAzxzq`(qLjRf_cvfwP*Q9;G7wysXf3cbmo5UY3 zzxH?|f)Czg;!YbYV^8^*pY;TFQ*DWz%zdVg9=j;5A#^TN}%3|yk>s}eLPEIbDf1%Z)te6BN7yLReY(8Dct&- zK=Wqpw}iRtX%@G4o6pgiM55!LzjoxrbpN53> z7!$+a1bPxj8<%)#NZ9UYyfo#my%$sI=fyMjYN^OJzj~n`6r)^69{i3;m>1@h2waX0 zX#nFC6nVIrrPtt86eN2-A#fmR%TqxyW{uk09GP)H85`)ac25U&>9si5SWw3sln?Pf zl424HJl98lt5D*6C_*x!3XythB-`UWW~?L@e?~fZ@BKa9h|>gt;%vhVc~op|!c2g8 z4l%VtKL_InW0nAMu-A#|Q?D38IJ%*dD;vTe4+q^J^NC&mjhsdJcJv=mDw3Eos;DEk zWdo?U-L8Pk%?|x%gctQh8^XMswmdjezd7}X?ko~~?e}rHcn-59p zgpKDY$2OA9aJgL~&Q_$I6+bjYw+~1+z9hRMRyZteY4l<%$6iS)nT2Bf>xt;xnyhmchHOd9O<_C*p=1C`5@Mf${ zB5!%L7?BX6!=ca`2vf>SmY>+=Qs+pFaFCx&lD79a!0eIAGj3+7dZJatol-+eGi<(x2~(37VqnLT7a-MuMGdNLKvO zIpvNTA_y^B;2wIy{0TIZL{oj~{6ML$3c4g1?N03%BX#z^3<@*`Px$6{fi>ncLsM1A zXe=uA+K6YqDDwb;K4BsOM1N2W&`DF7qMm%+G%PK8)+^X_knfE5sH0x zY?Y92eCU^e$+Yk^7=jxs_D13h31?LRRCx7W*FbFvP0fL}1;mN{>$oLD3JUcq_&1)9 zos{1c2rIXLKI~Vz>(GUoqe;^<@VcqVGpOn6hXrT=?V`SzaZb24^yd|#!tgraXH!9h^SdNO!@v#aRNe{(4I1FKj70W zPNZTUH{>sx;&eHwQx5I!jT{Mjfswe69Anx@IPXLTF+?4~z8x-W5}~#t*W;!j z^N648FgGX8{laYt4qVZsP^F*~Qgk<6&qb9_M`)E!y2-s5vMtS9c|-8P`P;ugEbzZ& zW!mz?l?^t%U*g3tp&uSrDm(R_kx%?hQ0bYps5jVg3gP-!ivFRXoce+|9!!Ke@g#7H2za^Gqyus+mP~$7OTGro~*T~&xXi+_?w`V<7@u-{z0gI{^)iX zTh5rM0?GNqP@UU#+Y|i>0V(g*q|?{T>H?X;U#{$er3wU09dnpsP;le&BH8WtMSLrQ zSZy7Rto7SRMdpuo2Y4J$Owf*)sjHWAsuYh5t4MAN;!yCZe?3`tJGK+TAXu8E@mE8+ z!m0gt%I{GI0h^yzxxca|9t5u^ndY(Z{mO{>-`^&#I*4Uhn@kD{Omz^H5C6pd=S1YU z-@kVANBe4P!T(zqg65x}KAdK`6#W$wFi_KNOg*)IICX8e*PNp-l^r52`Rkbf-#v!O zHS<$sKEEv2>z8n)ZD#wK@#z{zMkGu;cVlUE#haJ*-sp8fG3dyhX~ ze-hQX`6Z?;`NEGGfG)CsfT^f5G5o?QwGA6K@h2a8=NEV>D>2swR$VF=mT+8ZbNy;0 zOspXZdXqnVgV%p{w#{OG(JhACFWAi3wP)s}B-#s?q}9~DIG~?XAM(bS<_euu(80dS z4cQb=k%_Muv9z}dz8_2RCtHxt^>339FMZuCWNt`#dJ56h6ft&9SI)R8LKXveShppR zA(^cHhwgGxWxZkaTEGG#!yM4)xd`xR40hTImL$DgRM zqAty!8x*+nHveV;S)2RvPkS9}(+Z+0Vrnc$XDS2`5rFnU3jy=@)NqqNV_0^)4e#1V zfuD6SB|*}mK>5}HoL}vC7Na!NJnwFS>UaT-Jl7SID#ql|YE#`^RN-7fJ7l)0)}!ts z8&yd8Ws-k$uvI4&%~c>VYKdvy!P2^)v}fYHn1qbVvrdk|ZTtyw7n~DH3T94}q}q9DwwWSP%_I*lD9!dfaISUwOpnd0R+)XIjZrxX8Z$i)F;N zSOY3WQzzFhihc}^pdeu)K-OaF9V%n+hFo^4n zNgaLcOz~n5$d>z&N-0??cz+C0LZUAhRiSP0(VhAxs)TJ)Pw?U3Z>b@l2r`R4(^Mac zTyCp$)&xHF_C}DQJS;F~RKUG3Uxv|dy6{M^=$NMn_ICcuBTEnWiZS+)@a8y&_raq$ zb^fu@b+=p<-%R#sj2|Jq6s;+u^$MQ!PIsXu0*MX6*T@g!6?MZ$e-kYHNhxP$QynUK zri$vQ-kHnaz_ddro zK)k^v5Z`TnpFi=`^H}A74Chz8lgEJWQ?}w)6+?kUlQj`N=JEVEPi=2_kzVDl9)O#F zZC?)$MZoxe7t|NGSNo}%=F{1-yTg52#%E|E7Ko-wv*?FJ=y^418}zA=wh*qnO(g^l zfK7GqGOD6G%Fv^<=3LWv(2-7~eW-P~L>@oa3 zQ`WQ8gsD9maasRfvKC+{wXRe&cm^0->IGzwA~a5($5)Ry^h=A|oZ?GNlWKC7g^o*+3dXQ@S}A3WDYI+IFZDuxD?S>-M`*1?po=6z8qRhoLO=qe(D^n zXa7`=YXsqrUfv0;sIUGLlJ|{39yvR9fLH=OXQxW=UMr6Gixe2JDKXpx6%}!oxf63# z*?m=X@Z;D%z4dQN z((TAhWMt;hM$Ak-hU|~*QRa{!DfvXeJ&DLYiXvE6kDPI%3VF^y{LH~hnP<~F<*1~fY+1ewMGkp#F^)O6=ur+fUKh5_q5Gw$05gw&xEXxKuhcX=Htxd$OWE1o_D_8|v)jIbo-xs|V<=Hm?^o z)8#V4p?MbS?+dum-5?%ifkZZk+pLC5DVk*0itM=oA;c`siguz6b{19&ugMdMdBdL_ zcv=dr;Ia%OS-8FynRE{Ds;hn*O6?yTdUN;D%!cecm0&sRh$0K+suXoN-y~6o(wHT< zeg(>5=4Crrxxkq=YapLx%DKUprm{gV7Kd`X&zK>{(wq2c#p?DGz_UW#zEIfE?JQa+ z^kZ_fC9z2NkTDj9DF-s2PVCc{;w%7=T4akbXU;}rErIWa#!?ug3CS2ihWT1aA$v{Z@fu?v6>$X_ZCYFH3N~iaE+uBQr z*~)Ctb%^aUc}Q&%-#ZCfucg!$4OWd+oYpR6t+4C~LOk%*+@ln&S+Cgk8ph;F(Ewil zRc8(if?08Nzg5_In?lNILAgSwSXJIgMO{;ma}g^@y@I^2?Hk0z5UaDcW+zl53bfM= zCwPGJ+Fu`oio1Y1g;@hLgdKy$!FNuuo$vqS?mp_`9&GLs(${FL_ zKG=DY*yJJf=(D{?x- z_?7XM8X*Pg=Z}&JSincws#X3OBwx;|pRDtpqZ^B%ooR*D`x`omU0P2MU^*60tWYIB zS>^}%^{Fspo|SI%fIOHmD_@>$c2cZoDKdC%qwOd_xbOV+KL3EpEL!y$B-5JF-`I#l zmmgmi{8ZntN4E%&UbjO0p2ju&nE+y_Y-wVOqgdRXJeKDbxQu-`Jh^|o-I#iQNEdJ1 zhzF!>aGu9Iw07&@U;gRVV8xE`02ciHS;P`Bs#l~J>-w+@%akzGzN}_+cTVvylbTO+ zJP;9%%0Hl}n(~N*TC>`u&_BPll>|aL;x-cyqO-8-?~l8wbUDt9*{MWjvD?Wd>_G$4 zKsbyJPjT_XbBqq`+4VSk?q6H|P zIP8m=iSjhjvgNVDMl`d67nt4v)@U)K&jRyd{Ltq-4~wMB9tL zl2y2bQ?rG}U&Hw;g+qhsCYQas2R{?2{T8NIzb3mtXc9lOW~aNL!-9I=UnNo)BN+n` zI0Y>Ae@$rs)vF~Qos@8D>z(>ZVt&hMTzPp^YABi2qC!%o?=Ed_g^A>;Sd@`mIX>cj zyuo3J>;uQT>VRX!l(=(B(@#1Rl9F|M^s}UVHK8`xww_&{QnRE$8=56_l6=^b5j{x&O zLS)c?E>hC^7~E83TV`o8o=xG2D`lA&7ebTi0*rH0H;6S14H5%505Fr z>&U!{IeGTYd&ZLFq_ib%H5p?QY+@_8=Z6#QR2gkSQZiYxav1w4xj#wr&GksJk)lIYGTT)<* zD^t$k-6OV3?7qAlZ$ZNA@&ZI#hKGlb;(j)n)hW()z&ajEM3sv{&nWAFV4|Z&P?X+A zGR)jn$aKPK?_hw`$oK&VvAlO7gg@Pwrz`nA@V%}$jpP)+RyJ6KP=!1b??Xu4+O2Jr zL64sl{~D_3Ki9qc{ES;02`l53F>O;}U~U^ScGH$$FizmkIMA^|0=Q}fZ{h~wo9J^i zu8OF0N9k;$U_{&IR$JU4l?ss{FVc%+Ifv%A_a_$*Z9i+?2%)dHYi`b1 z@Luo`^RH52JDJ}tsSvLj0_@+?SJ-_d_0VJG7CUPB*cR<`0*~Zn*Rnpa;1Bn1l{c_|Jk==j;HWK{D9 zA;buQL{osujvW74e28~;$g|6nOc~*nQ5&ZVIx^-z{X4YXR6f2$!wKz(8Y)%8Tw| zl8I-W5yVp){CrN}2Po#l`^*afK|x|W4^|H*D`d=^&q>^J@ft5pRFZqD$DYP`!IDMv zyP870&&ZN-i-hWifJ|UqWrL=!S$oy^Ows&>(qkV)nc-gA-f39wOw020bIR*})-XGL zj8%Q{D8FP@ZCHG5=%jA;$mSx~IWqFQr9cB@%C>%}+?c1J4#mWP&*W~%ML$}q?JmvD zcB`t`o%{Bm#|3NiLxc~$IJ)MBb*|l}=k-qAcj_c%j+bw~8stX*NAkMOo7npCmCo1e zq$(fZ|JaQAM*N|%=EnTUWV|hc#O9R%^&aSNf|&k!>#s#=kH$nrNbFxS40QYh2c%ee z>T>&pMEE2Ai-s9F0CtuyJ3J?ceLc;vk&Un%5Iw0y;~@G!8QNa2+P#?DI6zgeR`ocwMgyvRZ&E$*f>08y3M zlNi+n3chp1=Nyr8>@>TBg+$=eZfYJPD0P`R_W+)UY@i%fTi}>KY~-XMz}FqiD;S=; zfODe2;piB&-m5+on`DPU#G46AVR=-W%5xt{e)c}x9pJp>eH@)YX4s=cVJ7+VcAJw* z`m%M?YP!gbc}6~QjN%=lqW=5o_cEo3V2@$t$xx1k6zOu+vF4E zy|F5tn=7&=WyX7Htc+Ewf_awgd@}|~S{dm6#E7TI1uk(uRMHa*gD2>%aUSF6{Wy;n zlQx`Qwj5Tg!CZ@eS5_yx&{<3uC+gePUW83Kq7ug``-ZFKP|5vGAh725G}e|?Ir-9H zUPQ4I{;|>pxj1+n5-q}l37KW7(XKb}-%YgZucxVIW+G%>-U4wU*e4CAVGCPwViDdL z?z36)?pnwfpZ7oW9|#y68D8Wxy_1GN^&-095#WEw4K04h+nxO^w404{yO0^mMPrtIu<57sR`yz(xYN?2#MC`?$6YxQKl5-zz5>A6BPZN6 z6!T6ON4~5WV5E|uT|G0goq6Ijn6h3nz{9MCNz8jO&=T*G;u0(5t*zsgh1vy?Z+#`OR%}&nfm_+V_GcItyh@38pj&)WJjsh{a z;dhAeMSA$Yf!M}T8+Y^ZSin{(Yd^i^Qa{D+Aa}_X#o|XC&_pV$7mb&do`REFqc!(T z&uD{dKQ@8e9e^bcQBCPn~gliE(sBtrprDjJK>l432KZFnRF0xqNJYWuz zZ9LBn=miXUl{xl$1Pqhpj1ITt6t3pRYr%(mG<&9}=R8^p?QO9dV+&ws%&c)s-4|%G z*pQ`VzodsKQrB_4o}}cA;t}Ocd!jKSvv<&n^n)NnU;cA%8N9{baS~FdlG`c(Hn9V@k&R?=!o=md7I4J&+I_b&8hm@pt!YOGb z1=~QS2VlQD@?NN&rJ|ba3^|yT^+8^lQ+*BTA!OTTcjankS&Y`myO7;x``Gede9fku zik5zMUEoG7ruDlu*>9aboJUL}MkihO=gK9|($PE@VmU4;@Gp<){m=R#+aH*eP*$f^ z;Z#svfzhAE?%t$d{2{iyUki`dXAfi`2^jUbmFlhNC4PnyO_ zZRA3nyexLXDS?f_hBd)_3$qiV4OX%&ec*ol{mh}q-#Ps4gdctDniK7~0nxENs!Ixx zCBdux;HV9HEPfbXTVTcTsKq}(2RGAoY1l>!YhAm;$(ICHch~)!d-rnQS=jYhlvP)w zs3)t;yyD@g1-=uRwsX!I`Q15wijl0=;h)PaIzr&i#=L60*Vph=%WP_+q-2Dj+z#w} z2$RELuXMhjZdgEmnl56t5sGX|rFnHMM=L5AQ!%Rr4pIC3GEBA;&t{Q>C;E%wUGp;V zGZfz;=C*;B|}-P+8d6w|uBkE><@X%d*?Df0ypG82r|NUBMv zv!{gy`@3Vxa8AA~KKY3>(3A6CIvlU-;y#Yf@i3#d;pMz24XfqCP$v4Q?PF}$io7~M z>XUK(tmg!DoQ{-MXL4HZFamjgXpKj9;Zaqx<9JL}23{}KayO;W z3MX~S_%{JqMc!;&*)E8Qc8q+lDX*>TO`1(2Q9mK>?a>mMT;T4`V3A{Rok6V6tj)Rk zQLzQr$i&HRA3Kco*gjd~eZWo(S(PX@<0MJC)HK(n1}pBQuG!Xg!+B zGcJc=3BC5`BeXN}4VI->b2NYhYR@cx?Ce1!mj6B!*LfsB?9e%#Z!Qht!F$Wc#D!2P7uRgMouy_oHDnP|SXDITg;ZmHQqDwpS09gIV47ww?QQJU`@HOy7OiM2 z#&h(Ci>xFeKAuP}^|b-ueYT<07K_wg8L(hUTLR(TVdhr+mMEYVF?6h;LRb8OUeE$g z#UAjwX3@{8Y9E+l)EbxE7jo%=eary#y?ulQYkMVs9>Uc-K&FId?WIjh?mB;V+v0KF zgR5GYeNT@}~0`4iuO#e!U)_p`Ug3MO74Z|Vui21CPoX8QB6DiGhsogJPUnWz2zRn`0WhQDF4Rn$T4HthBYu0^B32SKCSt)tQ5=Tmg#- z%~)P&z^N!88Tn?ylDQyRXgs2lYTo@Bu`C~LpzHDLwPMEl&^-@Q^tcB+L!e%@7)Mq` zc}8aW0lJYp!+;J4@+0}*KP?yo-4PjzwaIuO=7Zef#uJG_JkmrUa0&KxgUfyX1QaOa z*<_B=wnWMmBWqR>uTaYV!qNg*yL2)K=_yn?us|x`$hAGSc(w8_+3>_a^EZ0`Jc{*B zeQ)MPzy4E+;YK-1i8R{&bu8H4eMR+2H8T(HYondslU2V+?D9RUuH%9_TeJH8&Ze~6 zc3UaI%5VAdZY%<`ipACDxw<)KeYPZTI|$_WW`-m+mZ~$nTzlG-w&&yH{e$5SldCN6 z*xF-tml)TS#ZMoLRd6j-SQ`PSB)nYfUK(< zEu~tvclSp>O;w@8UcemGN?JhpPj`R_&dceA#YNSZrp5ssosRO$;bGl$srJ)jYYNRO znuwP!AlXxN_{L`q$!spwi_e~6BKXV!ei4%7A-i>PlBZ_}{F^|g3FY!_oNv>$EB9HE zaQ!g9j#Y`}av{o^7S3+Bq@~vCFvcoRTDYrJ?}f3k#ttV}n!l7$paL06u+Y!X#iy9@ zOUsRI*I%%tr}3XHi5nOOuc{oQXtZJ44Z@yjeP2Pj6{Up`cD5}-U!v*q0a_=i@Uqzl z80~VjaPls}*au1P*@t5;=<_u4)yb0yGus5(t5O}nhZ^D92zz^rbt`}crh_G>@??VF zl58tAwJHn1%*h@}W-Vu!XDf@az(_~m>VxuYafhx9k+gp~eVDM#chiG0mau~&UB{FL zkE@{1)NZPc?EpZf&~3MC4Shy2Bg38%@7B972Oj*A_~Eio&$wp-cpnCtEv_*G@I}Y) zJIkL(uGaQhY};r(1FtabG`C^&ppgmD3Z~Xi!JR1|wAMCIS#d3y?rJXW?UrsUZfY5D z;jA4P4dKJbrLdSKR={Q9lH*wuQ@Pq$EV{D?YbjBh6U!w zeA{|hH~-1C{<;%3JL$6^wns>H1PgS>YxbEwn`sec8-@>AI%&VfR*3#BHK=tJsBa7=L`yttTRmW${ z&+W@Lou{H-R-PUJ4vuQ>)&Tb=s!mR2`UNi3sh%lPepF4Y(ZC4Ddvbod;5y!1?~2?B z0Zajy8Vf6$a)?4OUY46*-qwt&4lfBTibT&D&YX`-PyQOr^mOxY0i110-4o9Ctzpxh zK3ST5u1N7_YL&QIcgOu;ShxNl$RmKGh3{gf!HMQQ^9qdJ)1!uG6O$f!(u8Z^l_05V zFf+9nY>3?#PjjJZGZR^9ex(j?eNK>QV$AOG)Pp)gpvGx_P{H^+`4{)Cv1gB4%swXiy2R3-qz?`G%{ccENme;Y~ z0X&2rErsqwqEp>{fef4?Bg&p0o1pQxYEn zk;+t8T+(p z_r%YKgb&_eLpZVv2N(gd&h=kq0vn}Z)}?+c!D%+U@2>Dt-f*oT5XwW&KjL+U;biur z-(-_kWc{4^NnE4Ped~1F$})mkLQXR?&iy~@`@wy?czEcyD6J4$-m*0`8-dmSwIGZwMlcID6d(?BL173;Hy=L@)&l-V(64ISmvnM=nB)-$}e z6}I~cOVzuiOLr=2eq(2&?NIxzvA0(rn9XH@?+n!(z9SDN^5&3>Gl#tkW_=MkW;DL}%}$3y zoMMEXl80*X1o8xrthz{)i~_o<)IY{6L@TK}J@^FjCOqWnWS-ii zcbTNCcjj`Lk_g=d6Z_-pOhm-5PMJbxq!Qr1MtIWTOfC5m2d46?2WWK%?D=S<{Lhe6 zCq@0~IeO$h^}Sc4mL7nE-N6s%tBWsn%cr#VNh6w8`f8M8)jbb8WFL$n92##voiM)W zPPy7c$A>|IZH|mC`-N@vDMPso?Y}h(WEmVDB=j76wrMZ>+WNIL|029_$$0Qx*JF-) zZrDS(qOvZwxIbOmUqs}j#k1bp3GMQ9K0Fj7AHf4JKyY_gx;Yj>II;%!Ug;=)O&L;m zrx)}p&ugBJsgqLt73}UpOBzxqbq2_kPCBC5WlWz%>!ag#FXuDFe58l2?w3eQOqkis zZgNn0zkB0FNY=#pJ9S;9?tX*+>(A{-&|WU@N9KdkaK##_gg+^el-cv_YuDR`dVxC1 zoR_W~6~4#Ap~v!{KM$lBg*LtTRKc1GEu^^b*KnicIjnK2eNtzb5S2KoUr8S|A^HQS zE~tClaVLu6f;69Jt1mmA*syh+#>T@;cn$Pb7TwPYeRX;50R{2Bk^YaGmw$OKPz=Ms zQqDdg@>0`O>%9lD;MSp~k4_8by3EDUjnvo=Yh2U7txddCKI9duP=oX0>zUc;pPCUo z!wVHdFmxOJH$k?2G2wYv|J5J?L0|I!@~rv)J3OdaaJ>2a>(c>a(#1yAL#OcSPmrjy zyLhZqrMvP{Vk1Ei<-hHkd)vd=9w-&y|H=RLpT7vlPq}{+pA*$-{ad5++L0Z@yM$c_j^PbE-(`Ky^ zZJjJCz@S2?^OhL-aLKfFA^+%@oN@OVit3@X7!vN2=#Sqh%0T`_^ZWd8p+R3c28nPV z3PxDDH|cw>TzK8mloP6!!+5et3S;Su*$Hj%P8|*JQs49Y;H}v3zHAwI)z?3^9^fg< zR#FSZADJkqXg#W&L2eaQE+RU!9;AycJuQmv*v6`tL`uoFYM3L-nTa3o2efLELsGRs zSq)8$WmCiEdX{otCpi(>&s^K}vfH5*U%VHF{fN+HOqD6Ig@POvM1|U=PPtME81S*_6Xr~O$0?^iEvMsay`Y!JRnEk5Ti{p!d%`((a}c-34)0tfD?P9i z>4BZSIz>a|-5o_$OmmxqD>TLAfelcOOK|+#DLL&(-SpkHwO6&4mHq?2(;D@G7Mnlb zdHS%+ah;kTGjSrbcz(k!A+tS?7}QC!EtCjV`QX&8L!+p9td3gxW`gia%g2^J0s_Q8 z&))wkqx63k*~NWm!XvheKmJY7dNoI2i}zr_o8erCClgqidSU!v9dH34!ZNa98*-a}698~FVZ0(?>EDzd zdZpY>Zd|oJXUl7y8Z-DUZv_98^XH`k-VNr>Z+?z+Iqeu=+wBMp#Kj|VgWAZUALqT& zG6r4B`P}4pefZixa2|G}IiE1e+iDYL{^YN>Jo~NYW!_%KphdjcaXq=~JG)Pw9e#ng z^b4M+4_(;YqO-v(Pk0o&@nP-m9bG7sY6cxYVTg27;mlq-vq=nRZFUz-hbDsXLNuTJ zRBO=#cu==j^F*sT3rAJ6(&oCi^E{Rtfd1wzD0ElQ)IYe6D}aB%qG0pGCfTS8$6+PJ zvaRt{OgBOPn`WM1Z~%D)lO(* zS7_c(5($`#OC*$#3w3beX0Gdntv$qdPe>&MFkFoI#qjG-g7GD`8oXv72TLQWf;rt> z=K}{Qj54?uvo_p+2JfH%-E*PKt%i-r(kchT?i2jdih<)JJ>`Hrmexqu(waGSE6aCd zETb$>vRcM4*I*`h`jqTppCT+!n~F0cokhQHnk@fmzp4T zy2n?XU?rX&ewoTQP7_l(Ak>z`I2tS2>^&~Qv4R`fWYyOIc3P4kv)jn>NOM$gw@8L) zjSrsO<_0k~$z`_n z@xX_>i%KL_3F)(#-k^vQr|qwL*_Pom+~9Asu|5wv6M|LPXM{E*j3{|201cJmBW?`5 z(PvF!sdAXo#-Ea#hmHFef+5Db7HDbn2_ZJRcS-E zBoa-&h*J)290uo2WCYcR?S^;fcM0z=ct|+FO)NVOU;m}Zgx)26m7nxK@|Fr zc#0xwSF%a7(t67TDcbbvv!r>$*ZkA&WTO*!O4F>$xXqEyg|$DaO<*5wuOA-8?p+gs ztF?!w#=SZ^2`TJJwwr#@gZ@+#l6Bdxiro%>qEHYFaiJ&I}?R^ zxFlLUt_~H(RO1}uP*Zqxd*Gj6OA8(q_nFrN3r0YscBbUu4mZG3K?fW-r*LoUQ9GP~IfizUe&oy^5@{$-s>3=|4@h^bL=W+$*p$}U zY!ExGVRrH@cYjRP>r*BB-dcu3)&b4Byrf@pk?w*VyZ53V}5Amd;!lQH@=~Htpk%1S$Z1Eb^9?d5cMP^(466r31?KqK+ z0WVWylyxJ-v)JZ5{d4TGiJ$AU7polAM#KJ>kx6~Y zLNx-Cmr~V~>5ZS7nwyse$H5NYzS*|aC?z=0_~=5gCaRu#Rcg?49K2#`23Zc#CiptD9w z<$=g-8~x{ZYA)Bt3In5lU@S$qA>8I~(5)|;Dn^W<^;Kp|W`$`I7QUcKUvTovtoxRO ze)CP6HXZVnz=iI``25F!xS0IZCZTN&uK~VyKrvCtb`cv5)nXHI3t5w#X4gppeseyj z7wf`IJdLSuYxe;G?oXyLBhT-UN0g|VLur}riP&jwPl9bnRX()&5Ujfi|^BX{Y(FSmL(Y-M9hX2-T>&3kg)urx!@39$R_R_yE6y|f}PI;15;UDj#b zoEs&PH8?GYt-um@NGA?9(;P>muI*YbQ+M*Lf0;K@R<~!%s8G<{$T8jZs1nq_$uM5# z%2io;8|&p5wCTu&iT)HOEGBTJqVpjlBgJ8*=$8n+G++$4g4Qm67Xv0Buy*DE?W}V< zl;oVZR+P_8hNr@tv_Knb_T-(efqMP@ z-7j9Tt{i$k2_pbX8p+`?B_JHGWdlON>G5A4@D!jO&O-tc?(3+21UI2{i^t6?_ool5 zvuMR>rqgVZrF&%}=p>WMswbMpW6?pD5OV(*+EhzTKCY$vFC@V~f@j4UtPp5ji|tNA z4NJx=IN3&GoZUwXlR&x+mio#GaUX7vt8jxfenq?PZgE z0Xj`hkWZ(}yHGCEq#n(nNoHW@q^DxA;A-i&O}&4D(SmeZ2l0w$sUw}MR)l_K!Yf1x z9?)asDr8}RtSpb*ci-N&9ljJdtDqK|crawjk9_;=Md8}FEdGc&!SdG3$P~mQAz!YA zkD7|~NKO@lNs|S+kchm`ANhjp(1Gnm(6-tCsjBnOPIV(j))qqp9y3H-Lhj|qQ;GWKB%eBmw? zr7?C5f?cJ*KD^A(oy8HJaep9q_rgOP;un`_=Iol5NjmX%+qGY8ywE_8on)4pId}XiWyEM z6Ay|k_qzwgNyz`^oRsz|Bv~k5LGu7iS+<}I{GY$AZikZGK7VZ6^`B*(EPmHiUo%;w zZ$Vz6%l;OJ2LHS?z_&r8HP1a8&inVH(WCV3%|^#G7JDs*Lqm*rR?rQC)+4?CSkv|> zLo_7@qL%DGVJ)Yfy2=L` zV-nLdW4|);*|vT*#q$l<$uHNLNbVQEX$`$0H@C??Jx$@Y#e52q#CBsii#;ffrHDwz ze9w+VB?P7H9NlZ$+XhALYpu2lnK4pi#eRvlj{;nf-V?y~)o%ojp29^3!P@-S^p76N zjJXt*KeD9t^__I&lzK6Hu4}XSc}!S5pCXAz(4_s;sKRlh9$gHFrUAi`;#tK61Qb^p zml#Vtz0-$Nd^fXBNHq9%`gLN3jIB_DzuII?ksiP@`ZCaES3zAP^Jw7V zSnh<3pVXg)%nWahOZxy`P7sY*)) zGCbi)lU_}^u?vvQxeBk>A#{GT?V%WFWc&=1h*JSHrucub_ts%;ZQJ^ADo6!d+-Y%w z77JclJV2mGfZ!zr4Jq!>7IzD7E$$jzi#rr|THF#!i?pS(zwCQt?|q(g&$;*9`};lL zdA=`y@Qh@!<{Fu6&bemBc;9!JH{b?sADom~q}cb`Xs;MGS(f)M6yz-?J~y-O5|_bt zqa-y^#k0${7U+i3Z7zQ{-|VV2Q;*)Nu2enb;>P2 zUmG!7gG4XK=8P|0yrQIWh$0@oxpSCH`_9IJ_Q%Vclhk^ra;ov(wthHTI_0mH`TUFF zGbv`wI5bU+I{BWZ-j2I|&K%v$So*>yzedt5v1ac)zmPHg%=1e9_z<-QiOq)|6_+)a zkh4gk=>&a3Zq^In?$nom?C1l6fCjgvHXE~J>1^ii62?QH`L`v)C6hzb{PwF)mQ@yF zlhdDxt2U2=f;F29uaQjjpK>guhz?MKmBE9Vf?_0}YhMzfg`tI+nv3`37D|rVOeR(~ z5(}FU@Tu4aGd`!!OyCXE`Z2Mc{x%vn5h`M@%S^KcPqfrz)-4vTZ;BIC=gbj&-b#P7 z$uG25DLT_<*_{(OH4dxJ1%19fTVe_^Ul5UbwD6QB%iX8}%IL6WTfpR_>HO`>NOE@a z2M2-OQJaX~kXc{HV1#*4vNbM{djW2S@w1VJZisqj8a|9z?%cg|BfovQ`%H80^8>3_ z%BBnN1)8e3r|>9mLV`7M-+_H!MCKfIM?!wu?4qtgWH5%o;JAna+ET*5mQI`r+`xiH zdh{UP#KCYPViEm^C2lJ)3QZU)@Sf)d=ul_xHGRo9tj-X-XQsI+kkzy{{(sVsGpRCsIODV3`ial08ThZHckb6a z+&Da6?@%W2rc1{8QR7iTmm6<>W%aXgSheLEXO>!B>2%q?+9;xJel2=Vc!1ibsNVW; z{>jPt5}uIBNbzrZh~#?Sam-2It<=@OiJ%ZzPf4<#Udy z5g;aK4QBlro8;66wZ@~}oAcY(d0N&$%R+rCGiS|U-ABR5 z5EQ5@LPPoKb|_mdOZah9sI32Iy@S>+6W6J;!Gzzr@!L%KZKYucu>IXt^S65+0U-Vb z5InW+XmQD9|1C|1GxpShXiC~(wfjFf4KMIK^t?w`<^IXti3h~}lcnUJdlJ(0eGc=y z{8a8_kM$7?M%XGnG9fG$E}UPguL%#gd7vv)nVI~F3+buenq)cLqJvb@Xc>ZPv^H}o zN2+mKCTRrM&st_+#<^_r8}cg~mzI_;v7a9_pqBT$(g0Up-v68J!9RI><&Q9Y(qCyM zqjz?R@S#J)+2>76wt!FUe@-d9|C2Vte>ZpWPaOEWhp&^p+EzcIIi#{o__JYghaFF8 zC1U)$|NiF>|HpCC-!)Fi>|2E$qe7ODNM(L0`RLB>uJbyjkNU>6!FrOD< zf}`>ok&>$tj_1oqFf|?7rgu`p2r$F|G1c$1?+@zA(+q=l1wfNbZ$`69lI4A)caz}C~mVMIT7S9Q9+J958Kci}cP za*vdvSK|Ty^Ymb+IX{WsE<5Ef3JG>zfCt&nb%*;@p&8PcT7Piv1jLYs>dc`GlY&=# zuGseTg{ALs<$x05CUG@GjZu&+N}W-g+cZ9PWwjHg0T(G4*&g|;D2`x<2;1|VTvqQJ zDrDBK6ptEt9Unoq1ERc2YSyIiR|Tc|oDA716vmaPCji#^ z@L_JF3O+@QwIlOD!=dtHygXO~EB~A}D4jhnD4Dea-oV497qO6^h$@X;W97+Ea;hl# z-4OlH%>Omw*nf6`|CP`Gz4$=a&g7XC5=4g(lu!Cu?c!VTC0F+dfW$gmoI=saEdl|& zPN*cFD!a$_N$_07R%MwtE*ltZgvpL@+d+PdxF2+JSW)FI+o?DMjLXrYyrG8Us;%!5 z#=QA@y*7B8xwlsp7_0wL_!F=$!B{4%h36nG^x0jMrba5ov89iR4V_8>9&~FN9~~dz zoRX-qD(pPQQwZs?*5YCH+q+Q}fr+)m&F$`SH!BV*>1<_Xgb6mX_Xmf@NDbDgR%I?0 zu8OBrJ#8%+mhs6L?A6PEWl=@9D?4onTpJd!O0m@`v!at7sqmQ`SojH+TA|3(ucCWo zBLJh}Nvm)LN{UO37YWxzR)1F=*5|RgRpLIJ?2@rNQXq+;Ir`q3GEpVfW-?r8bLe2* zgu>ZkD>4jL&l>8jR11e=0#oURW3nil2b=KSvppabw}-F5NfH;iA>s#aN;R1c(+-P_ zl?ik#)Cadk_!Z?6Taz(So3OUWs`-^EAE#I=QLvn36zdXbEFVwl#k(!JCmeC-ZBRj< zO=mM_v(Ex&Rf@Feq)dV8iEaf0ny;jDOkB3EH6Hqopf;nmp3RjVnU((pnPGUG zqpPx4&e?I#_4khKFV0P|8b{Q)3c<@QvX*w}cDYyD=$Nwgqmr#V!o6iXyW}Ihys`RU zd=_2>xjyI9)HHvVX!mWcvG`>G{RO%IJzd-@g3Bcti4*v%w_l9kSBuyCF&)WmOD%|3 zx<23O(89>=^a`0aF>-jEUQwG%rafeE^mb@`+yi^|Y{wA-KSj6=NaC{IHl~o19+eBb zGY4?j6Nicmapc)L znV0tH_kikoU*7It0H0)O-i4oc9M%M$K0ICg9QhX|oIbL>ZO943 zAw{i8GeKq84~`E%2Irew|H{pBB6$0|#+0E@M2g9PsY_CMMFMf+NS=HGl}3teLG-Vo zz~N~4_RITkOO(3hsvb%~NQ_WlJhUIBQ1Z-VK2?YkACkKZ@mjWYeHSr{)nmbdqB z=aca}i;4`k<|m1}J{w2j=mtYt7iFbu)X7p<84IJKE~$l*ZxdAeRaet?TIySxL^6{n zWcjL7@jG=>=_o|j=|p`2dL`d$5q&D#KbPVX?1HE7sJ6sJZ_x=jN7uD+eYI;mT<a&3Z{3GZQ^ z^qGebEca+dy@D==Wa0PA8)J2O##b8?oa$~B__-u2CPQ4w{?SQpcJL+PFn#WpdgFp5ZJp>E2KRai6;__&jzotcvWPA@snD2PhhZfQaO zu(~au1Jk>~d2lpWH-;vR-9NjYGbs}Em=+J<53cDB@^lT3^`=}a<98ufiMi-g3$k}{U>qG%1?{E%vk-p=AM*SsUtXSK$z z_hcxO31lGRv7b;yp_SA6MRFVyE4P323*ZaY^UL1b>~-}`1?T!Jvk01Wag6y@6Tmf! z`@fqs|Ec+>2qa$otYhpu-Q^4BHvfnZV|Nq-YMbOx(tIA=Qb&J-vi*Ha*EGH?ue1qj zz2zpSf8A`f;<{9vD~6c2)gV0wJHqMsrzZjX`9b1z6k<2`4M`ig8=5lFf$v-;> zK4GZ3R>*X(AJ4{eG0~`>&*W5-SAO`|<~Ejc*M!fJ&x;APxy4|US|ZfQd05m!ws5&% zb2zKFr0??%E>W&A&qooj4hBo6Bz53A`I=uwWp9?D39KR_yX@B{Rb}Z*E8Z=5+|H2N zUUr4SuucXr>G{{eC5M;pFC}?pLqb=()Oiw(9k|(pTwLGAI)=fIR?8~Ly zie5c3Ts3fA26X(B0fu>=!_Keutq9McKEd>hI%B zhrN`z?6`WK3;JYrsJ*rLSJ;;*XoknQTeJF+qq)Y5QeMEC{)}f@E8&0v=o?!(`SmjB z)Qi2&*ob<~_JDzht(}aEubacnHhN{@;bow@`W5{qZPqGnsgDJPC{CydSg1ES;kiZ4 zuwr-q4RSxewxeqbo!VFtzhMKG;%uqdNME|W=~VA8QT(HUBCr0whmgiNhAH}mkPgp2 zX0dNaVr|xk;gCwRnJEyvZh$J(E}<-Hvm<~Y$r=e7 zq78%fMmUa(y%t?tLCQNwc9c}|bigsHj}#{jj!4pa=8i->((OvW4)4jb(OP5Q&mz!| z-&$X5GdR75RO_Q#yTy6Drw$EPvY4eyr&!&e)#Sp0=}1btT6i==mzVZnc?J?tQkw z`uOu*+YkD8e%EF^z~+^j>E0$^lK^qtm%m;hs9UPK1ON0Jn_9162mID)Y(H1=?RoTHaONhEjxa&P6#VeXfv{CX zmXu+*z!vxGSB+%@;5v2wq-idmD8|yv8|=DeMQZ7!NRhwxlksmutY=2{+AW$}{6HEC zCOQ@x#!pK5&AX0^RZ5FZ+S5wHmAa~j_j9LvPzcj_`@8)hC-!QNEw(MH43DpM zLaL3uy`P+bwG$lTx#3z@={P_o5`{%0r>uW;^e^R?-3;a`+jYq#<*Z&XMca@(f2);2d zfkyttcp-y+0oZ4B0swBvQx6WmUK8ibvsx2U4Oa#a*Y*~F+%GaAV-H7`=}%6hG$>=p z(;Yc8$ti=al($*b!;ivlwVB#zra*gBZuHlWe^b8W*QWhM?AsrAmsm#pELw(d>%xgxC~X? zlKMcBcpxg>1R*kGlM6YjoJf`&K_5(yVrXMmB4p)>Um(q-A%MWEO>BD}Bdz*bTONCs ziS-N@f6D>4;YyEvx0~t7S3Bwm#CJ9 zLDFKn8QQCs2bw#CW!#w}BYFEcDyU6KTJ@^H0Zi^g`P!Ossh0}T>}5d1oXnNi?kLRE znJN7BiA~vFoA94T7>M z5!amOh#G;VvG!E`(f$k-6~!hwUF)?#CPqJXeQn&=%8WeSGm^vuJz%OqPHS0-*B3x` z=4}t$A4kIf-X>lNgX7swzlb)_#3b&m4N#gY-N@1MjPH~DlN!}a;5`Ckkl~{6_*=cj zLC)J{;1VSKYb}~aazO4aATn<}-+fK;ak&-aLv1m038m|3)6J5+Oe;ON(wk65ZnRtC zW04AYx~MLMb?-WhdX&(idoQ9&H_US8_B%|XMg>E`n=+|5r>{qhExhviVPLW6>=%zI zX%5KPgF=`svMqLXMSi{>9Lw-aXMXQa_|R%*d+77^wGV!eiCxXYyoCqe;L=UiltIvX z`w1$TUK*9__Gra;?(C@Y2bw`vwNA*@b}0g0zv{Q}{ChS|wQ*K(M3xw^An$gkp)AXs zQV+XZ#9OD;Lsqv>ZA#&Objm0x{UVqsBZ9FikcS*TOZyeAg=SkN{fh@t9Y4%N-@`@s z-Q@*5Z}w9iM8hn}fLx^8=}mm6#c`Zc`9$Sa{ka9ufc@Ax)EY+$vdV}%H`Ai@X@(4q zzU^4q;uNZ1jLaTwoZOv4p`;YsNDhKcXp{Qvr_>RZ+xCjWHd^f1CX6OrkMm}3ojmvK z4n)(w^nF=;7+>z-ww{12hFgJC9xsCuTO^EEQ~GI=Hh_RND~{zF5;-BhSkK^e;k1S**l^?G0+%eNsjR(yjj5c3RWw?!iViV zrsbRS68u-;nLi3C{k|dk+WvcYj=e%-U%zQp&Sx_yQGZvN=meUlOK_C5LeCiAGn$GB zJ*~ny{skFhA~ZvB;<*A9l`@Du01uv8Uk|Yv9xM0nT9*jktDL4)?vB zS?KEWtIONsGR@|YBD6sUoMJJuheAfd5jw~GnM-tm%Vi1>5uMcd>7cnxw)M&Cnr1^~lT6-g-4hleU(+()NOu=nn4VF!d@fh_%v%*-Ysp z4RcnVngxgGJBap02eR7*R(InDE&_TG1cs1zBM4{yracM)i2zHlUhgmFBsuVttzsO8Z0BN#F%F;Rt5Ih3DE zle}u-W8jGb=g+z~_LOjD7#XFOAAJJBu{cg_a=FavkAQLa%=o;=O<6&%Zb`h-e%pZ` z?g=iP0L1b`H1FN%MO8nQA5W5l*7Q)K<(3zWiu?1bD0DEhDV|?Rq>VWoi zKE=huW?0;Dp#YNjR2Yk|;=y(5}*H}eJ{_|2qze@|ckh`Nfhs?wlHtjDy{ zmIrTw2mg$HX(YOkTq>N~tuEj5#`~eFi10icAxL!eG)fo@I#pWYZ8I`DSPpmOtit90 z(601@*>~(U&F4Z(D#d;TK`2irJ}Y-NIvN+{J;%rm4;Dhpq%kw@pediC&OLg>+Inl` zlk%0WPl_T7yHyw4CId#{Vat>EQ%lG6-1%9{8VmVuaoa6;E&-F{!DhZ_G`=9|V!@56_zC8%Msg z9d$BTZKSm>Ow%LqVD0+ZP3kzMANgR|g&WDkUO05SGNfNer$YoJKOpTgOq8H)tAf*< znrm~l*q>l=^Nk`v*)bGFXU}I$|FA=AGa!HDELCIOhnL1{MoJZf%DO?C?q|B8BX3{l zu(x|}r}o_@Q=u_w~j8Y{rWZQOFxqpzH3z=m;g+ z=M_V)PgGGlZD>$Yk^yI*>;PR8lHeTN>%!mEXFp%uUB=8I1L`3!01m>xUlsoq``?VR z1R$EC)ORX_tQ6!ORpgQ@QK}-KFephNX9gHeC>6=L)U7H{!zqNphqNc0Ul5I5U(MJ& zL{i**TsGdbdR)!KdHj&)={xb|Nu3b1$29EN^_6Y2?JzRyUB4i(BbOE(ksRVWyUS+Y zFTG1;0!pynKt~t&WnMQRZh>$Jr+?D5D6?o`sWUls&Cv*N^`(CvAs}qjT;Ehb_3X+e z3u)<4hLvz6wMT=9q9fCs(TvSPx880y>tBq7#uhP*>*jR>KLQWCPn)@D}GJi!-_;T|Ij5;XJ-(8~!iB-M<-5|JAgu@iI-u z#)2RhFfxu)E>%JRx-;25f^^yY@$g+!f)nLPQ}^kFfQABmRS1%p$=JL-$?2ShS7OH3 zb4tCpN}zhn3?}%>c_!=mS9yGL+ZK;nIBj~EZT98r;nlgn*zQ!{ag)Ucoq*OC4S%tj zl%7kw9?X4sA@G{t^v`yfw)w>BrMvI@TP3#){%mjjrIG)?!T+0mm>=Yu{zc^cmieD= z!`3Gc_4lEZ#;(UUt4>vNUrygJzxnYuam?Kdjv=cbak$$Gw1Yw>j;mIKAWh`nXV<0l zi}7DGRiA}$-_izTyBGg&&?x_AHsBdfe?#tMiX?6Qk#cYBj_=Rfl3xHQskc|QRsY{n zrvm&2;6^Gz-b&y1%&VxXECkG63EiR6+wCUKP)e~&CUs?y0IKR(EaBzt;ROF7uSE9f zsxC&$RVzq1_w_!Bcd43Ym*$hGzFlxexGZZWm2^sf&0yt3+=TG4r0_$HGCCb911=U~ z#Di3c3*+Jk6$ivcP#uU=#t3^*tce5|(-zWz4bbP*tkMlq=wKO5Im&aJaEn&R zJl`ny%o@a;H;)p$tR-WE6-P-akX{PNC#bXg!Z|}idf0(Z=;S2Zjp5NzP{p+5z7Tm* z(sSV-eOa04HlO_+;n927x$yF@1&Ltx1l1@H+!U)V$bL;UvjLTee@-Lj4GthQ@0bvxKo&hleUp752$MMgS?2O6>xYKs(7FV7F-Yx1EX?nSA)!12e@<%bzd4p`65 zFO4x9HjmkGQN2}3iAt1~qT|=UNyA^47ZpLGS9@o%Xl<8tQ_Uier4&8}wyYyJL|mrH zwv;N(t8~9}7Z)2>r>*89L;2dQjGERW(-i|mKIf*9reccexue;1jH%mdM{?yoNy8#J zNxT5lTOsgwlULR0{DXBIIc8Sue8r|0!!}GzZeE7Xii~3_{p^vqm#D0#N=m8UmWV$l zBH0*cMsJftX!=~zowPp|;W|R97nubLEwBSbnNO{H5tg|e#7S5?VKf-jk`@!-5vvyK zLr^z6l)Mt4{NZM+M2}Z=)hGgI)41^5?kE2{_+s?eMPz+ty7Ym2Do`a$;oQ z%?Df@ofwT(^&n~-jdF4jj*u#zd?X%MRTKo|x|tU2gYIRLSC)KyyABvXLA8&5sHy-b z=D?EW;6oJ+NI0%d8%irB#`UkeCD>qI9u zC4Wy8)6~@bP;0*)Wj+53fFB?K68hbmaBcq0Crw%8>OXLCc@j)3n+zWH%H3k|e$E z{guLx@G=-{An?0${zdc3pAtV|Y=2r(0kR%{gFMG$HvaHa`q7^+L`btqusk*->dN2y^xc+F2;6e#KQUo zJxiI_VmZw5(m{XOToGdI0Ws33^ITR6_)Jl5=xEy*KDKA9qH4UMi`o-;p!mvNC%r$R z=YqptxRn3a&0}R&S+DG{*4bwOj#d_!&Fdk-vGh;aG z5jQMewuAUtlW#z)C+m{%x*Rq7x8NCW!OKbG5s{m{i2B?yEtrWt0(pHC;m@ETEEZEp z@MKB#jrxLZqgS%^mj2?vyoU_~_&g=mpmzD>XX*XRF6PYn81kB(GYO{17f0*jQI#bD zY#*dN%ynn-;jiIhZQMfVPDaS2I z@^y03<$%ULx9rxSYg0#Ur1d59ALVM{wxKP0jbqp?<&0bKRd>>p=g$xodAR6d*fT^` zH>}Knw ztOA0T4s#@TYScx|YZDGp6g*k&O8|?`2(qY|z^}2RA&t`zeyX*#&kZf{D;4Uts1fjZ30ixa5S<;kB z!-NJDFH;m^9yl)6!dO3ClB}nbEv11KAMKrPc1gB_*|IK_jhVoT*^SB0T^#Gt6cv6(z7rI`hVT5{M0|hxbaY zs>4-~%5!^7k!!+*Okq!&k}4JxI>qu0cd62+QJ`Lmp`wwNy4#pqf$UtCpwp89VBsSzGhwC_7!p0iUOtFjZXT9hI6R zP6ji+1@23~RXr;VLzaiYoUB)mrvECkje(8C3+H7=TQg^ zcw{;;7e7_oGnC}hv%fZ0!_I*dS*z%xMt#_cYg1VLCo2f(J^%d)XM1b;QR$)(Xt-Z zPLVkx1(yD;i838NUygD)sd&;1sdF^e@#QZsh#wo>MJ~iXkvQPkFQQqjcRO$na{pw+tEo<}oF|0gXs(QFKve&LuKe3`9w)36}tH&#coF|{0 zOed01xY*IAg|REueVx&9rmFHdzj5)#qCJ@NYE`3#yZ~^z}~y_O@w~uqM^k&*55|XiG7w_ z^JGT+VZIn|Rl~Y^nkR`y=WuKxvXE^zApvICBnqR{x_19%c6)TFZM78-g}YfHaA$U5 z8Ke58G7f=T8j_A?b2R`ykUG?%lP7&fIr2-0ub7Pv5-1AEfcE;R8C9MEh zL&CuRkDK-Low3Wce#{>OPR-SIjgYiQ!q6W#LQw~k8x*yeYzTow5E~X`My9t-oUoj{ zP7Wfr-Z4&=QO|Ks*Oy6o#bNde=o`1<6WxYPUd}FxV`;M&B6n}3UT(CmcR>#sA)X@1 zZmHIaB^F)y*PEtC*@p4WBG|J=04NL6F42X1PHmipF73*;ns0kuzW8ZM!mfnns=^@@x^d) zAZSxfTQ=J?7qG`^k}hBpe-ZP_iS)vGNa4@KpY+q+(fw+er*WhG=`t^SzzqRsq=NuU zexk5w3;_PlBWk*bC?TE&PARippL|#I&!mZ@Q4|l=WO-kYfMMpU!mE_t2X;$gn z{>3d9;BRuV*e8F)#lE=1s`Odt1b)aEd+QkfesEUuU%k+O=e);sKNY*K1mx-i|6%|> zX#%djc7F2@TKTUg1ph%3|HVW9YKDha<#}~|mcy`pQFb}G{YQTrUd`yb;W6c2z4O{4 z)_W{?-tl4Uc%ZT{5#7O(yk2fm5$CS26ws_M*6X;JCiBIW1vARpRczlPGelXhk4j6CFhH z*?xoHKN-_pRD&Cb6@sWc8w8SmCOO(w)HEZnR|(Wx7v^e=fc!zJa2?AV993w`Akc7Y z%B$zW9Okm2aY51S3zh)DQ6O&!XpWR>-zU|;>8RlT1?j-+8DOT1$E04dHEGHZNB>Y7 zrXYt9%V8S^`ChvF=oGSsi#TF+l2E0*#Ch80tHbJSELA7Ys?|tYJ2j)oA@9fm<>`NbY{w2ifQ>twFu~GSRd3$c^R%h%du|?1^p9pbqLqmleYv%FqTUTnzE;iM% z4ydf8n#Jb*T!>0{k2Z3r{*aVSBBjOLh|k?7Z9IiztREqT&GIeTNgsWnthg`Dt5RAP zXqpDN2K6$kSjF?oJfp-0qAV7ymKaL2i%N|e!Fue-?Xrb4&GiSUlyOB%W^uzK03dD_6Fui(l0M*lpJg;hYg-7ulGHzWYE?9#hV*U5 ztD%1LBEMp(Qw@VElvR4%fa(dw=0z&1WGiGX?Fdb{isNywHj?;KYxpW_-mW>?iT2~t z<@Lvjz5Gz*?`)uMUlr!Kv0x0=cGhZ@NDg?OFt zJ=(#hg!|O&hv!L2tfdAPiBJ)XgOV&oZ%VvZI$2@7ezf?|Z}j_L^;x%4>2_$|I>I-9aH+&aT{j1Hl(`ag?Z^SKK;mV(vJ4M&;(bW@sJc!{VS(KK!dlsjz$^}%w$K? zyqo(o!3rbyqk_tMbYy$(7)tSIW>jPh-~BRp>7eW&d&MNN_|S`rCmDF7Vv@~JSrf9& z$UDc%%F0x178h_Z@&H*tau=4)iF^<#h;`9*ov5jZ#`GtRz7}FXXbP`<3~a0XZiK`+ z_b$I3o|1sCW*nD0J}=)Y_oZ?B$n(`}rlCn&$C+~d{Hd0Sg7(T>srUkS?n+%_6Rw2R zDnG*`sCs?irE7h&<)-E>l@m|rN<`=Abd#(%nRV`%{gL(5l8c9<{V9AA4s0VQC+KI& zVo(kJDC|iYxlHv9bULlX$uQXA%C_nSkP5Wx_VvtCKcIjWdd0s`(+VXi6!JD}>6`za z5?+jyBdM3fC{$vRF!(D>C2#7yu4r|G46n3;J=Smf#Gl{4>B-cD(4jO9+&QLIU=AYK zVO2KTb5xdpW5+Fa2f-i+R7c2C$#|)NPo8(`q(>=jM(kwGaEW~>1(i4XQZ|I-?YQ*L z001)B9_?oylW-9er|%sG5hI98h5$@N&Cvk8mem5-ug0m-GYY)Wl+U+LshjHFefCC4ZMzw} zAmP4jeP%iRcg>D*i{v67zlkk??N-kT=QCRDwOV0)*1K$Eb+2aL<7)$b^zH2rf*<4B zFS>eT*I(Q7LZ-hQUR_b$_$Ch0k2+V;S`+(Tza2xEsoTDko3?&YA7m4KL5bs$?N7w- z#MV&H$%Czrs4lA?FR^`*Tc;luU#6gV zn)UJh99Ao{pNG_>t7m!kh@QS9N*Q~V zd)B7;O!H4=(KO@_+7%D&94PtKk{c7|?#|g0jIeYRtyR#AR`XNB^qsXy`RbQSQb~mm zz4ffqT*tP9+D`y(A$=)jo?Q~0UAn-%En#S9Lp}M=i`qU}5sQF*V@vRu^q1u|6ma#s z>y4I?etmZj{h26f{;BCiwgf;2?l2kg3Cd z&25<1N1DM@hYgQGSN6D_J*w|GFKP@|J3i@hXc<>-)tNmKLjI6HVj$w%z$%Dp9zLiD zU!R|6LKw3)x@}Sfwn>TxGuLede)Y`j8NiM^J7wnQRZ=fE%)5PmtbM^hXm2d|Em3&X z9o;xLgW&gje0!C6zUL=F(zP`2P=Om)PwVjEW~G7Mz)jilgO0>n?RMw8>o0rV*BmB# zn&#b3y;dMaM9`0(R28uU>E!yAs752~{JEs7Ldfi`2PxnJdCgTRXYLK92lohMB#=1m zKBWkn)|`tTPW^`dTRr28Uwq3K?gDR#IF-yU1a>Ie8}*hH^2LukOwdAK^YLxq1x>hj zNgLO%g3ExHFaVLZ(Vi!=^%uZ(H;BxK!1V`nb@EQt2J=`r3VF3}zY(x$EDVA(m;?Cysx&-#Sg?Wbed1k$RZ?QWV3=VM@p3@f7A8_IH?G3ThWKvPqrub?Q7hwe@^tt0#sp!#?2AZtQ zLyGiDvkLZGA#4kgdgtGMQU*$fQ`(FOFp<_qUHgUJxZs8!_`t>vp#> z>Elr_lx+u|O(yO<&0kZh>&yI+-Au>@O5gUF2FQ~D!oxKF#n->v7COtp%A~pN@%^z~ z$mXatI4?vJEsTm|JgJ;z4Vk!|K4Z)-sVCrMN!u*+D;N00QP|~BOLQgu zmDWrWOg{xIsh;2bnbas>Ohnf$|MazzhFKK3cAF*LY>+^F5%iTk)yO#704Ofv26;pw=J`ZyLG^PMe;p9 z%i{s0?=sBugq`XfMR5)0&|2wAC?5CXxn=8A^^A@kmlYgk_;~`p=`=eXw^Hwfk(ll< zFBcIFDwq+zeS(|(o?T_aBqL$|J<;S`E)uA%OI(rI0_HeLEGMp}g7)8BgplCHy99~VVbpo6fuE{nG&s$iQUYW^4N{tT1vIC{6xu$6?E)L z?X?_2S#mjf)loqDtd@As(R6)d%;QM(@WcfRjwN+~WgvSb4}v z1^f?;JK=QnFUdSI;i9MF5`yp=PMG^W0^V~+4Tk;mHw&rpQn~5)@%=+ z5UMVpgCqT?&P>KRNWALC4Ier}9fb?Nd5};c71hdgqDC}YoWo)nBs^T214^f$@I3i~ zm*g7FtJc|bpAYTr$BPef=9RA~0RZCPO}&4Igv0OL{XcL1y8)=##lYBkJ|WkBl&5!$ z6XIk|oYp=sYg?L*yBo$=;LXf-)TJJxxne&PTX9MxV_X zK7YgdPL6LK)+-}qUGL|HbTAwsVQgm)3Z7CNsj7==11Gtt^BkO}q9&7;#91^t!GR1M zBVo0GD1om2rpr24X=AM(;6tFvWp0vM9j8l1Srbp`PLC>|@)1w$o#?U*0m{cCwmdGb zkr{)?_YwCbB;ySTMy!_#B%;*<6}Re@Sb5D<(nom4#x&9yVj5SY@C;jo7zg)C`Cy+E zkT^fh(CsdSh7PLyO#2|^!2QLQf{UX|r^R0Y2|PM+OCG*+)n8=GPpsdxEYoioH?}O^ zy_{1s!Fe2zST(CG;F0|GZC(+EOC~uTgkL);13hqxOzg@xR5yW_*xHydBI-~GV2TLr?U3?t(pzfKAGp~YC!UoOUl>){c z7C#Ed>RIYpF6$n-9Wk!X1wO)+$86=FY}f6vEq-siCJUIp<;lD(#TjqBV}v|S5Hh~? z1d6J}))>UN3hI|Rma{&o%_O2aN`5FjEMf+I$-#c~Y>Q<+tQkmt@sJ*&LM&kADD4m{ zRYBblz2xKHYOpvox_Tzi_tYXXyFd$S(U1*YA6s#LR@S7Wi#ur=>VQ&+AuD=^4Hk?H z$_6cJQFIVt?|AW9A{}f;Lg@m7vn*buX%)9d)AI^P?u zyZ(G1A%@ljN@-dptyuZqHpF({O&3_GUHf*tVZG$sI;rH~eYoDb=yJJ#+KBrm4oOZ$ z2Si|oVawa6xz2Nq5A?Maz2zGx~dr_Ii8tJdq@$hZ=qCJ!!TAKE5 z$7YjX-)=>0dPA_X!C&}lLB%z9W5~MYJ_DV}3O2SvmAuqI`C87U6z7!G$8WTkhhJQ) zb=&P=B!zY>hp8p+$E%A(w`2#cb5&s&d zJ@2I%^w-@*V~4VS<0Y+i5yIPdyU*(8J`Z_`Fztcv6sA;nD!;<5^H*MuP<5P>2rpg^ zH=x2~IXHY5OOJj8S)tn##J&^E`W#Un$sND+sRnt{ClxSO=AgqcljzFFE$Ddga*Tui zX6sOPI=08CGO+cgNI>02or<;fWtpc{bqQj?+>C15oj>IP^Tw>{m)(Geb% zuofbf5nnk}9{N|#Qw-*}jtdJCT6eX$gxP=Fwl`&qVjwB%tt_-UNBVIltYuzZsW^Yw z|IFXWXpP0964MG+P+L8pI?#@`pELf(^xR6nn2pA_VfeMXgX&^#Zc2?nmcZd3s8zo; zE<+c4nn^T0YVeNrkIbqu&#c9VDMp=N@BG$le`I<6wF?!x(>_Sn`13XRfBGZI>+g+- zh(D6N{y*jk3q~k7G%_JA)?YZYq)5|B)j$Lz49TL3#HxW3HQlYoTE@5M3dL6=3`S=5T1y`wsky~!7lHo1%UAS zi~ru!;NNZU@(;emUyET87%pq|LuN$>qzv>USdyA9xV8zwL={CalWRIC%nSQHsjGC$E zm(AX%GY4*v(iV2Qoh%>H`<&&M)6JPyZv)n0Klwcesuoo?JYusvuL4L5m`}9Yb`oSa zxGNDv^|_Ud(rRyNn?CaQbKMnkoSd2gK=iI>eCCDD-bs!60L z$JdXvrjBwSg>G%|hC4Qpd1vW!>B%Ea%Ufto?xkf+}2?W?{M`jmyw@yUqC zF93`B1xL`%PR&HM4&=5}b?+t;TRyDXg7X6~QGzyjBU9RjbC%8S*$D~>HL@DyWJkVB z98%)aj^3P*e>*3Bp4tYxp5-u9DZ(aGfp}0;r?D)W<1$`}oT+{n|ID4GiBf8QspCnt ztq*@rt<%~?)VO^>ZwI<~2;4jNW{$34tA@29d%q&SH?0`V74`{Npf_S8_0f@hgvi|I z(Upo-{y7gZ%hK8`M|$*4NXACHNA`gP@u&iCw9kWiyB_ICrwMWs|eQJ1se-u-Yp z_K!IZLq+z_DWvf*c;X-G-{&fR0hFJ-JK61d#Fk7&Y(GZNA*oT$-6(WC(_M>h^69UG zm^SJ)PXehP_o#8^k_>0jwp_GG5~^d>zfb+(RH^iko9C=>MwETdd1n-&pI5(dK_aR< zDZNI2xzvEli1>*w6vD-vtY30G37K6{I?VW%WtZyda|o2LM*RX9zB_52(YBJ{dKcGQ zM+8r&+x2tmNSht3a3xct zSKT+8+e$5+d~*?v3Gu`dvS-^nqNtlg+jOaBG%M&c?Wd{pk1~F+IXY6YvwRl3v!K?X z2C8*fs@FT3rAtONeqEAC@?Z+E`}rn+Y@tdk8>0{Y;YFD}cU9m`eg6c>>nJRob5oYM4y#?uu^pWP)!*0c-BiiSNZ z#=bEWR)cKBq&5~GFN|`qXW2Zp_y-HeuZOP6IsO6=L%uuwKKWg8D4uwyMV=ZX5fQ6X zcENwKo zee=gsv~tDvQnQl_1rK9*K?`Z_NRH%fzofL`mcpMxEs{c`Sz7=Ejh#|QB>1QG6Sc># z?CG&(OG0Rtm@G$rk2qkRiH+nw7Ib4pjK7E-!82fJT)T8eiZxYB@3C;AuuIhsAE@1E21x%_n7` z2z@2$SaRWtN%kaRo+72UJrQLHDtutgb(%_Jk9NH7K$`ApD%*a^>4Qgjc2{ZAGx{l; z@&xGYXv4Cim>u)#4p=XpvVuWND?4{1c&18t}X5q zC{PM*e}~yK?{sGGnf;!#ulJm5&%Vw-@{rLiM_<)}QpR7kB!k8PqdQm^Nctv#Vq9O(`Rd%B!RMGiMG-!M>{8J}0( zG+|W?%|G49u>pl8Wb5QV%$l`X;dLLiPX&v*&4H@u`$XHjc_WAK@q{sApQmA+sd9lU zbXrYTq&ovaVJy0>Ix@`pZ?8A+Ft3+K5w7rsrYgl5PuQ;9Y3S7UT9L~Ktuu&OPXe(? z50E58jtuM=crdOUdAM32rmTJO&CR(6R(Mh!6yWfw6jm@k;rMGro+9^bkjWxE{#A5L zvMv|(OzGY4%3MfVU93<2Lq2NW?fUs@DlacvGBtxTZo5v+P}SP2{B0lf5nq;Pkaq`* zwgAFdal^UFSHf*_VO63FWxH-dMg%z(EuwPH$g*?6dZmH&O7>ePguHp@C|6-B&&uOrdf6)W3?bgiX{rGsIvPmxfY1mA5WgKj@MYW*(gy{L?J!(d`PYzV=&#GW2D zbKP83y$Nn+AJv`V&OXw?Z+iC){}~11?(mb;hT)Ds(KY_rPyX&{=D!Y#_-{q6_#a)F z7zN0W8x+y&vGfY!H`iOKV6?sUSAN^QITZTZ%>DfdFq!afe%Vo9f=Ba#CPC$Y5R{5( zSXUBU`zOQgpYFbb5E;D`l>KtiRq(TRSMJK{^Y6UT2pAP({v0SpPqon9no#2LPtWv! z@QXJhKca%K|CC4R{;$HdwH$)m&sJakCl~n_SOY}GWz6=#z^WlO8i$??Q$DQquNm0O zgZzEjt}pss3}AC-JZ-`^psX|GlXZ=!nGY;Qa{3w|rKJ+3KNl$cqt(?fOX8Gv! z+cz=Cm%?Xn7Tn9Hb)@1pfy&~i{=Ww;sO_8W7lQJV$M+5SF!(!BCvgHznPTbaVqw93;ffZTsk^F}&pXND0qt3oB ztCEW7p!Igm_J$3O#IadJ5e|4Zt>BZ7eTb~QhYL8}2lsvw)&j1o*4Tp!xE#+SS-DRh z)ZWlK`9dkVbUslEb0P$!SMAmlcQ2AYp|nhsrTk>6vr;ZZ9=7K=O$pq!t7YqF1r`g& zycTiVyrSb7^wPc|P4nAj*sh^_AyDrvHp_>Y34PPo1<@IV9QQTxPZr?;F&tc4RgEZS z)8^LJIeV4@UeA*#jR)-T_#dC-a`f+akYKIvll6@upku$TcXkv+Dcu6WkP|6-*J*87CD$=zDYb zR!8;C`Pc!gJen2bRVN8CSN?R|pvY2f=7FRP!<*ZbV5J8(cM}sPS_UD*7cE485L_{T zKqbA!_I^k-!`l?6OK)t8)kA}d@`S%tyMAkgO3|wU@`wX=1W8Qdm8}3oXP%uGLqfWj z$ujF5BN%I&ANf|DxgI^)(VyPG?1xuF{S-?WcUc$3+!BbAxC|<8#Tp8a7a4L@xfz2~ z!PCOL!^g>>#deiKd-Zb7DyLZ$2hMf9;!yt$$$Nf({@-WpVoLJvw>HQ8`LA*uHD57_ zbJ6?53*sbbVusmquc*~zE+0+gf`pm!n6Ahpls$PuUy^Lzx`9fZ&JdG2&}pK4^$=C> z@4>%|VlB6itbQ_8bt zE_A;h*hM`SVtVaM0Z@+d?bOYAWwrqr(qV(g~7 z55UZ&#a5@1DI?mu`f5e&b`Y4XzXG>7p8O+OF`mw(ECDJ)I5ceT3k;Fsx0(kjS8#<0 z>)4uS2yIJBH`3&z7ua?g9`blUGK&5ju6iyo$dfDkqR;BSnIpXQ^K5pT9>&Rz>82s) zJK;>$0=uWTj1a1`z9V13{CdO8M>)UmI1(!Up=gODEbBj2)i(2+;D4nj7p|0_cw;?==Xs58+~?}&KuN$e{3?l+ds5+pxePM zqEprny?V|Nk6Mw4*Zj)iP+))4Ln|WZ^@vf~pW1-04mT&%$+f;)rpij$CF}zBiP&2& z3~0Rj&T5vdU6M+tkGGtfC%uz5K}6=HuRd;KTQ%kPpy>aHg8~{iBRDQ1l&C`lWy@;{ zca#PAlmJE4tL{=pHOBCzjV>ht64vh&vr=~%Cnf5KO1Fz?iBkt=7w{BGX%z15La)FR zir)*Zo74mNrj^(qmyTL77C+n8ve`re@xo__m}j_qOFv7VazeBr9Q(OV{s6yDJ-&jhD+qimx=ByZ9O!#|#4+AW|02X^R|Qp0qN1uXRtz= zSU5PG8B3uo*2%aUpe+t&#IT#_kyKR%eCJxD+URF}^I5HdK#HdRlIw?}yn&Zyr>mmE zV1@Gtt!t!gNkNIp17Dj-*TjZ?y#* zvR-II=(5HjE~hP`O7z^l>konl13Lrg;fk@Ny+Sd5AekQ*gALy1?5mRV7aFJr~W1FkQYauX{dEP8%4*%s$y zM@#(NRk>uv2cy6@ufKl1*~yMUB)RC?rsp1>LPBw zP$_P=IK^!`28bXOziU`!qW0!dm#axX{;MPo2|%vrDaoLeS7Z`CU3*91)b_$aC*KFK zkZ(cmtUGw$4F(d?1XDoao(;m2SezLLRM6&@ZfK~tnD3qj+;4H!Xu?hxdg~gibA~S! z@tB(GrALm>AtL1xdCLc@w(^?HFDEZmH@h+l5=eo{*tw*c6q|F(f9peNIl#q}kGX;t>G=KCIUXhZSpYU+syL0XluQJq+mt(bM~c@6u{JH*iiNolib45nv7O6t1qI{mfhL zKb`m5RP_J5SU3=^^PFG(7v^-KONMi4l@YU7$vz zY?=CVe?`(ja4~~U23n~4sUs}B{X6;EoFX3$>0FbW%-0^&7`isBrg2pz+(58rxOR>x zP#oBRZd2eiaH(K2aJpDo>$b447a$?xf%T8r>`-y6y?ST9*L8!Wf@UCV=+4r0cfZeK zk%TST(ZNjP)paF2Rm|F{_V$&!rcXV_9C1q0VY-pycC*~Sm;|VE$-j2jgLVs=VgT$wpLN?QlQ zs9D+Iyq}Dwf%rnv9O@Z5cY3c6cXXBPU*h?!BY)cOMK+B-;jRrb@9Qd=x0i27O2X)I z^RCf|z75nh7{_qAo-r`i!&X&0OYaOqxMHOPScC%axhw)x(1rSy2eU@5{DiFplMC(z z?HnPybNX{h`=9Stw0J8E^Y5OWDPpvVJPpljuzDoE=1POx)nWY)9mvpS-Ko!FG;`&6k(4zxTfG)N2#I2bHfidru*R0oyfxMdWW})T#%BiQ5G2I zKyQ{J*fk=mUgNf$5-E2SXFAc4W+(T>{4lbkx3lP_w2ar!o{@XuQEUx}9{$hhN5*Yj zynAa>g-*%QmNfbAEMA+u1I{Be)jRsWU)WGrG&!TA|2X2IuN2 z=uYJPVOWiae%1%QUVuy@rk7KY3`Q7X+>jl)3<~QuwO+$_YemIUG+rLp#CIZ3nW-fK zlPGlH6`iOUhTRp_Cp;sI*f*imF4lS)HVkahG&Z1NOpn!Zo&X<$u{+}-G$KD{V!8b8bQb5gT;rV=;fweJ= zcd+ZJv&|0oZ;Q!a2TyDZTlCr!@~aiMIJa03FXfv_Dv4daMa`?9t*z+Z=L1h0hrJ?D zd-a#`_Lq-<8Ai2k^lS5$!$9+G&%599x*K(6end6j2$J`CobZ&z_0=cs-01a>e}B|} zWlKW5?p?@J?KL-7&!>dJ?{AlX`g_UxijVX6gr7fOgjgijp5}_HyDIVL6s4{p)T+cV zF2|m6%Ke>}cloFA0|2X+Xau1SHXvtCa*b`O!m)vI-Wll}Irvb@-o0NRQ~I^yp@DB; zOp&U9awI-@C+@?&6gwXrGSn{$8GWWpKwPvX_1u4c%~!eJ#(Wx=%fZv_8C1pWmYRW= z+nW-sM_i)$(I1?=2~W~Kz;9MRnXZIe;YlyAvmK5K6pg4;9FHX3YYLwZiVw=BQuIXS zN|Z`Lm8a?D#&K!Ko{{k!F0yr8)u)}`AmfFGW1i)hl9S$<)}=hslZ*0hYwKs{zR?|` zTINvwz_$d1_zACpQ&*pl@E5fe9A?qW_&S7Pf+ey|?QHJE}mpSyb7> z!~|c!DBw%Z<_LlJaFL%N#%=Y+d$a8#PVYb&gPIRc2V7;bjDrdz;x|-hgxM`;1&1@I z4U(S2r=0mW0cGh~+?Th-?@0Qdn!qAM$-|S-R)fZSL8?A|}IJ*_xP=FuzTgt98Ke9N(YvLnmdo?nothkx+r(+5R@kj8xMUIQ{33txPf^U>uGn+-g07>N@DHfMr5$14b?Jn?~ zHi2c~PCPg1SFiIPU&J&W12TO-E%aIc`E6@+>QOAa&qS^8_A@OUJf!PbNq=y)_|E!m z$H%G3{7SDKWJAQu^MQ019;5v%ON2E*&wW3NUABr=Nhh#fzP7UB)a_(lI7^Q~k`^jW3!#%zt%MuZaJXoojLm#Q}*L7=+bRC-VPR!fX zSU;aWEvxl5ovQSQB+U9cCvS!`41JU2DA)R&N7Zf~=l9!2A{3+2Ou4-mc#>0Y@#~v; zlN;M?(yxQHcbEW@1Mr{EsE?b#9Yi~`>`bI%{P!Rj6}xHP=+XN7y={^;j}J5L7S46Z zL}PskUxTH+lE92%Y{caz-1?OR%@wo48(8zCbfvH?Sb+E)b;!|3^hp;2RUITRIhaIq z>)y=qBhJj2Br01=CBPENQA}DM6P+#rk!)GJ*7L?RyRLYzds-|A^g%h#lMrs5f%j+* z10`&4vYI2xXl?WQR!r8#jNBqFm22wv!Z*ic659Y5Ag7$mDCgBv z#>4i(Lx~j3E@;h})ti&LCh4I^CCPOaTp>YV1Cnn&)<4-JrQA1s z;-uVNH2w!cI*7A`Jm2PCbhuE8h{7sh=!)TaoBE2SZ1cbVTx9?=PF-!!U0E)9-uRCI zd6oT^4RrQTi>#|9Me=G%so|8LvL_Mh1Dkg?CphGRVj0?}2h*HJu}VY!T4e_mAkiLk z)3vT{TyGzn4aXVlFUR0S)~QRXUDgs8DGA34-3UA2!gYxlXb7 zK9v@nuCW76rCMYiuR;His2r>A}mXK46?G+>>4mdheDcGf%+5U|;Jy6QK7tASh;o&v7_3BO2i z?G&-0V2X=)YL+zP`&OU43dmhO10SpCD{z=gC)$5*?kPwzUvb9nbD;Y=A@awON0KzwqsD%);7 zb(~h9Qv*mtcbRYY>)NBNHN0L`j#iug z0#S34i!bPRo0+!5U73SL>466oI*V{SCiMy!-^Nl~d~UIQWgx^1#<0>i2drAHf0jUt zQ#+Vwhz3M-< zH+L|1HJ>EB34-tzDj07*Pr6#Bz1)%K?kmWM+@q4`Miox5^-e#28N9{@rCC<3E$7~2 zJfeMBQq?KBp>6(IlSQ0goX?vtWN$HsP+R^(TtAIIM9A14CVvodFNe$Ai?5!x-5vwB zC*dGY%k`9;(a?OOj8MowFTf5dZ21rI>+s1IkAdkn_0UEN6WF2VPxM6?e7DrB9Y; z6p-bLOqO>K)4yj9(jXY~qI|RC8*3}xo{w3z4e6E(m9szg9SnSPm09|OVzS2W-Pqf{ zmAPvN!*yTK>zXAd>|cZo0tWLM+7LKI&(2RX`xhY&rEFHkbFetyYgM*32gqkC{U&%I zo*k??na44hwiepr1+08k35a9n53O3tWn`y;vepf5qcx>OpZxOB z2ibgxw;Gg*Q|eBcL~0_^(u&jH`fX*W7Hhb+X37pHdiv?QC=lZZ@MCeQ#tCwDUCH2( z_F{_42A0OqgNji%so4y&w;FEheIsX0anKc3+$X}hRfV1|UGE=DwNBttXOuU^NQ}A1 zC6lvLxKzcPW6a?EtnH;c!AZmX2oWd2LcMYi|u7nP<3jBIjhDfS2r7_qu7)YRDvaE3=03UW)y(NraQz z7tHp!So#PN3;OM*rxRqc&qcC;t-#jAbW8Wt|K88#f=v5|hRnNMN}z+Qc@&!OcE4#$ z8r(t_Ix}HnPx>fcG*ps)px4+2*mM!{@PoHq{>pVqms@GzT{BPn2CeaT3*3fM-obWq z>^%BLrPaFS(s}5b#R&G`UW$RiFNV17N-nO9 zfRU7_hH;Xr%#;ZL3$2PhXA!~CO+D+NJc8TW;P_h-v4s=9lUl@!`L=Og)gI%fnwsi{ z)3#R+a8xlO_-vhx%vX+be$!TCqm!#roF=a)EJ`C(+Y{9sq3qX}_C*SPd zBg`@!n^^af#w7A#(h82=*4_)v6`*vSfr9-0GA2j(m<=_MWn}sUh!J zKxbIiTpe6U!U9+|_VmPZCxLzwor5D1TmdYBE*PGvHAlR^83s zNA%dT*3AEW4HQS|R*!u~c^ky7;MhPAsj*e#;Vf8SH{Ms#l{UliBf-v6Zd(mGq{Zo= znaxo*;JS^6yU1-Pt7>iod$BuFwF&TT}_$|Bx$Qsu*)z(6&sp8^8$=NrS%>w?D ziSf{G-^Z>F$!?*%uIARzk#W;UV)d|XVhaA6GThMe3ti?^z&EE3R1|au7_bfzut1jI zJBGmwkfi#790PTqDN^rbrHNNV>#V+0MJ3lvo{7s@mTGI=> z2W7*Qp7fRj{Z4u&XCbBguGJ1YT+%YUgrU*#QO^xz3M?MV$Kq%311}b}gth)4$QI#r z>dfaJYl4>TL3VcNHWy=mD+K?Zd;4z|WPcO=^XzsK%bzW4RBrv5lJx5L!T+z79>@Mc zz+&;LMH@-}*Ajt02f?)wiO&`=tvSqFyY-#L8`f!a>gdsua?5m?!qb4WXhYg>hsoF2#UTzmD&i z@`bW;&x9no8enjiK`hGQ;|;a_mIPPg#bEchfFDr)dc>e0(A)?Ojkxv$oGr*Ks%vCoW9v<* zy~Bszj^7s`K6Y$2l$ZRz9{5C9^FhZA&Clm815>MwPA6UlUu2+SEXmlj@M?dGp{3xl z<2FMQy^)nMtrTA$J_Tjj~i@Zl-q5VboG>sHA09?q!n?{xnW4$OgT=`^H%-W^m_Z(^PAVTL z8;&7BT=2a22bmi1&Q}p4uA-o1NQyFDGB~_hX@aq;<%8Din%)Bjgqj#WFI>XW#( zo<*4SU|Pj{)-MK8MODIo5EyHMb96sbLwX&!;BlI?3Ys_XMV9R`>>$`)s4xf z8}3w7Kygz8vGb`h_xiNArklqr7Oj~rvt&VKhF+fw=Sb3@Gl@S0Oe&=c`?rc&5|B`L zKoP66{iT*k3B1|17LWTRQ5?@^kytCAUg~2ZVJ)y}BxfEy&v=>$I%J(qmqRo&$6Bxaf$3 z$+h}iOnn35RkkkXa-SO;UB97}^-EW+q1NFfb{pKA?rg%je~Z4%>o7s4n^|k%n@Zio zP-E|-V8Dw#N2Z|khbiEf4FXeFtSzw(kOo4A^nN)h6 zW)!74XhJ&_I+sLD;|#cSN+^~{&0bGzWTVoWwd&!+UV(|{N*NYZE9TO)DxaVsYm*l3 z%)eWR{50?dJ03~r9_yjMT$oAo2Z83+HtR%mgsMu9>{C@+QUl30VmcWi1F4(fdIU?i z#x3m`tcoA)6Bfzujwz(^w(z+3ki7cs-1sK0F!p<+O7D4-!bKdZc!b*{9vDeGQ*I4s z3(%j6akD{%|E6I~{$ax?*!p;Y=sI|FgTeU~h5_4O>G;Wz4K|QS-v?bjFN%|fjHr{M zXRLEh?Gubh=2?0;L&|eMJTf1@k2h1-Xs_{qT4a%N?Fk8Vy7N4X6;l{=X7mIE{vpf|$NB}^dhArO zoD@mxdYkk>bErip6+IZcOv5Jav$E+?#qOx5OL~}ci6^h-UqLJQ8gNQ3X zE9GM8Kn63q-Y#v^V=pLGezCGNrnaIJGoxd>&Qgt=-qF;t{J@gO!ChT7Pj?&SW{$|B zUOOPyE8rSXwy&4%dBM_O5pyI$^?R)R?le}SQQxu}$(;cy90iMZH7?ns^UM}X59L)6 z)Ky^DPjDK2VCq$xk`>ae{)Cw*hc0z{^Y-BDZc?6_exN~q$zpzK6HlWgR)qD;HM`s{ zva_U@^EB32MSxbl%9XY>IpB0bKg6%Yg12|hp-Oi2Kw+8E?1e>CWzb(6B=L8j#Xe_l zqE@OwaGo`PV1Oj&1}{g1rujs~hemoeQ0fsC0-!9+?AgyHcQo49xx{*#qRj?ElyXkx z->*jUEvPlbNa2~6O_uk|It^RzdM5q7Yc?%iunP@_D|LxwgmT-mN-F5{l$lLnHd>F&uEYOkgKkg@yy#;)C z8IR4RZ^pj1q2vATqswO{b(;@D##5(S93K_uk-RAf6kIgV=}m&CaOamwb_~@1JL#Wq ziEGwEG(C!sG=8n765rH7IkoijGhxM-CHADzUBekF6Zn0-(#x~dqI73;fH9&u{<*93 zMKD@tujq(#n;QT8Gif!KV5-zMiJE=f>IbRu8=7oCTZR?gC5AoE)kDy}fyInf{)?wn zVv={blyD9BdS4e)?|9qHz1u=ZNyKJ@;z&}*0f^(K;K5W%cz@0J2=Ha>U9m*Mc%$HVx z?OCTuO>G9u5mRu=(4*u)RMWh1Q5};mV5GPcL0&v;o#Il>JvvnX zliy|CrGIRI#AjOv9N|pG$|1}6*3zYxxc*%F>{&rAuF2HFVF$+8m|Rwsm&aX|Tx%iV zghG{0!^Uas4rnQQ^(()o`iIh3D^afi6c?qo>B(-TeQ5^T)V^%y)2Qv-t|ea(bQCZN z#GE6?X_L+S?|rV5Qws-6_n(QVbrlFM>C(p^E1A@KQf_kb^oIbRi5C(kv>$mo8*oJ3 zp2c~n3wH-)=Ie+txFE)~h?&2y(rUKB1IhqZ5|$fJp2)52FTA%F zHP&szMPOaqWw&M1SRL@)yLTX>>+UfW=p9uKmIH1RZCG5I=tb8eN_R9ZVv?&LwaDlk zWe1%D9KS!%k<_aWNFup`(WYgsJs;1Wz9jY38aelvx#X~AmFdE$flj|YgK?Mlz6H|a zupIX_O|15}cz*23nxk!4$fk^(-8rr?IanXpR~^^Nfz@P}r(-0{Sqm9)Zzi(=@2KM( zc|?QCx_J=$6f~)q?)yISw~9iv_H*;Qq?T_1TH8cUc}PBRtO%M~t&*p10<^{c9YEi8&KvkVB15n%}E*!>lmkm>X}u=hi)a=ap;iQ*7kaMY|0$lKly~q@h}w} z19sRd$`s6S#IET0S@SM!^`fG38;zo=qh!2q0hC929)$}F-NASN!1)TK`gx*LF%(#m z2G!^ZYY&ny3nFaw@t(B14@p#rmy#6kct^{VaZ`u(VohyQYuAR{<4qs1e%BHWZ+5tUtyfAgPd;*^8N}H)Yp(EXD`mUlzI&`*{W)>D|5IjoZ@x6Bz+omp| z84X>n{)3rPHJa>+wEISjApCC zGGV1R@4myaeBSjtYi8rgbtt9IWUgIP+2EKTvPQG&-_!!eTh{Bp1flR3BZcqBJ=ErO zur6~JW}hlPpHVX!Ud_ObO_a9T416O22KM%uA^D2g_KEeRKL0vY|Jq@v%zmpbm5t24 zd;v*~3SlXW24%LqSbve9uI1QT1kE=frwEc3dYrA1&1uimZW8ry+M-Rl_PrVvaBeLr zwL>PQxYVexFy;+!fo_^akQ7ub?GR+U2c`;HtT#?6B})Ee1C#{3x7)!U zjri*Gl)K&+Y* zUR_lVX*XSol#@~S+AkuHwkKj|$usFGR36rgapg)^13u@BHAzcfv|)(e*`IesUZF4y zD;)7chWW6m!#_!}C|aqwuJnEMjW{?M)w^UYWN2t*haG zf6ALv1r0b`HbLDh)hrm_-(=`h{BUY+&@4Ggv+xsRam#5~9#qUzj}Vl4-S~BQvP@2Y zSh7=_khoT!?Vyf+-Q0TuuN??|+*j!2H?s`AJqNzsI=}R>>3d~5r)X56LG6fC1bL+7 zg4w=gMVsy}CuFQ}3618Nj%}Y`?1W35NY~FB7u3hW%)f;OmDZGH7z*;A?i)My4%?dO zs$u4<>F8IxCI>%t2F-O61tF4BQOq;Fq?BAaxdA#}c|u7E8IF&~@8gWw$@ljN82I*N zrKKCaUXtl@w!zssJ#v(%_zu0e_O0BJ11@4|aTM*8WeF+%dUz#86Cte-9Ju@|-;7Lo zY6=-ets1t>Xe$KyoB4j0?+8+3(W=^l8HY1I)Lj!}#AB_QoQ-%r9mkpa_B1*g z*)U(+Oo*hF>}Z_Oj%d^KgDnCEqQm{dlD8L=&x-I93wr(XRVx%mO)tXxr_SbXL9#UL zRI^e1J4v-*@AT&2KL~8o4K1FHlP1lGMHz05ne~p1sogt$V=k-PqUfIeQC{-7gjwN{6bm(UVt}PQgXLoTgq*p1YoVT0JJYNO%8?b7ksN#<_Tp2ArMZ|eIjU#By~#W9|@Ez0RS5zDn3t5>56#u^iA zcsn&^%Njwid0iIKTr`A7#y}m-t@o2OV*CkVBv!E$B!39;>ZpOxdQQb92RL$oB) zq1i@wOdiFsXf&vL3el!D+czlRkBmGP+?6#4=L4zwgkm#!dcO_Oa+vzQbBg1C2#kAa zrmUo1?QL&FKh-as*4qoY$rawVcsBq5SRs_ugYy}6)sQVh! z2_A^tC)_lfrl7TO6}6ZFI zMwDpSOdmXt6u){=TWrEP1_z$HS}h5bTL59gTgOMl9Oyxw0h<-rD6+BJI5ErVln|b# zGi%Lks>XkX1I9Qt&VMFwlBT5}_-RmN*v@gNobvtvp=t*$*xR6?=^vC=ZXVN*@u-7i zR-(0RR5*AV0gKkQS<+B-lLo1xWX4_MLHRD$r$UZN_>zrcy=>)dh;@7kc5HQ_)@`pi ze)qnnH;^J$G><_7+%40apOUKS{DFWl4nHMg8(lpP(=m8>A0gnfRMRxb$3u1hQD5PD z+5SFKdW#CQ|K?+LY^=xKuE*>1Qx$o<^*t&=>kuf9S*1yfXJ#$!+cX;5K?5HS>dM?> zqU|wn6LYK?awZCJY6-rk!*|=wYFrjlyt@IV{oJb(*2_D^GhU?Q^b26k| zeb82LoA>m`6Ejueno=sjq_1OFOQPQaWie-Vht$d2Lpl z;6xN(SiMv+HAaG)w^b&^Ri^r?qE&Fgers!p#iuDAAv}tn>#V{9Vfq7fl3s1iBSVLa zs|t9@dyqa`_FNi1%MH23p3c;U1p$61EZDBMdz!VF6;B&y3*B=+0Z37nJej3zpJ$71TJ`|3Sbl(i*TCW>!87cK7y- zjhLw4cn*KE+ExOyrj3A0u1HD^HbnB3AdPJrBd{9QGb-A=Swtenb>{R zpdo#^@*8>AF9nMUriGc0W8Mo@(V7d2O=4&}sU4vfIj!8uJh2XZbp_w$2LtJ1g4^#H zX*qM$4(;(`PZs8BlN~2$b67RzD%(tgAwC5-D01irzlC}C(wa9fpcZ91t6?o%1H5Zj z+or6}!)e^)Er6lxQhz@l`0X1}Lsc+n?-FEaGhW@*xYWqd=Foq_*RE%TFYmKcyB+AE z+_ogleZzY9yl)RB$~f4r93OprT$p;nPv>*P=ctEAeCC`$>g%|P#3cR)>`OYN+s|m1 z68#5(YET6apV!hy^j=(a>Pw;=QKtnogJyjR+LpU5V?s0lE{qQj65sz zsHAk~(rgoTSE=~S8KL9j41d|WZYA+RLwtH<(WtxLsVwHTj23|h%o0-2ZLYxa{y5lZtRJw>^ zzy3Ayl5gnbti`5rNg`~yH9aPyl8=1Q&!I3m?bY%Zd3jR{5f^Va>@gp&kMf|_8}+B^ ze1N#IrGGd-{==O47bAiFhx-5C@!#L=>33cKT)JkZizD}@0L!RW_w_MA_2RR$V_dw` z#I(xlU<@pi_$%9Dp-H{FmOJ!ak^@%`I)mpo5mQ~$Hn0=s!n49l)qj>Re%*7pi2v&m zrAfw3l^=hFt^N(rft28PBKy_C_m48!6<^)>Yu3Hu%YT@4|Cr!6ID+XPVq{ebME?Jp z{E12YH)!4eXKg}BYLFk9uU;BO<*y{qo9rRt=@Hk1Rc;+KB(k+N6YDFbFOpJ+^5%+U z+-8o3+eow!+;MI~H&1=q;Z5&IB)E3-)xRHf|M~X69G#Uv5o4c~0;7JLrFsyH5PZRw zztKgiq!jn&amIJ0R3W`yJ=L5n9nI9*AxVEXIt`--dGRfF9%91DV&t(%HDEfx(POsn zezja%tVy*%fCE;X~geKT`5x7)K-_7CYFkLKT(^4Ye{ z20G2MmM`~*4e2Mz-PGkuHHKc*P>rq6ro@vxlpmC1BhAd-j@sb$a+uq*Yt6Q?v^VvH zJA5%SUWR)qsJ4ijzTSig(C+~6oK8deH;5Gz7R1dO1KR|KKuP8;S8<~G>lk>=#{P03 z?br=li^F|q$2!vd^nywaP2b8v6B~O29DCmmHfP$EbX^C4KfUMmnf2FIJ z_xpBEAvJq?Nd=Xa`Mp@K$1y4o&HJT|sn36$xKPI09d>{XiQ+$O>yJsr<+5Ggn!1z; z^Q~b3&u=_5oEh3*kE_*=JTT;J`Q+}nBl4M8f&AfCKU_tl-^h6nhMtIi+o>fCT)k_e+0mh< zBU0>-tP#obUd|URQx2Y_++Sz%#s?|(=3`y-6I>Z+C=eu}8q%~1>3i;7tPG3IQ8RAa&UX~BbDrD!Go?SE?)HfM6 z*BAgl6Wq-F>ty^F2+sJoy0$hdxKhhrE7!lWO`&6#7O_H)oUR+F2Ngl3%exyi#6wEK z{bKtK6D#vlFIELM*$>+k9#NERH{2UA#YLIP?BYyjuTN5<_nC{A5y16i&TJ~$9qxJc zxycy4O|5q@21nml7(PAQBzlQb4aj~5pXrVVp@Fk1PaH6x)V&m&mJ_IbBlx3t1K#gv z&cye6QVY2j4f5RbOWkH)9?VK&g1=bzv!^QxJXM3{IIoNi>FJJ8Ru=SwY*WxsT54>l z6G<}h*t0;^YKl`apRh3&oWjPJCY!>38lFmhRw`%L(Ez7mpobM7P!rwk7I!bVMnw84 zDcT>)ZCA{#Z`Vt@UA_HmFOo2_JCGSdbhZFP<`@|(bFauR>*;{=?Cje5EhoQS#xt=Z zk8#m4fh?6eR2pP(E&V+onmegx(`z5hTA;M{{eU6IirS&^>Hmwpw~lJ-Yx_Q_(;~$k zTHI-|;Izdp!6jIWOA;tvx!Vy%2QeIkF!H*+tGW`kF5xOd0QsaxF2molA&}T{`-GmX#YeGyOc|QasKk zB`iPsJY(N5IR$!zsAu-bdSz^+C>*i|qWxO!xsmBG(5YgeKbz^7_b@r+HjR*c(g<*{ zvB0WZSO5$d>bjR3;sAQqXz$rW&5_@5_bje$WwO*%wXc!#ak?^WR0Z;rmt#IH_<=b? zQQ5D)StTc!8Z~>x^!vjn(D(fz(@8OQ%b7DX)ZV`%o&OY|{`Pv_5_=r9tPj$L-Tb)! z^*}&F!Dz5m0j#J%5=-fX)w0m7(<3{3y|`|E+~F{ux@ZgZ@Q@m0VvF(oO8h?UeT}M# z&Z(;n@_$)tG*D{Uvy+{M8T3ajU!(|?T<}Vdznb$cO1^lt=pZxr1}%PX?Uz~Q-@<`^ zl2-Iz`@)G$y`HQm&=tJtbHFYCVwdzfFB^Cz(<%QR?#!@f;MkuG<}qcQ&C*L!alsfm z=(r5a4-W0)K2Ir&%GnmH6Akim>Y4q{>v84HjRjL~y#2ri{}mU_O?Ju+stDV21Vb&hJI zV@g$VND;#O)fNpoD3WWg&DV}+r6IEYO`VtT;nOA_IGY&F?&W;@D%J0`@j`EzDD^Jb zLgJN|rD8H_`yNXPBjknR-vM&jf7=fIGMLm)8~+2Oe1lnI^Ey{z)b(qt3*7fHX9mD3 z#oumXRLDiIA8#&-PDH zkFP!fApVK9du@G*Ti2~9Wn$KV{Kv6Xo3;0!bPWF#5dWuP!Whw93zQy%s%i9F2RNy> zWEzVw9(hxwb)|}XDTQ+sV`sj$uaus9X4^!@!8POK$4fWoN`LZur1Ta=pUCxvKRY&`ia? zM#0lbWcXDrC`eK6KdtDPMx=z>lSL~`^(tFs_hst?`iaeNBF|xdo+NvQMpKS2%`Bdu z-hKSLTG9&%an1LuuY@lyJ}$%9#7I6eQ8$oUSF+$fLN&jYK@Rpx7}V3+{*U(%%t2{s!5SvsdZYJqc;fm-E)Pt7m>tT%?B;ZFBn zc5ax5Q7&@P{B|}p4B}mj(`vAz9*8c?Nr$zOe++D1*MZhVuehryDlj`Q&_e*}8t6IcfBZT1Im?B%9@P~~TUD|AH-4^wSNaKd6LSEbjTo>c-EQtfzKzkk@ zb;Mx3NNwGAO-kJcj&o!IBP|uSa5w;NPlyk;mC@W3aJ%e~ZJ?T*tpaK@yx9GC$@u3@ zCcZoO>y)+#ADxQBuAlIv&xfnDYj^7T=@UMo_mslB;_~|TL-HMrRj;LW&deN*!r3uL zJ{DH-ZbAzd!^#(4f8c-E8zX%B$KVYS2ij@$og#9;2j*Xc?49cySRZe8=FdC9ea-`8 zue*b+GTtlaj5g1Ef+fnXluF-`=e@)ruzKt#@KfZ7LurlNG^tTK;fvnIK5J}_R~KLe zWDBDvr^EOy1Lm`XvCT`Rg(Z#N+!?EA%(Y$YIT47oa&Cyg>@`%Q&C~s=i)zwT7bhr? z^%e!Rr~-5YH z+hxb_<~yuv44(eaY}Cgdh^+IsdEH)SsX*sBszpemY;sBDG^Y8!^T=lFmVMZJTNvAX zwXTUq&5TGr-EaXd>Asi$3jfl}+kI3P=olc+;`LqugdAR3tdWNY_Nn^tVBKu*KP!6c z=S7z;)n6XKFSH%@rzfsx?b*i2a`2XR<1Jz1v>MI0g$SyLd~nxrL;YPthx*e-2aQIo z?hCtqXq~Z<$bh2?lLO2*%G_iFTm+HP)_53LcQd(29?n4re%d^*^iOA|%F~ z>$HC>Te@;4Y7a2+RmFZ@1H5!Gm27n@Cg~YXAl)=-k)kry0f~9DRBB+@l%wL}Vf0Z# zzL%?1-#E=;7SE^VmLv}h)xw6B->vb)pa+35e{Wm0n=l>b?IsacFp(Xck51IM{H^{2 z{9Y6|JV^OFK;aV=kgTbxbNT95=HP?MKlvcBqulG)CzebG#e+Nck{4`Z4+#04)ef+H zB)tsb+My5fEkLIq zot>kGz29CND+mL?q{a4Znm1_YWP5-Kq^(qgxR!m624ZsFGUc7~JF3^)<|fI=LVV9}0%^N)4bV=yxg-D9Hz!phg_ZXfvD!Xrt34iU7b1_Paz zS28O(^?%yf`j@lc=9aiDk6dnZCkh)UHBiZJ=VTjcCxAhmDvxXRjrK3T-|XT-8+)FD zuK@5`BLjuLzHDWA11>5(YTEXuB`#N+h%Wi7;`I=u;(hb?<=iBr(TP>LFp^W8-Xo-QX^zU_Q87K?$%E^jbxOP9wlYd?5SyXpD5d?fGc*aeZNy)rEs ztGl3T&o_pF>577Ss7@JJ*Y1d~75{WCUfh~CN1NoK6J!*rf^z6L!g>iPI}h_-?1=&d zJ&W~g4ZHB&m{*Y>i!Gg5-II?1LoS$-T}DM*06|gS@0#YhJ-rya^Ss?gFYZOQZp*Qx zWVv#H#WH5Blvgd!svgYmY_FAo?&CSr%jzZ5B;(?(KAg^mVQ0aVt6mCyIDz3US0LiH zUYSPN$TdG*Vjnuop)R<~TAL1%^IjdmpAG`u57~`;wYbZC;5*6U#PPB}olIyP$esX6 z1uUD1#3x@>yRz>vh+(YkTj_|Lz<5Zs#~D3TXkiE&WIF}_S$yReUA;hzZ3BBBFj_!X#v>1o-qEc$%?oa>ANykKuYI$Od zthz_h**QI4=9hXUA`gAR3s&dZH|9Jqnm*zQ8~H z=(ZV`8rIdYvp$5M!75aL-ZN`2UGG-$%7v7g`M?V_ zD_EMT(*Bcn?yh%+?iL$kt zN$9j-N-*Dlv&rTC5m8*`A>8vj8#S425~WK_@QYF(i~LFNA%otDna4Udng^7&`+Y}-z@Fe?zl@h44ZQ4;GWYHu z1g;A#1Y`<9!d}fqjL$^kWBm@S?^Q-lvjM&3$8*k*oFZb>ujB^$RO;ii zXB8Wq7<_${m24Uu0N&(^ZS>iw1*@_GyYHP@rdP6!~^O8Yny6IG0KMeUq8>`}Sx<4~S zY0OVwvH$6$JmEw^EgzfehcqT~?Agf8t%;Q;@N}l9PFiw|QZ_2*jSRJ#`lpr6Jc5m1 z)<$Uz3)xUr zjMZzGD-Rrq%~=a>%>D(C=khfF?pFvl1h(1+o@ zWc=+2Upc(76dt5d;r|NZqh{vLsrr&RXC68UkzForwQ><{nvHoPIIa~?jXgG*X$Qhm zJF)ZA;RWU4^tk~9H}B3Gt$GQIRMSyeWr1n|47jP**gjbPvjtN+MzocTdt;o0FXxwe zAucvXp=zr8y!WUiH;5ZdXMUe1>F zkW%I3bFfk3rNpkrqz&>c7M<0}aIS-gay)E%r6iP0(AT$72U^=&JTDAU)E7uM$s{MwST%Kes9P$o)Ye>N=gZ8=Z;Y0*#9w}G;wH43=g0j> zBRI}#(!q+`D-5*9en9lzs;O2a&c4SfFr*le1tuvK)Y+Y6@VYmbg4t&g2RhucvI{X zX{8w3W<~v8GjtIGmfOYx*i@0T^052W8Lt<|BS`WTksKk;!*j%_A zB&h_WpQm4)4Y^74(Vx?Xqd=C|F=0l-jworeJd5g|;rW)3Ue9dS)vvI=rW?H*$9y-P_U)#P(c>F4} z;{a0aFLWjT&dM=L&I>(hh0zrh`Xly6k%f}G(TCZ0C~Y3`!;&@C3b0F%wet znS2_Ipc; zO@4y7Q#Evi8$9jnZ+|W&t$;Z2b6jlw!48Ai6&T2N*=L1ZlDB9T`jS$lK+Q5yWMtQ0 zJX(^;0rFNg!lkTGP#+s*J?ET=k|#x~K1mq2dJ_jrAxR34kx^EjVZDdQsyabQ5yho3 zDLOy4LXsG*T@^dv+o-bnu^;znC`Iy{x)R*H(ww$t)U5jB#4T}Up^%^qf3K~AY=y6! ze1#i>#AnIkU{yCSOqYz={WuFhVb~Ko9@6g&w#m!jxUQ;nApGm>#!W6w7cmxelg)tL zR(H=W=TPOjeL+EQSi=#WS#OCOo%K^&Kd0Zzn}>0^{;m9g7ipho9?Krhn7H5*Aqnyf zZEUM%sqsLGFB0Fs<7E;QbHfWT<>Nc*7o$Pe$f}Q|@omY`ZDa{~JGiXEw5-(CI@EJ> zbBh!HQHNQ9U1`W>$m~ItAl>t<>cJMP+?D~lv?pR`>?j)gtIp|xSTFuvN~Y9sUVzlr zY$I^ic`!LAyxYv2Vv{Qu>`PKqGw#o6Yn=d!8^#Fb$gxF_)860}$Lx&0Vs5gd0IUqE zpb&)|h8d5dW^*)?liVV!C_kmNB)GGeR+N$>B`%bz;VLWOnm4Wo4Y>V_SwYM>Pk~us zV^G^f=R-V+qLyp%qdO=qB-diwV(SDH!nHMK2Hn2tk4@fJF2ANn=4td2bw#`5wZR)~ z3v4L{(qGK#-qmkqJzB8Mbu+1|tWe#7yH~w8~T3nn@h*R~xs^Lp=7W_%`{eQ#c-jIZM6Z#odv8!PcE*8zB?jHO6`sW z%y*BQ-@j+MROx#ANFw|6P%Hj@U&JzBnW|JaG^@N?{&Y;okQe94DkbUejDDz){C3P# z%O>S*?Q)gH2h<&|_hO7$2NCj zUo@Krk++;bV~F#+I5}KU)@gw=iJG_+Y0CiBwV)&Hc4Gap8sX}@PTgoORk)#_1WlwBez!Qcheh;?G5n()J6}9g)5-ItAnZqS3rVAWYYWC5Gq9EF5M}-KLu@8CyXKS&wyC_y)*N zP0``D3~k=K&ZvqmI17$x`p*V00R+|x-w#1fU;IRJ@qpU%gN%_$1t?10po@XZN6D$S z#(}x~nY_(6p;CjPA85X<_v*7IYttVO3@#!)tbpn-tmFJ*?6^z=M?llzb;PLm@gsY4 zKbJD9<7U}Yk&;jYyRCAWSuP`zz89SeuKHLwl4QeUrkc8%Pa(7<%!R|uU6-OO{-SC0 zU%Ew}-jsSL&wTi?vnC*M&91%?0HT%_V!<~{)G6vFCjIuVYRIpn9q{JaJ!Oj1T(uO- zgv%$I3l%DlH1d(60|UgQngBHup|*flBXog1gjd_81KD)2SGRn_+2%e}YBW~vIL*ea z+Olgf&->Kgu&nn`5!^vgxYU2F4n)@(>>tMau ztEq_6n^lDBj3W0$&}L}$xIEVWLB&WN_XN@a1dR{EaXPl13r|-EaQ=xuGlI(WdSXIx zbz%vN3u}zNkZ(mfDi`?{uJx1fAe~ukp$A;2D-4EQ%>60JWeXWLu9sO;-c^(&2DFRb z-sa*#n1}Ct?~JV;cVqe6xzg5D zq1vu|{nmA%`8PS?6Y%s3nkzOZ+PvXrfSuR-Qey}Z**I;z-7&B*1siM*9@HwSR{PMZ z#s6C7on=$1tv^HR7E*E#Ecu;8lHm56mJak0<}vHg1bKeOjm+AY_%skTtq=H?T%yPE z&!U%=BDbu0syo7GYRM1Z_l3Lar(DHg`?B$?gS($%L&ATE_exXH!v!0vZlU|>GVljUUVCiDT z$4ESqH6hpR;oBqJKDYcj$ZU#4uU&4vz}D44aQW_sncIbQhz2kM8l=?^9`CesB<0vS z(%EP=X7eh4+3=W!43@uGYjd*Sj}*A>*QPac)tY)B6=1CU$@uiAPlgqm6xfRHg!gTn zHpPEbL1DQXi}2=k^P4$7O;2cTECwIJ%9j)Rqn}rH)+};qm3+?YV@Lq? z+nN}3ROF$nc}L1wBPotZ|99W@a#n-wihaK2{^1a+m2aN0GnwCu;q-Id$v;++S`Jr> z7~T3%xqB>RAU5MZ-NUG08K0e8xGB_c!rpaCbHP;E%lk*Ia^1q~4mCyXmD-1m)ejaN zEbx$~`exVe3QJiax4+LM8uXIj6wUX;GF@lTH`~v_MPo6MNx`3xp5xqx!AIUMhSWvg zL%9(1+LKdU>&(5Ko5^>aR~{%I{dwP~J?uXQn^{kLJqNh_O89%+fgOKWRi_uj3);rq zPnOrsR)TnTgmbBfH2flf)sXNB<%CqvDbRtw$WG{=n;$JEoHYZnKKgUh>0W^g`mc97 zuQ$UuP3dI=jtpVCG9oUGUY4l)`zc;+_$rE5)aJGqj4?pz59jyP)j%i5GsyppNk9ZI z`~~n%#TWm2Cp43C$^G`DqYzIDl(pGzcIR~S*-BNeuA8B`-jLN90y$x$cC=3wwR+g6 zlc|BCxm> zml}=ET=iE-32*G4d1i0idk{s(ZlEo6((pa&{p^mkPU5p{ymRewY~JmIbTxTpdd!P$ zme8%EllY|IO8s@4F{@Zse&Iw}QY#J3#~mqw-g(Xg#js9Z-S*swhsXDbqOIB@j^w>? zOULf6y5v>dy40~(6}38H(2)d8dmbyQ<6%|nAn0919g%;$B}ng(7XgFy-eQ}HZPX3( zo2+jD3^4>sr&Pr4hF}9O!zyEYX5k}6N1`HO=R0=ICFoAT!_`1`FICJ(~(*9Pdna%`VxQ#(cT)U+Z#8#_JoSMX z=N=0-_GOe1oc0#*wli(o{VhfVPX4??H*8uE^$7ErIk8C-Erhk0$BHkFy4%bnz!y+G@aM>@dC8lW*2MJb;* zAb||rCrf0Sz@%jVZf~iF@R0nJKHFz~iiK-N$CEUS$d<6xb(rn3&pGJkcbDJfiB(-Y zD$uVff_Wd29l7^wGvOKgeIHMhO6>~j7QjfFTq>lqr*KSlc>213bpBm$(zg!Rnbn%$ zn=~~4Kf~ug&9DE@eEgpT-2Zonrn{DR=7&^^Ueia}S?oy_hb}ESh18+(VYVX^8 zwKeh?wS}fvEVkswJb2Z{0^f=PngH#*_sbc?iQ)_YBmFY#-@asb{;e-r*1v$Z6#2IS z*Q@jEyLA6nGM}Ls{&#~n;XnV*;O$JX5p`I-@>pmj_`_Kg@c&ijT$$hhpVB9Ctt_^;*H`Yb3ZLy3^;eaRvHG(-}PD+dLOsa5qKp7j*s7j@tL* zk*8u(RPhEv+1dy^x{@>9Cd*G7kxh*rcXctEHI{k*@@{HDMd0`yhn=YJ;)Gh~(e(n& z77nmO6_=pEm*QuhJVxwetkxK*2|bE0+8V6PY%hM?%4XK5W;4bx22iltcr!ry@|8KD zqWI2(_pkcoN6Y;6k3%jT7ic}#y72>4kX1Ym5m*T#kw!A1I#>`-ha1g-mNrJ9Tx3!| z(e57Ov(Vz20=kfQYHhBK@yy4Wq})>hvlxS+sBd2ZCi0eTCO<;%yXM*<_Gf46e<%o1 z@Z)^VzDq3{iKbQa=ehMJC1+f8s$Yk(r3JqlZfQ;L(}+-C%OImKewf-@&{e-8a{ zj3YxoJwZX1ngUjN9bj$|eIl0|%N7||$6u*g=j6%c{KU-9-=jWJHM zF-OqZ&-%V6*@-n={(fz!T)$3K^MW1gj7}egPf{TT* zc~kl_Y}sFLrA=Ly8mdFNc*fmRydiF_YHU^@)%xLdFTQT1-wg1&yI24Hvfl8$Ueh;@ zkMG|vLaGqIHs}kB_2__L5$}SGk4`cKI^**+3^FY5)@b)H1Nc$GNx#*^8Pgqmu~^05 zW%c{y_0caE1RG?sEN3_2*~Zk@n7>3IdltQy*CU;RJy%jv0^0y%1_ z;L~MnptYkoW@dMhK2JS*_Y-26D$4>~^g2Ms=0j%EZ&diwl^lRTR|x)xiz3!T$5J!5 zz6MGi8%hrYdYfE8?0xQ^W~7EnglphJr8Yeuy@X!bnm{+fV+{L2*wmYYzWxRBKztl@ zG!2{f8M{X1(%Und#iMwQZ{kN&%u1BmUCtaQK8#56OkI>W@L5Im*3S=7jn#XX3s0N= zKy-C$56%LMfF(b_kw=7U;$gzBk8H+iQw!1*zOb|lja8h90o&fj{%gJP7(O1wX*T)%kB4VcgJPSA>R?N7lnn@ zAN8vS6k~=^_4z)@Hb|ilI7^`T8P{K-RKe2(&BpF@J+X)!4UsH$RW5!WM#=w+Cabxv zx6~P_jA3{Sjiu=bE|J>e#C~<+NOVNMPbW>xhN5xlS z1il!3`zrRG;ppnz^M9BDNf-QMhyM%Zhn20z^uKkpyE{jJai2aFXpj9@%b&CRjQE?| zTFj>dOTSz88|GgAUDp6-3Aru|Tj@CY39zNSr~SSx!hTj>4rzqL<_D#EmZ*ED>=&u| z3D^v8!reOxf&}{gB)M90ozaq#eB?2$_8_0CFlxnn%SMy;L6Y@T$#%!5Z>l2V$7;+B z0{F4d)nb5Ok+_oHf4ZTneHK~SsFHzPLj{?HH%*5f2UGU^I?Qsm>@5p~W4p&^L)}2ks10=8n=GJ~s+>@O z72g{Z06>3K;Fx_R)+O9}yzC1feG9U9EOEp_Q0>g*6jv5^&RrnNs*Hv>7rp`+EXyL# zUvPHoAcwdCYQ_uz_2fB(W&`&KOS>QA^7?>T6+4h)6@D5U7Q z^MXEkyxqegA%|emv`!XuuNv4I*p;#}87uA2$WBOy+~C>CT?HSt*pDx?XWA~MG;}Fb za3P+vg`-56C>C(dapLQ(jSo4>83Rp?XsQkeDo0_YC0Dom=Y88PnN*b@x%FUpuM9qQ zZ{%Fj-FsoNf21LMzzD)iN2A*>=4~AF{&1(Y!E*Av*n^&CHW(_wjgbX)Q&|CH`fRQ! z%6d~@u)fKQyvc4`&5tz9c5+Z_1}69!=}8T~k+cbqkT_?FfTnR z>KCC!ah-OuPsUCVeS1Qt$5@3+=iavdG|sS8%gXjdkH-6RQbV<+>$u~=9Rs9 zOP;p6*tX7CtTp|Yhi=!#EuqUpe^b<0Hvg#+SM1IV4C;S#Xa91LlefzLmL1e+DF=_` zlB9QTM|d>o^3OoL|G}ZJ{;P8S@5cX2 zC!PEIT+dQt|L;B7|9e^_^@E(kU-31Nzt;%wp1sr=upoKpQLJdlFI=7Vux`4-$M)j2 zimJQR)cCKURFt7u_ZO-EB##$pIQwJgv>E&6!Yc5SpPcTyxTf?IaaG6fAOCrKfuE`c zXGNoPIKfHio#Rn{17*CUwuV%hECxL{Q_?&SUiHpnuc`L2YpZe@w<>pii4FX@FKhT#d&+&oSLKt9U5GT%ZsLV396v=!K)rVl~+Y3 z3~qhy0aut8Z7wQ@lg09rk2JX-rQBns3F@{T$OZ3#62|_$ZNOT``y4TeR;qHcr`~s zht<8mmn5jn%Ph(7om!F{##dD6X8Uyh-Tyj!ICq5rO@Ya|ig8TH)M*qI^2V1m&N8$5 z)HgmWS4w{Z$tfkOWH>fD7$_GvieKd*-C`qD#0K8pd9y#yr!?(Q@0l78b^tzkaOQKs zF?r_!)hWO3?j$RbBN0q6YWmgFG4JrY@ML71p*n>;jF5bJ0hlALcttz!)Z# zq08v;AarlBa+#$q*M176%`m**$wxxJYEF74LXp{4VyDI@g8vG)95(++54ZXu*{XJ> zo=$x?dZn8jg}9uhZ$t8WxAbu5K=8oS5`8-+7~m3lhkh?p_B7+fsoqJs=g01Z6OX{q zDKArc?bIX{byH9y>`=2WuJ0no#vN}3upQz}yI}4fd9XV6SU|h~{7DFCwt9WhRL{KU zGp65+z(+9cqunX4bMAXw5X3Lc^`$ns&F?08cz+pSk--Gk16glD1p8f!9=MqOkPLDATPg^j6KgtW~vVJ`7Z`^&qGHO)^?Wb1rcKL2%RD9Xl@6NsB z`KGg$X$F;c8AEagC8n0V8rH=_Zg>{PKhJIMH`^gcbg&X_AKWL2x<@#>f#&5!@*@P9 zdDavR6ksYa7GJV>hA{p2{%I?99hGQ9)knuXwyN6Cu-Q}65f6pWT>D_-lX9^po@XZ&wu8I!SrDkn5ea~-0 z!8iC?HV5VYLk&7+;*R8&94XG7BZU-u7t^q4E<(WyhJA>*kI?~ctonGP?K<(e{2dG6 z`Im3B6h4$~!mW{MPdMS)b4^1dLnGX*M(Te`P9|Z z-0LJ?&D(4u@b*&5(s+pp*4%Bq&4FlFoijKeU+5b#yPrxSJ3j!{x18<}21oVgTttFb zjU)HYwNA;b_=t|N8R)$qcxI z2@P8+iVZ_(GE8#r0Q6N63y1%zu?j7p)mA z^aR-++ofq_o0r%oXWCvAd~`r?PDAqZ<_jT`=U9?~$xF|%Fj`%9Q8vLYxQ4C=SkMkP z_lW~Zub7DSWOLI3{PEIm8A{E@lLU+-qy;DPdOFZAEBvIOS-V-=#=9?tn6 zJN(a=7hc2^eOEkUG`xJG_}OQdCYt)6p5*rG=SqLQJv7|o4LM%N54u17M_m_c1#S|W zSv2*jvODhOhEuiK>VF!nr%wZ{efnK0wi$vjX44THxQUrs6<~3M#S{GcQ706BKoYzz za$>Q8{ah)esZmRI!!&I(MReW#WUZog(yM$Y=+B?*zpkFo8)K+pA(~TXF z?q34=HV&%3=Y9YrmuMe9u^42Ld|pbMPi*$*(;t2-zrqU|&W*MG)7q})p~&ZOxRh5u zG{#b{wv*N;h}Y$cmxlw)O3j>oL;xo4o_%9M#kup6Y0euRMO%e)n;T;f?_9cfUPO;J zMNePb#~|5S&`R6@%2(X?C5@@0Hy0(Y!nK9uI=h>vd7hiiFJI$zfS4w-h9TWgsy0b| z{hL2hU8jCNr`|{ympXJVF9uHf=5v{0i^ZU?rdZTM-}8;O=*viyX*i<m|7QoEs^b%#kswUeXXS^i%m>&GvyDUbNb zvG9*Sy+AIB&MWs*39oG?_|s}opCs}2$$qVV(5CmkrJ~_ZO^k5htwSk_>t%9)uE$xH z&aPJgqMxp~XAEIvqRJ59@_ zFkXo>JZg#KXicQLfC9(q46qG+brQzY+4@82>drGTP zDS?7{g+6shR_$(a&Z)EY!T`t9HNr~IN>KZ@bg7gBu23rV)wI$4<>6+FIB32Jl;QBd zr`G)=y}RKo;Yk?jp?W6f(Vj6d-8cO;@mtIg8-8a-Ec;U_|DMKFilbYT{&|DddLdP} z#k!o()m)taeqgZE6$ke#WkS>F*P`9URw2PeJ-d(vdQRW9ZnkGP`cxhd0^M4mI-jQ$ zkoNaR=Z~PTD_qf9cJje_rC9qU$V*R2JSr@}Y0xz}%hlzu2lOzNA$O zn^2desE#LT7g)@-w4$t|>rxvEtafj!+Pkj$uCz2gjs3 zBp5l0ufCLSR7|dZK4g54;_6T(-H?&LxoKIYu?Wi>5&0oMA+7X%sdXn7^YKFcN0FO> z#|KLPdhf!jSW89ZvXDMK>(I-wsglxGlkCWeTcG!g*Zw#?P$xoti^&?6J#wKK zAr#ut>*96S#@5BrPh1+q?JfmvskZ)I4|>8D^TM1=O%bYtC?{L&x|ginXb} zCD?Gc`u&Fw6>>|0#ND45rzjR2eN^?MYX`R~jjr}BNY^j&r!?GeYJhQjC3z_e37Nt14K|VZ35ogjvQKj2?|7=Xe z44l-~IRox^Rqwv?D=AZfR33CJLW_uNCTQ3CQ40F-ZQ>y zYBnRg7ZACJapNqvBdCzZrYl@j$RqK4{=jU8u*P_NWeP|V9m1R8H zE+3Ze{@ToT!DYv2HPZGQ?R{v)^f~42X_(?&#mpZSZX+Nm6CWs3b}@!SI{)pGY;M;?def28*O^;^ItUL z*h9OaoTmHVIh$uA)9Vez8&|h|aDh&r54O}w`-AKS1IRF?uSWX`N2mL)4)15t{ot^E z&17~FZuw8SH2fTXj+aN$+r|xMi2W&?wyY`IO!dlX)-3@Ge43DL7(8>1xg9inf2II) zsj2@8Bgx3W9TL~kTWC`z7Z;YdujrjmyMv}CtZ}}jA_@Kl3=@?R`Q4M~-B_%Bxy?G^ z&v-GS{u3=QE>iO9AjQAKCN9t0dk37J9#nM<9dSr1{5V}LRyZ#d6V&+p7(v6Bd%V(K z5+z75^`Bl$c#_nrZubnsanvz^(&qPY+aZXF;l@26 z=L5Vwv8vLjl?9QmAuB<0XJ0tZi!p6|M+3PcAc9IPuv{?mG=PzL_3A6KI}$1Lo(95w>sFy7IVh zv50h67K#(@6$tIOa{D#4fPE~c z(1yG5hDU%u@XQx@*Z=#nuup2k=k>RC1r!Yf4FiRDylr^nXr7-|v4{KXKSnME^jp4m zstCtMlRrTGdw2!HzNL;Xi2x_doa5)hwNDRYk9os)N_@&Ip-3mo<;91qsAHZ2{zu2D zVmfwXUnrKXwNBb1NON0rNwMR?kCP0-{AMza8;k6;mEa4VAnC_vJaBP&TB{=K2fJL= z-NEIbb;MtksSYTFxG7DUUsjQOsKeCJFi#V-ymg=^B?+YNLZomy@69FDe2Y?8yOe|+%w`D9G|iw3Bz@VLM)^RqFuI^BZ`N)%Qdnfsr!XW^p5$dY5*F-Sj3NbN9&KJad*Tu| zR5Mzt8Z)8!==6NK9J?skce!_M_&uGM2^_18Yj_G6o^WhCAK~qf_)YvqONFD#B@n#M z3%8s#sHs(`H;I$chi3+yX`;B>4X-+5Z+SfHO^YQS{*X{tfst1}A{aB}2emX}d0xE4 zv6EvjbR8W`wy7poJp6g-RnhHuyYPAKXtw3cbOBRB?8f{-VF6=<*Qko>cpjgLXJ?sx z)?9J&sc9cj@CW5SAhZl z6i}Rm6jLf_|xTWN=*Iy(=1m;)T4M)T+ii&m+ z)+IjRKgH4}9l4{OPzJXcY!f^#aBKySd%2_qtKMsCJTa9a4$R5~d#G(jY5A5E7H8WR z{OX8H&1*~xEiSw;+G(BG`4N@X2}s03=Q>f_aj-J$H5MLXkL%}|V{&4%=xk*X3zUHr zyKL{wI||yj--wdZ=CEOHg6@Gn>z#Sh?G+SxA%%n?^YzyX)k)z-O^NJYB2Fd{LNOabAX7A7`*R`>XLIzLN<>W_9U;CXX2gO)O ze#-}oH_#)7>ztURpEeKQG6(UUmG0b2`$6o7H{N8Zn%D zl}OA@{kE6>c=%dFJdY0P9nR81KSc6T)jN2@n1kDFp7hwM;blo+TJBtTYH_e9YT#^e zYab}4{R9u5D$sm;>;09ZrIfC`(O~v zJe?W(tQe=W$^i>o%scg~W?tAQ-+cG_bG>_MS2zDp$5`QXpfOxaXT;0fN8Qf7Or8kM z=Z4j+#$HJvSMztt@hrl2VREUc;PhLRLL_fdO5CDrE7!2gX3D#f$!IL{R{XX0P>!?U zG=9!jWV#1Cy}83=eMRnJsQE}~w85reYsi>fRIqk}AVZF4za;)FN71u?AXs+;C1z%} z%~uUD3W19aibd7PM@2CddorR`HP=oIXCh@*arH8veDmF}B=Uo=2s!)_h>S;M$ixM7 zB)EU9+ZGIi#_?e^RrS8>SV`&cJM~53iaH)r^kx8+<9_jg=IhQEP4pkmD>qQkF}&Gk z)Sv$H>c4@}FX#+%Q29w|fVY|imtFS&qU`m+9D@>F=^qJx^WP>})YALc`(v-2Js_3Q zF&E3*g?q^_UmZwZI3qMq!V-oW<}f&Dz}=GCH0rp=k}<; zI|_?nlTkuiKH{Y=$b(+giw>f5K4Wuz8m#0`5&2)(d+V^aw!Tf63T=Tx(H5srT#FN| zEgm4Wc#%LM1PQ_2N{bZSHh^EVQW)xuFD!| z6+IXXxvlx5vl=6)Zn>2h*CVWDx?bGp+Oia3hblv3Ez@o8?(YNRr-(ha0L?NCjfSPG01My5~bk$|WDcgjZ2WR95AFL;naKE1A zGJDq-#uHJlZTd&aM4_g9Bg(Q_X@Fr&KfgrEbgF%0HZp=&By}z-Ao8dA$joCp$Ic?h zTLOmPUrIKQHbaXWN}= zshvHn=mWY#1C482T0rKW(W18qhJbdL{vnGE*IRr~+fOAJ6BCTeUas0u1~4UvX?Mjj z8I3)cMP6_1w0aT!%)^L3Xf%b#G`&Vp$b01*G$U`Nfwt>RGKjO?ov!worA?om4Z!izT(%GiV2-nO^npjfjNxYyt+|?E zfl5Caljr7Znw1oY5xPQ)99jU1I?C-ffT4sx+)of>oPW9{E zyzu;>o?Igy#Gzd?Q0t&oA{azQU$30+sB54@th|#26(fdM)b{Fex+RT^sL`M`8cisT(WPzQ{wx5)*e|x;NtZXqN(Q}vrszYKpi=1z2@V`{DU);T>DusS7lO_t z{-8U}Iq5aBur5##kDBnalfsGq*Vcz>E4!q067DN{;h)hi1HaHM=!SN$mPzdSeoL!$ zuQBh1jP0c{Licog2@iaLFCL(WI{8T8xGc?ao{NbTg#xV)NX>1YaR+|w6zLOLFb@)T zF$bcyOdFAtY1|Xz)4RJexhD8|2kHgP^7gaKx_%)oKjv^ub1-cG& z^r2i7yf*P_MX-nlevOo3)nqgy;F+BmZYv|{?L7ofFe2xR{;6Mry$yQ)N#9ZdEjF^% z%b{U9Dq!-`425T8RrLHJSgWD7mq~eu_=Ng=*z_#K9}bdcs@7neog~Aw?A1pbxn&Qv z6WfSIWJ_9STB9CkdiHF0Fv7>wC8xCbd-%XFzf*lYQXpdOte zkLf0SLojRj*ADiyN;WapSO{y<)vEGF~=T!c;E<*O7F~3$?zSfZ^Lo@v5 zuMhlBN1UD9p4@t!H}Egu(5Lsn9X;PSZ|6UGiOX$dN|B+)cj#2hY0Tk2A&##XJYVj= z_}iOqr5ZI|t~8)iwzN%jFNN#;;~xot>PJKu^N&18b)su?o=Ie^H-zq;ZdMk$sm0Zs z0d*@>%x2rsK#e+QrI;#){2Mg(;9mWC8ZXa*f!3eE5Mw1V92i;AaH}Z(-AlyR<7b3% zv)GmiSDx-~2KIb>*W2o-45Ll+BJ@R4+;aOW7N!oGOe|>@22@AK_xTJ_;^|X!l37tA zg?re+i3Gq^p?k8G__(mDk57~hFoO>#4CH0+R^52I`7=R_;r*eBV0c?n zK`V24r$d%?1%R%%7;BOjJ4WKtx9C z!WUxn!16C7$#1>W4TwA7@oY5Msdy-KlwjcZ8^NK~m}qWu_XC9E&3G^JHve9rT+Eh* zO5rppsyGIW(%RiQ@%9)KrJ-{7A@qs`lUUDA8ZW-}RAE*quzNY}Z%3&%NF~I4MhH|(-4m&^+A&Clza1#75jskFNIHHkqTXz?oF85kxk!mZJm2=ipRen-Y5<>@sV%gx@$<3tjU zD+THDYB5HVZqyebj1ej(zI?bz9VQ^c+j0w}`(CB5i|&c?q(H*@dGfzjn6}!l^hW7; zf`IKzGiCOc%u_9yI0sy->c5MFtJ)VpR*19f0v5}vOIxPzD?F1bru>exB2^II&nV;l z9Dl&_{6j2!Wz$5`c~2l-Z#N$4MW&_jK4^&25j$x!n%Qo(avQPq5MPY*6`5g}OexLs z_bV+sv^#=9JB*R}HL4;oPRpz?VG!c)5QrL*|E@9rpCJ(c_ki;LS1^(PuUPcI9k@8_ zIo(ih#JVfNm&6YWa-LAK>YNOAdm*Oz%*M>KdumcPz`H%cRo2$$yLM;->p+%Ec73p{1?%gI0B~ z?p5=XV~Q6 zbyt#jOLll>maDslv@XrM8NP{X(k>W%6ypy*qZHulkw+~}$+`Fx&sl0qj23?cTfiuF z>nFjqBDp~dU21dQYRx{f1wyo)5YJj`WGgT~dSRHv#Uz0QV6CURmo+S^e#A|X1IiUu>Qb?S3L4fcpx7STRBiwr4A@6@4j$o9PiXI zw&s~$YLB%W&M8fn=>g_@sdS_01vU%xU&2)r+9z;RnPtcv^sxt)^%fnh) zZHb^-+Jee~^f!wS>FJPi@p2Rm4Wx?TnOPj9P)J8os%5`!_@}A&T2ua0qy5?;jozp5 z!M!58JT@4Wdj8>{Zlod!=Ui<;9eB-^M^X7qqY2vn!+E2*q5U;kD0*@}p~3Hd=4|N< z(>RF6uWGZKMVRKA;J|ncpV)&lTCOkmtudebjA>BfKIfsi%2hY2$Z5As;Ine?hvqMc zvXJa$t@NiAg;D%bVtOus%B`%Kq9x&O$;wFYlIQfL6)?=*xm6TGvBhfKbc1rc=R-|& zF`AWA9~`F@ zq8+~s5$08F1novZ$V6_zua%sU30v@f5>4?V3tRQ&yzkqZ!K-Zp?(L}REaGqu|IZBy?77Fu7vF*nEy&|j+9!P&IA~1ywhvXtl(o{ zcroXN5;?D@`-Gfoj<(UZ(Mrs%7UH9-f(t%0>F+Vyt2rcvTHH;bz%Zg$tTacvtU?*) z9@{8$#hX1v5D|tHNTY<~uf;sC%v)y39UstGcyx2;#I9o<-~2GIUBvU*S39 z@4$O2=6ujfHBb?h`^psbrnd-(eMjGhGm{Ig_=Kh}OOqmzGc(r*SLkXCXEd9YRhJ)n zUaYF{j(XH)hq@*WO&hP-Q)f+4%*l*08aO}QHA6mN!t1=c!v>iZ98c!z{37VvG!-^{MQzX*O zU-oU?o1zCvLI8CWbrQ;MQjZ)S z=tfeuzYWPkITX$^INx9%Zz%pSTfxSxFHk>dsPh$GLDVf=rJy!aYk690_~dw6xaJ53G!+;gBfFTtv)PsrA_l-X;a-&cy=Cf8^l*vBo0OKyRv1V z8F`&n^RbCajB@@vN=Cx)mHF-W?9xLC%HF)U1>Y~zj=z~uEra!gYM~N-XtY%%z z(cQbv2DK7z=rU%iljru5k!M@nqirgK^^A5_q=0h_KU7;fysWIP8D22%5m6X_LV69H zo(1B+ub@s=e%#9nMz}W$@kX;Q;oxW^uECfHqmsUjF^z+3Q-o=_NTfE-IA*P~;b*I( zgWPFnai0UUa8KhPA&QIYn(hkaZTL&DIViuB$ ztfTrM+si$o*@fqx?_8~`BvW;dgQK=FSjEdmeRl1OwsfNbJlak&|MXX_obPa84NPrV z#cq_rXy5(76PjP&``r>F{*{<0b4>Ndx)KFQS)YxELO(i~zl@4iPj8mZ?$uB(ZD*1M z6_EWX(FV{-rJ44NhXiqLLcXQ&AWDer$hiLg_q4f4>-~jk&|c-nx=DL2tij`_c0ks9 z?akthtb|NsFBkBp$FI4XeNX$*XZw6|C8^pYGNqkxfM{e9C%=ZE`iZVYv?$ z2IU=+=pn>IZ`~>@(bX`4nRucb>ADr_VzXxHTOAx_6_m!*`T-VlO5L?8&7vGyV+|D$ z+6{~_!UnK`7cAE@J%%tg|MEY^|2T&K`Y!x-;Ks=Mh_S6 zzWOFGsTz|#G3#TmJLOD5zRCvyu_WGem%J;1sWU0r61&1zi5X&RnnWt{!KZtVvlbqtnK_CgKDRBnxg=!X62x1sX>+TO6tTwGBq5yT z=e@xa{66v>NS{J&H~)1!4O#kK_oVG))tvNih;-2tK6I*7b7u=j^7GWRysxihclWN0 zDCg=lqJu_Xy^NDJJ1EIgu6CYY{|2-pjlNv9o+>wD=&00t0g;-Raz79|EZsc8h!j{> zPkc#PkgQ;3WqJf@f+2O$h7$+^)Dj7qA{j6^BB=X8=gIUw@l(f)l;q+YxO|V~~ag(yG0}evf$0!=$6Cx(w6PmwepLC%T?3{{}n=rf6Fm=g{=)le#4_)8-+10 zh(KJf{lB6V6q|H)mMLirJDFK{w(w$Wsos;1`Sz>iA(PpuS~^Q(b!_H!9c=UM`^$Hc z={C>R8YD$-=7~Y0?l*IoE$l3;ejwu=Pp^OrQa1*9WO_6t7N4Q84)H8XrL#iQrSzNK zlMJ_lnY^Qm^wu^|2KTXhvOVnn$26r8KH(P|L1tF*ctx85?yw!aCTuZKIpguTi3qP7 zC=a@NBPx5N2ih32DEWFO)WxN;A6&Q!8#iHFyYVW-_lrIjd1Uv|p}mL$s!liRk~Azu zWHr&IrZSz}2sd}82Tc8tuLsjjDu?x)Hl>blic*e9}j{dy&N zRw&dKhnXtrG=$e+rX_|#0$*0O!|RiY43ELsV@T?)^?n&z%wtDQ*R@+15jaHtP&g~8 z86!8c-n8-k@e@KITWNj~P^2796spfd5=!0WjiKs#*Y_CmIZe1qlL64_lJ&VQtC_(4 zJYXef?n%!B5<@}GO0-}r#2(k^_#sZ!XvCQN#6b?d-ekQXWbodugg>XxI0Nk?mN7K8 ze=a@`Mok=5lO?O)Vxzy`N=LI=@bZXwb+kLI)w!VksD)3=k#)1415E_0H|nW9fVMV? z&iaO}wKB|amSgob9`NXgXgHRu^*Z@TFVD>&A5$<%o84)0zyIQ#&ztwQE4hUY79G;Qh zr=`G`q%%~+Q>8DG;3FZL@F4DFJ?-9Jc^u~=AZxegkhb`@AX*}WuI6MRFXOG~o%HaGA z@7{Tpsu>vO?;Bg)FRvJNKJ8Q7Okp4!sM$aFEuxffhLw-=xlvxyagL|l6tv@^9=iK= zOwVI4Q>rgu0V2O7dT{xANpu)-C587g36yXdl9vO2kb~kGTUCkTIi|KF#=z_sTG8UM zqg%Wg0laNpESx;j`hcc>*@gEW+1EHXTV*p;)m^J%-4y4 zFXynVmd_t%OV^~>J6RogTnNN(_gbjhbo}`+| z-2$0(t}UYOch^IPtXz^3ht{9V9_kG>XiQM2Z{;R6NswGY{LK5txS2Q>A*Y^SR#FwW zU#g?amb1W)KdDG>4nsKfRozv$eLW)f5oRMeN*wd?D%P@G+r#6n-#TY?WOhBXZ=mJV zszQZonN118hqW^#^bl3&^m+#8Zyd!u9<4GyGKgFf8LT?tt7+1Bv=wA&w%B8BG;2b% z@q>p0nEq_gH1S=I{}ODce1#2-SeSLq-XmwqpNJbtEM-9h1B?9`Jv);&l z8*)Cm3`42f%wN`oc5#u~Jzm*D2)OR9F};2VX_3svIViQK6)NC-TBfUSNJ7*lD%lUE z>7Ura0bZQm?rSC+dbt!#1q;#+-^ni%L*+b}495)~(0hgWWp{lYjsV^L=D+tt*|XwT zJZkT3XYxvXrmW9&lX?pGbX=D!+j%7f4D0DRBDQ>i&N%yA>v{NCq3IdTP)4YcEPp&r z7c4O~t?aRDCgF>${cl=kiVsp{)B6Qgafys=Z8^)$!&hx-8|(c)vzmXky^&pTRz?@Y z8p!CFFS-n%6wo$AKD|9~5VY`I9 z{{Ho6=5u#H83?S`rPRN(?(C%?W7^G<=b?LKk{;x^W%^93qq50$9&vQvTtqF zUv{u*p~cG~3?lGzM+sMm&HOLZO#Iy8aO=NMAnxj96|)WpQ_l`+ z>OyYuM8f|9wexYwvccE3zxeNo4#wS%LqQ22Sx*~?zvu3qX?s=#n=ugz<@KzXk-2TI z?hx~EDzm)~djHcIQ<39LW~?%7)SiydJbv-OX0}!VDe0DxUZ=Gwo*dMVlOMLrZ}@(f zUMK{lY#2M|Cuyq>$_rLJXUNoh0v0PA>$!Vt24vvlXkzgbReT4DGLvCyn zd_og5Si%$?9W)AK26L7(`A;Nej16$zho_&YxoN#`d8*WrLW)=bg^48QeuNE>rS>iTI@v z0zxN@fPXF&#N*Ei``jX3I7$Pv@J!HGH*>FewkhlFoNRxlq(cFb3aT`UnNmDdz!TR< zZir1>AxLa7ZI^KlL##kv3>eL#Zo;@B?)14NY;j14215XNoemqiK9?NLHL0;iJQ{V{ zCD)1W!M6J&mvK3lCuymuY#M`gen@JVp!N_f|8sdEoC~pHY7-YG=!Pg6&M{@UzUCes z5gl(6-Z$im1J*M#S~ zAZ7Zp+*Ls(j1>Y-J=Z61Yu)2H`$b3J)58!nrQ;$>8v^I$fn+c0y=m=vc5tl$Vt3y#X1{a&mBU#v;ipn%eS6w|2t zI*Z+*8JLSKf)%ji>Wt+X8pfoH+MUkRb%PSIxZ3KAr6`->>6WG{`^*Ca6<)Uf=hcep zS}a7}s`3%uQFHp5Ee?@Me(<8?99|m|_P07uBe|jaAdyr~fJ?@c>nL#Wiln97zB#ch z@t*0_g*kCcs;wv2i>cRtds^-* z6~RfBj-TzX2a57=*2hO;Ee8QyQB`4jLt@bpqQzR-dGiMe!%6JykldF0DL?zJo_uBg zf*)EsG`h$JzGghB^mhAP>EIb_vdi?q;?(?H(%MbhG4ra#X!BB$MyBwf(FCmx4B$>6g7&zOF%&Wcdh$R;cokP_WmUrlhWaN zwZ2wjT|mFS1)!7YkqH}sDyAzY-yKpMkOB(&L?=8nqf0#1@YpZ-5UKp8Kl8D)1pKQrb{f9Izc{jB$aR!PsSj@^%S z6Sh+WvfdLlw_FY$SGTlt9+FX3pE-is>}2#Z)hgBt(;vhIrW;?i33ru<-CEqzSquO|dqUMmH-W z?1y|_j~f>~A&*)qP-_}MyyUg-{Mg4{E=-eBQ%^MTz{%o~`pW9){E){|^i=TLd+Tj} zgR))PJ9ctN>#@P#2-qMzD(B$JMw{}>OHka;qhptvx;fu$W|WaxqQDbj`Wedm=cU+1 zBgliO)UJK#z9Xf^JsoE+GJTsAjA<(yAAfU|V!Zm7WuWBj64~o@-v^z)CJ)d(KQYmB za@APhSlC&hgNN4muKBT6^SDP0>X%~S_HP7{;vqM97i;@|BPbmc@W3@HHw&aRmN|3b z6nG$o%I7@}Y7%pa*De@m)A~2CiK^|Ro4AYTj z%}eGb^x!_Pz13&B0T_sR;8_lY>L!cd)%)t`6RDUdwUK-r}S$C$yRDZ5^iw6!QxmLOdn36 zcC`8ZHFoYqseoUmJ&Ee_cPb{wpwrk3S{F)vKPyx5rACII`cNgzkCFrq|T+l&TK!5B%UO#Kgl2n%Y+qbYE%iPPaCiQ(hA6I!c>0{qm{M?MtX1 zoS|theeGXt)M#cA(rrjOMm-13?= zzvi9KM?3A++Pi+tRKmxqO7Y(`3+=oj+i-T=HbQHNq&ySoG;@RZT-#>?ij?C?9Uh8x zQ(4#ey)`b|HUut=uSF3srnHU9=u#&Sm*Qd@Jub-Nx6)GNj#R+_BOd=F5uE|BA+a`c z`V0%a$XE#X(`m2sYd2WRIhuAb&qMx304xk7dS_JsI;`?1XZ#f>zYGa%j}%Ca@q)o& zg7u+saauef?Pqg0UPSwGma8|h`>9TTS;I;Bq>L^|4_oO=7s3|16XQxB)`Vo%yU%^I z{$eEcY)vwpZKt3C$+=QnjG^K!m2zxRk=A(MqRLiU5kHa8=#U{I1&W{6_FML-B~V3;GWy8LD5?@aU$$ zr4)Pm&y@aeET`cJyVCWTV;yFI)QNC#tC_Sz_r`o4Vy(#C8k5I1;LebkY(oda81S+K z5|wo!H%HP`4?b+7GJl$<;~kR$lmwnGAolu%iC;ww4o-d~_J$o%Ug%3ayO43t^Jo2o z-CZBjBO#&|qQ_5_+TyL}wVaO}d{5FXX8Eg`fEj?)tLk$j_6}q5-w2YVy6gfOjhy_5 z-yxAghpmEpdNWf$Y9-i{i}x%dkTYqer(N(l_FYQ{9QTQV?~#?UsIn@dKCV6j9F$JlQwm`__Lj^sd{w6yDHH?#|z_E)Uwyw4_Z94C+nA6bPSyeN>VgG#yj z2wiPsNECwiB*He&|rwaVS<~D3}GKee5q}&zkU~2mnr&jTF!9nu-k@y~;Nl1ud zz4+m%^M+c>^|&3=O%|ov;^x$MA?UJKA^eD`?pqYVO;CyOYML(HM+UVUCOfhwwgM4$ zXx(x{9wQ=T@XqW|{F41*GT{xIIF!nW=4f3d&SvMx1DHJ9YgoK4qIOh4Mb5N-y&fNAkCs+Jng>G0Py$X1JH zeN?Dca5TTW%3i?ScEkRS63+{WoyPXe!D0vOrzg9p^60x;n(0zE+zCtyIM%G$9&HtXi+9;{0EMl$dpjqA zJ@%V}ZLs|&y|PlJX;`^EggEXU4`{$OX%-_)jyj;1YczOzn8uSRJw5FZ_@It;pIBw6 zK_)|X)Puy-e{hwtM%|$~<*jSfq)H`YR=$yh3GYD6kk#W`Pp_5VlUiM|mMbXva#m~2 zZzKs`o2leYZ5X8&gU6@1?o1|^uPe8DZpZDSTT0k~?26oZkvdyaaT+f6@AFnXt}bQ( z0PcelCcD)4tF=$(9uV;{D)TCE@{aS*Q@t0OtoS;Q^FTtH`CqO^IBQi6AVqp#o^ksR%(2H3?= zQ$(CkOr>D&xU=#0q*qS4_6 zSYsUPSH*1}9@P$tG`kkU$R?xXMPG=LlX@?h8h;&q&pImFQ?#%YWpnmoFvwayv zH8alx-GW0VKX@&Wi*c|g>Fw;(kF&W$vX5vtV{+R>J4D@8(#P%F@E2B+H;P7 z_VG~5rUHa6!V*z)Kz8_|B*rO}nS49x?%54OMHl66xtJoG4~9+QWKJe(-jHq zE#op>n0)aE(FEdI8d;2cEKwV2X5vXp03Uo`GL@T1bh2GU#OnyL^3 z$5WE~?QXPZcJ0T+!z4nQE~E^pQt>G&Ynr2Lfcp+sj}|3fcV1dC3;U0{XN!7|PAG$Gi_tFtvX?tm|)|Kacz~S2q1?&0Oja3rxRPOM9&%cK&mjpEnZNG-!JozIVe}qq7{Y!3c|1V{RxvD?$Xpca* z_vi9|!dh4Sf0VTjU;9WI*4`0Wl#r?mplchY8!1YBmyKzN_ktIeg;&)vWH2}$3epOcwx!-|H?Exd5vYnYj(F2~9fE2=xl za8YH<*?T5OdQ7cu?fcI4C{+W|T#!$)NHM30E>$(Z6_lYbZLMA8E5e7EfIlPZ*9;Z~C?=8ngE+xXCo+BVoO%_` z*6?+lq3xaiRloK?sLTq(yU3+}#efo<)@4uoz7!e#1e5Tn>=s3vugX2sM!ndFJBfaR zS79A|gA)3l^)|7ob*lP?zQ4450P@&+s#XTA9IRnDPE&jstSg``86nl2nFI}LeZb!} z&PT+=L_xtSJN^y&DanKB0mTExaYOuOyU}CVY;zZPTv}37`E)kY&}ihY!q9{{y;hE! zYQsi_8uJGMUC}0f3t3A6|7n}z_kjJcDK1pp=V1VbCT-DcTZ*Q6h%YFBWin@5WNOP% zU;|v#o$~RF$8cC~yW$tzH8x}>{nf$cFrV~B3Q0F7wSlHRQo4SzyqbQKfBTVzlK_Kt zmC6{_J3cwSf+NirG1|3PymN9Ulb|3yD4qACr@`*~Ttr!3MMjUB#m3t#EJi)3>*XMG zZ3KXD|D|hY;pB;4PQ^5L|Lm&vjF^(;A(e)aKy~Sq{m$dMmsvxeYY~@J3aZI%Q}f>f z&n@hX*cU$N;7Pi;nfkzZ*SQCGUz8E%Dqtg{o$eb9$iU~l$g5|Q9fQwr*NlY+FG%i8 z49@}Rh;G|xZpgfgU21ySuRpQ*f>h+Q1h2}iCq7R{YRn|8$=2Sl6h8u&B;}|K^?nuT zg7Dn=ZkzdIpvC%#FtbVNDhy2O&MM!YA8kh z{KXMzM{Gg5jiv=%4SW;miJ-01Ki(loCm+&@bsLQ&%0N(Z0ezO zlI&pw6jUp{WSWm9dgyI1T)uc38m;BYDOQ=hW0cGORh0@r)Rht+n;R7umBe_u#nNrkpLW+W>c>N)wQ+U6k)?s9r5sQL)Cv6`&XpY|F~AVmUu~lN0oh0 zqu2|GI*otdx)mgPh-URTJ9=)ZF%7ZJA+uIBf1Eb!z}L#cH_XG}`}LrL=7J2MP*kaH zLHBH5YBsU4=5)q{P-9W=05-bU)R`TWzrS?X^L1Z5B6w0vZp+c&99G=~%$ARqgN~c1 zpiqtma}RuaOc{+OnsxMPXE({FC764iLPTorrZ@*RFLnOV7U5MaEW4AhPCi(g5!yMB zcMp-Y4j@G&^$5Eqq!sr$!)@><+6V+tS|Lt8D{e*A#caQR0hbBa;*ttW`s^A(VNu;F zJAWT}Fvu=GadAlY0dT!yAGB<%+&(w%+5>OX)Hit6HwZGQB6Jfg>IT~GXfa8Q-KNh` zNI0%`cz?hUs!Wm%u`spTxtviq^p>7Kx0CxE5Vpga#F+g0lutdRwO=kcI@HNC=3RC< z|2(}5!t}Yz7>owM$4=U(QfnUQh!oC1RLnDXt4deRKq|7cS4i#vl5sgI99f5KY}JqF zf|Do`dfXUg*=AueM6WE$>fr`GnYikfRYbSFktMEk)>Buykil<#flm)&(ioo9P}oyD z2BCB!xf}7a>2-l)BpY|S21krd$kW~aS9@k`5?&PkJa81wIj~4TV$bZPU^g0C0e!#k z{mEJ6O4Ol$*u;iEK|2B4x_J=i-}iXJ)u8~ajnuA=m_`z5JiPGimbtY?1KvE-c$Xq0 zk-yp9yhk1It4`lf2fObT`JG%yJm$yeh9gt>VZEsZKLkAKM)mv>DVqFyf1Z@I+XltV3u*=Gv8mT+Yz^RW&u>tq zN}-<$a4tSd=8EiA$#%^e`7}uI4cxit$P`P($X7M8SyLyG46PO*rLOGXGY+XY>x8~t zPA1v>FxOyJ$cetMkzQV#Y_C;Q;7P^96x1&{OtP{1>eM}PCS#EX+>xVdsk=)NJEi#g z>nzx#+ym_8KKvYq4HqI~glJ6^OcZPx%+TT3Ac@mE(^h#9<|+%VK)Z_g;JJKFy8Exu z`Stfp-v&h^JNKCUNQ}dBW9qwSEbWp9W~GptitFpE@c3%}f)}s)d_{oNg^xDr8`Ym> zP9?f8cG;7c^zVQ*kg0A`M-Co9eQZjDy2o3nJ$b)uFG%7TMPeK`n7&2!{&>O@TD^+x zOfOvm9rLnTEpY2zEAf}x&k!S#t4DKq%7Q4*Yks7xfdgLK5cg$({I!KB@1bg|4mKI2 zLwr%Jg_(5Cw`$7xKcNPo%;L_24~n?bJ@41^3*X-(3>&^)$LFhQHat5X-EbO~%rIy2 z$Ofg;430L1cB9@D2SeoSLgK0?wmlp|u-rK6q%JQmE2|Kt#;P9zH;P~fFqirO3s6O; zcG^P0ZZd42?Eg(T6{D5)+_|b__q$5$WKNl8d}2D*jm?KxD%BB5u?Y|#v*k<&-)l9TA z8&%E39s+38o&CW>@JCg`nkM}Be`a8Z8K&vtZ4U5 zCdX&f{o^lENHV)d24?teRi~`zSi#vnjBVwIEkD5tT>D?3Fgc^<_wxpxn@t7#yK6*x zHgq4&%fB|vT2GSB%e_KmXeU-Ii||d&4h)F$As$V57@~3ObHJ`bu`-lvTb~6sG>KXi zu=VNF=}fHy9yOQhV_peJ^}BX!SDF3JQIG7S;hW!@!m$Q2+%uv&!?nsZR%UdsWRINP zp4UxhIKO@L3S~_cojZ$NoWO3CyXdxfMS~~w!8TT9&Ia$)K zgM|NP!I|g|94YK2aQ&i;OD5W0yS_|kmVpI6e%dhAGXA1xuTi{Z(Ex>Jo<>Rw|uQL{Sp~cZvaVOn`g(r&CPz|ptEi#BMo3AuC z;5OtaVZ`lJdEDWiA1&)m57L(4*pEZKEMbwVSXaNJtX{?$Tp*8dT$01+SqE{)k!5Yu z3+WkuM}6_%jaEut3;BM(JSNP;HgevCo^DGAx%nG`#oU#O_P(QplDh}aM0x)*ksiF* z2bR%NPG;+MDdN8R`sGEvW8;n5h-T~pr*M@ci6U;l|EBc(=S!_&fYr|mglRKxOfpWy zX`OYZQL4Jm?_sF2ZfgU*?C7#UD!EO3zb#ke^iT2920{@T)1~^?9Zh@NZYs>2(bgq~ zn>$C%p^ly|v=+wku`YK%#UrrVnbe=GmFDZTRR)s68F#i=8{mtX?(|~M?xX1Q6Rk%x z*_9X?9N!Wra6gPx=C#d_g!0<4r>o;NiQ_^$T@bIxD!eNRkT23GQgeV%?<_{fw*aYpNy%Y<9$H83^U`o-jn|?7~2~l91MqO_R#i zzz)}qo`UZ0dh%94_L^c{kl1l~y1r>#D5t(52aia8x}b1kSps>;;OG!M3D4)k>Vmj7 z)hjWK-fa;i-TN<#W>K1X#?V!@%v3 zt$`NSAJHR)8VW8l6Wpo z(={o)bNm(_753i|lQGZmE;|s~zTdxG@yN@1{PkHi0kLam~RytXZ9OylP<@48O^ncn$ z9q}CL{b)Oi&u;=6kMCPvGHK$F&E<8-;LPSvB~I?X>PZ~^ka693oyzpoDOH?+NP4;6j>5M~`Y&9O@uF-3iG`O;`|AVWhKJFxn+ccd)BT06l` zytc1jyjE2rfR28n0^X`J)L$b4))#2`ho!=w$15=R4Z%L+e|8dnW18UjwcIOW?+>kK zKmS8!5$gZL%p(6#`V=WgqS}KLS*AU%F53`x+YrQ~cf<9HbYn_WXyeh|xNS);Hi54N z@CrROh0>bhzUS$Wi9o278DEgy)a23t{v5Q`Jdfq?gtKlSjX!A4{6Y-;;-@5_`qPUr z{gdzEf6US$vdRflepH*37NaBQ|0?EFOo(%@mzs^#j&b^MS+fGg?VB6QeN+yihzN|{ zwAx(f5hnrp`eLPnP&EgBYcOSk4<&x~@E>aC|M22Zv^V~lK;|~%f0m)sH?q00 zFR$ka9sc~_-W+l{23g|ygNcY-H8)=Jqf@C?ugK^BERFx#f4{?TTz{AJzA@ueEqDHQ zJ9b)P>!2SR(|g-sski(N%UijJko@@3R0flzKFu%*A4!A0K7d=o7GNDeD*3(M1l)cF z)^E(r+txjdn5=v^);K(Ak_ke>J#ohe$0q}yh5Pj#Hn)ALFY~Y_J}u0l^4oSZ-CMYg z9mch7^!H)>?T_B{GyR^zyFczNF~67VlvY58~9hoe}u%8 z<3>N7@O)SNBe{REc^o4j`NhB0zg4mAc?-|IhQiy~U#J)dB7>OJ}?#^+#U*NRCwchySI2`#+23 zpGEUWCH$Ax^3PiHA9f`FESmrSipFKeyr%p7?U%r5ah8LAW75$-IxvsR{i26&rKlD+ z*?7ojUCMp#>J8{ZQa$XmY!ame|KjhCcX2tik)kL`{!6EwEHQ6nfD&l`Z|uDVRGV#= zE}R+_pv7HYoCJ4=7m6i7C1HZ|xsAm1DGYG$q+Kj*{M-LD*!hY4pDjqL|@ zYV-lSG+kmmc`)2hpF zr6J`kqMJ)*tZCL2^%7ZNY*K9ZI!cDq`l6zg6gVXD*lc192 zNkrb!tOz4qyh1Jl{rqk?%_N^hMKX_~XUc<<#rj{!hU8wAWqGkUsqAEU-o47Mq92!hqwJ)SE|4eP&y!X!`<| zd~?Ex__Bn94^woJUd^V_gGtvpl&}>ytXY9%xQqKKqEGn}CzJn#(8iMhSYA>Yl_~h% zv~eP$g-6NoBUVo0UxM_hGI`dmeJ~VlbI>9>DA876bB(W)zC)> zBrT!$c8c@Y#=_FFrJhgYS>IfLC|`g5*e~ESp_G8Nf^CUMCJ@>dRm|uc$#993!+9Na!uVf@MCB9q8@f{5_a% z>g`f-2)RDZ!e`%`!yYj|Kyf}J*gGcmI8^SV)Mg+lqW=E$O7%=eVL-y1bR&<=x^V4O z{b*cWL{{}k{#?&2zK^lfu4UP!5TgawUy{>c5lsk;lo=vB{lRWLW;Nrq=-}<0RHsp! z;D(%|$=GO+;`O87N2HZ|23wT6bH^un_{Q{eclz3+b(05pO_MfRHRQCI1kS-1e2Y!W zig>fsCQSyuJAQKAG4CM-F358k30Ob?af#zjC9SF&yr2dGH?x5YOI?WnNdG*ScGt9g zlOEKO;sl9vo;(lX9egs#wJ}Ek+vLjG=Kgb$d|Ey^?tN%U#2Id9-1~kwy!ZT@8h`v< z^O8(Nc|>%PK1sJ?R7vowCMvm@zURgE2p~C@8TgW6ZT*7?`Qxet${+$mA30gG{bLy zq^P(nTo1l>&VKxMMA`f}6fLKKIfK3Bx3TLPuX-K90Y~GP`0c!CIQR`?xnv2W1Oz;M z&i)WwN|4wJ`M_TEH1!<3X$3lT57rKc-A5(xJiYjSy_kbdo`8a!ho0r0M*5Gfzm+7hd7j}kEXHMQ-&^)b; zAD?*(@1+dxf5LDc$@TnO`d6`&yT32|8xi_HDt@YKo5oT&NSd%d7zkj0MRVbDkKby$ z`%9$h{a*U>gNskM>{NQvK8cKh2vwy8kfcDUl@EL0_l3zLr^TOuS_Ec<_X~OKcgEEp zpKhYP0IZ?$e^sNPcWnCIWmoj+zdZf;|DJS`@mEW2f2IB(WdC#0$<~*tn(v*pk=x8x zr;}CP)}fVeZHJDiMlO?1LS`+yS)X6YGqZBLOyj!p4@&;CKVHFqe-N^vatX-r@h6}% z?ln_s=aFCkLiz@tU1-=exVhB2sn?}!C2>ned+m#;pY|~Eh50wfc90YM5sKu&H=TnE zMm(3h)G>i$p0ZALHI=osxvuoFk)}6IsgXOmweJ|Q{-I5V4=r5;H4x$yM@-XhB6{Uo z@MhUD>gfYyn86O2p|4G)cG~Gv(E(4t+NSTU)6h1JJ=PX@T00LW}QVT-uTD z1DLx%0r{@E)ZWRwWnFa-3re}P+X*jJ$2_FvRp&yJ;j|CQqQs9_wNGzPDpRd)OO$0g z+7l54-r=HeCMznK=?66&SY9n_;+u;9lc^AuCBPn z-Eq!~vW81apW3%YaExGDNpr}#Z4r#B%h24#p%D_yb%F*sg72qr~pMB&eFi4DexH2bA7yd-?!0!Nt0gr_{ z3=QW(3^#k_@bCFl4dED2%HZyk7{NEp8!wL&dKaRs*?B*G{u=bv#m<|7*o+ZSs}9JN zHI;H1?US?lgkuq8ms6B7O$+q#acQ^c@e1dAwQ<+C+tXv$&0-q%Sb3?Dpklq*j+ZP|?gK63X* z{CZguw32y3p&VmnukYXx&j=_#raIT&fM_W! zhxN(Hj`9;e+%>xAF(r;MtysCh`=?L|2W68azG^7lsYGX6=!h6gpX;+t}0 z#|6rw?)AwplI%DK%ZQN5OW+6D)!J8 z^&l(Sb5~Q(N}ypXY}@H=lq4pRn-?AI(DNOl+o^X-s|BL$B@qY2ELg z4I$f1`KOZ=H~$u$93+mY=6?dpToYz38`(<}=EP=NjJ5*R{yt~&x(vf4%DFwv@BXuc z)SvW`x;6?#c6t^G!$BZHThw~u8ONyp4H;hV?^cJ{o#nY98HOPs%H45AsPQMDTjf~^CU#F4!_xzQe3(J$cvdvbnpoaeH{BHm0YzZXdF?Goe` zFR^oeumf4irq3_0+MBrRfYoVmu6g8{{%U?!%G|Zo6m!d|RJL8F`k|Y@=Wuju!A@2! z++S~oSZ75Y(_y`Nbjj41f0O4{lT4uA!f+TTc3pt?uwC301alKKGhwInPXEG{ZuiOg zBl|_HS3r748c!cxL06zsZ4xKn>DzBhvK$8q>s{o;O9+rgI#ApM$<~0;gTB&m(_@BU zww-W|BjMUqq$|EBGe*Xjxa1(04(VzNrsN7U>VN#j*t0=Ph_T%ZPb&+CQW?Qwnx7VZ zEzERtbt!U;-I&OKfx9yXQY!~Vf5zViYJ#P+Gf%p}UEt}z{R2z3Mm_`Vvi#L6{BPJ< za+>Y({{HEn(E_3B?#rt|iX{}CGJ19Qo@1MYQ5rIu1Oti7Basrx*%CA>(n|Rhgv+yW z>x@Iz_j^?Cz3CC5RumbbnPghYTo~1Z>K&I!B6}*Qc;ZZv2JHzB%xVhdX0F0;JC5IK zqG&T!kgD7tn|v25m{&(*1+Z@;jN()RS|u(>XV{lTnXt?c9V;*S-^N28yi;ub2_UOv zdU_DxbWUHZ(q;XF(Ngka>GROBh;vBnJ58{k_tC0y>3PJJ)%C9q0i$1HI#fQi<^GVk z`MLL?XrDeJB=_6iS-eHIw_Cl&9%i?_ObBP_n$u)KuGV0>nuX2h-x z-+EhY&1M<$r+S6_sUD0xG0XZ3B9~AhYNwH3``J4Lx?Z+gGX4nP zy?2mK@~evf_;!C;@mE{?UHcVB{gVAx*UX~%V+Xz3R$^fy*i@R839tpfd2@)Lg)DvtbR zKo{@p+d2B`&z9PHHSb#={(>d)MD+8Wn0?`*V-)*Xcc#@}wjGne;GB@V9V3(f`aI#` zL{YD7VUFB9ThX^Ng`M_3(yTHvX;~vDhOkY!)~2<~Wi&Y-BC%Jo``=Zu+=$<5y}rEK zI}$E?q{m(?G(kVlZkbc-2C@I%Hy9;ekYv3>IHszhJP$R1St5}asCx$}wD5K^k7M)2HnXNcy_6}Ti|yMGBc*N0{J zCt_Dw#zpQFRmouuan5M&%Y4&Iqr32Cc&U%-iQC=axMgl#z~TKI-S|)Es@@%*{n_n8 z|5R5}*(Yq&w=-$9sB;4Pc}(`)(~BLW_YC*Zvs%Y|O$&KHgiR zD9jYkj(zY(L)npHC`Gb*?TuOQtltTl{I^rMikQ&HOJK&ve_HgB5B`(CbdVg}*m-^+ zLAb%8#HmBBVmiE0lV}JqX5$~dw&8FMJ8e9eA=Di=z}`)TpM%#Tm>Bk586W> z-8Jwev{wbD*l!{7__Z|)-#X;YicqwBKd(x5;jfWGyH-aPY|iqC=kqx$yW2L|wm1Qg zJ~L#7;nb`VCI6ALo#HaE%lT8FP==jKSC zO+R}#`VNMNWCglT;;ndHAT%|_1Xv#Kl)qgRLIKv4pXLhTJk(Iu>M+WM=nHc(S}|+_ zA%@Hg>96NroKe~ z%kh#<%Jmh!&&*rYRl1FTrQ?_E#y>RoCQ?t8ZQZAZZ+|FQ-{^6T8}+oB)@i~M%Arx2 zk5rVH-VrHR!;jV7@8hz&T2u9&4{_B9DOl;1l^|LdOE+&kJkMQ<&~d8}}ko?)ygn=sG-)fY&}I?h;L4#izq37WeORu_rn zHEPI02O&79KgLzqAT}3TC-{97Td?>t6OD?fZJrfw63lPh&?pg}R#Nnf;IEEt4y4o1 zl5W3Uov~JNF1K~3*#g~1Ccsv2Tmq{=mCS9eE{6Kf`;Sx2SV`0iWcG8du@QcMZ)~b5 z#+*f%08RO}L4ROQvVjsD;NqIupkzDE3d@(j52V}JIGPP%^UsnSx?zcl*m|3U!=7yb(szeIp59^Dr% z31gE#0p0GxYX8s;TtP88d9_;F*;ni{51D34yMD2+ic=APUB>KNI)3WfZA+Vi+#bkL zjjo_EdB4U~k`hUxz1vKT$(N_T>*Pu z>N(BaPaxifL5qH<4;7+59D~w9+)dabrlX=8_j3m5R?hHHne(w_7kj-va4$p!5v+M6 zU~(1$0WNPX77N}SrPbCO4^j79T6IMOi~G`Ju{owSnXymRSIGd_KQ%c zcoDdxA5Bg-Cr2sSM>P{#Oy~w**q)`*NuivYl`?aG|3_2Se#rVe>)ZK zCPp(`ORfP4qNw#zlHeWIqK!S5GBrF@#gCSo`ru)PW5D6k2prFiJ5oi++V$unCP_Pn z=H&Be2rT+B&9$4OEXt|%x$wWEnHnfZy#_M53dfs&g09gd;$8BZ-SmtqgdcdLtKZvBNTW+Nvx;sQ zw9{kx2Jdt8F-5(vMWc7?y7c5mvQ5Q{kVT1~0jx{ue__rm+kik7w%0clt>(Wy-SoNr zC8xU;ruaST&9z`P|KI)v`!Cwz-CyYVB?A28PDgl;nfFhCsolvXX4kR=^0E!gquqys;+T<6hgBStY3#ZOwI~s&-*#pwb&%kx zS`z-TuNMVT4K%Yr0dHk++pJ8cWX30`hLuLR^l-3jI>l3h=}W~;WWdsLpYg+kP}ur) z73m6498*%SMZk$UcT=5-f|F5%cJqZ=4131*MfHk7M z24wVNo=iGW8(*JbPpnm&iv&M%B*s3!?y7#a&4;hMeh0&kyc$9*x4>|je`N^gF$RDd0q8ia_a;FDJHn&oo#P>HY- zK_USn&%=vjW~ngjxZtp%t|(Tu1*w(gaC}I?S>24zQLy{ zZavwmwgBR{lKD5%wrQQLPC5&R~u}eSS;p;g#A9O(~{=*p^w{fWWbWBql9_7pph{OM!GGIuF`^=@f1^z zr4F};!0O|Q$D)$sic;Jq&C|KJkK)?F3t0LwuQ1L}XrzV6L*q*%O|S1+VX@x#s0Sb9 z8cg^U!KggL`AyNA?N|$%R~UuDG54&x{62^cK15`P@^GcD;|08(UYvBLI%2d#bRwUyfU7>@E>bPa@d$WdA5sx$k{hsxed4- za4XV7Cy>rEF2wlmOzNRv8`F>1fV5w?{<6R?3;eRcFAMy#z%L8@vcP}41!Df6g?t;^ ze;K@7`$BUz+vGz_q!1=$tBM(>`8w#6M((}4(u=?3Bwe35t=W5QecO8UF^%|+=i|WT zX*^>;o0*A2xyR-QLG>M6A)?>Hljlg6^~OgnqX#}=Chu()Z5`%)3i#}J4D}>Qo34V& zIVawOFr0s9H`HjK+Igew^CDRjLVXNWNF)e) zG8w0j7B5XNG388d^2MJlxY&uwY@w$RzL3 zV!Nh3y05OIB<}}pc?~JLMZ>i5DRN?Cm&DYOaurtzpeJ*V*_HBjuF{oGIR!Y@30FqL zva*$|@%s7iY>bQeiP-9<3Bt0NxK~?dWfUHk8pn-e5=Qf3xVJQ6tyt{piXz~qZ^2*9 zX8t#8o&dmq3iQHXJ@x-~*^><66riH;8+C~V z`V_XkKvB*%J$8z><1hEw^Xk;!EP5w|vbXyzeYU({BLdqM4vojQ+=rZbi?oOR}A2)!1!F?jW){5 zO!t4j<{1Khsrz4z{L6N~8pK|mMlte#8b|>Ez_kC0@h(|#jYs_*Jrk3yRTBZXF{>)o&c{(e=lXK4Ie}!w zR8%WDa$B~9a`TYize*~@=#pWcl162n;gvlFlY!}yk%KN&F^cDEl&w*F`V>}kqUce~ z?En(}jQ6q#baVY6OK$0&qe)F7a$p2JFq$PF7R)d$U0{@(h;IBC5Y_Nb=WyY21Dzr4g0Ya3I}YF_7U_%J_(4f`3WD$s^U{rWrbFmp>vRD?BaDKkPBM z$M$WcL?}b#6J73lc=DTR$$-Awpb=@3e#gPHT1Vz))h+TE&Qscxs4N}_8du{2^OlIX zr)MRgHjX*Uyv_X3iX0dK3UH0@{j$V(D`rFqOFwzNK{|L;sdWfTl z%1f?fi0@5rXrJVs)z;{qDiMRTQMQAJx`E`kT7%pD{+RDjBSIT(WSfOvaY8;+pWYow ze&N7NLVWg4uYo)#h?5bN+VF_O2c6YlJmRCMbN0{dUH!x|vnvz} zlGx)Tm9i6~D&e1LoMyZ=(wdxvobkC@{id4%t1`_!!Jr(Q~=6Tb)WZWK`DlFyE zm2#R91(PQos- z6N7+h3Wgg(b^gAgTi;lnQwB0<1Jsg)?kx2ER(@CDZV7j5T|>`Dcroh{BrE+!qH}2Y zCXra`VZqeIby$M$VP7h|aGW&mou4#V4W_3p8&&VD+#wY$Uj43r>{U;_EX71tXSQP8TP$)p%jF z!z;5J7{;oOTPN&N$uRB=Y%w#adqp(k7pP&2la>y2u3Qtz{xV9({NFXog)I$1U znlFC5itFWsur7FZcwSAxa@&(eUhDSk@aBTS|mk9Q5COYS3U^ju_srqIr59l_QZ zl$5pRzzjaegLw@_hm50J5NC}LoC#VC!DKXuS50hrjUqNZyavRzr{?RstUqPcSD8c! z%_Ie)u+d{QuGW(9ds674ks8zn0O-Oepbyc*Zz zgrv8A-zXgH4>pU?pRn&zBw!+)DBeC$=vfE#TMxeO20UaSM`SGN3%Ou!^VZeKRGD++ zudsmEsZNzFbR-UYY>cQH0<>#WYLk-k3YuFrEUJ^MNu^a8RY(}xI{VzZYCdW;`t>+b zh^GenyCVXrUGt4rDe^3b1$elbYAV$t6J@DL7q1*3~$m2P|kU4t(=W#?uq1V zfdt>^LOJ@#hRys zOi1iqFbMN-fD;8%OTnYy4tJv=`Gwr@X%)Pdw$aL5H5yM~+vt_jMD`XOMKV^)CD_O1 z<|fc9j)$fxrEsNg8DUHBVlkmq8V4bMPkx*MGlhTz&b$+6&Owa<&*>g&mGZ*ffj95s zwyHXC(QQ2DI(M10@*Ez7Bjx(ZMpu!^(MfUs>CETX45X!xrfEsZbgwzfa|I%{KGWU& zP@?qmHSP4r%Tq_x#hu2{ar)^KOfSoMc&L0hOgM7DvJaWj2AeR$lgyMSCc@Haj@=g% z4CtH-0CD(5d+~)wP z@_LeZCFgf*y^z-;$zP=FT6`FMnZToK?)`>tR|G6mG_(V!7(y?ZCAWp*Zj#LxNnsK# zXp@x5KJpNKLLXbji0W%Qxj`}wSn~xMhaWT%3!a{ew0!OCEnVI5D zKWzo9;TvN;96h({C$6|%ck7|<>n<1Uf=K0e31X0{?boe`XVJof(swO)AS=v9V%4=5 zf0VN>xJZbt%2k()Piu2KzOEU>?k-ekzGy|*zmmUBVZf;cEv~Lev8Xcr1M|$($v!1A zGC3LHS%HkBg_EB8oIT+z)aloNcWEq(i==RNabVnsxGfAn+a{^M<=3T&U#QltwJ-OV zZ`^c-Y@5Xq9^L&Y9-SWU9Z|ttQ=otq3huJdR%`B@#^Ewg1xZ7@jU3FgmnR?E2}}?S za(Qkyv2C`|!Ig@2hwDTdcxYVQYNB}}4xh*g-@JTPQVhEsc<$KNSg zZKuA~UIo6IKeR4qan4^*2;Yp)G?5RRsQPLI&qUa+XMw;)kCLfJ2KTnSA$=waLV9AW zgSr?`TiNv9z-qy_CNcw~CDo1ftC>=Esl+D8LA2-Oz&oj+p|-ME_69{BmFv;SWVdGC zoQ(V=M%h%<0kJJ$!coDxj8H?%nPXp$Ekx(HDuvpBl2dR&pwI2@>(;%tEZl zty6kDSgTL*$q=0a^$YXrpuKCg92{egnMxuiNg=86EcOE*OrVY^(I>btNIt3N4UrXp2qyr%j-8jdY z#z@zNxf$&cuWVpDSkuJmPEu^% z9N+EI%JSP?kj+B;DU(hLxk4+KAjXE?APxad5s6b0SV7^C^9DF+|1ZT*J<7k@q z=YyoESIs(-aU7Q7sp8dm?qpeIhFAodpdIL_r-+YXS`J)C&*w~#Q=+ZaH=?cA9#2@k zq*GVR)^)J-lyc%%v7v>tat7D>lap-XAa<`A?-HAmfX5DTF)9O`s z8U$Ysviydu1;2D9A~-spy4e@Bk+-*$R|$NtlO+O?nXWp!4C3SzKN!Tj7#8B{f0z2`xVufxsi{}Wu<_-VMBvq7T9)xsyYE@lIs|F? z?xg6_%-W{R9QEF$QsBv)f;dl{{`E8?vKYU>8Y_ZZpqUxo*odlFw<^lE3@1HXxh&f9 z#lU&7DNpE7JpjT=skDU6X#2 zqMDD@(K@1zTSk^P`7Vu9E_KN!?OIQ@zG-f1D8|Z-z2;9L+0At`nBYp9wm(4QQs4qf zxgQ>UvzE50^7Zyc<;@xxcy98!SPo8lxG`#(DG+A12Buc_5bc*tlscq9FwAgn3=M~r zq#B(#h%6Ps?(Qmnz*N#Rijsv?2*QxgF5T5dV2vqva*V}8v;l~x)5G@6x{^Ra5UF*5 z$H%*Cqz)aV8WtIT>{3?OcX8iz<*w!8)wLBLUe>xd*PIm;D&B)>LBo7lDXK>db{Rw% z-VO3{*~rIA@?m=M$~RgJKQXst zV#2dtJn6ctTB8$IThqN~G60e8N7|GQ(5siz`Q~fP6{j??>>s-PKz@`VYi}Q{!b=9S z+b8=0$rky)b*z%>KU7{#Vp4f%QEE(;P$fb)S8@`*aD1!9>c$Q(NecvoDwh9&kwPF+ zktGW9J}~n+e7i}$TV*AZQm<4_v7tXx_VaONQptmOPN}VxG)1f>TQ&Zsnjwc2jPc>U z%BcM7AIFCjS#l1v>Tih##8#)R6`1P<51u% z<3?rlJo7}XxJglxm}^!+fTdX|d2|Pz+^wd_Dxt*{>&(la)KiSoGIs5Z1-mS0m6Vif ziHmwt!we>v`J?nKQltDe3k96^Y8-4gWM~IP%08vIbOFaA~3YxX_JHNL( z-spd~*zZhs15c8iVJg3Fr&z@`rm-5B(k(drg7_dKJn#m!oqKE?u{Ao-(b(QxT_LTw zd5=U;0MpcrE~CoGO7C-0$7*+muu{we3lD_{%pOY^p%v<>tBUU{xRyq2NqUvW3Z_Em zI=k4R9S+!<77fSvR6mYv_&U&XdT}sVONeR&I<|VdPrd0;iXF60%VafQ4;kHuS>l&( zZSS{`PEb&^FO-h zbt=bC@Rasjz|_!fDE4mnvdn>Ub#)g7(;eKEI%B1$_wNH-8~v7y@> z^w0(xSnMXT3%rV6cFK>5%|x3f``ahpNUcBV%=RY%Pe7bt6jcw+)5G82a=JV9 zs(BhHwoB1uaNi!+U2BfAW1Aq6oTgU@29rw03S`2=1jZjpmvumLWil|!)0AqBMgq0| zDV*G;wP@9AzJbKxiWJ7vL*6>$4~1KL;4WYT#-bo4#lN{N*|T>aCEm3Rw072^G%>`; zTJ&W_qmyHKlulm zL{FmxW}*V@F8@KFx~Ar$><`RbYqC0=N(jsA-e5t^R_feQHJ@*Iv!KX(g9d>SP25hhH5i?-2wCQ1vMUF z@^B0rM^f|+ze_!K-goJBZ!M$Y4i<8lqO5AZNF{QZiHX}vZnLu3mvM0y9y;AAd+t!G z3#BcQm~Mwn0z|hLd#7K!r-V@9)=MAg0*}k814xoe_QFnn$;)R_!fM| zXuJI>2UzkoU;?9iybg^C2pGpW-={XXe-Ee%d$~|tUy#qch`4Y6%rz_LHxIHEPX=T* zg=XED6=kb?&2~TY$w%a7QIqipH^rRBOgtE~V`G?DR=8w(``eVg8Mw*%IqC&1YmusD z#&zV{QIKJZQC45yz~=4HqrgD1KH}g0;w_<;sx9|oJC?GwKV)Hx`-IdNWmi*OG$+w8 z&(9tOb9rSe`5Bg{*D$XY3%AQyO$<}nJZbYm2$x13C2j6`ZB3!RET5h%tPQ_>QYaRF z{_L4SnK*pNpNfi4dvj7`e|tC`%!y0ZY^a=Afpa#A)K@q9@a{}s_8vrCwZ~!aBKHR3 z945tvsif8nhI%)dh`JE?$dxi&@hFf$(^z zJsqnU_2`a#4z3Z&sQE`0?KM6j4>Z|CqitHcMdQtpdxJF!ADjw1oW~6xl3rb|nX*q1 zgbkJ%V0!EaLV=?7x3)_rU-B>FNBwxW)IbfQHKS1}I=Vc`M~_gUjnv{)v}^OD>}7FuS`#V26p{LI9yv`0QP<`Ms? zxW;z9ivnUt;=Wj{mzMG7Y(|!yuCW}IfCD8jg1h6`99xKbj?=Iofy$Kcska$r_hXQ* z8Eu8Dq&+=FAbS^5m(+xa-gNm=Ct>q<`7|f*dy6!yD`KmGdC6fK3ZPnvk-~k(@gn)E zn7YGJ-?7meH*r6^0y8sHekSqSq_ z2`t<&h7Id&x=5QH=7=|?>iuocE8irc4vu67xK?l$h5306zH4mKH}IT9F)<)W^NiPk z>-ua}yJt?DSC|_jhg!D^ZNoJBQh{M{zJxeZj5d3are#$MqxNq$@YGk|g;fgL@L%F` zma-t^0|bIOQSC+sp-=ki4o$6)ZSo01BSo17nX&d0Zdb}oTf7%4E7K)}o;GnN2dl0o zeZSw0=OASi8*OxW-UzQC$S1pg6pF4z!6Kd;r+B4q>1nYkjaS%JzD;X7PJU(Ig20|fU|%lCsxDU%G!K0^o)Rl^ zlet$8Zj@>`=OD#sQpX4cO6Kzxi$DT-je{64f@-)ZEPlkNV=%|%{YY#c4$jS!>X!>e zg{?B^D+g*yuJiA{xRJ5-i3=x=v4W)TJEmQV0MzKK!rP(gEDNL?*bTz-Uw#^gkSqDPmc$QRg-h z#NO{@k#hn0&K01vpm#!s9x|`fuqbfJ4_*B2HL=!bllo6X|RRl%jEN;Bc?7^l_Y=l9VOvSFRm~|Al z#&E;a0$L&lIOFrtFTnIrQ|^|+-Kq2`RP%14NjsR^ffB572gYoF#IU+rH$Ku`_cge& zzs-6$w0Z_v(^KUWA-2OgtANNfJPngLp3#yKwm^N_;&;HnZyMO8+lj2~)_-ZP9}o)u z(PBK!Jgako3r=mQtoU~8%_x`JW_@d|Cu%73!kJMUy7_4iO$ta$2_wmW}p?fVrY-HyPY9uz1~G7?UTY6?;l7ltrzRW%lCb5UAD^y zi&|$Gs#UpmTH%eM8KecV@XkvXHas%iEUaRt9}ux<4fuQucH^ zT6-7tneBde<)1KR|BKH1U5D;0sWPoR_kJz?m^u=l5n%$yy~-8j=iZ3~YaJY>0=H&K zTx+_#a*^D*tc6c)X1VcwSX*NN`JMc+h8qfw;OS|%i@~)v>*?G5#Mf23t=_Gm zY{|;iVSU#Mba=GzIG`o$g9SEI7=(+_YgwxnE1#x2tCjRP?+|mU;-9E-fy}?Q#Zt8} zTHpi(uIW2ix!hV@6Ec$r5t$>CiQ}%GI6LDB?U+YVtdex-c5-&f`9y%r4HaGYTk>13 zYNM`VsZ3p4(Y5VjH7a<7o`Oacu6Ue;iq1o?0$VFKYqE1%Bo^)iZSSFd6mE2*>vDe<0ai9%%5G$pVutkt-DAUkv+ zjMwV>!=5Y_0J(AfMxWv+Q@erwcI-S-N>B(~Wn`)<9b>xn#lpuYv_1VHozGIW-==Ve zJPNj?S|ZZxSmny7;e~|_=hk~pN$m;PmaCVKojzFwUnJq6maqX8Qc~?ukhd5`HT7|i z^A~ZHzprTzQ6EG|co4x=96`BfF2`NMTmT@an`s{MnprOW{1Eh@-h|fG^qg}@$jsYB ztumbdTTZD@P`);Ev$n*8qw$i%KADQvXmnUYa)7Z>d_i8K#23k#QLs5x6F*o1)a?)eCznDTtg3l)j2ED zqi`{%Co@}9!g5F!^2V5S(cx7(2QsCkcaE*sghl3sV1H~uczvm7a{x?fYm8hypD+H>jX~i9d1IEp2^`0}iHhxdq zn*z6s`L-C()XqNgn=Vc}5IOA!FeRzrzi2%tv(0rkkbsRI9)R7$c^^*5kJW}V-J$J} zM#u(-Q>x{p2@qYvCot7hv*X(pX%MKZa{XZ0aHp*|{qcqZ=bNoakAl$?_v_=f8TOO+ z{MzXit{U14z+j0mzfFEOxP|M4cBQrX@AO5{rs(OKE+3RLAE%b!Ov|Gh0AOM*Q_Uv( z5qJ__{i$fOyRO`|N~^fmun$k_`L=1&p0@;_T~;E*o;={%U&FnUD#lQ!&nG`L_nRTh z4CuRT@Aw`i6h6y*6&god&i)`N!eoH=tw@-8lL*o=3`cD~=8!*Ct)C^l;0Sup?o9Y2 zUxO~un=OB>qW%?tLEOj)I=+o;U`prmd7@8F+qG zN}VTdRKjmdBRbNuc$;yCP#-qIY5&zk10DQy6xd%EEMB5J^78RLMy3m7Ib6gd;0?6t zv>3o(0?KaBeT5z6`l!%mX)9HW6*N5*=19*(zMqQE>Jr^{=M+dP)~NB%59ksei*f%_ z%_L)~J%mGTj+SMchC>_7D<=(H{5+oo7Y0|!&ha!B! zfj4#lOlS3eOGa*j>TM~euzk!Oa|z;AO%?pe9=)4;e=b&hz?4g-uzU&hTK86p+$LX- zu2hKp_oUbnAGQ&BRXJm}q|p?4)4Q)uB`k(xeydL|zduh=`8#a!;ea%ce-nd?G}t5q zM>Vp|j>_=Fzhvun@XR75<#u@N5O0&aGZt*a z`AD3<=HWH6tioh-T7hJ~-Xu9<#5jkGrQ}8T;&yR2Zja z(@h}($G(Q~jTcVTn zQ+1&xW1hPKqiKu?*R2FQi3!?17a7e#FYJAT3E|I7m#pihaaxZ`)TKIo6U$fq#5(WywO^BU>lWSUd&A#o>E1w~fY z7IZOxd1T59;0Ap=ih3}P4- zGiI;$$ySma7)4k%u`x#LS?^kyBLdekalLIpjYOl=+X9jfpdxDHQpRq_)(XjpKfJ;^ zvzyYWyGVU1v}eTAN0_77#2zXbMUTNeCT6 zf`C_)7D#{;Lib7uA@mXuXoVl69hsPRJv5Jptz!Vmv3kG z$IkA|&g|?@f1Wev*E`R1-t(68K5<_Lwuuj#)9^f{?yc!t;^8HkLZryE3VX`-+FyK#PYsr~FqU*!1K5!H#XiI;KP2B{20b{_1 z_}Od{HZQ+jbH{DYSU$;{{g?jBzY;q>|40B4R~Wh9x{GXm5aPb^I>J;Jb( z%((c*2d~|*DLaY#9(!xYjy;IF)khKU*S7V%tULCe&CbyCWJ^^$dZzvCpkGm1@*LzF z-}diz-;HRA7}EhJ=pny?tKR57vVd(#9~!CLykvG#oi9J)Q+6N&>gU@7^P1T`WlsYJ z9cYTacEN>=@$;5lJNahxP%$mw)rJ;|pS%tP&xH6Vi>o z2cL6W>{Z?O(@wlSdqdOK(YBk8RvC3Stl+)y`4pm^eS;iywrO@K5_3mzXCQgN^U0-V zv)L0*jk$mMw&oDt{Hz{LNVN1g536X%3P;r$8O;Q)7}q+T+GDuYxm;hw5@L)76Zbn6 zcd_FoMQOWp3+sCX!Aj#=500N6FPMKqeI6_y!pk}YrWGNIReaJ5}mO=F@e=0Xy zIkhxJ?}XqNMDFQafN1A7Vch$gB%+d$hBe2VX4w>ous#3gzWYOZM#EkkX)jZ~<`tgS ztD=RJ+l9j1hpPvJ{ARzE?s2EPXv--7#s`x+PUQqEQIHJ%Dn<&GBB?i#jWJ1T`6zcp zm?)|9H`==)P@L!1!4d_o5s5{r0EpW$eoHW zL31=}IR%{3IpCYsKOW%zmiJY`@-TXB^EDtre1%Pp8po&T0IqL`Qq z+)Jmuy;k(sjJ^oQUOkzt)gbRf;fwQz^MfE~bomCsc%&OEwN~C#+#tkV3;ed6XV=`j zS*xiS$9USfD^vHNiy{hBIj2-UImL4F5a{WEdi1pa%xytg$yjoGE?#!;cyai4frpJr ztk8B4yCo*~Gb1qt=NPG;R+DKZ)w*EtB=JQZEs7mYy5m!)L3Y9B zxg({Bz?bV$RddK0czG&{`CCfTV2vM)JgFd347b&*B&ziE$%1KbT^Yx$X-3CRehhN5 zycyzmHMnMk#cZ?_dQ+dwWHueP7MSw7Py!vX=Abi836|!X@3XtHS@$5Q+Zgp&TM1>8 z5~KtgN>9Oox;X(!U$iNEE!VL^wh=kysq*Y%f2wkD@`NU`z+s>bTVP>y6L@?GKG<$Y z)zrzTbH}8D#FPaQw;Kr?p_SDT?rO%rE*+!yK@~`Zj|!pnLEiXfq4Ic9kXCMjqI#IW zdu4?hL13f4>!=1#$GLy0vcVImV>UfmeZ(s8@9fG^y}L|4*7r1eEF`NjKFkNYE-Nc4 zaSCI^<=g8QA5>R<-dy!yn#q7zso$XMP_NiKt?(hGw>nY z#iFs+_a%A0+LiJ2%(HWuCdl(ghF-3gB|fwE!qI{zX7(lQBHyl)wLaNUZjqHpuCr^~ zi5FTCVF!qlMX1p*z*yCjOZ?s%fDSG@FWitcXmeKZjMjEflVKe} zT;khymR`%rIJX`_74k3Rof*b*r?0-bT_latL6(WVsddMj87WLg2A%HW+4la zy}QXtW=Wdz7(DNb%*po4l-brYK!N*iYYS^mfVD;pxsjDbC$$6}nVjH6&sQ}G{tYJZ zFHqMxCg%~AeLyH`c65(Dthbp^%fMgqv0M^llgf&QAz{>vxb?`H+2tS`Mw|rZXH`dK zESb{KEQnmjKH)UiXm3xleO^J?5z8hKUL2Q|dpaAC>vdGz5OV0ma0kMvFh>?G_YBY- zb?0?uCAb^&9m6aGmNv1v-2X;lJ+Fm0qe7t!OYy46+p?-d*y$Fn^{HdD+~Ca@&KGJEU+x*okW9juw^l?ruzu^azk; zBHcm3shYcuqULxy(hJlt8-YZI3*!F15ARDPvGkhr-yip`5>+WDa%U+CNV0P#5}wpo|R}<>=s9g z^{P>?iGQ-Fl~)HFka_l^z-F0ju{T(~;EI7q^pP63l!Kz|*1VC-@iRjuW$UfSyA%y& z@31XHZqbxa3mTZ56?S95rP+|s9N3*ai>Ud%9*KaXiF@3a-lkEq_6z18LH9KR)SzFT5!96>WyVdpQN<+zi^BUvWYTshin9 z3sp5J1&h1&bF*0Z$UKm2)-)aujm zwVzRQpWST|dF0X!RLJ-;DsSe8gLQ_LWS;XdyuOQ=7OT~wPjDL;)y0$-qB!}<#3dVA z>>oXQRjvNS)tivUWM`M*ar3IvdaoVsZi#e=|aa9$G79FF6Y59$q1LjpOaXhlr9ouVmdaYV$ht$$B+DO zYC!bOj&0RbYguKX6?Fp?!b6Jz@n#aYNkIeM=X}ZRW(+j| zI#4=6*->P?Xkt!KK5IFlRCsUfqZnn6#~w4phM!k<484Id2rIKw%@dw=Y)3jbFT?iJ zV&;0A8TG^=-hop2B<}2aqRF2UxSF0g-N#6ZuAd?9Bfv(vNEy*?OeKN5WSMNF?&_&Y zQjAey`g^0n5=%Qxg#|lCF-QGsd6^@cqK?Kbz7KHHLx{N`ZB!^Gk?%8vDkA*l8G%{> zEH%}%I1rTExy;#OcJV^?zqO9%EkPD$0ProCq zLZ~Uy9iFXck6|mnnz;BIk9|(rjy6_Dpx=f!!gR*fm~p9^-$qZ!k^f;Pmi}%2PMaL255U9J?>1du(T*2 zhA_$qP*&))%oHqV(YoNhk?xMkP)B*p+ex?=UYp>@ zrMcV*g`6z9vZbNid8p;Vip6r^N$)l*Q`0}2*F2ocSAz{o)rQZX>wJ{vxYrA&xt6!I zY<}_E5^BuKF}=$CVkW^k@5jDZXthE}sM?AwUE!)ETl9VREj4+$n~31w{Ioh8 z7_(Bk&S2fu3^ZdYZY+0}X?-o?IZUM=DiGG&R{fQx=XeScXO}qfTPZa4Vd6&GBIr%> z^6-%%JJuWHqxCs*HI%bW_geTCSDE4aeXm9Ck{^5K-+z&q))KQ2A%XqLZ4Yv7ZItJ*S9hs zpKXcZVtvKQNa?#S_VCjvu$)p4aldIk6qeolJJfzZ8X}1iX_iA}^y>II`?re%gUse9GtYNegeOGac5RAoQrN9X8eX z`aVc{m2K2aiGI>9**3eVP8$jj4QGDq90G(`L{+F{N0%rnx0}A7s>Or2@P`+$bWN{J zSZZ=StjRJ?N2Q-v$fwofV{Zf)o;t*S=$QQs zS1jtuTSuSpP_7Bd@O(&w_C?4ckcVX%Yj@A-c*mMPr|Wy#H;_W)Gq)L@tZ0_|yWasp zFGn4*^B=MLiK5LCupgNo((IOVZ&Ekp2bRjW@kIN&oNYEPSAz!=>xTa3OXyNSyy1Xdoq|t7oTevkZ_|CO=7U z-I1$mZM6c*$w;dy9!@C(3kp@3ph1?sqPbMGTV}$6c4Kzo;_)tAbXeFOHtp{N%EEz0hE7`>(j>l<$23Zw;P9ttw|a?Wgap?rA!H z@%9hw_Wg37|7mykkKG;PnU?1+gLlS3xV^Z2r|a~%#IJ+@X|!MF`2~vq#-v}M_yvkz Vp!g4`@{0<;sPKQM!k&M|{vF_P74iT8 literal 0 HcmV?d00001 diff --git a/docs/images/bt/bt-install-3.jpeg b/docs/images/bt/bt-install-3.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..7790f89e8508c77ecd271e862be30f6eefd4c037 GIT binary patch literal 119602 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%0ApVcA!u3MGzMPnC;TAUKTB%? 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 literal 0 HcmV?d00001 diff --git a/docs/images/bt/bt-install-4.jpeg b/docs/images/bt/bt-install-4.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..38d7caee4cb0f3af3465cc80c876d281d5a45f76 GIT binary patch literal 162832 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 E!rPV}`l$VZ= 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�W~j%f+J^y0P#~RXzMC#Pn;Tnn(_!)$m%-ln2i% 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 literal 0 HcmV?d00001 diff --git a/docs/images/bt/bt-install-5.jpeg b/docs/images/bt/bt-install-5.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..aa1a7963cb9f9605e88a100c838f4f81d4f4fa5e GIT binary patch literal 75420 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 literal 0 HcmV?d00001 diff --git a/docs/images/bt/bt-install-6.jpeg b/docs/images/bt/bt-install-6.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..42359e65ba6e05e205bdfae7d6f6b54d7183e8f2 GIT binary patch literal 149223 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 $Rb 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 Date: Fri, 1 Nov 2024 17:35:09 +0800 Subject: [PATCH 171/352] fix --- docs/bt-cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/bt-cn.md b/docs/bt-cn.md index 7fe946db0..d55ff73f6 100644 --- a/docs/bt-cn.md +++ b/docs/bt-cn.md @@ -3,7 +3,7 @@ ## 拥有自己的宝塔 当你需要通过 宝塔面板 部署本项目之前,需要在服务器上先安装好 宝塔面板工具。 接下来的 部署流程 都建立在已有宝塔面板的前提下。宝塔安装请参考 ([宝塔官网](https://www.bt.cn/new/download.html)) -> 注意:本项目需要宝塔面板版本 9.2.0 以上 +> 注意:本项目需要宝塔面板版本 9.2.0 及以上 ## 一键安装 ![bt-install-1](./images/bt/bt-install-1.jpeg) @@ -19,7 +19,7 @@ ## 如何访问 ![bt-install-3](./images/bt/bt-install-3.jpeg) -通过根据服务器IP地址和配置的web端口(http://host:port),在浏览器中打开 ChatGPT-Next-Web。 +通过根据 服务器IP地址 和配置的 web端口 http://$(host):$(port)),在浏览器中打开 ChatGPT-Next-Web。 ![bt-install-4](./images/bt/bt-install-4.jpeg) 若配置了 访问权限密码,访问大模型前需要登录,请点击 登录,获取访问权限。 From c2c52a1f605f2eeeac1865a7342968a7cfc36cf6 Mon Sep 17 00:00:00 2001 From: weige <772752726@qq.com> Date: Fri, 1 Nov 2024 17:35:34 +0800 Subject: [PATCH 172/352] fix --- docs/bt-cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/bt-cn.md b/docs/bt-cn.md index d55ff73f6..115fbbd70 100644 --- a/docs/bt-cn.md +++ b/docs/bt-cn.md @@ -19,7 +19,7 @@ ## 如何访问 ![bt-install-3](./images/bt/bt-install-3.jpeg) -通过根据 服务器IP地址 和配置的 web端口 http://$(host):$(port)),在浏览器中打开 ChatGPT-Next-Web。 +通过根据 服务器IP地址 和配置的 web端口 http://$(host):$(port),在浏览器中打开 ChatGPT-Next-Web。 ![bt-install-4](./images/bt/bt-install-4.jpeg) 若配置了 访问权限密码,访问大模型前需要登录,请点击 登录,获取访问权限。 From fb2c15567dc218edc1ce4f6bfac746a25607fd9d Mon Sep 17 00:00:00 2001 From: weige <772752726@qq.com> Date: Fri, 1 Nov 2024 17:45:50 +0800 Subject: [PATCH 173/352] fix --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b9e994e50..94c5fd938 100644 --- a/README.md +++ b/README.md @@ -397,6 +397,9 @@ yarn dev > [简体中文 > 如何部署到私人服务器](./README_CN.md#部署) +### BT Install +> [简体中文 > 如何通过宝塔一键部署](./docs/bt-cn.md) + ### Docker (Recommended) ```shell From fbb7a1e853334a9de54034aa41a18119e4e86028 Mon Sep 17 00:00:00 2001 From: weige <772752726@qq.com> Date: Fri, 1 Nov 2024 18:20:16 +0800 Subject: [PATCH 174/352] fix --- README_CN.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README_CN.md b/README_CN.md index 3f339ea61..ccdcf28ff 100644 --- a/README_CN.md +++ b/README_CN.md @@ -264,6 +264,9 @@ BASE_URL=https://b.nextweb.fun/api/proxy ## 部署 +### 宝塔面板部署 +> [简体中文 > 如何通过宝塔一键部署](./docs/bt-cn.md) + ### 容器部署 (推荐) > Docker 版本需要在 20 及其以上,否则会提示找不到镜像。 From 2d3f7c922f5a3e52da30f45b67a74f0df908e147 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Wed, 16 Oct 2024 15:17:08 +0800 Subject: [PATCH 175/352] fix: vision model dalle3 --- app/utils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/utils.ts b/app/utils.ts index c444f8ef4..2e1f94016 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -266,7 +266,9 @@ export function isVisionModel(model: string) { model.includes("gpt-4-turbo") && !model.includes("preview"); return ( - visionKeywords.some((keyword) => model.includes(keyword)) || isGpt4Turbo + visionKeywords.some((keyword) => model.includes(keyword)) || + isGpt4Turbo || + isDalle3(model) ); } From 44fc5b5cbf44b7a362a916fbc3b1c3a34cc8e7cb Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Mon, 4 Nov 2024 17:00:45 +0800 Subject: [PATCH 176/352] fix: onfinish responseRes --- app/client/api.ts | 2 +- app/client/platforms/alibaba.ts | 6 ++++-- app/client/platforms/anthropic.ts | 5 +++-- app/client/platforms/baidu.ts | 7 ++++--- app/client/platforms/bytedance.ts | 7 ++++--- app/client/platforms/glm.ts | 2 +- app/client/platforms/google.ts | 2 +- app/client/platforms/iflytek.ts | 7 ++++--- app/client/platforms/moonshot.ts | 2 +- app/client/platforms/openai.ts | 2 +- app/client/platforms/tencent.ts | 7 ++++--- app/client/platforms/xai.ts | 2 +- app/store/chat.ts | 35 +++++++++++++++---------------- app/utils/chat.ts | 4 +++- app/utils/stream.ts | 2 +- 15 files changed, 50 insertions(+), 42 deletions(-) diff --git a/app/client/api.ts b/app/client/api.ts index 8fecf841f..1da81e964 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -70,7 +70,7 @@ export interface ChatOptions { config: LLMConfig; onUpdate?: (message: string, chunk: string) => void; - onFinish: (message: string) => void; + onFinish: (message: string, responseRes: Response) => void; onError?: (err: Error) => void; onController?: (controller: AbortController) => void; onBeforeTool?: (tool: ChatMessageTool) => void; diff --git a/app/client/platforms/alibaba.ts b/app/client/platforms/alibaba.ts index 86229a147..6fe69e87a 100644 --- a/app/client/platforms/alibaba.ts +++ b/app/client/platforms/alibaba.ts @@ -143,6 +143,7 @@ export class QwenApi implements LLMApi { let responseText = ""; let remainText = ""; let finished = false; + let responseRes: Response; // animate response to make it looks smooth function animateResponseText() { @@ -172,7 +173,7 @@ export class QwenApi implements LLMApi { const finish = () => { if (!finished) { finished = true; - options.onFinish(responseText + remainText); + options.onFinish(responseText + remainText, responseRes); } }; @@ -188,6 +189,7 @@ export class QwenApi implements LLMApi { "[Alibaba] request response content type: ", contentType, ); + responseRes = res; if (contentType?.startsWith("text/plain")) { responseText = await res.clone().text(); @@ -254,7 +256,7 @@ export class QwenApi implements LLMApi { const resJson = await res.json(); const message = this.extractMessage(resJson); - options.onFinish(message); + options.onFinish(message, res); } } catch (e) { console.log("[Request] failed to make a chat request", e); diff --git a/app/client/platforms/anthropic.ts b/app/client/platforms/anthropic.ts index 3645cbe6e..6747221a8 100644 --- a/app/client/platforms/anthropic.ts +++ b/app/client/platforms/anthropic.ts @@ -317,13 +317,14 @@ export class ClaudeApi implements LLMApi { }; try { - controller.signal.onabort = () => options.onFinish(""); + controller.signal.onabort = () => + options.onFinish("", new Response(null, { status: 400 })); const res = await fetch(path, payload); const resJson = await res.json(); const message = this.extractMessage(resJson); - options.onFinish(message); + options.onFinish(message, res); } catch (e) { console.error("failed to chat", e); options.onError?.(e as Error); diff --git a/app/client/platforms/baidu.ts b/app/client/platforms/baidu.ts index 2511a696b..9e8c2f139 100644 --- a/app/client/platforms/baidu.ts +++ b/app/client/platforms/baidu.ts @@ -162,6 +162,7 @@ export class ErnieApi implements LLMApi { let responseText = ""; let remainText = ""; let finished = false; + let responseRes: Response; // animate response to make it looks smooth function animateResponseText() { @@ -191,7 +192,7 @@ export class ErnieApi implements LLMApi { const finish = () => { if (!finished) { finished = true; - options.onFinish(responseText + remainText); + options.onFinish(responseText + remainText, responseRes); } }; @@ -204,7 +205,7 @@ export class ErnieApi implements LLMApi { clearTimeout(requestTimeoutId); const contentType = res.headers.get("content-type"); console.log("[Baidu] request response content type: ", contentType); - + responseRes = res; if (contentType?.startsWith("text/plain")) { responseText = await res.clone().text(); return finish(); @@ -267,7 +268,7 @@ export class ErnieApi implements LLMApi { const resJson = await res.json(); const message = resJson?.result; - options.onFinish(message); + options.onFinish(message, res); } } catch (e) { console.log("[Request] failed to make a chat request", e); diff --git a/app/client/platforms/bytedance.ts b/app/client/platforms/bytedance.ts index 000a9e278..a2f0660d8 100644 --- a/app/client/platforms/bytedance.ts +++ b/app/client/platforms/bytedance.ts @@ -130,6 +130,7 @@ export class DoubaoApi implements LLMApi { let responseText = ""; let remainText = ""; let finished = false; + let responseRes: Response; // animate response to make it looks smooth function animateResponseText() { @@ -159,7 +160,7 @@ export class DoubaoApi implements LLMApi { const finish = () => { if (!finished) { finished = true; - options.onFinish(responseText + remainText); + options.onFinish(responseText + remainText, responseRes); } }; @@ -175,7 +176,7 @@ export class DoubaoApi implements LLMApi { "[ByteDance] request response content type: ", contentType, ); - + responseRes = res; if (contentType?.startsWith("text/plain")) { responseText = await res.clone().text(); return finish(); @@ -241,7 +242,7 @@ export class DoubaoApi implements LLMApi { const resJson = await res.json(); const message = this.extractMessage(resJson); - options.onFinish(message); + options.onFinish(message, res); } } catch (e) { console.log("[Request] failed to make a chat request", e); diff --git a/app/client/platforms/glm.ts b/app/client/platforms/glm.ts index 10696ee82..a7965947f 100644 --- a/app/client/platforms/glm.ts +++ b/app/client/platforms/glm.ts @@ -177,7 +177,7 @@ export class ChatGLMApi implements LLMApi { const resJson = await res.json(); const message = this.extractMessage(resJson); - options.onFinish(message); + options.onFinish(message, res); } } catch (e) { console.log("[Request] failed to make a chat request", e); diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index a4b594ddf..53ff00aee 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -274,7 +274,7 @@ export class GeminiProApi implements LLMApi { ); } const message = apiClient.extractMessage(resJson); - options.onFinish(message); + options.onFinish(message, res); } } catch (e) { console.log("[Request] failed to make a chat request", e); diff --git a/app/client/platforms/iflytek.ts b/app/client/platforms/iflytek.ts index 55a39d0cc..cfc37b3b2 100644 --- a/app/client/platforms/iflytek.ts +++ b/app/client/platforms/iflytek.ts @@ -117,6 +117,7 @@ export class SparkApi implements LLMApi { let responseText = ""; let remainText = ""; let finished = false; + let responseRes: Response; // Animate response text to make it look smooth function animateResponseText() { @@ -143,7 +144,7 @@ export class SparkApi implements LLMApi { const finish = () => { if (!finished) { finished = true; - options.onFinish(responseText + remainText); + options.onFinish(responseText + remainText, responseRes); } }; @@ -156,7 +157,7 @@ export class SparkApi implements LLMApi { clearTimeout(requestTimeoutId); const contentType = res.headers.get("content-type"); console.log("[Spark] request response content type: ", contentType); - + responseRes = res; if (contentType?.startsWith("text/plain")) { responseText = await res.clone().text(); return finish(); @@ -231,7 +232,7 @@ export class SparkApi implements LLMApi { const resJson = await res.json(); const message = this.extractMessage(resJson); - options.onFinish(message); + options.onFinish(message, res); } } catch (e) { console.log("[Request] failed to make a chat request", e); diff --git a/app/client/platforms/moonshot.ts b/app/client/platforms/moonshot.ts index 22a34b2e2..b6812c0d7 100644 --- a/app/client/platforms/moonshot.ts +++ b/app/client/platforms/moonshot.ts @@ -180,7 +180,7 @@ export class MoonshotApi implements LLMApi { const resJson = await res.json(); const message = this.extractMessage(resJson); - options.onFinish(message); + options.onFinish(message, res); } } catch (e) { console.log("[Request] failed to make a chat request", e); diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 30f7415c1..6e893ed14 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -361,7 +361,7 @@ export class ChatGPTApi implements LLMApi { const resJson = await res.json(); const message = await this.extractMessage(resJson); - options.onFinish(message); + options.onFinish(message, res); } } catch (e) { console.log("[Request] failed to make a chat request", e); diff --git a/app/client/platforms/tencent.ts b/app/client/platforms/tencent.ts index 3610fac0a..580844a5b 100644 --- a/app/client/platforms/tencent.ts +++ b/app/client/platforms/tencent.ts @@ -142,6 +142,7 @@ export class HunyuanApi implements LLMApi { let responseText = ""; let remainText = ""; let finished = false; + let responseRes: Response; // animate response to make it looks smooth function animateResponseText() { @@ -171,7 +172,7 @@ export class HunyuanApi implements LLMApi { const finish = () => { if (!finished) { finished = true; - options.onFinish(responseText + remainText); + options.onFinish(responseText + remainText, responseRes); } }; @@ -187,7 +188,7 @@ export class HunyuanApi implements LLMApi { "[Tencent] request response content type: ", contentType, ); - + responseRes = res; if (contentType?.startsWith("text/plain")) { responseText = await res.clone().text(); return finish(); @@ -253,7 +254,7 @@ export class HunyuanApi implements LLMApi { const resJson = await res.json(); const message = this.extractMessage(resJson); - options.onFinish(message); + options.onFinish(message, res); } } catch (e) { console.log("[Request] failed to make a chat request", e); diff --git a/app/client/platforms/xai.ts b/app/client/platforms/xai.ts index deb74e66c..06dbaaa29 100644 --- a/app/client/platforms/xai.ts +++ b/app/client/platforms/xai.ts @@ -173,7 +173,7 @@ export class XAIApi implements LLMApi { const resJson = await res.json(); const message = this.extractMessage(resJson); - options.onFinish(message); + options.onFinish(message, res); } } catch (e) { console.log("[Request] failed to make a chat request", e); diff --git a/app/store/chat.ts b/app/store/chat.ts index 6900899e1..1bf2e1367 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -649,13 +649,14 @@ export const useChatStore = createPersistStore( stream: false, providerName, }, - onFinish(message) { - if (!isValidMessage(message)) return; - get().updateCurrentSession( - (session) => - (session.topic = - message.length > 0 ? trimTopic(message) : DEFAULT_TOPIC), - ); + onFinish(message, responseRes) { + if (responseRes?.status === 200) { + get().updateCurrentSession( + (session) => + (session.topic = + message.length > 0 ? trimTopic(message) : DEFAULT_TOPIC), + ); + } }, }); } @@ -669,7 +670,7 @@ export const useChatStore = createPersistStore( const historyMsgLength = countMessages(toBeSummarizedMsgs); - if (historyMsgLength > modelConfig?.max_tokens ?? 4000) { + if (historyMsgLength > (modelConfig?.max_tokens || 4000)) { const n = toBeSummarizedMsgs.length; toBeSummarizedMsgs = toBeSummarizedMsgs.slice( Math.max(0, n - modelConfig.historyMessageCount), @@ -715,22 +716,20 @@ export const useChatStore = createPersistStore( onUpdate(message) { session.memoryPrompt = message; }, - onFinish(message) { - console.log("[Memory] ", message); - get().updateCurrentSession((session) => { - session.lastSummarizeIndex = lastSummarizeIndex; - session.memoryPrompt = message; // Update the memory prompt for stored it in local storage - }); + onFinish(message, responseRes) { + if (responseRes?.status === 200) { + console.log("[Memory] ", message); + get().updateCurrentSession((session) => { + session.lastSummarizeIndex = lastSummarizeIndex; + session.memoryPrompt = message; // Update the memory prompt for stored it in local storage + }); + } }, onError(err) { console.error("[Summarize] ", err); }, }); } - - function isValidMessage(message: any): boolean { - return typeof message === "string" && !message.startsWith("```json"); - } }, updateStat(message: ChatMessage) { diff --git a/app/utils/chat.ts b/app/utils/chat.ts index ba1904625..9209b5da5 100644 --- a/app/utils/chat.ts +++ b/app/utils/chat.ts @@ -174,6 +174,7 @@ export function stream( let finished = false; let running = false; let runTools: any[] = []; + let responseRes: Response; // animate response to make it looks smooth function animateResponseText() { @@ -272,7 +273,7 @@ export function stream( } console.debug("[ChatAPI] end"); finished = true; - options.onFinish(responseText + remainText); + options.onFinish(responseText + remainText, responseRes); // 将res传递给onFinish } }; @@ -304,6 +305,7 @@ export function stream( clearTimeout(requestTimeoutId); const contentType = res.headers.get("content-type"); console.log("[Request] response content type: ", contentType); + responseRes = res; if (contentType?.startsWith("text/plain")) { responseText = await res.clone().text(); diff --git a/app/utils/stream.ts b/app/utils/stream.ts index 782634595..f186730f6 100644 --- a/app/utils/stream.ts +++ b/app/utils/stream.ts @@ -19,7 +19,7 @@ type StreamResponse = { headers: Record; }; -export function fetch(url: string, options?: RequestInit): Promise { +export function fetch(url: string, options?: RequestInit): Promise { if (window.__TAURI__) { const { signal, From 4b93370814b41e256de7cddc6264705883265d56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 10:24:30 +0000 Subject: [PATCH 177/352] chore(deps-dev): bump @testing-library/react from 16.0.0 to 16.0.1 Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 16.0.0 to 16.0.1. - [Release notes](https://github.com/testing-library/react-testing-library/releases) - [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/react-testing-library/compare/v16.0.0...v16.0.1) --- updated-dependencies: - dependency-name: "@testing-library/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 17 +++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 5bca3b327..a036969ac 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "@tauri-apps/cli": "1.5.11", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.2", - "@testing-library/react": "^16.0.0", + "@testing-library/react": "^16.0.1", "@types/jest": "^29.5.14", "@types/js-yaml": "4.0.9", "@types/lodash-es": "^4.17.12", diff --git a/yarn.lock b/yarn.lock index 9f8aa9f61..16b8b872e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1201,14 +1201,7 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.12.1", "@babel/runtime@^7.20.7", "@babel/runtime@^7.23.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.6.tgz#c05e610dc228855dc92ef1b53d07389ed8ab521d" - integrity sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ== - dependencies: - regenerator-runtime "^0.14.0" - -"@babel/runtime@^7.12.5", "@babel/runtime@^7.21.0": +"@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.25.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.0.tgz#3af9a91c1b739c569d5d80cc917280919c544ecb" integrity sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw== @@ -2134,10 +2127,10 @@ lodash "^4.17.21" redent "^3.0.0" -"@testing-library/react@^16.0.0": - version "16.0.0" - resolved "https://registry.npmmirror.com/@testing-library/react/-/react-16.0.0.tgz#0a1e0c7a3de25841c3591b8cb7fb0cf0c0a27321" - integrity sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ== +"@testing-library/react@^16.0.1": + version "16.0.1" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.0.1.tgz#29c0ee878d672703f5e7579f239005e4e0faa875" + integrity sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg== dependencies: "@babel/runtime" "^7.12.5" From e49466fa054c702898780967812abe2dabd4ba6b Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Mon, 4 Nov 2024 21:25:56 +0800 Subject: [PATCH 178/352] feat: update real 'currentSession' --- app/components/chat.tsx | 2 +- app/store/chat.ts | 31 ++++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 3d5b6a4f2..a62021db3 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -1607,7 +1607,7 @@ function _Chat() { title={Locale.Chat.Actions.RefreshTitle} onClick={() => { showToast(Locale.Chat.Actions.RefreshToast); - chatStore.summarizeSession(true); + chatStore.summarizeSession(true, session); }} />

diff --git a/app/store/chat.ts b/app/store/chat.ts index 1bf2e1367..4208692e2 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -352,13 +352,13 @@ export const useChatStore = createPersistStore( return session; }, - onNewMessage(message: ChatMessage) { - get().updateCurrentSession((session) => { + onNewMessage(message: ChatMessage, targetSession: ChatSession) { + get().updateTargetSession(targetSession, (session) => { session.messages = session.messages.concat(); session.lastUpdate = Date.now(); }); get().updateStat(message); - get().summarizeSession(); + get().summarizeSession(false, targetSession); }, async onUserInput(content: string, attachImages?: string[]) { @@ -428,7 +428,7 @@ export const useChatStore = createPersistStore( botMessage.streaming = false; if (message) { botMessage.content = message; - get().onNewMessage(botMessage); + get().onNewMessage(botMessage, session); } ChatControllerPool.remove(session.id, botMessage.id); }, @@ -598,9 +598,12 @@ export const useChatStore = createPersistStore( }); }, - summarizeSession(refreshTitle: boolean = false) { + summarizeSession( + refreshTitle: boolean = false, + targetSession: ChatSession, + ) { const config = useAppConfig.getState(); - const session = get().currentSession(); + const session = targetSession; const modelConfig = session.mask.modelConfig; // skip summarize when using dalle3? if (isDalle3(modelConfig.model)) { @@ -651,7 +654,8 @@ export const useChatStore = createPersistStore( }, onFinish(message, responseRes) { if (responseRes?.status === 200) { - get().updateCurrentSession( + get().updateTargetSession( + session, (session) => (session.topic = message.length > 0 ? trimTopic(message) : DEFAULT_TOPIC), @@ -719,7 +723,7 @@ export const useChatStore = createPersistStore( onFinish(message, responseRes) { if (responseRes?.status === 200) { console.log("[Memory] ", message); - get().updateCurrentSession((session) => { + get().updateTargetSession(session, (session) => { session.lastSummarizeIndex = lastSummarizeIndex; session.memoryPrompt = message; // Update the memory prompt for stored it in local storage }); @@ -745,7 +749,16 @@ export const useChatStore = createPersistStore( updater(sessions[index]); set(() => ({ sessions })); }, - + updateTargetSession( + targetSession: ChatSession, + updater: (session: ChatSession) => void, + ) { + const sessions = get().sessions; + const index = sessions.findIndex((s) => s.id === targetSession.id); + if (index < 0) return; + updater(sessions[index]); + set(() => ({ sessions })); + }, async clearAllData() { await indexedDBStorage.clear(); localStorage.clear(); From 4d3949718a979ff7db1c10d30b7ab66793c95892 Mon Sep 17 00:00:00 2001 From: Lloyd Zhou Date: Tue, 5 Nov 2024 01:09:27 +0800 Subject: [PATCH 179/352] merge main --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d054c36f0..7502b1410 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4 [MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple [Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu -[Deploy on Vercel](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [Deploy to Alibaba Cloud](https://computenest.aliyun.com/market/service-f1c9b75e59814dc49d52) +[Deploy on Vercel](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [BT Deply Install](https://www.bt.cn/new/download.html) [Deploy to Alibaba Cloud](https://computenest.aliyun.com/market/service-f1c9b75e59814dc49d52) [](https://monica.im/?utm=nxcrp) From 0ec423389fa08e4e4b046db5ad147194622b6218 Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Tue, 5 Nov 2024 11:06:20 +0800 Subject: [PATCH 180/352] chore: update readme --- README.md | 8 ++++++++ README_CN.md | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/README.md b/README.md index dd2c5b1ee..fce62ba37 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,14 @@ iflytek Api Key. iflytek Api Secret. +### `CHATGLM_API_KEY` (optional) + +ChatGLM Api Key. + +### `CHATGLM_URL` (optional) + +ChatGLM Api Url. + ### `HIDE_USER_API_KEY` (optional) > Default: Empty diff --git a/README_CN.md b/README_CN.md index ccdcf28ff..d4da8b9da 100644 --- a/README_CN.md +++ b/README_CN.md @@ -184,6 +184,13 @@ ByteDance Api Url. 讯飞星火Api Secret. +### `CHATGLM_API_KEY` (可选) + +ChatGLM Api Key. + +### `CHATGLM_URL` (可选) + +ChatGLM Api Url. ### `HIDE_USER_API_KEY` (可选) From b844045d231658b9e40fa0582936c6746e7a7ef4 Mon Sep 17 00:00:00 2001 From: ryanhex53 Date: Tue, 5 Nov 2024 07:44:12 +0000 Subject: [PATCH 181/352] Custom model names can include the `@` symbol by itself. To specify the model's provider, append it after the model name using `@` as before. This format supports cases like `google vertex ai` with a model name like `claude-3-5-sonnet@20240620`. For instance, `claude-3-5-sonnet@20240620@vertex-ai` will be split by `split(/@(?!.*@)/)` into: `[ 'claude-3-5-sonnet@20240620', 'vertex-ai' ]`, where the former is the model name and the latter is the custom provider. --- app/api/common.ts | 2 +- app/components/chat.tsx | 2 +- app/components/model-config.tsx | 6 ++++-- app/store/access.ts | 2 +- app/utils/model.ts | 8 ++++---- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/api/common.ts b/app/api/common.ts index b4c792d6f..322dedeed 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -71,7 +71,7 @@ export async function requestOpenai(req: NextRequest) { .filter((v) => !!v && !v.startsWith("-") && v.includes(modelName)) .forEach((m) => { const [fullName, displayName] = m.split("="); - const [_, providerName] = fullName.split("@"); + const [_, providerName] = fullName.split(/@(?!.*@)/); if (providerName === "azure" && !displayName) { const [_, deployId] = (serverConfig?.azureUrl ?? "").split( "deployments/", diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 3d5b6a4f2..2ff08253a 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -645,7 +645,7 @@ export function ChatActions(props: { onClose={() => setShowModelSelector(false)} onSelection={(s) => { if (s.length === 0) return; - const [model, providerName] = s[0].split("@"); + const [model, providerName] = s[0].split(/@(?!.*@)/); chatStore.updateCurrentSession((session) => { session.mask.modelConfig.model = model as ModelType; session.mask.modelConfig.providerName = diff --git a/app/components/model-config.tsx b/app/components/model-config.tsx index f2297e10b..0eac916eb 100644 --- a/app/components/model-config.tsx +++ b/app/components/model-config.tsx @@ -28,7 +28,8 @@ export function ModelConfigList(props: { value={value} align="left" onChange={(e) => { - const [model, providerName] = e.currentTarget.value.split("@"); + const [model, providerName] = + e.currentTarget.value.split(/@(?!.*@)/); props.updateConfig((config) => { config.model = ModalConfigValidator.model(model); config.providerName = providerName as ServiceProvider; @@ -247,7 +248,8 @@ export function ModelConfigList(props: { aria-label={Locale.Settings.CompressModel.Title} value={compressModelValue} onChange={(e) => { - const [model, providerName] = e.currentTarget.value.split("@"); + const [model, providerName] = + e.currentTarget.value.split(/@(?!.*@)/); props.updateConfig((config) => { config.compressModel = ModalConfigValidator.model(model); config.compressProviderName = providerName as ServiceProvider; diff --git a/app/store/access.ts b/app/store/access.ts index 3b0e6357b..4e2cb1603 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -226,7 +226,7 @@ export const useAccessStore = createPersistStore( .then((res) => { const defaultModel = res.defaultModel ?? ""; if (defaultModel !== "") { - const [model, providerName] = defaultModel.split("@"); + const [model, providerName] = defaultModel.split(/@(?!.*@)/); DEFAULT_CONFIG.modelConfig.model = model; DEFAULT_CONFIG.modelConfig.providerName = providerName; } diff --git a/app/utils/model.ts b/app/utils/model.ts index 0b62b53be..0b95713e1 100644 --- a/app/utils/model.ts +++ b/app/utils/model.ts @@ -79,10 +79,10 @@ export function collectModelTable( ); } else { // 1. find model by name, and set available value - const [customModelName, customProviderName] = name.split("@"); + const [customModelName, customProviderName] = name.split(/@(?!.*@)/); let count = 0; for (const fullName in modelTable) { - const [modelName, providerName] = fullName.split("@"); + const [modelName, providerName] = fullName.split(/@(?!.*@)/); if ( customModelName == modelName && (customProviderName === undefined || @@ -102,7 +102,7 @@ export function collectModelTable( } // 2. if model not exists, create new model with available value if (count === 0) { - let [customModelName, customProviderName] = name.split("@"); + let [customModelName, customProviderName] = name.split(/@(?!.*@)/); const provider = customProvider( customProviderName || customModelName, ); @@ -139,7 +139,7 @@ export function collectModelTableWithDefaultModel( for (const key of Object.keys(modelTable)) { if ( modelTable[key].available && - key.split("@").shift() == defaultModel + key.split(/@(?!.*@)/).shift() == defaultModel ) { modelTable[key].isDefault = true; break; From 00d6cb27f719caffd24518db3dd656b7380a9062 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 5 Nov 2024 17:42:55 +0800 Subject: [PATCH 182/352] update version --- src-tauri/tauri.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 415825b13..7e08d9070 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "NextChat", - "version": "2.15.6" + "version": "2.15.7" }, "tauri": { "allowlist": { From 8e2484fcdf476a1248ae91541d6d491e5881b49b Mon Sep 17 00:00:00 2001 From: ryanhex53 Date: Tue, 5 Nov 2024 13:52:54 +0000 Subject: [PATCH 183/352] Refactor: Replace all provider split occurrences with getModelProvider utility method --- app/api/common.ts | 4 ++-- app/components/chat.tsx | 3 ++- app/components/model-config.tsx | 11 +++++++---- app/store/access.ts | 5 +++-- app/utils/model.ts | 19 +++++++++++++++---- test/model-provider.test.ts | 31 +++++++++++++++++++++++++++++++ 6 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 test/model-provider.test.ts diff --git a/app/api/common.ts b/app/api/common.ts index 322dedeed..495a12ccd 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -1,8 +1,8 @@ import { NextRequest, NextResponse } from "next/server"; import { getServerSideConfig } from "../config/server"; import { OPENAI_BASE_URL, ServiceProvider } from "../constant"; -import { isModelAvailableInServer } from "../utils/model"; import { cloudflareAIGatewayUrl } from "../utils/cloudflare"; +import { getModelProvider, isModelAvailableInServer } from "../utils/model"; const serverConfig = getServerSideConfig(); @@ -71,7 +71,7 @@ export async function requestOpenai(req: NextRequest) { .filter((v) => !!v && !v.startsWith("-") && v.includes(modelName)) .forEach((m) => { const [fullName, displayName] = m.split("="); - const [_, providerName] = fullName.split(/@(?!.*@)/); + const [_, providerName] = getModelProvider(fullName); if (providerName === "azure" && !displayName) { const [_, deployId] = (serverConfig?.azureUrl ?? "").split( "deployments/", diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 2ff08253a..cee54d891 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -120,6 +120,7 @@ import { createTTSPlayer } from "../utils/audio"; import { MsEdgeTTS, OUTPUT_FORMAT } from "../utils/ms_edge_tts"; import { isEmpty } from "lodash-es"; +import { getModelProvider } from "../utils/model"; const localStorage = safeLocalStorage(); @@ -645,7 +646,7 @@ export function ChatActions(props: { onClose={() => setShowModelSelector(false)} onSelection={(s) => { if (s.length === 0) return; - const [model, providerName] = s[0].split(/@(?!.*@)/); + const [model, providerName] = getModelProvider(s[0]); chatStore.updateCurrentSession((session) => { session.mask.modelConfig.model = model as ModelType; session.mask.modelConfig.providerName = diff --git a/app/components/model-config.tsx b/app/components/model-config.tsx index 0eac916eb..e845bfeac 100644 --- a/app/components/model-config.tsx +++ b/app/components/model-config.tsx @@ -7,6 +7,7 @@ import { ListItem, Select } from "./ui-lib"; import { useAllModels } from "../utils/hooks"; import { groupBy } from "lodash-es"; import styles from "./model-config.module.scss"; +import { getModelProvider } from "../utils/model"; export function ModelConfigList(props: { modelConfig: ModelConfig; @@ -28,8 +29,9 @@ export function ModelConfigList(props: { value={value} align="left" onChange={(e) => { - const [model, providerName] = - e.currentTarget.value.split(/@(?!.*@)/); + const [model, providerName] = getModelProvider( + e.currentTarget.value, + ); props.updateConfig((config) => { config.model = ModalConfigValidator.model(model); config.providerName = providerName as ServiceProvider; @@ -248,8 +250,9 @@ export function ModelConfigList(props: { aria-label={Locale.Settings.CompressModel.Title} value={compressModelValue} onChange={(e) => { - const [model, providerName] = - e.currentTarget.value.split(/@(?!.*@)/); + const [model, providerName] = getModelProvider( + e.currentTarget.value, + ); props.updateConfig((config) => { config.compressModel = ModalConfigValidator.model(model); config.compressProviderName = providerName as ServiceProvider; diff --git a/app/store/access.ts b/app/store/access.ts index 4e2cb1603..4796b2fe8 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -21,6 +21,7 @@ import { getClientConfig } from "../config/client"; import { createPersistStore } from "../utils/store"; import { ensure } from "../utils/clone"; import { DEFAULT_CONFIG } from "./config"; +import { getModelProvider } from "../utils/model"; let fetchState = 0; // 0 not fetch, 1 fetching, 2 done @@ -226,9 +227,9 @@ export const useAccessStore = createPersistStore( .then((res) => { const defaultModel = res.defaultModel ?? ""; if (defaultModel !== "") { - const [model, providerName] = defaultModel.split(/@(?!.*@)/); + const [model, providerName] = getModelProvider(defaultModel); DEFAULT_CONFIG.modelConfig.model = model; - DEFAULT_CONFIG.modelConfig.providerName = providerName; + DEFAULT_CONFIG.modelConfig.providerName = providerName as any; } return res; diff --git a/app/utils/model.ts b/app/utils/model.ts index 0b95713e1..a1b7df1b6 100644 --- a/app/utils/model.ts +++ b/app/utils/model.ts @@ -37,6 +37,17 @@ const sortModelTable = (models: ReturnType) => } }); +/** + * get model name and provider from a formatted string, + * e.g. `gpt-4@OpenAi` or `claude-3-5-sonnet@20240620@Google` + * @param modelWithProvider model name with provider separated by last `@` char, + * @returns [model, provider] tuple, if no `@` char found, provider is undefined + */ +export function getModelProvider(modelWithProvider: string): [string, string?] { + const [model, provider] = modelWithProvider.split(/@(?!.*@)/); + return [model, provider]; +} + export function collectModelTable( models: readonly LLMModel[], customModels: string, @@ -79,10 +90,10 @@ export function collectModelTable( ); } else { // 1. find model by name, and set available value - const [customModelName, customProviderName] = name.split(/@(?!.*@)/); + const [customModelName, customProviderName] = getModelProvider(name); let count = 0; for (const fullName in modelTable) { - const [modelName, providerName] = fullName.split(/@(?!.*@)/); + const [modelName, providerName] = getModelProvider(fullName); if ( customModelName == modelName && (customProviderName === undefined || @@ -102,7 +113,7 @@ export function collectModelTable( } // 2. if model not exists, create new model with available value if (count === 0) { - let [customModelName, customProviderName] = name.split(/@(?!.*@)/); + let [customModelName, customProviderName] = getModelProvider(name); const provider = customProvider( customProviderName || customModelName, ); @@ -139,7 +150,7 @@ export function collectModelTableWithDefaultModel( for (const key of Object.keys(modelTable)) { if ( modelTable[key].available && - key.split(/@(?!.*@)/).shift() == defaultModel + getModelProvider(key)[0] == defaultModel ) { modelTable[key].isDefault = true; break; diff --git a/test/model-provider.test.ts b/test/model-provider.test.ts new file mode 100644 index 000000000..41f14be02 --- /dev/null +++ b/test/model-provider.test.ts @@ -0,0 +1,31 @@ +import { getModelProvider } from "../app/utils/model"; + +describe("getModelProvider", () => { + test("should return model and provider when input contains '@'", () => { + const input = "model@provider"; + const [model, provider] = getModelProvider(input); + expect(model).toBe("model"); + expect(provider).toBe("provider"); + }); + + test("should return model and undefined provider when input does not contain '@'", () => { + const input = "model"; + const [model, provider] = getModelProvider(input); + expect(model).toBe("model"); + expect(provider).toBeUndefined(); + }); + + test("should handle multiple '@' characters correctly", () => { + const input = "model@provider@extra"; + const [model, provider] = getModelProvider(input); + expect(model).toBe("model@provider"); + expect(provider).toBe("extra"); + }); + + test("should return empty strings when input is empty", () => { + const input = ""; + const [model, provider] = getModelProvider(input); + expect(model).toBe(""); + expect(provider).toBeUndefined(); + }); +}); From c4e19dbc59e59b71c81cf33600f7a2be235b0ccc Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Wed, 6 Nov 2024 11:06:18 +0800 Subject: [PATCH 184/352] fix: updateCurrentSession => updateTargetSession --- app/components/chat.tsx | 80 +++++++++++++++++++++++------------------ app/store/chat.ts | 29 ++++++--------- 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index a62021db3..c5deeefa5 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -148,7 +148,8 @@ export function SessionConfigModel(props: { onClose: () => void }) { text={Locale.Chat.Config.Reset} onClick={async () => { if (await showConfirm(Locale.Memory.ResetConfirm)) { - chatStore.updateCurrentSession( + chatStore.updateTargetSession( + session, (session) => (session.memoryPrompt = ""), ); } @@ -173,7 +174,10 @@ export function SessionConfigModel(props: { onClose: () => void }) { updateMask={(updater) => { const mask = { ...session.mask }; updater(mask); - chatStore.updateCurrentSession((session) => (session.mask = mask)); + chatStore.updateTargetSession( + session, + (session) => (session.mask = mask), + ); }} shouldSyncFromGlobal extraListItems={ @@ -345,12 +349,14 @@ export function PromptHints(props: { function ClearContextDivider() { const chatStore = useChatStore(); + const session = chatStore.currentSession(); return (
- chatStore.updateCurrentSession( + chatStore.updateTargetSession( + session, (session) => (session.clearContextIndex = undefined), ) } @@ -460,6 +466,7 @@ export function ChatActions(props: { const navigate = useNavigate(); const chatStore = useChatStore(); const pluginStore = usePluginStore(); + const session = chatStore.currentSession(); // switch themes const theme = config.theme; @@ -476,10 +483,9 @@ export function ChatActions(props: { const stopAll = () => ChatControllerPool.stopAll(); // switch model - const currentModel = chatStore.currentSession().mask.modelConfig.model; + const currentModel = session.mask.modelConfig.model; const currentProviderName = - chatStore.currentSession().mask.modelConfig?.providerName || - ServiceProvider.OpenAI; + session.mask.modelConfig?.providerName || ServiceProvider.OpenAI; const allModels = useAllModels(); const models = useMemo(() => { const filteredModels = allModels.filter((m) => m.available); @@ -513,12 +519,9 @@ export function ChatActions(props: { const dalle3Sizes: DalleSize[] = ["1024x1024", "1792x1024", "1024x1792"]; const dalle3Qualitys: DalleQuality[] = ["standard", "hd"]; const dalle3Styles: DalleStyle[] = ["vivid", "natural"]; - const currentSize = - chatStore.currentSession().mask.modelConfig?.size ?? "1024x1024"; - const currentQuality = - chatStore.currentSession().mask.modelConfig?.quality ?? "standard"; - const currentStyle = - chatStore.currentSession().mask.modelConfig?.style ?? "vivid"; + const currentSize = session.mask.modelConfig?.size ?? "1024x1024"; + const currentQuality = session.mask.modelConfig?.quality ?? "standard"; + const currentStyle = session.mask.modelConfig?.style ?? "vivid"; const isMobileScreen = useMobileScreen(); @@ -536,7 +539,7 @@ export function ChatActions(props: { if (isUnavailableModel && models.length > 0) { // show next model to default model if exist let nextModel = models.find((model) => model.isDefault) || models[0]; - chatStore.updateCurrentSession((session) => { + chatStore.updateTargetSession(session, (session) => { session.mask.modelConfig.model = nextModel.name; session.mask.modelConfig.providerName = nextModel?.provider ?.providerName as ServiceProvider; @@ -547,7 +550,7 @@ export function ChatActions(props: { : nextModel.name, ); } - }, [chatStore, currentModel, models]); + }, [chatStore, currentModel, models, session]); return (
@@ -614,7 +617,7 @@ export function ChatActions(props: { text={Locale.Chat.InputActions.Clear} icon={} onClick={() => { - chatStore.updateCurrentSession((session) => { + chatStore.updateTargetSession(session, (session) => { if (session.clearContextIndex === session.messages.length) { session.clearContextIndex = undefined; } else { @@ -646,7 +649,7 @@ export function ChatActions(props: { onSelection={(s) => { if (s.length === 0) return; const [model, providerName] = s[0].split("@"); - chatStore.updateCurrentSession((session) => { + chatStore.updateTargetSession(session, (session) => { session.mask.modelConfig.model = model as ModelType; session.mask.modelConfig.providerName = providerName as ServiceProvider; @@ -684,7 +687,7 @@ export function ChatActions(props: { onSelection={(s) => { if (s.length === 0) return; const size = s[0]; - chatStore.updateCurrentSession((session) => { + chatStore.updateTargetSession(session, (session) => { session.mask.modelConfig.size = size; }); showToast(size); @@ -711,7 +714,7 @@ export function ChatActions(props: { onSelection={(q) => { if (q.length === 0) return; const quality = q[0]; - chatStore.updateCurrentSession((session) => { + chatStore.updateTargetSession(session, (session) => { session.mask.modelConfig.quality = quality; }); showToast(quality); @@ -738,7 +741,7 @@ export function ChatActions(props: { onSelection={(s) => { if (s.length === 0) return; const style = s[0]; - chatStore.updateCurrentSession((session) => { + chatStore.updateTargetSession(session, (session) => { session.mask.modelConfig.style = style; }); showToast(style); @@ -769,7 +772,7 @@ export function ChatActions(props: { }))} onClose={() => setShowPluginSelector(false)} onSelection={(s) => { - chatStore.updateCurrentSession((session) => { + chatStore.updateTargetSession(session, (session) => { session.mask.plugin = s as string[]; }); }} @@ -812,7 +815,8 @@ export function EditMessageModal(props: { onClose: () => void }) { icon={} key="ok" onClick={() => { - chatStore.updateCurrentSession( + chatStore.updateTargetSession( + session, (session) => (session.messages = messages), ); props.onClose(); @@ -829,7 +833,8 @@ export function EditMessageModal(props: { onClose: () => void }) { type="text" value={session.topic} onInput={(e) => - chatStore.updateCurrentSession( + chatStore.updateTargetSession( + session, (session) => (session.topic = e.currentTarget.value), ) } @@ -990,7 +995,8 @@ function _Chat() { prev: () => chatStore.nextSession(-1), next: () => chatStore.nextSession(1), clear: () => - chatStore.updateCurrentSession( + chatStore.updateTargetSession( + session, (session) => (session.clearContextIndex = session.messages.length), ), fork: () => chatStore.forkSession(), @@ -1061,7 +1067,7 @@ function _Chat() { }; useEffect(() => { - chatStore.updateCurrentSession((session) => { + chatStore.updateTargetSession(session, (session) => { const stopTiming = Date.now() - REQUEST_TIMEOUT_MS; session.messages.forEach((m) => { // check if should stop all stale messages @@ -1087,7 +1093,7 @@ function _Chat() { } }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [session]); // check if should send message const onInputKeyDown = (e: React.KeyboardEvent) => { @@ -1118,7 +1124,8 @@ function _Chat() { }; const deleteMessage = (msgId?: string) => { - chatStore.updateCurrentSession( + chatStore.updateTargetSession( + session, (session) => (session.messages = session.messages.filter((m) => m.id !== msgId)), ); @@ -1185,7 +1192,7 @@ function _Chat() { }; const onPinMessage = (message: ChatMessage) => { - chatStore.updateCurrentSession((session) => + chatStore.updateTargetSession(session, (session) => session.mask.context.push(message), ); @@ -1711,14 +1718,17 @@ function _Chat() { }); } } - chatStore.updateCurrentSession((session) => { - const m = session.mask.context - .concat(session.messages) - .find((m) => m.id === message.id); - if (m) { - m.content = newContent; - } - }); + chatStore.updateTargetSession( + session, + (session) => { + const m = session.mask.context + .concat(session.messages) + .find((m) => m.id === message.id); + if (m) { + m.content = newContent; + } + }, + ); }} >
diff --git a/app/store/chat.ts b/app/store/chat.ts index 4208692e2..e2dbe90b0 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -357,7 +357,7 @@ export const useChatStore = createPersistStore( session.messages = session.messages.concat(); session.lastUpdate = Date.now(); }); - get().updateStat(message); + get().updateStat(message, targetSession); get().summarizeSession(false, targetSession); }, @@ -396,10 +396,10 @@ export const useChatStore = createPersistStore( // get recent messages const recentMessages = get().getMessagesWithMemory(); const sendMessages = recentMessages.concat(userMessage); - const messageIndex = get().currentSession().messages.length + 1; + const messageIndex = session.messages.length + 1; // save user's and bot's message - get().updateCurrentSession((session) => { + get().updateTargetSession(session, (session) => { const savedUserMessage = { ...userMessage, content: mContent, @@ -420,7 +420,7 @@ export const useChatStore = createPersistStore( if (message) { botMessage.content = message; } - get().updateCurrentSession((session) => { + get().updateTargetSession(session, (session) => { session.messages = session.messages.concat(); }); }, @@ -434,7 +434,7 @@ export const useChatStore = createPersistStore( }, onBeforeTool(tool: ChatMessageTool) { (botMessage.tools = botMessage?.tools || []).push(tool); - get().updateCurrentSession((session) => { + get().updateTargetSession(session, (session) => { session.messages = session.messages.concat(); }); }, @@ -444,7 +444,7 @@ export const useChatStore = createPersistStore( tools[i] = { ...tool }; } }); - get().updateCurrentSession((session) => { + get().updateTargetSession(session, (session) => { session.messages = session.messages.concat(); }); }, @@ -459,7 +459,7 @@ export const useChatStore = createPersistStore( botMessage.streaming = false; userMessage.isError = !isAborted; botMessage.isError = !isAborted; - get().updateCurrentSession((session) => { + get().updateTargetSession(session, (session) => { session.messages = session.messages.concat(); }); ChatControllerPool.remove( @@ -591,8 +591,8 @@ export const useChatStore = createPersistStore( set(() => ({ sessions })); }, - resetSession() { - get().updateCurrentSession((session) => { + resetSession(session: ChatSession) { + get().updateTargetSession(session, (session) => { session.messages = []; session.memoryPrompt = ""; }); @@ -736,19 +736,12 @@ export const useChatStore = createPersistStore( } }, - updateStat(message: ChatMessage) { - get().updateCurrentSession((session) => { + updateStat(message: ChatMessage, session: ChatSession) { + get().updateTargetSession(session, (session) => { session.stat.charCount += message.content.length; // TODO: should update chat count and word count }); }, - - updateCurrentSession(updater: (session: ChatSession) => void) { - const sessions = get().sessions; - const index = get().currentSessionIndex; - updater(sessions[index]); - set(() => ({ sessions })); - }, updateTargetSession( targetSession: ChatSession, updater: (session: ChatSession) => void, From 3086a2fa77d2815af05236bae4a13a4da387730b Mon Sep 17 00:00:00 2001 From: opchips Date: Wed, 6 Nov 2024 12:56:24 +0800 Subject: [PATCH 185/352] add claude35haiku not vision --- app/constant.ts | 1 + app/utils.ts | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/constant.ts b/app/constant.ts index 1d60e1ec6..d10e624cb 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -328,6 +328,7 @@ const anthropicModels = [ "claude-3-sonnet-20240229", "claude-3-opus-20240229", "claude-3-haiku-20240307", + "claude-3-5-haiku-20241022", "claude-3-5-sonnet-20240620", "claude-3-5-sonnet-20241022", "claude-3-5-sonnet-latest", diff --git a/app/utils.ts b/app/utils.ts index 2e1f94016..2dd80b8a3 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -254,6 +254,7 @@ export function getMessageImages(message: RequestMessage): string[] { export function isVisionModel(model: string) { // Note: This is a better way using the TypeScript feature instead of `&&` or `||` (ts v5.5.0-dev.20240314 I've been using) + const excludeKeywords = ["claude-3-5-haiku-20241022"]; const visionKeywords = [ "vision", "claude-3", @@ -266,9 +267,10 @@ export function isVisionModel(model: string) { model.includes("gpt-4-turbo") && !model.includes("preview"); return ( - visionKeywords.some((keyword) => model.includes(keyword)) || - isGpt4Turbo || - isDalle3(model) + !excludeKeywords.some((keyword) => model.includes(keyword)) && + (visionKeywords.some((keyword) => model.includes(keyword)) || + isGpt4Turbo || + isDalle3(model)) ); } From adf7d8200b63ba9e389c3df2b801b82a272a85bf Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Wed, 6 Nov 2024 13:55:57 +0800 Subject: [PATCH 186/352] fix: glm chatpath --- app/constant.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/constant.ts b/app/constant.ts index 1d60e1ec6..406e95609 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -232,7 +232,7 @@ export const XAI = { export const ChatGLM = { ExampleEndpoint: CHATGLM_BASE_URL, - ChatPath: "/api/paas/v4/chat/completions", + ChatPath: "api/paas/v4/chat/completions", }; export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang From 85cdcab850cadbbd346d38b34603e3eb00e3e715 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Wed, 6 Nov 2024 14:53:08 +0800 Subject: [PATCH 187/352] fix: botMessage reply date --- app/store/chat.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/store/chat.ts b/app/store/chat.ts index e2dbe90b0..af4993d12 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -428,6 +428,7 @@ export const useChatStore = createPersistStore( botMessage.streaming = false; if (message) { botMessage.content = message; + botMessage.date = new Date().toLocaleString(); get().onNewMessage(botMessage, session); } ChatControllerPool.remove(session.id, botMessage.id); From e0bbb8bb68429d160c50af512eaa5181b50dc2c3 Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Wed, 6 Nov 2024 16:58:26 +0800 Subject: [PATCH 188/352] style: improve classname by clsx --- app/components/auth.tsx | 6 +++-- app/components/button.tsx | 24 ++++++++++-------- app/components/chat-list.tsx | 13 +++++----- app/components/chat.tsx | 30 +++++++++++----------- app/components/exporter.tsx | 14 ++++++----- app/components/home.tsx | 17 ++++++++----- app/components/input-range.tsx | 3 ++- app/components/markdown.tsx | 12 ++++++--- app/components/mask.tsx | 3 ++- app/components/message-selector.tsx | 14 +++++------ app/components/new-chat.tsx | 5 +++- app/components/plugin.tsx | 8 ++++-- app/components/sd/sd-panel.tsx | 3 ++- app/components/sd/sd.tsx | 8 ++++-- app/components/sidebar.tsx | 17 +++++++------ app/components/ui-lib.tsx | 39 +++++++++++++++++------------ 16 files changed, 130 insertions(+), 86 deletions(-) diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 539a52eec..5375bda3f 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -18,6 +18,8 @@ import { trackSettingsPageGuideToCPaymentClick, trackAuthorizationPageButtonToCPaymentClick, } from "../utils/auth-settings-events"; +import clsx from "clsx"; + const storage = safeLocalStorage(); export function AuthPage() { @@ -54,7 +56,7 @@ export function AuthPage() { onClick={() => navigate(Path.Home)} >
-
+
@@ -163,7 +165,7 @@ function TopBanner() { onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} > -
+
{Locale.Auth.TopTips} diff --git a/app/components/button.tsx b/app/components/button.tsx index 87b4abd30..157d5d73d 100644 --- a/app/components/button.tsx +++ b/app/components/button.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import styles from "./button.module.scss"; import { CSSProperties } from "react"; +import clsx from "clsx"; export type ButtonType = "primary" | "danger" | null; @@ -22,12 +23,16 @@ export function IconButton(props: { }) { return (
diff --git a/app/components/chat-list.tsx b/app/components/chat-list.tsx index 03b1a5c88..63dc4d5ff 100644 --- a/app/components/chat-list.tsx +++ b/app/components/chat-list.tsx @@ -18,6 +18,7 @@ import { Mask } from "../store/mask"; import { useRef, useEffect } from "react"; import { showConfirm } from "./ui-lib"; import { useMobileScreen } from "../utils"; +import clsx from "clsx"; export function ChatItem(props: { onClick?: () => void; @@ -45,11 +46,11 @@ export function ChatItem(props: { {(provided) => (
{ draggableRef.current = ele; @@ -63,7 +64,7 @@ export function ChatItem(props: { > {props.narrow ? (
-
+
{props.showToast && context.length > 0 && (
props.setShowModal(true)} > @@ -332,10 +333,9 @@ export function PromptHints(props: { {props.prompts.map((prompt, i) => (
props.onPromptSelect(prompt)} onMouseEnter={() => setSelectIndex(i)} @@ -395,7 +395,7 @@ export function ChatAction(props: { return (
{ props.onClick(); setTimeout(updateWidth, 1); @@ -1596,9 +1596,12 @@ function _Chat() {
)} -
+
setIsEditingMessage(true)} > {!session.topic ? DEFAULT_TOPIC : session.topic} @@ -1872,7 +1875,7 @@ function _Chat() { )} {getMessageImages(message).length > 1 && (