From 1d7286c16192a91cc58480733d6448919d8b79bc Mon Sep 17 00:00:00 2001 From: Phil Huang Date: Thu, 17 Aug 2023 23:50:38 +0800 Subject: [PATCH 001/108] Improve the text in tw.ts Adoption of text that is more closely aligned with the usage of zh-tw --- app/locales/tw.ts | 68 +++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 45c3caa02..ad1ee0bb6 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -20,7 +20,7 @@ const tw: PartialLocaleType = { Retry: "重試", Delete: "刪除", }, - Rename: "重命名對話", + Rename: "重新命名對話", Typing: "正在輸入…", Input: (submitKey: string) => { var inputHints = `輸入訊息後,按下 ${submitKey} 鍵即可發送`; @@ -31,8 +31,8 @@ const tw: PartialLocaleType = { }, Send: "發送", Config: { - Reset: "重置默认", - SaveAs: "另存为面具", + Reset: "重置預設", + SaveAs: "另存新檔", }, }, Export: { @@ -62,7 +62,7 @@ const tw: PartialLocaleType = { Lang: { Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language` - All: "所有语言", + All: "所有語言", }, Avatar: "大頭貼", FontSize: { @@ -70,7 +70,7 @@ const tw: PartialLocaleType = { SubTitle: "聊天內容的字型大小", }, InjectSystemPrompts: { - Title: "注入系統提示", + Title: "匯入系統提示", SubTitle: "強制在每個請求的訊息列表開頭添加一個模擬 ChatGPT 的系統提示", }, Update: { @@ -86,12 +86,12 @@ const tw: PartialLocaleType = { TightBorder: "緊湊邊框", SendPreviewBubble: { Title: "預覽氣泡", - SubTitle: "在预览气泡中预览 Markdown 内容", + SubTitle: "在預覽氣泡中預覽 Markdown 内容", }, Mask: { Splash: { - Title: "面具启动页", - SubTitle: "新建聊天时,展示面具启动页", + Title: "面具啟動頁面", + SubTitle: "新增聊天時,呈現面具啟動頁面", }, }, Prompt: { @@ -109,7 +109,7 @@ const tw: PartialLocaleType = { Search: "搜尋提示詞", }, EditModal: { - Title: "编辑提示词", + Title: "編輯提示詞", }, }, HistoryCount: { @@ -179,53 +179,53 @@ const tw: PartialLocaleType = { Edit: "前置上下文和歷史記憶", Add: "新增一條", }, - Plugin: { Name: "插件" }, + Plugin: { Name: "外掛" }, Mask: { Name: "面具", Page: { - Title: "预设角色面具", - SubTitle: (count: number) => `${count} 个预设角色定义`, - Search: "搜索角色面具", - Create: "新建", + Title: "預設角色面具", + SubTitle: (count: number) => `${count} 個預設角色定義`, + Search: "搜尋角色面具", + Create: "新增", }, Item: { - Info: (count: number) => `包含 ${count} 条预设对话`, - Chat: "对话", + Info: (count: number) => `包含 ${count} 條預設對話`, + Chat: "對話", View: "查看", - Edit: "编辑", + Edit: "編輯", Delete: "删除", - DeleteConfirm: "确认删除?", + DeleteConfirm: "確認删除?", }, EditModal: { Title: (readonly: boolean) => - `编辑预设面具 ${readonly ? "(只读)" : ""}`, - Download: "下载预设", - Clone: "克隆预设", + `編輯預設面具 ${readonly ? "(只读)" : ""}`, + Download: "下載預設", + Clone: "克隆預設", }, Config: { - Avatar: "角色头像", - Name: "角色名称", + Avatar: "角色頭像", + Name: "角色名稱", }, }, NewChat: { Return: "返回", - Skip: "跳过", - Title: "挑选一个面具", - SubTitle: "现在开始,与面具背后的灵魂思维碰撞", - More: "搜索更多", - NotShow: "不再展示", - ConfirmNoShow: "确认禁用?禁用后可以随时在设置中重新启用。", + Skip: "跳過", + Title: "挑選一個面具", + SubTitle: "現在開始,與面具背後的靈魂思維碰撞", + More: "搜尋更多", + NotShow: "不再呈現", + ConfirmNoShow: "確認禁用?禁用後可以随時在設定中重新啟用。", }, UI: { - Confirm: "确认", + Confirm: "確認", Cancel: "取消", - Close: "关闭", - Create: "新建", - Edit: "编辑", + Close: "關閉", + Create: "新增", + Edit: "編輯", }, Exporter: { Model: "模型", - Messages: "消息", + Messages: "訊息", Topic: "主題", Time: "時間", }, From 35b0bd76f82b16ed4f598193e68bb7a3279a70da Mon Sep 17 00:00:00 2001 From: Algorithm5838 <108630393+Algorithm5838@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:12:27 +0300 Subject: [PATCH 002/108] Update markdown.tsx --- 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 4a84969c5..e7a35b802 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -115,6 +115,7 @@ function _MarkDownContent(props: { content: string }) { ]} components={{ pre: PreCode, + p: (pProps) =>

, a: (aProps) => { const href = aProps.href || ""; const isInternal = /^\/#/i.test(href); @@ -150,7 +151,6 @@ export function Markdown( ref={mdRef} onContextMenu={props.onContextMenu} onDoubleClickCapture={props.onDoubleClickCapture} - dir="auto" > {props.loading ? ( From e78b15b9f010d366ed47b1019e5110e2cc286a04 Mon Sep 17 00:00:00 2001 From: Clarence Dan Date: Fri, 18 Aug 2023 17:12:02 +0800 Subject: [PATCH 003/108] Update chat.module.scss Specify styles for iOS devices. --- app/components/chat.module.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/components/chat.module.scss b/app/components/chat.module.scss index d407d28e4..77b6ae1a1 100644 --- a/app/components/chat.module.scss +++ b/app/components/chat.module.scss @@ -349,6 +349,14 @@ padding: 7px; } } + /* Specific styles for iOS devices */ + @media screen and (max-device-width: 812px) and (-webkit-min-device-pixel-ratio: 2) { + @supports (-webkit-touch-callout: none) { + .chat-message-edit { + top: -10%; + } + } + } } .chat-message-status { From 16685ddb6c49099eeee718c5abe96c256427eb90 Mon Sep 17 00:00:00 2001 From: Clarence Dan Date: Fri, 18 Aug 2023 17:21:58 +0800 Subject: [PATCH 004/108] Update chat.module.scss --- app/components/chat.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/chat.module.scss b/app/components/chat.module.scss index 77b6ae1a1..16790ccb1 100644 --- a/app/components/chat.module.scss +++ b/app/components/chat.module.scss @@ -353,7 +353,7 @@ @media screen and (max-device-width: 812px) and (-webkit-min-device-pixel-ratio: 2) { @supports (-webkit-touch-callout: none) { .chat-message-edit { - top: -10%; + top: -8%; } } } From aa3f96f89cfdf92cc23f338bc6e11c54bc1c4bae Mon Sep 17 00:00:00 2001 From: Clarence Dan <48417261+ClarenceDan@users.noreply.github.com> Date: Fri, 18 Aug 2023 18:18:16 +0800 Subject: [PATCH 005/108] Update globals.scss --- app/styles/globals.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/styles/globals.scss b/app/styles/globals.scss index beb30d782..def28680c 100644 --- a/app/styles/globals.scss +++ b/app/styles/globals.scss @@ -349,7 +349,7 @@ pre { justify-content: center; border: var(--border-in-light); box-shadow: var(--card-shadow); - border-radius: 10px; + border-radius: 11px; } .one-line { From 50eb7a5f98b094e955b5268a7154ce4e1f436c45 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Fri, 18 Aug 2023 18:47:54 +0800 Subject: [PATCH 006/108] Update tauri.conf.json --- 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 2ec2c1a84..b09715cb3 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "ChatGPT Next Web", - "version": "2.9.3" + "version": "2.9.4" }, "tauri": { "allowlist": { From f84572443fc53394d2e7372d0856ae72039c60e9 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 21 Aug 2023 16:00:19 +0800 Subject: [PATCH 007/108] Update faq-cn.md --- docs/faq-cn.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/faq-cn.md b/docs/faq-cn.md index f9463eb95..bf79ef7d9 100644 --- a/docs/faq-cn.md +++ b/docs/faq-cn.md @@ -101,7 +101,7 @@ keepalive_timeout 300; # 设定keep-alive超时时间为65秒 项目通过环境变量 CODE 设置了访问密码。第一次使用时,需要到设置中,输入访问码才可以使用。 -## 使用时提示"You exceeded your current quota, ..." +## 使用时提示 "You exceeded your current quota, ..." API KEY 有问题。余额不足。 @@ -122,6 +122,9 @@ API KEY 有问题。余额不足。 注意,关闭此特性后,用户会在第一次访问网站时加载所有资源,如果用户网络状况较差,可能会引起较长时间的白屏,从而影响用户使用体验,所以自行考虑。 +## 使用时遇到 "NotFoundError: Failed to execute 'removeChild' on 'Node': The node...." +请关闭浏览器自身的自动翻译功能,并关闭所有自动翻译插件。 + # 网络服务相关问题 ## Cloudflare 是什么? From a4040fc1ee1665851b0c6fe005bacdc778d51c92 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 21 Aug 2023 18:30:44 +0800 Subject: [PATCH 008/108] Update README_CN.md --- README_CN.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README_CN.md b/README_CN.md index 16d3ec19b..568cd229a 100644 --- a/README_CN.md +++ b/README_CN.md @@ -179,6 +179,9 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s [见项目贡献者列表](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors) +### 相关项目 +- [one-api](https://github.com/songquanpeng/one-api): 一站式大模型额度管理平台,支持市面上所有主流大语言模型 + ## 开源协议 [MIT](https://opensource.org/license/mit/) From e1142216eca8c91701457a2a85cbe45d1e7c3ec9 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 21 Aug 2023 18:33:45 +0800 Subject: [PATCH 009/108] fix: #2672 should use correct resend index --- app/components/chat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 656208585..9a9488dd2 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -802,7 +802,7 @@ function _Chat() { (m) => m.id === message.id, ); - if (resendingIndex <= 0 || resendingIndex >= session.messages.length) { + if (resendingIndex < 0 || resendingIndex >= session.messages.length) { console.error("[Chat] failed to find resending message", message); return; } From 4952e41132a9f5b917ae15013bbdf4170876fc90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:56:27 +0000 Subject: [PATCH 010/108] chore(deps): bump next from 13.4.9 to 13.4.19 Bumps [next](https://github.com/vercel/next.js) from 13.4.9 to 13.4.19. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v13.4.9...v13.4.19) --- updated-dependencies: - dependency-name: next dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 108 +++++++++++++++++++++++++-------------------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index cbb51546b..8ddc36c1b 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "html-to-image": "^1.11.11", "mermaid": "^10.2.3", "nanoid": "^4.0.2", - "next": "^13.4.9", + "next": "^13.4.19", "node-fetch": "^3.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/yarn.lock b/yarn.lock index 9c7688bc5..6d58d787d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1131,10 +1131,10 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@next/env@13.4.9": - version "13.4.9" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.9.tgz#b77759514dd56bfa9791770755a2482f4d6ca93e" - integrity sha512-vuDRK05BOKfmoBYLNi2cujG2jrYbEod/ubSSyqgmEx9n/W3eZaJQdRNhTfumO+qmq/QTzLurW487n/PM/fHOkw== +"@next/env@13.4.19": + version "13.4.19" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.19.tgz#46905b4e6f62da825b040343cbc233144e9578d3" + integrity sha512-FsAT5x0jF2kkhNkKkukhsyYOrRqtSxrEhfliniIq0bwWbuXLgyt3Gv0Ml+b91XwjwArmuP7NxCiGd++GGKdNMQ== "@next/eslint-plugin-next@13.2.3": version "13.2.3" @@ -1143,50 +1143,50 @@ dependencies: glob "7.1.7" -"@next/swc-darwin-arm64@13.4.9": - version "13.4.9" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.9.tgz#0ed408d444bbc6b0a20f3506a9b4222684585677" - integrity sha512-TVzGHpZoVBk3iDsTOQA/R6MGmFp0+17SWXMEWd6zG30AfuELmSSMe2SdPqxwXU0gbpWkJL1KgfLzy5ReN0crqQ== +"@next/swc-darwin-arm64@13.4.19": + version "13.4.19" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.19.tgz#77ad462b5ced4efdc26cb5a0053968d2c7dac1b6" + integrity sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ== -"@next/swc-darwin-x64@13.4.9": - version "13.4.9" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.9.tgz#a08fccdee68201522fe6618ec81f832084b222f8" - integrity sha512-aSfF1fhv28N2e7vrDZ6zOQ+IIthocfaxuMWGReB5GDriF0caTqtHttAvzOMgJgXQtQx6XhyaJMozLTSEXeNN+A== +"@next/swc-darwin-x64@13.4.19": + version "13.4.19" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.19.tgz#aebe38713a4ce536ee5f2a291673e14b715e633a" + integrity sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw== -"@next/swc-linux-arm64-gnu@13.4.9": - version "13.4.9" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.9.tgz#1798c2341bb841e96521433eed00892fb24abbd1" - integrity sha512-JhKoX5ECzYoTVyIy/7KykeO4Z2lVKq7HGQqvAH+Ip9UFn1MOJkOnkPRB7v4nmzqAoY+Je05Aj5wNABR1N18DMg== +"@next/swc-linux-arm64-gnu@13.4.19": + version "13.4.19" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.19.tgz#ec54db65b587939c7b94f9a84800f003a380f5a6" + integrity sha512-vdlnIlaAEh6H+G6HrKZB9c2zJKnpPVKnA6LBwjwT2BTjxI7e0Hx30+FoWCgi50e+YO49p6oPOtesP9mXDRiiUg== -"@next/swc-linux-arm64-musl@13.4.9": - version "13.4.9" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.9.tgz#cee04c51610eddd3638ce2499205083656531ea0" - integrity sha512-OOn6zZBIVkm/4j5gkPdGn4yqQt+gmXaLaSjRSO434WplV8vo2YaBNbSHaTM9wJpZTHVDYyjzuIYVEzy9/5RVZw== +"@next/swc-linux-arm64-musl@13.4.19": + version "13.4.19" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.19.tgz#1f5e2c1ea6941e7d530d9f185d5d64be04279d86" + integrity sha512-aU0HkH2XPgxqrbNRBFb3si9Ahu/CpaR5RPmN2s9GiM9qJCiBBlZtRTiEca+DC+xRPyCThTtWYgxjWHgU7ZkyvA== -"@next/swc-linux-x64-gnu@13.4.9": - version "13.4.9" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.9.tgz#1932d0367916adbc6844b244cda1d4182bd11f7a" - integrity sha512-iA+fJXFPpW0SwGmx/pivVU+2t4zQHNOOAr5T378PfxPHY6JtjV6/0s1vlAJUdIHeVpX98CLp9k5VuKgxiRHUpg== +"@next/swc-linux-x64-gnu@13.4.19": + version "13.4.19" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.19.tgz#96b0882492a2f7ffcce747846d3680730f69f4d1" + integrity sha512-htwOEagMa/CXNykFFeAHHvMJeqZfNQEoQvHfsA4wgg5QqGNqD5soeCer4oGlCol6NGUxknrQO6VEustcv+Md+g== -"@next/swc-linux-x64-musl@13.4.9": - version "13.4.9" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.9.tgz#a66aa8c1383b16299b72482f6360facd5cde3c7a" - integrity sha512-rlNf2WUtMM+GAQrZ9gMNdSapkVi3koSW3a+dmBVp42lfugWVvnyzca/xJlN48/7AGx8qu62WyO0ya1ikgOxh6A== +"@next/swc-linux-x64-musl@13.4.19": + version "13.4.19" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.19.tgz#f276b618afa321d2f7b17c81fc83f429fb0fd9d8" + integrity sha512-4Gj4vvtbK1JH8ApWTT214b3GwUh9EKKQjY41hH/t+u55Knxi/0wesMzwQRhppK6Ddalhu0TEttbiJ+wRcoEj5Q== -"@next/swc-win32-arm64-msvc@13.4.9": - version "13.4.9" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.9.tgz#39482ee856c867177a612a30b6861c75e0736a4a" - integrity sha512-5T9ybSugXP77nw03vlgKZxD99AFTHaX8eT1ayKYYnGO9nmYhJjRPxcjU5FyYI+TdkQgEpIcH7p/guPLPR0EbKA== +"@next/swc-win32-arm64-msvc@13.4.19": + version "13.4.19" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.19.tgz#1599ae0d401da5ffca0947823dac577697cce577" + integrity sha512-bUfDevQK4NsIAHXs3/JNgnvEY+LRyneDN788W2NYiRIIzmILjba7LaQTfihuFawZDhRtkYCv3JDC3B4TwnmRJw== -"@next/swc-win32-ia32-msvc@13.4.9": - version "13.4.9" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.9.tgz#29db85e34b597ade1a918235d16a760a9213c190" - integrity sha512-ojZTCt1lP2ucgpoiFgrFj07uq4CZsq4crVXpLGgQfoFq00jPKRPgesuGPaz8lg1yLfvafkU3Jd1i8snKwYR3LA== +"@next/swc-win32-ia32-msvc@13.4.19": + version "13.4.19" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.19.tgz#55cdd7da90818f03e4da16d976f0cb22045d16fd" + integrity sha512-Y5kikILFAr81LYIFaw6j/NrOtmiM4Sf3GtOc0pn50ez2GCkr+oejYuKGcwAwq3jiTKuzF6OF4iT2INPoxRycEA== -"@next/swc-win32-x64-msvc@13.4.9": - version "13.4.9" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.9.tgz#0c2758164cccd61bc5a1c6cd8284fe66173e4a2b" - integrity sha512-QbT03FXRNdpuL+e9pLnu+XajZdm/TtIXVYY4lA9t+9l0fLZbHXDYEKitAqxrOj37o3Vx5ufxiRAniaIebYDCgw== +"@next/swc-win32-x64-msvc@13.4.19": + version "13.4.19" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.19.tgz#648f79c4e09279212ac90d871646ae12d80cdfce" + integrity sha512-YzA78jBDXMYiINdPdJJwGgPNT3YqBNNGhsthsDoWHL9p24tEJn9ViQf/ZqTbwSpX/RrkPupLfuuTH2sf73JBAw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -4654,12 +4654,12 @@ neo-async@^2.6.2: resolved "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -next@^13.4.9: - version "13.4.9" - resolved "https://registry.yarnpkg.com/next/-/next-13.4.9.tgz#473de5997cb4c5d7a4fb195f566952a1cbffbeba" - integrity sha512-vtefFm/BWIi/eWOqf1GsmKG3cjKw1k3LjuefKRcL3iiLl3zWzFdPG3as6xtxrGO6gwTzzaO1ktL4oiHt/uvTjA== +next@^13.4.19: + version "13.4.19" + resolved "https://registry.yarnpkg.com/next/-/next-13.4.19.tgz#2326e02aeedee2c693d4f37b90e4f0ed6882b35f" + integrity sha512-HuPSzzAbJ1T4BD8e0bs6B9C1kWQ6gv8ykZoRWs5AQoiIuqbGHHdQO7Ljuvg05Q0Z24E2ABozHe6FxDvI6HfyAw== dependencies: - "@next/env" "13.4.9" + "@next/env" "13.4.19" "@swc/helpers" "0.5.1" busboy "1.6.0" caniuse-lite "^1.0.30001406" @@ -4668,15 +4668,15 @@ next@^13.4.9: watchpack "2.4.0" zod "3.21.4" optionalDependencies: - "@next/swc-darwin-arm64" "13.4.9" - "@next/swc-darwin-x64" "13.4.9" - "@next/swc-linux-arm64-gnu" "13.4.9" - "@next/swc-linux-arm64-musl" "13.4.9" - "@next/swc-linux-x64-gnu" "13.4.9" - "@next/swc-linux-x64-musl" "13.4.9" - "@next/swc-win32-arm64-msvc" "13.4.9" - "@next/swc-win32-ia32-msvc" "13.4.9" - "@next/swc-win32-x64-msvc" "13.4.9" + "@next/swc-darwin-arm64" "13.4.19" + "@next/swc-darwin-x64" "13.4.19" + "@next/swc-linux-arm64-gnu" "13.4.19" + "@next/swc-linux-arm64-musl" "13.4.19" + "@next/swc-linux-x64-gnu" "13.4.19" + "@next/swc-linux-x64-musl" "13.4.19" + "@next/swc-win32-arm64-msvc" "13.4.19" + "@next/swc-win32-ia32-msvc" "13.4.19" + "@next/swc-win32-x64-msvc" "13.4.19" node-domexception@^1.0.0: version "1.0.0" From a1c7f86ff36d3e3f1289a6d09479f42fe371db1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:57:07 +0000 Subject: [PATCH 011/108] chore(deps-dev): bump eslint-config-next from 13.2.3 to 13.4.19 Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 13.2.3 to 13.4.19. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/commits/v13.4.19/packages/eslint-config-next) --- updated-dependencies: - dependency-name: eslint-config-next dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 106 ++++++++++++++++++++++++--------------------------- 2 files changed, 51 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index cbb51546b..b2ba6d238 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "@types/spark-md5": "^3.0.2", "cross-env": "^7.0.3", "eslint": "^8.44.0", - "eslint-config-next": "13.2.3", + "eslint-config-next": "13.4.19", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", "husky": "^8.0.0", diff --git a/yarn.lock b/yarn.lock index 9c7688bc5..01584be73 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1136,10 +1136,10 @@ resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.9.tgz#b77759514dd56bfa9791770755a2482f4d6ca93e" integrity sha512-vuDRK05BOKfmoBYLNi2cujG2jrYbEod/ubSSyqgmEx9n/W3eZaJQdRNhTfumO+qmq/QTzLurW487n/PM/fHOkw== -"@next/eslint-plugin-next@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.2.3.tgz#5af8ddeac6dbe028c812a0e59c41952c004d95d5" - integrity sha512-QmMPItnU7VeojI1KnuwL9SLFWEwmaNHNlnOGpoTwdLoSiP9sc8KYiAHWEc4/44L+cAdCxcZYvn7frcRNP5l84Q== +"@next/eslint-plugin-next@13.4.19": + version "13.4.19" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.4.19.tgz#93d130c37b47fd120f6d111aee36a60611148df1" + integrity sha512-N/O+zGb6wZQdwu6atMZHbR7T9Np5SUFUjZqCbj0sXm+MwQO35M8TazVB4otm87GkXYs2l6OPwARd3/PUWhZBVQ== dependencies: glob "7.1.7" @@ -1548,49 +1548,50 @@ 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== -"@typescript-eslint/parser@^5.42.0": - version "5.57.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.57.0.tgz#f675bf2cd1a838949fd0de5683834417b757e4fa" - integrity sha512-orrduvpWYkgLCyAdNtR1QIWovcNZlEm6yL8nwH/eTxWLd8gsP+25pdLHYzL2QdkqrieaDwLpytHqycncv0woUQ== +"@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" + integrity sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg== dependencies: - "@typescript-eslint/scope-manager" "5.57.0" - "@typescript-eslint/types" "5.57.0" - "@typescript-eslint/typescript-estree" "5.57.0" + "@typescript-eslint/scope-manager" "6.4.0" + "@typescript-eslint/types" "6.4.0" + "@typescript-eslint/typescript-estree" "6.4.0" + "@typescript-eslint/visitor-keys" "6.4.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.57.0": - version "5.57.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.57.0.tgz#79ccd3fa7bde0758059172d44239e871e087ea36" - integrity sha512-NANBNOQvllPlizl9LatX8+MHi7bx7WGIWYjPHDmQe5Si/0YEYfxSljJpoTyTWFTgRy3X8gLYSE4xQ2U+aCozSw== +"@typescript-eslint/scope-manager@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz#3048e4262ba3eafa4e2e69b08912d9037ec646ae" + integrity sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig== dependencies: - "@typescript-eslint/types" "5.57.0" - "@typescript-eslint/visitor-keys" "5.57.0" + "@typescript-eslint/types" "6.4.0" + "@typescript-eslint/visitor-keys" "6.4.0" -"@typescript-eslint/types@5.57.0": - version "5.57.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.57.0.tgz#727bfa2b64c73a4376264379cf1f447998eaa132" - integrity sha512-mxsod+aZRSyLT+jiqHw1KK6xrANm19/+VFALVFP5qa/aiJnlP38qpyaTd0fEKhWvQk6YeNZ5LGwI1pDpBRBhtQ== +"@typescript-eslint/types@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.4.0.tgz#5b109a59a805f0d8d375895e42d9e5f0037f66ee" + integrity sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg== -"@typescript-eslint/typescript-estree@5.57.0": - version "5.57.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.57.0.tgz#ebcd0ee3e1d6230e888d88cddf654252d41e2e40" - integrity sha512-LTzQ23TV82KpO8HPnWuxM2V7ieXW8O142I7hQTxWIHDcCEIjtkat6H96PFkYBQqGFLW/G/eVVOB9Z8rcvdY/Vw== +"@typescript-eslint/typescript-estree@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz#3c58d20632db93fec3d6ab902acbedf593d37276" + integrity sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA== dependencies: - "@typescript-eslint/types" "5.57.0" - "@typescript-eslint/visitor-keys" "5.57.0" + "@typescript-eslint/types" "6.4.0" + "@typescript-eslint/visitor-keys" "6.4.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" -"@typescript-eslint/visitor-keys@5.57.0": - version "5.57.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.57.0.tgz#e2b2f4174aff1d15eef887ce3d019ecc2d7a8ac1" - integrity sha512-ery2g3k0hv5BLiKpPuwYt9KBkAp2ugT6VvyShXdLOkax895EC55sP0Tx5L0fZaQueiK3fBLvHVvEl3jFS5ia+g== +"@typescript-eslint/visitor-keys@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz#96a426cdb1add28274abd7a34aefe27f8b7d51ef" + integrity sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA== dependencies: - "@typescript-eslint/types" "5.57.0" - eslint-visitor-keys "^3.3.0" + "@typescript-eslint/types" "6.4.0" + eslint-visitor-keys "^3.4.1" "@vercel/analytics@^0.1.11": version "0.1.11" @@ -2872,20 +2873,20 @@ 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== -eslint-config-next@13.2.3: - version "13.2.3" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.2.3.tgz#8a952bfd856f492684a30dd5fcdc8979c97c1cc2" - integrity sha512-kPulHiQEHGei9hIaaNGygHRc0UzlWM+3euOmYbxNkd2Nbhci5rrCDeMBMPSV8xgUssphDGmwDHWbk4VZz3rlZQ== +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" + integrity sha512-WE8367sqMnjhWHvR5OivmfwENRQ1ixfNE9hZwQqNCsd+iM3KnuMc1V8Pt6ytgjxjf23D+xbesADv9x3xaKfT3g== dependencies: - "@next/eslint-plugin-next" "13.2.3" + "@next/eslint-plugin-next" "13.4.19" "@rushstack/eslint-patch" "^1.1.3" - "@typescript-eslint/parser" "^5.42.0" + "@typescript-eslint/parser" "^5.4.2 || ^6.0.0" eslint-import-resolver-node "^0.3.6" eslint-import-resolver-typescript "^3.5.2" eslint-plugin-import "^2.26.0" eslint-plugin-jsx-a11y "^6.5.1" eslint-plugin-react "^7.31.7" - eslint-plugin-react-hooks "^4.5.0" + eslint-plugin-react-hooks "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" eslint-config-prettier@^8.8.0: version "8.8.0" @@ -2971,7 +2972,7 @@ eslint-plugin-prettier@^4.2.1: dependencies: prettier-linter-helpers "^1.0.0" -eslint-plugin-react-hooks@^4.5.0: +"eslint-plugin-react-hooks@^4.5.0 || 5.0.0-canary-7118f5dd7-20230705": version "4.6.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== @@ -5326,7 +5327,7 @@ semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.7: +semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -5682,6 +5683,11 @@ trough@^2.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876" integrity sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g== +ts-api-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.2.tgz#7c094f753b6705ee4faee25c3c684ade52d66d99" + integrity sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ== + ts-dedent@^2.2.0: version "2.2.0" resolved "https://registry.npmmirror.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" @@ -5697,23 +5703,11 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - tslib@^2.1.0, tslib@^2.4.0, tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" From 30c656cedae8461b6baeaa7d82250dd1b2cfb73a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:57:23 +0000 Subject: [PATCH 012/108] chore(deps): bump mermaid from 10.2.3 to 10.3.1 Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 10.2.3 to 10.3.1. - [Release notes](https://github.com/mermaid-js/mermaid/releases) - [Changelog](https://github.com/mermaid-js/mermaid/blob/develop/CHANGELOG.md) - [Commits](https://github.com/mermaid-js/mermaid/compare/v10.2.3...v10.3.1) --- updated-dependencies: - dependency-name: mermaid dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 80 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index cbb51546b..32150b84c 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "emoji-picker-react": "^4.4.7", "fuse.js": "^6.6.2", "html-to-image": "^1.11.11", - "mermaid": "^10.2.3", + "mermaid": "^10.3.1", "nanoid": "^4.0.2", "next": "^13.4.9", "node-fetch": "^3.3.1", diff --git a/yarn.lock b/yarn.lock index 9c7688bc5..11d42ad6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1000,10 +1000,10 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@braintree/sanitize-url@^6.0.2": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz#6110f918d273fe2af8ea1c4398a88774bb9fc12f" - integrity sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg== +"@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== "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" @@ -1415,6 +1415,23 @@ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@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" + integrity sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw== + +"@types/d3-scale@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.3.tgz#7a5780e934e52b6f63ad9c24b105e33dd58102b5" + integrity sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ== + dependencies: + "@types/d3-time" "*" + +"@types/d3-time@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.0.tgz#e1ac0f3e9e195135361fa1a1d62f795d87e6e819" + integrity sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg== + "@types/debug@^4.0.0": version "4.1.7" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" @@ -2290,6 +2307,13 @@ cytoscape@^3.23.0: heap "^0.2.6" lodash "^4.17.21" +"d3-array@1 - 2": + version "2.12.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" + integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + dependencies: + internmap "^1.0.0" + "d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.2.0: version "3.2.3" resolved "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.3.tgz#39f1f4954e4a09ff69ac597c2d61906b04e84740" @@ -2406,6 +2430,11 @@ d3-hierarchy@3: dependencies: d3-color "1 - 3" +d3-path@1: + version "1.0.9" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" + integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== + "d3-path@1 - 3", d3-path@3, d3-path@^3.1.0: version "3.1.0" resolved "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" @@ -2426,6 +2455,14 @@ d3-random@3: resolved "https://registry.npmmirror.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4" integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ== +d3-sankey@^0.12.3: + version "0.12.3" + resolved "https://registry.yarnpkg.com/d3-sankey/-/d3-sankey-0.12.3.tgz#b3c268627bd72e5d80336e8de6acbfec9d15d01d" + integrity sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ== + dependencies: + d3-array "1 - 2" + d3-shape "^1.2.0" + d3-scale-chromatic@3: version "3.0.0" resolved "https://registry.npmmirror.com/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#15b4ceb8ca2bb0dcb6d1a641ee03d59c3b62376a" @@ -2457,6 +2494,13 @@ d3-shape@3: dependencies: d3-path "^3.1.0" +d3-shape@^1.2.0: + version "1.3.7" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" + integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== + dependencies: + d3-path "1" + "d3-time-format@2 - 4", d3-time-format@4: version "4.1.0" resolved "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" @@ -2683,10 +2727,10 @@ domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" -dompurify@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.3.tgz#4b115d15a091ddc96f232bcef668550a2f6f1430" - integrity sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ== +dompurify@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.5.tgz#eb3d9cfa10037b6e73f32c586682c4b2ab01fbed" + integrity sha512-F9e6wPGtY+8KNMRAVfxeCOHU0/NPWMSENNq4pQctuXRqqdEPW7q3CrLbR5Nse044WwacyjHGOMlvNsBe1y6z9A== domutils@^2.8.0: version "2.8.0" @@ -3640,6 +3684,11 @@ internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5: resolved "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== +internmap@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95" + integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== + is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" @@ -4266,19 +4315,22 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -mermaid@^10.2.3: - version "10.2.3" - resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-10.2.3.tgz#789d3b582c5da8c69aa4a7c0e2b826562c8c8b12" - integrity sha512-cMVE5s9PlQvOwfORkyVpr5beMsLdInrycAosdr+tpZ0WFjG4RJ/bUHST7aTgHNJbujHkdBRAm+N50P3puQOfPw== +mermaid@^10.3.1: + version "10.3.1" + resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-10.3.1.tgz#2f3c7e9f6bd7a8da2bef71cce2a542c8eba2a62e" + integrity sha512-hkenh7WkuRWPcob3oJtrN3W+yzrrIYuWF1OIfk/d0xGE8UWlvDhfexaHmDwwe8DKQgqMLI8DWEPwGprxkumjuw== dependencies: - "@braintree/sanitize-url" "^6.0.2" + "@braintree/sanitize-url" "^6.0.1" + "@types/d3-scale" "^4.0.3" + "@types/d3-scale-chromatic" "^3.0.0" cytoscape "^3.23.0" cytoscape-cose-bilkent "^4.1.0" cytoscape-fcose "^2.1.0" d3 "^7.4.0" + d3-sankey "^0.12.3" dagre-d3-es "7.0.10" dayjs "^1.11.7" - dompurify "3.0.3" + dompurify "^3.0.5" elkjs "^0.8.2" khroma "^2.0.0" lodash-es "^4.17.21" From 3499dfb285b0638e7948cae0142c62cc8c772272 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:57:49 +0000 Subject: [PATCH 013/108] chore(deps-dev): bump prettier from 2.8.8 to 3.0.2 Bumps [prettier](https://github.com/prettier/prettier) from 2.8.8 to 3.0.2. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/2.8.8...3.0.2) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-major ... 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 cbb51546b..1d9c4bdc4 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "eslint-plugin-prettier": "^4.2.1", "husky": "^8.0.0", "lint-staged": "^13.2.2", - "prettier": "^2.8.8", + "prettier": "^3.0.2", "typescript": "4.9.5", "webpack": "^5.88.1" }, diff --git a/yarn.lock b/yarn.lock index 9c7688bc5..cb6d3736c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4947,10 +4947,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.8.8: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.2.tgz#78fcecd6d870551aa5547437cdae39d4701dca5b" + integrity sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ== prop-types@^15.0.0, prop-types@^15.8.1: version "15.8.1" From 4d3fdbdd80777c516d11cfedb03f2e2776476d2a Mon Sep 17 00:00:00 2001 From: reece00 <37351410+reece00@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:35:51 +0800 Subject: [PATCH 014/108] The mobile terminal ishitbottom does not perform -10 --- app/components/chat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 9a9488dd2..dfda4055b 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -935,7 +935,7 @@ function _Chat() { const isTouchTopEdge = e.scrollTop <= edgeThreshold; const isTouchBottomEdge = bottomHeight >= e.scrollHeight - edgeThreshold; - const isHitBottom = bottomHeight >= e.scrollHeight - 10; + const isHitBottom = bottomHeight >= e.scrollHeight - (isMobileScreen ? 0 : 10); const prevPageMsgIndex = msgRenderIndex - CHAT_PAGE_SIZE; const nextPageMsgIndex = msgRenderIndex + CHAT_PAGE_SIZE; From 1debde30462d0cc933978a71e39d77705ad4a1b3 Mon Sep 17 00:00:00 2001 From: pengoosedev <73521518+pengoosedev@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:08:08 +0900 Subject: [PATCH 015/108] docs: Add README_KO --- README_KO.md | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 README_KO.md diff --git a/README_KO.md b/README_KO.md new file mode 100644 index 000000000..86fba5115 --- /dev/null +++ b/README_KO.md @@ -0,0 +1,187 @@ +

+프리뷰 + +

ChatGPT Next Web

+ +개인 ChatGPT 웹 애플리케이션을 한 번의 클릭으로 무료로 배포하세요. + +[데모 Demo](https://chat-gpt-next-web.vercel.app/) / [피드백 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discord 참여](https://discord.gg/zrhvHCr79N) / [QQ 그룹](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [개발자에게 기부](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) / [기부 Donate](#기부-donate-usdt) + +[![Vercel로 배포하기](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web) + +[![Gitpod에서 열기](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) + +![메인 화면](./docs/images/cover.png) + +
+ +## 사용 시작 + +1. [OpenAI API Key](https://platform.openai.com/account/api-keys)를 준비합니다. +2. 오른쪽 버튼을 클릭하여 배포를 시작하십시오: + [![Vercel로 배포하기](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web). Github 계정으로 바로 로그인하십시오. API Key와 [페이지 접근 비밀번호](#페이지-접근-비밀번호-설정) CODE를 환경 변수 페이지에 입력하십시오. +3. 배포가 완료되면 사용을 시작하십시오. +4. (선택 사항) [사용자 정의 도메인 바인딩](https://vercel.com/docs/concepts/projects/domains/add-a-domain) : Vercel에서 할당한 도메인 DNS가 일부 지역에서 오염되어 있습니다. 사용자 정의 도메인을 바인딩하면 직접 연결할 수 있습니다. + +## 업데이트 유지 + +위의 단계대로 프로젝트를 배포한 경우 "업데이트가 있습니다"라는 메시지가 항상 표시될 수 있습니다. 이는 Vercel이 기본적으로 새 프로젝트를 생성하고이 프로젝트를 포크하지 않기 때문입니다. 이 문제는 업데이트를 올바르게 감지할 수 없습니다. +아래 단계를 따라 다시 배포하십시오: + +- 기존 저장소를 삭제합니다. +- 페이지 오른쪽 상단의 포크 버튼을 사용하여 이 프로젝트를 포크합니다. +- Vercel에서 다시 선택하여 배포하십시오. [자세한 튜토리얼 보기](./docs/vercel-cn.md#새-프로젝트-만드는-방법). + +### 자동 업데이트 활성화 + +> Upstream Sync 오류가 발생한 경우 수동으로 Sync Fork를 한 번 실행하십시오! + +프로젝트를 포크한 후 GitHub의 제한으로 인해 포크한 프로젝트의 동작 페이지에서 워크플로우를 수동으로 활성화해야 합니다. Upstream Sync Action을 활성화하면 매시간마다 자동 업데이트가 활성화됩니다: + +![자동 업데이트](./docs/images/enable-actions.jpg) + +![자동 업데이트 활성화](./docs/images/enable-actions-sync.jpg) + +### 수동으로 코드 업데이트 + +수동으로 즉시 업데이트하려면 [GitHub 문서](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork)에서 포크된 프로젝트를 어떻게 원본 코드와 동기화하는지 확인하십시오. + +이 프로젝트에 별표/감시를 부여하거나 작성자를 팔로우하여 새 기능 업데이트 알림을 받을 수 있습니다. + +## 페이지 접근 비밀번호 설정 + +> 비밀번호가 설정된 후, 사용자는 설정 페이지에서 접근 코드를 수동으로 입력하여 정상적으로 채팅할 수 있습니다. 그렇지 않으면 메시지를 통해 권한이 없는 상태가 표시됩니다. + +> **경고** : 비밀번호의 길이를 충분히 길게 설정하십시오. 최소 7 자리 이상이 좋습니다. 그렇지 않으면 [해킹될 수 있습니다](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/518). + +이 프로젝트는 제한된 권한 제어 기능을 제공합니다. Vercel 프로젝트 컨트롤 패널의 환경 변수 페이지에서 `CODE`라는 환경 변수를 추가하십시오. 값은 쉼표로 구분된 사용자 정의 비밀번호로 설정됩니다. (아래 예시의 경우 `code1` `code2` `code3` 3개의 비밀번호가 생성됩니다.) + +``` +code1,code2,code3 +``` + +이 환경 변수를 추가하거나 수정한 후에는 프로젝트를 다시 배포하여 변경 사항을 적용해야 합니다. + +## 환경 변수 +> 이 프로젝트에서 대부분의 설정 요소들은 환경 변수를 통해 설정됩니다. [Vercel 환경변수 수정 방법.](./docs/vercel-ko.md)。 + +## OPENAI_API_KEY (필수 항목) + +OpenAI 키로, openai 계정 페이지에서 신청한 api key입니다. + +## CODE (선택 가능) + +접근 비밀번호로, 선택적입니다. 쉼표를 사용하여 여러 비밀번호를 구분할 수 있습니다. + +**경고** : 이 항목을 입력하지 않으면, 누구나 여러분이 배포한 웹사이트를 직접 사용할 수 있게 됩니다. 이로 인해 토큰이 빠르게 소진될 수 있으므로, 이 항목을 반드시 입력하는 것이 좋습니다. + +## BASE_URL (선택 가능) + +> 기본값: `https://api.openai.com` + +> 예시: `http://your-openai-proxy.com` + +OpenAI 인터페이스 프록시 URL입니다. 만약, 수동으로 openai 인터페이스 proxy를 설정했다면, 이 항목을 입력하셔야 합니다. + +**참고**: SSL 인증서 문제가 발생한 경우, BASE_URL의 프로토콜을 http로 설정하세요. + +## OPENAI_ORG_ID (선택 가능) + +OpenAI 내의 조직 ID를 지정합니다. + +## HIDE_USER_API_KEY (선택 가능) + +사용자가 API Key를 직접 입력하는 것을 원하지 않는 경우, 이 환경 변수를 1로 설정하세요. + +## DISABLE_GPT4 (선택 가능) + +사용자가 GPT-4를 사용하는 것을 원하지 않는 경우, 이 환경 변수를 1로 설정하세요. + +## HIDE_BALANCE_QUERY (선택 가능) + +사용자가 잔액을 조회하는 것을 원하지 않는 경우, 이 환경 변수를 1로 설정하세요. + +## 개발 + +아래 버튼을 클릭하여 개발을 시작하세요: + +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) + +코드 작성을 전, 프로젝트 루트 디렉토리에 `.env.local` 파일을 새로 만들고 해당 파일에 환경 변수를 입력해야 합니다: + +``` +OPENAI_API_KEY=<여기에 여러분의 api 키를 입력하세요> + +#중국 사용자들은 이 프로젝트에 포함된 프록시를 사용하여 개발할 수 있습니다. 또는 다른 프록시 주소를 자유롭게 선택할 수 있습니다. +BASE_URL=https://chatgpt1.nextweb.fun/api/proxy +``` + + +### 로컬 환경에서의 개발 + +1. nodejs 18과 yarn을 설치하세요. 자세한 사항은 ChatGPT에 문의하십시오. +2. `yarn install && yarn dev` 명령을 실행하세요. ⚠️ 주의: 이 명령은 로컬 개발 전용입니다. 배포용으로 사용하지 마십시오! +3. 로컬에서 배포하고 싶다면, `yarn install && yarn build && yarn start` 명령을 사용하세요. pm2와 함께 사용하여 프로세스를 보호하고, 강제 종료되지 않도록 할 수 있습니다. 자세한 내용은 ChatGPT에 문의하세요. + +## 배포 + +### 컨테이너 배포 (추천) + +> Docker 버전은 20 이상이어야 합니다. 그렇지 않으면 이미지를 찾을 수 없다는 메시지가 표시됩니다. + +> ⚠️ 주의: docker 버전은 대부분의 경우 최신 버전보다 1~2일 뒤처집니다. 따라서 배포 후 "업데이트 가능" 알림이 지속적으로 나타날 수 있으며, 이는 정상적인 현상입니다. + +```shell +docker pull yidadaa/chatgpt-next-web + +docker run -d -p 3000:3000 \ + -e OPENAI_API_KEY="sk-xxxx" \ + -e CODE="페이지 접근 비밀번호" \ + yidadaa/chatgpt-next-web +``` + +프록시를 지정하려면 다음을 사용하세요: + +```shell +docker run -d -p 3000:3000 \ + -e OPENAI_API_KEY="sk-xxxx" \ + -e CODE="페이지 접근 비밀번호" \ + --net=host \ + -e PROXY_URL="http://127.0.0.1:7890" \ + yidadaa/chatgpt-next-web +``` + +로컬 프록시에 사용자 이름과 비밀번호가 필요한 경우, 아래와 같이 사용하세요: + +```shell +-e PROXY_URL="http://127.0.0.1:7890 사용자이름 비밀번호" +``` + +다른 환경 변수를 지정해야 하는 경우, 위의 명령에 `-e 환경변수=환경변수값`을 추가하여 지정하세요. + +### 로컬 배포 + +콘솔에서 아래의 명령을 실행하세요: + +```shell +bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh) +``` + +⚠️ 주의: 설치 중 문제가 발생한 경우, docker로 배포하세요. + +## 감사의 말 + +### 기부자 + +> 영문 버전 참조. + +### 기여자 + +[프로젝트 기여자 목록 보기](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors) + +### 관련 프로젝트 +- [one-api](https://github.com/songquanpeng/one-api): 통합 대형 모델 할당 관리 플랫폼, 주요 대형 언어 모델 모두 지원 + +## 오픈소스 라이센스 + +[MIT](https://opensource.org/license/mit/) From bc0f18409863c0f7654050b49d79a2666bcb99c5 Mon Sep 17 00:00:00 2001 From: pengoosedev <73521518+pengoosedev@users.noreply.github.com> Date: Wed, 23 Aug 2023 01:08:31 +0900 Subject: [PATCH 016/108] docs: Add cloudflare-pages-ko --- docs/cloudflare-pages-ko.md | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/cloudflare-pages-ko.md diff --git a/docs/cloudflare-pages-ko.md b/docs/cloudflare-pages-ko.md new file mode 100644 index 000000000..68a96232f --- /dev/null +++ b/docs/cloudflare-pages-ko.md @@ -0,0 +1,39 @@ +## Cloudflare 페이지 배포 가이드 + +## 새 프로젝트를 만드는 방법 +이 프로젝트를 Github에서 포크한 다음 dash.cloudflare.com에 로그인하고 페이지로 이동합니다. + +1. "프로젝트 만들기"를 클릭합니다. +2. "Git에 연결"을 선택합니다. +3. Cloudflare 페이지를 GitHub 계정과 연결합니다. +4. 포크한 프로젝트를 선택합니다. +5. "설정 시작"을 클릭합니다. +6. "프로젝트 이름" 및 "프로덕션 브랜치"의 기본값을 사용하거나 필요에 따라 변경합니다. +7. "빌드 설정"에서 "프레임워크 프리셋" 옵션을 선택하고 "Next.js"를 선택합니다. +8. node:buffer 버그로 인해 지금은 기본 "빌드 명령어"를 사용하지 마세요. 다음 명령을 사용하세요: + `` + npx https://prerelease-registry.devprod.cloudflare.dev/next-on-pages/runs/4930842298/npm-package-next-on-pages-230 --experimental- minify + ``` +9. "빌드 출력 디렉토리"의 경우 기본값을 사용하고 수정하지 마십시오. +10. "루트 디렉토리"는 수정하지 마십시오. +11. "환경 변수"의 경우 ">"를 클릭한 다음 "변수 추가"를 클릭합니다. 다음에 따라 정보를 입력합니다: + + - node_version=20.1`. + - next_telemetry_disable=1`. + - `OPENAI_API_KEY=자신의 API 키` + - ``yarn_version=1.22.19`` + - ``php_version=7.4``. + + 실제 필요에 따라 다음 옵션을 선택적으로 입력합니다: + + - `CODE= 선택적으로 액세스 비밀번호를 입력하며 쉼표를 사용하여 여러 비밀번호를 구분할 수 있습니다`. + - `OPENAI_ORG_ID= 선택 사항, OpenAI에서 조직 ID 지정` + - `HIDE_USER_API_KEY=1 선택 사항, 사용자가 API 키를 입력하지 못하도록 합니다. + - `DISABLE_GPT4=1 옵션, 사용자가 GPT-4를 사용하지 못하도록 설정` 12. + +12. "저장 후 배포"를 클릭합니다. +13. 호환성 플래그를 입력해야 하므로 "배포 취소"를 클릭합니다. +14. "빌드 설정", "기능"으로 이동하여 "호환성 플래그"를 찾습니다. +"프로덕션 호환성 플래그 구성" 및 "프리뷰 호환성 플래그 구성"에서 "nodejs_compat"를 입력합니다. +16. "배포"로 이동하여 "배포 다시 시도"를 클릭합니다. +17. 즐기세요! \ No newline at end of file From b146cd889fd3d17220a6d8200c8625a4a076496c Mon Sep 17 00:00:00 2001 From: pengoosedev <73521518+pengoosedev@users.noreply.github.com> Date: Wed, 23 Aug 2023 01:28:54 +0900 Subject: [PATCH 017/108] Add faq-ko --- docs/faq-ko.md | 230 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 docs/faq-ko.md diff --git a/docs/faq-ko.md b/docs/faq-ko.md new file mode 100644 index 000000000..9eb6bbbb2 --- /dev/null +++ b/docs/faq-ko.md @@ -0,0 +1,230 @@ +# 자주 묻는 질문 + +## 어떻게 빠르게 도움을 받을 수 있나요? + +1. ChatGPT / Bing / Baidu / Google 등에 질문합니다. +2. 인터넷 사용자에게 질문합니다. 문제의 배경 정보와 자세한 문제 설명을 제공하세요. 질 좋은 질문은 유용한 답변을 쉽게 받을 수 있습니다. + +# 배포 관련 질문 + +각종 배포 방법에 대한 자세한 튜토리얼 참조: [링크](https://rptzik3toh.feishu.cn/docx/XtrdduHwXoSCGIxeFLlcEPsdn8b) + +## 왜 Docker 배포 버전이 계속 업데이트 알림을 주나요? + +Docker 버전은 사실상 안정된 버전과 같습니다. latest Docker는 항상 latest release version과 일치합니다. 현재 우리의 발행 빈도는 하루 또는 이틀에 한 번이므로 Docker 버전은 항상 최신 커밋보다 하루나 이틀 뒤처집니다. 이것은 예상된 것입니다. + +## Vercel에서 어떻게 배포하나요? + +1. Github 계정을 등록하고, 이 프로젝트를 포크합니다. +2. Vercel을 등록합니다(휴대폰 인증 필요, 중국 번호 사용 가능), Github 계정을 연결합니다. +3. Vercel에서 새 프로젝트를 생성하고, Github에서 포크한 프로젝트를 선택합니다. 환경 변수를 필요에 따라 입력한 후 배포를 시작합니다. 배포 후에는 VPN이 있는 환경에서 Vercel이 제공하는 도메인으로 프로젝트에 접근할 수 있습니다. +4. 중국에서 방화벽 없이 접근하려면: 도메인 관리 사이트에서 도메인의 CNAME 레코드를 추가하고, cname.vercel-dns.com을 가리키게 합니다. 그런 다음 Vercel에서 도메인 접근을 설정합니다. + +## Vercel 환경 변수를 어떻게 수정하나요? + +- Vercel의 제어판 페이지로 이동합니다. +- chatgpt next web 프로젝트를 선택합니다. +- 페이지 상단의 Settings 옵션을 클릭합니다. +- 사이드바의 Environment Variables 옵션을 찾습니다. +- 해당 값을 수정합니다. + +## 환경 변수 CODE는 무엇이며, 반드시 설정해야 하나요? + +이것은 당신이 사용자 정의한 접근 비밀번호입니다. 다음 중 하나를 선택할 수 있습니다: + +1. 설정하지 않습니다. 해당 환경 변수를 삭제합니다. 주의: 이 경우 누구나 프로젝트에 접근할 수 있습니다. +2. 프로젝트를 배포할 때 환경 변수 CODE를 설정합니다(여러 비밀번호는 쉼표로 구분). 접근 비밀번호를 설정하면 사용자는 설정 페이지에서 접근 비밀번호를 입력해야만 사용할 수 있습니다. [관련 설명 참조](https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/README_CN.md#%E9%85%8D%E7%BD%AE%E9%A1%B5%E9%9D%A2%E8%AE%BF%E9%97%AE%E5%AF%86%E7%A0%81) + +## 왜 내 배포 버전에 스트리밍 응답이 없나요? + +> 관련 토론: [#386](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/386) + +nginx 리버스 프록시를 사용하는 경우, 설정 파일에 다음 코드를 추가해야 합니다: + +```nginx +# 캐시하지 않고, 스트리밍 출력 지원 +proxy_cache off; # 캐시 비활성화 +proxy_buffering off; # 프록시 버퍼링 비활성화 +chunked_transfer_encoding on; # 청크 전송 인코딩 활성화 +tcp_nopush on; # TCP NOPUSH 옵션 활성화, Nagle 알고리즘 금지 +tcp_nodelay on; # TCP NODELAY 옵션 활성화, 지연 ACK 알고리즘 금지 +keepalive_timeout 300; # keep-alive 타임아웃을 65초로 설정 +``` + +netlify에서 배포하는 경우, 이 문제는 아직 해결되지 않았습니다. 기다려 주십시오. + +## 배포했지만 액세스할 수 없는 경우. + +다음의 사항들을 확인해보세요: + +- 서비스가 배포 중인가요? +- 포트가 올바르게 매핑되었나요? +- 방화벽에서 포트가 열렸나요? +- 서버 경로가 유효한가요? +- 도메인 이름이 올바른가요? + +## 프록시란 무엇이며 어떻게 사용하나요? + +중국 및 일부 국가에서는 OpenAI의 IP 제한으로 인해 OpenAI API에 직접 연결할 수 없으며 프록시를 거쳐야 합니다. 프록시 서버(정방향 프록시)를 사용하거나 OpenAI API에 대해 설정된 역방향 프록시를 사용할 수 있습니다. + +- 정방향 프록시 예: 사이언티픽 인터넷 래더. 도커 배포의 경우 환경 변수 HTTP_PROXY를 프록시 주소(예: 10.10.10.10:8002)로 설정합니다. +- 역방향 프록시 예: 다른 사람이 구축한 프록시 주소를 사용하거나 Cloudflare를 통해 무료로 설정할 수 있습니다. 프로젝트 환경 변수 BASE_URL을 프록시 주소로 설정합니다. + +## 국내 서버를 배포할 수 있나요? + +예. 하지만 해결해야 할 문제가 있습니다: + +- github 및 openAI와 같은 사이트에 연결하려면 프록시가 필요합니다; +- 도메인 이름 확인을 설정하려면 국내 서버를 신청해야 합니다; +- 국내 정책에 따라 프록시가 엑스트라넷/ChatGPT 관련 애플리케이션에 액세스하지 못하도록 제한되어 차단될 수 있습니다. + +## 도커 배포 후 네트워크 오류가 발생하는 이유는 무엇인가요? + +https://github.com/Yidadaa/ChatGPT-Next-Web/issues/1569 에서 토론을 참조하세요. + +## 사용 관련 문제 + +## "문제가 발생했습니다, 나중에 다시 시도하세요"라는 메시지가 계속 뜨는 이유는 무엇인가요? + +여러 가지 이유가 있을 수 있으니 순서대로 확인해 주세요: + +- 코드 버전이 최신 버전인지 확인하고, 최신 버전으로 업데이트한 후 다시 시도해 주세요; +- API 키가 올바르게 설정되었는지 확인해주세요. 환경 변수 이름은 모두 대문자이며 밑줄이 있어야 합니다; +- API 키가 사용 가능한지 확인해 주세요; +- 위 단계를 수행한 후에도 문제를 확인할 수 없는 경우, 이슈 영역에 신규 이슈를 제출하고 버셀의 런타임 로그 또는 도커 런타임 로그를 첨부해 주시기 바랍니다. + +## ChatGPT 응답이 왜곡되는 이유는 무엇인가요? + +설정 - 모델 설정 섹션에 '온도'에 대한 값이 있는데, 이 값이 1보다 크면 응답이 왜곡될 수 있으니 1 이내로 다시 설정해 주세요. + +## "권한이 없는 상태입니다, 설정 페이지에서 액세스 비밀번호를 입력하세요"? + +프로젝트에서 환경 변수 CODE에 접근 비밀번호를 설정했습니다. 처음 사용할 때는 설정 페이지에서 액세스 코드를 입력해야 합니다. + +## 사용 시 "현재 할당량을 초과했습니다, ..."라는 메시지가 표시됩니다. + +API 키에 문제가 있습니다. 잔액이 부족합니다. + +## "오류: CSS 청크 xxx를 로드하지 못했습니다..."와 함께 사용. + +첫 번째 화이트 스크린 시간을 줄이기 위해 청크 컴파일이 기본적으로 활성화되어 있으며, 기술 원칙은 아래를 참조하세요: + +- https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading +- https://stackoverflow.com/questions/55993890/how-can-i-disable-chunkcode-splitting-with-webpack4 +- https://github.com/vercel/next.js/issues/38507 +- https://stackoverflow.com/questions/55993890/how-can-i-disable-chunkcode-splitting-with-webpack4 + +그러나 NextJS는 호환성이 좋지 않아 구형 브라우저에서 이 오류가 발생할 수 있으므로 컴파일 시 청크 컴파일을 비활성화할 수 있습니다. + +버셀 플랫폼의 경우 환경 변수에 `DISABLE_CHUNK=1`을 추가하고 다시 배포합니다; +자체 컴파일 및 배포한 프로젝트의 경우, 빌드 시 `DISABLE_CHUNK=1 yarn build`를 사용하여 빌드합니다; +Docker 사용자의 경우, Docker가 프로젝트를 패키징할 때 이미 빌드하기 때문에 이 기능을 해제하는 것은 지원되지 않습니다. + +이 기능을 끄면 사용자가 웹사이트를 처음 방문할 때 모든 리소스를 로드하므로 인터넷 연결 상태가 좋지 않은 경우 흰색 화면이 길게 표시되어 사용자 경험에 영향을 줄 수 있으므로 사용자가 직접 고려하시기 바랍니다. + +"## NotFoundError: '노드': 노드....에서 'removeChild'를 실행하지 못했습니다." 오류가 발생했습니다. +브라우저의 자체 자동 번역 기능을 비활성화하고 모든 자동 번역 플러그인을 닫아주세요. + +## 웹 서비스 관련 문제 + +## 클라우드플레어란 무엇인가요? + +Cloudflare(CF)는 CDN, 도메인 관리, 정적 페이지 호스팅, 엣지 컴퓨팅 기능 배포 등을 제공하는 웹 서비스 제공업체입니다. 일반적인 용도: 도메인 구매 및/또는 호스팅(리졸브, 동적 도메인 등), 서버에 CDN 설치(벽에서 IP를 숨기는 기능), 웹사이트 배포(CF 페이지). CF는 이러한 서비스 대부분을 무료로 제공합니다. + +## Vercel이란 무엇인가요? + +Vercel은 개발자가 최신 웹 애플리케이션을 더 빠르게 빌드하고 배포할 수 있도록 설계된 글로벌 클라우드 플랫폼입니다. 이 프로젝트와 많은 웹 애플리케이션을 클릭 한 번으로 Vercel에 무료로 배포할 수 있습니다. 코드, 리눅스, 서버, 수수료가 필요 없고 OpenAI API 프록시를 설정할 필요도 없습니다. 단점은 중국에서 장벽 없이 액세스하려면 도메인 이름을 바인딩해야 한다는 것입니다. + +## 도메인 네임은 어떻게 얻나요? + +1) 도메인 네임 공급업체로 이동하여 해외에서는 Namesilo(알리페이 지원), 클라우드플레어 등, 중국에서는 월드와이드웹과 같은 도메인 네임을 등록합니다. 2) 무료 도메인 네임 공급업체: 예: eBay; +2. 무료 도메인 네임 제공업체: eu.org(두 번째 레벨 도메인 네임) 등..; +3. 친구에게 무료 2단계 도메인 네임을 요청합니다. + +## 서버를 얻는 방법 + +- 외국 서버 제공업체의 예: 아마존 클라우드, 구글 클라우드, 벌터, 밴드왜건, 호스트데어 등; + 해외 서버 문제: 서버 라인은 해당 국가의 액세스 속도에 영향을 미치므로 CN2 GIA 및 CN2 라인 서버를 권장합니다. 국내 서버의 접속에 문제가 있는 경우(심각한 패킷 손실 등) CDN(Cloudflare 및 기타 제공 업체)을 설정해 볼 수 있습니다. +- 국내 서버 제공업체: 알리윈, 텐센트 등; + 국내 서버 문제: 도메인 이름 확인을 신청해야 하며, 국내 서버 대역폭이 더 비싸고, 해외 사이트(Github, openAI 등)에 액세스하려면 프록시가 필요합니다. + +## 서버는 언제 신청해야 하나요? + +중국 본토에서 운영되는 웹사이트는 규제 요건에 따라 신고해야 합니다. 실제로 서버가 중국에 있고 도메인 네임 레졸루션이 있는 경우 서버 제공업체가 규제 신고 요건을 시행하며, 그렇지 않으면 서비스가 종료됩니다. 일반적인 규칙은 다음과 같습니다: +|서버 위치|도메인 네임 공급자|파일링 필요 여부| +|---|---|---| +|국내|국내|예 +|국내|외국|예 +|외국|외국인|아니요 +|외국|국내|일반적으로 아니요| + +서버 공급자를 전환한 후 파일링을 전환해야 합니다. + +## OpenAI 관련 질문 + +## OpenAI 계정은 어떻게 가입하나요? + +chat.openai.com으로 이동하여 등록하세요. 다음이 필요합니다: + +- 유효한 래더(OpenAI는 지역별 기본 IP 주소를 지원합니다) +- 지원되는 이메일 주소(예: Outlook이나 qq가 아닌 Gmail 또는 회사/학교 이메일) +- SMS 인증을 받을 수 있는 방법(예: SMS 활성화 웹사이트) + +## OpenAI API는 어떻게 열 수 있나요? API 잔액은 어떻게 확인하나요? + +공식 웹사이트 주소(래더 필요): https://platform.openai.com/account/usage +일부 사용자는 래더 없이 잔액 조회 에이전트를 구축한 경우가 있으니, 해당 사용자에게 요청해 주시기 바랍니다. API 키 유출을 방지하기 위해 신뢰할 수 있는 소스인지 확인하시기 바랍니다. + +## 새로 등록한 OpenAI 계정에 API 잔액이 없는 이유는 무엇인가요? + +(4월 6일 업데이트) 새로 등록된 계정은 일반적으로 24시간 후에 API 잔액이 표시됩니다. 현재 새로 등록된 계정에는 $5의 잔액이 표시됩니다. + +## OpenAI API를 충전하려면 어떻게 해야 하나요? + +OpenAI는 특정 지역의 신용카드만 사용할 수 있습니다(중국 신용카드는 사용할 수 없음). 충전 방법의 몇 가지 예는 다음과 같습니다: + +1. 가상 신용카드로 결제하기 +2. 해외 신용카드 신청 +3. 온라인에서 신용카드를 충전할 사람 찾기 + +## GPT-4 API 액세스는 어떻게 사용하나요? + +- GPT-4 API 액세스는 별도의 신청이 필요합니다. 다음 주소로 이동하여 정보를 입력하여 신청 대기열 대기자 명단에 들어가세요(OpenAI 조직 ID를 준비하세요): https://openai.com/waitlist/gpt-4-api. + 그런 다음 이메일 메시지를 기다립니다. +- ChatGPT Plus를 사용하도록 설정했다고 해서 GPT-4 권한이 있는 것은 아니며, 서로 관련이 없습니다. + +## Azure OpenAI 인터페이스 사용 방법 + +참조: [#371](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/371) + +## 내 토큰이 왜 이렇게 빨리 소모되나요? + +> 관련 토론: [#518](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/518) + +- GPT 4에 액세스 권한이 있고 매일 GPT 4 API를 사용하는 경우, GPT 4 가격이 GPT 3.5의 약 15배이기 때문에 청구 금액이 급격히 증가합니다; +- GPT 3.5를 자주 사용하지 않는데도 요금이 급격하게 증가하는 경우 아래 단계를 따라 확인하시기 바랍니다: + - 오픈아이 공식 웹사이트로 이동하여 API 키 소비 기록을 확인하고, 매 시간마다 토큰이 소비되고 매번 수만 개의 토큰이 소비된다면 키가 유출된 것이므로 즉시 삭제하고 재생성하시기 바랍니다. 즉시 키를 삭제하고 다시 생성하시기 바랍니다. 지저분한 웹사이트에서 잔액을 확인하지 마세요. ** + - 비밀번호 설정이 5자리 이내의 문자와 같이 매우 짧으면 블라스팅 비용이 매우 낮습니다. 도커의 로그 기록을 검색하여 누군가 많은 수의 비밀번호 조합을 시도했는지 확인하는 것이 좋습니다. 키워드: 액세스 코드를 얻었습니다. +- 이 두 가지 방법을 사용하면 토큰이 소비되는 이유를 빠르게 찾을 수 있습니다: + - 오픈아이 소비 기록은 비정상적이지만 도커 로그는 정상이라면 API 키가 유출되고 있다는 뜻입니다; + - 도커 로그에서 액세스 코드 버스트 레코드가 많이 발견되면 비밀번호가 버스트된 것입니다. + + +## API의 가격은 어떻게 청구되나요? + +OpenAI의 청구 지침은 https://openai.com/pricing#language-models 에서 확인할 수 있습니다. +OpenAI는 토큰 수에 따라 요금을 청구하며, 일반적으로 1000토큰은 영어 단어 750개 또는 중국어 문자 500개를 나타냅니다. 입력(프롬프트)과 출력(완료)은 별도로 청구됩니다. + +|모델|사용자 입력(프롬프트) 청구 |모델 출력(완료) 청구 |인터랙션당 최대 토큰 수 | +|----|----|----|----| +|GPT-3.5-TURBO|$0.0015 / 1천 토큰|$0.002 / 1천 토큰|4096| +|GPT-3.5-TURBO-16K|$0.003 / 1천 토큰|$0.004 / 1천 토큰|16384| |GPT-4|$0.004 / 1천 토큰|16384 +|GPT-3.5-TURBO-16K|$0.003 / 1천 토큰|$0.004 / 1천 토큰|16384| |GPT-4|$0.03 / 1천 토큰|$0.06 / 1천 토큰|8192 +|GPT-4-32K|$0.06 / 1천 토큰|$0.12 / 1천 토큰|32768| + +## gpt-3.5-터보와 gpt3.5-터보-0301(또는 gpt3.5-터보-mmdd) 모델의 차이점은 무엇인가요? + +공식 문서 설명: https://platform.openai.com/docs/models/gpt-3-5 + +- GPT-3.5-TURBO는 최신 모델이며 지속적으로 업데이트될 예정입니다. +- gpt-3.5-turbo-0301은 3월 1일에 고정된 모델의 스냅샷으로, 변경되지 않으며 3개월 후에 새로운 스냅샷으로 대체될 예정입니다. \ No newline at end of file From d3de07ecf3b1439f42f452c08d114184da71d2b9 Mon Sep 17 00:00:00 2001 From: pengoosedev <73521518+pengoosedev@users.noreply.github.com> Date: Wed, 23 Aug 2023 01:33:46 +0900 Subject: [PATCH 018/108] docs: Add vercel-ko --- docs/vercel-ko.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/vercel-ko.md diff --git a/docs/vercel-ko.md b/docs/vercel-ko.md new file mode 100644 index 000000000..725a827dc --- /dev/null +++ b/docs/vercel-ko.md @@ -0,0 +1,39 @@ +# Vercel 사용 방법 + +## 새 프로젝트 생성 방법 +이 프로젝트를 Github에서 포크한 후, 다시 배포하려면 Vercel에서 새로운 Vercel 프로젝트를 생성해야 하며, 다음 단계를 따라야 합니다. + +![vercel-create-1](./images/vercel/vercel-create-1.jpg) +1. Vercel 콘솔 홈 페이지로 이동합니다; +2. 새로 추가를 클릭합니다; +3. 프로젝트를 선택합니다. + +![vercel-create-2](./images/vercel/vercel-create-2.jpg) +1. Git 리포지토리 가져오기에서 chatgpt-next-web을 검색합니다. 2. 새 포크를 선택합니다; +2. 새로 포크된 프로젝트를 선택하고 가져오기를 클릭합니다. + +![vercel-create-3](./images/vercel/vercel-create-3.jpg) +1. 프로젝트 구성 페이지에서 환경 변수 설정을 클릭하여 환경 변수 설정을 시작합니다; +2. OPENAI_API_KEY, CODE ([Access Code](https://github.com/Yidadaa/ChatGPT-Next-Web/blob/357296986609c14de10bf210871d30e2f67a8784/docs/faq-cn.md#%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F-code-%E6%98%AF%E4%BB%80%E4%B9%88%E5%BF%85%E9%A1%BB%E8%AE%BE%E7%BD%AE%E5%90%97)). 환경 변수를 설정합니다; +3. 환경 변수의 값을 입력합니다; +4. 추가를 클릭하여 환경 변수 추가를 확인합니다; +5. OPENAI_API_KEY를 추가해야 하며, 그렇지 않으면 작동하지 않습니다; +6. 배포를 클릭하여 도메인 이름 생성을 완료하고 배포가 완료될 때까지 약 5분간 기다립니다. + +## 사용자 정의 도메인 네임 추가 방법 +[TODO] + +## 환경 변수 변경 방법 +![vercel-env-edit](./images/vercel/vercel-env-edit.jpg) +1. 버셀 프로젝트의 내부 콘솔로 이동하여 상단의 설정 버튼을 클릭합니다; +2. 왼쪽의 환경 변수를 클릭합니다; +3. 기존 항목 오른쪽에 있는 버튼을 클릭합니다; +4. 편집을 선택하여 수정하고 저장합니다. + +⚠️️ 참고: 환경 변수를 변경할 때마다 [프로젝트를 재배포](#如何重新部署)해야 변경 사항을 적용할 수 있습니다! + +## 재배포 방법 +![vercel-redeploy](./images/vercel/vercel-redeploy.jpg) +1. 버셀 내부 프로젝트 콘솔로 이동하여 상단의 배포 버튼을 클릭합니다; +2. 목록에서 맨 위 항목 오른쪽에 있는 버튼을 선택합니다; +3. 재배포를 클릭하여 재배포합니다. \ No newline at end of file From 0113d4499b310b1e507ffb9738dd85585bd88336 Mon Sep 17 00:00:00 2001 From: B0zal Date: Wed, 23 Aug 2023 21:14:43 +0700 Subject: [PATCH 019/108] [Feature] Better JSON Exporter #2692 [+] A view looks better [+] auto minify json when click a copy in markdown and download Co-Authored-By: wangwentong-lunaon <39506652+wangwentong-lunaon@users.noreply.github.com> Co-Authored-By: Yifei Zhang Co-Authored-By: B0zal <48602426+kfear1337@users.noreply.github.com> --- app/components/exporter.tsx | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/app/components/exporter.tsx b/app/components/exporter.tsx index 604b8823d..2e3cd84aa 100644 --- a/app/components/exporter.tsx +++ b/app/components/exporter.tsx @@ -565,21 +565,32 @@ export function MarkdownPreviewer(props: { ); } +// modified by BackTrackZ now it's looks better + export function JsonPreviewer(props: { messages: ChatMessage[]; topic: string; }) { - const msgs = props.messages.map((m) => ({ - role: m.role, - content: m.content, - })); - const mdText = "\n" + JSON.stringify(msgs, null, 2) + "\n"; + const msgs = { + messages: [ + { + role: "system", + content: "You are an assistant that " + props.topic, + }, + ...props.messages.map((m) => ({ + role: m.role, + content: m.content, + })), + ], + }; + const mdText = "```json\n" + JSON.stringify(msgs, null, 2) + "\n```"; + const minifiedJson = JSON.stringify(msgs); const copy = () => { - copyToClipboard(JSON.stringify(msgs, null, 2)); + copyToClipboard(minifiedJson); }; const download = () => { - downloadAs(JSON.stringify(msgs, null, 2), `${props.topic}.json`); + downloadAs(JSON.stringify(msgs), `${props.topic}.json`); }; return ( @@ -587,12 +598,12 @@ export function JsonPreviewer(props: { -
-
{mdText}
+
+
); -} +} \ No newline at end of file From 8cac51abbefcb6e939f1190d50eb4b966680542a Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Thu, 24 Aug 2023 10:54:28 +0800 Subject: [PATCH 020/108] chore: #2699 Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e1f42e1a0..25801e643 100644 --- a/README.md +++ b/README.md @@ -230,8 +230,8 @@ yarn dev docker pull yidadaa/chatgpt-next-web docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY="sk-xxxx" \ - -e CODE="your-password" \ + -e OPENAI_API_KEY=sk-xxxx \ + -e CODE=your-password \ yidadaa/chatgpt-next-web ``` @@ -239,9 +239,9 @@ You can start service behind a proxy: ```shell docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY="sk-xxxx" \ - -e CODE="your-password" \ - -e PROXY_URL="http://localhost:7890" \ + -e OPENAI_API_KEY=sk-xxxx \ + -e CODE=your-password \ + -e PROXY_URL=http://localhost:7890 \ yidadaa/chatgpt-next-web ``` From d8b6ebf6cbcfcad7865f51e4a75e912a9aa87d8f Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Thu, 24 Aug 2023 11:09:17 +0800 Subject: [PATCH 021/108] fix: #2699 remove double quotes in readme --- README_CN.md | 10 +++++----- README_ES.md | 10 +++++----- README_JA.md | 10 +++++----- README_KO.md | 10 +++++----- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README_CN.md b/README_CN.md index 568cd229a..1111540e9 100644 --- a/README_CN.md +++ b/README_CN.md @@ -135,8 +135,8 @@ BASE_URL=https://chatgpt1.nextweb.fun/api/proxy docker pull yidadaa/chatgpt-next-web docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY="sk-xxxx" \ - -e CODE="页面访问密码" \ + -e OPENAI_API_KEY=sk-xxxx \ + -e CODE=页面访问密码 \ yidadaa/chatgpt-next-web ``` @@ -144,10 +144,10 @@ docker run -d -p 3000:3000 \ ```shell docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY="sk-xxxx" \ - -e CODE="页面访问密码" \ + -e OPENAI_API_KEY=sk-xxxx \ + -e CODE=页面访问密码 \ --net=host \ - -e PROXY_URL="http://127.0.0.1:7890" \ + -e PROXY_URL=http://127.0.0.1:7890 \ yidadaa/chatgpt-next-web ``` diff --git a/README_ES.md b/README_ES.md index 34e9678f9..a5787a996 100644 --- a/README_ES.md +++ b/README_ES.md @@ -130,8 +130,8 @@ Antes de empezar a escribir código, debe crear uno nuevo en la raíz del proyec docker pull yidadaa/chatgpt-next-web docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY="sk-xxxx" \ - -e CODE="页面访问密码" \ + -e OPENAI_API_KEY=sk-xxxx \ + -e CODE=your-password \ yidadaa/chatgpt-next-web ``` @@ -139,10 +139,10 @@ También puede especificar proxy: ```shell docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY="sk-xxxx" \ - -e CODE="页面访问密码" \ + -e OPENAI_API_KEY=sk-xxxx \ + -e CODE=your-password \ --net=host \ - -e PROXY_URL="http://127.0.0.1:7890" \ + -e PROXY_URL=http://127.0.0.1:7890 \ yidadaa/chatgpt-next-web ``` diff --git a/README_JA.md b/README_JA.md index 6018a1b01..72a0d5373 100644 --- a/README_JA.md +++ b/README_JA.md @@ -196,8 +196,8 @@ yarn dev docker pull yidadaa/chatgpt-next-web docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY="sk-xxxx" \ - -e CODE="your-password" \ + -e OPENAI_API_KEY=sk-xxxx \ + -e CODE=your-password \ yidadaa/chatgpt-next-web ``` @@ -205,9 +205,9 @@ docker run -d -p 3000:3000 \ ```shell docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY="sk-xxxx" \ - -e CODE="your-password" \ - -e PROXY_URL="http://localhost:7890" \ + -e OPENAI_API_KEY=sk-xxxx \ + -e CODE=your-password \ + -e PROXY_URL=http://localhost:7890 \ yidadaa/chatgpt-next-web ``` diff --git a/README_KO.md b/README_KO.md index 86fba5115..519dd9d9b 100644 --- a/README_KO.md +++ b/README_KO.md @@ -135,8 +135,8 @@ BASE_URL=https://chatgpt1.nextweb.fun/api/proxy docker pull yidadaa/chatgpt-next-web docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY="sk-xxxx" \ - -e CODE="페이지 접근 비밀번호" \ + -e OPENAI_API_KEY=sk-xxxx \ + -e CODE=페이지 접근 비밀번호 \ yidadaa/chatgpt-next-web ``` @@ -144,10 +144,10 @@ docker run -d -p 3000:3000 \ ```shell docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY="sk-xxxx" \ - -e CODE="페이지 접근 비밀번호" \ + -e OPENAI_API_KEY=sk-xxxx \ + -e CODE=페이지 접근 비밀번호 \ --net=host \ - -e PROXY_URL="http://127.0.0.1:7890" \ + -e PROXY_URL=http://127.0.0.1:7890 \ yidadaa/chatgpt-next-web ``` From 19dd71eb051a3c301c37ab21538147a88915ee8b Mon Sep 17 00:00:00 2001 From: huni Date: Thu, 24 Aug 2023 22:04:42 +0800 Subject: [PATCH 022/108] setup shell support for debian --- scripts/setup.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/setup.sh b/scripts/setup.sh index 751a9ac17..43af2b186 100644 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -10,7 +10,7 @@ case "$(uname -s)" in exit 1 fi else - if [[ ! "$(cat /etc/*-release | grep '^ID=')" =~ ^(ID=\"ubuntu\")|(ID=\"centos\")|(ID=\"arch\")$ ]]; then + if [[ ! "$(cat /etc/*-release | grep '^ID=')" =~ ^(ID=\"ubuntu\")|(ID=\"centos\")|(ID=\"arch\")|(ID=\"debian\")$ ]]; then echo "Unsupported Linux distribution." exit 1 fi @@ -32,6 +32,9 @@ if ! command -v node >/dev/null || ! command -v git >/dev/null || ! command -v y if [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=ubuntu" ]]; then sudo apt-get update sudo apt-get -y install nodejs git yarn + elif [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=debian" ]]; then + sudo apt-get update + sudo apt-get -y install nodejs git yarn elif [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=centos" ]]; then sudo yum -y install epel-release sudo yum -y install nodejs git yarn From 925d28495af7b4d9f722da59fe97115c6898acb7 Mon Sep 17 00:00:00 2001 From: huni Date: Thu, 24 Aug 2023 23:24:26 +0800 Subject: [PATCH 023/108] fix bug of ! near check of system. --- scripts/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/setup.sh b/scripts/setup.sh index 43af2b186..73ed61b13 100644 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -10,7 +10,7 @@ case "$(uname -s)" in exit 1 fi else - if [[ ! "$(cat /etc/*-release | grep '^ID=')" =~ ^(ID=\"ubuntu\")|(ID=\"centos\")|(ID=\"arch\")|(ID=\"debian\")$ ]]; then + if [[ !"$(cat /etc/*-release | grep '^ID=')" =~ ^(ID=\"ubuntu\")|(ID=\"centos\")|(ID=\"arch\")|(ID=\"debian\")$ ]]; then echo "Unsupported Linux distribution." exit 1 fi From 507b7fee56b8c63ffee6bbbe684f4acedaf27640 Mon Sep 17 00:00:00 2001 From: B0zal Date: Fri, 25 Aug 2023 06:31:33 +0700 Subject: [PATCH 024/108] [+] Language indonesia (my country) --- app/locales/id.ts | 349 +++++++++++++++++++++++++++++++++++++++++++ app/locales/index.ts | 3 + 2 files changed, 352 insertions(+) create mode 100644 app/locales/id.ts diff --git a/app/locales/id.ts b/app/locales/id.ts new file mode 100644 index 000000000..f73d94ef9 --- /dev/null +++ b/app/locales/id.ts @@ -0,0 +1,349 @@ +import { SubmitKey } from "../store/config"; +import { PartialLocaleType } from "./index"; + +const id: PartialLocaleType = { + WIP: "Coming Soon...", + Error: { + Unauthorized: + "Akses tidak diizinkan. Silakan [otorisasi](/#/auth) dengan memasukkan kode akses.", + }, + Auth: { + Title: "Diperlukan Kode Akses", + Tips: "Masukkan kode akses di bawah", + Input: "Kode Akses", + Confirm: "Konfirmasi", + Later: "Nanti", + }, + ChatItem: { + ChatItemCount: (count: number) => `${count} pesan`, + }, + Chat: { + SubTitle: (count: number) => `${count} pesan`, + Actions: { + ChatList: "Buka Daftar Chat", + CompressedHistory: "Ekspor Riwayat Terkompresi", + Export: "Ekspor Semua Pesan sebagai Markdown", + Copy: "Salin", + Stop: "Berhenti", + Retry: "Coba Lagi", + Pin: "Pin", + PinToastContent: "2 pesan telah ditandai", + PinToastAction: "Lihat", + Delete: "Hapus", + Edit: "Edit", + }, + Commands: { + new: "Mulai Chat Baru", + newm: "Mulai Chat Baru dengan Masks", + next: "Chat Selanjutnya", + prev: "Chat Sebelumnya", + clear: "Bersihkan Percakapan", + del: "Hapus Chat", + }, + InputActions: { + Stop: "Berhenti", + ToBottom: "Ke Bagian Bawah", + Theme: { + auto: "Otomatis", + light: "Tema Terang", + dark: "Tema Gelap", + }, + Prompt: "Prompts", + Masks: "Masks", + Clear: "Bersihkan Percakapan", + Settings: "Pengaturan", + }, + Rename: "Ubah Nama Chat", + Typing: "Mengetik...", + Input: (submitKey: string) => { + var inputHints = `${submitKey} untuk mengirim`; + if (submitKey === String(SubmitKey.Enter)) { + inputHints += ", Shift + Enter untuk membalut"; + } + return inputHints + ", / untuk mencari prompt, : untuk menggunakan perintah"; + }, + Send: "Kirim", + Config: { + Reset: "Reset ke Default", + SaveAs: "Simpan sebagai Masks", + }, + }, + Export: { + Title: "Ekspor Pesan", + Copy: "Salin Semua", + Download: "Unduh", + MessageFromYou: "Pesan dari Anda", + MessageFromChatGPT: "Pesan dari ChatGPT", + Share: "Bagikan ke ShareGPT", + Format: { + Title: "Format Ekspor", + SubTitle: "Markdown atau Gambar PNG", + }, + IncludeContext: { + Title: "Sertakan Konteks", + SubTitle: "Apakah akan menyertakan masks", + }, + Steps: { + Select: "Pilih", + Preview: "Pratinjau", + }, + }, + Select: { + Search: "Cari", + All: "Pilih Semua", + Latest: "Pilih Terbaru", + Clear: "Bersihkan", + }, + Memory: { + Title: "Prompt Memori", + EmptyContent: "Belum ada yang tersedia.", + Send: "Kirim Memori", + Copy: "Salin Memori", + Reset: "Reset", + ResetConfirm: + "Jika Anda mereset, riwayat obrolan saat ini dan memori historis akan dihapus. Apakah Anda yakin ingin melakukan reset?", + }, + Home: { + NewChat: "Obrolan Baru", + DeleteChat: "Anda yakin ingin menghapus percakapan yang dipilih?", + DeleteToast: "Percakapan telah dihapus", + Revert: "Kembali", + }, + Settings: { + Title: "Pengaturan", + SubTitle: "Semua Pengaturan", + Danger: { + Reset: { + Title: "Setel Ulang Semua Pengaturan", + SubTitle: "Mengembalikan semua pengaturan ke nilai default", + Action: "Setel Ulang", + Confirm: "Anda yakin ingin mengembalikan semua pengaturan ke nilai default?", + }, + Clear: { + Title: "Hapus Semua Data", + SubTitle: "Menghapus semua pesan dan pengaturan", + Action: "Hapus", + Confirm: "Anda yakin ingin menghapus semua pesan dan pengaturan?", + }, + }, + Lang: { + Name: "Bahasa", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language` + All: "Semua Bahasa", + }, + Avatar: "Avatar", + FontSize: { + Title: "Ukuran Font", + SubTitle: "Ubah ukuran font konten chat", + }, + InjectSystemPrompts: { + Title: "Suntikkan Petunjuk Sistem", + SubTitle: + "Tambahkan petunjuk simulasi sistem ChatGPT di awal daftar pesan yang diminta dalam setiap permintaan", + }, + InputTemplate: { + Title: "Template Input", + SubTitle: "Pesan baru akan diisi menggunakan template ini", + }, + + Update: { + Version: (x: string) => `Version: ${x}`, + IsLatest: "Versi terbaru", + CheckUpdate: "Periksa Pembaruan", + IsChecking: "Memeriksa pembaruan...", + FoundUpdate: (x: string) => `Versi terbaru ditemukan: ${x}`, + GoToUpdate: "Perbarui Sekarang", + }, + AutoGenerateTitle: { + Title: "Hasilkan Judul Otomatis", + SubTitle: "Hasilkan judul yang sesuai berdasarkan konten percakapan", + }, + SendKey: "Kirim", + Theme: "Tema", + TightBorder: "Batas Ketat", + SendPreviewBubble: { + Title: "Pratinjau Obrolan", + SubTitle: "Pratinjau Obrolan dengan markdown", + }, + Mask: { + Splash: { + Title: "Layar Pembuka Masks", + SubTitle: + "Tampilkan layar pembuka masks sebelum memulai percakapan baru", + }, + Builtin: { + Title: "Sembunyikan Masks Bawaan", + SubTitle: "Sembunyikan Masks bawaan dari daftar masks", + }, + }, + Prompt: { + Disable: { + Title: "Nonaktifkan Otomatisasi", + SubTitle: "Aktifkan/Matikan otomatisasi", + }, + List: "Daftar Prompt", + ListCount: (builtin: number, custom: number) => + `${builtin} bawaan, ${custom} penggunaan khusus`, + Edit: "Edit", + Modal: { + Title: "Daftar Prompt", + Add: "Tambahkan", + Search: "Cari Prompt", + }, + EditModal: { + Title: "Edit Prompt", + }, + }, + HistoryCount: { + Title: "Jumlah Pesan Riwayat", + SubTitle: "Jumlah pesan yang akan dikirim setiap permintaan", + }, + CompressThreshold: { + Title: "Batas Kompresi Riwayat", + SubTitle: + "Jika panjang pesan melebihi batas yang ditentukan, pesan tersebut akan dikompresi", + }, + Token: { + Title: "Kunci API", + SubTitle: "Gunakan kunci Anda untuk melewati batas kode akses", + Placeholder: "Kunci API OpenAI", + }, + Usage: { + Title: "Saldo Akun", + SubTitle(used: any, total: any) { + return `Digunakan bulan ini: ${used}, total langganan: ${total}`; + }, + IsChecking: "Memeriksa...", + Check: "Periksa", + NoAccess: "Masukkan kunci API untuk memeriksa saldo", + }, + AccessCode: { + Title: "Kode Akses", + SubTitle: "Kontrol akses diaktifkan", + Placeholder: "Diperlukan kode akses", + }, + Endpoint: { + Title: "Endpoint", + SubTitle: "Harus dimulai dengan http(s):// untuk endpoint kustom", + }, + Model: "Model", + Temperature: { + Title: "Suhu", + SubTitle: "Semakin tinggi nilainya, semakin acak keluarannya", + }, + TopP: { + Title: "Top P", + SubTitle: "Tidak mengubah nilai dengan suhu", + }, + MaxTokens: { + Title: "Token Maksimum", + SubTitle: "Panjang maksimum token input dan output", + }, + PresencePenalty: { + Title: "Penalti Kehadiran", + SubTitle: "Semakin tinggi nilai, semakin mungkin topik baru muncul", + }, + FrequencyPenalty: { + Title: "Penalti Frekuensi", + SubTitle: "Semakin tinggi nilai, semakin rendah kemungkinan penggunaan ulang baris yang sama", + }, + }, + Store: { + DefaultTopic: "Percakapan Baru", + BotHello: "Halo! Bagaimana saya bisa membantu Anda hari ini?", + Error: "Terjadi kesalahan, silakan coba lagi nanti.", + Prompt: { + History: (content: string) => + "Ini adalah ringkasan singkat dari riwayat percakapan: " + content, + Topic: + "Buat judul berisi empat hingga lima kata untuk percakapan kita yang tidak akan disertakan dalam ringkasan percakapan, seperti instruksi, format, kutipan, tanda baca awal, tanda kutip pendahuluan, atau karakter tambahan. Silakan coba dengan kutipan berakhir.", + Summarize: + "Buat ringkasan percakapan dalam 200 kata yang akan digunakan sebagai promp di masa depan.", + }, + }, + Copy: { + Success: "Berhasil disalin ke clipboard", + Failed: "Gagal menyalin, berikan izin untuk memberikan izin", + }, + Context: { + Toast: (x: any) => `Dengan ${x} promp kontekstual`, + Edit: "Pengaturan Obrolan Saat Ini", + Add: "Tambahkan Promp", + Clear: "Bersihkan Konteks", + Revert: "Kembali ke Posisi Sebelumnya", + }, + Plugin: { + Name: "Plugin", + }, + Mask: { + Name: "Masks", + Page: { + Title: "Template Promp", + SubTitle: (count: number) => `${count} template prompt`, + Search: "Cari template", + Create: "Buat", + }, + Item: { + Info: (count: number) => `${count} prompt`, + Chat: "Obrolan", + View: "Lihat", + Edit: "Edit", + Delete: "Hapus", + DeleteConfirm: "Anda yakin ingin menghapus?", + }, + EditModal: { + Title: (readonly: boolean) => + `Edit Template Prompt ${readonly ? "(hanya baca)" : ""}`, + Download: "Unduh", + Clone: "Duplikat", + }, + Config: { + Avatar: "Avatar Bot", + Name: "Nama Bot", + Sync: { + Title: "Gunakan Konfigurasi Global", + SubTitle: "Gunakan konfigurasi global dalam percakapan ini", + Confirm: + "Pastikan untuk mengganti konfigurasi kustom dengan konfigurasi global?", + }, + HideContext: { + Title: "Sembunyikan Prompt Konteks", + SubTitle: "Tidak menampilkan prompt konteks dalam obrolan", + }, + Share: { + Title: "Bagikan Masks Ini", + SubTitle: "Buat tautan untuk masks ini", + Action: "Salin Tautan", + }, + }, + }, + NewChat: { + Return: "Kembali", + Skip: "Lewati", + Title: "Pilih Masks", + SubTitle: "Berkonversasilah dengan diri Anda di balik masks", + More: "Lebih Lanjut", + NotShow: "Jangan Tampilkan Sekarang", + ConfirmNoShow: + "Pastikan untuk menonaktifkannya? Anda dapat mengaktifkannya nanti melalui pengaturan.", + }, + + UI: { + Confirm: "Konfirmasi", + Cancel: "Batal", + Close: "Tutup", + Create: "Buat", + Edit: "Edit", + }, + Exporter: { + Model: "Model", + Messages: "Pesan", + Topic: "Topik", + Time: "Waktu", + }, + URLCommand: { + Code: "Kode akses terdeteksi dari url, konfirmasi untuk mendaftar ? ", + Settings: "Pengaturan terdeteksi dari url, konfirmasi untuk diterapkan ?", + }, +}; + +export default id; diff --git a/app/locales/index.ts b/app/locales/index.ts index 528600bec..79e314fac 100644 --- a/app/locales/index.ts +++ b/app/locales/index.ts @@ -1,6 +1,7 @@ import cn from "./cn"; import en from "./en"; import tw from "./tw"; +import id from "./id"; import fr from "./fr"; import es from "./es"; import it from "./it"; @@ -25,6 +26,7 @@ const ALL_LANGS = { tw, jp, ko, + id, fr, es, it, @@ -48,6 +50,7 @@ export const ALL_LANG_OPTIONS: Record = { tw: "繁體中文", jp: "日本語", ko: "한국어", + id: "Indonesia", fr: "Français", es: "Español", it: "Italiano", From f65b0128e7cea3f4440f4f55ded8a3e2290bf7a6 Mon Sep 17 00:00:00 2001 From: B0zal Date: Fri, 25 Aug 2023 12:39:24 +0700 Subject: [PATCH 025/108] Issue #2702 should be fixed now kiw kiw where the ChatGPTicon.src glitches and breaks when exporting the image for the second time without refreshing the page. --- app/components/exporter.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/components/exporter.tsx b/app/components/exporter.tsx index 2e3cd84aa..ba82c2851 100644 --- a/app/components/exporter.tsx +++ b/app/components/exporter.tsx @@ -383,7 +383,7 @@ export function PreviewActions(props: { function ExportAvatar(props: { avatar: string }) { if (props.avatar === DEFAULT_MASK_AVATAR) { return ( - ; + return ; } export function ImagePreviewer(props: { @@ -422,6 +422,7 @@ export function ImagePreviewer(props: { ]) .then(() => { showToast(Locale.Copy.Success); + refreshPreview(); }); } catch (e) { console.error("[Copy Image] ", e); @@ -447,11 +448,19 @@ export function ImagePreviewer(props: { link.download = `${props.topic}.png`; link.href = blob; link.click(); + refreshPreview(); } }) .catch((e) => console.log("[Export Image] ", e)); }; + const refreshPreview = () => { + const dom = previewRef.current; + if (dom) { + dom.innerHTML = dom.innerHTML; // Refresh the content of the preview by resetting its HTML for fix a bug glitching + } + }; + return (
Date: Fri, 25 Aug 2023 17:09:39 +0700 Subject: [PATCH 026/108] [+] FineTuned Sysmessage Depends for local language this for JSON Exporter --- app/components/exporter.tsx | 6 +++--- app/locales/ar.ts | 3 +++ app/locales/bn.ts | 4 ++-- app/locales/cn.ts | 5 ++++- app/locales/cs.ts | 4 ++-- app/locales/de.ts | 3 +++ app/locales/en.ts | 3 +++ app/locales/es.ts | 3 +++ app/locales/fr.ts | 3 +++ app/locales/id.ts | 3 +++ app/locales/it.ts | 3 +++ app/locales/jp.ts | 1 + app/locales/ko.ts | 3 +++ app/locales/ru.ts | 3 +++ app/locales/tr.ts | 3 +++ app/locales/tw.ts | 1 + app/locales/vi.ts | 3 +++ 17 files changed, 46 insertions(+), 8 deletions(-) diff --git a/app/components/exporter.tsx b/app/components/exporter.tsx index 2e3cd84aa..df794adce 100644 --- a/app/components/exporter.tsx +++ b/app/components/exporter.tsx @@ -575,7 +575,7 @@ export function JsonPreviewer(props: { messages: [ { role: "system", - content: "You are an assistant that " + props.topic, + content: `${Locale.FineTuned.Sysmessage} ${props.topic}`, }, ...props.messages.map((m) => ({ role: m.role, @@ -602,8 +602,8 @@ export function JsonPreviewer(props: { messages={props.messages} />
- +
); -} \ No newline at end of file +} diff --git a/app/locales/ar.ts b/app/locales/ar.ts index a86b1648a..520cb2635 100644 --- a/app/locales/ar.ts +++ b/app/locales/ar.ts @@ -233,6 +233,9 @@ ${builtin} مدمجة، ${custom} تم تعريفها من قبل المستخد Plugin: { Name: "المكوّن الإضافي", }, + FineTuned: { + Sysmessage: "أنت مساعد ي", + }, Mask: { Name: "الأقنعة", Page: { diff --git a/app/locales/bn.ts b/app/locales/bn.ts index 9eda157f5..d9559d719 100644 --- a/app/locales/bn.ts +++ b/app/locales/bn.ts @@ -268,8 +268,8 @@ const bn: PartialLocaleType = { Clear: "সঙ্গতি পরিস্কার করুন", Revert: "পূর্ববর্তী অবস্থানে ফিরে যান", }, - Plugin: { - Name: "প্লাগইন", + FineTuned: { + Sysmessage: "আপনি একটি সহকারী যা", }, Mask: { Name: "মাস্ক", diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 19e804b3a..3f4324a6f 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -288,7 +288,10 @@ const cn = { Revert: "恢复上下文", }, Plugin: { - Name: "插件", + Name: "你是一个助手 ", + }, + FineTuned: { + Sysmessage: "你是一个助手", }, Mask: { Name: "面具", diff --git a/app/locales/cs.ts b/app/locales/cs.ts index b8a3b974c..d1d5d6357 100644 --- a/app/locales/cs.ts +++ b/app/locales/cs.ts @@ -185,8 +185,8 @@ const cs: PartialLocaleType = { Edit: "Kontextové a paměťové pokyny", Add: "Přidat pokyn", }, - Plugin: { - Name: "Plugin", + FineTuned: { + Sysmessage: "Jste asistent, který", }, Mask: { Name: "Maska", diff --git a/app/locales/de.ts b/app/locales/de.ts index 59b1fc927..e0bdc52b7 100644 --- a/app/locales/de.ts +++ b/app/locales/de.ts @@ -192,6 +192,9 @@ const de: PartialLocaleType = { Plugin: { Name: "Plugin", }, + FineTuned: { + Sysmessage: "Du bist ein Assistent, der", + }, Mask: { Name: "Mask", Page: { diff --git a/app/locales/en.ts b/app/locales/en.ts index 64cdc38bb..981357274 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -295,6 +295,9 @@ const en: LocaleType = { Plugin: { Name: "Plugin", }, + FineTuned: { + Sysmessage: "You are an assistant that", + }, Mask: { Name: "Mask", Page: { diff --git a/app/locales/es.ts b/app/locales/es.ts index 6145eccc8..a6ae154f4 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -190,6 +190,9 @@ const es: PartialLocaleType = { Plugin: { Name: "Plugin", }, + FineTuned: { + Sysmessage: "Eres un asistente que", + }, Mask: { Name: "Mask", Page: { diff --git a/app/locales/fr.ts b/app/locales/fr.ts index a98d4a432..f5200f271 100644 --- a/app/locales/fr.ts +++ b/app/locales/fr.ts @@ -244,6 +244,9 @@ const fr: PartialLocaleType = { Plugin: { Name: "Extension", }, + FineTuned: { + Sysmessage: "Eres un asistente que", + }, Mask: { Name: "Masque", Page: { diff --git a/app/locales/id.ts b/app/locales/id.ts index f73d94ef9..c3a2a5f88 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -274,6 +274,9 @@ const id: PartialLocaleType = { Plugin: { Name: "Plugin", }, + FineTuned: { + Sysmessage: "Anda adalah asisten yang", + }, Mask: { Name: "Masks", Page: { diff --git a/app/locales/it.ts b/app/locales/it.ts index 6a2eabf40..bf20747b1 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -191,6 +191,9 @@ const it: PartialLocaleType = { Plugin: { Name: "Plugin", }, + FineTuned: { + Sysmessage: "Sei un assistente che", + }, Mask: { Name: "Mask", Page: { diff --git a/app/locales/jp.ts b/app/locales/jp.ts index e27a4c6d9..c3e00fa09 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -204,6 +204,7 @@ const jp: PartialLocaleType = { Add: "追加", }, Plugin: { Name: "プラグイン" }, + FineTuned: { Sysmessage: "あなたはアシスタントです" }, Mask: { Name: "キャラクタープリセット", Page: { diff --git a/app/locales/ko.ts b/app/locales/ko.ts index 194e44769..717ce30b2 100644 --- a/app/locales/ko.ts +++ b/app/locales/ko.ts @@ -186,6 +186,9 @@ const ko: PartialLocaleType = { Plugin: { Name: "플러그인", }, + FineTuned: { + Sysmessage: "당신은 어시스턴트입니다", + }, Mask: { Name: "마스크", Page: { diff --git a/app/locales/ru.ts b/app/locales/ru.ts index 313acf544..bf98b4eb8 100644 --- a/app/locales/ru.ts +++ b/app/locales/ru.ts @@ -191,6 +191,9 @@ const ru: PartialLocaleType = { Plugin: { Name: "Плагин", }, + FineTuned: { + Sysmessage: "Вы - ассистент, который", + }, Mask: { Name: "Маска", Page: { diff --git a/app/locales/tr.ts b/app/locales/tr.ts index 46fdd6285..06996d83d 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -191,6 +191,9 @@ const tr: PartialLocaleType = { Plugin: { Name: "Plugin", }, + FineTuned: { + Sysmessage: "Sen bir asistansın", + }, Mask: { Name: "Mask", Page: { diff --git a/app/locales/tw.ts b/app/locales/tw.ts index ad1ee0bb6..15f6648e6 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -180,6 +180,7 @@ const tw: PartialLocaleType = { Add: "新增一條", }, Plugin: { Name: "外掛" }, + FineTuned: { Sysmessage: "你是一個助手" }, Mask: { Name: "面具", Page: { diff --git a/app/locales/vi.ts b/app/locales/vi.ts index 2117734b0..8f53a3dc1 100644 --- a/app/locales/vi.ts +++ b/app/locales/vi.ts @@ -186,6 +186,9 @@ const vi: PartialLocaleType = { Plugin: { Name: "Plugin", }, + FineTuned: { + Sysmessage: "Bạn là một trợ lý", + }, Mask: { Name: "Mẫu", Page: { From 63c93a42b5fa5973edf102a6ece29aa294538375 Mon Sep 17 00:00:00 2001 From: B0zal Date: Fri, 25 Aug 2023 18:15:50 +0700 Subject: [PATCH 027/108] [+] Fixed language missing for finetuned --- app/locales/bn.ts | 3 +++ app/locales/cn.ts | 2 +- app/locales/cs.ts | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/locales/bn.ts b/app/locales/bn.ts index d9559d719..2d2266b3f 100644 --- a/app/locales/bn.ts +++ b/app/locales/bn.ts @@ -268,6 +268,9 @@ const bn: PartialLocaleType = { Clear: "সঙ্গতি পরিস্কার করুন", Revert: "পূর্ববর্তী অবস্থানে ফিরে যান", }, + Plugin: { + Name: "প্লাগইন", + }, FineTuned: { Sysmessage: "আপনি একটি সহকারী যা", }, diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 3f4324a6f..7c0473bb3 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -288,7 +288,7 @@ const cn = { Revert: "恢复上下文", }, Plugin: { - Name: "你是一个助手 ", + Name: "插件", }, FineTuned: { Sysmessage: "你是一个助手", diff --git a/app/locales/cs.ts b/app/locales/cs.ts index d1d5d6357..57aa803e4 100644 --- a/app/locales/cs.ts +++ b/app/locales/cs.ts @@ -185,6 +185,9 @@ const cs: PartialLocaleType = { Edit: "Kontextové a paměťové pokyny", Add: "Přidat pokyn", }, + Plugin: { + Name: "Plugin", + }, FineTuned: { Sysmessage: "Jste asistent, který", }, From 5e23ad2db1628d096eaf0f38b610011ed5c8fd65 Mon Sep 17 00:00:00 2001 From: B0zal Date: Fri, 25 Aug 2023 18:43:37 +0700 Subject: [PATCH 028/108] Security Update Potentially unsafe external link --- app/components/sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index b0b778fc5..634639f1d 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -174,7 +174,7 @@ export function SideBar(props: { className?: string }) {
From 22a6819f7b81b654fbb1632f425b42411f94cc98 Mon Sep 17 00:00:00 2001 From: B0zal Date: Fri, 25 Aug 2023 18:48:38 +0700 Subject: [PATCH 029/108] Security Update [+] Protect Prototype --- app/utils/merge.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/utils/merge.ts b/app/utils/merge.ts index 758d6df84..fd7a4da98 100644 --- a/app/utils/merge.ts +++ b/app/utils/merge.ts @@ -1,9 +1,13 @@ export function merge(target: any, source: any) { Object.keys(source).forEach(function (key) { - if (source[key] && typeof source[key] === "object") { + if ( + source.hasOwnProperty(key) && // Check if the property is not inherited + source[key] && + typeof source[key] === "object" || key === "__proto__" || key === "constructor" + ) { merge((target[key] = target[key] || {}), source[key]); return; } target[key] = source[key]; }); -} +} \ No newline at end of file From 0ae57589a9cabc1a87dfb5fd056057133aae9b7d Mon Sep 17 00:00:00 2001 From: B0zal Date: Sun, 27 Aug 2023 06:09:55 +0700 Subject: [PATCH 030/108] This branch only for scanner --- .github/workflows/codeql.yml | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..b75ec37b6 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,55 @@ +# Modified by backtrackz +# Mark TRAP cache skipped because it only a few lines +name: "CodeQL" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '18 8 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + id: codeql + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" + codeql-path: /opt/hostedtoolcache/CodeQL/2.14.1/x64/codeql/codeql + upload-trap-cache: true + upload-trap-cache-exclude: '.*' + + - name: Mark TRAP cache skipped + if: steps.codeql.outputs.upload_trap_cache_skipped == 'true' + run: echo "::set-output name=trap_cache_skipped::true" + + - name: Start Analysis + if: steps.codeql.outputs.upload_trap_cache_skipped == 'false' + run: echo "Starting analysis..." \ No newline at end of file From 71d06647d07e22b3a1d114ef07bc4fe5373fc19d Mon Sep 17 00:00:00 2001 From: B0zal Date: Sun, 27 Aug 2023 09:15:05 +0700 Subject: [PATCH 031/108] Delete codeql.yml it won't work if someone try, must have licence advance security --- .github/workflows/codeql.yml | 55 ------------------------------------ 1 file changed, 55 deletions(-) delete mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index b75ec37b6..000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,55 +0,0 @@ -# Modified by backtrackz -# Mark TRAP cache skipped because it only a few lines -name: "CodeQL" - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - schedule: - - cron: '18 8 * * 2' - -jobs: - analyze: - name: Analyze - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} - timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'javascript' ] - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - - name: Perform CodeQL Analysis - id: codeql - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" - codeql-path: /opt/hostedtoolcache/CodeQL/2.14.1/x64/codeql/codeql - upload-trap-cache: true - upload-trap-cache-exclude: '.*' - - - name: Mark TRAP cache skipped - if: steps.codeql.outputs.upload_trap_cache_skipped == 'true' - run: echo "::set-output name=trap_cache_skipped::true" - - - name: Start Analysis - if: steps.codeql.outputs.upload_trap_cache_skipped == 'false' - run: echo "Starting analysis..." \ No newline at end of file From 3bd76b9156627116b8bbcf038e08e35d84438447 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 28 Aug 2023 00:02:52 +0800 Subject: [PATCH 032/108] feat: close #2580 only use 3.5 to summarize when not using custom models --- app/constant.ts | 2 ++ app/store/chat.ts | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/constant.ts b/app/constant.ts index 8b28af323..ba0b22c7f 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -63,6 +63,8 @@ Knowledge cutoff: 2021-09 Current model: {{model}} Current time: {{time}}`; +export const SUMMARIZE_MODEL = "gpt-3.5-turbo"; + export const DEFAULT_MODELS = [ { name: "gpt-4", diff --git a/app/store/chat.ts b/app/store/chat.ts index bcdd99569..20603fe48 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -11,6 +11,7 @@ import { DEFAULT_INPUT_TEMPLATE, DEFAULT_SYSTEM_TEMPLATE, StoreKey, + SUMMARIZE_MODEL, } from "../constant"; import { api, RequestMessage } from "../client/api"; import { ChatControllerPool } from "../client/controller"; @@ -80,6 +81,11 @@ function createEmptySession(): ChatSession { }; } +function getSummarizeModel(currentModel: string) { + // if it is using gpt-* models, force to use 3.5 to summarize + return currentModel.startsWith("gpt") ? SUMMARIZE_MODEL : currentModel; +} + interface ChatStore { sessions: ChatSession[]; currentSessionIndex: number; @@ -501,7 +507,7 @@ export const useChatStore = create()( api.llm.chat({ messages: topicMessages, config: { - model: "gpt-3.5-turbo", + model: getSummarizeModel(session.mask.modelConfig.model), }, onFinish(message) { get().updateCurrentSession( @@ -555,7 +561,11 @@ export const useChatStore = create()( date: "", }), ), - config: { ...modelConfig, stream: true, model: "gpt-3.5-turbo" }, + config: { + ...modelConfig, + stream: true, + model: getSummarizeModel(session.mask.modelConfig.model), + }, onUpdate(message) { session.memoryPrompt = message; }, From 49046125235d11f85ee0dc81f2424f2cde91f1eb Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 28 Aug 2023 00:50:53 +0800 Subject: [PATCH 033/108] feat: close #2430 add a simple user maual --- README.md | 10 ++++ docs/user-manual-cn.md | 101 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 docs/user-manual-cn.md diff --git a/README.md b/README.md index 1662e8c7e..5e76e9a7a 100644 --- a/README.md +++ b/README.md @@ -257,6 +257,16 @@ If your proxy needs password, use: bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh) ``` +## Documentation + +> Please go to the [docs][./docs] directory for more documentation instructions. + +- [Deploy with cloudflare (Deprecated)](./docs/cloudflare-pages-en.md) +- [Frequent Ask Questions](./docs/faq-en.md) +- [How to add a new translation](./docs/translation.md) +- [How to use Vercel (No English)](./docs/vercel-cn.md) +- [User Manual (Only Chinese, WIP)](./docs/user-manual-cn.md) + ## Screenshots ![Settings](./docs/images/settings.png) diff --git a/docs/user-manual-cn.md b/docs/user-manual-cn.md new file mode 100644 index 000000000..883bbc23e --- /dev/null +++ b/docs/user-manual-cn.md @@ -0,0 +1,101 @@ +# 用户手册 User Manual + +> No english version yet, please read this doc with ChatGPT or other translation tools. + +本文档用于解释 ChatGPT Next Web 的部分功能介绍和设计原则。 + +## 面具 (Mask) + +### 什么是面具?它和提示词的区别是什么? + +面具 = 多个预设提示词 + 模型设置 + 对话设置。 + +其中预设提示词(Contextual Prompts)一般用于 In-Context Learning,用于让 ChatGPT 生成更加符合要求的输出,也可以增加系统约束或者输入有限的额外知识。 + +模型设置则顾名思义,使用此面具创建的对话都会默认使用对应的模型参数。 + +对话设置是与对话体验相关的一系列设置,我们会在下方的章节中依次介绍。 + +### 如何添加一个预设面具? + +目前仅能够通过编辑源代码的方式添加预设面具,请根据需要编辑 [mask](../app/masks/) 目录下对应语言的文件即可。 + +编辑步骤如下: + +1. 在 ChatGPT Next Web 中配置好一个面具; +2. 使用面具编辑页面的下载按钮,将面具保存为 JSON 格式; +3. 让 ChatGPT 帮你将 json 文件格式化为对应的 ts 代码; +4. 放入对应的 .ts 文件。 + +后续会增加使用旁加载的方式加载面具。 + +## 对话 (Chat) + +### 对话框上方的按钮的作用 + +在默认状态下,将鼠标移动到按钮上,即可查看按钮的文字说明,我们依次介绍: + +- 对话设置:当前对话的设置,它与全局设置的关系,请查看下一小节的说明; +- 颜色主题:点击即可在自动、暗黑、浅色之间轮换; +- 快捷指令:项目内置的快捷填充预设提示词,也可以在对话框中输入 / 进行搜索; +- 所有面具:进入面具页面; +- 清除聊天:插入一个清除标记,标记上方的聊天将不会发给 GPT,效果相当于清除了当前对话,当然,你也可以再次点击该按钮,可取消清除; +- 模型设置:更改当前对话的模型,注意,此按钮只会修改当前对话的模型,并不会修改全局默认模型。 + +### 对话内设置与全局设置的关系 + +目前有两处设置入口: + +1. 页面左下角的设置按钮,进入后是全局设置页; +2. 对话框上方的设置按钮,进入后是对话设置页。 + +在新建对话后,该对话的设置默认与全局设置保持同步,修改全局设置,则新建对话的对话内设置也会被同步修改。 + +一旦用户手动更改过对话内设置,则对话内设置将与全局设置断开同步,此时更改全局设置,将不会对该对话生效。 + +如果想恢复两者的同步关系,可以将“对话内设置 -> 使用全局设置”选项勾选。 + +### 对话内设置项的含义 + +点开对话框上方的按钮,进入对话内设置,内容从上到下依次为: + +- 预设提示词列表:可以增加、删除、排序预设提示词 +- 角色头像:顾名思义 +- 角色名称:顾名思义 +- 隐藏预设对话:隐藏后,预设提示词不会出现在聊天界面 +- 使用全局设置:用于表示当前对话是否使用全局对话设置 +- 模型设置选项:剩余的选项与全局设置选项含义一致,见下一小节 + +### 全局设置项的含义 + +- model / temperature / top_p / max_tokens / presence_penalty / frequency_penalty 均为 ChatGPT 的设置参数,详情请查阅 OpenAI 官方文档,再次不再赘述; +- 注入系统级提示信息、用户输入预处理:详情请看 [https://github.com/Yidadaa/ChatGPT-Next-Web/issues/2144](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/2144) +- 附带历史消息数:用户每次输入消息并发送时,所携带的最近 n 条消息数量; +- 历史消息长度压缩阈值:当已经产生的聊天字数达到该数值以后,则自动触发历史摘要功能; +- 历史摘要:是否启用历史摘要功能。 + +### 什么是历史摘要? + +历史摘要功能,也是历史消息压缩功能,是保证长对话场景下保持历史记忆的关键,合理使用该功能可以在不丢失历史话题信息的情况下,节省所使用的 token。 + +由于 ChatGPT API 的长度限制,我们以 3.5 模型为例,它只能接受小于 4096 tokens 的对话消息,一旦超出这个数值,就会报错。 + +同时为了让 ChatGPT 理解我们对话的上下文,往往会携带多条历史消息来提供上下文信息,而当对话进行一段时间之后,很容易就会触发长度限制。 + +为了解决此问题,我们增加了历史记录压缩功能,假设阈值为 1000 字符,那么每次用户产生的聊天记录超过 1000 字符时,都会将没有被总结过的消息,发送给 ChatGPT,让其产生一个 100 字所有的摘要。 + +这样,历史信息就从 1000 字压缩到了 100 字,这是一种有损压缩,但已能满足大多数使用场景。 + +### 什么时候应该关闭历史摘要? + +历史摘要可能会影响 ChatGPT 的对话质量,所以如果对话场景是翻译、信息提取等一次性对话场景,请直接关闭历史摘要功能,并将历史消息数设置为 0。 + +### 当用户发送一条消息时,有哪些信息被发送出去了? + +当用户在对话框输入了一条消息后,发送给 ChatGPT 的消息,包含以下几个部分: + +1. 系统级提示词:用于尽可能贴近 ChatGPT 官方 WebUI 的使用体验,可在设置中关闭此信息; +2. 历史摘要:作为长期记忆,提供长久但模糊的上下文信息; +3. 预设提示词:当前对话内设置的预设提示词,用于 In-Context Learning 或者注入系统级限制; +4. 最近 n 条对话记录:作为短期记忆,提供短暂但精确的上下文信息; +5. 用户当前输入的消息。 From 4a182517daeda046604fafc98c14df26d6eafd97 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 28 Aug 2023 01:04:24 +0800 Subject: [PATCH 034/108] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d54e47cd..07455d00d 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ One-Click to get well-designed cross-platform ChatGPT web UI. - New in v2: create, share and debug your chat tools with prompt templates (mask) - Awesome prompts powered by [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) and [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts) - Automatically compresses chat history to support long conversations while also saving your tokens -- I18n: English, 简体中文, 繁体中文, 日本語, Français, Español, Italiano, Türkçe, Deutsch, Tiếng Việt, Русский, Čeština, 한국어 +- I18n: English, 简体中文, 繁体中文, 日本語, Français, Español, Italiano, Türkçe, Deutsch, Tiếng Việt, Русский, Čeština, 한국어, Indonesia ## Roadmap From b0c32159e744448b9fbdfa63b1f46bed5050f9a2 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 28 Aug 2023 01:15:46 +0800 Subject: [PATCH 035/108] Update tauri.conf.json --- 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 b09715cb3..2256d5b34 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "ChatGPT Next Web", - "version": "2.9.4" + "version": "2.9.5" }, "tauri": { "allowlist": { From 05aa5206693377ab7bbea70f85a0f12a542145cf Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 28 Aug 2023 11:06:12 +0800 Subject: [PATCH 036/108] Revert "chore(deps): bump next from 13.4.9 to 13.4.19" --- package.json | 2 +- yarn.lock | 108 +++++++++++++++++++++++++-------------------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index 26a77c801..6610083bd 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "html-to-image": "^1.11.11", "mermaid": "^10.3.1", "nanoid": "^4.0.2", - "next": "^13.4.19", + "next": "^13.4.9", "node-fetch": "^3.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/yarn.lock b/yarn.lock index a0e4c9654..cbce2ef17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1131,10 +1131,10 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@next/env@13.4.19": - version "13.4.19" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.19.tgz#46905b4e6f62da825b040343cbc233144e9578d3" - integrity sha512-FsAT5x0jF2kkhNkKkukhsyYOrRqtSxrEhfliniIq0bwWbuXLgyt3Gv0Ml+b91XwjwArmuP7NxCiGd++GGKdNMQ== +"@next/env@13.4.9": + version "13.4.9" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.9.tgz#b77759514dd56bfa9791770755a2482f4d6ca93e" + integrity sha512-vuDRK05BOKfmoBYLNi2cujG2jrYbEod/ubSSyqgmEx9n/W3eZaJQdRNhTfumO+qmq/QTzLurW487n/PM/fHOkw== "@next/eslint-plugin-next@13.4.19": version "13.4.19" @@ -1143,50 +1143,50 @@ dependencies: glob "7.1.7" -"@next/swc-darwin-arm64@13.4.19": - version "13.4.19" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.19.tgz#77ad462b5ced4efdc26cb5a0053968d2c7dac1b6" - integrity sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ== +"@next/swc-darwin-arm64@13.4.9": + version "13.4.9" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.9.tgz#0ed408d444bbc6b0a20f3506a9b4222684585677" + integrity sha512-TVzGHpZoVBk3iDsTOQA/R6MGmFp0+17SWXMEWd6zG30AfuELmSSMe2SdPqxwXU0gbpWkJL1KgfLzy5ReN0crqQ== -"@next/swc-darwin-x64@13.4.19": - version "13.4.19" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.19.tgz#aebe38713a4ce536ee5f2a291673e14b715e633a" - integrity sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw== +"@next/swc-darwin-x64@13.4.9": + version "13.4.9" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.9.tgz#a08fccdee68201522fe6618ec81f832084b222f8" + integrity sha512-aSfF1fhv28N2e7vrDZ6zOQ+IIthocfaxuMWGReB5GDriF0caTqtHttAvzOMgJgXQtQx6XhyaJMozLTSEXeNN+A== -"@next/swc-linux-arm64-gnu@13.4.19": - version "13.4.19" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.19.tgz#ec54db65b587939c7b94f9a84800f003a380f5a6" - integrity sha512-vdlnIlaAEh6H+G6HrKZB9c2zJKnpPVKnA6LBwjwT2BTjxI7e0Hx30+FoWCgi50e+YO49p6oPOtesP9mXDRiiUg== +"@next/swc-linux-arm64-gnu@13.4.9": + version "13.4.9" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.9.tgz#1798c2341bb841e96521433eed00892fb24abbd1" + integrity sha512-JhKoX5ECzYoTVyIy/7KykeO4Z2lVKq7HGQqvAH+Ip9UFn1MOJkOnkPRB7v4nmzqAoY+Je05Aj5wNABR1N18DMg== -"@next/swc-linux-arm64-musl@13.4.19": - version "13.4.19" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.19.tgz#1f5e2c1ea6941e7d530d9f185d5d64be04279d86" - integrity sha512-aU0HkH2XPgxqrbNRBFb3si9Ahu/CpaR5RPmN2s9GiM9qJCiBBlZtRTiEca+DC+xRPyCThTtWYgxjWHgU7ZkyvA== +"@next/swc-linux-arm64-musl@13.4.9": + version "13.4.9" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.9.tgz#cee04c51610eddd3638ce2499205083656531ea0" + integrity sha512-OOn6zZBIVkm/4j5gkPdGn4yqQt+gmXaLaSjRSO434WplV8vo2YaBNbSHaTM9wJpZTHVDYyjzuIYVEzy9/5RVZw== -"@next/swc-linux-x64-gnu@13.4.19": - version "13.4.19" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.19.tgz#96b0882492a2f7ffcce747846d3680730f69f4d1" - integrity sha512-htwOEagMa/CXNykFFeAHHvMJeqZfNQEoQvHfsA4wgg5QqGNqD5soeCer4oGlCol6NGUxknrQO6VEustcv+Md+g== +"@next/swc-linux-x64-gnu@13.4.9": + version "13.4.9" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.9.tgz#1932d0367916adbc6844b244cda1d4182bd11f7a" + integrity sha512-iA+fJXFPpW0SwGmx/pivVU+2t4zQHNOOAr5T378PfxPHY6JtjV6/0s1vlAJUdIHeVpX98CLp9k5VuKgxiRHUpg== -"@next/swc-linux-x64-musl@13.4.19": - version "13.4.19" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.19.tgz#f276b618afa321d2f7b17c81fc83f429fb0fd9d8" - integrity sha512-4Gj4vvtbK1JH8ApWTT214b3GwUh9EKKQjY41hH/t+u55Knxi/0wesMzwQRhppK6Ddalhu0TEttbiJ+wRcoEj5Q== +"@next/swc-linux-x64-musl@13.4.9": + version "13.4.9" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.9.tgz#a66aa8c1383b16299b72482f6360facd5cde3c7a" + integrity sha512-rlNf2WUtMM+GAQrZ9gMNdSapkVi3koSW3a+dmBVp42lfugWVvnyzca/xJlN48/7AGx8qu62WyO0ya1ikgOxh6A== -"@next/swc-win32-arm64-msvc@13.4.19": - version "13.4.19" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.19.tgz#1599ae0d401da5ffca0947823dac577697cce577" - integrity sha512-bUfDevQK4NsIAHXs3/JNgnvEY+LRyneDN788W2NYiRIIzmILjba7LaQTfihuFawZDhRtkYCv3JDC3B4TwnmRJw== +"@next/swc-win32-arm64-msvc@13.4.9": + version "13.4.9" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.9.tgz#39482ee856c867177a612a30b6861c75e0736a4a" + integrity sha512-5T9ybSugXP77nw03vlgKZxD99AFTHaX8eT1ayKYYnGO9nmYhJjRPxcjU5FyYI+TdkQgEpIcH7p/guPLPR0EbKA== -"@next/swc-win32-ia32-msvc@13.4.19": - version "13.4.19" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.19.tgz#55cdd7da90818f03e4da16d976f0cb22045d16fd" - integrity sha512-Y5kikILFAr81LYIFaw6j/NrOtmiM4Sf3GtOc0pn50ez2GCkr+oejYuKGcwAwq3jiTKuzF6OF4iT2INPoxRycEA== +"@next/swc-win32-ia32-msvc@13.4.9": + version "13.4.9" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.9.tgz#29db85e34b597ade1a918235d16a760a9213c190" + integrity sha512-ojZTCt1lP2ucgpoiFgrFj07uq4CZsq4crVXpLGgQfoFq00jPKRPgesuGPaz8lg1yLfvafkU3Jd1i8snKwYR3LA== -"@next/swc-win32-x64-msvc@13.4.19": - version "13.4.19" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.19.tgz#648f79c4e09279212ac90d871646ae12d80cdfce" - integrity sha512-YzA78jBDXMYiINdPdJJwGgPNT3YqBNNGhsthsDoWHL9p24tEJn9ViQf/ZqTbwSpX/RrkPupLfuuTH2sf73JBAw== +"@next/swc-win32-x64-msvc@13.4.9": + version "13.4.9" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.9.tgz#0c2758164cccd61bc5a1c6cd8284fe66173e4a2b" + integrity sha512-QbT03FXRNdpuL+e9pLnu+XajZdm/TtIXVYY4lA9t+9l0fLZbHXDYEKitAqxrOj37o3Vx5ufxiRAniaIebYDCgw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -4707,12 +4707,12 @@ neo-async@^2.6.2: resolved "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -next@^13.4.19: - version "13.4.19" - resolved "https://registry.yarnpkg.com/next/-/next-13.4.19.tgz#2326e02aeedee2c693d4f37b90e4f0ed6882b35f" - integrity sha512-HuPSzzAbJ1T4BD8e0bs6B9C1kWQ6gv8ykZoRWs5AQoiIuqbGHHdQO7Ljuvg05Q0Z24E2ABozHe6FxDvI6HfyAw== +next@^13.4.9: + version "13.4.9" + resolved "https://registry.yarnpkg.com/next/-/next-13.4.9.tgz#473de5997cb4c5d7a4fb195f566952a1cbffbeba" + integrity sha512-vtefFm/BWIi/eWOqf1GsmKG3cjKw1k3LjuefKRcL3iiLl3zWzFdPG3as6xtxrGO6gwTzzaO1ktL4oiHt/uvTjA== dependencies: - "@next/env" "13.4.19" + "@next/env" "13.4.9" "@swc/helpers" "0.5.1" busboy "1.6.0" caniuse-lite "^1.0.30001406" @@ -4721,15 +4721,15 @@ next@^13.4.19: watchpack "2.4.0" zod "3.21.4" optionalDependencies: - "@next/swc-darwin-arm64" "13.4.19" - "@next/swc-darwin-x64" "13.4.19" - "@next/swc-linux-arm64-gnu" "13.4.19" - "@next/swc-linux-arm64-musl" "13.4.19" - "@next/swc-linux-x64-gnu" "13.4.19" - "@next/swc-linux-x64-musl" "13.4.19" - "@next/swc-win32-arm64-msvc" "13.4.19" - "@next/swc-win32-ia32-msvc" "13.4.19" - "@next/swc-win32-x64-msvc" "13.4.19" + "@next/swc-darwin-arm64" "13.4.9" + "@next/swc-darwin-x64" "13.4.9" + "@next/swc-linux-arm64-gnu" "13.4.9" + "@next/swc-linux-arm64-musl" "13.4.9" + "@next/swc-linux-x64-gnu" "13.4.9" + "@next/swc-linux-x64-musl" "13.4.9" + "@next/swc-win32-arm64-msvc" "13.4.9" + "@next/swc-win32-ia32-msvc" "13.4.9" + "@next/swc-win32-x64-msvc" "13.4.9" node-domexception@^1.0.0: version "1.0.0" From 885f2a32260b93adfbf58818913ba25ddac28d94 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 5 Sep 2023 01:54:28 +0800 Subject: [PATCH 037/108] feat: close #2752 auto re-fill unfinished input --- app/components/chat.tsx | 21 ++++++++++++++++++++- app/constant.ts | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index dfda4055b..6fb497303 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -80,6 +80,7 @@ import { MAX_RENDER_MSG_COUNT, Path, REQUEST_TIMEOUT_MS, + UNFINISHED_INPUT, } from "../constant"; import { Avatar } from "./emoji"; import { ContextPrompts, MaskAvatar, MaskConfig } from "./mask"; @@ -935,7 +936,8 @@ function _Chat() { const isTouchTopEdge = e.scrollTop <= edgeThreshold; const isTouchBottomEdge = bottomHeight >= e.scrollHeight - edgeThreshold; - const isHitBottom = bottomHeight >= e.scrollHeight - (isMobileScreen ? 0 : 10); + const isHitBottom = + bottomHeight >= e.scrollHeight - (isMobileScreen ? 0 : 10); const prevPageMsgIndex = msgRenderIndex - CHAT_PAGE_SIZE; const nextPageMsgIndex = msgRenderIndex + CHAT_PAGE_SIZE; @@ -1013,6 +1015,23 @@ function _Chat() { // edit / insert message modal const [isEditingMessage, setIsEditingMessage] = useState(false); + // remember unfinished input + useEffect(() => { + // try to load from local storage + const key = UNFINISHED_INPUT(session.id); + const mayBeUnfinishedInput = localStorage.getItem(key); + if (mayBeUnfinishedInput && userInput.length === 0) { + setUserInput(mayBeUnfinishedInput); + localStorage.removeItem(key); + } + + const dom = inputRef.current; + return () => { + localStorage.setItem(key, dom?.value ?? ""); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return (
diff --git a/app/constant.ts b/app/constant.ts index ba0b22c7f..2141820ce 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -44,6 +44,7 @@ export const NARROW_SIDEBAR_WIDTH = 100; export const ACCESS_CODE_PREFIX = "nk-"; export const LAST_INPUT_KEY = "last-input"; +export const UNFINISHED_INPUT = (id: string) => "unfinished-input-" + id; export const REQUEST_TIMEOUT_MS = 60000; From 2c077aca5a4a345a2544fcab36160047aa51eac2 Mon Sep 17 00:00:00 2001 From: Ricky Robinett Date: Wed, 6 Sep 2023 15:36:12 -0400 Subject: [PATCH 038/108] fix cloudflare deployment instructions --- docs/cloudflare-pages-en.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cloudflare-pages-en.md b/docs/cloudflare-pages-en.md index ee8ff6a6b..2279ff232 100644 --- a/docs/cloudflare-pages-en.md +++ b/docs/cloudflare-pages-en.md @@ -12,7 +12,7 @@ Fork this project on GitHub, then log in to dash.cloudflare.com and go to Pages. 7. In "Build Settings", choose the "Framework presets" option and select "Next.js". 8. Do not use the default "Build command" due to a node:buffer bug. Instead, use the following command: ``` - npx https://prerelease-registry.devprod.cloudflare.dev/next-on-pages/runs/4930842298/npm-package-next-on-pages-230 --experimental-minify + npx @cloudflare/next-on-pages --experimental-minify ``` 9. For "Build output directory", use the default value and do not modify it. 10. Do not modify "Root Directory". @@ -35,4 +35,4 @@ Fork this project on GitHub, then log in to dash.cloudflare.com and go to Pages. 14. Go to "Build settings", "Functions", and find "Compatibility flags". 15. Fill in "nodejs_compat" for both "Configure Production compatibility flag" and "Configure Preview compatibility flag". 16. Go to "Deployments" and click "Retry deployment". -17. Enjoy. \ No newline at end of file +17. Enjoy. From f7a6fa987322d800bd058f7ffa2641361c53b12d Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Thu, 7 Sep 2023 17:43:17 +0800 Subject: [PATCH 039/108] Update README_CN.md --- README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index 1111540e9..e593e45da 100644 --- a/README_CN.md +++ b/README_CN.md @@ -114,7 +114,7 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填 OPENAI_API_KEY= # 中国大陆用户,可以使用本项目自带的代理进行开发,你也可以自由选择其他代理地址 -BASE_URL=https://chatgpt1.nextweb.fun/api/proxy +BASE_URL=https://chatgpt2.nextweb.fun/api/proxy ``` ### 本地开发 From 505c8cde81e2db83f9bd92fa05237a09dd3f645c Mon Sep 17 00:00:00 2001 From: shoito <37051+shoito@users.noreply.github.com> Date: Sat, 9 Sep 2023 16:10:24 +0900 Subject: [PATCH 040/108] improve japanese translations --- app/locales/jp.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/locales/jp.ts b/app/locales/jp.ts index c3e00fa09..b63e8ba3a 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -19,7 +19,11 @@ const jp: PartialLocaleType = { Copy: "コピー", Stop: "停止", Retry: "リトライ", + Pin: "ピン", + PinToastContent: "コンテキストプロンプトに1つのメッセージをピン留めしました", + PinToastAction: "表示", Delete: "削除", + Edit: "編集", }, Rename: "チャットの名前を変更", Typing: "入力中…", @@ -33,7 +37,7 @@ const jp: PartialLocaleType = { Send: "送信", Config: { Reset: "リセット", - SaveAs: "另存为面具", + SaveAs: "保存", }, }, Export: { From 38f6956e71a3d582b24e67ee93d263fcc7367725 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 11 Sep 2023 00:20:23 +0800 Subject: [PATCH 041/108] feat: close #2754 add import/export to file --- app/components/error.tsx | 7 +- app/components/mask.tsx | 6 +- app/components/settings.tsx | 104 +++++++------- app/locales/cn.ts | 11 ++ app/store/access.ts | 151 +++++++++----------- app/store/chat.ts | 133 ++++++++++-------- app/store/config.ts | 165 +++++++++++----------- app/store/mask.ts | 172 +++++++++++------------ app/store/prompt.ts | 266 +++++++++++++++++------------------- app/store/sync.ts | 156 ++++++++++++--------- app/store/update.ts | 162 ++++++++++------------ app/utils/clone.ts | 3 + app/utils/store.ts | 55 ++++++++ app/utils/sync.ts | 162 ++++++++++++++++++++++ 14 files changed, 880 insertions(+), 673 deletions(-) create mode 100644 app/utils/clone.ts create mode 100644 app/utils/store.ts create mode 100644 app/utils/sync.ts diff --git a/app/components/error.tsx b/app/components/error.tsx index b38341e22..914740f96 100644 --- a/app/components/error.tsx +++ b/app/components/error.tsx @@ -4,8 +4,8 @@ import GithubIcon from "../icons/github.svg"; import ResetIcon from "../icons/reload.svg"; import { ISSUE_URL } from "../constant"; import Locale from "../locales"; -import { downloadAs } from "../utils"; import { showConfirm } from "./ui-lib"; +import { useSyncStore } from "../store/sync"; interface IErrorBoundaryState { hasError: boolean; @@ -26,10 +26,7 @@ export class ErrorBoundary extends React.Component { clearAndSaveData() { try { - downloadAs( - JSON.stringify(localStorage), - "chatgpt-next-web-snapshot.json", - ); + useSyncStore.getState().export(); } finally { localStorage.clear(); location.reload(); diff --git a/app/components/mask.tsx b/app/components/mask.tsx index 3d8ce3a26..1ee1c239a 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -410,7 +410,7 @@ export function MaskPage() { const closeMaskModal = () => setEditingMaskId(undefined); const downloadAll = () => { - downloadAs(JSON.stringify(masks), FileName.Masks); + downloadAs(JSON.stringify(masks.filter((v) => !v.builtin)), FileName.Masks); }; const importFromFile = () => { @@ -452,11 +452,13 @@ export function MaskPage() { icon={} bordered onClick={downloadAll} + text={Locale.UI.Export} />
} + text={Locale.UI.Import} bordered onClick={() => importFromFile()} /> @@ -604,7 +606,7 @@ export function MaskPage() { - maskStore.update(editingMaskId!, updater) + maskStore.updateMask(editingMaskId!, updater) } readonly={editingMask.builtin} /> diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 1e6ef7139..19c54515f 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -10,6 +10,9 @@ import ClearIcon from "../icons/clear.svg"; import LoadingIcon from "../icons/three-dots.svg"; import EditIcon from "../icons/edit.svg"; import EyeIcon from "../icons/eye.svg"; +import DownloadIcon from "../icons/download.svg"; +import UploadIcon from "../icons/upload.svg"; + import { Input, List, @@ -49,6 +52,7 @@ import { Avatar, AvatarPicker } from "./emoji"; import { getClientConfig } from "../config/client"; import { useSyncStore } from "../store/sync"; import { nanoid } from "nanoid"; +import { useMaskStore } from "../store/mask"; function EditPromptModal(props: { id: string; onClose: () => void }) { const promptStore = usePromptStore(); @@ -75,7 +79,7 @@ function EditPromptModal(props: { id: string; onClose: () => void }) { readOnly={!prompt.isUser} className={styles["edit-prompt-title"]} onInput={(e) => - promptStore.update( + promptStore.updatePrompt( props.id, (prompt) => (prompt.title = e.currentTarget.value), ) @@ -87,7 +91,7 @@ function EditPromptModal(props: { id: string; onClose: () => void }) { className={styles["edit-prompt-content"]} rows={10} onInput={(e) => - promptStore.update( + promptStore.updatePrompt( props.id, (prompt) => (prompt.content = e.currentTarget.value), ) @@ -127,14 +131,15 @@ function UserPromptModal(props: { onClose?: () => void }) { actions={[ - promptStore.add({ + onClick={() => { + const promptId = promptStore.add({ id: nanoid(), createdAt: Date.now(), title: "Empty Prompt", content: "Empty Prompt Content", - }) - } + }); + setEditingPromptId(promptId); + }} icon={} bordered text={Locale.Settings.Prompt.Modal.Add} @@ -244,19 +249,31 @@ function DangerItems() { function SyncItems() { const syncStore = useSyncStore(); const webdav = syncStore.webDavConfig; + const chatStore = useChatStore(); + const promptStore = usePromptStore(); + const maskStore = useMaskStore(); - // not ready: https://github.com/Yidadaa/ChatGPT-Next-Web/issues/920#issuecomment-1609866332 - return null; + const stateOverview = useMemo(() => { + const sessions = chatStore.sessions; + const messageCount = sessions.reduce((p, c) => p + c.messages.length, 0); + + return { + chat: sessions.length, + message: messageCount, + prompt: Object.keys(promptStore.prompts).length, + mask: Object.keys(maskStore.masks).length, + }; + }, [chatStore.sessions, maskStore.masks, promptStore.prompts]); return ( } - text="同步" + text={Locale.UI.Sync} onClick={() => { syncStore.check().then(console.log); }} @@ -264,50 +281,25 @@ function SyncItems() { - - - { - syncStore.update( - (config) => (config.server = e.currentTarget.value), - ); - }} - /> - - - - { - syncStore.update( - (config) => (config.username = e.currentTarget.value), - ); - }} - /> - - - - { - syncStore.update( - (config) => (config.password = e.currentTarget.value), - ); - }} - /> +
+ } + text={Locale.UI.Export} + onClick={() => { + syncStore.export(); + }} + /> + } + text={Locale.UI.Import} + onClick={() => { + syncStore.import(); + }} + /> +
); @@ -562,6 +554,8 @@ export function Settings() { + + - - { + return `${overview.chat} 次对话,${overview.message} 条消息,${overview.prompt} 条提示词,${overview.mask} 个面具`; + }, + ImportFailed: "导入失败", + }, Mask: { Splash: { Title: "面具启动页", @@ -355,6 +363,9 @@ const cn = { Close: "关闭", Create: "新建", Edit: "编辑", + Export: "导出", + Import: "导入", + Sync: "同步", }, Exporter: { Model: "模型", diff --git a/app/store/access.ts b/app/store/access.ts index b60211631..9eaa81e5e 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -1,28 +1,7 @@ -import { create } from "zustand"; -import { persist } from "zustand/middleware"; import { DEFAULT_API_HOST, DEFAULT_MODELS, StoreKey } from "../constant"; import { getHeaders } from "../client/api"; -import { BOT_HELLO } from "./chat"; import { getClientConfig } from "../config/client"; - -export interface AccessControlStore { - accessCode: string; - token: string; - - needCode: boolean; - hideUserApiKey: boolean; - hideBalanceQuery: boolean; - disableGPT4: boolean; - - openaiUrl: string; - - updateToken: (_: string) => void; - updateCode: (_: string) => void; - updateOpenAiUrl: (_: string) => void; - enabledAccessControl: () => boolean; - isAuthorized: () => boolean; - fetch: () => void; -} +import { createPersistStore } from "../utils/store"; let fetchState = 0; // 0 not fetch, 1 fetching, 2 done @@ -30,72 +9,74 @@ const DEFAULT_OPENAI_URL = getClientConfig()?.buildMode === "export" ? DEFAULT_API_HOST : "/api/openai/"; console.log("[API] default openai url", DEFAULT_OPENAI_URL); -export const useAccessStore = create()( - persist( - (set, get) => ({ - token: "", - accessCode: "", - needCode: true, - hideUserApiKey: false, - hideBalanceQuery: false, - disableGPT4: false, +const DEFAULT_ACCESS_STATE = { + token: "", + accessCode: "", + needCode: true, + hideUserApiKey: false, + hideBalanceQuery: false, + disableGPT4: false, - openaiUrl: DEFAULT_OPENAI_URL, + openaiUrl: DEFAULT_OPENAI_URL, +}; - enabledAccessControl() { - get().fetch(); +export const useAccessStore = createPersistStore( + { ...DEFAULT_ACCESS_STATE }, - return get().needCode; - }, - updateCode(code: string) { - set(() => ({ accessCode: code?.trim() })); - }, - updateToken(token: string) { - set(() => ({ token: token?.trim() })); - }, - updateOpenAiUrl(url: string) { - set(() => ({ openaiUrl: url?.trim() })); - }, - isAuthorized() { - get().fetch(); + (set, get) => ({ + enabledAccessControl() { + this.fetch(); - // has token or has code or disabled access control - return ( - !!get().token || !!get().accessCode || !get().enabledAccessControl() - ); - }, - fetch() { - if (fetchState > 0 || getClientConfig()?.buildMode === "export") return; - fetchState = 1; - fetch("/api/config", { - method: "post", - body: null, - headers: { - ...getHeaders(), - }, - }) - .then((res) => res.json()) - .then((res: DangerConfig) => { - console.log("[Config] got config from server", res); - set(() => ({ ...res })); - - if (res.disableGPT4) { - DEFAULT_MODELS.forEach( - (m: any) => (m.available = !m.name.startsWith("gpt-4")), - ); - } - }) - .catch(() => { - console.error("[Config] failed to fetch config"); - }) - .finally(() => { - fetchState = 2; - }); - }, - }), - { - name: StoreKey.Access, - version: 1, + return get().needCode; }, - ), + updateCode(code: string) { + set(() => ({ accessCode: code?.trim() })); + }, + updateToken(token: string) { + set(() => ({ token: token?.trim() })); + }, + updateOpenAiUrl(url: string) { + set(() => ({ openaiUrl: url?.trim() })); + }, + isAuthorized() { + this.fetch(); + + // has token or has code or disabled access control + return ( + !!get().token || !!get().accessCode || !this.enabledAccessControl() + ); + }, + fetch() { + if (fetchState > 0 || getClientConfig()?.buildMode === "export") return; + fetchState = 1; + fetch("/api/config", { + method: "post", + body: null, + headers: { + ...getHeaders(), + }, + }) + .then((res) => res.json()) + .then((res: DangerConfig) => { + console.log("[Config] got config from server", res); + set(() => ({ ...res })); + + if (res.disableGPT4) { + DEFAULT_MODELS.forEach( + (m: any) => (m.available = !m.name.startsWith("gpt-4")), + ); + } + }) + .catch(() => { + console.error("[Config] failed to fetch config"); + }) + .finally(() => { + fetchState = 2; + }); + }, + }), + { + name: StoreKey.Access, + version: 1, + }, ); diff --git a/app/store/chat.ts b/app/store/chat.ts index 20603fe48..9b6039020 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -18,6 +18,7 @@ import { ChatControllerPool } from "../client/controller"; import { prettyObject } from "../utils/format"; import { estimateTokenLength } from "../utils/token"; import { nanoid } from "nanoid"; +import { createPersistStore } from "../utils/store"; export type ChatMessage = RequestMessage & { date: string; @@ -140,12 +141,22 @@ function fillTemplateWith(input: string, modelConfig: ModelConfig) { return output; } -export const useChatStore = create()( - persist( - (set, get) => ({ - sessions: [createEmptySession()], - currentSessionIndex: 0, +const DEFAULT_CHAT_STATE = { + sessions: [createEmptySession()], + currentSessionIndex: 0, +}; +export const useChatStore = createPersistStore( + DEFAULT_CHAT_STATE, + (set, _get) => { + function get() { + return { + ..._get(), + ...methods, + }; + } + + const methods = { clearSessions() { set(() => ({ sessions: [createEmptySession()], @@ -184,7 +195,7 @@ export const useChatStore = create()( }); }, - newSession(mask) { + newSession(mask: Mask) { const session = createEmptySession(); if (mask) { @@ -207,14 +218,14 @@ export const useChatStore = create()( })); }, - nextSession(delta) { + nextSession(delta: number) { const n = get().sessions.length; const limit = (x: number) => (x + n) % n; const i = get().currentSessionIndex; get().selectSession(limit(i + delta)); }, - deleteSession(index) { + deleteSession(index: number) { const deletingLastSession = get().sessions.length === 1; const deletedSession = get().sessions.at(index); @@ -271,7 +282,7 @@ export const useChatStore = create()( return session; }, - onNewMessage(message) { + onNewMessage(message: ChatMessage) { get().updateCurrentSession((session) => { session.messages = session.messages.concat(); session.lastUpdate = Date.now(); @@ -280,7 +291,7 @@ export const useChatStore = create()( get().summarizeSession(); }, - async onUserInput(content) { + async onUserInput(content: string) { const session = get().currentSession(); const modelConfig = session.mask.modelConfig; @@ -580,14 +591,14 @@ export const useChatStore = create()( } }, - updateStat(message) { + updateStat(message: ChatMessage) { get().updateCurrentSession((session) => { session.stat.charCount += message.content.length; // TODO: should update chat count and word count }); }, - updateCurrentSession(updater) { + updateCurrentSession(updater: (session: ChatSession) => void) { const sessions = get().sessions; const index = get().currentSessionIndex; updater(sessions[index]); @@ -598,56 +609,60 @@ export const useChatStore = create()( localStorage.clear(); location.reload(); }, - }), - { - name: StoreKey.Chat, - version: 3.1, - migrate(persistedState, version) { - const state = persistedState as any; - const newState = JSON.parse(JSON.stringify(state)) as ChatStore; + }; - if (version < 2) { - newState.sessions = []; + return methods; + }, + { + name: StoreKey.Chat, + version: 3.1, + migrate(persistedState, version) { + const state = persistedState as any; + const newState = JSON.parse( + JSON.stringify(state), + ) as typeof DEFAULT_CHAT_STATE; - const oldSessions = state.sessions; - for (const oldSession of oldSessions) { - const newSession = createEmptySession(); - newSession.topic = oldSession.topic; - newSession.messages = [...oldSession.messages]; - newSession.mask.modelConfig.sendMemory = true; - newSession.mask.modelConfig.historyMessageCount = 4; - newSession.mask.modelConfig.compressMessageLengthThreshold = 1000; - newState.sessions.push(newSession); + if (version < 2) { + newState.sessions = []; + + const oldSessions = state.sessions; + for (const oldSession of oldSessions) { + const newSession = createEmptySession(); + newSession.topic = oldSession.topic; + newSession.messages = [...oldSession.messages]; + newSession.mask.modelConfig.sendMemory = true; + newSession.mask.modelConfig.historyMessageCount = 4; + newSession.mask.modelConfig.compressMessageLengthThreshold = 1000; + newState.sessions.push(newSession); + } + } + + if (version < 3) { + // migrate id to nanoid + newState.sessions.forEach((s) => { + s.id = nanoid(); + s.messages.forEach((m) => (m.id = nanoid())); + }); + } + + // Enable `enableInjectSystemPrompts` attribute for old sessions. + // Resolve issue of old sessions not automatically enabling. + if (version < 3.1) { + newState.sessions.forEach((s) => { + if ( + // Exclude those already set by user + !s.mask.modelConfig.hasOwnProperty("enableInjectSystemPrompts") + ) { + // Because users may have changed this configuration, + // the user's current configuration is used instead of the default + const config = useAppConfig.getState(); + s.mask.modelConfig.enableInjectSystemPrompts = + config.modelConfig.enableInjectSystemPrompts; } - } + }); + } - if (version < 3) { - // migrate id to nanoid - newState.sessions.forEach((s) => { - s.id = nanoid(); - s.messages.forEach((m) => (m.id = nanoid())); - }); - } - - // Enable `enableInjectSystemPrompts` attribute for old sessions. - // Resolve issue of old sessions not automatically enabling. - if (version < 3.1) { - newState.sessions.forEach((s) => { - if ( - // Exclude those already set by user - !s.mask.modelConfig.hasOwnProperty("enableInjectSystemPrompts") - ) { - // Because users may have changed this configuration, - // the user's current configuration is used instead of the default - const config = useAppConfig.getState(); - s.mask.modelConfig.enableInjectSystemPrompts = - config.modelConfig.enableInjectSystemPrompts; - } - }); - } - - return newState; - }, + return newState as any; }, - ), + }, ); diff --git a/app/store/config.ts b/app/store/config.ts index 7070ea05e..5fa136a06 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -3,6 +3,7 @@ import { persist } from "zustand/middleware"; import { LLMModel } from "../client/api"; import { getClientConfig } from "../config/client"; import { DEFAULT_INPUT_TEMPLATE, DEFAULT_MODELS, StoreKey } from "../constant"; +import { createPersistStore } from "../utils/store"; export type ModelType = (typeof DEFAULT_MODELS)[number]["name"]; @@ -21,6 +22,8 @@ export enum Theme { } export const DEFAULT_CONFIG = { + lastUpdate: Date.now(), // timestamp, to merge state + submitKey: SubmitKey.CtrlEnter as SubmitKey, avatar: "1f603", fontSize: 14, @@ -55,13 +58,6 @@ export const DEFAULT_CONFIG = { export type ChatConfig = typeof DEFAULT_CONFIG; -export type ChatConfigStore = ChatConfig & { - reset: () => void; - update: (updater: (config: ChatConfig) => void) => void; - mergeModels: (newModels: LLMModel[]) => void; - allModels: () => LLMModel[]; -}; - export type ModelConfig = ChatConfig["modelConfig"]; export function limitNumber( @@ -98,85 +94,80 @@ export const ModalConfigValidator = { }, }; -export const useAppConfig = create()( - persist( - (set, get) => ({ - ...DEFAULT_CONFIG, - - reset() { - set(() => ({ ...DEFAULT_CONFIG })); - }, - - update(updater) { - const config = { ...get() }; - updater(config); - set(() => config); - }, - - mergeModels(newModels) { - if (!newModels || newModels.length === 0) { - return; - } - - const oldModels = get().models; - const modelMap: Record = {}; - - for (const model of oldModels) { - model.available = false; - modelMap[model.name] = model; - } - - for (const model of newModels) { - model.available = true; - modelMap[model.name] = model; - } - - set(() => ({ - models: Object.values(modelMap), - })); - }, - - allModels() { - const customModels = get() - .customModels.split(",") - .filter((v) => !!v && v.length > 0) - .map((m) => ({ name: m, available: true })); - - const models = get().models.concat(customModels); - return models; - }, - }), - { - name: StoreKey.Config, - version: 3.7, - migrate(persistedState, version) { - const state = persistedState as ChatConfig; - - if (version < 3.4) { - state.modelConfig.sendMemory = true; - state.modelConfig.historyMessageCount = 4; - state.modelConfig.compressMessageLengthThreshold = 1000; - state.modelConfig.frequency_penalty = 0; - state.modelConfig.top_p = 1; - state.modelConfig.template = DEFAULT_INPUT_TEMPLATE; - state.dontShowMaskSplashScreen = false; - state.hideBuiltinMasks = false; - } - - if (version < 3.5) { - state.customModels = "claude,claude-100k"; - } - - if (version < 3.6) { - state.modelConfig.enableInjectSystemPrompts = true; - } - - if (version < 3.7) { - state.enableAutoGenerateTitle = true; - } - - return state as any; - }, +export const useAppConfig = createPersistStore( + { ...DEFAULT_CONFIG }, + (set, get) => ({ + reset() { + set(() => ({ ...DEFAULT_CONFIG })); }, - ), + + mergeModels(newModels: LLMModel[]) { + if (!newModels || newModels.length === 0) { + return; + } + + const oldModels = get().models; + const modelMap: Record = {}; + + for (const model of oldModels) { + model.available = false; + modelMap[model.name] = model; + } + + for (const model of newModels) { + model.available = true; + modelMap[model.name] = model; + } + + set(() => ({ + models: Object.values(modelMap), + })); + }, + + allModels() { + const customModels = get() + .customModels.split(",") + .filter((v) => !!v && v.length > 0) + .map((m) => ({ name: m, available: true })); + + const models = get().models.concat(customModels); + return models; + }, + }), + { + name: StoreKey.Config, + version: 3.8, + migrate(persistedState, version) { + const state = persistedState as ChatConfig; + + if (version < 3.4) { + state.modelConfig.sendMemory = true; + state.modelConfig.historyMessageCount = 4; + state.modelConfig.compressMessageLengthThreshold = 1000; + state.modelConfig.frequency_penalty = 0; + state.modelConfig.top_p = 1; + state.modelConfig.template = DEFAULT_INPUT_TEMPLATE; + state.dontShowMaskSplashScreen = false; + state.hideBuiltinMasks = false; + } + + if (version < 3.5) { + state.customModels = "claude,claude-100k"; + } + + if (version < 3.6) { + state.modelConfig.enableInjectSystemPrompts = true; + } + + if (version < 3.7) { + state.enableAutoGenerateTitle = true; + } + + if (version < 3.8) { + state.lastUpdate = Date.now(); + } + + return state as any; + }, + }, ); diff --git a/app/store/mask.ts b/app/store/mask.ts index 02132b77d..82c41fece 100644 --- a/app/store/mask.ts +++ b/app/store/mask.ts @@ -1,11 +1,10 @@ -import { create } from "zustand"; -import { persist } from "zustand/middleware"; import { BUILTIN_MASKS } from "../masks"; import { getLang, Lang } from "../locales"; import { DEFAULT_TOPIC, ChatMessage } from "./chat"; import { ModelConfig, useAppConfig } from "./config"; import { StoreKey } from "../constant"; import { nanoid } from "nanoid"; +import { createPersistStore } from "../utils/store"; export type Mask = { id: string; @@ -25,14 +24,6 @@ export const DEFAULT_MASK_STATE = { }; export type MaskState = typeof DEFAULT_MASK_STATE; -type MaskStore = MaskState & { - create: (mask?: Partial) => Mask; - update: (id: string, updater: (mask: Mask) => void) => void; - delete: (id: string) => void; - search: (text: string) => Mask[]; - get: (id?: string) => Mask | null; - getAll: () => Mask[]; -}; export const DEFAULT_MASK_AVATAR = "gpt-bot"; export const createEmptyMask = () => @@ -46,89 +37,92 @@ export const createEmptyMask = () => lang: getLang(), builtin: false, createdAt: Date.now(), - } as Mask); + }) as Mask; -export const useMaskStore = create()( - persist( - (set, get) => ({ - ...DEFAULT_MASK_STATE, +export const useMaskStore = createPersistStore( + { ...DEFAULT_MASK_STATE }, - create(mask) { - const masks = get().masks; - const id = nanoid(); - masks[id] = { - ...createEmptyMask(), - ...mask, - id, - builtin: false, - }; + (set, get) => ({ + ...DEFAULT_MASK_STATE, - set(() => ({ masks })); + create(mask?: Partial) { + const masks = get().masks; + const id = nanoid(); + masks[id] = { + ...createEmptyMask(), + ...mask, + id, + builtin: false, + }; - return masks[id]; - }, - update(id, updater) { - const masks = get().masks; - const mask = masks[id]; - if (!mask) return; - const updateMask = { ...mask }; - updater(updateMask); - masks[id] = updateMask; - set(() => ({ masks })); - }, - delete(id) { - const masks = get().masks; - delete masks[id]; - set(() => ({ masks })); - }, + set(() => ({ masks })); + get().markUpdate(); - get(id) { - return get().masks[id ?? 1145141919810]; - }, - getAll() { - const userMasks = Object.values(get().masks).sort( - (a, b) => b.createdAt - a.createdAt, - ); - const config = useAppConfig.getState(); - if (config.hideBuiltinMasks) return userMasks; - const buildinMasks = BUILTIN_MASKS.map( - (m) => - ({ - ...m, - modelConfig: { - ...config.modelConfig, - ...m.modelConfig, - }, - } as Mask), - ); - return userMasks.concat(buildinMasks); - }, - search(text) { - return Object.values(get().masks); - }, - }), - { - name: StoreKey.Mask, - version: 3.1, - - migrate(state, version) { - const newState = JSON.parse(JSON.stringify(state)) as MaskState; - - // migrate mask id to nanoid - if (version < 3) { - Object.values(newState.masks).forEach((m) => (m.id = nanoid())); - } - - if (version < 3.1) { - const updatedMasks: Record = {}; - Object.values(newState.masks).forEach((m) => { - updatedMasks[m.id] = m; - }); - newState.masks = updatedMasks; - } - - return newState as any; - }, + return masks[id]; }, - ), + updateMask(id: string, updater: (mask: Mask) => void) { + const masks = get().masks; + const mask = masks[id]; + if (!mask) return; + const updateMask = { ...mask }; + updater(updateMask); + masks[id] = updateMask; + set(() => ({ masks })); + get().markUpdate(); + }, + delete(id: string) { + const masks = get().masks; + delete masks[id]; + set(() => ({ masks })); + get().markUpdate(); + }, + + get(id?: string) { + return get().masks[id ?? 1145141919810]; + }, + getAll() { + const userMasks = Object.values(get().masks).sort( + (a, b) => b.createdAt - a.createdAt, + ); + const config = useAppConfig.getState(); + if (config.hideBuiltinMasks) return userMasks; + const buildinMasks = BUILTIN_MASKS.map( + (m) => + ({ + ...m, + modelConfig: { + ...config.modelConfig, + ...m.modelConfig, + }, + }) as Mask, + ); + return userMasks.concat(buildinMasks); + }, + search(text: string) { + return Object.values(get().masks); + }, + }), + { + name: StoreKey.Mask, + version: 3.1, + + migrate(state, version) { + const newState = JSON.parse(JSON.stringify(state)) as MaskState; + + // migrate mask id to nanoid + if (version < 3) { + Object.values(newState.masks).forEach((m) => (m.id = nanoid())); + } + + if (version < 3.1) { + const updatedMasks: Record = {}; + Object.values(newState.masks).forEach((m) => { + updatedMasks[m.id] = m; + }); + newState.masks = updatedMasks; + } + + return newState as any; + }, + }, ); diff --git a/app/store/prompt.ts b/app/store/prompt.ts index e743f914c..c6cff1a65 100644 --- a/app/store/prompt.ts +++ b/app/store/prompt.ts @@ -1,9 +1,8 @@ -import { create } from "zustand"; -import { persist } from "zustand/middleware"; import Fuse from "fuse.js"; import { getLang } from "../locales"; import { StoreKey } from "../constant"; import { nanoid } from "nanoid"; +import { createPersistStore } from "../utils/store"; export interface Prompt { id: string; @@ -13,19 +12,6 @@ export interface Prompt { createdAt: number; } -export interface PromptStore { - counter: number; - prompts: Record; - - add: (prompt: Prompt) => string; - get: (id: string) => Prompt | undefined; - remove: (id: string) => void; - search: (text: string) => Prompt[]; - update: (id: string, updater: (prompt: Prompt) => void) => void; - - getUserPrompts: () => Prompt[]; -} - export const SearchService = { ready: false, builtinEngine: new Fuse([], { keys: ["title"] }), @@ -62,130 +48,136 @@ export const SearchService = { }, }; -export const usePromptStore = create()( - persist( - (set, get) => ({ - counter: 0, - latestId: 0, - prompts: {}, +export const usePromptStore = createPersistStore( + { + counter: 0, + prompts: {} as Record, + }, - add(prompt) { - const prompts = get().prompts; - prompt.id = nanoid(); - prompt.isUser = true; - prompt.createdAt = Date.now(); - prompts[prompt.id] = prompt; + (set, get) => ({ + add(prompt: Prompt) { + const prompts = get().prompts; + prompt.id = nanoid(); + prompt.isUser = true; + prompt.createdAt = Date.now(); + prompts[prompt.id] = prompt; - set(() => ({ - latestId: prompt.id!, - prompts: prompts, - })); + set(() => ({ + prompts: prompts, + })); - return prompt.id!; - }, - - get(id) { - const targetPrompt = get().prompts[id]; - - if (!targetPrompt) { - return SearchService.builtinPrompts.find((v) => v.id === id); - } - - return targetPrompt; - }, - - remove(id) { - const prompts = get().prompts; - delete prompts[id]; - SearchService.remove(id); - - set(() => ({ - prompts, - counter: get().counter + 1, - })); - }, - - getUserPrompts() { - const userPrompts = Object.values(get().prompts ?? {}); - userPrompts.sort((a, b) => - b.id && a.id ? b.createdAt - a.createdAt : 0, - ); - return userPrompts; - }, - - update(id, updater) { - const prompt = get().prompts[id] ?? { - title: "", - content: "", - id, - }; - - SearchService.remove(id); - updater(prompt); - const prompts = get().prompts; - prompts[id] = prompt; - set(() => ({ prompts })); - SearchService.add(prompt); - }, - - search(text) { - if (text.length === 0) { - // return all rompts - return get().getUserPrompts().concat(SearchService.builtinPrompts); - } - return SearchService.search(text) as Prompt[]; - }, - }), - { - name: StoreKey.Prompt, - version: 3, - - migrate(state, version) { - const newState = JSON.parse(JSON.stringify(state)) as PromptStore; - - if (version < 3) { - Object.values(newState.prompts).forEach((p) => (p.id = nanoid())); - } - - return newState; - }, - - onRehydrateStorage(state) { - const PROMPT_URL = "./prompts.json"; - - type PromptList = Array<[string, string]>; - - fetch(PROMPT_URL) - .then((res) => res.json()) - .then((res) => { - let fetchPrompts = [res.en, res.cn]; - if (getLang() === "cn") { - fetchPrompts = fetchPrompts.reverse(); - } - const builtinPrompts = fetchPrompts.map( - (promptList: PromptList) => { - return promptList.map( - ([title, content]) => - ({ - id: nanoid(), - title, - content, - createdAt: Date.now(), - } as Prompt), - ); - }, - ); - - const userPrompts = - usePromptStore.getState().getUserPrompts() ?? []; - - const allPromptsForSearch = builtinPrompts - .reduce((pre, cur) => pre.concat(cur), []) - .filter((v) => !!v.title && !!v.content); - SearchService.count.builtin = res.en.length + res.cn.length; - SearchService.init(allPromptsForSearch, userPrompts); - }); - }, + return prompt.id!; }, - ), + + get(id: string) { + const targetPrompt = get().prompts[id]; + + if (!targetPrompt) { + return SearchService.builtinPrompts.find((v) => v.id === id); + } + + return targetPrompt; + }, + + remove(id: string) { + const prompts = get().prompts; + delete prompts[id]; + + Object.entries(prompts).some(([key, prompt]) => { + if (prompt.id === id) { + delete prompts[key]; + return true; + } + return false; + }); + + SearchService.remove(id); + + set(() => ({ + prompts, + counter: get().counter + 1, + })); + }, + + getUserPrompts() { + const userPrompts = Object.values(get().prompts ?? {}); + userPrompts.sort((a, b) => + b.id && a.id ? b.createdAt - a.createdAt : 0, + ); + return userPrompts; + }, + + updatePrompt(id: string, updater: (prompt: Prompt) => void) { + const prompt = get().prompts[id] ?? { + title: "", + content: "", + id, + }; + + SearchService.remove(id); + updater(prompt); + const prompts = get().prompts; + prompts[id] = prompt; + set(() => ({ prompts })); + SearchService.add(prompt); + }, + + search(text: string) { + if (text.length === 0) { + // return all rompts + return this.getUserPrompts().concat(SearchService.builtinPrompts); + } + return SearchService.search(text) as Prompt[]; + }, + }), + { + name: StoreKey.Prompt, + version: 3, + + migrate(state, version) { + const newState = JSON.parse(JSON.stringify(state)) as { + prompts: Record; + }; + + if (version < 3) { + Object.values(newState.prompts).forEach((p) => (p.id = nanoid())); + } + + return newState as any; + }, + + onRehydrateStorage(state) { + const PROMPT_URL = "./prompts.json"; + + type PromptList = Array<[string, string]>; + + fetch(PROMPT_URL) + .then((res) => res.json()) + .then((res) => { + let fetchPrompts = [res.en, res.cn]; + if (getLang() === "cn") { + fetchPrompts = fetchPrompts.reverse(); + } + const builtinPrompts = fetchPrompts.map((promptList: PromptList) => { + return promptList.map( + ([title, content]) => + ({ + id: nanoid(), + title, + content, + createdAt: Date.now(), + }) as Prompt, + ); + }); + + const userPrompts = usePromptStore.getState().getUserPrompts() ?? []; + + const allPromptsForSearch = builtinPrompts + .reduce((pre, cur) => pre.concat(cur), []) + .filter((v) => !!v.title && !!v.content); + SearchService.count.builtin = res.en.length + res.cn.length; + SearchService.init(allPromptsForSearch, userPrompts); + }); + }, + }, ); diff --git a/app/store/sync.ts b/app/store/sync.ts index 1a111f75a..fc6028098 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -1,7 +1,15 @@ import { Updater } from "../typing"; -import { create } from "zustand"; -import { persist } from "zustand/middleware"; import { StoreKey } from "../constant"; +import { createPersistStore } from "../utils/store"; +import { + AppState, + getLocalAppState, + mergeAppState, + setLocalAppState, +} from "../utils/sync"; +import { downloadAs, readFromFile } from "../utils"; +import { showToast } from "../components/ui-lib"; +import Locale from "../locales"; export interface WebDavConfig { server: string; @@ -20,68 +28,86 @@ export interface SyncStore { headers: () => { Authorization: string }; } -const FILE = { - root: "/chatgpt-next-web/", -}; - -export const useSyncStore = create()( - persist( - (set, get) => ({ - webDavConfig: { - server: "", - username: "", - password: "", - }, - - lastSyncTime: 0, - - update(updater) { - const config = { ...get().webDavConfig }; - updater(config); - set({ webDavConfig: config }); - }, - - async check() { - try { - const res = await fetch(this.path(""), { - method: "PROFIND", - headers: this.headers(), - }); - console.log(res); - return res.status === 207; - } catch (e) { - console.error("[Sync] ", e); - return false; - } - }, - - path(path: string) { - let url = get().webDavConfig.server; - - if (!url.endsWith("/")) { - url += "/"; - } - - if (path.startsWith("/")) { - path = path.slice(1); - } - - return url + path; - }, - - headers() { - const auth = btoa( - [get().webDavConfig.username, get().webDavConfig.password].join(":"), - ); - - return { - Authorization: `Basic ${auth}`, - }; - }, - }), - { - name: StoreKey.Sync, - version: 1, +export const useSyncStore = createPersistStore( + { + webDavConfig: { + server: "", + username: "", + password: "", }, - ), + + lastSyncTime: 0, + }, + (set, get) => ({ + webDavConfig: { + server: "", + username: "", + password: "", + }, + + lastSyncTime: 0, + + export() { + const state = getLocalAppState(); + const fileName = `Backup-${new Date().toLocaleString()}.json`; + downloadAs(JSON.stringify(state), fileName); + }, + + async import() { + const rawContent = await readFromFile(); + + try { + const remoteState = JSON.parse(rawContent) as AppState; + const localState = getLocalAppState(); + mergeAppState(localState, remoteState); + setLocalAppState(localState); + location.reload(); + } catch (e) { + console.error("[Import]", e); + showToast(Locale.Settings.Sync.ImportFailed); + } + }, + + async check() { + try { + const res = await fetch(this.path(""), { + method: "PROFIND", + headers: this.headers(), + }); + console.log(res); + return res.status === 207; + } catch (e) { + console.error("[Sync] ", e); + return false; + } + }, + + path(path: string) { + let url = get().webDavConfig.server; + + if (!url.endsWith("/")) { + url += "/"; + } + + if (path.startsWith("/")) { + path = path.slice(1); + } + + return url + path; + }, + + headers() { + const auth = btoa( + [get().webDavConfig.username, get().webDavConfig.password].join(":"), + ); + + return { + Authorization: `Basic ${auth}`, + }; + }, + }), + { + name: StoreKey.Sync, + version: 1, + }, ); diff --git a/app/store/update.ts b/app/store/update.ts index dd4d3c724..42b86586c 100644 --- a/app/store/update.ts +++ b/app/store/update.ts @@ -1,24 +1,7 @@ -import { create } from "zustand"; -import { persist } from "zustand/middleware"; import { FETCH_COMMIT_URL, FETCH_TAG_URL, StoreKey } from "../constant"; import { api } from "../client/api"; import { getClientConfig } from "../config/client"; - -export interface UpdateStore { - versionType: "date" | "tag"; - lastUpdate: number; - version: string; - remoteVersion: string; - - used?: number; - subscription?: number; - lastUpdateUsage: number; - - getLatestVersion: (force?: boolean) => Promise; - updateUsage: (force?: boolean) => Promise; - - formatVersion: (version: string) => string; -} +import { createPersistStore } from "../utils/store"; const ONE_MINUTE = 60 * 1000; @@ -35,7 +18,9 @@ function formatVersionDate(t: string) { ].join(""); } -async function getVersion(type: "date" | "tag") { +type VersionType = "date" | "tag"; + +async function getVersion(type: VersionType) { if (type === "date") { const data = (await (await fetch(FETCH_COMMIT_URL)).json()) as { commit: { @@ -55,75 +40,76 @@ async function getVersion(type: "date" | "tag") { } } -export const useUpdateStore = create()( - persist( - (set, get) => ({ - versionType: "tag", - lastUpdate: 0, - version: "unknown", - remoteVersion: "", +export const useUpdateStore = createPersistStore( + { + versionType: "tag" as VersionType, + lastUpdate: 0, + version: "unknown", + remoteVersion: "", + used: 0, + subscription: 0, - lastUpdateUsage: 0, - - formatVersion(version: string) { - if (get().versionType === "date") { - version = formatVersionDate(version); - } - return version; - }, - - async getLatestVersion(force = false) { - const versionType = get().versionType; - let version = - versionType === "date" - ? getClientConfig()?.commitDate - : getClientConfig()?.version; - - set(() => ({ version })); - - const shouldCheck = Date.now() - get().lastUpdate > 2 * 60 * ONE_MINUTE; - if (!force && !shouldCheck) return; - - set(() => ({ - lastUpdate: Date.now(), - })); - - try { - const remoteId = await getVersion(versionType); - set(() => ({ - remoteVersion: remoteId, - })); - console.log("[Got Upstream] ", remoteId); - } catch (error) { - console.error("[Fetch Upstream Commit Id]", error); - } - }, - - async updateUsage(force = false) { - const overOneMinute = Date.now() - get().lastUpdateUsage >= ONE_MINUTE; - if (!overOneMinute && !force) return; - - set(() => ({ - lastUpdateUsage: Date.now(), - })); - - try { - const usage = await api.llm.usage(); - - if (usage) { - set(() => ({ - used: usage.used, - subscription: usage.total, - })); - } - } catch (e) { - console.error((e as Error).message); - } - }, - }), - { - name: StoreKey.Update, - version: 1, + lastUpdateUsage: 0, + }, + (set, get) => ({ + formatVersion(version: string) { + if (get().versionType === "date") { + version = formatVersionDate(version); + } + return version; }, - ), + + async getLatestVersion(force = false) { + const versionType = get().versionType; + let version = + versionType === "date" + ? getClientConfig()?.commitDate + : getClientConfig()?.version; + + set(() => ({ version })); + + const shouldCheck = Date.now() - get().lastUpdate > 2 * 60 * ONE_MINUTE; + if (!force && !shouldCheck) return; + + set(() => ({ + lastUpdate: Date.now(), + })); + + try { + const remoteId = await getVersion(versionType); + set(() => ({ + remoteVersion: remoteId, + })); + console.log("[Got Upstream] ", remoteId); + } catch (error) { + console.error("[Fetch Upstream Commit Id]", error); + } + }, + + async updateUsage(force = false) { + const overOneMinute = Date.now() - get().lastUpdateUsage >= ONE_MINUTE; + if (!overOneMinute && !force) return; + + set(() => ({ + lastUpdateUsage: Date.now(), + })); + + try { + const usage = await api.llm.usage(); + + if (usage) { + set(() => ({ + used: usage.used, + subscription: usage.total, + })); + } + } catch (e) { + console.error((e as Error).message); + } + }, + }), + { + name: StoreKey.Update, + version: 1, + }, ); diff --git a/app/utils/clone.ts b/app/utils/clone.ts new file mode 100644 index 000000000..2958b6b9c --- /dev/null +++ b/app/utils/clone.ts @@ -0,0 +1,3 @@ +export function deepClone(obj: T) { + return JSON.parse(JSON.stringify(obj)); +} diff --git a/app/utils/store.ts b/app/utils/store.ts new file mode 100644 index 000000000..cd151dc49 --- /dev/null +++ b/app/utils/store.ts @@ -0,0 +1,55 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; +import { Updater } from "../typing"; +import { deepClone } from "./clone"; + +type SecondParam = T extends ( + _f: infer _F, + _s: infer S, + ...args: infer _U +) => any + ? S + : never; + +type MakeUpdater = { + lastUpdateTime: number; + + markUpdate: () => void; + update: Updater; +}; + +type SetStoreState = ( + partial: T | Partial | ((state: T) => T | Partial), + replace?: boolean | undefined, +) => void; + +export function createPersistStore( + defaultState: T, + methods: ( + set: SetStoreState>, + get: () => T & MakeUpdater, + ) => M, + persistOptions: SecondParam>>, +) { + return create>()( + persist((set, get) => { + return { + ...defaultState, + ...methods(set as any, get), + + lastUpdateTime: 0, + markUpdate() { + set({ lastUpdateTime: Date.now() } as Partial< + T & M & MakeUpdater + >); + }, + update(updater) { + const state = deepClone(get()); + updater(state); + get().markUpdate(); + set(state); + }, + }; + }, persistOptions), + ); +} diff --git a/app/utils/sync.ts b/app/utils/sync.ts new file mode 100644 index 000000000..ab1f1f449 --- /dev/null +++ b/app/utils/sync.ts @@ -0,0 +1,162 @@ +import { + ChatSession, + useAccessStore, + useAppConfig, + useChatStore, +} from "../store"; +import { useMaskStore } from "../store/mask"; +import { usePromptStore } from "../store/prompt"; +import { StoreKey } from "../constant"; +import { merge } from "./merge"; + +type NonFunctionKeys = { + [K in keyof T]: T[K] extends (...args: any[]) => any ? never : K; +}[keyof T]; +type NonFunctionFields = Pick>; + +export function getNonFunctionFileds(obj: T) { + const ret: any = {}; + + Object.entries(obj).map(([k, v]) => { + if (typeof v !== "function") { + ret[k] = v; + } + }); + + return ret as NonFunctionFields; +} + +export type GetStoreState = T extends { getState: () => infer U } + ? NonFunctionFields + : never; + +const LocalStateSetters = { + [StoreKey.Chat]: useChatStore.setState, + [StoreKey.Access]: useAccessStore.setState, + [StoreKey.Config]: useAppConfig.setState, + [StoreKey.Mask]: useMaskStore.setState, + [StoreKey.Prompt]: usePromptStore.setState, +} as const; + +const LocalStateGetters = { + [StoreKey.Chat]: () => getNonFunctionFileds(useChatStore.getState()), + [StoreKey.Access]: () => getNonFunctionFileds(useAccessStore.getState()), + [StoreKey.Config]: () => getNonFunctionFileds(useAppConfig.getState()), + [StoreKey.Mask]: () => getNonFunctionFileds(useMaskStore.getState()), + [StoreKey.Prompt]: () => getNonFunctionFileds(usePromptStore.getState()), +} as const; + +export type AppState = { + [k in keyof typeof LocalStateGetters]: ReturnType< + (typeof LocalStateGetters)[k] + >; +}; + +type Merger = ( + localState: U, + remoteState: U, +) => U; + +type StateMerger = { + [K in keyof AppState]: Merger; +}; + +// we merge remote state to local state +const MergeStates: StateMerger = { + [StoreKey.Chat]: (localState, remoteState) => { + // merge sessions + const localSessions: Record = {}; + localState.sessions.forEach((s) => (localSessions[s.id] = s)); + + remoteState.sessions.forEach((remoteSession) => { + const localSession = localSessions[remoteSession.id]; + if (!localSession) { + // if remote session is new, just merge it + localState.sessions.push(remoteSession); + } else { + // if both have the same session id, merge the messages + const localMessageIds = new Set(localSession.messages.map((v) => v.id)); + remoteSession.messages.forEach((m) => { + if (!localMessageIds.has(m.id)) { + localSession.messages.push(m); + } + }); + + // sort local messages with date field in asc order + localSession.messages.sort( + (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(), + ); + } + }); + + // sort local sessions with date field in desc order + localState.sessions.sort( + (a, b) => + new Date(b.lastUpdate).getTime() - new Date(a.lastUpdate).getTime(), + ); + + return localState; + }, + [StoreKey.Prompt]: (localState, remoteState) => { + localState.prompts = { + ...remoteState.prompts, + ...localState.prompts, + }; + return localState; + }, + [StoreKey.Mask]: (localState, remoteState) => { + localState.masks = { + ...remoteState.masks, + ...localState.masks, + }; + return localState; + }, + [StoreKey.Config]: mergeWithUpdate, + [StoreKey.Access]: mergeWithUpdate, +}; + +export function getLocalAppState() { + const appState = Object.fromEntries( + Object.entries(LocalStateGetters).map(([key, getter]) => { + return [key, getter()]; + }), + ) as AppState; + + return appState; +} + +export function setLocalAppState(appState: AppState) { + Object.entries(LocalStateSetters).forEach(([key, setter]) => { + setter(appState[key as keyof AppState]); + }); +} + +export function mergeAppState(localState: AppState, remoteState: AppState) { + Object.keys(localState).forEach((k: string) => { + const key = k as T; + const localStoreState = localState[key]; + const remoteStoreState = remoteState[key]; + MergeStates[key](localStoreState, remoteStoreState); + }); + + return localState; +} + +/** + * Merge state with `lastUpdateTime`, older state will be override + */ +export function mergeWithUpdate( + localState: T, + remoteState: T, +) { + const localUpdateTime = localState.lastUpdateTime ?? 0; + const remoteUpdateTime = localState.lastUpdateTime ?? 1; + + if (localUpdateTime < remoteUpdateTime) { + merge(remoteState, localState); + return { ...remoteState }; + } else { + merge(localState, remoteState); + return { ...localState }; + } +} From 5dced2808802fb015e0c5e6e70fbdb9d794bd183 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 11 Sep 2023 00:22:14 +0800 Subject: [PATCH 042/108] fixup: add en locales --- app/locales/en.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/locales/en.ts b/app/locales/en.ts index 981357274..e31295787 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -180,6 +180,14 @@ const en: LocaleType = { Title: "Auto Generate Title", SubTitle: "Generate a suitable title based on the conversation content", }, + Sync: { + LastUpdate: "Last Update", + LocalState: "Local Data", + Overview: (overview: any) => { + return `${overview.chat} chats,${overview.message} messages,${overview.prompt} prompts,${overview.mask} masks`; + }, + ImportFailed: "Failed to import from file", + }, Mask: { Splash: { Title: "Mask Splash Screen", @@ -355,6 +363,9 @@ const en: LocaleType = { Close: "Close", Create: "Create", Edit: "Edit", + Export: "Export", + Import: "Import", + Sync: "Sync", }, Exporter: { Model: "Model", From c73a91a0f5d90a3a4b341feba3aff30c7aaed4b9 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 11 Sep 2023 00:24:05 +0800 Subject: [PATCH 043/108] fixup: fix type errors --- app/store/chat.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/store/chat.ts b/app/store/chat.ts index 9b6039020..269cc4a33 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -195,7 +195,7 @@ export const useChatStore = createPersistStore( }); }, - newSession(mask: Mask) { + newSession(mask?: Mask) { const session = createEmptySession(); if (mask) { From 415e9dc9131594adec4af5510cd7379fa46a258e Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 11 Sep 2023 00:34:51 +0800 Subject: [PATCH 044/108] fixup: minor sync fixup --- app/components/settings.tsx | 3 ++- app/store/config.ts | 2 -- app/store/mask.ts | 2 -- app/store/sync.ts | 20 +------------------- 4 files changed, 3 insertions(+), 24 deletions(-) diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 19c54515f..4106c9704 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -22,6 +22,7 @@ import { Popover, Select, showConfirm, + showToast, } from "./ui-lib"; import { ModelConfigList } from "./model-config"; @@ -275,7 +276,7 @@ function SyncItems() { icon={} text={Locale.UI.Sync} onClick={() => { - syncStore.check().then(console.log); + showToast(Locale.WIP); }} /> diff --git a/app/store/config.ts b/app/store/config.ts index 5fa136a06..b01319542 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -1,5 +1,3 @@ -import { create } from "zustand"; -import { persist } from "zustand/middleware"; import { LLMModel } from "../client/api"; import { getClientConfig } from "../config/client"; import { DEFAULT_INPUT_TEMPLATE, DEFAULT_MODELS, StoreKey } from "../constant"; diff --git a/app/store/mask.ts b/app/store/mask.ts index 82c41fece..dfd4089b7 100644 --- a/app/store/mask.ts +++ b/app/store/mask.ts @@ -43,8 +43,6 @@ export const useMaskStore = createPersistStore( { ...DEFAULT_MASK_STATE }, (set, get) => ({ - ...DEFAULT_MASK_STATE, - create(mask?: Partial) { const masks = get().masks; const id = nanoid(); diff --git a/app/store/sync.ts b/app/store/sync.ts index fc6028098..466a98cf5 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -17,17 +17,6 @@ export interface WebDavConfig { password: string; } -export interface SyncStore { - webDavConfig: WebDavConfig; - lastSyncTime: number; - - update: Updater; - check: () => Promise; - - path: (path: string) => string; - headers: () => { Authorization: string }; -} - export const useSyncStore = createPersistStore( { webDavConfig: { @@ -39,18 +28,11 @@ export const useSyncStore = createPersistStore( lastSyncTime: 0, }, (set, get) => ({ - webDavConfig: { - server: "", - username: "", - password: "", - }, - - lastSyncTime: 0, - export() { const state = getLocalAppState(); const fileName = `Backup-${new Date().toLocaleString()}.json`; downloadAs(JSON.stringify(state), fileName); + set({ lastSyncTime: Date.now() }); }, async import() { From 57158890c3640efb5254a7b4e66aad7d534ea5fc Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 11 Sep 2023 00:39:56 +0800 Subject: [PATCH 045/108] fixup --- app/components/settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 4106c9704..9de603bb3 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -270,7 +270,7 @@ function SyncItems() { } From 605dd72354764ea2f07753130cbafe1d55b50d97 Mon Sep 17 00:00:00 2001 From: B0zal Date: Mon, 11 Sep 2023 08:49:08 +0700 Subject: [PATCH 046/108] [+] CodeQL Report Fix log injection vulnerability in useSyncStore Severity : High Sanitize the 'res' object before logging it in the 'check' method of useSyncStore to prevent log injection attacks. The 'res' object is now sanitized by extracting only the necessary properties ('status', 'statusText', and 'headers') and logging the sanitized object instead. This ensures that only safe and expected data is logged, mitigating the risk of log injection vulnerabilities. --- app/store/sync.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/store/sync.ts b/app/store/sync.ts index 466a98cf5..502cf71cb 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -56,7 +56,12 @@ export const useSyncStore = createPersistStore( method: "PROFIND", headers: this.headers(), }); - console.log(res); + const sanitizedRes = { + status: res.status, + statusText: res.statusText, + headers: res.headers, + }; + console.log(sanitizedRes); return res.status === 207; } catch (e) { console.error("[Sync] ", e); From e36abc3ac6c68a8f804af3d61ed8ce78e6d0af12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 10:31:50 +0000 Subject: [PATCH 047/108] chore(deps): bump emoji-picker-react from 4.4.8 to 4.5.1 Bumps [emoji-picker-react](https://github.com/ealush/emoji-picker-react) from 4.4.8 to 4.5.1. - [Release notes](https://github.com/ealush/emoji-picker-react/releases) - [Commits](https://github.com/ealush/emoji-picker-react/commits) --- updated-dependencies: - dependency-name: emoji-picker-react dependency-type: direct:production update-type: version-update:semver-minor ... 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 6610083bd..584640e06 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@hello-pangea/dnd": "^16.3.0", "@svgr/webpack": "^6.5.1", "@vercel/analytics": "^0.1.11", - "emoji-picker-react": "^4.4.7", + "emoji-picker-react": "^4.5.1", "fuse.js": "^6.6.2", "html-to-image": "^1.11.11", "mermaid": "^10.3.1", diff --git a/yarn.lock b/yarn.lock index cbce2ef17..fc36251b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2762,10 +2762,10 @@ elkjs@^0.8.2: resolved "https://registry.npmmirror.com/elkjs/-/elkjs-0.8.2.tgz#c37763c5a3e24e042e318455e0147c912a7c248e" integrity sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ== -emoji-picker-react@^4.4.7: - version "4.4.8" - resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-4.4.8.tgz#cd18e942720d0d01e3d488a008f5e79aa315ec87" - integrity sha512-5bbj0PCvpjB64PZj31wZ35EoebF2mKoHqEEx9u2ZLghx7sGoD1MgyDhse851rqROypjhmK9IUY15QBa7mCLP0g== +emoji-picker-react@^4.5.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-4.5.1.tgz#341f27dc86ad09340a316e0632484fcb9aff7195" + integrity sha512-zpm0ui0TWkXZDUIevyNM0rC9Jyqc08RvVXH0KgsbSkDr+VgMQmYLu6UeI4SIWMZKsKMjQwujPpncRCFlEeykjw== dependencies: clsx "^1.2.1" From 8469f448b50ad9e2db96ecc003a9bb7cdc8573da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 10:32:00 +0000 Subject: [PATCH 048/108] chore(deps): bump react-router-dom from 6.14.1 to 6.15.0 Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.14.1 to 6.15.0. - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.15.0/packages/react-router-dom) --- updated-dependencies: - dependency-name: react-router-dom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 6610083bd..46c3c16ef 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-markdown": "^8.0.7", - "react-router-dom": "^6.14.1", + "react-router-dom": "^6.15.0", "rehype-highlight": "^6.0.0", "rehype-katex": "^6.0.3", "remark-breaks": "^3.0.2", diff --git a/yarn.lock b/yarn.lock index cbce2ef17..2fa343ed3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1221,10 +1221,10 @@ tiny-glob "^0.2.9" tslib "^2.4.0" -"@remix-run/router@1.7.1": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.1.tgz#fea7ac35ae4014637c130011f59428f618730498" - integrity sha512-bgVQM4ZJ2u2CM8k1ey70o1ePFXsEzYVZoWghh6WjM8p59jQ7HxzbHW4SbnWFG7V9ig9chLawQxDTZ3xzOF8MkQ== +"@remix-run/router@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.8.0.tgz#e848d2f669f601544df15ce2a313955e4bf0bafc" + integrity sha512-mrfKqIHnSZRyIzBcanNJmVQELTnX+qagEDlcKO90RgRBVOZGSGvZKeDihTRfWcqoDn5N/NkUcwWTccnpN18Tfg== "@rushstack/eslint-patch@^1.1.3": version "1.2.0" @@ -5092,20 +5092,20 @@ react-redux@^8.1.1: react-is "^18.0.0" use-sync-external-store "^1.0.0" -react-router-dom@^6.14.1: - version "6.14.1" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.1.tgz#0ad7ba7abdf75baa61169d49f096f0494907a36f" - integrity sha512-ssF6M5UkQjHK70fgukCJyjlda0Dgono2QGwqGvuk7D+EDGHdacEN3Yke2LTMjkrpHuFwBfDFsEjGVXBDmL+bWw== +react-router-dom@^6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.15.0.tgz#6da7db61e56797266fbbef0d5e324d6ac443ee40" + integrity sha512-aR42t0fs7brintwBGAv2+mGlCtgtFQeOzK0BM1/OiqEzRejOZtpMZepvgkscpMUnKb8YO84G7s3LsHnnDNonbQ== dependencies: - "@remix-run/router" "1.7.1" - react-router "6.14.1" + "@remix-run/router" "1.8.0" + react-router "6.15.0" -react-router@6.14.1: - version "6.14.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.1.tgz#5e82bcdabf21add859dc04b1859f91066b3a5810" - integrity sha512-U4PfgvG55LdvbQjg5Y9QRWyVxIdO1LlpYT7x+tMAxd9/vmiPuJhIwdxZuIQLN/9e3O4KFDHYfR9gzGeYMasW8g== +react-router@6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.15.0.tgz#bf2cb5a4a7ed57f074d4ea88db0d95033f39cac8" + integrity sha512-NIytlzvzLwJkCQj2HLefmeakxxWHWAP+02EGqWEZy+DgfHHKQMUoBBjUQLOtFInBMhWtb3hiUy6MfFgwLjXhqg== dependencies: - "@remix-run/router" "1.7.1" + "@remix-run/router" "1.8.0" react@^18.2.0: version "18.2.0" From 1bbf310c460f809f3344b29c71e38c4244bb9cdb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 10:32:25 +0000 Subject: [PATCH 049/108] chore(deps-dev): bump typescript from 4.9.5 to 5.2.2 Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.9.5 to 5.2.2. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v4.9.5...v5.2.2) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-major ... 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 6610083bd..3a04b7495 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "husky": "^8.0.0", "lint-staged": "^13.2.2", "prettier": "^3.0.2", - "typescript": "4.9.5", + "typescript": "5.2.2", "webpack": "^5.88.1" }, "resolutions": { diff --git a/yarn.lock b/yarn.lock index cbce2ef17..bf69bb6f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5786,10 +5786,10 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typescript@4.9.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== unbox-primitive@^1.0.2: version "1.0.2" From ff60ffca3e5483f8f474af1fa1d1d15fded4889a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 10:32:34 +0000 Subject: [PATCH 050/108] chore(deps-dev): bump eslint from 8.44.0 to 8.49.0 Bumps [eslint](https://github.com/eslint/eslint) from 8.44.0 to 8.49.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.44.0...v8.49.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 88 +++++++++++++++++++++++++--------------------------- 2 files changed, 44 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index 6610083bd..bba4d18ae 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@types/react-katex": "^3.0.0", "@types/spark-md5": "^3.0.2", "cross-env": "^7.0.3", - "eslint": "^8.44.0", + "eslint": "^8.49.0", "eslint-config-next": "13.4.19", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", diff --git a/yarn.lock b/yarn.lock index cbce2ef17..b8a546882 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1012,15 +1012,15 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.4.0": - version "4.5.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" - integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== +"@eslint-community/regexpp@^4.6.1": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.0.tgz#11195513186f68d42fbf449f9a7136b2c0c92005" + integrity sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg== -"@eslint/eslintrc@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d" - integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A== +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -1032,10 +1032,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.44.0": - version "8.44.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af" - integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw== +"@eslint/js@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.49.0.tgz#86f79756004a97fa4df866835093f1df3d03c333" + integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w== "@fortaine/fetch-event-source@^3.0.6": version "3.0.6" @@ -1055,10 +1055,10 @@ redux "^4.2.1" use-memo-one "^1.1.3" -"@humanwhocodes/config-array@^0.11.10": - version "0.11.10" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" - integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== +"@humanwhocodes/config-array@^0.11.11": + version "0.11.11" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" + integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" @@ -1779,7 +1779,7 @@ ajv-keywords@^3.5.2: resolved "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -3050,40 +3050,40 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" - integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" - integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.44.0: - version "8.44.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.44.0.tgz#51246e3889b259bbcd1d7d736a0c10add4f0e500" - integrity sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A== +eslint@^8.49.0: + version "8.49.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.49.0.tgz#09d80a89bdb4edee2efcf6964623af1054bf6d42" + integrity sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.1.0" - "@eslint/js" "8.44.0" - "@humanwhocodes/config-array" "^0.11.10" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "8.49.0" + "@humanwhocodes/config-array" "^0.11.11" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.0" - eslint-visitor-keys "^3.4.1" - espree "^9.6.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -3093,7 +3093,6 @@ eslint@^8.44.0: globals "^13.19.0" graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" @@ -3105,13 +3104,12 @@ eslint@^8.44.0: natural-compare "^1.4.0" optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f" - integrity sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: acorn "^8.9.0" acorn-jsx "^5.3.2" @@ -3635,7 +3633,7 @@ immutable@^4.0.0: resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be" integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -5588,7 +5586,7 @@ strip-final-newline@^3.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +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" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== From b5e89d444055ee92b806855499f505f8b12bfa52 Mon Sep 17 00:00:00 2001 From: B0zal Date: Tue, 12 Sep 2023 06:56:55 +0700 Subject: [PATCH 051/108] [+] Updated Auth Page - Made changes to the Auth Page to reset the input field for the access code when the "Later" button is clicked. This ensures that only expected user is logged by entering access code or entering their OpenAI API Key, mitigating the risk of small bug issue --- app/components/auth.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 1ca83dcd3..9a5b0c655 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -15,6 +15,7 @@ export function AuthPage() { const access = useAccessStore(); const goHome = () => navigate(Path.Home); + const resetAccessCode = () => access.updateCode(""); // Reset access code to empty string useEffect(() => { if (getClientConfig()?.isApp) { @@ -48,7 +49,10 @@ export function AuthPage() { type="primary" onClick={goHome} /> - + { + resetAccessCode(); + goHome(); + }} />
); From 6f83fbd21278c90cd978108abe54291c38ec10d7 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Wed, 13 Sep 2023 02:51:02 +0800 Subject: [PATCH 052/108] feat: add webdav support --- 3 | 119 ++++++++++++++ app/api/cors/[...path]/route.ts | 44 +++++ app/components/settings.tsx | 279 +++++++++++++++++++++++++++----- app/constant.ts | 10 +- app/icons/cloud-fail.svg | 1 + app/icons/cloud-success.svg | 1 + app/icons/config.svg | 1 + app/icons/connection.svg | 1 + app/locales/cn.ts | 31 +++- app/locales/en.ts | 32 +++- app/store/sync.ts | 93 ++++++----- app/utils/cloud/index.ts | 33 ++++ app/utils/cloud/upstash.ts | 39 +++++ app/utils/cloud/webdav.ts | 78 +++++++++ app/utils/cors.ts | 50 ++++++ next.config.mjs | 43 +++-- 16 files changed, 751 insertions(+), 104 deletions(-) create mode 100644 3 create mode 100644 app/api/cors/[...path]/route.ts create mode 100644 app/icons/cloud-fail.svg create mode 100644 app/icons/cloud-success.svg create mode 100644 app/icons/config.svg create mode 100644 app/icons/connection.svg create mode 100644 app/utils/cloud/index.ts create mode 100644 app/utils/cloud/upstash.ts create mode 100644 app/utils/cloud/webdav.ts create mode 100644 app/utils/cors.ts diff --git a/3 b/3 new file mode 100644 index 000000000..371bd01ac --- /dev/null +++ b/3 @@ -0,0 +1,119 @@ +export const OWNER = "Yidadaa"; +export const REPO = "ChatGPT-Next-Web"; +export const REPO_URL = `https://github.com/${OWNER}/${REPO}`; +export const ISSUE_URL = `https://github.com/${OWNER}/${REPO}/issues`; +export const UPDATE_URL = `${REPO_URL}#keep-updated`; +export const RELEASE_URL = `${REPO_URL}/releases`; +export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/commits?per_page=1`; +export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`; +export const RUNTIME_CONFIG_DOM = "danger-runtime-config"; +export const DEFAULT_API_HOST = "https://chatgpt1.nextweb.fun/api/proxy"; + +export enum Path { + Home = "/", + Chat = "/chat", + Settings = "/settings", + NewChat = "/new-chat", + Masks = "/masks", + Auth = "/auth", +} + +export enum SlotID { + AppBody = "app-body", +} + +export enum FileName { + Masks = "masks.json", + Prompts = "prompts.json", +} + +export enum StoreKey { + Chat = "chat-next-web-store", + Access = "access-control", + Config = "app-config", + Mask = "mask-store", + Prompt = "prompt-store", + Update = "chat-update", + Sync = "sync", +} + +export const MAX_SIDEBAR_WIDTH = 500; +export const MIN_SIDEBAR_WIDTH = 230; +export const NARROW_SIDEBAR_WIDTH = 100; + +export const ACCESS_CODE_PREFIX = "nk-"; + +export const LAST_INPUT_KEY = "last-input"; +export const UNFINISHED_INPUT = (id: string) => "unfinished-input-" + id; + +export const STORAGE_KEY = "chatgpt-next-web"; + +export const REQUEST_TIMEOUT_MS = 60000; + +export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown"; + +export const OpenaiPath = { + ChatPath: "v1/chat/completions", + UsagePath: "dashboard/billing/usage", + SubsPath: "dashboard/billing/subscription", + ListModelPath: "v1/models", +}; + +export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang +export const DEFAULT_SYSTEM_TEMPLATE = ` +You are ChatGPT, a large language model trained by OpenAI. +Knowledge cutoff: 2021-09 +Current model: {{model}} +Current time: {{time}}`; + +export const SUMMARIZE_MODEL = "gpt-3.5-turbo"; + +export const DEFAULT_MODELS = [ + { + name: "gpt-4", + available: true, + }, + { + name: "gpt-4-0314", + available: true, + }, + { + name: "gpt-4-0613", + available: true, + }, + { + name: "gpt-4-32k", + available: true, + }, + { + name: "gpt-4-32k-0314", + available: true, + }, + { + name: "gpt-4-32k-0613", + available: true, + }, + { + name: "gpt-3.5-turbo", + available: true, + }, + { + name: "gpt-3.5-turbo-0301", + available: true, + }, + { + name: "gpt-3.5-turbo-0613", + available: true, + }, + { + name: "gpt-3.5-turbo-16k", + available: true, + }, + { + name: "gpt-3.5-turbo-16k-0613", + available: true, + }, +] as const; + +export const CHAT_PAGE_SIZE = 15; +export const MAX_RENDER_MSG_COUNT = 45; diff --git a/app/api/cors/[...path]/route.ts b/app/api/cors/[...path]/route.ts new file mode 100644 index 000000000..c461d250b --- /dev/null +++ b/app/api/cors/[...path]/route.ts @@ -0,0 +1,44 @@ +import { NextRequest, NextResponse } from "next/server"; + +async function handle( + req: NextRequest, + { params }: { params: { path: string[] } }, +) { + if (req.method === "OPTIONS") { + return NextResponse.json({ body: "OK" }, { status: 200 }); + } + + const [protocol, ...subpath] = params.path; + const targetUrl = `${protocol}://${subpath.join("/")}`; + + const method = req.headers.get("method") ?? undefined; + const shouldNotHaveBody = ["get", "head"].includes( + method?.toLowerCase() ?? "", + ); + + const fetchOptions: RequestInit = { + headers: { + authorization: req.headers.get("authorization") ?? "", + }, + body: shouldNotHaveBody ? null : req.body, + method, + // @ts-ignore + duplex: "half", + }; + + console.log("[Any Proxy]", targetUrl); + + const fetchResult = fetch(targetUrl, fetchOptions); + + return fetchResult; +} + +export const GET = handle; +export const POST = handle; +export const PUT = handle; + +// nextjs dose not support those https methods, sucks +export const PROFIND = handle; +export const MKCOL = handle; + +export const runtime = "edge"; diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 9de603bb3..8e43e1d1a 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -12,6 +12,12 @@ import EditIcon from "../icons/edit.svg"; import EyeIcon from "../icons/eye.svg"; import DownloadIcon from "../icons/download.svg"; import UploadIcon from "../icons/upload.svg"; +import ConfigIcon from "../icons/config.svg"; +import ConfirmIcon from "../icons/confirm.svg"; + +import ConnectionIcon from "../icons/connection.svg"; +import CloudSuccessIcon from "../icons/cloud-success.svg"; +import CloudFailIcon from "../icons/cloud-fail.svg"; import { Input, @@ -54,6 +60,7 @@ import { getClientConfig } from "../config/client"; import { useSyncStore } from "../store/sync"; import { nanoid } from "nanoid"; import { useMaskStore } from "../store/mask"; +import { ProviderType } from "../utils/cloud"; function EditPromptModal(props: { id: string; onClose: () => void }) { const promptStore = usePromptStore(); @@ -247,12 +254,183 @@ function DangerItems() { ); } +function CheckButton() { + const syncStore = useSyncStore(); + + const couldCheck = useMemo(() => { + return syncStore.coundSync(); + }, [syncStore]); + + const [checkState, setCheckState] = useState< + "none" | "checking" | "success" | "failed" + >("none"); + + async function check() { + setCheckState("checking"); + const valid = await syncStore.check(); + setCheckState(valid ? "success" : "failed"); + } + + if (!couldCheck) return null; + + return ( + + ) : checkState === "checking" ? ( + + ) : checkState === "success" ? ( + + ) : checkState === "failed" ? ( + + ) : ( + + ) + } + > + ); +} + +function SyncConfigModal(props: { onClose?: () => void }) { + const syncStore = useSyncStore(); + + return ( +
+ props.onClose?.()} + actions={[ + , + } + bordered + text={Locale.UI.Confirm} + />, + ]} + > + + + + + + + { + syncStore.update( + (config) => (config.useProxy = e.currentTarget.checked), + ); + }} + > + + {syncStore.useProxy ? ( + + { + syncStore.update( + (config) => (config.proxyUrl = e.currentTarget.value), + ); + }} + > + + ) : null} + + + {syncStore.provider === ProviderType.WebDAV && ( + <> + + + { + syncStore.update( + (config) => + (config.webdav.endpoint = e.currentTarget.value), + ); + }} + > + + + + { + syncStore.update( + (config) => + (config.webdav.username = e.currentTarget.value), + ); + }} + > + + + { + syncStore.update( + (config) => + (config.webdav.password = e.currentTarget.value), + ); + }} + > + + + + )} + + {syncStore.provider === ProviderType.UpStash && ( + + + + )} + +
+ ); +} + function SyncItems() { const syncStore = useSyncStore(); - const webdav = syncStore.webDavConfig; const chatStore = useChatStore(); const promptStore = usePromptStore(); const maskStore = useMaskStore(); + const couldSync = useMemo(() => { + return syncStore.coundSync(); + }, [syncStore]); + + const [showSyncConfigModal, setShowSyncConfigModal] = useState(false); const stateOverview = useMemo(() => { const sessions = chatStore.sessions; @@ -267,42 +445,71 @@ function SyncItems() { }, [chatStore.sessions, maskStore.masks, promptStore.prompts]); return ( - - - } - text={Locale.UI.Sync} - onClick={() => { - showToast(Locale.WIP); - }} - /> - + <> + + +
+ } + text={Locale.UI.Config} + onClick={() => { + setShowSyncConfigModal(true); + }} + /> + {couldSync && ( + } + text={Locale.UI.Sync} + onClick={async () => { + try { + await syncStore.sync(); + showToast(Locale.Settings.Sync.Success); + } catch (e) { + showToast(Locale.Settings.Sync.Fail); + console.error("[Sync]", e); + } + }} + /> + )} +
+
- -
- } - text={Locale.UI.Export} - onClick={() => { - syncStore.export(); - }} - /> - } - text={Locale.UI.Import} - onClick={() => { - syncStore.import(); - }} - /> -
-
-
+ +
+ } + text={Locale.UI.Export} + onClick={() => { + syncStore.export(); + }} + /> + } + text={Locale.UI.Import} + onClick={() => { + syncStore.import(); + }} + /> +
+
+
+ + {showSyncConfigModal && ( + setShowSyncConfigModal(false)} /> + )} + ); } diff --git a/app/constant.ts b/app/constant.ts index 2141820ce..f76eb3a97 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -7,7 +7,9 @@ export const RELEASE_URL = `${REPO_URL}/releases`; export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/commits?per_page=1`; export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`; export const RUNTIME_CONFIG_DOM = "danger-runtime-config"; -export const DEFAULT_API_HOST = "https://chatgpt1.nextweb.fun/api/proxy"; + +export const DEFAULT_CORS_HOST = "https://chatgpt2.nextweb.fun"; +export const DEFAULT_API_HOST = `${DEFAULT_CORS_HOST}/api/proxy`; export enum Path { Home = "/", @@ -18,6 +20,10 @@ export enum Path { Auth = "/auth", } +export enum ApiPath { + Cors = "/api/cors", +} + export enum SlotID { AppBody = "app-body", } @@ -46,6 +52,8 @@ export const ACCESS_CODE_PREFIX = "nk-"; export const LAST_INPUT_KEY = "last-input"; export const UNFINISHED_INPUT = (id: string) => "unfinished-input-" + id; +export const STORAGE_KEY = "chatgpt-next-web"; + export const REQUEST_TIMEOUT_MS = 60000; export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown"; diff --git a/app/icons/cloud-fail.svg b/app/icons/cloud-fail.svg new file mode 100644 index 000000000..6e6a35fe5 --- /dev/null +++ b/app/icons/cloud-fail.svg @@ -0,0 +1 @@ + diff --git a/app/icons/cloud-success.svg b/app/icons/cloud-success.svg new file mode 100644 index 000000000..8c5f3d6fd --- /dev/null +++ b/app/icons/cloud-success.svg @@ -0,0 +1 @@ + diff --git a/app/icons/config.svg b/app/icons/config.svg new file mode 100644 index 000000000..7e1d23a27 --- /dev/null +++ b/app/icons/config.svg @@ -0,0 +1 @@ + diff --git a/app/icons/connection.svg b/app/icons/connection.svg new file mode 100644 index 000000000..036873020 --- /dev/null +++ b/app/icons/connection.svg @@ -0,0 +1 @@ + diff --git a/app/locales/cn.ts b/app/locales/cn.ts index a1753417a..1b8850f45 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -179,7 +179,35 @@ const cn = { SubTitle: "根据对话内容生成合适的标题", }, Sync: { - LastUpdate: "上次同步", + CloudState: "云端数据", + NotSyncYet: "还没有进行过同步", + Success: "同步成功", + Fail: "同步失败", + + Config: { + Modal: { + Title: "配置云同步", + }, + SyncType: { + Title: "同步类型", + SubTitle: "选择喜爱的同步服务器", + }, + Proxy: { + Title: "启用代理", + SubTitle: "在浏览器中同步时,必须启用代理以避免跨域限制", + }, + ProxyUrl: { + Title: "代理地址", + SubTitle: "仅适用于本项目自带的跨域代理", + }, + + WebDav: { + Endpoint: "WebDAV 地址", + UserName: "用户名", + Password: "密码", + }, + }, + LocalState: "本地数据", Overview: (overview: any) => { return `${overview.chat} 次对话,${overview.message} 条消息,${overview.prompt} 条提示词,${overview.mask} 个面具`; @@ -366,6 +394,7 @@ const cn = { Export: "导出", Import: "导入", Sync: "同步", + Config: "配置", }, Exporter: { Model: "模型", diff --git a/app/locales/en.ts b/app/locales/en.ts index e31295787..ebbf1a376 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -181,7 +181,36 @@ const en: LocaleType = { SubTitle: "Generate a suitable title based on the conversation content", }, Sync: { - LastUpdate: "Last Update", + CloudState: "Last Update", + NotSyncYet: "Not sync yet", + Success: "Sync Success", + Fail: "Sync Fail", + + Config: { + Modal: { + Title: "Config Sync", + }, + SyncType: { + Title: "Sync Type", + SubTitle: "Choose your favorite sync service", + }, + Proxy: { + Title: "Enable CORS Proxy", + SubTitle: "Enable a proxy to avoid cross-origin restrictions", + }, + ProxyUrl: { + Title: "Proxy Endpoint", + SubTitle: + "Only applicable to the built-in CORS proxy for this project", + }, + + WebDav: { + Endpoint: "WebDAV Endpoint", + UserName: "User Name", + Password: "Password", + }, + }, + LocalState: "Local Data", Overview: (overview: any) => { return `${overview.chat} chats,${overview.message} messages,${overview.prompt} prompts,${overview.mask} masks`; @@ -366,6 +395,7 @@ const en: LocaleType = { Export: "Export", Import: "Import", Sync: "Sync", + Config: "Config", }, Exporter: { Model: "Model", diff --git a/app/store/sync.ts b/app/store/sync.ts index 502cf71cb..29b6a82c2 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -1,15 +1,18 @@ import { Updater } from "../typing"; -import { StoreKey } from "../constant"; +import { ApiPath, StoreKey } from "../constant"; import { createPersistStore } from "../utils/store"; import { AppState, getLocalAppState, + GetStoreState, mergeAppState, setLocalAppState, } from "../utils/sync"; 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; @@ -17,22 +20,43 @@ export interface WebDavConfig { password: string; } +export type SyncStore = GetStoreState; + export const useSyncStore = createPersistStore( { - webDavConfig: { - server: "", + provider: ProviderType.WebDAV, + useProxy: true, + proxyUrl: corsPath(ApiPath.Cors), + + webdav: { + endpoint: "", username: "", password: "", }, + upstash: { + endpoint: "", + username: "", + apiKey: "", + }, + lastSyncTime: 0, + lastProvider: "", }, (set, get) => ({ + coundSync() { + const config = get()[get().provider]; + return Object.values(config).every((c) => c.toString().length > 0); + }, + + markSyncTime() { + set({ lastSyncTime: Date.now(), lastProvider: get().provider }); + }, + export() { const state = getLocalAppState(); const fileName = `Backup-${new Date().toLocaleString()}.json`; downloadAs(JSON.stringify(state), fileName); - set({ lastSyncTime: Date.now() }); }, async import() { @@ -50,47 +74,36 @@ export const useSyncStore = createPersistStore( } }, - async check() { + getClient() { + const provider = get().provider; + const client = createSyncClient(provider, get()); + return client; + }, + + async sync() { + const localState = getLocalAppState(); + const provider = get().provider; + const config = get()[provider]; + const client = this.getClient(); + try { - const res = await fetch(this.path(""), { - method: "PROFIND", - headers: this.headers(), - }); - const sanitizedRes = { - status: res.status, - statusText: res.statusText, - headers: res.headers, - }; - console.log(sanitizedRes); - return res.status === 207; + const remoteState = JSON.parse( + await client.get(config.username), + ) as AppState; + mergeAppState(localState, remoteState); + setLocalAppState(localState); } catch (e) { - console.error("[Sync] ", e); - return false; + console.log("[Sync] failed to get remoate state", e); } + + await client.set(config.username, JSON.stringify(localState)); + + this.markSyncTime(); }, - path(path: string) { - let url = get().webDavConfig.server; - - if (!url.endsWith("/")) { - url += "/"; - } - - if (path.startsWith("/")) { - path = path.slice(1); - } - - return url + path; - }, - - headers() { - const auth = btoa( - [get().webDavConfig.username, get().webDavConfig.password].join(":"), - ); - - return { - Authorization: `Basic ${auth}`, - }; + async check() { + const client = this.getClient(); + return await client.check(); }, }), { diff --git a/app/utils/cloud/index.ts b/app/utils/cloud/index.ts new file mode 100644 index 000000000..63908249e --- /dev/null +++ b/app/utils/cloud/index.ts @@ -0,0 +1,33 @@ +import { createWebDavClient } from "./webdav"; +import { createUpstashClient } from "./upstash"; + +export enum ProviderType { + WebDAV = "webdav", + UpStash = "upstash", +} + +export const SyncClients = { + [ProviderType.UpStash]: createUpstashClient, + [ProviderType.WebDAV]: createWebDavClient, +} as const; + +type SyncClientConfig = { + [K in keyof typeof SyncClients]: (typeof SyncClients)[K] extends ( + _: infer C, + ) => any + ? C + : never; +}; + +export type SyncClient = { + get: (key: string) => Promise; + set: (key: string, value: string) => Promise; + check: () => Promise; +}; + +export function createSyncClient( + provider: T, + config: SyncClientConfig[T], +): SyncClient { + return SyncClients[provider](config as any) as any; +} diff --git a/app/utils/cloud/upstash.ts b/app/utils/cloud/upstash.ts new file mode 100644 index 000000000..6f9b30f6b --- /dev/null +++ b/app/utils/cloud/upstash.ts @@ -0,0 +1,39 @@ +import { SyncStore } from "@/app/store/sync"; + +export type UpstashConfig = SyncStore["upstash"]; +export type UpStashClient = ReturnType; + +export function createUpstashClient(config: UpstashConfig) { + return { + async check() { + return true; + }, + + async get() { + throw Error("[Sync] not implemented"); + }, + + async set() { + throw Error("[Sync] not implemented"); + }, + + headers() { + return { + Authorization: `Basic ${config.apiKey}`, + }; + }, + path(path: string) { + let url = config.endpoint; + + if (!url.endsWith("/")) { + url += "/"; + } + + if (path.startsWith("/")) { + path = path.slice(1); + } + + return url + path; + }, + }; +} diff --git a/app/utils/cloud/webdav.ts b/app/utils/cloud/webdav.ts new file mode 100644 index 000000000..5386b4d19 --- /dev/null +++ b/app/utils/cloud/webdav.ts @@ -0,0 +1,78 @@ +import { STORAGE_KEY } from "@/app/constant"; +import { SyncStore } from "@/app/store/sync"; +import { corsFetch } from "../cors"; + +export type WebDAVConfig = SyncStore["webdav"]; +export type WebDavClient = ReturnType; + +export function createWebDavClient(store: SyncStore) { + const folder = STORAGE_KEY; + const fileName = `${folder}/backup.json`; + const config = store.webdav; + const proxyUrl = + store.useProxy && store.proxyUrl.length > 0 ? store.proxyUrl : undefined; + + return { + async check() { + try { + const res = await corsFetch(this.path(folder), { + method: "MKCOL", + headers: this.headers(), + proxyUrl, + }); + + console.log("[WebDav] check", res.status, res.statusText); + + return [201, 200, 404].includes(res.status); + } catch (e) { + console.error("[WebDav] failed to check", e); + } + + return false; + }, + + async get(key: string) { + const res = await corsFetch(this.path(fileName), { + method: "GET", + headers: this.headers(), + proxyUrl, + }); + + console.log("[WebDav] get key = ", key, res.status, res.statusText); + + return await res.text(); + }, + + async set(key: string, value: string) { + const res = await corsFetch(this.path(fileName), { + method: "PUT", + headers: this.headers(), + body: value, + proxyUrl, + }); + + console.log("[WebDav] set key = ", key, res.status, res.statusText); + }, + + headers() { + const auth = btoa(config.username + ":" + config.password); + + return { + authorization: `Basic ${auth}`, + }; + }, + path(path: string) { + let url = config.endpoint; + + if (!url.endsWith("/")) { + url += "/"; + } + + if (path.startsWith("/")) { + path = path.slice(1); + } + + return url + path; + }, + }; +} diff --git a/app/utils/cors.ts b/app/utils/cors.ts new file mode 100644 index 000000000..773f152aa --- /dev/null +++ b/app/utils/cors.ts @@ -0,0 +1,50 @@ +import { getClientConfig } from "../config/client"; +import { ApiPath, DEFAULT_CORS_HOST } from "../constant"; + +export function corsPath(path: string) { + const baseUrl = getClientConfig()?.isApp ? `${DEFAULT_CORS_HOST}` : ""; + + if (!path.startsWith("/")) { + path = "/" + path; + } + + if (!path.endsWith("/")) { + path += "/"; + } + + return `${baseUrl}${path}`; +} + +export function corsFetch( + url: string, + options: RequestInit & { + proxyUrl?: string; + }, +) { + if (!url.startsWith("http")) { + throw Error("[CORS Fetch] url must starts with http/https"); + } + + let proxyUrl = options.proxyUrl ?? corsPath(ApiPath.Cors); + if (!proxyUrl.endsWith("/")) { + proxyUrl += "/"; + } + + url = url.replace("://", "/"); + + const corsOptions = { + ...options, + method: "POST", + headers: options.method + ? { + ...options.headers, + method: options.method, + } + : options.headers, + }; + + const corsUrl = proxyUrl + url; + console.info("[CORS] target = ", corsUrl); + + return fetch(corsUrl, corsOptions); +} diff --git a/next.config.mjs b/next.config.mjs index c8f17de8c..4faa63e54 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -35,27 +35,29 @@ const nextConfig = { }, }; +const CorsHeaders = [ + { key: "Access-Control-Allow-Credentials", value: "true" }, + { key: "Access-Control-Allow-Origin", value: "*" }, + { + key: "Access-Control-Allow-Methods", + value: "*", + }, + { + key: "Access-Control-Allow-Headers", + value: "*", + }, + { + key: "Access-Control-Max-Age", + value: "86400", + }, +]; + if (mode !== "export") { nextConfig.headers = async () => { return [ { source: "/api/:path*", - headers: [ - { key: "Access-Control-Allow-Credentials", value: "true" }, - { key: "Access-Control-Allow-Origin", value: "*" }, - { - key: "Access-Control-Allow-Methods", - value: "*", - }, - { - key: "Access-Control-Allow-Headers", - value: "*", - }, - { - key: "Access-Control-Max-Age", - value: "86400", - }, - ], + headers: CorsHeaders, }, ]; }; @@ -76,15 +78,6 @@ if (mode !== "export") { }, ]; - const apiUrl = process.env.API_URL; - if (apiUrl) { - console.log("[Next] using api url ", apiUrl); - ret.push({ - source: "/api/:path*", - destination: `${apiUrl}/:path*`, - }); - } - return { beforeFiles: ret, }; From 859cf6930fc3cbe5b1eeb52d8c481a6cd95d63c0 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Wed, 13 Sep 2023 02:51:57 +0800 Subject: [PATCH 053/108] fixup --- 3 | 119 -------------------------------------------------------------- 1 file changed, 119 deletions(-) delete mode 100644 3 diff --git a/3 b/3 deleted file mode 100644 index 371bd01ac..000000000 --- a/3 +++ /dev/null @@ -1,119 +0,0 @@ -export const OWNER = "Yidadaa"; -export const REPO = "ChatGPT-Next-Web"; -export const REPO_URL = `https://github.com/${OWNER}/${REPO}`; -export const ISSUE_URL = `https://github.com/${OWNER}/${REPO}/issues`; -export const UPDATE_URL = `${REPO_URL}#keep-updated`; -export const RELEASE_URL = `${REPO_URL}/releases`; -export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/commits?per_page=1`; -export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`; -export const RUNTIME_CONFIG_DOM = "danger-runtime-config"; -export const DEFAULT_API_HOST = "https://chatgpt1.nextweb.fun/api/proxy"; - -export enum Path { - Home = "/", - Chat = "/chat", - Settings = "/settings", - NewChat = "/new-chat", - Masks = "/masks", - Auth = "/auth", -} - -export enum SlotID { - AppBody = "app-body", -} - -export enum FileName { - Masks = "masks.json", - Prompts = "prompts.json", -} - -export enum StoreKey { - Chat = "chat-next-web-store", - Access = "access-control", - Config = "app-config", - Mask = "mask-store", - Prompt = "prompt-store", - Update = "chat-update", - Sync = "sync", -} - -export const MAX_SIDEBAR_WIDTH = 500; -export const MIN_SIDEBAR_WIDTH = 230; -export const NARROW_SIDEBAR_WIDTH = 100; - -export const ACCESS_CODE_PREFIX = "nk-"; - -export const LAST_INPUT_KEY = "last-input"; -export const UNFINISHED_INPUT = (id: string) => "unfinished-input-" + id; - -export const STORAGE_KEY = "chatgpt-next-web"; - -export const REQUEST_TIMEOUT_MS = 60000; - -export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown"; - -export const OpenaiPath = { - ChatPath: "v1/chat/completions", - UsagePath: "dashboard/billing/usage", - SubsPath: "dashboard/billing/subscription", - ListModelPath: "v1/models", -}; - -export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang -export const DEFAULT_SYSTEM_TEMPLATE = ` -You are ChatGPT, a large language model trained by OpenAI. -Knowledge cutoff: 2021-09 -Current model: {{model}} -Current time: {{time}}`; - -export const SUMMARIZE_MODEL = "gpt-3.5-turbo"; - -export const DEFAULT_MODELS = [ - { - name: "gpt-4", - available: true, - }, - { - name: "gpt-4-0314", - available: true, - }, - { - name: "gpt-4-0613", - available: true, - }, - { - name: "gpt-4-32k", - available: true, - }, - { - name: "gpt-4-32k-0314", - available: true, - }, - { - name: "gpt-4-32k-0613", - available: true, - }, - { - name: "gpt-3.5-turbo", - available: true, - }, - { - name: "gpt-3.5-turbo-0301", - available: true, - }, - { - name: "gpt-3.5-turbo-0613", - available: true, - }, - { - name: "gpt-3.5-turbo-16k", - available: true, - }, - { - name: "gpt-3.5-turbo-16k-0613", - available: true, - }, -] as const; - -export const CHAT_PAGE_SIZE = 15; -export const MAX_RENDER_MSG_COUNT = 45; From dc555b2206ae84ce2598774398f49d967357d37d Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Wed, 13 Sep 2023 02:52:28 +0800 Subject: [PATCH 054/108] fixup --- app/api/cors/[...path]/route.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/api/cors/[...path]/route.ts b/app/api/cors/[...path]/route.ts index c461d250b..90404cf89 100644 --- a/app/api/cors/[...path]/route.ts +++ b/app/api/cors/[...path]/route.ts @@ -33,12 +33,6 @@ async function handle( return fetchResult; } -export const GET = handle; export const POST = handle; -export const PUT = handle; - -// nextjs dose not support those https methods, sucks -export const PROFIND = handle; -export const MKCOL = handle; export const runtime = "edge"; From b589f48aa99e1bc3b5544b4fc81cab27385c699e Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Wed, 13 Sep 2023 03:01:28 +0800 Subject: [PATCH 055/108] Update tauri.conf.json --- 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 2256d5b34..d8b677bf6 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "ChatGPT Next Web", - "version": "2.9.5" + "version": "2.9.6" }, "tauri": { "allowlist": { From 368701610f039241eeb0fda27db28803b607527e Mon Sep 17 00:00:00 2001 From: yhua1998 <101091026+yhua1998@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:57:30 +0800 Subject: [PATCH 056/108] fix: Width changes abruptly when dragging the sidebar (jumps) In useRef is non-responsive, we can't get the modified config.sidebarWidth dynamic modification value in handleMouseUp --- app/components/sidebar.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 634639f1d..c42138efc 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useCallback } from "react"; import styles from "./home.module.scss"; @@ -53,7 +53,7 @@ function useHotKey() { } function useDragSideBar() { - const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x); + const limit = useCallback((x: number) => Math.min(MAX_SIDEBAR_WIDTH, x)); const config = useAppConfig(); const startX = useRef(0); @@ -71,14 +71,16 @@ function useDragSideBar() { }); const handleMouseUp = useRef(() => { - startDragWidth.current = config.sidebarWidth ?? 300; + // In useRef the data is non-responsive, so `config.sidebarWidth` can't get the dynamic sidebarWidth + // startDragWidth.current = config.sidebarWidth ?? 300; window.removeEventListener("mousemove", handleMouseMove.current); window.removeEventListener("mouseup", handleMouseUp.current); }); const onDragMouseDown = (e: MouseEvent) => { startX.current = e.clientX; - + // Remembers the initial width each time the mouse is pressed + startDragWidth.current = config.sidebarWidth window.addEventListener("mousemove", handleMouseMove.current); window.addEventListener("mouseup", handleMouseUp.current); }; From 28103c901dba611a531117fca2589cbc73f43b55 Mon Sep 17 00:00:00 2001 From: yhua1998 <101091026+yhua1998@users.noreply.github.com> Date: Wed, 13 Sep 2023 16:24:21 +0800 Subject: [PATCH 057/108] Refactor: sidebar drag --- app/components/sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index c42138efc..db13bc9b4 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -53,7 +53,7 @@ function useHotKey() { } function useDragSideBar() { - const limit = useCallback((x: number) => Math.min(MAX_SIDEBAR_WIDTH, x)); + const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x); const config = useAppConfig(); const startX = useRef(0); From 48e6087b1be1562c50de3b4aa648445df5510539 Mon Sep 17 00:00:00 2001 From: yhua1998 <385362330@qq.com> Date: Wed, 13 Sep 2023 16:55:31 +0800 Subject: [PATCH 058/108] fix: The width of the sidebar changes abruptly by dragging it multiple times over and over again (bouncing) --- app/components/sidebar.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index db13bc9b4..4519c4be9 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -67,7 +67,13 @@ function useDragSideBar() { lastUpdateTime.current = Date.now(); const d = e.clientX - startX.current; const nextWidth = limit(startDragWidth.current + d); - config.update((config) => (config.sidebarWidth = nextWidth)); + config.update((config) => { + if (nextWidth < MIN_SIDEBAR_WIDTH) { + config.sidebarWidth = NARROW_SIDEBAR_WIDTH; + } else { + config.sidebarWidth = nextWidth; + } + }); }); const handleMouseUp = useRef(() => { @@ -80,7 +86,7 @@ function useDragSideBar() { const onDragMouseDown = (e: MouseEvent) => { startX.current = e.clientX; // Remembers the initial width each time the mouse is pressed - startDragWidth.current = config.sidebarWidth + startDragWidth.current = config.sidebarWidth; window.addEventListener("mousemove", handleMouseMove.current); window.addEventListener("mouseup", handleMouseUp.current); }; From 4f3261b262209cf439b5220e02ed319314efb2ac Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Wed, 13 Sep 2023 20:05:58 +0700 Subject: [PATCH 059/108] [+] Some improvements to the Indonesian language. --- app/locales/id.ts | 225 ++++++++++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 95 deletions(-) diff --git a/app/locales/id.ts b/app/locales/id.ts index c3a2a5f88..6ed0e7210 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -60,7 +60,9 @@ const id: PartialLocaleType = { if (submitKey === String(SubmitKey.Enter)) { inputHints += ", Shift + Enter untuk membalut"; } - return inputHints + ", / untuk mencari prompt, : untuk menggunakan perintah"; + return ( + inputHints + ", / untuk mencari prompt, : untuk menggunakan perintah" + ); }, Send: "Kirim", Config: { @@ -114,36 +116,37 @@ const id: PartialLocaleType = { SubTitle: "Semua Pengaturan", Danger: { Reset: { - Title: "Setel Ulang Semua Pengaturan", - SubTitle: "Mengembalikan semua pengaturan ke nilai default", - Action: "Setel Ulang", - Confirm: "Anda yakin ingin mengembalikan semua pengaturan ke nilai default?", + Title: "Reset Semua Pengaturan", + SubTitle: "Reset semua item pengaturan ke nilai default", + Action: "Reset", + Confirm: "Konfirmasi untuk mereset semua pengaturan ke nilai default?", }, Clear: { Title: "Hapus Semua Data", - SubTitle: "Menghapus semua pesan dan pengaturan", + SubTitle: "Semua data yang tersimpan secara lokal akan dihapus", Action: "Hapus", - Confirm: "Anda yakin ingin menghapus semua pesan dan pengaturan?", + Confirm: + "Apakah Anda yakin ingin menghapus semua data yang tersimpan secara lokal?", }, }, Lang: { - Name: "Bahasa", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language` - All: "Semua Bahasa", - }, - Avatar: "Avatar", - FontSize: { - Title: "Ukuran Font", - SubTitle: "Ubah ukuran font konten chat", - }, - InjectSystemPrompts: { - Title: "Suntikkan Petunjuk Sistem", - SubTitle: - "Tambahkan petunjuk simulasi sistem ChatGPT di awal daftar pesan yang diminta dalam setiap permintaan", - }, - InputTemplate: { - Title: "Template Input", - SubTitle: "Pesan baru akan diisi menggunakan template ini", - }, + Name: "Bahasa", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language` + All: "Semua Bahasa", + }, + Avatar: "Avatar", + FontSize: { + Title: "Ukuran Font", + SubTitle: "Ubah ukuran font konten chat", + }, + InjectSystemPrompts: { + Title: "Suntikkan Petunjuk Sistem", + SubTitle: + "Tambahkan petunjuk simulasi sistem ChatGPT di awal daftar pesan yang diminta dalam setiap permintaan", + }, + InputTemplate: { + Title: "Template Input", + SubTitle: "Pesan baru akan diisi menggunakan template ini", + }, Update: { Version: (x: string) => `Version: ${x}`, @@ -154,9 +157,39 @@ const id: PartialLocaleType = { GoToUpdate: "Perbarui Sekarang", }, AutoGenerateTitle: { - Title: "Hasilkan Judul Otomatis", - SubTitle: "Hasilkan judul yang sesuai berdasarkan konten percakapan", + Title: "Hasilkan Judul Otomatis", + SubTitle: "Hasilkan judul yang sesuai berdasarkan konten percakapan", + }, + Sync: { + CloudState: "Pembaruan Terakhir", + NotSyncYet: "Belum disinkronkan", + Success: "Sinkronisasi Berhasil", + Fail: "Sinkronisasi Gagal", + + Config: { + Modal: { + Title: "Konfigurasi Sinkronisasi", + }, + SyncType: { + Title: "Tipe Sinkronisasi", + SubTitle: "Pilih layanan sinkronisasi favorit Anda", + }, + Proxy: { + Title: "Aktifkan Proxy CORS", + SubTitle: "Aktifkan Proxy untuk menghindari pembatasan lintas sumber", + }, + ProxyUrl: { + Title: "Titik Akhir Proxy", + SubTitle: "Hanya berlaku untuk Proxy CORS bawaan untuk proyek ini", + }, + + WebDav: { + Endpoint: "Titik Akhir WebDAV", + UserName: "Nama Pengguna", + Password: "Kata Sandi", + }, }, + }, SendKey: "Kirim", Theme: "Tema", TightBorder: "Batas Ketat", @@ -176,76 +209,77 @@ const id: PartialLocaleType = { }, }, Prompt: { - Disable: { - Title: "Nonaktifkan Otomatisasi", - SubTitle: "Aktifkan/Matikan otomatisasi", - }, - List: "Daftar Prompt", - ListCount: (builtin: number, custom: number) => - `${builtin} bawaan, ${custom} penggunaan khusus`, - Edit: "Edit", - Modal: { - Title: "Daftar Prompt", - Add: "Tambahkan", - Search: "Cari Prompt", - }, - EditModal: { - Title: "Edit Prompt", - }, + Disable: { + Title: "Nonaktifkan Otomatisasi", + SubTitle: "Aktifkan/Matikan otomatisasi", }, - HistoryCount: { - Title: "Jumlah Pesan Riwayat", - SubTitle: "Jumlah pesan yang akan dikirim setiap permintaan", + List: "Daftar Prompt", + ListCount: (builtin: number, custom: number) => + `${builtin} bawaan, ${custom} penggunaan khusus`, + Edit: "Edit", + Modal: { + Title: "Daftar Prompt", + Add: "Tambahkan", + Search: "Cari Prompt", }, - CompressThreshold: { - Title: "Batas Kompresi Riwayat", - SubTitle: - "Jika panjang pesan melebihi batas yang ditentukan, pesan tersebut akan dikompresi", - }, - Token: { - Title: "Kunci API", - SubTitle: "Gunakan kunci Anda untuk melewati batas kode akses", - Placeholder: "Kunci API OpenAI", + EditModal: { + Title: "Edit Prompt", }, - Usage: { - Title: "Saldo Akun", - SubTitle(used: any, total: any) { - return `Digunakan bulan ini: ${used}, total langganan: ${total}`; - }, - IsChecking: "Memeriksa...", - Check: "Periksa", - NoAccess: "Masukkan kunci API untuk memeriksa saldo", + }, + HistoryCount: { + Title: "Jumlah Pesan Riwayat", + SubTitle: "Jumlah pesan yang akan dikirim setiap permintaan", + }, + CompressThreshold: { + Title: "Batas Kompresi Riwayat", + SubTitle: + "Jika panjang pesan melebihi batas yang ditentukan, pesan tersebut akan dikompresi", + }, + Token: { + Title: "Kunci API", + SubTitle: "Gunakan kunci Anda untuk melewati batas kode akses", + Placeholder: "Kunci API OpenAI", + }, + Usage: { + Title: "Saldo Akun", + SubTitle(used: any, total: any) { + return `Digunakan bulan ini: ${used}, total langganan: ${total}`; }, - AccessCode: { - Title: "Kode Akses", - SubTitle: "Kontrol akses diaktifkan", - Placeholder: "Diperlukan kode akses", - }, - Endpoint: { - Title: "Endpoint", - SubTitle: "Harus dimulai dengan http(s):// untuk endpoint kustom", - }, - Model: "Model", - Temperature: { - Title: "Suhu", - SubTitle: "Semakin tinggi nilainya, semakin acak keluarannya", - }, - TopP: { - Title: "Top P", - SubTitle: "Tidak mengubah nilai dengan suhu", - }, - MaxTokens: { - Title: "Token Maksimum", - SubTitle: "Panjang maksimum token input dan output", - }, - PresencePenalty: { - Title: "Penalti Kehadiran", - SubTitle: "Semakin tinggi nilai, semakin mungkin topik baru muncul", - }, - FrequencyPenalty: { - Title: "Penalti Frekuensi", - SubTitle: "Semakin tinggi nilai, semakin rendah kemungkinan penggunaan ulang baris yang sama", - }, + IsChecking: "Memeriksa...", + Check: "Periksa", + NoAccess: "Masukkan kunci API untuk memeriksa saldo", + }, + AccessCode: { + Title: "Kode Akses", + SubTitle: "Kontrol akses diaktifkan", + Placeholder: "Diperlukan kode akses", + }, + Endpoint: { + Title: "Endpoint", + SubTitle: "Harus dimulai dengan http(s):// untuk endpoint kustom", + }, + Model: "Model", + Temperature: { + Title: "Suhu", + SubTitle: "Semakin tinggi nilainya, semakin acak keluarannya", + }, + TopP: { + Title: "Top P", + SubTitle: "Tidak mengubah nilai dengan suhu", + }, + MaxTokens: { + Title: "Token Maksimum", + SubTitle: "Panjang maksimum token input dan output", + }, + PresencePenalty: { + Title: "Penalti Kehadiran", + SubTitle: "Semakin tinggi nilai, semakin mungkin topik baru muncul", + }, + FrequencyPenalty: { + Title: "Penalti Frekuensi", + SubTitle: + "Semakin tinggi nilai, semakin rendah kemungkinan penggunaan ulang baris yang sama", + }, }, Store: { DefaultTopic: "Percakapan Baru", @@ -261,8 +295,9 @@ const id: PartialLocaleType = { }, }, Copy: { - Success: "Berhasil disalin ke clipboard", - Failed: "Gagal menyalin, berikan izin untuk memberikan izin", + Success: "Tersalin ke clipboard", + Failed: + "Gagal menyalin, mohon berikan izin untuk mengakses clipboard atau Clipboard API tidak didukung (Tauri)", }, Context: { Toast: (x: any) => `Dengan ${x} promp kontekstual`, @@ -341,7 +376,7 @@ const id: PartialLocaleType = { Model: "Model", Messages: "Pesan", Topic: "Topik", - Time: "Waktu", + Time: "Tanggal & Waktu", }, URLCommand: { Code: "Kode akses terdeteksi dari url, konfirmasi untuk mendaftar ? ", From 5a7bdcfe59a812611b8bf865408ce86f22103b64 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Wed, 13 Sep 2023 20:17:30 +0700 Subject: [PATCH 060/108] [+] Some improvements to the Indonesian language. --- app/locales/id.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/locales/id.ts b/app/locales/id.ts index 6ed0e7210..5068500d1 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -116,10 +116,11 @@ const id: PartialLocaleType = { SubTitle: "Semua Pengaturan", Danger: { Reset: { - Title: "Reset Semua Pengaturan", - SubTitle: "Reset semua item pengaturan ke nilai default", - Action: "Reset", - Confirm: "Konfirmasi untuk mereset semua pengaturan ke nilai default?", + Title: "Setel Ulang Semua Pengaturan", + SubTitle: "Mengembalikan semua pengaturan ke nilai default", + Action: "Setel Ulang", + Confirm: + "Anda yakin ingin mengembalikan semua pengaturan ke nilai default?", }, Clear: { Title: "Hapus Semua Data", @@ -176,15 +177,16 @@ const id: PartialLocaleType = { }, Proxy: { Title: "Aktifkan Proxy CORS", - SubTitle: "Aktifkan Proxy untuk menghindari pembatasan lintas sumber", + SubTitle: + "Aktifkan Proxy untuk menghindari pembatasan atau pemblokiran lintas sumber", }, ProxyUrl: { - Title: "Titik Akhir Proxy", + Title: "Lokasi Titik Akhir Proxy CORS", SubTitle: "Hanya berlaku untuk Proxy CORS bawaan untuk proyek ini", }, WebDav: { - Endpoint: "Titik Akhir WebDAV", + Endpoint: "Lokasi Titik Akhir WebDAV", UserName: "Nama Pengguna", Password: "Kata Sandi", }, From 261bf0b29800485a61aa8194bb1da8254361b9d3 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Wed, 13 Sep 2023 20:49:22 +0700 Subject: [PATCH 061/108] Changes "Nama" -> "User" --- app/locales/id.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/locales/id.ts b/app/locales/id.ts index 5068500d1..244c5ade1 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -187,7 +187,7 @@ const id: PartialLocaleType = { WebDav: { Endpoint: "Lokasi Titik Akhir WebDAV", - UserName: "Nama Pengguna", + UserName: "User Pengguna", Password: "Kata Sandi", }, }, From 6535986484abe66c8f989c811e4e815d2c8e0728 Mon Sep 17 00:00:00 2001 From: Algorithm5838 <108630393+Algorithm5838@users.noreply.github.com> Date: Fri, 15 Sep 2023 06:06:34 +0300 Subject: [PATCH 062/108] Update markdown.tsx --- app/components/markdown.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index e7a35b802..1a1fbf416 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -151,6 +151,7 @@ export function Markdown( ref={mdRef} onContextMenu={props.onContextMenu} onDoubleClickCapture={props.onDoubleClickCapture} + dir="auto" > {props.loading ? ( From bd69116df2ccaf70e6948514f381cc8375fd507e Mon Sep 17 00:00:00 2001 From: Amor Zara <132665015+a6z6@users.noreply.github.com> Date: Fri, 15 Sep 2023 11:21:42 +0800 Subject: [PATCH 063/108] Update route.ts Correct typo error and make warning more specific. --- app/api/config/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/config/route.ts b/app/api/config/route.ts index 7749e6e9e..0bfc955bf 100644 --- a/app/api/config/route.ts +++ b/app/api/config/route.ts @@ -4,7 +4,7 @@ import { getServerSideConfig } from "../../config/server"; const serverConfig = getServerSideConfig(); -// Danger! Don not write any secret value here! +// Danger! Do not hard code any secret value here! // 警告!不要在这里写入任何敏感信息! const DANGER_CONFIG = { needCode: serverConfig.needCode, From 2c92f75c861c07d8d787cca4b81c7445ba72ce27 Mon Sep 17 00:00:00 2001 From: Amor Zara <132665015+a6z6@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:32:42 +0800 Subject: [PATCH 064/108] Update .env.template --- .env.template | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.env.template b/.env.template index 0f4bf0e7c..1ff575f11 100644 --- a/.env.template +++ b/.env.template @@ -15,7 +15,6 @@ BASE_URL= # Specify OpenAI organization ID.(optional) # Default: Empty -# If you do not want users to input their own API key, set this value to 1. OPENAI_ORG_ID= # (optional) @@ -31,4 +30,4 @@ DISABLE_GPT4= # (optional) # Default: Empty # If you do not want users to query balance, set this value to 1. -HIDE_BALANCE_QUERY= \ No newline at end of file +HIDE_BALANCE_QUERY= From c900459f73602a2589bdc6ca4ca2b93e2cd8c508 Mon Sep 17 00:00:00 2001 From: Gerhard Tan Date: Mon, 18 Sep 2023 09:37:19 +0800 Subject: [PATCH 065/108] Encode google font url --- app/components/home.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/home.tsx b/app/components/home.tsx index 745298d56..285ca0f58 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -115,7 +115,7 @@ const loadAsyncGoogleFont = () => { getClientConfig()?.buildMode === "export" ? remoteFontUrl : proxyFontUrl; linkEl.rel = "stylesheet"; linkEl.href = - googleFontUrl + "/css2?family=Noto+Sans:wght@300;400;700;900&display=swap"; + googleFontUrl + "/css2?family=" + encodeURIComponent("Noto Sans:wght@300;400;700;900") + "&display=swap"; document.head.appendChild(linkEl); }; From d713d016000e09f245fc9496bd9864293aaa95c0 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 19 Sep 2023 01:47:15 +0800 Subject: [PATCH 066/108] feat: close #2848 click drag icon to toggle sidebar width --- app/components/sidebar.tsx | 79 +++++++++++++++++++++++--------------- app/constant.ts | 1 + app/store/config.ts | 9 ++++- 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 4519c4be9..3ca167896 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -17,6 +17,7 @@ import Locale from "../locales"; import { useAppConfig, useChatStore } from "../store"; import { + DEFAULT_SIDEBAR_WIDTH, MAX_SIDEBAR_WIDTH, MIN_SIDEBAR_WIDTH, NARROW_SIDEBAR_WIDTH, @@ -57,39 +58,57 @@ function useDragSideBar() { const config = useAppConfig(); const startX = useRef(0); - const startDragWidth = useRef(config.sidebarWidth ?? 300); + const startDragWidth = useRef(config.sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH); const lastUpdateTime = useRef(Date.now()); - const handleMouseMove = useRef((e: MouseEvent) => { - if (Date.now() < lastUpdateTime.current + 50) { - return; - } - lastUpdateTime.current = Date.now(); - const d = e.clientX - startX.current; - const nextWidth = limit(startDragWidth.current + d); + const toggleSideBar = () => { config.update((config) => { - if (nextWidth < MIN_SIDEBAR_WIDTH) { - config.sidebarWidth = NARROW_SIDEBAR_WIDTH; + if (config.sidebarWidth < MIN_SIDEBAR_WIDTH) { + config.sidebarWidth = DEFAULT_SIDEBAR_WIDTH; } else { - config.sidebarWidth = nextWidth; + config.sidebarWidth = NARROW_SIDEBAR_WIDTH; } }); - }); - - const handleMouseUp = useRef(() => { - // In useRef the data is non-responsive, so `config.sidebarWidth` can't get the dynamic sidebarWidth - // startDragWidth.current = config.sidebarWidth ?? 300; - window.removeEventListener("mousemove", handleMouseMove.current); - window.removeEventListener("mouseup", handleMouseUp.current); - }); - - const onDragMouseDown = (e: MouseEvent) => { - startX.current = e.clientX; - // Remembers the initial width each time the mouse is pressed - startDragWidth.current = config.sidebarWidth; - window.addEventListener("mousemove", handleMouseMove.current); - window.addEventListener("mouseup", handleMouseUp.current); }; + + const onDragStart = (e: MouseEvent) => { + // Remembers the initial width each time the mouse is pressed + startX.current = e.clientX; + startDragWidth.current = config.sidebarWidth; + const dragStartTime = Date.now(); + + const handleDragMove = (e: MouseEvent) => { + if (Date.now() < lastUpdateTime.current + 20) { + return; + } + lastUpdateTime.current = Date.now(); + const d = e.clientX - startX.current; + const nextWidth = limit(startDragWidth.current + d); + config.update((config) => { + if (nextWidth < MIN_SIDEBAR_WIDTH) { + config.sidebarWidth = NARROW_SIDEBAR_WIDTH; + } else { + config.sidebarWidth = nextWidth; + } + }); + }; + + const handleDragEnd = () => { + // In useRef the data is non-responsive, so `config.sidebarWidth` can't get the dynamic sidebarWidth + window.removeEventListener("pointermove", handleDragMove); + window.removeEventListener("pointerup", handleDragEnd); + + // if user click the drag icon, should toggle the sidebar + const shouldFireClick = Date.now() - dragStartTime < 300; + if (shouldFireClick) { + toggleSideBar(); + } + }; + + window.addEventListener("pointermove", handleDragMove); + window.addEventListener("pointerup", handleDragEnd); + }; + const isMobileScreen = useMobileScreen(); const shouldNarrow = !isMobileScreen && config.sidebarWidth < MIN_SIDEBAR_WIDTH; @@ -97,13 +116,13 @@ function useDragSideBar() { useEffect(() => { const barWidth = shouldNarrow ? NARROW_SIDEBAR_WIDTH - : limit(config.sidebarWidth ?? 300); + : limit(config.sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH); const sideBarWidth = isMobileScreen ? "100vw" : `${barWidth}px`; document.documentElement.style.setProperty("--sidebar-width", sideBarWidth); }, [config.sidebarWidth, isMobileScreen, shouldNarrow]); return { - onDragMouseDown, + onDragStart, shouldNarrow, }; } @@ -112,7 +131,7 @@ export function SideBar(props: { className?: string }) { const chatStore = useChatStore(); // drag side bar - const { onDragMouseDown, shouldNarrow } = useDragSideBar(); + const { onDragStart, shouldNarrow } = useDragSideBar(); const navigate = useNavigate(); const config = useAppConfig(); @@ -206,7 +225,7 @@ export function SideBar(props: { className?: string }) {
onDragMouseDown(e as any)} + onPointerDown={(e) => onDragStart(e as any)} >
diff --git a/app/constant.ts b/app/constant.ts index f76eb3a97..c6cba3ef0 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -43,6 +43,7 @@ export enum StoreKey { Sync = "sync", } +export const DEFAULT_SIDEBAR_WIDTH = 300; export const MAX_SIDEBAR_WIDTH = 500; export const MIN_SIDEBAR_WIDTH = 230; export const NARROW_SIDEBAR_WIDTH = 100; diff --git a/app/store/config.ts b/app/store/config.ts index b01319542..956e5f3eb 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -1,6 +1,11 @@ import { LLMModel } from "../client/api"; import { getClientConfig } from "../config/client"; -import { DEFAULT_INPUT_TEMPLATE, DEFAULT_MODELS, StoreKey } from "../constant"; +import { + DEFAULT_INPUT_TEMPLATE, + DEFAULT_MODELS, + DEFAULT_SIDEBAR_WIDTH, + StoreKey, +} from "../constant"; import { createPersistStore } from "../utils/store"; export type ModelType = (typeof DEFAULT_MODELS)[number]["name"]; @@ -29,7 +34,7 @@ export const DEFAULT_CONFIG = { tightBorder: !!getClientConfig()?.isApp, sendPreviewBubble: true, enableAutoGenerateTitle: true, - sidebarWidth: 300, + sidebarWidth: DEFAULT_SIDEBAR_WIDTH, disablePromptHint: false, From 61ca60c550295c75e3e3feb8061455d298c27501 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 19 Sep 2023 01:58:52 +0800 Subject: [PATCH 067/108] fix: #2817 min-height for landscape orientation on mobile phone --- app/components/home.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/home.module.scss b/app/components/home.module.scss index 77f1c8538..b836d2bec 100644 --- a/app/components/home.module.scss +++ b/app/components/home.module.scss @@ -6,7 +6,7 @@ color: var(--black); background-color: var(--white); min-width: 600px; - min-height: 480px; + min-height: 370px; max-width: 1200px; display: flex; From adb860b4646c0c7548a059c5a8e8b3349ebdeca8 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 19 Sep 2023 02:12:43 +0800 Subject: [PATCH 068/108] fix: #2820 try to fix 520 error code --- app/api/cors/[...path]/route.ts | 9 +++++++-- app/components/settings.tsx | 2 +- app/locales/cn.ts | 1 + app/locales/en.ts | 1 + app/utils/cloud/webdav.ts | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/api/cors/[...path]/route.ts b/app/api/cors/[...path]/route.ts index 90404cf89..1f70d6630 100644 --- a/app/api/cors/[...path]/route.ts +++ b/app/api/cors/[...path]/route.ts @@ -26,13 +26,18 @@ async function handle( duplex: "half", }; - console.log("[Any Proxy]", targetUrl); + const fetchResult = await fetch(targetUrl, fetchOptions); - const fetchResult = fetch(targetUrl, fetchOptions); + console.log("[Any Proxy]", targetUrl, { + status: fetchResult.status, + statusText: fetchResult.statusText, + }); return fetchResult; } export const POST = handle; +export const GET = handle; +export const OPTIONS = handle; export const runtime = "edge"; diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 8e43e1d1a..fb1d688f0 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -275,7 +275,7 @@ function CheckButton() { return ( Date: Tue, 19 Sep 2023 02:21:31 +0800 Subject: [PATCH 069/108] fixup --- app/api/cors/[...path]/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/cors/[...path]/route.ts b/app/api/cors/[...path]/route.ts index 1f70d6630..0217b12b0 100644 --- a/app/api/cors/[...path]/route.ts +++ b/app/api/cors/[...path]/route.ts @@ -40,4 +40,4 @@ export const POST = handle; export const GET = handle; export const OPTIONS = handle; -export const runtime = "edge"; +export const runtime = "nodejs"; From 83fed429971fcc758ada9af12d52a2936b537456 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 19 Sep 2023 03:18:34 +0800 Subject: [PATCH 070/108] feat: add upstash redis cloud sync --- app/components/settings.tsx | 39 ++++++++++++++++++- app/locales/cn.ts | 6 +++ app/locales/en.ts | 6 +++ app/store/sync.ts | 4 +- app/utils/cloud/upstash.ts | 74 ++++++++++++++++++++++++++++++++++--- app/utils/cloud/webdav.ts | 2 - app/utils/format.ts | 15 ++++++++ app/utils/sync.ts | 3 ++ 8 files changed, 137 insertions(+), 12 deletions(-) diff --git a/app/components/settings.tsx b/app/components/settings.tsx index fb1d688f0..8ed6b7738 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -50,7 +50,7 @@ import Locale, { } from "../locales"; import { copyToClipboard } from "../utils"; import Link from "next/link"; -import { Path, RELEASE_URL, UPDATE_URL } from "../constant"; +import { Path, RELEASE_URL, STORAGE_KEY, UPDATE_URL } from "../constant"; import { Prompt, SearchService, usePromptStore } from "../store/prompt"; import { ErrorBoundary } from "./error"; import { InputRange } from "./input-range"; @@ -413,7 +413,42 @@ function SyncConfigModal(props: { onClose?: () => void }) { {syncStore.provider === ProviderType.UpStash && ( - + + { + syncStore.update( + (config) => + (config.upstash.endpoint = e.currentTarget.value), + ); + }} + > + + + + { + syncStore.update( + (config) => + (config.upstash.username = e.currentTarget.value), + ); + }} + > + + + { + syncStore.update( + (config) => (config.upstash.apiKey = e.currentTarget.value), + ); + }} + > + )} diff --git a/app/locales/cn.ts b/app/locales/cn.ts index ac4a1777f..b2afc7534 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -207,6 +207,12 @@ const cn = { UserName: "用户名", Password: "密码", }, + + UpStash: { + Endpoint: "UpStash Redis REST Url", + UserName: "备份名称", + Password: "UpStash Redis REST Token", + }, }, LocalState: "本地数据", diff --git a/app/locales/en.ts b/app/locales/en.ts index 3f3fa7ce9..697d09d1f 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -210,6 +210,12 @@ const en: LocaleType = { UserName: "User Name", Password: "Password", }, + + UpStash: { + Endpoint: "UpStash Redis REST Url", + UserName: "Backup Name", + Password: "UpStash Redis REST Token", + }, }, LocalState: "Local Data", diff --git a/app/store/sync.ts b/app/store/sync.ts index 29b6a82c2..ff9f650c0 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -1,5 +1,5 @@ import { Updater } from "../typing"; -import { ApiPath, StoreKey } from "../constant"; +import { ApiPath, STORAGE_KEY, StoreKey } from "../constant"; import { createPersistStore } from "../utils/store"; import { AppState, @@ -36,7 +36,7 @@ export const useSyncStore = createPersistStore( upstash: { endpoint: "", - username: "", + username: STORAGE_KEY, apiKey: "", }, diff --git a/app/utils/cloud/upstash.ts b/app/utils/cloud/upstash.ts index 6f9b30f6b..5f5b9fc79 100644 --- a/app/utils/cloud/upstash.ts +++ b/app/utils/cloud/upstash.ts @@ -1,25 +1,87 @@ +import { STORAGE_KEY } from "@/app/constant"; import { SyncStore } from "@/app/store/sync"; +import { corsFetch } from "../cors"; +import { chunks } from "../format"; export type UpstashConfig = SyncStore["upstash"]; export type UpStashClient = ReturnType; -export function createUpstashClient(config: UpstashConfig) { +export function createUpstashClient(store: SyncStore) { + const config = store.upstash; + const storeKey = config.username.length === 0 ? STORAGE_KEY : config.username; + const chunkCountKey = `${storeKey}-chunk-count`; + const chunkIndexKey = (i: number) => `${storeKey}-chunk-${i}`; + + const proxyUrl = + store.useProxy && store.proxyUrl.length > 0 ? store.proxyUrl : undefined; + return { async check() { - return true; + try { + const res = await corsFetch(this.path(`get/${storeKey}`), { + method: "GET", + headers: this.headers(), + proxyUrl, + }); + console.log("[Upstash] check", res.status, res.statusText); + return [200].includes(res.status); + } catch (e) { + console.error("[Upstash] failed to check", e); + } + return false; + }, + + async redisGet(key: string) { + const res = await corsFetch(this.path(`get/${key}`), { + method: "GET", + headers: this.headers(), + proxyUrl, + }); + + console.log("[Upstash] get key = ", key, res.status, res.statusText); + const resJson = (await res.json()) as { result: string }; + + return resJson.result; + }, + + async redisSet(key: string, value: string) { + const res = await corsFetch(this.path(`set/${key}`), { + method: "POST", + headers: this.headers(), + body: value, + proxyUrl, + }); + + console.log("[Upstash] set key = ", key, res.status, res.statusText); }, async get() { - throw Error("[Sync] not implemented"); + const chunkCount = Number(await this.redisGet(chunkCountKey)); + if (!Number.isInteger(chunkCount)) return; + + const chunks = await Promise.all( + new Array(chunkCount) + .fill(0) + .map((_, i) => this.redisGet(chunkIndexKey(i))), + ); + console.log("[Upstash] get full chunks", chunks); + return chunks.join(""); }, - async set() { - throw Error("[Sync] not implemented"); + async set(_: string, value: string) { + // upstash limit the max request size which is 1Mb for “Free” and “Pay as you go” + // so we need to split the data to chunks + let index = 0; + for await (const chunk of chunks(value)) { + await this.redisSet(chunkIndexKey(index), chunk); + index += 1; + } + await this.redisSet(chunkCountKey, index.toString()); }, headers() { return { - Authorization: `Basic ${config.apiKey}`, + Authorization: `Bearer ${config.apiKey}`, }; }, path(path: string) { diff --git a/app/utils/cloud/webdav.ts b/app/utils/cloud/webdav.ts index 6c96c9062..c87fdd71e 100644 --- a/app/utils/cloud/webdav.ts +++ b/app/utils/cloud/webdav.ts @@ -20,9 +20,7 @@ export function createWebDavClient(store: SyncStore) { headers: this.headers(), proxyUrl, }); - console.log("[WebDav] check", res.status, res.statusText); - return [201, 200, 404, 401].includes(res.status); } catch (e) { console.error("[WebDav] failed to check", e); diff --git a/app/utils/format.ts b/app/utils/format.ts index 450d66696..2e8a382b9 100644 --- a/app/utils/format.ts +++ b/app/utils/format.ts @@ -11,3 +11,18 @@ export function prettyObject(msg: any) { } return ["```json", msg, "```"].join("\n"); } + +export function* chunks(s: string, maxBytes = 1000 * 1000) { + const decoder = new TextDecoder("utf-8"); + let buf = new TextEncoder().encode(s); + while (buf.length) { + let i = buf.lastIndexOf(32, maxBytes + 1); + // If no space found, try forward search + if (i < 0) i = buf.indexOf(32, maxBytes); + // If there's no space at all, take all + if (i < 0) i = buf.length; + // This is a safe cut-off point; never half-way a multi-byte + yield decoder.decode(buf.slice(0, i)); + buf = buf.slice(i + 1); // Skip space (if any) + } +} diff --git a/app/utils/sync.ts b/app/utils/sync.ts index ab1f1f449..1acfc1289 100644 --- a/app/utils/sync.ts +++ b/app/utils/sync.ts @@ -69,6 +69,9 @@ const MergeStates: StateMerger = { localState.sessions.forEach((s) => (localSessions[s.id] = s)); remoteState.sessions.forEach((remoteSession) => { + // skip empty chats + if (remoteSession.messages.length === 0) return; + const localSession = localSessions[remoteSession.id]; if (!localSession) { // if remote session is new, just merge it From f1e7db6a88611a62a6ef6446c768ab16bd943173 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 19 Sep 2023 03:33:17 +0800 Subject: [PATCH 071/108] feat: auto fill upstash backup name --- app/store/sync.ts | 52 +++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/app/store/sync.ts b/app/store/sync.ts index ff9f650c0..c194162fc 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -22,27 +22,29 @@ export interface WebDavConfig { export type SyncStore = GetStoreState; -export const useSyncStore = createPersistStore( - { - provider: ProviderType.WebDAV, - useProxy: true, - proxyUrl: corsPath(ApiPath.Cors), +const DEFAULT_SYNC_STATE = { + provider: ProviderType.WebDAV, + useProxy: true, + proxyUrl: corsPath(ApiPath.Cors), - webdav: { - endpoint: "", - username: "", - password: "", - }, - - upstash: { - endpoint: "", - username: STORAGE_KEY, - apiKey: "", - }, - - lastSyncTime: 0, - lastProvider: "", + webdav: { + endpoint: "", + username: "", + password: "", }, + + upstash: { + endpoint: "", + username: STORAGE_KEY, + apiKey: "", + }, + + lastSyncTime: 0, + lastProvider: "", +}; + +export const useSyncStore = createPersistStore( + DEFAULT_SYNC_STATE, (set, get) => ({ coundSync() { const config = get()[get().provider]; @@ -108,6 +110,16 @@ export const useSyncStore = createPersistStore( }), { name: StoreKey.Sync, - version: 1, + version: 1.1, + + migrate(persistedState, version) { + const newState = persistedState as typeof DEFAULT_SYNC_STATE; + + if (version < 1.1) { + newState.upstash.username = STORAGE_KEY; + } + + return newState as any; + }, }, ); From 37b49400db0358c9eeb1d63c8843b405097c869e Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 19 Sep 2023 11:03:03 +0800 Subject: [PATCH 072/108] Update constant.ts --- app/constant.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/constant.ts b/app/constant.ts index c6cba3ef0..9e23ed510 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -8,7 +8,7 @@ export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/c export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`; export const RUNTIME_CONFIG_DOM = "danger-runtime-config"; -export const DEFAULT_CORS_HOST = "https://chatgpt2.nextweb.fun"; +export const DEFAULT_CORS_HOST = "https://nb.nextweb.fun"; export const DEFAULT_API_HOST = `${DEFAULT_CORS_HOST}/api/proxy`; export enum Path { From b050417ab1caf4d2c5da99039f57c84712559648 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 19 Sep 2023 11:03:22 +0800 Subject: [PATCH 073/108] Update tauri.conf.json --- 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 d8b677bf6..77b02a3ba 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "ChatGPT Next Web", - "version": "2.9.6" + "version": "2.9.7" }, "tauri": { "allowlist": { From 175b4e7f92abba782bdd2561c5e479bc315d6d9f Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 19 Sep 2023 11:04:10 +0800 Subject: [PATCH 074/108] Update README_CN.md --- README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index e593e45da..b31e8b367 100644 --- a/README_CN.md +++ b/README_CN.md @@ -114,7 +114,7 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填 OPENAI_API_KEY= # 中国大陆用户,可以使用本项目自带的代理进行开发,你也可以自由选择其他代理地址 -BASE_URL=https://chatgpt2.nextweb.fun/api/proxy +BASE_URL=https://nb.nextweb.fun/api/proxy ``` ### 本地开发 From eae7d6260f6d0968a59a07576bd86937b12a673a Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 19 Sep 2023 23:26:52 +0800 Subject: [PATCH 075/108] fix: should not tight border in desktop app --- app/components/home.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/components/home.tsx b/app/components/home.tsx index 285ca0f58..07d5e88b3 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -115,7 +115,10 @@ const loadAsyncGoogleFont = () => { getClientConfig()?.buildMode === "export" ? remoteFontUrl : proxyFontUrl; linkEl.rel = "stylesheet"; linkEl.href = - googleFontUrl + "/css2?family=" + encodeURIComponent("Noto Sans:wght@300;400;700;900") + "&display=swap"; + googleFontUrl + + "/css2?family=" + + encodeURIComponent("Noto Sans:wght@300;400;700;900") + + "&display=swap"; document.head.appendChild(linkEl); }; @@ -125,6 +128,8 @@ function Screen() { const isHome = location.pathname === Path.Home; const isAuth = location.pathname === Path.Auth; const isMobileScreen = useMobileScreen(); + const shouldTightBorder = + config.tightBorder && !isMobileScreen && !getClientConfig()?.isApp; useEffect(() => { loadAsyncGoogleFont(); @@ -134,11 +139,9 @@ function Screen() {
{isAuth ? ( From 26e50cefea3afe79ef98cef951ef404d077aaf3d Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Wed, 20 Sep 2023 02:09:14 +0800 Subject: [PATCH 076/108] Update chat.tsx --- app/components/chat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 6fb497303..4e6aedecc 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -937,7 +937,7 @@ function _Chat() { const isTouchTopEdge = e.scrollTop <= edgeThreshold; const isTouchBottomEdge = bottomHeight >= e.scrollHeight - edgeThreshold; const isHitBottom = - bottomHeight >= e.scrollHeight - (isMobileScreen ? 0 : 10); + bottomHeight >= e.scrollHeight - (isMobileScreen ? 4 : 10); const prevPageMsgIndex = msgRenderIndex - CHAT_PAGE_SIZE; const nextPageMsgIndex = msgRenderIndex + CHAT_PAGE_SIZE; From 23eb7732d7011ce9476ab6309c92509e094fca81 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Wed, 20 Sep 2023 17:47:35 +0800 Subject: [PATCH 077/108] feat: support more http status check for webdav --- app/utils/cloud/webdav.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/utils/cloud/webdav.ts b/app/utils/cloud/webdav.ts index c87fdd71e..3a1553c10 100644 --- a/app/utils/cloud/webdav.ts +++ b/app/utils/cloud/webdav.ts @@ -21,7 +21,7 @@ export function createWebDavClient(store: SyncStore) { proxyUrl, }); console.log("[WebDav] check", res.status, res.statusText); - return [201, 200, 404, 401].includes(res.status); + return [201, 200, 404, 301, 302, 307, 308].includes(res.status); } catch (e) { console.error("[WebDav] failed to check", e); } From 70b0580fb7e51110d2b6624268e35621db6b647d Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Tue, 26 Sep 2023 04:59:19 +0700 Subject: [PATCH 078/108] UI Page [sidebar] [+] fix(sidebar.tsx): update onClick function to conditionally navigate to different paths based on config.dontShowMaskSplashScreen value Ref : [Feature] Make the mask selection more streamlined Yidadaa#2895 --- app/components/sidebar.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 3ca167896..6212d05d9 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -160,7 +160,13 @@ export function SideBar(props: { className?: string }) { icon={} text={shouldNarrow ? undefined : Locale.Mask.Name} className={styles["sidebar-bar-button"]} - onClick={() => navigate(Path.NewChat, { state: { fromHome: true } })} + onClick={() => { + if (config.dontShowMaskSplashScreen !== true) { + navigate(Path.NewChat, { state: { fromHome: true } }); + } else { + navigate(Path.Masks, { state: { fromHome: true } }); + } + }} shadow /> Date: Tue, 26 Sep 2023 20:45:09 +0300 Subject: [PATCH 079/108] Update chat.tsx --- app/components/chat.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 4e6aedecc..005ee46e9 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -1155,7 +1155,13 @@ function _Chat() { {isUser ? ( ) : ( - + <> + {["system", "context"].includes(message.role) ? ( + + ) : ( + + )} + )}
From 398e229c7760c71419d0d851323b253e3edea79b Mon Sep 17 00:00:00 2001 From: Algorithm5838 <108630393+Algorithm5838@users.noreply.github.com> Date: Tue, 26 Sep 2023 21:49:17 +0300 Subject: [PATCH 080/108] Update chat.tsx --- app/components/chat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 005ee46e9..cca096eb8 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -1156,7 +1156,7 @@ function _Chat() { ) : ( <> - {["system", "context"].includes(message.role) ? ( + {["system"].includes(message.role) ? ( ) : ( From 3bfcdf9c41c9a8311cb8c4b6a8a9bc5f05ce322d Mon Sep 17 00:00:00 2001 From: Eric R Date: Thu, 28 Sep 2023 06:10:22 -0400 Subject: [PATCH 081/108] [ADDED] MacOS detect --- app/utils.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/utils.ts b/app/utils.ts index 37c17dd76..67d008b93 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -173,3 +173,15 @@ export function autoGrowTextArea(dom: HTMLTextAreaElement) { export function getCSSVar(varName: string) { return getComputedStyle(document.body).getPropertyValue(varName).trim(); } + +/** + * Detects if the Operation system is MacOS + */ +export function isMacOS(): boolean { + if (window !== 'undefined') { + let userAgent = window?.navigator?.userAgent + if (userAgent.indexOf('Mac') != -1) return true + } + + return false +} From f3d5fc7a845aeba3dc120b46196dc3978c77faef Mon Sep 17 00:00:00 2001 From: Eric R Date: Thu, 28 Sep 2023 06:11:48 -0400 Subject: [PATCH 082/108] [FIXED] now the default key should be CMD on MacOS --- app/store/config.ts | 3 ++- app/utils.ts | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/store/config.ts b/app/store/config.ts index 956e5f3eb..53df36ec2 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -1,4 +1,5 @@ import { LLMModel } from "../client/api"; +import { isMacOS } from "../utils"; import { getClientConfig } from "../config/client"; import { DEFAULT_INPUT_TEMPLATE, @@ -27,7 +28,7 @@ export enum Theme { export const DEFAULT_CONFIG = { lastUpdate: Date.now(), // timestamp, to merge state - submitKey: SubmitKey.CtrlEnter as SubmitKey, + submitKey: isMacOS() ? SubmitKey.MetaEnter : SubmitKey.CtrlEnter, avatar: "1f603", fontSize: 14, theme: Theme.Auto as Theme, diff --git a/app/utils.ts b/app/utils.ts index 67d008b93..3db8e7c41 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -178,10 +178,10 @@ export function getCSSVar(varName: string) { * Detects if the Operation system is MacOS */ export function isMacOS(): boolean { - if (window !== 'undefined') { - let userAgent = window?.navigator?.userAgent - if (userAgent.indexOf('Mac') != -1) return true - } + if (typeof window !== "undefined") { + let userAgent = window?.navigator?.userAgent; + if (userAgent.indexOf("Mac") != -1) return true; + } - return false + return false; } From f1ca03e3788d89295f889eb67da270adbee6137f Mon Sep 17 00:00:00 2001 From: EricGit Date: Thu, 28 Sep 2023 13:21:17 -0400 Subject: [PATCH 083/108] [FIXED] now it should detects all macintosh --- app/utils.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/utils.ts b/app/utils.ts index 3db8e7c41..1b76285cc 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -175,13 +175,13 @@ export function getCSSVar(varName: string) { } /** - * Detects if the Operation system is MacOS + * Detects Macintosh */ export function isMacOS(): boolean { if (typeof window !== "undefined") { - let userAgent = window?.navigator?.userAgent; - if (userAgent.indexOf("Mac") != -1) return true; + let userAgent = window.navigator.userAgent.toLocaleLowerCase(); + const macintosh = /iphone|ipad|ipod|macintosh/.test(userAgent) + return !!macintosh } - - return false; + return false } From 4a599e986f1c56c3e3543e61b9403bdd01593c37 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Sat, 30 Sep 2023 22:16:34 +0700 Subject: [PATCH 084/108] UI Page [Auth Page] [+] feat(auth.tsx): add support for resetting access token in resetAccessCode function [+] fix(auth.tsx): fix formatting issue in resetAccessCode function [+] feat(locales): add support for sub tips in Auth component for multiple languages : - Add sub tips in Arabic locale (ar.ts) - Add sub tips in Bengali locale (bn.ts) - Add sub tips in Chinese locale (cn.ts) - Add sub tips in English locale (en.ts) - Add sub tips in Indonesian locale (id.ts) --- app/components/auth.tsx | 23 ++++++++++++++++++----- app/locales/ar.ts | 1 + app/locales/bn.ts | 1 + app/locales/cn.ts | 1 + app/locales/en.ts | 1 + app/locales/id.ts | 8 ++++++-- 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 9a5b0c655..4e5ab8dc6 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -15,7 +15,7 @@ export function AuthPage() { const access = useAccessStore(); const goHome = () => navigate(Path.Home); - const resetAccessCode = () => access.updateCode(""); // Reset access code to empty string + const resetAccessCode = () => { access.updateCode(""); access.updateToken(""); }; // Reset access code to empty string useEffect(() => { if (getClientConfig()?.isApp) { @@ -42,6 +42,16 @@ export function AuthPage() { access.updateCode(e.currentTarget.value); }} /> +
{Locale.Auth.SubTips}
+ { + access.updateToken(e.currentTarget.value); + }} + />
- { - resetAccessCode(); - goHome(); - }} /> + { + resetAccessCode(); + goHome(); + }} + />
); diff --git a/app/locales/ar.ts b/app/locales/ar.ts index 520cb2635..d5844acd6 100644 --- a/app/locales/ar.ts +++ b/app/locales/ar.ts @@ -10,6 +10,7 @@ const ar: PartialLocaleType = { Auth: { Title: "تحتاج إلى رمز الوصول", Tips: "يرجى إدخال رمز الوصول أدناه", + SubTips: "أو أدخل مفتاح واجهة برمجة تطبيقات OpenAI الخاص بك", Input: "رمز الوصول", Confirm: "تأكيد", Later: "لاحقًا", diff --git a/app/locales/bn.ts b/app/locales/bn.ts index 2d2266b3f..2db132cec 100644 --- a/app/locales/bn.ts +++ b/app/locales/bn.ts @@ -10,6 +10,7 @@ const bn: PartialLocaleType = { Auth: { Title: "একটি অ্যাক্সেস কোড প্রয়োজন", Tips: "নীচে অ্যাক্সেস কোড ইনপুট করুন", + SubTips: "অথবা আপনার OpenAI API কী প্রবেশ করুন", Input: "অ্যাক্সেস কোড", Confirm: "নিশ্চিত করুন", Later: "পরে", diff --git a/app/locales/cn.ts b/app/locales/cn.ts index b2afc7534..8eeef9e01 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -13,6 +13,7 @@ const cn = { Auth: { Title: "需要密码", Tips: "管理员开启了密码验证,请在下方填入访问码", + SubTips: "或者输入你的 OpenAI API 密钥", Input: "在此处填写访问码", Confirm: "确认", Later: "稍后再说", diff --git a/app/locales/en.ts b/app/locales/en.ts index 697d09d1f..458d53dd5 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -15,6 +15,7 @@ const en: LocaleType = { Auth: { Title: "Need Access Code", Tips: "Please enter access code below", + SubTips: "Or enter your OpenAI API Key", Input: "access code", Confirm: "Confirm", Later: "Later", diff --git a/app/locales/id.ts b/app/locales/id.ts index 244c5ade1..4b61f6434 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -1,15 +1,19 @@ +import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; import { PartialLocaleType } from "./index"; +const isApp = !!getClientConfig()?.isApp; const id: PartialLocaleType = { WIP: "Coming Soon...", Error: { - Unauthorized: - "Akses tidak diizinkan. Silakan [otorisasi](/#/auth) dengan memasukkan kode akses.", + Unauthorized: isApp + ? "Kunci API tidak valid, silakan periksa di halaman [Pengaturan](/#/settings)." + : "Akses tidak diizinkan, silakan masukkan kode akses di halaman [autentikasi](/#/auth), atau masukkan kunci API OpenAI Anda.", }, Auth: { Title: "Diperlukan Kode Akses", Tips: "Masukkan kode akses di bawah", + SubTips: "Atau masukkan kunci API OpenAI Anda", Input: "Kode Akses", Confirm: "Konfirmasi", Later: "Nanti", From bc00be9065a9ea1af8ab38925400f2bfd8dc10b3 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Sat, 30 Sep 2023 22:33:18 +0700 Subject: [PATCH 085/108] Feat & Fix UI Page [Auth Page] [+] feat(auth.tsx): add goChat function to navigate to chat page [+] fix(auth.tsx): change onClick event from goHome to goChat --- app/components/auth.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 4e5ab8dc6..8e2b964ab 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -15,6 +15,7 @@ export function AuthPage() { const access = useAccessStore(); const goHome = () => navigate(Path.Home); + const goChat = () => navigate(Path.Chat); const resetAccessCode = () => { access.updateCode(""); access.updateToken(""); }; // Reset access code to empty string useEffect(() => { @@ -57,7 +58,7 @@ export function AuthPage() { Date: Sat, 30 Sep 2023 22:59:24 +0700 Subject: [PATCH 086/108] Refactor Locale Indonesia [+] refactor(id.ts): remove unused import and isApp variable [+] fix(id.ts): update Unauthorized error message --- app/locales/id.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/locales/id.ts b/app/locales/id.ts index 4b61f6434..1036e66b6 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -1,15 +1,11 @@ -import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; import { PartialLocaleType } from "./index"; -const isApp = !!getClientConfig()?.isApp; const id: PartialLocaleType = { WIP: "Coming Soon...", Error: { - Unauthorized: isApp - ? "Kunci API tidak valid, silakan periksa di halaman [Pengaturan](/#/settings)." - : "Akses tidak diizinkan, silakan masukkan kode akses di halaman [autentikasi](/#/auth), atau masukkan kunci API OpenAI Anda.", - }, + Unauthorized: "Akses tidak diizinkan, silakan masukkan kode akses atau masukkan kunci API OpenAI Anda. di halaman [autentikasi](/#/auth) atau di halaman [Pengaturan](/#/settings).", + }, Auth: { Title: "Diperlukan Kode Akses", Tips: "Masukkan kode akses di bawah", From 04b638aa064e346f72a6fcef295f151379c1be17 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Sun, 1 Oct 2023 00:23:19 +0700 Subject: [PATCH 087/108] Fix & Refactor UI Page [Auth Page] [+] fix(auth.tsx): fix conditional rendering of token input field [+] refactor(auth.tsx): improve code readability by using conditional rendering for token input field --- app/components/auth.tsx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 8e2b964ab..b82d0e894 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -43,16 +43,20 @@ export function AuthPage() { access.updateCode(e.currentTarget.value); }} /> -
{Locale.Auth.SubTips}
- { - access.updateToken(e.currentTarget.value); - }} - /> + {!access.hideUserApiKey ? ( + <> +
{Locale.Auth.SubTips}
+ { + access.updateToken(e.currentTarget.value); + }} + /> + + ) : null}
Date: Tue, 3 Oct 2023 08:08:11 +0700 Subject: [PATCH 088/108] Client App [Notification] [+] feat(global.d.ts): add support for window.__TAURI__.notification methods [+] feat(update.ts): add notification for new version availability [+] fix(Cargo.toml): add tauri feature "notification-all" to enable notifications [+] fix(tauri.conf.json): enable all notification features in tauri configuration --- app/global.d.ts | 5 +++++ app/store/update.ts | 29 +++++++++++++++++++++++++++++ src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 6 ++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/app/global.d.ts b/app/global.d.ts index 524ce77db..dc1d52653 100644 --- a/app/global.d.ts +++ b/app/global.d.ts @@ -13,5 +13,10 @@ declare module "*.svg"; declare interface Window { __TAURI__?: { writeText(text: string): Promise; + notification:{ + requestPermission(): Promise; + isPermissionGranted(): Promise; + sendNotification(options: string | Options): void; + }; }; } diff --git a/app/store/update.ts b/app/store/update.ts index 42b86586c..facb57321 100644 --- a/app/store/update.ts +++ b/app/store/update.ts @@ -2,8 +2,10 @@ import { FETCH_COMMIT_URL, FETCH_TAG_URL, StoreKey } from "../constant"; import { api } from "../client/api"; import { getClientConfig } from "../config/client"; import { createPersistStore } from "../utils/store"; +import ChatGptIcon from "../icons/chatgpt.png"; const ONE_MINUTE = 60 * 1000; +const isApp = !!getClientConfig()?.isApp; function formatVersionDate(t: string) { const d = new Date(+t); @@ -80,6 +82,33 @@ export const useUpdateStore = createPersistStore( set(() => ({ remoteVersion: remoteId, })); + if (window.__TAURI__?.notification && isApp) { + // Check if notification permission is granted + await window.__TAURI__?.notification.isPermissionGranted().then((granted) => { + if (!granted) { + // Send a notification without waiting for permission (because we don't neeed a permisison once client is already click "check") + window.__TAURI__?.notification.sendNotification({ + title: "ChatGPT Next Web", + body: `A new version (${remoteId}) is available.`, + icon: `${ChatGptIcon.src}`, + sound: "Default" + }); + } else { + // Request permission to show notifications + window.__TAURI__?.notification.requestPermission().then((permission) => { + if (permission === 'granted') { + // Show a notification using Tauri + window.__TAURI__?.notification.sendNotification({ + title: "ChatGPT Next Web", + body: `A new version (${remoteId}) is available.`, + icon: `${ChatGptIcon.src}`, + sound: "Default" + }); + } + }); + } + }); + } console.log("[Got Upstream] ", remoteId); } catch (error) { console.error("[Fetch Upstream Commit Id]", error); diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index ac5d04e83..fee1c860f 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -17,7 +17,7 @@ tauri-build = { version = "1.3.0", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.3.0", features = ["clipboard-all", "dialog-all", "shell-open", "updater", "window-close", "window-hide", "window-maximize", "window-minimize", "window-set-icon", "window-set-ignore-cursor-events", "window-set-resizable", "window-show", "window-start-dragging", "window-unmaximize", "window-unminimize"] } +tauri = { version = "1.3.0", features = ["notification-all", "fs-all", "clipboard-all", "dialog-all", "shell-open", "updater", "window-close", "window-hide", "window-maximize", "window-minimize", "window-set-icon", "window-set-ignore-cursor-events", "window-set-resizable", "window-show", "window-start-dragging", "window-unmaximize", "window-unminimize"] } tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } [features] diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 77b02a3ba..147fc9944 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -44,6 +44,12 @@ "startDragging": true, "unmaximize": true, "unminimize": true + }, + "fs": { + "all": true + }, + "notification": { + "all": true } }, "bundle": { From d2ad01a9ffd8fd0645013aca862c691af3c2f01f Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Tue, 3 Oct 2023 08:49:03 +0700 Subject: [PATCH 089/108] Client App Fix Issue [Bug] 'export' button does not work #2884 [+] fix(exporter.tsx): add async keyword to download function [+] feat(exporter.tsx): add support for saving image file using window.__TAURI__ API [+] feat(global.d.ts): add types for window.__TAURI__ API methods [+] feat(locales): add translations for download success and failure messages [+] feat(sync.ts): add support for generating backup file name with date and time [+] fix(utils.ts): add async keyword to downloadAs function and add support for saving file using window.__TAURI__ API --- app/components/exporter.tsx | 58 ++++++++++++++++++++++++++++--------- app/global.d.ts | 7 +++++ app/locales/cn.ts | 4 +++ app/locales/en.ts | 4 +++ app/locales/id.ts | 4 +++ app/store/sync.ts | 8 ++++- app/utils.ts | 43 ++++++++++++++++++++++----- 7 files changed, 106 insertions(+), 22 deletions(-) diff --git a/app/components/exporter.tsx b/app/components/exporter.tsx index 5b3e8a9a1..0a885d874 100644 --- a/app/components/exporter.tsx +++ b/app/components/exporter.tsx @@ -433,25 +433,55 @@ export function ImagePreviewer(props: { const isMobile = useMobileScreen(); - const download = () => { + const download = async () => { showToast(Locale.Export.Image.Toast); const dom = previewRef.current; if (!dom) return; - toPng(dom) - .then((blob) => { - if (!blob) return; - - if (isMobile || getClientConfig()?.isApp) { - showImageModal(blob); + + const isApp = getClientConfig()?.isApp; + + try { + const blob = await toPng(dom); + if (!blob) return; + + if (isMobile || (isApp && window.__TAURI__)) { + if (isApp && window.__TAURI__) { + const result = await window.__TAURI__.dialog.save({ + defaultPath: `${props.topic}.png`, + filters: [ + { + name: "PNG Files", + extensions: ["png"], + }, + { + name: "All Files", + extensions: ["*"], + }, + ], + }); + + if (result !== null) { + const response = await fetch(blob); + const buffer = await response.arrayBuffer(); + const uint8Array = new Uint8Array(buffer); + await window.__TAURI__.fs.writeBinaryFile(result, uint8Array); + showToast(Locale.Download.Success); + } else { + showToast(Locale.Download.Failed); + } } else { - const link = document.createElement("a"); - link.download = `${props.topic}.png`; - link.href = blob; - link.click(); - refreshPreview(); + showImageModal(blob); } - }) - .catch((e) => console.log("[Export Image] ", e)); + } else { + const link = document.createElement("a"); + link.download = `${props.topic}.png`; + link.href = blob; + link.click(); + refreshPreview(); + } + } catch (error) { + showToast(Locale.Download.Failed); + } }; const refreshPreview = () => { diff --git a/app/global.d.ts b/app/global.d.ts index dc1d52653..e0a2c3f06 100644 --- a/app/global.d.ts +++ b/app/global.d.ts @@ -13,6 +13,13 @@ declare module "*.svg"; declare interface Window { __TAURI__?: { writeText(text: string): Promise; + invoke(command: string, payload?: Record): Promise; + dialog: { + save(options?: Record): Promise; + }; + fs: { + writeBinaryFile(path: string, data: Uint8Array): Promise; + }; notification:{ requestPermission(): Promise; isPermissionGranted(): Promise; diff --git a/app/locales/cn.ts b/app/locales/cn.ts index b2afc7534..746f3580b 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -323,6 +323,10 @@ const cn = { Success: "已写入剪切板", Failed: "复制失败,请赋予剪切板权限", }, + Download: { + Success: "内容已下载到您的目录。", + Failed: "下载失败。", + }, Context: { Toast: (x: any) => `包含 ${x} 条预设提示词`, Edit: "当前对话设置", diff --git a/app/locales/en.ts b/app/locales/en.ts index 697d09d1f..62823b051 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -329,6 +329,10 @@ const en: LocaleType = { Success: "Copied to clipboard", Failed: "Copy failed, please grant permission to access clipboard", }, + Download: { + Success: "Content downloaded to your directory.", + Failed: "Download failed.", + }, Context: { Toast: (x: any) => `With ${x} contextual prompts`, Edit: "Current Chat Settings", diff --git a/app/locales/id.ts b/app/locales/id.ts index 244c5ade1..7e1366f96 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -301,6 +301,10 @@ const id: PartialLocaleType = { Failed: "Gagal menyalin, mohon berikan izin untuk mengakses clipboard atau Clipboard API tidak didukung (Tauri)", }, + Download: { + Success: "Konten berhasil diunduh ke direktori Anda.", + Failed: "Unduhan gagal.", + }, Context: { Toast: (x: any) => `Dengan ${x} promp kontekstual`, Edit: "Pengaturan Obrolan Saat Ini", diff --git a/app/store/sync.ts b/app/store/sync.ts index c194162fc..c34ae7b9b 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -1,3 +1,4 @@ +import { getClientConfig } from "../config/client"; import { Updater } from "../typing"; import { ApiPath, STORAGE_KEY, StoreKey } from "../constant"; import { createPersistStore } from "../utils/store"; @@ -20,6 +21,7 @@ export interface WebDavConfig { password: string; } +const isApp = !!getClientConfig()?.isApp; export type SyncStore = GetStoreState; const DEFAULT_SYNC_STATE = { @@ -57,7 +59,11 @@ export const useSyncStore = createPersistStore( export() { const state = getLocalAppState(); - const fileName = `Backup-${new Date().toLocaleString()}.json`; + const datePart = isApp + ? `${new Date().toLocaleDateString().replace(/\//g, '_')} ${new Date().toLocaleTimeString().replace(/:/g, '_')}` + : new Date().toLocaleString(); + + const fileName = `Backup-${datePart}.json`; downloadAs(JSON.stringify(state), fileName); }, diff --git a/app/utils.ts b/app/utils.ts index 37c17dd76..19e55ce63 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -31,12 +31,41 @@ export async function copyToClipboard(text: string) { } } -export function downloadAs(text: string, filename: string) { - const element = document.createElement("a"); - element.setAttribute( - "href", - "data:text/plain;charset=utf-8," + encodeURIComponent(text), - ); +export async function downloadAs(text: string, filename: string) { + if (window.__TAURI__) { + const result = await window.__TAURI__.dialog.save({ + defaultPath: `${filename}`, + filters: [ + { + name: `${filename.split('.').pop()} files`, + extensions: [`${filename.split('.').pop()}`], + }, + { + name: "All Files", + extensions: ["*"], + }, + ], + }); + + if (result !== null) { + try { + await window.__TAURI__.fs.writeBinaryFile( + result, + new Uint8Array([...text].map((c) => c.charCodeAt(0))) + ); + showToast(Locale.Download.Success); + } catch (error) { + showToast(Locale.Download.Failed); + } + } else { + showToast(Locale.Download.Failed); + } + } else { + const element = document.createElement("a"); + element.setAttribute( + "href", + "data:text/plain;charset=utf-8," + encodeURIComponent(text), + ); element.setAttribute("download", filename); element.style.display = "none"; @@ -46,7 +75,7 @@ export function downloadAs(text: string, filename: string) { document.body.removeChild(element); } - +} export function readFromFile() { return new Promise((res, rej) => { const fileInput = document.createElement("input"); From ddfd05b008a80238eea870eba7e84e142dd74c47 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Tue, 3 Oct 2023 09:12:41 +0700 Subject: [PATCH 090/108] Fix & Feat Client App [Notification] [+] fix(update.ts): remove unnecessary notification sending when permission is not granted [+] feat(update.ts): add notification for already up to date version --- app/store/update.ts | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/app/store/update.ts b/app/store/update.ts index facb57321..5a08e36a3 100644 --- a/app/store/update.ts +++ b/app/store/update.ts @@ -86,24 +86,28 @@ export const useUpdateStore = createPersistStore( // Check if notification permission is granted await window.__TAURI__?.notification.isPermissionGranted().then((granted) => { if (!granted) { - // Send a notification without waiting for permission (because we don't neeed a permisison once client is already click "check") - window.__TAURI__?.notification.sendNotification({ - title: "ChatGPT Next Web", - body: `A new version (${remoteId}) is available.`, - icon: `${ChatGptIcon.src}`, - sound: "Default" - }); + return } else { // Request permission to show notifications window.__TAURI__?.notification.requestPermission().then((permission) => { if (permission === 'granted') { - // Show a notification using Tauri - window.__TAURI__?.notification.sendNotification({ - title: "ChatGPT Next Web", - body: `A new version (${remoteId}) is available.`, - icon: `${ChatGptIcon.src}`, - sound: "Default" - }); + if (version === remoteId) { + // Show a notification using Tauri + window.__TAURI__?.notification.sendNotification({ + title: "ChatGPT Next Web", + body: "Already up to date", + icon: `${ChatGptIcon.src}`, + sound: "Default" + }); + } else { + // Show a notification for the new version using Tauri + window.__TAURI__?.notification.sendNotification({ + title: "ChatGPT Next Web", + body: `A new version (${remoteId}) is available.`, + icon: `${ChatGptIcon.src}`, + sound: "Default" + }); + } } }); } From b558d1afc6a95d917500064bb77870864ccc1958 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Wed, 4 Oct 2023 02:10:26 +0700 Subject: [PATCH 091/108] Feat & Fix "Client App [Notification]" [+] feat(update.ts): add support for localization in update notifications [+] fix(update.ts): add missing semicolon in useUpdateStore function --- app/store/update.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/store/update.ts b/app/store/update.ts index 5a08e36a3..2b088a13d 100644 --- a/app/store/update.ts +++ b/app/store/update.ts @@ -3,6 +3,7 @@ import { api } from "../client/api"; import { getClientConfig } from "../config/client"; import { createPersistStore } from "../utils/store"; import ChatGptIcon from "../icons/chatgpt.png"; +import Locale from "../locales"; const ONE_MINUTE = 60 * 1000; const isApp = !!getClientConfig()?.isApp; @@ -86,7 +87,7 @@ export const useUpdateStore = createPersistStore( // Check if notification permission is granted await window.__TAURI__?.notification.isPermissionGranted().then((granted) => { if (!granted) { - return + return; } else { // Request permission to show notifications window.__TAURI__?.notification.requestPermission().then((permission) => { @@ -95,15 +96,16 @@ export const useUpdateStore = createPersistStore( // Show a notification using Tauri window.__TAURI__?.notification.sendNotification({ title: "ChatGPT Next Web", - body: "Already up to date", + body: `${Locale.Settings.Update.IsLatest}`, icon: `${ChatGptIcon.src}`, sound: "Default" }); } else { + const updateMessage = Locale.Settings.Update.FoundUpdate(`${remoteId}`); // Show a notification for the new version using Tauri window.__TAURI__?.notification.sendNotification({ title: "ChatGPT Next Web", - body: `A new version (${remoteId}) is available.`, + body: updateMessage, icon: `${ChatGptIcon.src}`, sound: "Default" }); From ad5093ce0593e21df3e7c782a0396725662a425a Mon Sep 17 00:00:00 2001 From: KeithHello Date: Wed, 4 Oct 2023 16:38:28 +0900 Subject: [PATCH 092/108] Change log - config.ts - line 72 remove type confirmation of x as x always has type 'number' - line 135 remove redundant local variable - chat.ts - delete unused import --- app/locales/index.ts | 2 +- app/store/chat.ts | 3 --- app/store/config.ts | 10 ++++------ 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/locales/index.ts b/app/locales/index.ts index 79e314fac..a32d3207c 100644 --- a/app/locales/index.ts +++ b/app/locales/index.ts @@ -65,7 +65,7 @@ export const ALL_LANG_OPTIONS: Record = { }; const LANG_KEY = "lang"; -const DEFAULT_LANG = "en"; +const DEFAULT_LANG = "cn"; const fallbackLang = en; const targetLang = ALL_LANGS[getLang()] as LocaleType; diff --git a/app/store/chat.ts b/app/store/chat.ts index 269cc4a33..56ac8db6c 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -1,6 +1,3 @@ -import { create } from "zustand"; -import { persist } from "zustand/middleware"; - import { trimTopic } from "../utils"; import Locale, { getLang } from "../locales"; diff --git a/app/store/config.ts b/app/store/config.ts index 956e5f3eb..b64884795 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -46,13 +46,13 @@ export const DEFAULT_CONFIG = { modelConfig: { model: "gpt-3.5-turbo" as ModelType, - temperature: 0.5, + temperature: 0.6, top_p: 1, max_tokens: 2000, presence_penalty: 0, frequency_penalty: 0, sendMemory: true, - historyMessageCount: 4, + historyMessageCount: 1, compressMessageLengthThreshold: 1000, enableInjectSystemPrompts: true, template: DEFAULT_INPUT_TEMPLATE, @@ -69,7 +69,7 @@ export function limitNumber( max: number, defaultValue: number, ) { - if (typeof x !== "number" || isNaN(x)) { + if (isNaN(x)) { return defaultValue; } @@ -132,9 +132,7 @@ export const useAppConfig = createPersistStore( .customModels.split(",") .filter((v) => !!v && v.length > 0) .map((m) => ({ name: m, available: true })); - - const models = get().models.concat(customModels); - return models; + return get().models.concat(customModels); }, }), { From 1505372e204d8363ec9c67a61d004806de81c200 Mon Sep 17 00:00:00 2001 From: KeithHello Date: Wed, 4 Oct 2023 18:08:29 +0900 Subject: [PATCH 093/108] Change log - config.ts - line 72: remove type confirmation of x as x always has type 'number' - line 135: remove the redundant local variable - chat.ts - delete the unused import --- app/locales/index.ts | 2 +- app/store/config.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/locales/index.ts b/app/locales/index.ts index a32d3207c..79e314fac 100644 --- a/app/locales/index.ts +++ b/app/locales/index.ts @@ -65,7 +65,7 @@ export const ALL_LANG_OPTIONS: Record = { }; const LANG_KEY = "lang"; -const DEFAULT_LANG = "cn"; +const DEFAULT_LANG = "en"; const fallbackLang = en; const targetLang = ALL_LANGS[getLang()] as LocaleType; diff --git a/app/store/config.ts b/app/store/config.ts index b64884795..ca230cc3c 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -46,13 +46,13 @@ export const DEFAULT_CONFIG = { modelConfig: { model: "gpt-3.5-turbo" as ModelType, - temperature: 0.6, + temperature: 0.5, top_p: 1, max_tokens: 2000, presence_penalty: 0, frequency_penalty: 0, sendMemory: true, - historyMessageCount: 1, + historyMessageCount: 4, compressMessageLengthThreshold: 1000, enableInjectSystemPrompts: true, template: DEFAULT_INPUT_TEMPLATE, From 88f8c43472db225184287640fb09eae9aa122b3f Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Tue, 3 Oct 2023 21:09:41 +0800 Subject: [PATCH 094/108] Improve tw 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 15f6648e6..e9f38d097 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -7,13 +7,13 @@ const tw: PartialLocaleType = { Unauthorized: "目前您的狀態是未授權,請前往[設定頁面](/#/auth)輸入授權碼。", }, ChatItem: { - ChatItemCount: (count: number) => `${count} 條對話`, + ChatItemCount: (count: number) => `${count} 則對話`, }, Chat: { - SubTitle: (count: number) => `您已經與 ChatGPT 進行了 ${count} 條對話`, + SubTitle: (count: number) => `您已經與 ChatGPT 進行了 ${count} 則對話`, Actions: { - ChatList: "查看訊息列表", - CompressedHistory: "查看壓縮後的歷史 Prompt", + ChatList: "檢視訊息列表", + CompressedHistory: "檢視壓縮後的歷史 Prompt", Export: "匯出聊天紀錄", Copy: "複製", Stop: "停止", @@ -23,15 +23,15 @@ const tw: PartialLocaleType = { Rename: "重新命名對話", Typing: "正在輸入…", Input: (submitKey: string) => { - var inputHints = `輸入訊息後,按下 ${submitKey} 鍵即可發送`; + var inputHints = `輸入訊息後,按下 ${submitKey} 鍵即可傳送`; if (submitKey === String(SubmitKey.Enter)) { inputHints += ",Shift + Enter 鍵換行"; } return inputHints; }, - Send: "發送", + Send: "傳送", Config: { - Reset: "重置預設", + Reset: "重設", SaveAs: "另存新檔", }, }, @@ -46,7 +46,7 @@ const tw: PartialLocaleType = { Title: "上下文記憶 Prompt", EmptyContent: "尚未記憶", Copy: "複製全部", - Send: "發送記憶", + Send: "傳送記憶", Reset: "重設對話", ResetConfirm: "重設後將清除目前對話記錄以及歷史記憶,確認重設?", }, @@ -71,22 +71,22 @@ const tw: PartialLocaleType = { }, InjectSystemPrompts: { Title: "匯入系統提示", - SubTitle: "強制在每個請求的訊息列表開頭添加一個模擬 ChatGPT 的系統提示", + SubTitle: "強制在每個請求的訊息列表開頭新增一個模擬 ChatGPT 的系統提示", }, Update: { - Version: (x: string) => `當前版本:${x}`, + Version: (x: string) => `目前版本:${x}`, IsLatest: "已是最新版本", CheckUpdate: "檢查更新", IsChecking: "正在檢查更新...", FoundUpdate: (x: string) => `發現新版本:${x}`, GoToUpdate: "前往更新", }, - SendKey: "發送鍵", + SendKey: "傳送鍵", Theme: "主題", TightBorder: "緊湊邊框", SendPreviewBubble: { Title: "預覽氣泡", - SubTitle: "在預覽氣泡中預覽 Markdown 内容", + SubTitle: "在預覽氣泡中預覽 Markdown 內容", }, Mask: { Splash: { @@ -101,7 +101,7 @@ const tw: PartialLocaleType = { }, List: "自定義提示詞列表", ListCount: (builtin: number, custom: number) => - `內建 ${builtin} 條,用戶定義 ${custom} 條`, + `內建 ${builtin} 條,使用者定義 ${custom} 條`, Edit: "編輯", Modal: { Title: "提示詞列表", @@ -132,7 +132,7 @@ const tw: PartialLocaleType = { }, IsChecking: "正在檢查…", Check: "重新檢查", - NoAccess: "輸入API Key查看餘額", + NoAccess: "輸入 API Key 檢視餘額", }, AccessCode: { Title: "授權碼", @@ -150,7 +150,7 @@ const tw: PartialLocaleType = { }, PresencePenalty: { Title: "話題新穎度 (presence_penalty)", - SubTitle: "值越大,越有可能擴展到新話題", + SubTitle: "值越大,越有可能拓展到新話題", }, FrequencyPenalty: { Title: "頻率懲罰度 (frequency_penalty)", @@ -163,7 +163,7 @@ const tw: PartialLocaleType = { Error: "出錯了,請稍後再嘗試", Prompt: { History: (content: string) => - "這是 AI 與用戶的歷史聊天總結,作為前情提要:" + content, + "這是 AI 與使用者的歷史聊天總結,作為前情提要:" + content, Topic: "Use the language used by the user (e.g. en for english conversation, zh-hant for chinese conversation, etc.) to generate a title (at most 6 words) summarizing our conversation without any lead-in, quotation marks, preamble like 'Title:', direct text copies, single-word replies, quotation marks, translations, or brackets. Remove enclosing quotation marks. The title should make third-party grasp the essence of the conversation in first sight.", Summarize: @@ -192,16 +192,16 @@ const tw: PartialLocaleType = { Item: { Info: (count: number) => `包含 ${count} 條預設對話`, Chat: "對話", - View: "查看", + View: "檢視", Edit: "編輯", - Delete: "删除", - DeleteConfirm: "確認删除?", + Delete: "刪除", + DeleteConfirm: "確認刪除?", }, EditModal: { Title: (readonly: boolean) => - `編輯預設面具 ${readonly ? "(只读)" : ""}`, + `編輯預設面具 ${readonly ? "(只讀)" : ""}`, Download: "下載預設", - Clone: "克隆預設", + Clone: "複製預設", }, Config: { Avatar: "角色頭像", @@ -215,7 +215,7 @@ const tw: PartialLocaleType = { SubTitle: "現在開始,與面具背後的靈魂思維碰撞", More: "搜尋更多", NotShow: "不再呈現", - ConfirmNoShow: "確認禁用?禁用後可以随時在設定中重新啟用。", + ConfirmNoShow: "確認停用?停用後可以隨時在設定中重新啟用。", }, UI: { Confirm: "確認", From e35c807216a3813cd902dc3c2cf20b818c639242 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Sat, 7 Oct 2023 23:48:50 +0800 Subject: [PATCH 095/108] Update tauri.conf.json --- 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 77b02a3ba..8161e79e8 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "ChatGPT Next Web", - "version": "2.9.7" + "version": "2.9.8" }, "tauri": { "allowlist": { From bdb49b11711977638a41845de36c6ea4166f2ef2 Mon Sep 17 00:00:00 2001 From: Kong Gaowen Date: Mon, 9 Oct 2023 18:50:10 +0800 Subject: [PATCH 096/108] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 似乎这里引入了一个逻辑判断错误,会导致打包之后的界面变宽变高。 --- app/components/home.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/home.tsx b/app/components/home.tsx index 07d5e88b3..dd22142d7 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -129,7 +129,7 @@ function Screen() { const isAuth = location.pathname === Path.Auth; const isMobileScreen = useMobileScreen(); const shouldTightBorder = - config.tightBorder && !isMobileScreen && !getClientConfig()?.isApp; + config.tightBorder && !isMobileScreen && getClientConfig()?.isApp; useEffect(() => { loadAsyncGoogleFont(); From f21f922160a6f024d0fe5e52a0ced59144c2d2cc Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 9 Oct 2023 22:46:36 +0800 Subject: [PATCH 097/108] Update tauri.conf.json --- 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 68f9c07c0..e530203f6 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "ChatGPT Next Web", - "version": "2.9.8" + "version": "2.9.9" }, "tauri": { "allowlist": { From f54db695af55ea925369950be9b1b8988461544b Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 10 Oct 2023 11:54:25 +0800 Subject: [PATCH 098/108] fix: #2981 full screen button not works --- app/components/home.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/components/home.tsx b/app/components/home.tsx index dd22142d7..811cbdf51 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -128,8 +128,7 @@ function Screen() { const isHome = location.pathname === Path.Home; const isAuth = location.pathname === Path.Auth; const isMobileScreen = useMobileScreen(); - const shouldTightBorder = - config.tightBorder && !isMobileScreen && getClientConfig()?.isApp; + const shouldTightBorder = getClientConfig()?.isApp || (config.tightBorder && !isMobileScreen); useEffect(() => { loadAsyncGoogleFont(); From ea59ab51767fc0fe3e7ad611bf2f47fb357ddc11 Mon Sep 17 00:00:00 2001 From: Jason O'Gray <1228915+ograycode@users.noreply.github.com> Date: Tue, 10 Oct 2023 11:24:45 -0400 Subject: [PATCH 099/108] Fix type in sync.ts Simple typo fix. --- app/store/sync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/store/sync.ts b/app/store/sync.ts index c34ae7b9b..b74f6895f 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -101,7 +101,7 @@ export const useSyncStore = createPersistStore( mergeAppState(localState, remoteState); setLocalAppState(localState); } catch (e) { - console.log("[Sync] failed to get remoate state", e); + console.log("[Sync] failed to get remote state", e); } await client.set(config.username, JSON.stringify(localState)); From 74fcaab5e9aa9538bff31e01f669dd9811a64c26 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Wed, 11 Oct 2023 04:36:04 +0700 Subject: [PATCH 100/108] Refactor Mask UI Page [Masks] [+] refactor(mask.tsx): refactor simple search to be case-insensitive --- app/components/mask.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/components/mask.tsx b/app/components/mask.tsx index 1ee1c239a..9fe1d485a 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -393,11 +393,13 @@ export function MaskPage() { const [searchText, setSearchText] = useState(""); const masks = searchText.length > 0 ? searchMasks : allMasks; - // simple search, will refactor later + // refactored already, now it accurate const onSearch = (text: string) => { setSearchText(text); if (text.length > 0) { - const result = allMasks.filter((m) => m.name.includes(text)); + const result = allMasks.filter((m) => + m.name.toLowerCase().includes(text.toLowerCase()) + ); setSearchMasks(result); } else { setSearchMasks(allMasks); From ec33281ff5f4856e056d660861699198b584ee92 Mon Sep 17 00:00:00 2001 From: mcheping520 Date: Wed, 11 Oct 2023 21:29:50 +0800 Subject: [PATCH 101/108] =?UTF-8?q?=E6=8F=90=E4=BE=9B=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E8=AE=B0=E5=BD=95=E6=95=99=E7=A8=8B=EF=BC=8C?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E7=BF=BB=E8=AF=91=E5=A4=9A=E5=9B=BD=E8=AF=AD?= =?UTF-8?q?=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 32 ++++++++++++++++++++++++++++++++ README_CN.md | 32 ++++++++++++++++++++++++++++++++ README_JA.md | 32 ++++++++++++++++++++++++++++++++ README_KO.md | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+) diff --git a/README.md b/README.md index 07455d00d..6474f63e7 100644 --- a/README.md +++ b/README.md @@ -257,6 +257,38 @@ If your proxy needs password, use: bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh) ``` +## Synchronizing Chat Records (UpStash) +### Prerequisites +- GitHub account +- Your own ChatGPT-Next-Web server set up +- [UpStash](https://upstash.com) + +### Getting Started +1. Register for an UpStash account. +2. Create a Database. + + ![Register and Log In](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-17-32.png) + + ![Create a Database](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-24-04.png) + + ![Select a Server](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-25-49.png) + +3. Find the REST API and copy both UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN (⚠Caution⚠: Do not disclose your Token!) + + ![Copy](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-28-12.png) + +4. Copy UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN into your synchronization configuration, and click **Check Availability**. + + ![Synchronization Step 1](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-04.png) + + If everything is in order, you've succeeded. + + ![Synchronization Availability Check Completed](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-52.png) + +5. Success! + + ![Great job!](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-34-15.png) + ## Documentation > Please go to the [docs][./docs] directory for more documentation instructions. diff --git a/README_CN.md b/README_CN.md index b31e8b367..a3dd5c64b 100644 --- a/README_CN.md +++ b/README_CN.md @@ -169,6 +169,38 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s ⚠️ 注意:如果你安装过程中遇到了问题,请使用 docker 部署。 +## 同步聊天记录(upStash) +### 准备工作 +- GitHub账号 +- 拥有自己搭建过的ChatGPT-Next-Web的服务器 +- [UpStash](https://upstash.com) + +### 开始教程 +1. 注册UpStash账号 +2. 创建数据库 + + ![注册登录](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-17-32.png) + + ![创建数据库](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-24-04.png) + + ![选择服务器](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-25-49.png) + +3. 找到REST API,分别复制UPSTASH_REDIS_REST_URL和UPSTASH_REDIS_REST_TOKEN(⚠切记⚠:不要泄露Token!) + + ![复制](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-28-12.png) + +4. UPSTASH_REDIS_REST_URL和UPSTASH_REDIS_REST_TOKEN复制到你的同步配置,点击**检查可用性** + + ![同步1](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-04.png) + + 如果没什么问题,那就成功了 + + ![同步可用性完成的样子](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-52.png) + +5. Success! + + ![好耶~!](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-34-15.png) + ## 鸣谢 ### 捐赠者 diff --git a/README_JA.md b/README_JA.md index 72a0d5373..4735326d4 100644 --- a/README_JA.md +++ b/README_JA.md @@ -223,6 +223,38 @@ docker run -d -p 3000:3000 \ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh) ``` +## チャット履歴の同期(UpStash) +### 必要なもの +- GitHub アカウント +- 独自の ChatGPT-Next-Web サーバーのセットアップ +- [UpStash](https://upstash.com) + +### はじめに +1. UpStash アカウントを登録します。 +2. データベースを作成します。 + + ![登録とログイン](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-17-32.png) + + ![データベースの作成](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-24-04.png) + + ![サーバーの選択](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-25-49.png) + +3. REST API を見つけ、UPSTASH_REDIS_REST_URL および UPSTASH_REDIS_REST_TOKEN の両方をコピーします(⚠注意⚠:トークンを公開しないでください!)。 + + ![コピー](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-28-12.png) + +4. UPSTASH_REDIS_REST_URL および UPSTASH_REDIS_REST_TOKEN を同期設定にコピーし、**可用性を確認** をクリックします。 + + ![同期ステップ 1](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-04.png) + + すべてが正常であれば、成功です。 + + ![同期の可用性チェックが完了しました](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-52.png) + +5. 成功! + + ![おめでとうございます!](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-34-15.png) + ## スクリーンショット ![Settings](./docs/images/settings.png) diff --git a/README_KO.md b/README_KO.md index 519dd9d9b..c0220168c 100644 --- a/README_KO.md +++ b/README_KO.md @@ -169,6 +169,38 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s ⚠️ 주의: 설치 중 문제가 발생한 경우, docker로 배포하세요. +## 채팅 기록 동기화 (UpStash) +### 사전 요구 사항 +- GitHub 계정 +- 자체 ChatGPT-Next-Web 서버 설치 +- [UpStash](https://upstash.com) + +### 시작하기 +1. UpStash 계정 등록. +2. 데이터베이스 만들기. + + ![등록 및 로그인](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-17-32.png) + + ![데이터베이스 만들기](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-24-04.png) + + ![서버 선택](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-25-49.png) + +3. REST API를 찾아 UPSTASH_REDIS_REST_URL 및 UPSTASH_REDIS_REST_TOKEN을 복사합니다 (⚠주의⚠: 토큰을 노출하지 마세요!). + + ![복사](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-28-12.png) + +4. UPSTASH_REDIS_REST_URL 및 UPSTASH_REDIS_REST_TOKEN을 동기화 구성에 복사하고 **가용성 확인**을 클릭합니다. + + ![동기화 단계 1](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-04.png) + + 모든 것이 정상이라면, 성공입니다. + + ![동기화 가능성 확인 완료](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-52.png) + +5. 성공! + + ![잘 했어요!](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-34-15.png) + ## 감사의 말 ### 기부자 From 1e77df381a6b0d4eac1eeefb2610ae00780d0042 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Thu, 12 Oct 2023 17:31:29 +0800 Subject: [PATCH 102/108] Update constant.ts --- app/constant.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/constant.ts b/app/constant.ts index 9e23ed510..e03e00971 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -8,7 +8,7 @@ export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/c export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`; export const RUNTIME_CONFIG_DOM = "danger-runtime-config"; -export const DEFAULT_CORS_HOST = "https://nb.nextweb.fun"; +export const DEFAULT_CORS_HOST = "https://ab.nextweb.fun"; export const DEFAULT_API_HOST = `${DEFAULT_CORS_HOST}/api/proxy`; export enum Path { From b5d3348d996c5ca65aef870f4435cf1610a5ace0 Mon Sep 17 00:00:00 2001 From: mcheping520 Date: Thu, 12 Oct 2023 18:17:38 +0800 Subject: [PATCH 103/108] =?UTF-8?q?=E5=B7=B2=E6=8B=86=E5=88=86=E5=8D=95?= =?UTF-8?q?=E4=B8=AA=E6=96=87=E4=BB=B6=E3=80=81=E7=BF=BB=E8=AF=91=E5=A4=9A?= =?UTF-8?q?=E5=9B=BD=E8=AF=AD=E8=A8=80=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 30 +---------------------------- README_CN.md | 31 ------------------------------ README_JA.md | 32 ------------------------------- README_KO.md | 31 ------------------------------ docs/images/upstash-1.png | Bin 0 -> 23846 bytes docs/images/upstash-2.png | Bin 0 -> 66350 bytes docs/images/upstash-3.png | Bin 0 -> 50434 bytes docs/images/upstash-4.png | Bin 0 -> 37931 bytes docs/images/upstash-5.png | Bin 0 -> 68485 bytes docs/images/upstash-6.png | Bin 0 -> 67791 bytes docs/images/upstash-7.png | Bin 0 -> 85775 bytes docs/synchronise-chat-logs-cn.md | 31 ++++++++++++++++++++++++++++++ docs/synchronise-chat-logs-en.md | 31 ++++++++++++++++++++++++++++++ docs/synchronise-chat-logs-es.md | 31 ++++++++++++++++++++++++++++++ docs/synchronise-chat-logs-ja.md | 31 ++++++++++++++++++++++++++++++ docs/synchronise-chat-logs-ko.md | 31 ++++++++++++++++++++++++++++++ 16 files changed, 156 insertions(+), 123 deletions(-) create mode 100644 docs/images/upstash-1.png create mode 100644 docs/images/upstash-2.png create mode 100644 docs/images/upstash-3.png create mode 100644 docs/images/upstash-4.png create mode 100644 docs/images/upstash-5.png create mode 100644 docs/images/upstash-6.png create mode 100644 docs/images/upstash-7.png create mode 100644 docs/synchronise-chat-logs-cn.md create mode 100644 docs/synchronise-chat-logs-en.md create mode 100644 docs/synchronise-chat-logs-es.md create mode 100644 docs/synchronise-chat-logs-ja.md create mode 100644 docs/synchronise-chat-logs-ko.md diff --git a/README.md b/README.md index 6474f63e7..3fe76f4f0 100644 --- a/README.md +++ b/README.md @@ -258,36 +258,8 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s ``` ## Synchronizing Chat Records (UpStash) -### Prerequisites -- GitHub account -- Your own ChatGPT-Next-Web server set up -- [UpStash](https://upstash.com) -### Getting Started -1. Register for an UpStash account. -2. Create a Database. - - ![Register and Log In](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-17-32.png) - - ![Create a Database](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-24-04.png) - - ![Select a Server](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-25-49.png) - -3. Find the REST API and copy both UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN (⚠Caution⚠: Do not disclose your Token!) - - ![Copy](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-28-12.png) - -4. Copy UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN into your synchronization configuration, and click **Check Availability**. - - ![Synchronization Step 1](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-04.png) - - If everything is in order, you've succeeded. - - ![Synchronization Availability Check Completed](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-52.png) - -5. Success! - - ![Great job!](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-34-15.png) +| [简体中文](./docs/synchronise-chat-logs-cn.md) | [English](./docs/synchronise-chat-logs-en.md) | [Italiano](./docs/synchronise-chat-logs-es.md) | [日本語](./docs/synchronise-chat-logs-ja.md) | [한국어](./docs/synchronise-chat-logs-ko.md) ## Documentation diff --git a/README_CN.md b/README_CN.md index a3dd5c64b..ce9309fd2 100644 --- a/README_CN.md +++ b/README_CN.md @@ -169,37 +169,6 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s ⚠️ 注意:如果你安装过程中遇到了问题,请使用 docker 部署。 -## 同步聊天记录(upStash) -### 准备工作 -- GitHub账号 -- 拥有自己搭建过的ChatGPT-Next-Web的服务器 -- [UpStash](https://upstash.com) - -### 开始教程 -1. 注册UpStash账号 -2. 创建数据库 - - ![注册登录](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-17-32.png) - - ![创建数据库](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-24-04.png) - - ![选择服务器](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-25-49.png) - -3. 找到REST API,分别复制UPSTASH_REDIS_REST_URL和UPSTASH_REDIS_REST_TOKEN(⚠切记⚠:不要泄露Token!) - - ![复制](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-28-12.png) - -4. UPSTASH_REDIS_REST_URL和UPSTASH_REDIS_REST_TOKEN复制到你的同步配置,点击**检查可用性** - - ![同步1](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-04.png) - - 如果没什么问题,那就成功了 - - ![同步可用性完成的样子](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-52.png) - -5. Success! - - ![好耶~!](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-34-15.png) ## 鸣谢 diff --git a/README_JA.md b/README_JA.md index 4735326d4..72a0d5373 100644 --- a/README_JA.md +++ b/README_JA.md @@ -223,38 +223,6 @@ docker run -d -p 3000:3000 \ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh) ``` -## チャット履歴の同期(UpStash) -### 必要なもの -- GitHub アカウント -- 独自の ChatGPT-Next-Web サーバーのセットアップ -- [UpStash](https://upstash.com) - -### はじめに -1. UpStash アカウントを登録します。 -2. データベースを作成します。 - - ![登録とログイン](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-17-32.png) - - ![データベースの作成](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-24-04.png) - - ![サーバーの選択](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-25-49.png) - -3. REST API を見つけ、UPSTASH_REDIS_REST_URL および UPSTASH_REDIS_REST_TOKEN の両方をコピーします(⚠注意⚠:トークンを公開しないでください!)。 - - ![コピー](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-28-12.png) - -4. UPSTASH_REDIS_REST_URL および UPSTASH_REDIS_REST_TOKEN を同期設定にコピーし、**可用性を確認** をクリックします。 - - ![同期ステップ 1](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-04.png) - - すべてが正常であれば、成功です。 - - ![同期の可用性チェックが完了しました](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-52.png) - -5. 成功! - - ![おめでとうございます!](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-34-15.png) - ## スクリーンショット ![Settings](./docs/images/settings.png) diff --git a/README_KO.md b/README_KO.md index c0220168c..6ec7fe0e6 100644 --- a/README_KO.md +++ b/README_KO.md @@ -169,37 +169,6 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s ⚠️ 주의: 설치 중 문제가 발생한 경우, docker로 배포하세요. -## 채팅 기록 동기화 (UpStash) -### 사전 요구 사항 -- GitHub 계정 -- 자체 ChatGPT-Next-Web 서버 설치 -- [UpStash](https://upstash.com) - -### 시작하기 -1. UpStash 계정 등록. -2. 데이터베이스 만들기. - - ![등록 및 로그인](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-17-32.png) - - ![데이터베이스 만들기](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-24-04.png) - - ![서버 선택](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-25-49.png) - -3. REST API를 찾아 UPSTASH_REDIS_REST_URL 및 UPSTASH_REDIS_REST_TOKEN을 복사합니다 (⚠주의⚠: 토큰을 노출하지 마세요!). - - ![복사](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-28-12.png) - -4. UPSTASH_REDIS_REST_URL 및 UPSTASH_REDIS_REST_TOKEN을 동기화 구성에 복사하고 **가용성 확인**을 클릭합니다. - - ![동기화 단계 1](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-04.png) - - 모든 것이 정상이라면, 성공입니다. - - ![동기화 가능성 확인 완료](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-33-52.png) - -5. 성공! - - ![잘 했어요!](https://mcheping520.gitee.io/graphic-bed/image/Snipaste_2023-10-11_19-34-15.png) ## 감사의 말 diff --git a/docs/images/upstash-1.png b/docs/images/upstash-1.png new file mode 100644 index 0000000000000000000000000000000000000000..5813c521f3c9b4f33988d4a6257bc5dffe16b134 GIT binary patch literal 23846 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

oK?_RPpa_6X!+56upt8s(AbT(r5^|pbArE0^` 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 literal 0 HcmV?d00001 diff --git a/docs/images/upstash-3.png b/docs/images/upstash-3.png new file mode 100644 index 0000000000000000000000000000000000000000..06c53d190e14a5166f8967c37b10753d71ea4456 GIT binary patch literal 50434 zcmdq|WmJ}5)IN%0AT1#!9ZE?kAgGkmDc#-D($Y#wH%OOscL_+hNJ)c|(jlFDKEL<> ze%NE|vCo(D<(y|I@Z7Q1y5~J>&3Rpca&N`3&`HpdkdUw>#Dx`*kZ$+D{|?mKa3m2! zUITuiy%$%vM?%7LK>WWoZ$kM24&pk9s5vOwm^e7=+ZiJ%TARJsw|6ksCi*dlghYiT zA^b+^L-N*~vl{-yJjxyiPybFEVUSu`Ngl(k&LF|9$yn#95M8-Z^qwx9K(lhc+yeoZB_;+SyMgkX>6UHql|q~*S78AEv?kHK9SN#D4|HBNerL8;~gIdjm3 zLHJXc>^s}0g+;l4h_@+5#>$;eRmo@M^@r}XaJjXljchSPm}$sL5=+qOtJ1Z;$dQ}4rL62Vj{L*vEY_mrHChn z`aXgZPTKgaYWPgb?@4{Uy*EzB^S73=vvY{g)#kXxXwFdrhpBxC zQN5$~-WQg?Wo2czJ&WW>8y(h1nmu>|g%zrpK$7Y29^pyMH8{@A&f?+WiL=sDAup)m z{G!Ci-^O{AtJGRi^jMkG;#XCV80f&q+OZ<_QC95+oWjD&=bmdXG$@N-w`NiytJmB` zMy6JVMNDc}n#jNt)lK*{#I%H7`iNvm>Z{ktB>l3{C4}?enqbMAx>;7o2VYp2DF^7) z@VS)XX8jsEPz1(5J3AEk_)6?k-in7W3AuEKn)V7ePvTHOsgNja<5zhrkXsJW3D0tf zlbF=wino^9EnMK#o(j{&sT%9(Y}{W-q{-}0W?`+}bZGZ+LBMkOb0_l=Wi zIZ=nR{$8-RSAgUFw&_GTo{jEoR~aY|s_T4|=5-zdS^6#UQ7B#ZGHjn;6OgOYV4Sl~{{3~$}Wh2~N3G{^sA z&Gk|BMl&!cJ&`ZM*imA;k*`>ISmtZGGkbKwhL9fTbg+(!Xi+`seo57X5K^|>acqS| zd8i=1wZv|%4dN~*dG`1tZf}uWrlB;V@cqT7+d8utj&p0<>p8QHwY&&Vh3^x!NzQOy zDQKthJvhJ{j(pUeV*4pl*GDAx$(0#>MA>^E7Y-^EQpMZou&$K)AV(n{b~*1i^`}}# zX802corwAAeK42b>5{87VHE#4JeeP@nIHeG3}!d}G((8NoZ!5CU1YNt2Ydvh8$Mp%%U{Ks zOtA&7M;k|*6O??e4q2k1TiO~rnVFgXm=6t_vfrk>^7QoFp03`Ut}aw!s#ev9N!@MB zC@}o>JukLvvH4H+dZQi_v1-ZXEBQtQU{g}?yPvKOCUu@Pxi)zYrSdIxhqpLx5{3sf z*{}Wh(;gsjbNR1CyODy=wd!~DyP$ju2mn${v9A{H_4YD8Rt(Wb4P2UCX|33Tiy(mH zDdaaeZNG9q-8(!oVDaNe2y=JF4$bEd{hst#uQEoEodx4)EY%x;$Z*2<`Y>^wu6$~Vz%H^6L(J6LV56a(ZB-6cX|PZ) zg9m$}QuKGSJWCag^jeM2Brr`a3%&POP&TKTN zyu2I@4NbejL7pi#n22rFX~lNH+uqrklJi|eZS6_y6V;vhCe3BnL)WX}EHSZA5&}HD zOeH2Br>#2IBlA$v=&N{MkMqOdtv(nRJUD`5=0^>Vo1iP%N=)FMM8n7)Qc~KSZPnYa z@t^_mcyAM)`OfRgP!G!r8-Sc+tW=S_6_=C7#p3o z3)Cx3)YR0>%{M5$F88;lDxDWRR3G8N1&jS`E#KVF?5fSjy}i8^6ch{$48R==3JDpr zPr;*BX%vfQfnMyNoRrFEiGsgQdTwzHa^?+7XsQ9l-I=K^m5gPYYjk$Dx38Rk94Vfe zm9;xtKbj|>1u8&PoZ518?Y7(#DVR~F)8zWxd`w(i{PX9}RK5tEpu$n}e8|RDibcwW z>L+Mpv+L{Y`}gnPn3x#v%lk~R=0B2Of<_Vrck;O&GUzs6ju)zG)>x`HxjM!fgPOi{ z+!zZ93E{GuK0Q6{=<4zh2$(3**34HrxN1R0`Zyx>2v50C%f$hG`gTMIH+0V$x=HadC0`{j6P_nnIx-|kBh4@>Wu<@z!f~;Gs*bzV|=_C1Q=Hya9nfF~Vi6}71mrI_I`i-h$M(AA`cE>d$!&l6!+}k z+P^EENbyIJ;;8~&o@WQfJ9iP5y|mO`v6Ziwo67HTetofPpYM%>hmXI!x#_k$-vqCL zU?Ugd?`^^YlUaH4y>>v1%$s%tDz?R|3}3F)m6U9`VH7mw#9 z=ShuXE|(Ka?(z%ivIxm+(9K}@goW*lWJ_5`bN~CjySq?hrJ2;I@ILR#YhQ3i#-J{LT)-HK#l&gQS; zne;b4qM;-yFKlTRN>|x$(eLgOp!-GB$!v_}k0M+!*~^M|!teL%d~maF3#UC*~mx(vw22nOs)B1 zfDA)W9DM28j}!=RZGIT0KayS6evn7^c2rnOO7?}3^IV@Uh0pS^OneYD=uKt}vQc7y3+_3x0GeZMI zLr9s~KkVrx<^-!?bEAkb#WLZe3!=6^rI$_p=CH1CENivUoRW0%{prA`J1F2Qm*A~` zm56q4;ePgP|N81;*23A@x&M($ z5`|Lk@>4!SUz`N#F04PhbB*yAwG~J6ei%fn`>oFmyJj04E!zFD0`8M9z9O#ER8TF} z0B6snS|W{_0JaCN4UHPzFVlLi0mhD_eeY}q4jCa)P#}x(g7M|v=zlLvC`@rlNoT6e z6q648!Rf>6w_EAM6%0}t`u^$86XgPY0)ppY{@@Vv$2TA_f5o{tw{*8=NA_XU`5l_7CSpTpkI!Tj#e-?#Nvr! z%?UUIekEV2K&7x$w}l^W;q`<~(^~XWO15#qLYw}t^51p!^^SjkX4o!u-Sc^qt&p$8 zlpw7Ft{bEYS&JfFw7QDLUx;=`8{;gLMN0V!g(}6;bczZJBZEoY>Ty=nRqMWJ4Q~m${ZDfpUkUIlGj4tazhfnhm6T$d%;8-i zEvmJ8M<*tNLH$}AN}Fj!)FaAPKD1`S#s>Bdb4_mC{T0V0i2c+2?+>kQ3k$GW`E_x! z+hV)q-(&&r{3G`Zow7Ww`U6n-(b3Ub%c)G2!b)yE;#F|AOI@KDXlRgLYd1R0Ra?aO z8qxT6bay-d{i%Pj+`GTO54#_1Ow6tE!inB!`iK569sbHeN*g1&Ql{SyMF69h(>^HJ z&F3415fKsKB)~S)Uf6o;e~IGJge3(merWZ^8G*Q(%I8M$((Vy2FYm>CJew}UF^guE zgC!YuhZ(CoU0!6hX|mla=s-(jgKCH`Opq#fTRs#>12O@ziVk> z;ifk-hWU7bh&(>^BiMSJ?)`c8>=}q*g@o7r^gZke_4W1XqRsxa`3YPdDhdmCR2cW) zzI}USWQ0y8(P4kF6NKEqau7<&?dt04c{1nB&(H50(dfJzMk#RJ6G;nuPDKX%$6kxD zlI&!mdl-VBOG|ensD&VAw!l+EXwle>lFX){pn#j2ArpY1;E|{6OQ($AUO)&4`q&SfxcH*_ zUg*Ynp$iXK2-4c(B!t@tbOI(!O8(iInVAY>P$EdudV6|ay#M_%Eo}kv$6)t8Fw`Qg zdUntkE-t5)zSxtUIa1h9K+)lpfeic_nmi;KgUwtugM|H>JIMVNaYVCDl~V&(DIBJ6 zZB-G49zS-QdEQU2U!(rH#uKa~c*qb`@vUs$TK^wcHwzb8b2rpop!oa$4gc6hoo1Z~ z`=r>|?1{~X{^iby(v+X*Xq6gsT>lD0<>L}R)BA+XuzLmpzDsn148`6)%WQg{0EB{1 z(*Berl#YXggO)bSpfkvzd~%*(zbn$)Pp4E?QWCO_V-(uMpPX(}9wMxZIAn5}Gjngtp%e4hg|b%s~{-zW09k@Zl$CN@S$| zuHfmPr>!(ko=9cLO*X=PNY{+`K}AK(ZmO-=0ceD-P4k0;gYyW}-WwVmlqsEND+z#Q_(^S&>Z^*6o>h@vOPC1W|v4bmE;3V*Z2+ zl|1NLwPjdJ5H~z!d{b{H-Nv{2YukH_abrSrtlsdA#x!~1_%r9-N+w5ZtS_Yd2~8Kl zt`6~@jUVOkseJ=mj6+_sU6*06toF^ya*|ytPNX**ZN2Lk!$iS;AW5|`9k@JE+*zQ! zrHN@&!4$i18S`?z{31|D_z}~AEu;vs9QQCP)t?&|Fs|}Gjz(j;_*-K+R7|a{xIU7j zJdcvKc`nhSou2qqIqe-9Mt2^&1Y zkjOQLK;f1(=gqjxlLmBBWcB$CuQ?%9Tx{}pr&cXzE2QKU(w9~SoFhkfEL(KC+kYpY znc2C%=NAyCFnSV)R-ioW-Z*R0=zc-BUE9x+!f7c=M=g9YhrW4ZKAYpgYB}=RP3B^p zkPCC`r?&YcBZuSv05&*&@S?3XMu zV@*<#!E99VD!UC;HWnc-if78y57xUouN;w(?sud4_&KfHwSN-w#Zk!~8PJRnladeL ziPOtisGs?8ZfVWJm=II%oxlZ+b&OnlGNykkqB-_Dd$fcP0+&l}@?U9v% zE8)?VC;rj7fhrcBsWf?NttOTtlV!KgxYfQWDq{ViU;pmxi?xB_Lo(^E`YR%GHMcj& zqh>k^uVXXU5E>KExOrOckTUIyvwH3E zYLA@et9Gte2FX#NNh{t|-!2+;ho0UJeS>dJ5A87ldf~4x(NT6YPp-?j?N2C_Gu*9H zByOik;_v9){#emh4|!(M)dI@#}$miOAm z9Kt?&UBzX!)R9G{I|#JI?yd6;iwuUH&SY(r~lMs(!Pwi|v2T5@Y>ghi6ssfXErCea#nw9kGN-*!f}we6jkZgDGh-H;ai z?0#h)e{5xS+~;)@tMZHCG`^rqhMkPpvNE#Xapsb9%6Q}hVrg*~`#ug$4Zb+`l0lv` z_xB@i3FjI*zLIk5YfnMFP7$%-+60@cZwPWdqVgp^-Wp}riMw*+XgsO$d_uwg{nac! z3f=yv)uhM1IP0|-2jT>T=ej3g_(a$gR21xTU~Ts11;+7bX6)3$H~nu7$6gA4KAW-^ z{A|5B6_b6uO7H z;G&E)m+m<)a3AU81N$hw*4d^~623TzI(o~=*UeVZj5`LaRS6@p3yr=tWmWn|uG>+& zj-;NM%XU+Vlv>j@tT_fD^s)~Gb+QSAGOspmXB-PB%3YhRM^)x>ihphMmFkuqCU1J( z_`ryH8h(xGcC+E+RRa%{yv_)cqL^mB%~x<&nl2c;iku)8%*Nf_mVBA~JQ0=J(u9t>W? zrC!J@F=;v)d`JOWal@#2z2#zlZnRj0m8mQCGR%ab3~rSU4k;R0s7_mQr-qK~PIfjC zo+{yi%Y5lP*To`S>eaL&Gn*H;=_NpLs?>L3dJBmGBbi#QjBSM7?NGn6qT}kkjwM+v zu#;JTkZrWmKP0ADt?LS765n2j&+(r92|wtJyEeq}VUQ)Fyrh=b0V{XtlQS4?y zO=*hhpH?tcJN>DhYLSvF%rRcPy|UUBBMnuRJ!@}z7 zvT4DyCFGSR<4slD6wwjNI1TQnqN1i)Bs=F*!u@jMDlOF3qx{b6Kf`cDD6+->7YmR% zb6&YGe0M^Dcy{clWvOn0RexKW=;m2!%Wu4q%g~J~4Mn4>G6~vGwY4&jy$fP1W!J`7 zuTptsdz({a+!FpBswO(2Sy{gz{9$4&Z&$yq^T4p-ePl_#Z~?^c7eZy}eJ&FG6p!}~ zaj?ldWELJcVn*Ajmx71f7?rRNPkVZ@W3T>(H?L);$Sf`}?pKEo%4Zbpp60oGNwJ-e zW@&xvs@&soXwE)xo_(m2xijh~wfKoLky4->v%Rs(BJ0}n)nmMJjp8!-i0sOaYSYcUnyA#NCXJI~Rc{ z$Nl;!{UrCvhX>C5EodL|Fm;D3+9gOe{_qF*R^$BJ(xgkozZjx_1-B9^^ z-ubN{`Z+Nwwf~)fP_cG2>a8Rx?RroC-lKmKz7bU#HnR#(F!zX*iZd6Ek~U2r6uaGb zAoZm2t#jD!n0aYRyD{&VcoDjjYqj|4%F>#5*k(Zjl~Mc=G7SU`V#VJ+c0^ES~1V`YZh z*4eAB%5%c;;I95Z6JMN$90j@UadNVOVxCnL?D~lYE1u883|CH{uGdny#2hY6!i*Un zJ+ZBO$uj6#kn?A6m?lki{?P5DP#{h8okopktfRoEr*&x6H|aawSpEXxkhtmnv*uJ((Pk@97BlX!2vLJ~3Y9OFbabgjPKx{k&UdS|?E_cPIX6LqqHdYS zHmQ^~zN78tx3VJZZPCFto50spn*V$Tw>dIXchP*3@&(eYc^@Qw zlCUcoqRk7l2zc#Q}A!NZeA-#aAV|L!?W zsj|UkNr?UMH6y;SarMGgI8M}zpz>C~L(+vE8gcE|)LDOXJ)RB0bkA28&9y zHT;`NBn{P%s?<9yos!Pgn`7@F#gPBNVIIiO@5=h)pVb|@c1=SUeZ;ytjTO2=(P`On zhkx1I@8yx-S#R`gV#0NheznH_&rgs*v)Aq#*}Rr`RHaM4$&z`0+fl&!Y3E8=jJM~) zt|9rCkj$9|{yUo3ty_MZNJ=3lqZ7u1F9DL(%}E}XDv*7>?{jDd^#b%EuF?MtaQ z*+B=}NrwWvV*|QHkk%4=_F1)RK?>$uHk~c-Ie?Go7dDkBt^IDQ)TXm~*0ajmZ5D!Q zV}W|o_f*$xXQ20ww-K-R5oqUYM3MG z!ttL-?@9?rZUMbVMk>GNamy~M$hzmWNf2VQ;{2v-0tu<_z9}l9{C&;FH5e?o#q!Z7n-9<<%YsMb5k$MeST>&I3rX0 zzN?-hO)^;evdOI*8+qMj7d;hkfq!GlK(3S)C4TpL0j}WZ)-Uty(zBa<6AyJYrr(+T z$l4sE=GC{JQwdFL>sY{3Vqnv(^d@cYeJBzba38M*Q(5-B&0(K%{EO^kL$SueFB>C_ z1AA-FY9A_wmfLo8{Jga*M-#brG{HyM)ncHO)h*GYOS575d?||c=MBELz*!GCDcBQf zYYS`#DaQye*1VWN=JgFF4GJ*A;0%sSP*nK3z*atG9dxaEGqd~1d2zleg#InbG3A)j zF)mkLX+rI5x$2;q5Uu#$+ei#a5a#QuTvclh++OAJYb*9X;{RDZ#~{aWK;gix8J9xm zi$jBn7U!9?xww$?MDry12%S{Gx%yZl7#e%@Iens$n_S_ehE=;u$eB) z)8me8Fe)+z3h!UA79_{7j~E-Uqa{7ZjbXDfi|W=$@XNm0c0L+Q=+Jv__K->YO#7Jh zMP-%+6M>u8c*$J4=26z^RjMz}FbW?Q%FWm=k6zGqQk1xGO50>n5qIjd{x7hzu=F&{ z3e+EzzJFqm*Ot@7Nrp{?eHwuS#rLV8?1yvwKbbWoQEe|=3AGgyu4rSVnI3YB68}!~ zK$f1{sDAWVdH5!g7{jtx;q?W~#Cj64-zL@HNzZtnrN++#iQ`trD5} zM)0P-e-qd_va(=;^)TVgQ(&q=c^3Wcl&W`Mm`8$jgOks}dmctsSfd9-+1k{RNLED=5WqxLJMT+Opdqu6Qx(L38^S0x^Yo}K z+f7VROu`-Mk??jRe{fc!hvcjrDiO8$X}0~ggp?|| zqgJO9wXWoazkVKVp!4#Dcy$^%tTUdc=gtsCyST_<0G{!683R&f{co$zI~TzL_ZM|Y zX@}Npcd2~;a#=a1@P3lR#|`}a`gLHZzu_NWoHv~ZC8I4pCke@{#q*9S2fBmFXTF*& zZqv;2;nu7y-v9n8Jxy(SjcU6-QvN&EJ}8dK(yU6DiV8C;^_gnJ`_wY^2b=ar<0OKp z+nOBV6t_nOR8jjA)96NzHu`=QfAz4yiuQb_bH{g=uC;4+cuGAhFy&;jd`OBKK zQ#;0!gS!^FaX#wh^YOBq2XZ=&QT8hTD$Fl^}s-FUHr>zxHQm-Z2H_lrD~PuMRo zzg}(BIasn4Q8MJ|$owuz=f#KW6xz4`1WEyF6OR6;zRwDlyvf=$x ziL3dmu1a}deN>s?3(w!Lt}x#CskTZTzLAYDtASwj*J+D=i$`zL`J;-S z-n_Y3NZ%P>nwIHVcW~4`I52OmaG`v|XGLg#^lD|Lx8V6K3FaZs5xV@RsiM@UeuF&` zSKktjOP}oJf7L91bQz2T%9%1BQob=MHTk_^~#jOd<_yr9y0ZfQld8j67G7F zN2Jd7zS&Ki@rqjX?gtzFX?)!Dd~qSfUHy-13%Fz3Mc5SAQ@72<%Wf$;DeQv_btt>KG6zFhGwwa1HT^hF7 zb89Z~sNpCnB=-lgY)T~SmnRflr&dFY8%IUhD7Ji0ybo47eJ+Agj)tx|aU?U|Ckf0z z=5*AuJ!-Si`(L?ll-O2dzuU$|<~B0Yd-ER;nI8$E)O&Eyhu?#`(t2z0!MoRi!g0@I z7!s7&U46?WvILc0%X^zEYir&e{j`fC2oFf}kbm;SIkP=mq;eseAze|zQW4b2GqmD!A!4#$DEaZRPvu}6RHlDNN-{n6xmK>{>Z>CTm28tmn`hkC>?b=?T_IKO zV@hMTI22#h4Gfkczb)iJEl{yddOm2?6)(MbXH}FpDYDhXq#W~~O_?E@6l#C-IW~3N z!^GutO$^)BFB*r(IbJl`{dHzw93va7gre$s=9%-r3od zm~QUeHXV+KvGMVx=o34i+?G{hn*;12jv$jt(dl9*h7lk`-;=Dbue0fV%Tp`QMD!SF zSt1@21_LDE9GVc-8pU!9v$L~L;~&5_7We;jHlp>vK%1X%^KLNSsyOs1Y2bR*(o|@f zL`Lpkp4t2BZ)pQo_I1WECktUPbT~#sKB~P?EBg(&Af5#;7z*j__sCU5qxv;t!2fUn z{(ruEodIq9ilRZ4B$l|^S&TSi7Ph)=lggXFPjl$^(Dy2|Xa7B<{%wDz=BFhlMx3d#dPX}VG9 zOQKN&*bxuiZ_J-bBC6emW32r($h6oGXOrz$B>e6bC?anPL$Dp%RU1$zqk6B_$Z1EVUic(EJ6Em{>^ya&oKstLJodbkL4`@j{K6 z@By?!!3>j}w@o{nY0TQO&;+cYzsgMsn&T44l!s=zeJZ)WA@PeK#1`+Gn z=Fu@P^reuxm!H}*AN&OD4q%Z0Fp~52t5TjEH8j{*SxW%yN=;3z<>Pj;Q*Av5D3Uqo zw;8b)tCbr--wcpGu4^gIh^9F}N8E3&PM|mKi_@ed039_r0v&r4WaO!%D#TNSLy;z1 z;UNi$E1+bGe6u!Q;rs|C_{ojxN%gb-yxs$|ggXqV?`n11&CDG~I{Kmt*Z8_ab8|C`Mpc;*P=SzsbpD~Yv8!WDhK~0O&&yN5pdz4C z(Y?%WfRb`v?!k@j6-JfMSAy2OYQ7Q-IX^!S1LWuDgP`EJ))HV;_x9djBqNv%KK4-* z7=ozWeD=`ozrMZ(6jar`GN>c~y*svxHzntlNted_^$^H0R8k3JAaB`06qbh18`fV>XkWw?>c4$n@rzA zoC5WfN#bJZ)>g=OfOY<7�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 VGU|OGIf8+qD5nZ3kum-B{{XAfItBm$ literal 0 HcmV?d00001 diff --git a/docs/images/upstash-4.png b/docs/images/upstash-4.png new file mode 100644 index 0000000000000000000000000000000000000000..ae301c4d349833777c748c260daae33f940a3f25 GIT binary patch literal 37931 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 literal 0 HcmV?d00001 diff --git a/docs/images/upstash-5.png b/docs/images/upstash-5.png new file mode 100644 index 0000000000000000000000000000000000000000..728a374600f5b568e4b0f94d8c71213015b85f0e GIT binary patch literal 68485 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%0D@n_}Gxxgz9VKTk&{Xgxc1 z`m`y^vYeco>gpeWJumAO<>%{STP!XfNTLZzAqpBcS@qH4h2E3KTg^@&pU^4Zx3Cj| z2@jomTrivTC=-o4-@MV^{4`8g6NqoDojX@-JBy1?urr^o)^a7!+>kl5FR7`{F9>N? z*9|Fg7-06~s;H&~)CEV;7bvdyy|Y|;*@{ERF#l$`>R-zp#O9)ds- zj(Pg;BUd`vzJssBKxBw&nw*Xp7eg&}(NO5^Ds~t^4f%~B>|#Of?N`LacD_0|H=(>| z8z+OjgMUE4fX|X&^5~w}2eGlL$5Z+clZ;3M?)t>^egQpx#?B4F?OxYG)Yu$%F1OxX z6|*;y*39bc{EuX+QqS$kE{}hJe?-UOMM`4l&8a{AW?^qmrrhLvoBm@1D(${C7k)^U81}ONxTWAN z-&)t4?3uJbjJw{X?gmXES#R^neLN&m82^0Y>Sj>JVSHRKYH3K$?r7j~0L`%Le>?p- zX#03I)x%D+`xGM}=K9D)ggSPSJYYt}8TlS;`n-4DVEAKePkcRj>eps7fAl7RfD&bP zduMysTg=Ncrx^RlS(0iI2=lpaNQ4xIIZ;*=-C+u_EZZO@8e!iTM3|1j*c@8px>}b_ z!wkYaObW`V#TVA3u+8AC6DKVdy}`=IB9x8F*Snpjb`uoAw2JI4$J;rH?^XgrHqWquz`-JGE%qdwENGcw-h90GBi$iRe=C1cZryD3XuybBa`#{ zcZ1ss7%vF$T3P$kA(F>M;W(&BID)WK(d~(*1Lt2WoYP@sVghFk#)sBiz`Dmsp9|Sx zk6$p6DyA>4fyDn*iS3I`f9*p)Y~e!R5P=o042TM{iTHRHjAya)9^WPO4vr1|Ox@Mb zMRcOVFV7Rnzf=Unsj}R}OiCs1+C%779RCg9HBVo{l6$zx6hJ!gc+2 z2)pqk?|S1$mzJK6`0$4sJbsmrTdxL|NJehPn!A_)z($puK?#nmOkHasrhpPf=wZ+j z(;@;4(0FSr)P4G?%`{HQY=3W`pm;n+De#6II&!4IAhXJtmBhBM%zLKs0I(l85I=pw z0)h;%Fm(R&<4S7pqajVBkbnG|TSnj>4921u)Pd8~wX~X(C)&KoSxY{~9CnS;FBz{aS)0a5 zo^1#5D_CEgQ$NG-HM&yoGjc@EV7~jlNnaU4ITI#DA54)V%ybNB2ehenF$0`LFn|pm z^=-sH?9HxhHwW0l7^=<7I_dj|5YyE%BDenxbC?*VpHJQ3b|=1NLhk}0DDg6CAA;5b z)zkHK>;*KrIt%E)0*1B%+#b-pPd55GyLY zDF?x)1QIuVLoEtOC4oB@ML<$q?&W1|eR%yQ+BX0#DA)}$^Wm}J;4p(N7q%k+U`g@u zhDJshOlb}19*9VO0^);FkNRt)^UAteghmV;PLN^>riW=Bik6mMFTXi9G;|Ig7y$M| zLk_@Pd|)W^-avwafN}935Yao|x=}_}H8h~&`vJdTbaZr;@q_FO6ci(mA3uiZ7Vy14 zEtuIeIR_K8u%5>%SdkfJCIn?Ex?6eLdpw$G4S;|@f3BL^tdSs%ELVU^YXF+_S>i;m zd;w>UEk=P?NC^7v_B8Z%cO7E4d6DGpL8ArXxH& zJiyE#4EDN;A{@}euhE;TP^cksjxWR&1K8l*0@O&*WiWEV8Ojp#sA1S~MHRf5JT1AM z35gUL$PonjfCzZpw)G4ZTKL-mV+a`Lqg*8GF!Q2WJVQEW-sU3(Q8&B}98{o&T7H1u zA|@z-MUjmdr@v{fo*QdUAt_KS zp%RvnmBobF8#jLA1!wC10w#}Q0zJ8xvD|N5|HxbEMZ>;7fjb7WKvUuaw|pK)z63 zwO$kLQz0AoH{LP=oBhm^JFX#Yq3ELdjf+JE1wYl*jRN>QX5y3b95R2s;PPU z;>8bKARrf1oN%m)#|Qo#A`@~A14M&Ajy(cLW6E=sh){BDDnGzLy)j=${AoNF88CHHwec7oQ#1?@FJ@{Yq=d52c>+^K&J%-67sbF*0_Wa3{*YQnVvkk)wkm5 zccMf;G>!-Wa05sT2RvwZJ;orj2Z?#z7cjG&n7Vf74y`W-DpAZLuk_o(Oj8RNEbQj1 zNLPnr`Y#=Ya+HWS!Tc%Ycf1-s^VtpD3f{fV_3KdOh{$nbll+1;@1MY(la&P z05pG;DU8S-nCw z42(-FD>zd+Ee$yRseB=k)`PAF{MN&DZcHG&A!TBY4G=ZyL8=reW@cK)hk+VUiax)tkx>_56GH<7+=!X;!}xCibB`syw7^-3hZVO9a}yw$ z)7I987zW`*>o2hsXnKk^ndPF7%qpFKsJI`)|IDzpo_#SiyM!&(`R5=;!XdLAgHix^ z$gGU#U?T=$bBGx81_3|R{}~w>#l_NqP4RaG4fDsLkirl+NwAIJKee#+&;9%d3$g7N zuu8D9-ObH2V8aUX@&GW}qCL)^FbAE0JU1y|1!y*0WK7V)fI$U&f`<~EB&NPMjLzb* zb%Uy!rO5_hn(EUp*vx>*f|r7C9Z2o~T-&H*jRc(#>5fna^n$ilRbp^7QEmC4#;!^4-d^*~!9 zW9`%#NOSBQ9l^r!D~!cmPK<+}0nG#EPR|q>Ky?blA6244P`w3aA|*)LBa9R&w*-)n z7Lzc{<0ey1ZOA?7xMk)E)E%LMS4;d;p5q##hZbTA!{dS{6wUC9b0JB2YEeUIKOs%A zn`iu+@dS>On;u*!`sRh=n4dp?ViOx09UVO0dUcMN!G;7g$PWlMp`KO&0w|1#f1MeF zOc^Xp?9t$n<&wOx3qqdKUFHE13J%xK2^b~Ikll(C*I|z&>G1~gYW_LY3NTAVM1)QO zrBwbDycS?iwm8x6Lko?ti|Lt}N+137)^+Uu&Ux?>!IqC#4RtjB8xv>z?^K#SE@0fX z3(8NB^|z0g!0QcxGW0T?9ynj2>e<=*aL^fA&YQ4_hbu#1rxs-aDudnQBexnGG3ZbR zA;T*`>{eEAF968n+T2#Sita$Zlb9l#B+8f+p4n*Oiho_2P3qGm6q1~)UUq2JJ?+Zs<#^5P*JJFUDv?hJHD~hRJAT4@6~?G4B>#m zAt5)AJc?-jBS(nw1~4Xe?{%NlX`>w)^`rN)($c=DY(48579O&xg|pEIhFV zwi#peEpg5`v=K;MP>`ys>NE((Q~zQwQAnn<9Rf}VqciFs)?fu@*5C9<#n~42OVcDkyLQ-ehJ*jO~R1U0a(rsY!(j zMMJ-4QDLD_oQ6@qJQXUxj)>@&uU^%v9rt*2dRh{jF0jTju!;zP)jkvr84L~;MfStE zW(4O&A#asu04Og@#Q!r^J0@fsWFprv8JV{Q*G<*bg3HS{uz>+T1w)Det8<6Kaa$rH z=4WQIZ3Uk3erRq!3KUpMs>9I~C=6IGY@*n<>rBakc4F}K0#uZ6;(%h!KRtye6F5F* zLhv$txAZ3?El+vgq@J5e2ni2I;y)kq-G9v?8;qavxZNHPhjX_&V*rlFX@I0g+fDNk z1)*qyLsVQ`@Zm{3-WI~$*9Wm_5{XCPU`wmbg!)qP`jPkn4&AkM9p`UhFCzpzKdLc0 zWC5supFA%(dyH&qYb$_E2KaDksmDA2t>}bsWw0ZpY-49+3}~?j`PL40i5N@L`GDlk zvBp}liOt7&i{&Hva8oBo#{uhym1ceFFY7JdofinKN>06k<_eX<&6|Wka%`e6i`ElN z7O1pHmgjT|&c1hHqjmdtq%C;abQLzwD_`GOUqdI}Ef-;E3`-J_e79?>$TUEGg~J+j&!C+oU~J5F7%O@h z6GN2z)7DN-3-HcEsRn2n{|hk(q|1ZU4v;k1=A6dQ-Mk5>2%;PU(0*23wX%X{3M9tD zFSUtQ%nU%?36uWqRAG7AeDgfyj!dA_`T0}5t~pV&7f%v}eBa}-krCt+*dA-MX>M#= zJEyNNg;H!~-ym)nE-z$Dd@&7>7@a?R7D4|lH^rj3-7GJ{=Hi6;!hj@o)ALeNHgRHy z0xKbl0b79lV*nr`L{XYAWAwp3Ooe*;c8pCLQ*S`Al~jR0JnAFG%lq7r-8O62O-$PA zsTgoj^w)+aB5UhCRE8^^z3Txgdw8Qipx`xmRs`1h3Aso_9i#|Y-yog2>qkZ`Wzy>* z^0X;Wmy|%-0}dA$I9El;NMp}{oSGSAvbNNFkfy;n+ zO~m>=;Mad=A^cqNvbB2#*l`eS`Prlf#FrM^9YS^_s4 z^%XV-)QZrb;8sBuitP#33fah%%L4f+Der8=VI|(Pdw2g;BB`O?0=Wf1GwR}A;{9VA z#B)6a+64gd`A4ZZzx;uO_8&E%O6Jo^`}*x0w7UrAOndSKZDs5>o9VFFimsj>6DS-B z$V74-T7&*qM(FWm%tzPyT#nN!;1*YMw$#^e-5KOQ^eq-w68ja3!uRi0-&R8!R9}z6 zym@O+F0>{%Qo*Gm+2r%Z7i6S0_(Y+lN__Zm`NQF?zV$RbHmFwqti3?>f!QE(oSglL zsC&%&G9zO=#?&)T;yRmWL0Q>4+RsJfu6-C?or|&yg0Ll+hVR_&G}>KsGw>DrRA)g}PL7D6;I-N?W+H|G zr_|WDMU)euO9VkE4S$HHO2$%zhQvqfJ~??rP5dgh@@dJOHI89Nm4>@%Y?E2 z=Rwv!p}o%!v)7eGo3En^BbRJ=^RYGBQ})8uk67pknz=g>g(uCEI1N?3&-F3Pt9@Ep$gZRK480jLKw)YVEM}khO*uIIhY$hJh{xY7K z6QuNVAX!!x!>dUXq-AO}PE0qPal0pCh@hHue z%8@GAco{;lDiJz{=P>#BE_K-TmR=WZBFV_P?feJ|3|$`}2MDVdPHn2z@dlpNA|M5p zZ)33zPZXGBsBBQezthKb5bRRd+=o1}JI0ChH+|QBoc{z=6d;UIHW(T{OBcyV64IEy zXh;NWf;#=X>e27?R|TqEh!s=|{FngvlD4;{+GY zIO|_T5&YGKYz}e1Y`?m}Y`qM+KQw<4cLJaPqwPjgmIV^uBp|(?fF?hJ6sC|rVMBS* zM}vop>Vk!4IELDJXCrO4r=z22YKtJ5(3gIdnTSID3*#QXbfgM1D4JRZ?%7~z5$@Ou0J=*@&M zAc{_OH#vz3iT_maw?Fdh?&^|xvm9+pB&KGx5G1xTF9XKlfKr1#q>t2b(9HkG&z~3w zglm+ScZ(uC?B{M=33#2aY#aMXL>-vKnF_J2*P-uvvG*XFiY8B^kG zq}7ebpi)Tx=NJFe_x^wSka@9AE&xyQqBxA4q!POli`NT6(>_IyADdMa5MJr_E#J(h zNDto-Uq1Hl9KqLIf5!^`ulbOCgLWIqJAs4BH-*=&r%am{zDsD4>Mf)tPsozV6r&6Q z^@!V_y<+nHLhp{$;5^Xh?yDsLEanpySKpF`56%!4)iHuMh3UPnh z$U4|gQBYz(a6YZ&`gyv1#2_kUw({M_%jIePL&Pj#8PPT9kvt7?OIh zw#&<|U`D%?+^lw-v@^uqFD6W)pPxr*j4G8jx{~9+K<*uWl1Kqm@r|&DWxaX@CnW3S z8}H^UOv3_>+1-scX{VF^Uv$2L_6#orUf1UYnt-rMGplRYba@$oo(7T=8C48mY?aPv zpQk*TK{(TR(XMuFcA>&`XWBk~P=Cl1$u``04X>YPW`eG$G7bqOJfd`w96`zTm6MSs z+rSSYb3pWhpZR}=@|9&)UI<>4ZKxWY;W*@Hj+}W|4((!6%2^$x*X&}*NJ1a%C0VIH znlT|g;fCa+enBNKDJlxC>tTCHW)_t>!ejuOKPypbc96W~NBjCn$l)}%Ah3Ol@&AnN z|IaLxh$>_6GeI19w%%RH#=(M;{b5Y6@58VdqC!TD_M#Zc%&OhZECT8iA#(-XW}pS=*=H2Q;~@#% zAFVF7w-NB%3iA~vnp!}Au(zSEQ96T~1#mV`U~3&peaO- z{eFMdC;(97V`GJG3)lYnr&L6E|m0+k@L&3c=&7Y6>XJ=<^H*#fZ1CF!P zbB;thST}9Z%15sJ%h- znCHkV*qsbAZxp$M27)LPSREX-+{oUpAHIR)*$a6Fc1q?f17cE_(6e3f=?i04h#cXNHFG zKr+)PqlHpPK0&3KB!{54uNJ}+z2)Ul?d`w&G4?2zkkDtSoh*bQlLjvOrmT!@3i9Dk z_zKYY#zwdZAp}AqXc?|Zz8TB`oSc=Noyd5ZA=VyHCH6DbHsY8N1?MDI4vfAH_$drk z<)$zG0<201GGO#z=%k0i&_-= z5-5n#b5%V{CxU^1UDI%)(JMs`KV#@_5FP?T8<6jyFUd~=v_s^KIG`~kCOZCc0j0vB zb_35B6(>YtDDiM)>!we&eO-lCv~LgoGB_F)Dx#srrTatuXX71c2b?iTE0d5|@T^my ztwx6oTt_Vmf*VX17FV#sQb3uuothdGuW`X(L1MY4F7GA&1a5$H=g-3l)7INtURt__ zPT^0h7`>9B;%CioRGdXda_A^JcTXD@sy7bK6t7+00;|scyV$cE=l{Sgh=a||upTlp zjkJl)Rc1o)?zO@5>eWE$rZcY{(470CZmG{oSbY;R7^(z%faLe|^iari_@fV`r=@)j z2%@-_NUR+mzD+J-eB7|yQ?{Ev9nI;XXsu`H?xB5!83GUZpUY@OQ&Uo)=zWlsgvd*n zvN6d~sr(ltXZ`)zm}yXa4%h?;H&mHaoS4YY#N-UcH~E$wGSha*+r@M`hz;av7nxHi z7<*BYoN-}iWTZdDmoqx1>71y>==>0+6zV(Kogn- z{EB_S$G6`o!i3nUh!0fBz{(yGaoI|o2H=@BN2I!gT?+`Xv`_z?~nQIw!N^~VV81SJH5ycmCr#}co!Z3y)hCu4kHF?@< zaJT~Z>Ul#xG&LQEM13+5<}c%1t1|`BKz6X|{QN!>{rl4Jo{$u{u!-DXTB`4zoj?tj z@tS+kVpp2s>8^vIVja8}7Zt|T58^0abd-Z-)$ukRMUoGUf~(Jr2(2^VFFQ2GXPDPI zG7yAn?_bN#yb0VhBsWx?FbM!)@+W-0)YSW_d^v&;nqu@0Km`~EUk>ctc&_Lae0+Qs z-7s4iqJ-n^@oxf!!`^7hYB5u;E3oROKB?7c`2Xp-vVxbEDw(idQGWTaY*&Ss88Fla zrx2FN)6`>)HsWwnmOOq8m{$GiS*!&h$|b692WY8qiMcu>p-&xLz69V5YPwT@ZC5YR z+S;o;R@{$(MF!-)SAeF$X^PF6aMZA`yu(0*^z_48_a@yMAwa;o{o(}+-N1M5@Jchp zY<(uup1J%Tq`RR$1o4ZA0vHYvDNI-f0Gl>vR!4$I?aM+8Xg$tR+dKm};}=00;t35v zdasO2l*zr6=lE5i$-%2bRzbK%7X=S-aFD$E@y)RW$D$5FA$B)Z;J<&rGiT#ej2Z&m z@|P3>#|CN~7ol*I?7-OEa^g0e`T?{^hK59aQ7k;-+sVg(7ShwVmDfs~U{Gpqq~}&c zxiYY)``baas1#mb4%Bhe(tgFnv&!3vXO%2mF!g0^7%kwBd9hOVr)MVNe)#4I_=6UEBwX&s%sd{@9YeDwC9wD#b4)JbQ5J`lLU>hH)qw zr-jijobEN}9l<-W3)D&{OoB;aP?~Xm3*>`H+J-8kYOZg+5MmB^Ke}w6Fsns@S}NE$ zXJ{B2gDSEd>;rLlt$+Do@dkC?A6ApO_jW0)1f9p0WP!Ts`}Yvq8;*|ojR#?Rg2P3Y za$?#X#!W!|%E-Vlgu{Daz;`A8N-2*Q>?`(2iHGOnux>^yRzrMaBE->PQdf9;%rKu} z@pgQCCt?ViGi$G>W8v1lyjI`OCCk?G_39kJ9qealOQ6^ov%D&o#LmndJexa1-*xCF z3Lj3E55VFG$Km%XE%{4v1xnaj4 zmuS79kW5>UiMMsl&ugp|Y>mAVb+1JSw&AAt@10>e2PtJFI3*@DxNe5gkfM7bac;og zB<&JwQfDQKX9hQ!jb$oPG0=dQzlCW+} zoBy06CNVmlT2-d%XVdlN7+axai)^!-^p6KzTy8>v=4V?k+OX!|q{+P@1 zA_Cg6@ThzdCx^0ziZiF9E0fLJD;C1tn1-5txuAy%e=~sN9ewqVfm+GTj3c6pF#EPd zeOCJ#Nj@41G97OhcwN^AgLCH;&oDsy9G>m08LUW;B4YjHePt@mIAJAz#$6Ogacs$G zy*O_pehl>vp$Ulw(u#STkK*SmK4yS(!wDFYf&(iFK`Rs8_Jg&2>qr^d>I zv%=uHkf#M{y7);gy>-Vq#!Fxg$`;-^iBKhDR5>hiodrVhM@;us5_%C0p00q%ZD6<~ z;@7YND?b|nrnhw$kId_Po{Bl)9{{jBo6wb-OyQZNqszv-0RN91n^-dnZ%)f8u@Mvk zz%600Q#urlu;j}5Ol-^GOq>i&1!Jy%&kbJJfhDfQar;_aW{W^hF7j@jv_wTkahJv8 zG5rf^$BdwMap9l5diANLMTeWdlt^(+?VQgMj^h^-YsGu95QcE1Z^w7Eh}c=mRx(#o z;rJ$+@R=FZ$!HKDQ^DlKh~Qv+Chq9PS^%#z)7CozXR)aw^58z|mizbpZ{KbX>~%^# ze6QXJ2_NvHLVpMD6NA^Cn7ZjR-EN^J0htFl0VYF8rCbxwF^1+Lo!M1PX5%R$r2+%% zAQ!?gZl!X7i}uv5-a>oBu_F{-AGq$d5!`!-_z?a%** z766{6)~jNEm9b*h55QJ6TXliahD--C?f6C@2F?$6JX*zp=mp<}k%9jmSjh3awL-^s zC*DF##K6SZ7$Sj+vE{B0Y2!`<+y?eDKK=++-f}6iY>2J%3^WPz2B=P)Hkt?Xwg;hX zLWkP#f8_0+JcBjlA@pRi5N&f|<^ja>V5_1^u0m{$q}}7p%o5f{Y5hzGJ9T5wpS+XMrIzZ~6at4d}Dxkow{22{AgV}mr&CTX3@~lo!NV!g&u8FkSuXYxy zVogihwG8g%>@)dx6_c0E#|~<5pXYiOV573Qu_EdAV}+V5lAqA(doq*8B7KUf?B5zP zk0inDYRmm;@J>`MGH+qA##QaZCYe1t9^DJ8CHiSui8P*^m5mJ(f-l>R!U zHoa+FKqvhq=X_PGo)deWO(o1d>Ve`*byLe(mip66Nm5 zIu`k+Cj@fMdb_|2UKBD88c15 ze}g7h`vu8Kec2cweSGJO@jM}@BWo_m+fY`)B#Dcise7zz^N%A|A1KX4Uni;0uBjuJ z3}JEsg+e3_e+yxKAdCM^0!^aX9IkwlmZI8HsM8l;JaOZ5E^)?nw|f%EbqSFf*%FpW0?c|kJ2#*U+`XI`ue2ChvuX6xFSRa z3du|rjeq?CmeV6!nHGKgoL{;ID+>1+5=%1RF4XJ3r8FOc)*)jAcQSxODE&}G%_R9r_fAwqO4a6&PkkgEb%i8G5SL^(ZYZG={%D zd#yN7I@i9p+IIX-`yEN=!;VL3>mKXzi!*MbP-ysQ^}uav)@`HkixAb7T%joC)Tz<; za;eQ96E2Sk&n8?>`lCDi!X2@qBo~PqdGX!HROlb&WZ?@e1hWYUrM=Y5oec;fMjvpMH zDbfQfx%#2_UcnINsjJ>`PXdQdjXwzrdLFQ7Zu^Zho-Bmw>PM1DQ#^mKc7#(=$ReGs zpfdj6HsbI7+4g1Q<142kUwp3#@dv4iKlqc((UfPxIPui#9q}0IH_T?WCxysAo)c_t zyWgorp6>98b3Iw_@Jt$IZ2!j}f~1eq`}HQJuDhz1RnP0rYz_+I)yP$1@v8j&-Ar?@ znR0wW)xDYFy#Y~b;@wSI?YVbX+24qiKcMw8&9H7x+pc8p=*h|%8>-m6aq2c};-$5* zz7sng9=oBFT@ZIY&l~1Aijcsx%Ui8-6d-_N^2O`-&IrlO? z;#)aOSyO#rd2i>`=OQmo#^&D8BCTn*EwNg=7FFxw9UpSKa+)p&l~p_LKOC<)P|o#> zQ8+F89O+Kg>!ekx5x(0@0cy8`hJNWMG%7v`nq-{SwGcUB-ePcBkR$fsz6(rgU$kzp zr_H{J3ohdvVdsh`qecPVVql$lF`H@R=;*GLJ=B#u+l$UF*3NcTt*dLWH!Ws#bepo0MtkUjqSKw0<3@rJ8+x<{>IeasYd4ZfCsr_r$Y zA(=^HWpya*@-UXVzFmSgEa(;2oVVVAvFntv*YFj6ZY_r`QZsbx%h&p+f|(zFp(m@* zCVpbr_CZ#*$d{-`;qMG{^`)O4*yKG+tp9{jeJ`&e+ce!fuG$$la<7nU?7}Ou`NLaM z&X$}L7xaMo_!G@>h0Q_&*cx)~@+?bts906om?*41*Nc8GL9QW%Z|84q`g35oTDh$9 zz`IDRoV~{sH)#mq^W(R|hSF_DReNq%M@C(z``Jj+p~Cx0FMa>J`|cKBx--$AtG>Tg zym3iHx0CWc7x9M|ygIs%c740U99}|0V{$p7kvZBASt{dipme_}%PDd$bsMLClD@BB}WUhig(_6$Qs}29dJS zO{CJ32xT0)+RPI|E6c5WQMa@xV$o7KXv{ zUEixBi-F$t%au&qPAzVF;7gdfSR8T&{1n3DD<5Cdukq-4_ItC65P6061h-fH&T5^^0=*U!CgMh@dmmd6#dy|F=T(n+*8Q(W^N+IB?&qulF1~!J(}K zS{9x0Ovq6djp1!3AMq^O9nF8!qm@eYg>69Eyq7zgQrWU|9uVQh>~@5FRP|3kk<*oA zg;vd;pYa&7QpVMWIeAw8zKd!9F~bjQYwtV7O?g#U*)$$LjQ><^7r#6~i{dIEi1;u&#oh5CXHR6!_hX9!2wbuX&PafKNc7l3)^>f{rFViXdgRJz=DB}j zRSv7WxCc*0sowC{ZlG^$PH=8L4LCwVT?trV$@a7vYgVzr-vmZ!Z zT@5OeZ?|x)Z4uc{^4}{$GJ3L0qxaH+-=pCh2FzV< zUw{6lAe)72f69XS$!*>WkWyd{Fbo5$WUU%3-X~v_#Ez?AB*f$?@ocrgWOt>0@Fi7!~qKORDDaf*t zUDyY_k6>f=9dopJh5i7r3i@+|e15X8gRm4jf8ZinNT*qGotgxgeUWb-WOxB0CGb^3 zA10S{q)q_2d8NOd>kyyTcKZ_Bjp2;pwTo8P)^7c`_tZMa;5$!yEw@C<&bK4hPH?pa z1gkr6BNY^o1hE4tVLRIUJ2}UFvp>Ec&F;2wAl?#{$suGZb*HlvA-8Gq*Mw;=MrN$?iCgDjKdB;MSIXlXcMFF$C9a4OxA~3ydw&sYZJ(5E^m_PUQBrc7W zPgA1&23TChmI=hatjrTg_@HxulECcl2~c%aJ!bD;xWgle!FNl@Y?h`E?t*5aC%P_@ z9j>YzCuz7Ig2w=Vl)-gX%UH$ssqFnlDq~A*vJBg@x`kPHod4$^mr5c43lHJeu+^(J zHg88BT4Oq4Hi$UGYemC=1$r*ez`F(#>S2*S-V8cB5N|C%{zQ8IN!^yNe=oDBC@u)` zibxeKhlF(eLzm41wc(HFbGNxXyFsKroX z<3n~HU$p}m0&puJ*UhwDdg7n7Bf{^9ppa|b()*WjDGB*|gJ4Mpq^cNs((vQs=KbG6 zG22*wV>@-|GLqM@xGf3`yHQ`V(+pKy#*Mj4 zVf%;|9=@j->P5JG2ycX=IJ>mNbFfsInF4iGk0G>i;afrK$3%NJ*S*5HrU6`S3{kQ8 zE>y}wdw0S5;zcexY-RYqOyiRIyDDrm(qSTX<*#o^*WJ`d5F&=m$9pv1d2VHEdmsh) zA12b4_aW;aQ@s~GuK!~t#=-XXY3&hSK@}X>f`B%;{4m@-VuovGx;_egluHOz6 z9vnK3jL-d#_beL?(zSjK9q;WdvYx9c>4>MiwFLVYlhjydmuN$Da;iW zQWh!I!Wfm%xJ&iYeq`=;0A&U*F6nc@eLg1%lxNbB>v|>z(xWvoCa+3{-aKv4eI)6k z%Xf1k@Y>S7ruCv;;f8mf-_(SIxQ~nV%_yKWJcnq2j&clNg1ACVRJ1)`_SMlQsH>nR zn|DnOwu0*dJ06i1j=!bz;wxTesZYsC%l+Y{wU4@O9~pw?v|OXzJ1R5MaZyz~Nyg>I zCS28>GSd}!n>l_(FJE5$nl_HzXM78C^82e)E@ZcdOJsOXNBei3tv-_E|W5}jWh-1uO#KIxR1v^M*_ylA0Paq7kE z-UAaOQ$<>=#4Y#vRg9!{xIvMpELun!)l+sGexTL`vaBFCmtvC-XaB>C!oRF$jhdhm zc~Uv|B*+SX1g9TigHR2=oTMS%qVz=gQszYKmoEy6TXwKA{dj;eFj-ks*>&qKFa{xI z=e(rRa^!}?5k4Kh-nO!(iQ6&8blG2?&hDRacE50-{us_bjXaDnZ;BInhCww#G)JOv z*W55!HB{}=^+z!hw+_X(y)8l_Hy+1vb6u&hb5B2+Y<+|HA`xMPm0y8PwQ9##%ow(v z_vMt@F;;J4#$96j_Puk5`_X5|L>|l12gA2>eD14GN0t{rSP&v9&Cg@LVar3>pP0zb zwBH^x&L1w{o?PmA@bKr$pi2h_JYa5Npx&)2J{yV2vL%zn*2SS@BoT-cs}Lt_e^h0< zcIA3lN$X+X!P%ae0uNqy2|y7|M>%?1UR^n?q=45W|e zOl10bAr@mCS9A5t7#jCyN57k}#`B8r|{H625!5s&bzHkd3KHB~#yH~}$ z{FUInQ}4zlh6c8U0VMOjQ1h{8Z<{*bz391_k&U@m3mFdw*;~e}C(gc&jNEs$*iFFj z?rZgXRwu2W)?cO#&Xl@)w)=|Xa=3(R9#d^dh(n9f*7y{`6#8)Dm!*gnp1$uK-ahg) zG-kD9Pnku#tZ2YhTd$q_yjsHM;{>zMTHI0maCUf>m*Lxk7r|<~uJjkCYSp%OdMxKo zIQcI;+WxjD{Yd1KYX&FC#45jwU=fl+?zIIqJ_*`xIUAAXDa$OoZ$9#DT&tkdzN1>~ z`9}19eAZ+mt$y8Vv)WwXY%9Cs_zdGd0y0zz z3cjtEE&n8_@%Yk5+o$qe3*SwyJ&as+;%L@XSL@jS?CJ|^%DkrETgW|=`VZD93{BMY zD~zM;3@~RlZ=j#x*7>0*oSpxrNHoLA;motlL|NOtq%vukf1!)39ugNV=8o{pt-i&3Lv@mhUxJZ=};rK)i! z&&y9;qlH?HuADc1vAv4_u)u<$JTvRNVa*GljOIOs-!>?)@&)^!eEL*w**&vV^09GR z(%iDay}{?gz#~YH7lpR&V)Q(WVe0OD+D{xMUJB@9B+nRQ0;ES=4kpIt_M9@x~VAB~?Xn+%}$ zdZj(?-WjxTl-Z%i?VhgpsNx8dI^C81`4mIkCHj&cWiB|n;9wo7rnr-bZsJ0<$uZDvX#P~h!zqzhl@AbUN z`#$$`e?Ry4Yr7#UAMV(%xHM6Lf#b1U_`%Inhac?lt~lQ6&eUK_RR@R)6we4Mlvp)&Bz_EFS#&P$(Y+s1(s=QC`7bYQm} zGH;D~E%)SK@*f1vUvOP@Auvy#1KvpzJ-xla&|&NER^mh2AW2%M4|;EYEb(@Wp0W-z zc4+zCA=FZ_Q?@K9Dq|3ec*3#k#%*feGQ`9$R%6}VpkC48jO)#HUCemw%-Qgn#f@qK z*qy;wR}& z%YtOgb==B-6_|I{;%7Sr_cp2=pC6Y#T8!BSA%UT@^%tMqhHu5evbx49l=lgLT$xJg z22_MNKzz`~)CE5aU=)u;G63BMOReV{sk^8|Z>;jkGM-PU5B|pyB4TrzM>;unyC%@` zb|DK3+xuS`0ZIl-D-U0X7dwC93N5Q^YnpaUP6*n}U{xEHCI6|werGlLOPTXRCWClf zah2D6sGOCKDj;|0EDRQTPjTz{ME&w}PDE z)O}l}xkgS{&T3u#1-oH4Z~vuOmGbsgMLwb3VJP#Qn1i@sTSf}Q*}2DL-OeZ3n>*cD z;0!0@5r5>2bqa>q<@LbG0()lRCjLb^Hy>1$*eyar>9eDMDzLVm1fUqmsRd%Kj6xamT&UDdUmk{WM=3HDig z2|8t~!Aj>3Xt4z1^LTpYLZPRp*4OBsKvTZ$Zvx%C2pxEE#%?Z z1KlonThRt)mx>vKlT;dYuyn4ZUsMo z)`ET_5!17AxC?53u8G1P&$13LR>U8Fu4Cw*pqgld?!u5-RhexrbT!LiQ zkX&Qd6*jJhdC#78B~C%anFDJpAIE5;@1%2R$_C~Zb+IpTcP4P0X=cIaDn>C$t1-x* zy#WvYQThU*Ig+@mkLqaTYW@!PrMdprpY9Ou@YODnusKb~5ok=8DfMkpFZVEX5xSMl zGq5ZqjN{I&YC34@%;WS+^gmoCdVF-Wc9#kw1}nM-ZyUu>0v!ULauTgpyr8C8m7$nm zM>YDUnuaE&GMH?WM_ z)J4lI;m<^FJeCt;+lhLxeOc`7tHl!X4^G>S$LSB~qpx15w8*&b&4|W&PwJ%N2&XAX zcy+BKT}YF}eRwZvTDIh}24T1pluhk=nA0|F!<|z;&)f52u_}~-=OM@&Wle@j3#^a+ zDWM6Qf9-y()ds8KZ4RJS3#5|wp&6mO;{mok^N8NPgXwRdtQ5cHX8$T8FdWaq_+0s*rn$~hd+qRuwupO1#r82F z4*2NRaw1lmYKGm}iSp1*9)z|SMW>$NBd$!80@^AdWe|pjis;^Hh4#WALI)y{ifjxY zqy75buvcvbzH%W3&pp9z#G*Y6DdR=8i3Wkw>jk^Lpos{Sm7;AfPvG{)Dp7KY`=JI9 z{PcW#WXw~VB;y&Whb5mE`_p!!LuyiuMQ4m6-rTJ~^uDDa_@2>{On;YsIwsI8xNo36 zXYFf^hjwW(`P3(fki#Bj+&!&(LSI(LXJ8|&Os8jWro-#bcm=f^!6KMe`vxtFZVF0% zl&3HCg`d-ZsnOaB{0wa77O%uPGWF50fffx;}e=xVkq*$9Z#oK4t~(hqx-7$LO%6*tNJv z7JsRyU_aN=Vf#E$;Pcv4TFlVyTFdUa#$_I)qt$x!!a{YxE1uNEU#G%B^{u{>Ich8{ z+9Gb~GHC+NR#I$cN;q=ss_-#vRe1ypQXnDqxgt^TN2W$TMR}1vv+94e5fvr-)Z2-V znCYxmmk1(@9IpGT8BuXX^yqQZPM)BHwEb?S4;P%3n5jP)%c}y}oMoYr3w4XI-}mf> zg2G>ZAG%B?#sv^!dE32ZY;zol-jsGk3OKdV?&k}R1v(ryk1?22B{T_C2HQyosDUri z*Pt!2eeOSM2YGUTTn=?yGrqE>K$z$WUpA-BG(U=06&scEPx9pD24n@lfuRyVR2F|V z7cO9awA(EW*t_ttp89DAdatH1sZPqVVTjf;b!!x@@2(H^4}D$Z6rOHmBu}U|F=8+B zGtTF@W{lVMsHCu$&`UDv*Y6KysBE}x0m?XVF+0rc06di}rxJ)LLrGObl<{6Vo5a=f zz1Wz4i%}Lrmzvhc1*joQ<=Z{dL6u5ZbY#y$d>|w&wtDC7{91zNj_Gg?M2$JHe_JfY$?txXODP?b4@=8#R^R2?n+VF);=3EG?E>O8!qq`4t@Z~lhzJF4 z*CrAOFVZWb6o2VBZVEhZp4utkm_G5%TOk=+3?i>VQ*2TckV6ERV!4Kr4@nS0?M2hY z*6W$;NAwNhdK;AbT?v6}9qxB#!4Lyyybiz-{R0AW);7WYGdVqQAg}84e|JrnO4h;t_+dRe?AHC}2>nf`-aVLs(&jHu!d`nA*#DA{GP3 z7QcJ9PIUgUbn9V6_hx#MM9Y-cwT)NCqR(rafOU)iL@3qYSaL*}HTKs~5KyLBEb<3> z7d`~qvyvtPgf(_@wDwTW&SCTde4@6G6n$4ly7Q9jUb?X;bxPBkn*;O7b+(pz|0*N$ZYANri+k7H6^au22BX1%~tSd8?{;A9<_ GfBp|ed?L&M literal 0 HcmV?d00001 diff --git a/docs/images/upstash-6.png b/docs/images/upstash-6.png new file mode 100644 index 0000000000000000000000000000000000000000..0991c89cf80ea13e7c9d451219e0a4a4f21d46d7 GIT binary patch literal 67791 zcmbrm2UJvT(=FKM1R|(J1#OaKBxe*`BqKTJoF!*akt6~VnvA@NiPEBb?(#)H?5W){*ybbA6wG=(Mm8j{?+(= zhh+^P!_0jW%n zsejL&&9|7nYV{Fg3!&IDQiZzSjz+ z=-G+4h>@7CyBo)s-o0&}kwMkMz4yf`(np9<=?9`(TX~R$hn3AMQ;s(LbtOj2FYO-^ zXx)InKTj?gP!R+Utw=ji!=KNzG0}R&S0nOaH6I?`*^rHR$cv;9(+rO*y#7 z9r`L=w?!wvCe8flOP8e(aOt)#l~uDsDBP&UxnshYXuUdGJf!VzrKhK-qB3-grSi>m zLm-Oiem1ZDd;-6-_42yL>(}2+y2y*wJ$Dx6YP^eTIirlcH+(3DSNAwA28&AJbiN3M zt8hBmwkRg|Ikd`z`+C(2|J$p-%L>1L-|!{U=5bu^$MZgT;a?xNxZL!nOK|_U^Q33# zgxgC0+2dp(Y{x*?qhvUki8e>$b@EOnaK`gt;!Aipwo%V9a z733P3*R8XbH0gNJ@|MN*_pD6Xl?S^+IyL(f?n$omvCb2&CX>~kn93~)M@L6i;@_37 z_7`tSN=mxV$C@lJFOQCm2_MczsA#(yt^F>mmp$H!uF+Icp+!rmV6+pXqPWoa3GdxI zIbHV-rA7y4WM{8b?N7wBn{*ngT!~n%K3>x(u^c()&N6C?8V73y=0yf9u9i-Etyk^i zTz%2+jC-{n?7Y7>W}|Z&Ohex8@y2?hQcXDH+c!h~=+QEJlPAuugh8p_zM&{+C@Cp- ztY5siUL|>@eoQ;r$EAjT*=DvR7wO=urt5vc0i#AEK%5AqdRr0RV za5kp8+;XG<7QJ74jpBE?W40#qWU0+0c;bOG0|SHe-{E|N=8*dg$|uJ)=k6LBV+yi@ z;7d7~Ij@sn^CWimboDGb)EqRj91K-eRY|1UZLPfjg-%Qy|NZ-T?>$7y6dU5wd9i z{{34Zlk;bPc2?F=o6Z@*(Nli@<7}PNW%!U5^AMR$rEMEiDOJ{YjMr}V`4Q&)h~j*c z!hL^jyu@k@9_(>+&B>gJ@G;5xn(r;5<&~ALBw?0_U*zt8^I@m`_So*AmHi^6SWFqq zZ8PD6Cls?tuPf@lR<_`0V*do41i$_GZS=m@1NP2exFnpHGV#tt=>xtN?x&RHX-tHY zFR?#YCP}Kz`qI9B{W@k{VWsceIGtNMVQys=Y?|_lDSSO)A#?*1XbQf65;|65Mfufg ztOR$RUD$RSB_}7h*j#46ur{3kB749uZpeP26Sp|Kj(>qN&wjo=c0=M>%e|W~!M|CF zaUPmkSZI{l&CP@g%b=P=DRh3k(VNbq#D*LrBO{0>xr+4THQwirLt4!g+_6&dm`*b>v1ymBWez%s<0{x1G@VTW+y&*qOGKZcMC^0nUB|80*q8T)&5 zl$5mZbg#7L6rSNmGdSwOnPHXoWOw(^eHokK?|MtUskyni`6}VDu_QA;6n~r_CY@(y zWDpAlR9KIf!|nMVEev6FYOuSg!r5k`()ILc_kDP&5MPPdO|I^$R^XPtjGCsmBCSuYfwdCluw^<&YUWoR{8Abr52I0!lyf6 zg}k8^3OG@9_~uz0D_UM&9)*-^s4X<G^QenAmtAO9_roz&{AX8+}PBWl|%F4?9U4GYApX7=oM+=O=ks2?b?N>rv^z-vu zhaHaJTywf=1KWe4UzlQQF8LT0N6f< zdd6OFUBOLP26MABGOq9T!qa*Vj#bTEd9<8;B0RJ8S)mM%)!QAA$ zc82sTH-5FZ=j7&gw6#^7pB)o1sVlyIy)nn)?Ycfug$xU@nKUsr9_;TY4sL=Z@dg~= zcz@j+5`#*Ps+@)fzraVDU)7$+UVDW}o^Z#6L34ew!c_}j+#B>a`+xHha!-2L8yL*| zilD7G`B=HxMCOl2;RP2Lc?H|tV{7(TQ&UsCO*JpR(d_`fyH4$8`K-*acUjJlhH9#} zJNYY|*Uw-zB$Gk19%cQD3dk21JkCxIH`d4bo!4?QGXCgn+j43i2(VPky7#3?gM&CM zKAK$bs3AA=8;Y^6Smmk?a;f*johrRu!-MY|x85am%sA=%aORhmNA4Fe9z*cn=zAQUU+$LZ9G21oc2ToU*Ck4cnxw{1g&fyqzUJmYf3r`{YgBYRIs2bK^kVhka>?Lr z^B(vC7d!daJpiCoACIron5WYRiz+B6+!Z=_xHjca(b>}SVDtA2M@Md<&4my}0Gh}?_Z2?d3}qq=>M=c;p*a6xGg(cf?V`84JOEjl zUwDV|vQ5?QAbcKD-C3yjiH~p{%eygfm-&wP7w0AC2hGAIHj~=0HwQIrsPj}~98P|0 zL82`*AIJhQLAxxln*Fp^HxGlMSZ|n@j^`S&Ea}e4+c37}@nUo`pXWP;0QE8EiqqlN z+(us38*sPZ#TF{|_V%BOH-pl!h^KlePI!29^k~(_TO)g5Ysnpu#|DWWeqGHGBr}(b zFP2!ZfCrXLp3HM&({p-~S1SdCOH*_6tjx^AKR$Ts>gt2=q`Xeu&f3&Oz=A}{)>)rn zuSW&Eul>`CW;lmnyt}tY&hL~{S$UF~{^bZZ!_xL)fLN|F0BGswzF-Y^RQT@h?kQMF z?2o|S^^OiK==e1JZ3?jFaT9kn{@Jy6lU{$4{mJX%g3K88OH}$@M^((;*H-G)`wb2b z1_%vLkvXHT`*ue$Y1G9xYH4Xr@t#fxvE=F0c$pU*j+i9nLQrum@!qJtmaJyxk$4iY z4sV_BOss%2U_&J~%-hs9uRhT_g3GTAVernA7MJ?cA%%#H&%jfGIDEQ`@qWnn_(Vfw zF}n+}0u5K>?T8LjjmqFXy-vXCtpiS_F_%Gj6m9mb!iVk zHgGXzub8MLzlU(m(urxJhXCL$Ae>;KOt~XNjr^0QAX#Ez41Irc2e)C8*O8gfJu9xU zlf!LptI;CcnI`PUaySE2a-h8f0Uli7Xem7@WHi$YkU!+#4bSjUa=~)1(<2}Z{vkQ4 z`O}c#Zl9)|Xo+B6Gy^;lXr1N|3rI*vz=oa>{x$HP>CniiC_*A4;oW`(Y}kErw>E7U zfKK&chmG|}0d>)CEyQ~Yx5Z@PlPNr8*dk=~)ycEHQiv&s+Y3WOL)$ODL2~sxoQoc} z-p6Jgx{U8tRh}+a7Zw(h1l_q##2(%G!()AO`5oPiUD?4}tlZAtUaMh2W~PZlSy>rn z?x*R{jff{Knm^v4xe3rZg*h_E2kNSf*k%U=UwlU+{@gp(E3; zl0hQKFiq!iM_5yx?=6}Lae6dB6tAscw5XytQ*elAZL+4OqQV`Z11#_q>kVDrXWRMB z?Pv-no8hl8*B1h^@Q~l>Uu_)YQ~tr8Y^e@j?IvxF=i;q@{83#Z!BO z$+)uymLZx1a|A5|7kf>Q#&Rn~!W$8fsi}>Ag;S>_yg518%#2-m^uVS3-p|Vqg}ns0 zx%Yu|P$Ls%N+J5y`{U7;XYjytzUkO08;e~VWR3ciP@%Z+@ag?y3fI}NE4K(O$IA!S z_t%H>DY>~z|M};U!xosYX0I6I3E3uT8mY&&v%BlL)AK4YPek=jJL2x_8=h*&tnh^I z%00S+C3LZiAH&Jc8{T(^0-sx1U-w>0jlTHqCpMq&X6yKVDSwXgKI%)F0KANfyurHE zjmAaPIW|9Zu7t-5`40j`VBQtokB?$5JnO!B^QIJy!q;p>4BuUEa(2Yed(Y_WrF?XO zTi>~JnH?zlTX-J=artsB4iM%?*NuTyUwF<6Wc>Q|_awlupTR1jw1QM~wBOCHnT%Tm z@{V|WoeBHK=lI8-)oX2osAxC^0%iIX`>~t<=a0SrU%%4-{YS!4L~t2at}@Ne0>-ui zocaLNSGsCQ@(jH)Ymt&>(TiFGR>5#+zAVS9cm&rWYby|sY!yiAYA&#>i%V#9RWQ*UfIU94)vUoe@a9}FByIZJ=B%s5+ z-{!tI6#pC5AVE7JRLV8bA0H4D&+;czmtRROHWy@lXv}VFW^o z3GAM+iqfyO)qGI89`#0(ReR`Wy;{7`N{hBo(5|WsCr8mR4^pis;WehDxrc030f)gP z;Op7`>jg&5NcJ^Y2SQv_goC73szFWYcy^I`sPXo-S12kLRX!d5m^Pu6ml{P~-+9}t z*$ME&s*KLhx>jdb^VMhNDpt{EZo5B)Oob{Mx$2astG`f@rNcgnl_9{9kzu`SI~a2N zOsTb!OeN}@-_;m*by?NA&XuJlJW|6KM+lFq=vAr#l7dFT!MqG2SrxzcBYifjq-OKQ zJY)Z`$L9u^AP`~%;Ro{tUbS1inN!!?Iv)yIEm`ooPP|t8hYERf?~66h$_-&(_9TG_ z4PKpQ?O(?3vP5#~6YqWVbI_z!*TxLixHR(hp;#J@N~kD6t5l&=_b{a0`Ng7)rGmDCkF^a8yv`|_7GvI!ir#P2C*vPnu&W+rD zxh`wPZdQ}^v%S?W@pj?w`J+p;?tvK#v$c3K1W(zD?G9M&Cpfbg%nqYVqFy8=hT1(& z>RWsM6NuP248`RBQZFVXt66H<_5~Lr=_Am4WCsT%z?+&|*LE**Q%9t-i zb1nzrj9|*yI}3z`bNza^@b4>=M(g_H!7kr3F@XSSt6KmG_17uo$Y!RGo^K3){bYv;EsW zr976e-Dyaf?Wy{-9{tz=IrCkES#10kPnEA2|B{#}jiNNNx3$ez&n+2SRnQn6rm10w z51r`Wd4Oa$u@Z1VRc@85wSF%RXtA;4yT`RGa8c^)BI2#8OVz~Mnl+M?F8)R4@7~_< zw@0F!)Dbl&Rk^sh#z=itEp6@PjmNsSZkxq({T{8qIY*-&#hhG0AfA#^$NMIDgf;iN)K&dSi%)T2z9txDzd%BPSFH({=@PCfs*j!koB8cjrptix8M-QfGJE53Yl=wO~QN!qxa z1U>DBZPq6K!yu)VU2{^85Xb}VV)!GB8;WV-WCnXhUR!e9BbL_4X~t$*HG2t19NXI~ zhkH?l3yhr%Rs9KtGlrhc;-V4kHfxoYe-%@QC!Df;Zdp}$?z+uPU~ApK8di1z#&oYV z5VEI<$DhoVjEG>W($YfzR(oYP3r{a;o;UU-X$wdr0)Fr%+s-{5dVQnz zp4Bh2QZ)mCi0vbqVgdvrMy}4p9o&G_6&L+XN)DUwrK$pWhYj*<2rpujjg+jMis%jD zcznouthm7i+G>ABK0$JOyW~e9Np57M_$2;CVX^NeEN1lhY>d zo#s;a-#ZojcVBqD@0v+J`&WBh5B6>XSBKOAqpIR&OBH6ePM*OiC6&3*EddD& zX=%^DDu5D}%nM3pAB^5E87ZejBav)7Q6(PI=;zPz4JAJ{QB!J7jw&kHNXSzAzbb7| zO_V44*xsT+q%2w{mhtKFs+QGZToz;7&(tEL0i~l_g0W?b%-$d0zMkAVYrRs&S3u5< zTVdU@v4hQbgAZ=GlXX}k5CNjsBWf&z-OWi~2xG&T>DN7*SIb)%o z#+P%8^E3UMo9~z^YGAgQ{#bZcg=G7hPDPziukvK1L4vS`GUr!riyDQOU z*0Khk!GWSUf1cVugz(J#__F*Hx1 zd3*((%11hC8T^OTOmz5}x65pc!`PLT@ZNWXxy1uH^rRCeon-PG_v?{`tSSp^Ll5JJ zzZIFXkAHP4xrF#B#kpkAKIK!4@7Z!m#6G7rOVTl=n7p3lK38-rSb9=JS?@I}@z28^ z5h0SfTXh*s##U;@VKw+VCaRY_r}ZsT;2by{#abghCR+Homzi8OIa(X$-jo;=b2jI3 za9{KKB;9|bH;ie%7IW(aeXZs$Dyi?`rsIsnD{chhC(jN2PqDH_+sUI}2n@{nl^p{t z8k|Knu5ngg@@Qb*taaOwo4iq6Q=k5ZNRd=>$>ku62|V!z!86wMXqDN!lee^<l>Y*Ivoi8+loei)bZOL9q@Nc##h3yWzx z1r38BPRrt__+cfM)gd|3^$}k0ndYA_9je`2UMKT<_c$o|Rb*DAoHyQ8z=nP|uKM8A zzj+Y7DAGhrjMKCtA91HWQ$kI((3y2s9^Dhcm^UgD8q^4rdK@gU%C22^>Hb5bft!~k+%`ffXDg*^=yMQ;cC(So z2D!eeF4cRkXW)6Tmh9ulGME)?r@SanzMXv3WNj{>3;~Rj)INWe2=OdnY*jf^k+F?N zG9}hXEnk)Kk1lijKv=Pv>D}-D6exnfuqZvB4>vv- zfxxjV)1*gdrKi*O_%@(Y<;yfTHGkb*&}DAvaUETen)2^$TUg1F=Dcggj5n_DW?DFA zoQ2`^RsSV-^ix`eoJNl$NL~yHTT+QJiT! z%r)*2=!?bZM2?qoP1+_8UYeTPs zu%xYZeSgf7D^)%=Je=VAWq9gCE4Y_0D%6}_uV+Pld;R2qcbGYvp?aou3d>&~naZU1 zEja-1i!$OQ#jR%r%Jeh$6`@hDm|8u4r__na;|E1Rfc*cH#vK73Q1Lo8O;yOAyUl~Zb+go8}65`@|#55xcydP$G;yVbh zUxs!SfmYj(#}Iyv`HIy$SfN2A`}I~JZODu!Gc3})n6Ib!6CUt$T~YAGo4)#SscfM$ zKdP&RVx?%%5_h%Qc(4{S=17$vnVd{oq^G$hJe#9P(--m(9P4eYBr@zforrt=@aU^H`t$+MS~m_u&n!K7yz6=|!&;1w}(f z;R2JkX3qYX+|$XzFB$2kb{`9n(K&~1ojMNr1sPq;x7-qL>#%vo1Ik}wSbDD(Tci6$ zTl`x>Y0>xGYNk;`5v{4OzU2W1U-q7rSNH!D;3SSkA1O>{sTUDa77#e7`BpaLj!JAj zbFc~043jPBS5whLt;ISfOU}NK%h3|hV>th0tesM*ugz=oTUmU7;>h;RUNz*xVIl3Y z8d9Q5Cnklaz&XKkof?VM!FYcBcnyId_UZN+d7bYh=QjJ?_VbfPgI=O>mzA%NZ5)z3 zYH9^G)U$r|2L=Y9w-shR$B9_VQ*rXQ1dvFR{liX~YxrSZR!L_;2jJ}D*9BupZnTMx zyKCNC{Xu0ZL7d&6_NxvrjX5K$6>s(AD~D#y=ra^*X5Ie+A|@H2osYMe;QU@Pza^jm z49ADF7B`W^0qlJmLhjizLKzB9aLg+Fd*et81?eiY@E}=25j^Ac4*kHG=+y3IPDcF0 zBPOn)?ozjUz7lu!Wg&i9ROYsgD&I=0jAcTf@k7hU41GJxhHlf>5zmnK0em<@)`@Fc zdq-TorS0arEAirdGfuraQ9;#AQ(kF=$b4@R$IV^~S2#ZXAG=?F(hn3v2wUYdi+=nV zw&WUH4f6>$FCNupEh;LK8p@UXK;9mu;Ln8bf3;d$4zsi|C8u6K>YA<6Mdmd;?*5|{urDZR@eYWird*b6(sANPBiNsRd1@Z0X?=H)8QsYK zKH?giHwBOI5zXj5>DlD9FJ7d7J8qNd?iCFUkf&^MD*3hGX+G{-3JAF8_px9vY*6~8 zAv=D@2gNkQ>+}6D(Mobd6tDJ_w%fu|&G@Q>M(eAHo%zp267s_E@vqEi(<6Qs8Lh0) zlE!r`nUQ|=LL#xa%d+LmozWgllPoSn21nGqN~E_Of488Jlna++s(jpqOsQXY+GqO5 zRV$7o)jPu{e|738pjmEp;Pgi+kp+lFqGLr-3YYkd5IpX=*WXW@3 z@!yrO;suE|Zb4y5{0a2K#LQ~CAyxAybfynnMV+3+n;Cy&qkh3UM!Mh6;YUII;3u(d zY~SD1?C8aoQN@Kv$nXyBYA*kbV?!&0@iS$kiQ=|}rzom;M^DG)#y)>M&Bcedp+F0X z`^Sg7zbHATzSDS3LMoZ2ogO3>t?`WJZqKtzd}%>(GR0k(R+d~wj^F}IbeD0aBHvv| z!H;8TKazOt(bN~nBX2Os{8IDiKvumE@Td~hG>?-?)F{x5hdCxcWwH_ozQjJOs#Xt` zRB}0PTVCw3-kaTKu?luyHkDS&>``s4ajZLchV}a;lG~xBu1q8hkQbUzu7DF!?c^y zh#0lxSPx3PS2wt{3*fnCX|ynk*=N{KX7 z2dr(a{oHgn3qRPreC6@25H}NYc-^B#-H-wb6DiAA<&i{z0RbyDE=PSqnWO%qti2#3O>WXnQCtPP?28gYj%tSW80BO^?y6Q>0&Grq{bZWE+EcMM+_waR{JX# zFC=xP_ux$CnjO8)R3s0jy>p%Fe@|^3>3vY3SBsK%cpULwj~JSU4m5oRm$50FJ#KQ{ znJ*E_=Y4i=P7)Fy$E{_*#_D{4)gq$X=A0^u)mI%_js5g`dwa2!bF^U>U7XN4;3M6- z2e+>!b@Um8bF~!!d=yIRntDT39%UOJrD1mw@$(0O0k7RiP38rAzMlKRWLW4S!W?b? zU}$V?WtV2|?nf+CWc|6a>sF;*rpX-f>mbd(z>utFG3ehi_qR!Uk$>Ou0bohRIP}wz zVXQb2_bzq86ygj|%_IfRRlorQ;Vt)C{{@5Z@8VpIFfGkl`mp&U+{_VbS3h^6En{+8Ao2U#GC`) zj3ZnFaX~&)5jq}43@rmQ`!lo2|072Id?2g^;AHx}ks4q;!k`(RC|<2AKDEyt{nnD-qKS>9Gmwaj;CGiF3Np=GoL?Od|8S3W`gY`9;LuLrH`Y&UF(q&Yp z;#Ql6C6~sQsN}^>9Sb%#HXtkEc?4L@c>Se>#Lc#XXciq&Dp}%Y+uQ#2b9QFM>Vc@< zp`qx-k4?A{HNYpJ5$LN&{LN1D%>$Fdk&Ex1Nk~Wl-Qfnme>R;tVD6{q=H^DA)v{A- zo6t6=Pb`|NOj{nciS3k8#lWU`a!+jT#|#flE&zn{UC*ZWz{0-E3iRmH)6>8_eYa`< z1hjpk>fi)941 zck|!FjBPSL9a)zt=6UzNnwi)1&mC_u|y+PHo-OjrsPg z;u{_Ko8PY|^e}bU8er!zW%*7D)%oP3{M0?kJd6-_m4>NI8YNG32FBc6TY7f3jDohH za4b4ZtFBSqq^gyME3EjM#MLm;Jqq)bl;M^>%T|GOWYwZy35N}r7R}+2BC9HqN$vimRw#$RQZ@NsPl869 ztb3z6hSa+H9qu|NEo&+$si`VV5}rM^s@68PGV9VY$oVMtS_}PD`0uNKVQudB-XAEU zxwVfQ%17vJGRUo4rF7YO@`-_j<)eTi)S}9#CW#-R7HiwnmUrAp!kQDM$Pxhw=e;Mm zR6N89caSzXR2sV+xd-qh@T?&OVv=A`QW2XCus}CJ|pPBVZjL5)Rm+sYaj}f$;RVH&XB`FhzV48*&3Oj}0%TUmdMJbgcB zFApuD-&1{2-?RPp^VsFJI)9&xjP@%Qf7;}=D(SR0=~UORzT*9n&0^wx9ivrTcmh}a z-0#dnLK8OW^=ofGe1~dO76CnW6pxJmeFDz4X(1fQ2bWN>v-XWz`9UF;i&rb-@#4IP5j0BVz(wk>}H zUHAp14Xm=x^;`1a6Zo{d)!sV-CZWbzBOs`KKUSvL-9hqdv;el&+LqI7koaMB*9}Hy z?SeTkH_eXez!dD!*aV*mzq3PQoP%H;$s>zT8uetPXfZS zRgsg!rMft-ZhiqgZJ8+#xsU}Eg6Q}{j(EMkLWN(w{1e^;h__SaWELxdy=|BWbUBOz zYVilj%NI(TqP%8$xAp{s^>Wq6zmp92m>A*Jq_3(1dQ~b3#sH-s_3)Esth}@#*)d_J89fq7uq0ul#lLaBMBo=7mOMxR zk@XQ_c{2aUJ1 z&s#}dvMAKr_NzUP+gi;~mGU+-+IIBm_%j+Oiloor=ycXJ>xsLas)WhO`kP&rMKXFq zVHjPOm)0bDA}tZz)caWHk^Ws$n=dyqrNr;G>%TNTND63KC~=>)6Iv0-^9l(NQ|7Qp zh#+)h9xJ;@{lc>I1X-3kApNPDhZY&*b8;@;(QYI~#3QQ2VbXG^$nKx(oijTG_)p7E zayH7e3?}QmlE)Ojg@qw)w0lO@2H(Au|3pI@RIL5CX{PVOBbt0?qx}cj!R6nVKdv>6 zE?Qvp<*~gqIaR7{s1VagV7p`-%q0893ECLv^3GavX0`WfToy~qYI?#Np+o)74*OSk z)$ubIeiu18y`^GgAYC`&?~EZ@ycOACN;Iyo`G;0i`#aM_+?q7b)P-iE#{ERa~a_4ANh zd3B&xdAX?oWkX3?x#;0fQhm|CD^|Q?{1$Qtq#io<_4pmov(G$?HA>jl?`zg_&6afZ z`DJb<&>Lr`C3jPjAPU!RZ~K){!7Qe1PomJRUE|J>?d%Kdp5maJ?2%HYTQQSM4pNIr zR}jVtwVprgOxv=uw+^QaEc(Qrl9-f)aq*HcOEj{I75yz=fogV0TQLkRYGc-ymp`@* z=r^_WOAM|TRbcefB2j5kL4q>rp?xe>j)#m(->nJ#l<19S9r>zy*6YO35_GN!o%jnD z!LC8w>vhik$Fyf-h-U;a%bl<#9M3N2ah}NjUlm-AHLuaI48^SO6+`Q%cz*<(ju+c5 ziY5;(dz>nVPS(rP=@uBTq@wEGy?B!Pght=6EI5w7B0dY)B}AzlRsfpkV8wlZn6*f# z7_}U*eR%OX^6NXJ7O$09cH5Q1*!LRnj-sc1J|i=GZ=R2!!?ci{X1oL4x^}dEOh6Y# zW0q5WEA zWpjPA>sdd^XFBjD1~MByz;dq}^ZUn%GA01&>;Kr;C4jUCG+0z|hc96Ph{tcrG_6Zv z>K7S5>%dyJY)}w}B5u^ThR`Pra1N2D;-wpIRiKiaIT8cm2)5|}~BLiCE=^rS}zd;{FF-`uaMuyB(YGb+<$jG~TeAH+n@)?6T*MVY1ywf9w`8OMJ z!u&&VL)IV^awiE6OI?6A6k!l$Q-J?v_Ig%W13g;CN)KLPkCvjD;R&JmkQOi}FQ0i2 zTQ6X3JXjjIgn99*#|L=x=O;Mz8-=;C-Cd^titz4Cnv!2Pjxx$;1g7@r-Bm)xO-#2Y z&~LFH@7656Y%nMyFeYnX;6a|HB^9{MqdQpZBQEF#Tt6L#UoJjnJ!I%Hg5MDM|I*yo z-GOkO|KFX40ID7sD?r@A&m35W(cA7&A{OnUvDJFnc$fo^S)k$sW`(#eA|ulYL)tHd zt3f(Z|AL#JpZ_EQw0NA{+_G|>3Rh|c=w|H>Yfi^%mK^w^+loDpo#H#97C%Ba!mm-E z7e-T4C8Panj7rDw9`bQ`pPjU~w~OcM!Hh95Ise-aNlzaXtDnOBdgK0}?d^G`Y-Kbw z%XX~Y-TCq778YJTC{@q3sX2v#nrfK5w=Q2wvmM|3`R)>?=FE-JnL@ig~DVj?S#YhHW=GKC>cdtChB$PSC4ypGP!kfYTp`bOTE z?X9gSMm4tH3dS}9IcU>0Y=(r~#xhXu^OUXyuwTN^3cS6uz1^WqlO`fC047I4>jZ*G zum+48!BF?XttOZKjK(`KWCuK-G?=CdZ|msjs4mIiwEG1fIx=!+fB$@0rKbGNl*r-_ zO=hf0uu9uz@%m^t0+C{BZN0X#GIK2)k6eIwa}BFEGwDnKt)GLVqqH_F+zm`m|JOv( z2}}<+pRR#%w->r7guN014y0BW7sX^{X`wANyMH`3HWm~V)La?RJTp?|?(FQmf3&*- zq1D~p{V;76rit(0d!a{GmJaPDD3TZ zfM1k+0r9omWfLi!2ljk`b2a5G6)lkpi_64dRI&%;t|iYHMOuF8S4{hHZQCDvI)4fR z{7Jg;?(Wxea?epLH79o7-rfzrNQ37CX}`q46Kk*q7Xgu-zgK%_r`K|3x`;5d4h%1E z&B?8wtyQeAjg_)A$!jsIG=#0Ktz|ei$#086(&<~8le6x*?oN-Ephpw*zzqDM=qe~ZBvI>?+cH{O z<4-7r?PkSPR8$&j(F`hEmwvtqc`$WcRPRf%w7yOusNAe5FK>sHkM`f$KlFgnX^NpM zt4{8y1umF%3kt9tH06j$Y`wRyOBmERHB)R?mf&4W0VS9ekCLJy2M~SKhh{6%pS_fk zky(5X1r%!NL1bnw(K9UMGiEDiAKKinhoWsjJDlePLlY(zAVx*7skILwk&)1!BJhu0 zS$8}x;*kgbzc&<7qzT>abQm-5OyCU;4!&~b3d)czwzaJ-&~z|IE%{K5vCU5;I$+1` zbWx!cbTjbdM*+IM2C-zJG!RpI!oYem1wS7j%+%Lx#`3`g?Be3$bz!^X)lz?1%Fx9R zp>7|Xk9M$Qb6yv(Ks)gs^h1-;{3;_XWI^ILjQ#6-1_sbcN#4^9H@q^{G&IO4+HVmu z{s6^OikCS^gFwrOTm1Y5cA|evp~>9bJmdi{!I^T#`VmOYLOg|>ezZli_+PUJk2FP4 zW^Kw$T!rci5vdBfJ+6|blmw)Y2sHZs^~;bH@dF#DH_$Irf=qq<*hzy$Q=XoP9U$jj zNQ{_v-(9*k;kiG)K}jji9nm%iJ33gAesz6a*y-=^#)x)(`WKiW=JC_3GwGy}IIv_X zR+py4B!7&CdjLILT->r#1&dbsJ)El`lClOt&c@wJ?RA~At?2G)Tg(vI(=z5e*Fm++ z{~#nRFDWUBVq(f;u{#+QiUK<+VpMD}nH%{D zDVUp{;ZYe`b@JC)1*x4@rOQ)%s25(kcH0AXMP_E^{-k$Jt?n>Z_i;hZrMRAjxLQdJ zVhvWpB|*z|E~pEd5Q4y2jme|IAeL(4;^!V6oI@U~7CK`f^;}fwgQhKw7v?Wyh`HI> zd-v}{p1`wt$`5G3J^;EYM+Zfith2bfSka|dEe$eh%FsnbDv0UkRzG<419`58|=>&VCmrB%aY!XOfw zqlj-Xunq#An5ZaG)IwL%WQi47&BOT>OifToX4JRfh zddTi>+$}FJmse1rwGoyjARp`-$#vNHXIXIIGzB4}{ zr8NpQwL|q3L{qcTil9yh5jk~aH4K4Zs*jfUtM{v*y;)gVc@4y?snOauC+pl?^gw7y zR(J*ymDrAXYRM>&3Z8-Hz8=&ylrk;9ejN^$RyTHe-nx7d)au@Ff3M0|5C}sP>`(L7 zaI?66>DbEhvM5E>d|_FB{?j$*q)1)pKVk#COBto3k-rmV;P8gUMJD55(d}V_&`kq8 zr;5`==ZRQB_r0$#gX+2fg?SjbZB930rDaLyCkwC11nV|(IRc;T!~}0l?tpk6laayW zvVo2D7FgVprIdY8z4JPeFvvHFh;Y{lARv5v!+J^%#_`U=#OZ0Gc#R@B)-C1V#~O`* zWR_JxnQ{+F6x0Z&E>&d&sp|*WdhR(ahIW1n@FU?}W|k z`P!t?5d;>g=YqEA&;uMBhhi9}vj_serKZk-%3U7R&(EJfhoFYiFO?Dbc$cyLz}-48@bq_Qn*{M*(EXq?utE}M(eu%5yzA76uc6^gSB ze_meR6Q`J`B{3jM&39OmKkI1$^hD3Z^sw4wK@60rpyapaim#}ss1L0yRSp2^>)Jh-&j2~?`PqI=60g1SHz!aYJiGiWef_NFhYp8+ESvA@ z#K7?I56}>voSbxP)aq^=Y|g+JDon*&h7zn!K@tH7Oc8u?xR3<faG)AS(N4;Z?NHSwdYF& zwKZf?Uc1@rP2o^}twb?oTXU3_Hm=x7q;4IpV4}sSUWJn5+600an1h{t8&(715gGOb z`yB-T1bh(aO#b$bikf<^C5*CG_p{LmXfBzlsZ%kSbvPn7WbL~*4bw1i2e`Pn$<_N7 zd1dqPgu+KGh1~Zn0oZKVnnIIs7+@VRhCk*g9b5qQh#T*qBk=!i^au-_|KBQs{`;2D z(8QTHffn!q1kRmnmR7g@1^)y5%L`y_m#_R!`b)^iux$vb%dzW!H0^UZ`zyk>OqrEf zv(m-3G7oO&kyH>N6QAw$hyQWv2`TOXmtegQjX==#h$QQ(Md_t!WPh6B`Iq)V3kW;I z(^f{0C8uUE0A^%pc-g1+3-*T``#l^Vr$dd@kbq^?bGaB8R<`Eal(RW|UP|8q(xN*W z-ZlrKI&h}nW%fczEIo5l$g|R!8rScP@zK%I;o){ubyrHq5*K@$tjc$>{6{udnTU{Z zbak(!q+}_v72iWhM?)ii+?s`vQ89ZUklExnmcjOY)?@}TUkNB%RT%^GsGkCD8?l7s zSc08fWSXH|%(Ki_&FNce(6{}xaP8uS>;cm2m;Gv#w6)I`Q$i(Dd$H0CMYcswQ)Ehw+S#Me%6GTDfHM z>%XAs%gm%}n!ExoG&?`9UG3qjew((rbLx#|wFfWA-{8h!Z0o`0hwW-=YO)~C6Px1J z^!7f-et7`|kUKpPyINaYArb7Yj=dleM}a7xo11&bz~%jf>q27bZ#&6HcX;0X`3Wym zGJDnIa(h6~Re5p`_BTt_PI&W787LBMfFWg|6NEgEAZ61rFz^G&fiEEe72!WUrZNdU z_}dsj!Z23IOpMbxTw=!))c6%!6VM{)xN_HlRVNs-00es=9}M?CZs>CTi=}pEz$P#^ z;(E3VYGj}Wn6mje@55+sm?g9@HNBo#0`lIJo+n{qjD&4K$gqH{Y~0$vK3S1Iuw~8; z2zshK9bphC!5~oI`T@`#)|9sBd=)1RO3;M-R` zBkw{Et_~x`U76c{jirpe^Tt4x=|WCuebLYNK&s+4!39`5|nUuRgIbV-TOX2V^`ilgvNA%H#C;j}k~E+JU;pp@WL;pykB?+-0PI0MYY zs10({zGA)Gqf)(vh9H<>Q(Riw?3I{d&|ZJs4yxCCZk{0L_SeQLu+x$~=MjkaV8qX@ z_z!OTOQedhHmlj%+5#=?FIA1Ia#$ibS7#(NF)|8l)b}F&B1RegB13 zVnvyW5*acjb3&pdWfmnQ$vl-YE%Thrp)!ReLl03TgiJ}aBuNq(G9@yk%&|YKXMguT zetRGLuXn%i@%raE9$~F@-`9Oz=lPjVHtLZ2g@uLcYDHc|k|5gy)v5_{Js=@O|L=jj zx{+B^73c81b!T|(aS|=RAQgd!f6(!?$i1u&7Ot)LUd3K}u;)H7X9&Xf!^c?x~s|dnvRE!CIVCI&rRtWutuI>XbPp+#2bFpjNrBY zsdoD67{6-W-UwgifUv|wzXkbi<30=MZ5PQ;n{B3_;UmMewlP$xu5x#Ab&Rw0d=c${ zQ_4k-6NkKvHi#}C5|kZUrbb4e3R88gcu`gnSnsF^NQj?*ww+$p=5lje%}6^oKmCPK zw@OJ#$%;EZXh{JS;COmeR5bnVoB6(mAr~{R(c6GEhLjJmy=mqSm~xSul^Y}1*`=lP zuzwpru?W)=c+=KK4lHu^pWpq&=x%cI4Tk5w%WeQcPMjtZ%Gzuur>AjlqjdU7&b;w` zadfYXLb{)yU#BY>7*afiw`F;yd=&^s#YKcO{Oq&=CRmQl96V~|L=n{LP_3fC*U6hF zrAS+akBnG_)DO-P6}fb^wc8T*Y1*Y0J$z{I%kOf4d>B-Bwva}4 zhc8UG#l^)ib7IF~WMl+@<6V>C^$k|woV+}8L2<3pv_k*O@SJIt=Og^FUE;v00;yHL zZ5=~HIpkIGnq=CKB$;REc#`q3D!2` zFWyjItn1-nwD+p2p`8=_nKcL!fm7If{B1gn$M5W9`YYYh=I5A|FG}x*9xrW2;T*JM6-)n|rqUGF2;B{u;B+cLW#>Ylr)u)5s}#@ZiCdCmkE1Z1m9#v?Qo) z_9kM;1I(L_i-IgfTmvnJPe~%w z(2^o54y+e)X>v-{YkedPd!_6A;QJ-78JwPszy1y&j)eHz@jYWha61;K74{ z2wuN_P5hP>Zed|Txp|X%{gv`e9A|)H8uzP8ZVPJ=%ph6-Kg4Tu(%D*BJ^1GzL`?|u z^70-$coq35Xw33-(iMCb3cDO2u~G_m9_nbv!z-3ldS`%r_imWLZ9f*k{x{pWZ*XjE zY-x!Ice8IE<@lh@$|)A_{Q6xykoHGt)5YhFGd&FU~a#&mmYKL(cX7ix>Te zWyr@6IiSTofuXd=cXsfY;^XhA-Pc=m{S&a6aBx(5k%7uXanaFtky3#3%x8%$S5K&) zIK1Pd+Sm+?nkt%VYS+z*2g)3G(q$^7)1BQnK z)ZWYdnQP|W$;yiA@h+qLjuik6i=O~R(T0@|JEDX>SeoH%CwVqkX41U1!o&LU9}AOPD#vFVu^>|ung zsGZ`mv9X@`x^`{&z%}TuHh$$PV-;Wnx?CNHj(82(aOG!2v~_gW0ZFZ_u97>ehZ%58 zgtC|LZ&Jz6%WEETLCZIENCX4v1Q<6}P>7W|&-DNWSo#Ld2N z?%YlZcj zgUpHr4>UWJrB)ulaAA*F*l@!&b@grT??;V!tpt9fgOJz*>_I5f;=tC>Fqn3;r&J+B*nKNrtZ?aw@gGBX z6=;lOvM48KpSXA&iiGaxx!NR?Qv`$&d$!rf|I!zcyG&D zwB8cENaBE)8vzvYSZxQ8xn(hJDZ%dM<~DoJ>LbDOy=?SA)8U&sW`$-KMn^{xe}*1g zg)qeR1qC+K^vP$}X8PffS!1(^zGKrBt~@IjS48?YG5vfsmKkR$Y!Bu;_CX_h45vp> z4ZSkE!CC5s5`IC!i#6W28;2-Gj^*AHlQdxYjkY#%^Z{Tz)bco9VeFZu{b;p^_`wTj zIxc(xpOR$1RSN%{x;8~{K=G1&Y)_D0cE{uEG)r2U2iw|+Yg)4pJ10msrHcWy`>l~* z*41@_%cfbABU9(<)v}GsG2_y%-7&8ah61_HScJskYd8XvEAG*eWV8>-$T;;^S3Y_a zlRJTJ8L9hV#vhrmF$you{)JqCCtjNq@yV`oWg3C zJo%0<6wxuZPmFxk{>-U;wM0v_e}B~4!!sga65h173P^=gKe-L-2M-X?a;S=up@6#tE{-;DJ10;68b6`e6O zymR|DcqPT`-Lb7Q+QV|%-XqNGnk3w~2EWDj^ux_sJIV_OuQOK*a$L;3bt^RLws+0? z%Lry-!mjt8!Fl7`Pvu>onsTyPzWVh37RUvXQck;d>!lNQ zgLpDy`axE7$R1w04O?^1KBl%w%|%X@>rSNz)!@8yzd2M<&8D2~vC7uTYvc)Ked5cO zYP`>8jgiSQ{u~Q`y?uMe5xI}Np;Xk=i!%edxj`ogtSl^p??w7K#xwbOS!h~FQ=)3w zK%=78#p5$iY4Prp){;88IJ`a47lAe>3c3)j#N0j+Q*5iH6}`wZpa<(ypgq%65Qn5j zdEy1DG!8vR6=b!YD>CKTlv`9pcWq#iqYDs}O4eRyNy`KNQ3um^?Efe=blThh)oX}q z4QEaYj1fXxEY4b6va+%k8h|-?@U|9*aX~?p;=MunN#hnNHCq~f>xIZ-C>!SCc15=D7HzeE{S!)=!``bwDy^xvQR!$rSaG89# zRHIG{&(u;_z4_iDPWnf{yKH|rNu~JDMP1xmZ0y9Kf;YbH)FC08r2UE)_o#g*ZR-=8 zPTyS0L($0EJ;EfpT5_p>{$p&nqHc+*nDC_LCW=08OG``il6Od#ICk$&n-E>%V~d(o zXuu<%zr`Ipo>&AOJ^ip-n#rTL?d{EBrE{sFDFy!b1E&9J6ImN7;VX*Xw>tcDpY;95 z3Ov~XSG24h?Cl>udUX2~KdH`MHmzpzQ;ipQ80)7~>gOJwL3ZY?+qZ}Mq!POv$`JcP zG?tk;4jMRA@ABmX{oI)q&BJrGFCUsbLb)keM65Hm*!r`+zCJZ|*CWH;{(ic6_COBs z<4e9uls2GeSVneIp0r-$&EHb0OR7Ad@_I?gRKuo8o=L;C5wM`hewvcVghIw?IbI$9 z7f{m8$l8>MnwXlJB5^I_(p#Ul?KX~*MG}iT+|4SPwDxvopRq~X7^if-LI-hW;Dt6@ zD7pK-eEG-s?-oajA7`8~+J8y9IZJZOGCE|mk(E~n8YBwR&G>l6_)}J;JvIt_$3mNj zW1^!;9%2F_FE`JF=1bQR+%J)+c9BzVPEG&CFAi;lfU^9Pga|M+g6&^RGP7PKw^%hK zeg>~NlrYIMXy{(jFIPzzd1>r5d&N@e>h}-(i>vP&7FbKAXyi07kMR`$?!MWbNE1;) zyuNqM==kXm0AtKKr74b~O+TTg_SQozSkuaCq^_<`Yuw^K`j+8O5CK&CoEBz@e7NH+ zhcgpXG^^v^n#CKP$@i{;nqE^ph%?X44!O7JXC8jMt6H7!(Dm`WeWyA8d&!T`BP?M7 zBgKPhZDnO;sB$(3-TUXC$h%vY!Jn`{GJK;!?QOk<;c1~E*0(52>bCe(FURK}8BS{1 zTHOymA~AeWi$R5`_8W1oUzcp56K12G-atWMob6O|SV%Iel62#+S$*umWvXe7>%(&( z4^ws&(1mZI|C;*2Ylz0{quNG})=XAt#fV1d(`!^hkvQg+qQ=;wP9QgC+c)Fct2Q+U zM^AE7)J4Og2@(ovk;xO$lv^uJ`@6djbyg;aVrwQkM4`Z9V`Brpk=)h(Mt4)Ze87#3 z>z9;kN?NKph*Xkpa@8so8aW76q*ci;FHd5%s5u<)L|;c+yGeJqmcNRW`!0qt(=@eg z*XGzgN}AuT?qCfAqe6*{-A^%1vGSiK`&T|=-qrU-%m!itx2aJOZ1665vG?!w|K;w7GFmED z*`+h$$Bg>+_g2mCTp3qU)T!S{5GK8KM30yf-2S9V6kQBh7NCG~jGTs8UWNtxQb0IT zimeQL>7F++na!qpRXBcVS4^Rl%o*IR5o9g-`c`*NY|j$u3uKN^?da&>Ip6 z5DyCnN5rZ*BI&{mtE8AI0a9WU0N&WdOEv=`B&HyG zlD4+?CS7-%J77mC3q0j2YuW(Fw6 z#ibPg0ap~&H#VzvcQmzVJ`e7S!Gn&Eg-Q#LyCN6(jaG9B7B)87g9p;r$E`l*V{VmFDba666Amy%@QaDXz8O{e`I--A1{hFA4y5MVr>YTZezxuh zbjPd?ncJPDq}|)8*2W=#$-aLd?VZp1npY@8Tr(I*;L&>-6cG%JMjPpAXr3VtxS=Jm zUsyOxD3Tv_{~o$#!11pR8~%+E`@#=)j3TgS-={6^*n{@3GosU7D&ANTA7iUAyLkGv zv*$Ls8yL0p^Pd4>eU) z2(`v0g>6ys*G(}_1udK5IVGp!fgmAdA+D{+q|Cw2?i*-gTo6JQa15nCzdA{OaL#E7 z94Ghz4KyrZ<#{O@OHWUaL~m3_Y9~(kA;qt;v9WfgtC-xq0sYPE^g_Q{+OzgvQ=BLC zZKiSn0=(08^7J1j7tf4W;#nh{=3!3$^I?kt%c%5EfU;<4O)Fz|UpvC33)xMJl?tH; z@WWexN9fW4q32G3UL1-w!y5Jtfw=Wu0bI#-NFqoppgkL07k(*jxkH41_{rdisIe z8Y#tsx4rO2OAQ`VkNHeUAC!`M-_dbQgM?QTODd?TNxzj0U~4V|tgIE)TdFKM! zO!xPjfntk4MY5Skn5X;X#{=)bYn}dpxI@=J@|o=erM9}7nza4}1qGVL-NmMr`XaZb zF6u6;;-yGj^<2r>+1Y5uQr{b8udlEF{OQ*B3P=c=&>xV9kizkNmm^*!lkE0?3!Hcw>noOIAxqN1&!8Mr{~%g*LY-*R zn>m?1M32PgVQgNP&I*Tzg~8w8B6nAqJEx{*56_6S1cU&%)3jj?5QBYP`H`P5RP_aT z66v#nC|fNekckwmDnh)xn|Xdg^aK&l@B!QQj6buD^m%vHah|2S{Qi9*TWZLyl8TCo zP0Up2w#M-%I*0SONQXDpr{{-XzwWZ9?9Hfx$2W$QW6WUvNj>#KkqyPg7pe?ROd<=U zTnr?&JAniJzGAtMjpn+l>ad8210siUmz`vR7Iy*x-k2(F|K)w8WgHC)RuoDB#{{XM zqk+AzJFaz2;Ccw0fSr+pFpO#j8W*f-hU6`xva)P(%21{u4?T4-PnDN3HWt6U*k6bC z7+JRgqQ)iQwa6G4I9=2Lc>J|>b!Voh{UOPyG%Yd`6%!Sm`u<%+T)g?h3v54NF08{; zT)_^Oyr(Mt14O=#a$A_n*eyN96yBdL+_YbJmUz1Ny^x$Ok27VHUPms)bvsufTnsMj zkvSp~tao!-cq#TH%hlJ=I#HRKnc1~jEb|fVlP6E`DX+tH26!ZdEAkkZJ8jRX5w?BZsiw3%oT2=O zd%bhiE$kcbR+LTqn6|0Bb|{mTmHq1SddMYN^3(3j_I8v51NYlAe2@!@kXznOS3NvD zTwLA-$l`^7f+OSMeF0sJiT1x9Cj1+BgAx<&^vb6$)vT(Nnj^I-s-qBKOLup;AQi`+Jx2fq7u{~qkmBlY}}g@cD)*Po6}(ORpO>U2FR`X z$<0c7v(l7k%UmH1ekLGOnf7YHF~h?Tr@uw;gAJdIS#HA*&-0>eneGvKED&BfJTWpk zfBv`!5inTjT`(3lR_a2_F`4Oy7uN%Z{Ujb6KllFqy(XH zi9sKK0MxrCUH;f+*Ch^i^4DxXb}!fPA9E5rGf)pu(uul{7RQt36(&|4>v&XbAwO?B&lR?%@!I z&X6FM0%?cCVMa5&dhFC<#MOzXCYLQNc>C{n*a42w(9}c^n3AND*9j$JJPSHja^>O2 zGFCkqy4ao7<-n*yq+MR-*iJQe=7*ec=sV)M_v3Lr?KykrKMno>{9BkQBP~e!fhYlG z<=SV@JOTP5Rvpp(g}1Odd>tOH33x){`+5s_S=HdYQuBG&5+){_Ny)`5)%^xjdXP>BfoP3%8(CzeN*Eu*m6Z~ zM_wf`T~QyYJLIt@R>?i|e(l3jCSA0boXpVCe8YmM$jH|sDgG*}M<8S&hmeEO21z+< zOdRllFhoVW+eEwRTkg|2our`IRL0;KFa5wi0wIXIwB$htxVyh%V_KE zmoHx;oOxI?wAPx*0;W{-_DVE8H`*Bk?s9k|*PfP^7KilLdG!V>sUHKi0RUN+bk}n9 zSk9W7Mh`qHE*2LPv+*;400?X2em=BMOVtGSN*0V#L9I)MtSh5QnZHy&@{}2gLL~S~ zo=3P-<_|Uqpo#~Tlrom1nv&9eJyDX-g=ar{bO3U)6oGA@*6DWy>f&Rx?!;*_0+-32 za_g4Iw|6{iscUk{<<)c7jdjb(pqhO~pBFVXwX26!Q{K@_R1J9NEIW5Xvd}400}g|H zq$USt78&hCypul5=)8P{Rqyz*6h>Dv*vFs(HI5Yo_h8@5B?fohf?C+eRI%4wLPk~= z(?9@EwvAifY_VEt688@x(=ntUAm$v(cGdQS-&$r8j`T*9Z*tdxCE;c}nH4W7M`z~% z9L_Dw38rG)#W%lisOMKyEI|UabQRl7=R~#JXe+?0S3XK=1jHLZJoAs=>cWunTrD=t z(6cS|N4`%@X`MP{RAQyBs~ZPba*=)g%i%e&kFNVXk!$bw*6JSRRdWHnd48sr2xh_A zOn-+)Y}GiJTS}UV?ZBj2SXh>nRj*tTBNw7v3LdX*m)(N*jmCT;xpFeB7{b2FrCWpR#>Z|2Nb>8@!g~-?)YQ8sD^a-p!IWZbV z*Q%(DbUcMxr-z^45tVPC3o0mL0=|!Qg!6|DSg#XbWuFm20(bPrNp#!z-0jaDL+T%2 zoirTFGx{xwcq$$Y2*O8xtzbWVJNa+N0HtRhoa1G;2R$f~rQ({&73#w?k=-_2&R1#5 zdr?*b&sKR_7#*f&t^Nr3k++RU+2jhm8fV&*$wHO6or2vS?@F+g1 zH_tCBGA!sqZ357~wpD*+b#Joq2_u|xf+CM2 z4Gk8s!ATW2);yIQmR^B=!l?J)E^UCkUS9I@3=ii)a)A-TCsj2&yI80qAx*RLQjvBh z-8f8MzdWnM4lRyR*{;n&fn6Fu=7qzJHK#7z{*hj@@n>LTU%=Oc)r`XtZGRiW=BB2h zlSepd)6&w0zkZe3rEOyJ8FieN0CUfP?Ho&zY6`s!NfU6y6IdZ&&8e2dLPxeXKH@w& z1#Gb@c9@jXA7>3)ycjYV+}5(2v^}8i-OB)V@Uo(HgH34-MAGkw+jHZ(9Cl3HID8C8 z5jtLt9Nx1I!$^L|Z2&ic>xqbVlUn~8SX85;=H-tFmWGb_@!7EQRi{0dbnE83-2y}w z$4sJJd5gK{QOkZ8b&l9~lfQw)Uh*@|(pzf|SPQ?FVozfw)iXG_*Ktm)%p^T22?IZn zqJ&{e2W;XF^o=@zXfN!XTecdk73uXEkH8i7q^XYYDt}Ye$x-=ssR6SywD<*;PJ?Iq zfJ^c2nzDS~he!r}Ar|7{A}4eIO7znJe+XIVY!I~cLaNN9#&uZjXJC`J96KkcFOtEr zt3&$)sq9)nN1@rx3c8To35Y&-&fH8*RgjZIGXC{U6HF<>Ii#jWb%nrQEOPkJp(L-% z9oOg?xo7x2x;yxpR4j z1t}+ls#UtX3eL^Vw|;Ot1tR1L*EJV|QNXP(I*`r14)RdI*-&95I~p~dh@dM1C3BB7 z-esD{(;3_2doO z#FjG`E~IB>>UKWe$oQl*V^A2IOOj$M{YjSV?TWd6Ya2uM%c%dtbD!`O?C65_ea2sbsyY%G~7t?3AWv)nr%lnX|IY6Zc7 z-B7n6`#V)D7v&7z2kOd@`nrE^2EM&Wp><4;8kX+#j0|wvO7(wIvZYaz;OFd8Gt6`H zGqdhjByc7r&gQc!4=1~Bv(&##Vh%sPmm^t~=H7^UL%_?1^k@O5$Tg#9P8=#X6sHxD zw2vpgFxMD*2RJ!CeE1Lh7-oHX;@^YtOr zei?7@#Nd^MB7eGfoT`--fGVf-YfyB|D{JjF+Fv?~0s>S4j8<&l1dDE8gH?&Wxen_39d0Cb`#|nI+agR-xE@s#LMv;HMya<^-kkCU6$FoJv zbxb|NE0t<1#ViHw_qu8fW%&Nx!PH(h{!hx*TR1M7U%rH|WKA!iyZu#D(+)bik}GNz z(sV_!Y39XF8-IKx$0Q`iJU`Y6eiRZ%k!fUf>b{ix9kuKpj0$Mqrm)88-HJzH)@aY& zYO@kGvxYroXa1K+xeI9O-?4m_#)z8Ga|5+EbT_94U{LoIPi_FO{Jbi-p_|- zqWj}VC{tpm0=en1F++b1=4SH4U!dU9ooB9L@?IHr)~@=0j;isCu+U_8e0ujTu$W4T z&7bPBz5OL6%{}sNl!qn<%@+NOq$$kxK~D~!3YjrvKK9&uXm<}55pb0h%g`{6V}MXz z^d?N~Ne&5^-o3FUGx_#1yI6ClObm5?-*88B+P=uG0m2W$`fV$pfo!XO#per+!rYam zhVLE<;f*|rEp&Sn&KMi3H^gNsZR&@waU-mJrqxR7pscKowKYZ{y_0GCE^;Fq|1*l# zb(Sr2z7PZngw6orprPhmm~Db{3m~qvs(2H_RYf0o>XB~Q+j$dfE1}*ums`YXx1IN)5T>D%{ zy>x|eDj+Vqd-pDS#X+B&i{={oC$(2f zB?hbw%XJnh`9MWTn@Fq{a>S@9hp`%t7gyQfu!9X`L;EMY*dFwdGgq`Va`f>){E#b` z(M~LFESm%L4ll>X@lKg_<|KnTyQ>MCluN6JJkE%8Vb<47>iG?qsw$m4Ebzu!ZqD+x zi|17y8H$@ceO>hpvDv}g$56tcMm&9*(cg$X^6BA2<0{Vs79O`77-^SRb_|jvI=N>A zcX4xXkO!{ueE9s?8<)~hffQ+HxOgZvcP^2d>-{r862Knu=s+Mi=l}k}l{%b`KNcVw zSoX~wnP09bJ_`7)GJ*6F!y|RG7?WE9W{!{cz{dWQ7XkQ6&uk{F_}bS zVfUoE7aWQhG9`7md+?dnn)gcX$ghYV_4CEud}`_cm=)JLUY6fwJ0@;3z-hRJpDzE} z0k7dVQJUcRI$_m;$9VPT5 ze7gns(g9)NOII6g-%tLo_<{5d>GMKGrj-pV5$|PUR?C(-_ttS9hk>EZgU*@a^c#5 z>t-N^8X6kt`Ht$RiT3$Sk_QnEh3%}Sc1@5w{iL30$P+HP(?aZ1W_>fB?x zMegqDbwzH#1;sJS|JVO_j8d`b{}dK zwgZ_CK1rdqE3WxCAwu;&xGErDKw+Yqq&ibldC9+FrjmuEF>l3;g#}Nbs+~QX%)igl zvn!kaBvwN+7oln)l``eIs%3lm{k;a7Kw}M2Hu9_-pt#sL7;e{xyc~7=QsHW8XJ?10 zHx^Hbxuw$ffK-xWLRBuQs5D?MBtoEU6vQdCqTnF_sk4utpAgoN^7hS0sM=Yw0|-hy z5O(OH(^69t5);u$<6|OiquyLdRMg4ALE-B6TVUmv=kWe#XJ_Y_X8uaM{%S2l@8_eU zgc5Fy4u*uohkNa0Fox&7Oq;JJoCU0`JfZMH?w|@;JP*+O@G+zr=s%pAoD3PWnl(oq z1?mNV2J?b)=cTscAxFu~0XG}4WiUN_p#bKLM8}X75HtuM41BGas+0_aR6O5FD=CiH z;*nCd?2aK9QAj(hU;0tKh4vHkY_`+U`C}eYr-wcTi6upAY>s%ZaJx+iSQ6~zi7hJm z;zoos){q|m?<~-kS|8L?aBI=jJ6JvW02zyf9OU->$D1`p4Wx|vDWPw}rnzbFOy zK8oQ~h|U}?hw7=Ss-p2ey0^k@RKr|{EAS|yUO~|6MeMMlN8jV2kfU#(AMH%HP{+y0 z7@wXFOx-?J>Dn}BznrY>^|VB^S6C@fF=}c?fmag}%7-2H+c&g2#|d7F8SCr*`hM|u z?{cD6WMysgj$vqs7*u(Dik~;fDneD?)Kn3Jr9y8)zNe|l$8r9-$Ao+P5kVH3xxq>V zbm3M&S-6e?X!nI2JUnDV|Iw~19D(>oz2X| zp}YN+TDzdL!MPYycbpFUTJpBKQOz=Eflqd(?2b3}bW68*Sr z8lwsroO2f;mL06~MK_n5n~VGORh+CBhg0sUuQVWaQ_8p-Hax;CE(u#@@EW#8HsfQ}BWLg`1(Qok(pDK_(aT?>#UL9ygCyxV%tOA0t zPprnQ7I*&PnO5Hz2F$jM#_IXqb*{-`4k#WL86ZG)2{>N&X?GAmv1)hX^uU)@#fQJ> zz!u>?Y4Yr@3WkB+-ouxZXaXZ>XlX+xRO_5DtxLs@Y6+JE)B=I0>PxKVT};6J(>t(! zeW1ZIYT5Tio6US@Wom@+Oxr(Op!EdpS@GqwLmAonjWgA@Rw8qs(*{Pr(G8O!UM)R6 zz~ZgNpg*!3C(ud&`d_G!>5u$k zSQ)=EgyZKZ#n!74%MjI|*$0p`>;DF&Q^HgIht=M{MS7;^h@f>LFnM>ari7Y`3JB|5 z70Zbs9(|;4-oI~Avb+ohz4EnbQy?ms8v8JV%-0XCCq(E+dQ`CPMAo?}q7bP{+>}r2rGfL=vvJyvoXyvp1kn zB2P;7^jtvSA>fw)eg4+)#)#->&Rx6Q6w;;BcQQ1f?p^IbD!`X7d$8pNtow$kz{E&i z)39*_z$f8d!!^@5vF07oc#Rxg#krTjypW!Vh81dCJP-hs zToGE%7KLM@>iKi*_H$dDh{UBg;rX>~m9T3RGnq=1kvcb&V~A(IXq zDk&+vpcT6akkjkoZSmXGpguvtj6*YbA{a~uc_1o;El-}znK5B0U^r*L#{)%U>>BM74ns+V^Ru1@ z&IW10&6Y|WH@B%lU_*#;p*CN*CnO>-zl;rur~;xj)HcJ*2Auo91Qb{*@|*r^RM0Qz zf-s-F7%{sJ*V=gIYnLUt3O>YQ*vd&Kh-u$20mR406SB~fV7`Q7dyM0_b(yGtGWeUpFez%v5l>rlvhyLB_dt6S)xD`4p%9upJb~;zrpKW3-uA62{$;mFt*~8~~j9g28(K z;)}d42doU&RTDJUMJkQGSPXQWa)At=A75(3If$wWItkjfV?jq}p+PCevr69DP&VS2 zBM*nhhe9d<#QC>1aFGG?&%K3SJh@Z%efpe=N*NppY2m@)}{x zRWX+Qj}{;`7Yu#u29&Kyf$n;+92TL_`1|N4WR8*}E&hsD~BKfut}WW*T=&`*+`$fhH8 zzk~5+<+k3Hves%sWHDxVcA4IbsP-$3T+zW@H(QqBV zot4E?+N1R`yb0i9=~X8y@>l4=?{3w%I+o_CN}ip6^zrdyi3HKfdotSO>B4wO@^CVe zlQ!@;wzg{Yy{&=O3cyT_SzK@4F`;1C1d?lrie|X+uo=WU5VA0zQfZzfjWbLXef9~o z3z}nm4cwRBMlrz4!NoOQqZ)Iqb^QqySsoJ?6SHj3kMCkUFa~mmbE9Yc*{r-SX+LfT zni?BFP7FaeJ%+o%-bE0dB^%Do&A}T9ZsK4{s5unRD4^jKmJ46HEkFGKE{(4e(4>L7|n?%Bj4!`PHjC;um#J-_jEzy?JvU2;QUtc^@ma z*^${!9y^Y-i+AsC)|*#2wi2O$B$KB%{>qN9)t!a2?8_D9tKV7r@nD3h4B=5KC(eCE zT^4sJ))KQX$y2^D`3lxoAqS9yge)vp6xEUU&6nKrLm<4X6J=#&oC9Ks>Gh>mRcTJu zoR%}8dbWLbkiVF*^xPq_71NE7E zN!vd!v7}Dvcc@+k!e^9PklMFbC!oQTlw=Ih{%b=hfEfg!dy4JPAC@?Au=y^mf45Jy z$><1)j85O`KfAn!=4i&4;dSkMSd3xTj1iPgcKTZJ<`D9$j6_LF7m0^e@{5a|c`uX; zMWQ{xQ%2*J9JYD&jjoQ)3lwwVjoZ!y|NLb1Qvr<>h}$DaYH*P)o{&FmwKyO28&~Y? zK4H8oLT^r-F1&Z%*mz>k6F37}V%VFjF$0=OWcC7AZBc%{In{-k^t|h=5(pHAT{HMq z+0gUByIr;9B0ca!wB!B z$8(tfkxz4eWI{w)IaBe5mKBdqRFfMrdSC+Kyw0e?a$=u=03u9Er!&ycs%vTW)%YCh zXC6;k{uG~@s^J!P= zD?Fg7^TFXNK)TY{P1NTa{-d(AY57^S&n7{1+t9xjj=%n`_~U~wK_(`jbo!UvbtUxG z1}JA7jQIWFpxGFs3W&5uaTg~w0_#+$sh|{= zB-d|OefZg?m2a-lBv zAEidFm1(cAvUt^ti7ihZyT#np{1dt+JP29A!JFUTlQ8=_@(Aeja9o;JE_(UQtKC!~ zLGqf*#I{YiPuToNTHzxIZw$KFDWk1ZpQg&o6Q?nyx#!QH zPjS75?F%%d{Jbd|k>kf3A^S7l@#MkdF!tY1NLA9W3QbQCIAUJ;kOIiMkik#rj^f{^ zH4d4VQA#_0@UuyHn3H2olk&@0@%MGw`u<*EZOgvvaJ>8ZM|#C0&j};L5m=O*wCB)K z*nqrp=6y3)rlD>us()&Nzo+m53e2jBEy-o?$m>d7{j|vKijk)X1&C@=iM)bu$Pp!?G=_f* zsxV)%v?-%x?%$NqwWxt?!B9xkBcq}m9UL+sfWlc`yUIFrp;lX~I;Ll}B_u*?CcL`! zWB>@$_ph#6~O}6y%uo?!tI7 zM}Lo!FMBOO($?)}rQqw7wuJ0J z)iy)x-P^BM*|bLemo?k*n%-plR59$d*l(hu3i9*e87E|6N&h8mU(qWqQ8c@u zt%p2d?ZLr#CF3O-oC4W~To9i9x^PZBOAjOL;_*<7tykg8+*b32 z8_0;vI%W-hoF61nFjMuGSVwi(DIyG}hX3H6%cP2T5WV`w-Z;p<{a^E)T;Q^L=zXD? z1a8?qXM5Nm>X@IkUgfxPB3UaJ9|j&lm{PnJg9L+sQ|3N+0I|x7?heb0hdoZHc8UrM zU+S75MeEmcJUP?P+8Q-Q-qM??&uUr^J`dIdsYflA^=`Wj{a-UkbDG)#LYSGkz-%46 z^5R8E`W0lE9~A3>advhVcULXUbR{vLgJg%{bEUg2Bp&X4EZc5+*Z2eR0P0PWJOx!T zzW60X&8m%ff>YYu3hAHv^A8bcK^0Oq(4HR41BiIly3fbkTC`=z=zOA zhVou+mgdrfi@E~5#n-Q2VNbNFzt!b5RV$JvPoWTas&L0RMefso+4<`%PbaXM*L+R!t&s;u{Jtjw1 z(eg*Hy)7`8N&%mRngZr9yVTgID9h<+?!ecoVy~SAtEiKkEP!Vs;O^i%&Oqw8zm}54 z%OX*Mgq*tQnGX-1U+pfsVDgBq+|=;QnL+!*=-%dBQUTS@5MSNh7t-VE1UeKBn#FZ( zpKJRq)3epGc?3@_q!4${TNRWTLsiN_d{yYXFum5aVh*{Lmr7(@+{UPQKm-{>JK{1( z;fR8VrGS2?cBh>0S7%Q^UdX=#8H&enJZA+ozh<`uidKVZZgq=mFSgZXX}(+l$&pQqmQlq#}SLqq}q5#bK-& z57AYHFTe;;a9p`!VyIdjF@>#h05YB($eI0s2*G2{d+n zmL;2FFzlNlHF6V0G;#{a;S8?aQ!ie;c;|4=2JJ+h6I`J%#h?a`P(`J$$c4Zjr(Ef+ zbtBO+F_;5VO|Z#dyc>wd<0yd#X*}P{r75_!&zi!$Ktah!ZhI(>G6a$B%M_IH6x3tT z>B_s``+`|$bz2es^#kK9X(Qwn5F% zFnLPn;t|=@*b}0E6CXHs?fU-h8}>B}=&*Pa9UDuvb`-x+=aD9DtD~i*MI;hWo^1BJ z(Q0RME~%vV2ZU8pDNf*75H^O6;W5);TmwBGrU;l;_A^DmJcD2(zr)i{DQ!&cUKO|K z7bCp(jyGCchPBt<-=cggR2!WT6U(01FDY#)9h_r^4-xuemEm^BQ$y=vX7lT|c=^37 zUovk-Yx_L<`9~3UDT=*=)9!n#xc486Hwb73o#45!E84Ix>SSR!y1lGvh`Lzf3X9~hzvihqM3TQ?ZUnONFB{SamWGw+CD{#lY zJ^NDl2VdhR-E*RB?P-UKYikFrq+qBC%tQVIMIZH*au^i$ii#rd8QiqJh;8Ici=W;m zsXk#USsFRdz2o+WE9|0@zEZ_pLhWvHbjekadBAwD>(z1vzdtKi~d{ zDxSzQB6~PEf;OGTKX9Fbu znP5xp-(bD9g%A1JnoTTV~Z@j~M3OFcL^a zfF*!kyWj$E*GgnIYhlFXsM`^)c9`aXW=rguw8sO8-u^<4K3XMcUQ%;sR^kDPv}G!D z^Ne^_;m@JW2cGN$m@uwcx=isUV4+*L^jLDbC!rd|cqE-X9EF+0JCDIFgKcG_muBC! z>p9v&5X}I!Qo45poGhf5s@;{NUun~p3VqVu*8rekSO80iL4&SuKG&5)9VG6m23 z3f&$6N549C0q804@&a?kt@DL#25fBR3qCjtoWDH(y|z3*Q@i1R?|pP!oUWD@EHH~+ z`&8+|=U?;1IemVro~4I`D=^`===k@R$VA5c|0m=X? zq{=~viPzyf55O%WF>xMDnm+_v1fCeR#Ds*ed+4X*!nwSKgoU@&;*nfxg1~A3qEp1; z%uK|^#ww)074~HqNL7Ebd!DEWiOLaVMCve;FMC4qSc_mL@H)B2ey|KeuC-hbN`vLR3LyO;B}dvHF&E_t&`Ud8Tz5;%DWMfgIWn*0C^NzSlB zI))xg86ZfsF$G;K;8Q7Q83gb90gw~N zQf&QKIy2F=cRQ5f#i~J;APW?^sod}{kn>Z~paBy^cg7nk$N834y!vKJ3W&~&NC-k^ z5FVtp%(ugHLXog1NV|1r7{bxFWt3WZsUCnc8PI-gI}9qQbp}@Z z7nzuts)y_UK=1asw-=6Nc!7@Y1!TVgZvH3O(S?5MaztPctc@MQTf!SQh%!Un58pHj zHM9>njDVI+PfW;rPON+g+HdSV2trb-qOiO}Pu6wC5nJU)F_$~AH#Z``PW ziv7(f+JFW=T42}}rbQq*xVT<%!&$XRY#mfOrLB#K=Jj$dz^fpb%?&XUK@}h)w9b-S z=J9|}c^-ZdqFaENMR_bCwC&0uv;zPr!}&%nB4NOCJ309i#O}|G9(_axy7=CR_6qcO zhx&;jea%_|iAJLRPwhK{x6*E>Y6QT8C3wi6<7bH z0UZ9^LwLUg_hCLjcCPwE%J zHO&falVChc;J5$~@yY2KsH8s4)Y7|WME)RB>6$(QY!F@897jtTjGpz@Ef%~j=K-bA zcPF|1o+90LjIGZBnZD}*3)6~45suEB8!1j!O`)L-36Ez=LLymr9{upVJJPV`(T{?% zoHuX0R~>%1&lH!xxtuxPpI7$m<1@qJGQjm;Mor2ca_-+aEVBB7ndj)<{o5MX*5%6Q z+qT@u7u}>ZQwvLuyG`e6{7*PEA)!Rx(#@NY<|(gENr%2etfddGEn>^C zh=BTC! zPNq_Ni`DxRdyvY34+;E>Q3b_^3DVL1ySus$;vq9*{{3O33QrC~SXPpn@DBL-Rr5NJ zJU;#eFAFPoftQ$hNRh)ESZ(AA;%+<}zxP80K zXYmSE$Q((!os@pK{BLrmGmr(OJC^7Pky$j7jXMljxPenigW|hX?A||62gF2<25hoW z*U(6ny}V21TVAIB&$&5hzHEV?!c^m!)`q$ABW_+G9j2G|s=r@fJR&RmtgP%e7T~Xh zd-wK6s3zRGvru;&`Zltr3;C1qLqUAKb@MSJBO^E=Fs5f}>euXSU64FKe<)syp9#yP zug^48@95E^K$6jBChb^RT9T8O@9F;X1L+sp9mJm_E%=d@2Vl;Tcm#R{2QFO4T#7^q zK|!FXsDdG=C8s)?l!*umjw5fAM;$c&^UBJhM{o)vlnedoFq`gQ6r6as}w}j;Kk6-b_n7@8mQE>1cOfU-S%SqnTFPcM3$__ZIfV_fZKT zuOGYcu>>pZ!6!~5`WcIN($#BbgRacv$kp#>V$=)_4X-maY%eEy~D3gGR* zN0mDd&=Clj#ek?yfPpYAIXgHsWVXJ3{n$tp8{SC*uEUOTGdM?q8Y3V+f!_-{GE{xr zm<7l-0^lXEQVR+;x3}wxCGmygqq+SYQODdH>(*NUz`@5p7pCvli{m=-eR^8yN*}Nq z4rXR|oOdhK1t%$w;dC90rb^cdFYbnr3PFgsTdWW%v%WUp4!tH>+Y!}-OSuX`!Cf5u zq*pnYot*BWkwEDrTUAlPK4AL3_7PAIC;|}N<6#ks1hU%N0Cn|(605i4Od( zJef%SZ@j&EIF@bOH+mJJlA&bIoD3O~P^QS7WV(yWScxJ-=CKe8$rMrvWeAxPkts&+B(~zt8i1>wUkqZELM>d%ym8p4@b~uJb&P{@0RH`$HpO~ zeQik!#t{kb@I;1ig{-c6ck^g4 z{q}A2dU2tC=c;VY1q5;)#4+GM+2Zx8mT4O@kFGZk`Jzap;-(0w9-cwlRJlAVjMNl5 zDrV|;sF08XJ%#L!xq>l^ODaf1)wQzvfppkFy3d!3AYjG5fIJ-(DF|{zAQl*j{ft+- zOB_)wLPUmV8MAn>Mc&omlM#=LiRnoHMVQ(k*;$-}oDf1O1Vw1@73AK4!ih1|1?o7s z&RqnB^V1%{ zac6z(_GYhTdAlVTwE>|b_&Igg2Tmm0U95pGQR^y3GiKH z2%rEZ-X`sth{mn{aIMSa!af8o7fYXcPr29W+;#}XvH#n&ra=IUJ~dfPGt<=6ln`$y z*pvv{VU7$c%^A3#Q8S0G)N+h^AE}6Bj7M76bSQW|2*(wYBF2gk+ks9HqBw-2ztVbY znQsLDG&UntrQ>^xgv7-ifa~Ig0UOs1_6L~(y%=)BCxj=f?Q&6ETs!LccBrj>b>w3Q z?C5(66OA{oO<^a8)D6^87oqo>Emlp|jHmalg8{N6`s}ILV|+T zTdZwuB@?t4wts-&GA|F<>NawJbcA4z&Xqdfs@UrO7%JBFF|&=)0;jm|x!4X?QyxEt zl~lnf&%8QD&;ZTNIZ-oI29@h`xp4y(JjW|r;!Y1&i(}?4e+)$4vo$& zV`(wa9$SXq0!tAG{MWU#aIO&G2iPedJiE1pl2Z2Y;W_!U*U;L+kqmX6_jpk|+J6)z zKG0U(PWe4EgSO!>q1_y}qmz>QdV4D%e?dr(pP%1@2u=IIn*P_bpO2$vg3^=h`4Dg5 zBhOL6(qyU#9YX;Ih{c~_hLF*I{OU2&_~0FUM;=a~z=PN^P(Kqf_T1duc*1Az5s=(S z>v%hI2;c#S>D**n5S;`8^(UC_lKwHMR$+P@73>;XqWy>iMm1uPrY$HS&{23PLM?T~ z@l}iU+d)-&oML2zoM?4+PboX zj`iLRmVICl&?8MSO-<#x zQqaucBhLoZOxZWIo>biPC&~*`$dHP$dGqF9lzXE1mUPJ?5oL#vO0>CXS_4Ac!8eZ6 z%1@*;w&#{%Yk;mC>U<0xN5O)(2~^G-UHHPvZc#x&jKNw!E9oB)ATK8e9)5(AKfCU` z*atW!fdGhz*g;SU;}aTfBy3E^RdqMUUbi>9&RexZ)!7zu4D@k}C%x^URy9X04)-bA zsfN>LK410*e8wtnZibxt+H9_PViTw{#6L`WwQg6{!*awQG|$+IjZk}PMiI6gVZzmk zJrhtXjz^SBsAkZs&n+w<+5<8Z{HZ!yrFR$y8T#Hl&ZKMTZ}9{D?NI41Tnbf5As)lv zXyCLk-Ki`3P;mzWo=Ves$hv#?(=V6n!dR|BjI$i-SD9Tex}`=8+T4h`)D34RCmt$) zzIsZ=vZ5jv?DEi73JTg_6U~KE7ek+(w~v*HAxdWryVm%X4?VQ$KsJzY<5mp&8u$q+ z^3A1&h_ryKD>-?SU+%cy0ALBn2_c;UuMdTzdF(zv#Mn*1<@$^z80~+zZqfL!|S z%S8hbp9I+=uBT)mVHme4CN6I9z?}Qv^nr8=+Ay}`98vrVSC}0LTp!A#ebQ9q{-Oa9 zWs_rgxNutays(Krm8D9BCLGC5D>;2FEqW#6K+>DYk5Z=?^q5M5doUVSt0>`SU1^r*=AT zWQ5Vds^#KWRcML}9jAHFvZEqh?z5V%6|f*?l=9QXOE2>r2K)( zCA1ceHXddGn2;Y}oE#=jKJwcqCYIZ{3g5&uCmUta}PJ>_I^=o;2vYCQdnS#z`MX_Cs;pm7l-Lar3XCQTDzFiIcS zh$u1mrx;XWT^fX^+5J}?*{&QMMrdONZnMg|+G6pFw( zZ*=|CSs=71RY4aaEa>Nt4P_xZ%;D^L<40?k|P)E@C&=5ux2A-V* zzm09uF+63(Bp3Q;pOu&C>6dhMKVhRBxtD|E8d-D%?5d^)~^cHJC@Bjw{cHRR1Jb(u>A;Bi!$bkah=dq;q*?r)oW3>p8G@4PIvs{6w zS}}EIU})&jZ+;e+*|cBSyg<~Pcp#(4ADv@d_-;K10pqKa`RTd>I7QSqGFz-+7`a0? zFtdcyFG1qs?KL#_wIzbth;;{O^r{`Q?p}UOj?dnzE)#}5LvZF3SwMo!0IywrD11f+V&z*aKLjf@SKJEDH!Dch= zuc7UfmzFL@COI%6Ik^W4tG#7*9kzr?jtGX2u5at?tVgun9#x2$$l!WCIbGBG;{Y=?in`%PUNp^@1JS`hIJd0JF|S1m;KDdC8JR^u(S%9O zQOs&#FI6(glXBCIKICAOe3?aqJNvjg(;!{cD34Srm7t9Q3!+HsDw0MR0UZ^nfG7Gk zaG#hQ!&NdlVtn91@gcN(z;(Z^))8ltY&-BGY>z~Bzbq+Q1K{hnqLIG7gwL|G-0n@L zaBX#CL%_CGN$;NGg4PzhDif3XB>nJZ=Uc}ZLx->=#a>Un@mp*6;|>JbLf~L%I0xJy zgn&^RQ_;-4#t)r~$72pG8$}pyO8S#0Fp6wctrg6RZ0i7G4lK+=?vanQu8YeYz=}cy zY5{K=+yQFP^W*gisKa|YIu@}NW7D~v0(b>U{x@W6Jq4xW8XrPmi=rcZ!`8+IZB4pP zdRgCZQCSE_QPnTL+m4A2p2Pq{hw<(U5_Vu@VAH_#LAR~`I8YW59`{pi5e?0e!-qwT z3&@QkAuetW6va?!{8hpxcwLB;*Pfy0nN)vm9i933h*_l=6fnEi*otgp!aZ8^_m0(g{@PpcH%glGP-=go3MYCJklz*gM_Ud=e|r9OGMzi#%UE77=Ao&1U~vX(JOb z>f*Nmg-4QspI|fS^YihutQbS+poVLqo;LzGn1>Rw8^jNkN;WKe|zqW{Jc zS@H@~{JjScv=vjnp_tUqjNz-F$m$mXKD4qSy2H;NC*-)Id*ziaHF2zu=*%7T=SPyM zcZxv5^`j>isea$B^?_Bd50;Bxn z`}f(w8L}bQCCxjg=U=ur?n#jMR$rw$n}_jtm)yqIc;$7d#2J;6xZpIgG8O0s0SfYm?#2CYWk1bQlthS<-#c$u_1F$i426eUOKvR zMVB4dTM$an+S)_C9gI7(%ZoE2N_2FI)z%PZV7XpgrlcgtPq&~h`v@>^UC)FRCI_LA zP%KSZ+c)~C*c~KyfyI`~i?>epB*l@c2tfxtt$0%My!iJ+a-b#{P_1iVfQTgw_=aFz zyuoeqVL}>0J*E2O$_?03-Xr`>K@{NFdfbl<*^YTm%ti#rl@F`kI?j|EK`(`Jp z@Khh}K-g_fmRTnU@w&2qOx&;>VGfX++oi%zYKjX<*Dca8X~L?oKltO0)JQ#fLLdc= z)k7WWZoiKtW|nUi%(xE@T!bfrM55eG$SM9?zWlrq;RD~#_<|jwtx8qdp!|V!FW)|X zeDzI+;>EUYCpz)h>tz4Pyk!2EUH)6Xyu0rAi*x3g8L&**~LLB-&KHtUsA=sS?=B zu+7KY8{rFxrBb;l9tCa;A$-Vu864zg*cMzWL%V}`S=6C(Q&Snwo}u7j9xWh=bAe$6 zqJp6_CFdq|I7Jg1aBaSi>a_2VQzw+7Ve<-au%??%eMgAy_eWLXaf@G*2P{v)i`%Gh;Rz z<}Q!Tp{@jS38V(O3k@-X7y+Eu_Z~nu^rMXzBf1Omz{Q&BHT-w?TnT!)AGLqZ*^hVXXVxTwHE~WfO@D!FbBb%3x~nyzzpzq}jR^ zQ{EbMnJzFm{;blbQ?axhfF*XiP)Q>S%8(Bb!C-QdaE9SC{4IjzU{Fd1tb5D(9q{lq zHg4xO1i55lf?;D2!{4F9AQ%+x2#(8s_U!k<0w{9K6nXai`Mae15*HhQ%q%am?=0+m z{fG2^mpfoD~pTRSmN$zswO0Q8Z;GW3OUy}C+ z8Un}~@-4mG_Ju}@m6X1S%`#NMG#>!g0N~@HQqKA^I_GI`KS?-u1q7@C(E)hDcTwtS z^A~~+MR`u3r*%m4MyJKE`5O@oyE% z;#PgV4PF=@1A6!qFsRSX{qfA7ek|}TJNp-OG)T-zR@}Su7T6Cee=f}eaEF+Wwv~oP zji9Y^9zQ1K64rEdYY+?~*jU7@;#_(wL{&*#RImkr909nAS$CM+MpQo+BvjSa6&Jd% zm6+bS0=@+n>I&Q(;QsdFXi~YkH&tR4D>(l|R74=|H%dXQEbs_)%Bc>URrVP8O~9}~#>kaW%K#^OBAuzsv<4>~(cSclhN~4` zY-B90{a9N&gp3fVkoQu(xm=W%p3W)fj$q3QJdPl|!RmS#80=CO;N$xqF5O!KHz0gV zV6*zx1b)ijR!IT501`LHtdb+?F2*(!bTZKNnp%)x1ds*86*!Tm3ld45qnXHu5{)~q zXXxlSjiwa_;E^0$4>|ZbonCdXYtwZ5dB>a=>W*bB%&e5cfJe~tD_5@YX@VAm1`UB< zkS9a0o!uxes6>YmyHLzPY1Y?gmT7p0Zpyfrutr?LZX9AtN{T`z19V49cb02kXiQE7SkJq7!!?vC||jc|RNSwcGM zJ*}rlFv$!&@i{=cW@cs>9#k0s!UkI)hMyvN+&?(D4qG2&eN&qxn5m1-09#MMvkI~r zsy|TAX0MzGfexZmaSzb_0{(SF7k=eRR&X%U=HLLp)jXM`&@?y=1O(4D(j0*Y0I7oV z2wCPhV%)~NKzJR3xC49*_8qh_uwrm>a)ReZvxdt76st@6i3N}g5WJAlxexk+b+n4&q(-10FUH=bkBg2KpJ3 zK9IfC6Y<730`w0T*U?NvfV8c)QqVvWDIcA=$OQ?a1C@{{=ivk(1|YMRRs(h*NLPx{ zm4oorjOM=?91JkG$Epy|Fpwr3d02~AaTk#Gh4+kQUxuHZy8pGvFV`DmOW^H|D39Kp zeAjpU)1e=6rk*x9ZRU>)+y=No1YShfL+_6_b}`>bB7s`*N;F?RKpya&{l7#4=_*{Y z&78?0j1CcAnIM&+=!NOU5B5*kv?p=&Wm_wWd0T20kU53$$Bb$&Srgab#}hX*NMw>ob#Y&6i(hMk!D;6Wbxyn#DH*^xTT~J ziHjiE6+q`y*imSpDZ++|QSZ;-EWyEo@P#%=zaUCbPF`%UT)*knP|B&w1R*GH1w<}j zD#3t2G%hXOjz;gxm!jn4>bkmg(0AF{+M;nM9c^&->jgE_n%h+pvk3MQguJX3@w!}4#_p0JC{OOCRnuydQkhRaArL@DuV=KiL@oQ>+ ze&W|Jd`?JW`F!K5&1Ga|kyi{f47}ydz(5=&54b{b%#VXpljqnRT@R6b$rW*sa=J9Pu}XLO=ll`ONM$J$DXc$8iZ(*N8bI>m8R6xM>c89%(h1 z+1cH&{u5yq@~Dk)@I(}zSSghOt_`s^+hxap@pnG#M`2}l5Ex&FcQ`@O&lfJN=;+pL z<3ccAJ%c;mf~8zvYWC}DkJpTXZx8BPs_omcJ)#ss0%VERz~p4}kkk0C5=@VAsg{fhAbTo4PxvO$0;8l`J`rAYUX8zem7t4(x)7>hl}B(Y!u}1G1ToViOL>q1;1on? zAIeP7tsL`L9)Z1n@E~k>8>q{HX?OSddYpH-=TMhp?*M^{L2hVsF`WXp6T*fvPSu4o zwjLhPCf*7Od5`$BFHz7m7Iw0;vtt55(80ez9HBx=KauznEh5N;T61ym!`SE_Df)3l zv?x=7b;*Ya8x$;N-tXND%=FDUbFxbvop$7`Qi)5+kT)3s(hCDqy`nSqXQZ}#L zphMO0mbi+#@B0Fn6?H|5iemxULBM?M z=mtWbu+$@pYZ;(2cOXQRju|ietnl(khJnb3UK!^bstjk2Q}}NV4pT^-TK(N9*7~r) zC%t_Px~wdMX2C!9nRqo!r_85`Q}K4~0PZ{nlw#=S!1HqC$-iFuk?3yqWJ0~A{0%4x z_sL%QBbBV2tP>xF_wRo)R)0wxTUnh5KnS4IK> zD&fbwhK^S9@}!4 zs}sHOXlwAj*<~jsEsa``hytDbgb%3R=n}d9PDEk${a5XE%{kQT}>P zcMEb&ey*sMa@O+J1UFmej=JH`_~)G9fo4p{*^ zzzP4>w;pSYb1@UupImkVUwz@F<|w|ykTxDkz=-jAW;&XtG1jLqSSK`rn! zy3rvVvnVD&J=1n`ksdRFp-L`IAfzxwab^7zlllxdhuOHO-k>%sFE3w-VVI3}Djgfj zQ5Y?+TAPbpNv7SP@7=Oy-9c_uDp*!#dcOdzg-y|!eJAca1LQ*6fQR9>Kf6VT7n(hCp)$&BFwS)dw@crAvi?B2Z~ z>EmLnDCUrb)}xElnux-a8)(@~3ZnA09YY025bSjDsvt*9#(Qzf!M1Qq&BN3MOx{~Q zjRyeJgXMlwhe@H^FFE|gjM|#>U}~k*HWD)A9Npq4J~Q}wjXS$4D8dnWyG1ChSKsNvpf=#@Iq?MbOe;1 z(c0FgLR&;>{uXnIX1h~r{y{vEVnj#&)=Hce0T=#ULG5h<1mYi#O&=;bFTZa)Ni}xW0cNfSN!>V=%8EGZR@C zmiZ-^aKb#4z4?Kc$o`h*3@dgI#z-=l+pxR098r^i7eu zdRN;q6C%x*v`WcqD8l~lZ(LnViX)u2*VW1E&4~Xev*2nIZzGJ>C|tz5#v?CbBC@sV=Zl0KVf}jm78HyA`bA~tVJ1d&c7b0S>3+oVs65uVD77o@I zdp40MB6J02r>8;8L(_reToN7%{0$=Bx;k|McHdSVVuU31PHd6={d!1H`$U>yy|Iaq zmHhSBP=9~*kW+B!{y(t4`CWn72FMG4>QDr{vzEj#M8VjGWYPCE1l7V!jqsZkfehgQ zCCG`6>cwdDvyJ-8krQv6Jm79XkNSe}EkmK38Pq{4a09^5JF5TZyVPFfO4K?uu4c1F z%=t#P;>?ff*{HXodM`SII8R-sC%FWH@awRS3(J@($Bm;TJr-*2|CuWwiGPEv40h8% zbc*hBy}EHJ#6KZXus@33dYxM`OjH?f;`*jKb8+({^x}a~$BN$%^XXt9VxF0I{O>%+ z%(fq+|NXFC$=EKwnVy_GRtrA3v-R zhP*h$qI_p+(!m?>X-|&{7aanMQB4AT4_6DIL$!gyG>eBw<_aYfvSD)jO4i?%AA^7dj0n?ez@}8KUc-2s{IPC@g$Dy>-qj0a0Rz%gfZyL`~J6 zb2fO;a*|Qx%=e+8n}qcUx@Vjoz^n@jen+N;V5NZvhpiupaloCxDM5b=uw%uPPx}v* zz@Qk58Vhni0Hf%KI+CPU6-R$7t@wrOXCgcRu_?7|ui$KQbj$}f1`W4S_M_Y?qLl@p zGMhBJG%|6cy^42LR&95(uFS;-hMUStVvsT8AVhERK;G-6aWbEny=>{D4l zXCVNCi*)3st&}W)hK;s@&R-SeAU0oHH4#<3rzQds@T;V*cB2vxq{B2vn(f=icG6OW zL$2@OAnuE>(VJ#?)5JWGwcm*yEiD#BXE^%AmIA|-~!YcE0T&^AxY8W z_XS$o2+?WtyMXdh=-xQ`4adm$?`Zo`DK5cUI55rGcs}3g?_9GDnG>Dx%-~-GkU0M? z%t9gos(R=+Q4DS`->ex$=x-0YLEX`0Wgx(S;~okNoEAt#6}dfj?nO07EV`eRN;MYV?ys8FnsSwACN2VVoI-bF|E>F>WWKFwu$l=Tii*-!o}fp?XYf zZn9oTcAE>@!p~h@m;F33&l$){Wex<&*h$y@@z~+v16!3`d6BGKP+g!#qpNMu+kwxb zCpX&OJ+g7Ar1mS8a9v$67ggY=|6$2%mq9a#t;f+kZR`^CRB2}|F?$Hogt0j!5}1p_ zUjh04-Suo7gb1buSstuharcf%;EwZG5Lqj4fzKMcKbQCH~V#hExzC{OIoPepO%OHD%NNo0gM*6!A zB&GB5^AGI0Ne2RS`_`=}3qcW=$L0JdTb{%6Zsm~Yj0;RyyF@a8XxyNp$+>gr_Mqp) zV7#{SO-WoK|G_v=B?EQZQyd8c&uio^I)|2P^M{6o$zclGNNaiv=Z+EJ_gHir(#XOF zh*nv-k#-_6Xz*l!a&pL>J87vB%GO|Asgj{6+(J%HbyGQz&NmC>3>OzEc&d8DhVhmP zfj`H^#bLl1w99CX=#(M**3i;=^OP9esVs2#um|EKwfPBuO`p#K1`7dN!7TvaaVBS; z$k_$%eAK>yzCk=nYEI@Nv&Adku)gPi)4H%M?+C7eBa%=fdi;K3n!AsRM^chucrWnj zL_STF_5XCeP&zCdz}SOlx`r4I>iVtf24IDMTho3F4}(!1TU+SczzVP{>_ko|F}?~d zKCQYxXfwj+0x26ZypbyE@52XD>r~bp6nZe^acf3#g&-6GCuwiQ=Jt#5Fc3I09(P8h5`w>G9Ei5p? zg*XS{(ONS`VjkUFjPL|Q6mCBZjA;Bcv)U93-d~j;=FBAPg83y-EB4qH&ZaJkfKBSw ztC33I5ZgO3@kdaKW9!ct23VQ=xv2a@LPDzcLY9lelU#|MoE-Ka?m)D5QwwK=Vv(-s z`dE613O78_^MSO z2xB3}DXGHBcq;HLBjYEMMJF$s#p?<)x@d@1WAoZ4; zQPc+BHOy{2oKhMN)zYNj+6K8skb)gq{f*C7YNwg)V_*Jr^5kT4)x zFuN^R^ob||nM)tp`J+8>xZ{cMFU!D;_SKt}=$-#0DPWJp8UrW~dPU{~F9iI^r?UoF ztsK%kd@@1eX02uaw%NS%;>@LD59B-~h=@}-{=cP zNyx^B5l}XA&q5n(PV^GIrg>Y>L8?NyqwTjl%+(_N6<59wY0&ThJH*D+^n=!n)Cjzi z0%&8g;VS$WPiu{G+nE>{^% z5&b7SHZX@AJx)f~RF5!w%gUPxYZbGe&T_|wd+d=ww znu~wh(O&XgTiTQM1Pf`%Q$|G3M@=e=mTB@)17Pu+Be79)U2{=;q;boL@P z5_sS=qb>lX_{hfm>BM1W7PXEvj&y1=UlW?=7M;})h&dh||M|1TtO_9wi|wry<1b(d z!<&T*)eEyQfF7XM!rGjOy9LzrHL?r%qlvp-^BRabM@uN-(jct-|JA32;^u_?roPU( zwGx2MK*s*FQwg(}F@nf+z6d%e)UF73g%}nED2{Td=m|X0m#hp07G9_vW*BT9pgj;& z(OrCE?7-6Yg9+Mz2SJaW&9|(@!vInY0FOPi&Bv!H{1N9S*4@*Sgv1KbL%v=(=4DQ~ zXwq;_Ovk6SSmVmEUtTP9-;8# zr(0h;HVM?a=PfPoiJE}21~mcv7D9fnbCp2VOG}rh`fH#9p%1MMWfU1bQ1pxmVk?Nl zK;@u&*w46dx*`bd!H#pY`K=4!nVKrXL;31~60a;SLhJ-C0jWaYSjBbdQM!Y?9{Zgt z?vMEhnA?j?Ue61LsL|KY^fQNLwSmztDRCD!DA+{0(;I_feD)Aq^Xow7gZCxC0O^sp zLQ+b`>({SE$583Q`;4LD|L=S=qnj3kZ`Jg}vD2;ewYT?<@JVRZ-6w>!GYw%e>VNO^ z{XM;$$kvA{YcdWM@tuT)4MbISPBv&$2Gy-MmAjck+heqz0`d77p=}fR30E1#6O})+ z^i_6_^fq46IJP}|kijH)n-Dud&+T}%i|k+_j^F=cpJ#@*^ZtY>D1n*s$Ua8FcMxqm`83W2%* z5qSLuOgb2Lb)~ss=r^*PDFQGw1K!(-$1vE#?lDo`S1JY-h8TEb6h_R<@edw&!`6a^ z6$}l8Bk)z|#EWmDt;Ad)sJy&TW8yeM&I^*dd=|^4K@a~T5}v2uu>nd1zXr3cY?$b+ zzvVFp$s#<3+!w6@VE*ZuECIn zGo!!a79paV_j(2LY{7LM`4tdp$bq80T;vH36yO2OEU2`IZbR$o^04B@EjF7)Zv~Fi z^8UjN5R^#liFFKo>BlsDej%Qpg2b#F_FZ=*pq=%|AOFG{deQkzZRk=C9-JW#4E-@4 z&EMcAW*@Mjc>=QxO$Ykj*RQ+RnQ-)(mV3H@;x9QM1!x)(YtDt~O;)^`zu_iORb4O& z+W9XnfGQ@Z*ya+Nzn-!<8ie>DZWkUO9AINYG8RVP%%QpyCd#EpVGOeZbhlD>uftoLZY$YgzQ-1?Ze~vBM0W zOVwUJ63Nit`Z=WYWf1x?SsSS`({ zUlpvqZ`K}3AYTnMG>n(yiXnqnk+l%9mbn;tf=?4bWOY4tN>Z$fqErJ1O^45Z>NWCb zH+;XhJszjp@Z-EBR&K`qE%Ihi5KXrj8Of)mlF7o_+8XVDT%=6IF@im@`qInpe@RO2 zKF2XVHwfs8y3{-T5fAgovug#Z3ESn54S)MN^iwzKB)wV`-y^$Ak9Tz4;TWBwv_^X1 zR6O&F*b4=B5dx{D-bSH0f61Pc_&Y~%YQ*`8R4_dQ&K#@P3AwO&JS+DNkEs<;B7W}1 zx1YPhh#ySd1`ZjxFh-~nSTZ|7DgUQ0tFA+a;k_DMMw*hDnz0Dp@&{#vUf$EMcPoiZ zi;Ipr_0nhJ-&4!1QvXU8-NIbnVJqdEGL}bz!r3C1lhd(PtTYZ2ra?L!gIgFP<781) zGa|gNBUPZmkwd@pyandIBHg=#Iw;9RSJv3$U5@ODYjkmP8trQ$ z3<-Ct>g#Vc4$)YDw)8{Tt7}STre>z${Gb&5!d3|ME#>F@<2>6Q7wqIU(vtFU(*oyDV4lgk9hF}x z@oy=Tfbjnj^uLL=r~T<&yr%erxp}j-IJ1z@`o{c(yv(z)kGmEp-6m4YzWB@?on(Cf zn%3+UF59U z+DG|_1we6dwOqQCZuiy3j~7eqR@#^+u;J!muZ^{ZecZ9PG=zm|-=(bWYI$J?3b@BK zSDKj`V7VJ_>n(TUAH0}ZSp#W{owwg%5r<>uwrf28or>1iJ(OKXSYNyNUd~zuF=_i3 z0!cf?T=;_6>H{_|@1(Iy%osl?Ci^$V*)L?Is=2E>^%MAKodp-jv}c#3aNTyQ8NmUb4WKpRYy**h=X5Yx(dQHiKO%e{X1UI8$9R#S6kPHBPE{RRP*=R-*<1 z7fQwO(Zb*K=f&u2vCstT9Z27m!bBoHSKiMWu9`Y={$U7G!XLD(Vkck|* ziZ4j?^9{I9?}ezaPha2fPwd+7dHzp*;hBa=8>`aocXf(*B1c54e(Hjz#zSKG zfh4{y#@K*Fq4=ElpIb=fk2TK~YA(P=`pk7nDctyQvp|mc?`;}&wD)dZ`dZ&RapCKd z3L@$j?p%DvggV)`vTt~XC5Zd2;q%!iPy$MT<>eu?np=IuRyW8nT|pN}TrNm>jPEdY zqpl(H;DGN)*IDMjC{~PB=OCF6xC;%(+Admt7n?H?vsD+mbSfqF>DbNj$jY`j+r^Z0 zr{2Jd({Mf-A&@Pq~xFpS_4*l2<;!JbV;{<&8Hm-XEVF+s7_*)qJ?H$@R%V?VG1~nMGO)x9q%l1fUzo7CU%DD30*_UDF6xSVv zjlAre*O^i->0Bt~b-492VVC@hnShbkBzX}hH3cTVrLB&C&xh;~(IwF5l{fLkZ!-7J zzIf$V#rBNYLW9Lc`m#y?BHI&PXGMjC^tF;tRcJ$Drw>p{Al&ZH4?A5xebe-7(uiqQ zo^tMZbEZk`ycMR&MK&Bef>2cGXIR-(#L22;zVFY-Kih2a^0d@(=NBD0!%mECPx6~k zOgqSgDgEN!8(pu$6Pc8h^qhhoPa2KY&}-|#dhLbHYV>w;Hfb)NFZ(9dg(A3i-dbE+ zcP65ZIi3$b0&ckX`rFUC4 z-$36wVtRoB?@$7R24+9XPErTgpakblf!qs%D=;FU0qI(p3q630;v2}?F&o|8Sis>k z+u|Ve8Xy#XMIOXlz^)K(;tX5HxAq<}T8e=49t#+R0RRp2c$T-DE&AKs=RM~!+9p`6 zcy$G8=+c7bxN`RzK$2$>TmmfL%ws6_fNwomMNaRjY9e<+J%XeBckTp?Dx7OS)suAv z<}AgTy}w7QLxbLh@nAteJZ#&;h6-A^<2P}g?({a*rTe{98xe>;xHY z(2dveue5gTY58!b^oId2mUcl8=5k(LVx|ekW$+o=g4YFd0l4@Pd<-W}G&ww-AqHR3 z$bc5{!;=HoJ%$P4)%z+D8!c@Oiy4RK&t%T^so;P*ai|9ob>ok3Tsty%Jp^_F$J8BHBKF1_ z0ABz>U}yaI#iD{SL@Dv5K%-TO_@)WrObw2AyFET+$D9>9NPn=UimYPw__^E8jJ!RK z%(5tFw4Z_ID!KU^)QK8%rfbb2>?|P3`=D6!!>Bzm?cfh}TAR zrTqHhAV!=KFoGBt&jPH;hv-2L5@Ku2ibXDst37jiSEZA)XJGSRdV13sG`7QcC{mq+ zWpx%d1Qpow5U7TsWEevLgp$)|R&U{iV}!@KOm=Dtkw&P6&tAJW0~Z6Q`?njwfngs? zZ?NX^{FPt#+H}&bV&)-olS?I^m|*-UGJheJ4L7oKor!0l-_FCREMBOu{XtuMzqP}- z_ej;D?FQDz?tL31wm(fMXE1?K>O!*jQwNz}AF0?T{83Uj7KFSn$VO&#Mq%MK*th~F z_rfzV3dRX;8FBihwRK$q25Jh*zSA75?x1=BytAqv<+F>Hi9DOy5^xb zCx^MAHNYC{XKY633m3ySs7uk8S$$_Y`y7}AO3B?ra~o`{gKrP|bVXRwVhhvi5*zMaB$7(jP6!;@4_Q#`{iD*qZM{!>c<2f~ z%gN~}TNqM`%%q^EHzN1Uo~dDeOYWq0NlE@8aa95O=j9%oYP-a)=(YoQ zQTOS~KZ^70yJNd>*`W!C31k{n_9d~Ffn?)N;Wp^p6ADDC6N?5HG3tK6_`P&uexBT? zhs#36`#FB+W65`Adll-}5@fKg;;ko1+do>7nOk-@NC-hF zxT&6=BNU9)VzBQ0Zb<&seH9^}x0q-LWx%oyP z%1^H1`L^pU7y^6=I9CW{$+KsGagn?nPa@t~ zS5N2s>6XXcdS;e_H(JjBT-rq2$ZVq`B)@yg9fyZGF<#0YGXf{J5i;kRVA?*%x;+#3 zGunK40U((Iwa2x%CBRN2EhR3IE+y`UEZK$9X|4dt?puHcUPC^?o4eJVs;>0yz|x75N1n_>Jp=6{g76U zX>LI^{a=cod(1QypE=V7pmigm*;}f6Qf_L&>{&+khdG=5yZ8p%XAkGH;MnGmO)R@{ zv}#YPD}M9hny>UN09x8ANbHH!cmg#~U{0ambf_7%FYC6OFs>uG_b)RuPMTn*{YJV* zMvKrvo6Y}qK0BUJ936Nb=9KP?$gr@X0k-aMw$F2No=&GbXQ$lJi|8ouk%-|*2Me{FHI?)vhDg^Xl;MCYVfY}=LO?j4x+x` zVy8*p>4q8<=YZ?>zMe9-vWsJ1>{ep6x?y{6`|`!3ytnaegi-!}1hVpLdA+2_fH1sG^CsgbZBwpYy09pzdu z^Ub>%C>pqwRJqnOQaF58ji>n2Z55u=?a?oep)3lzSz{rsu(Gm}>$w~&+dS4_a}o86 zB>i|tekwMDDakB)>eKqPy=^ntd@~b_@!VqF>~Tu@8&R5+_RJE>0ne-1!l*gek}SJz zEABOYM0WpPH@$+3#^7wdFb|e;vIw&Uis5n975JVuhtnAhPo>06t>L-_-%jZr8w(pQ zSMpwekcev0Fyo?|T3IuWYC31&b=>W=dRo~gQs++*o1J<&i`JhFrdm!6`%aljwDU4& zjhL+s|KL=3~s$89RXa=&h97c&UwBi(meU#SWrUY(52=WhLep8={ZMgodnV1 z<(u2LaHC0Fk~v7*!6V8Ff){Bk;P*Vl&zRtQ7NVS zA-!Q^koVda2h7&ckK4Q1c1Dt`+ujz8PVqjj zR4QFlP79qVdnKSJdt!_8-sj07YCsICVw|TR2n^9!jvc(zv=(8u{_NhbT$bK)a+QIIKrw&jNSU8Np8qb}e)Y#-zOH)JL9GMAdnPWo9bu&oj(PW8M0d+Y z9Sh^n@|z8*slubNG z=DxA!&i#ANstva2_ZVN|yBh2L_;^KUx=G%g-<91P&y7}NW7+N7=Qvkit_Ud}B9V^1 zw6s*+Z^XOv?eg%(`ter^cHhg(ACOD`?UmZY7ub+w5%<^O(v$at>W1gy?+23$^DJmv zJq_tGNYrel7BOem>dJTLVGZpywQQz-A6J?oFF1eh(@N}_^PS7?qgH6YN#eECG4Q%% z=ZjCg9=jTmT*z+2Qv716hpa+{Gx~YM@ViE)BPzB_BDr?))*4*uG=tc9nFtQ{@%4n(+HwcVsuT@kEtywGyuR>2m{nzn6DGlx#_ zR$QKVNNEbN{gK2r5^SxzH<_@z73@xOTtg z-1=MLQ(Ij96Ynh92E*O7UuWmm#-!u>;|~{Q#+AmGYh@-6cuXDFzO7wzWlOel!1{9m zE1?SWF-osG&q-z!^n(^(->_fQpp(#v=g?}cXc85CYQbz{>!3VcrSa07JAUq>@YSa2 zFoxUXoYs*F{NX=xZ?l+wEc`9W9l}Q+$trL=At5AY<$8Vjmu%7H3=vf=p{ES_5<6Ol zr~hI$9KUt^EJw*VJGQS%%|rtle8Il>k+eu_`E|XY9<5R3Bj+5WwfRrDu}sb>(?^>B zoz|Q|y(n_3X8ry^hxdH=brHd0z7esz95q>ErTW=?W%OC^`Bn8b_wLcZy=U+4dg0$j zLQYSjRBqqW6w1`tn!8V3+~@Lr5x)8-N4jOxKGbI062J1RM`5-^%YnD_!FT096f^*b zk(6SF+kV*a?E#14{tH(5=F=%Ea=&J1ylm8??}Z3k$s6t7%M;Oj{P$-A!6!P}u2H=< zKZ~Q43wCDzF!Y=utYFP>Rs|i`FYPy z=fV=X);%{Y5^9G$J7_Zn47? zQ>i*`9zPwLeAf4K5$n3wPFqgic%siA`CP-``^e9E=Xt(c#`Mtx6&X(KN^i5@kL~U0 zxfvJ|A7~z4v^m--KK7XU5%mo!;wea4FQze0Dj^;N3;cf$5G)(}mrz(j-7(&zL;f_R z+oT{N^mwDjj&r+R;!Ow&iK4cDT*d=%r)Q-uHzLu{*U*>zJ4x&RWs`^SgD|Hp#o4F|YrPn-N53 zRB*2OA>%W}yYu&8rcYQZeg7jgUF$$TmC-Bhn&(_JCUo_I+|)LnG3MMM0!0GGJB6ce zM@5+`UM-XeiFqz-X&y~K?j0JXqM|Qh6E$fXz40@kRZ79A;Ht+XOMPu8^IL_VU1;f> zJ~+N_d82u*?yLVfhS9%cFYx?Ly*EDLa1lxCfaa;&*6kIKDoju0^F=OxFyU#xT^gFf zdOv02)Wat-mRWrhBVF_77J3yIgm|7(&^{7A(HfG`p{}*_u*8F-wbY~$tKwp3`QjXh zwM%-^mm;d7hHj22ex2){`Y9laV<}hpElt{QG7J9LsplG_|5o|DWpZ29 zdRHc6>GH3h;FaB1Za-X-K3wekK}d+pO*Ze~a7-%00pXPB@P41%-&yK&8!P&9JD+kl zd(CgJzr&;6c{#?o6L+G7bvmte{QnAw3U~E8e&)YfGvm>bRCljqrcJBq57~B3va1Dg%(etL6WtG0! zReIU<{jeR?91h(schNFsz%=4Oph%7Hvl!~)0%%nFoCE^Kb z8ltTuLF6T(Jj-$G?vJWsMUIq2)D>ZJn3#Mz4?Wm@5&r)HfHdfT?^t>l&cbz}r>Cc< zrzg|B7|gvxELfOA^9aq?eEwYCyqj1Ky(MREk*z3T$0-tae3s+!3>+23OrfZJ<@G3$ zm)I%D7amhT)0FUfC|F|5xyk!}Gc(CZR9xt@r&(qs;<&#P2-;fGK6*AShCb)QPu`NBT!P^D(zjD#cCj z>Po3ZBBmrsA}Oh{DMXT_5Q!x zDPr|ewkjnZC)v}lB5_HQZmAgkSY`w>002xK151mQA|*-5lkc1~z1DCSGgH}&xMX$i z1GBG3#1avu9Cts>+Qor_K*XInmi^dDKyJV`n^WPYeb!7JkM_w z=SK}QlH(0kPS=?zwWWztO3I`oorVq(?H75Pv#@0JV8%UW005X;l%j#1Lge`m&xIo) zuVoB(@?+(E`^`gSOMd4K-|}^lcrxbo_?c8ik@@Y7({1~sZNoG{hDM4=CwlB3Ds7vX z$kd4ief{CegiV6TG?a7?ejMnCibTT+oA;eCgu^on_|oaxLqo+3002{l8kp~_6tg0d zq~z-;fGJ10qsU4*M9!WUc1dB5 zaP`94m@h|0BjS}kIgTU4{XJ4jDV3Cz+MZjllNUNWl<*99Z6H_9Taq9p&YjiYdCMb}9ZN(8 zni)%ld*z!mIm(Vkd;8<^-(J?jVLhPJr)x)_kL^M*0|3BOa&a?Ea)+`VDU<*E*V}u- z!*58(RJ$@-RvA~UzkbNq)~qL!(P*qs4S)K@aDmNC%%qePjrB?;cfGEkX*HFt)-JLO ztx-1oNHp-OJ#E-dJR0wnGC6qY zIyH_xcn9u_dtxg^%8@Fbj7IvqV{zw?2gPgedPwpo<~getJ7X`txu*7(?yin&`0xNQ zWxXXwx-$pYom!E|bQdNE6BCn0tT7wz7GQVYz5by`zLe(~dT&lws8{@Mn-z_|oNc*u4Vu>|edqn$XbOHEXk%YV2hZjNUOlvY+A zj79s$^@#!C2Jwi}GTjA8r=Rgiu{5=~3zHNx6Em}Rw&BASx30_ySmyBmQG2|qyu$wA zte!}U_nVBi7ExH_c)W4jjyaA^f;DDEsc3fhF;Pm{wv&h_B8grF~-?-RLlSXFy$~a6AL1Z+0b+k3-8R?^M3NoFSKOOaBG@f(Wuz-r`)C{ zX33h7T>P=*!m31mzS4C?M4Hf9=*)_gd$^-5r9(vhF>db?dtddO{g9+mrYS#O?R;{T zqibWmwVZ;A82|vLCZ>x;GcgnQ^@d}y(7Z*}UVWsnGz>LsZp_M+=gw&fTQr{+@0~Q? zdr$O+$S{Q0>k!#eI#Sw7IpMHs_&_^wMD9Cm{P``TyGLVkN(vKydylj1W~p7(JT6qs z001y0rS3EfAq*C#5MH7A{?ilRTvEMcT440MzTFr2*fHbGX+0Fy6q6(-#Uzx^7c+E~ zP#jNEG_FY{q_T;07P72VRxfmxEw+tu4qso=Zv-;{08CNL%!-6yjf5eDM+mRbyqkag z--E%@0{`gQ?9RqM(_p;znrdzLDn$+{B9cl;K|~~_Tb4UFFHEjorE&v~7~^W?5jtI6 z^Rd3(Zg+?r003YLVIpD{M1qCRY_RYM&0~53|LrF~C@#t?@Q<~yRf?kh+}+FJu!_Z{ z?J%>_b@ur_ys006jNxzn=+v0&C&7(#ehcuY^= zd;jviV6d#fpIu82UVrU~mg5hYrePURyy4@ zG^R1_Vm%37Ik;bAq37l0CgP}=0RUjiVsbaozGOGe!w3KDNV{hFnrMH-)UWm4a9aHF z55LnANtvIQuj!g&kF>4}007{+#dK*lD{?o}R`0!Y`q2IZ3vXUx`tv-d!77t8uJ-KS zx#QQ{HDWCi=`SrUO(qf}Y}gF|0B}7UoXv`vh!iPBOufB*yLY|R)!8w3(Gpt;kH?dd zcy4QJ{oZ%KeWsyNW1%~aqm;_c%XRD|vKasXrl?D@nJyO1q$H92;B3<$fA?HpUw6gK zS(-ODp0G{Bn6!J(ZEdZ;c;~3Er~@V@mOqf`T+m{FuB|X zR7@m@n1x29vCx@yAxvUj2!mK(uyFnz>u>+~ij@Jsm5hgdUXLymDThhY1eC9PcJF%T ze{KEXgQkv-P9hozW|zm~xv6r7rU}QfZQFKi8|#|^0AOlKWwU!Cg~qHA3&A?EE`(03 zv(T9|7J9HEIDg^XB{fSbXU-}vDG6A)hUq>2=AoKptFN&WU7hWXr%#?e^KRqmcMko( zy&s-CAB{!_`ZqY3-M;4M=jB>fPEL-dYeL|_W&i+yG?P*~w$tC=9}0)NyL$%TNB;j6 Wr3#_1-IJRD0000Dd^e%j85RgzoLO|(8Qd+vZq`SL2Bm@McyQN#YLy+#0Zs`(`uDkjE=H7Y# zc<;>JGmP>%=X1{9Yp;0Lv)3Y2URDhK8U8Z}g3u+zg%u$PHUxs;_K;!0Cz*Pj$lxCo zJ8^YK2*P%H`hi(CA$9{FVmXO?a#FH2adOpnFou+D%rS>uc9Vmjz&8e#h{_q*p~NuO0YSE zu6ZEaxvXg@Nv)@pN?&JdDAlIpPGC5IBMBB32~(J($HzDA@ZccfGl6ScW+YLa_sF&v z-?5|1hl(>F-{s8FlQUE_L_{ASAFQ>t=x8kc?Cj5ffPc(W|3k|6sx zkZpvvtKIJpuhqfgy}P-&@$mF)X4Kl***W0xT{dDKPrXa(B>eR}h3RcS@kiSRqk}^> z{D?#bZNldPSnvXvMs!^!NC=3Ci13J2>H^+SfoytPZo9BGGir5armnP;lc1NDsi~*y z%(Z?kjsPaSKsib0hczs$4gA!U(b^h_eyH;O`$c=KH;1|7v)qd~@A{}nSy@@x+1Yt` z(z*>0;NgFKqRSbdU3eOvyAyeJPlG*Y{KKNjJ7i`D6lCOZ!d~(PZt$?HYXn53%7Aj2 zEe?)!5fxF)QIu!TzJ@*x6m)cF-AOan7_zyEiHYUq5kd(;O!(}lZ->d1>@N>^bkVaS0gC< zW|YATMDTcT>>3yO?|0?w?d@S;VBnTGz#87Zeapo3qq(`cqGA+x>n$>~$NQc0>}-9Q z6O_<=z8oUrHLPE74O%u14i572V_BMi&(2UWFfdS1-b!G1%_IBIV)Z>kYuGE!4dcc2 z@I>#jM)MDMiiwH2@@i9~xC{;q6c!V!udlznFfc&!CsBpHgS&%+gFE-Qx6|||Ax$qV zq#}yk?nVp<2&ibin15gW1;d|2N1$Tn77RmXcK-%SFiB3%x&7fUK|$J%!)!XXI08}Q zVdJx5a{I^=7(jR09IbQy8{Xs0{vT?n zXQSL)Tw2pWq>|FocN>xX3=5wancPqR0A0*JcAdzTSn;`~5AM1?=_OvG{8jm>w6n9b zyu6(IsA1zzf2@$nTYogX|6W2A*=tbAQ<0gFQ0H}hI?Dg(wLOv!cJKZB_betuZnuZ^ z;7Nx@H!tx^2m7xAo`%27^0zlOx<5YLyKJ`KUl2@xQ_BiZ=J##WZXaSOj4>WeXtelsuWHn{>d**Re77FP z*mgQV+w5}4<#|!6Q^D`JCGGBhOa*CU`|6qLh|liC&2}m>0*OD_;g^o)l)8m z$NlQ>+Hfid_povE?NRgP(bAwvH46(%Zv??MwHE-)t1F$v^mF$&$_omwlWtt_(e8YvBHcL$r1RN9O z(8uG=8g9PK<5zh3zv8JoUJ`KFkV%}j-5%vg*)(qAKuPS@_|<`A{orn$HoTByWK>jw zvG1wA_xrko0|WT@_&YIdmcRQpPd9oaeJ__>pDrvNaA#5aeOA=a-~t=-_;A^d9cr}E z6Y=xs&t})-wu^boqB7XedcEJ?aJw8Nal0O!ZVpBg^S2pve=R9*8gRY%fQ^mqu-1ui z>1<$N@Vi`F%KHeoNY_vYqd&1xQCX9_7mXqXljLM%X!UszBqAcB%rvBsN-3T6!*P2A zsA5ZD;m#>Hel8QEW}P${HoZ46;wlBls9Hs1h?RW6yXU-sDdn^!BD zZftB!G&bncQ{atA%OwLfm7^&&zwm;})fEz?@uoDS5b1 zf84ZR?pSSWWR#nkxk3{D55Dr#U1Iy4an~3074OTX-({MP4h|1znUAF!wO{8wwubP^ zR6TG0?Pg_V5y#!IWW+y+K$sNs+Hp76Qp$8&W$oJnm&C|UaAbdV9QgV^^jIw@(#WTm5{ z<8vFfVOC&pa6w+)aMJsLVG}h~)sYmoFdBaQbzudCaSZ;uv9U3`gW2~$K35x&w^P{g zc&)SM)iWx1(4AVD4rj>HmDfWg|HDpxXBP&)G(s$je^#$krpI1UcNnhHX0_>P{(3)f zg-=@ieKL~wVm5+fSsXKPJK$h!z)~?jIi3ZuK}fjO49zJ28N=zV(%-8D#~BYEi(cNc#Fd{`kRE zkXrbU;j$SC@rDL3ws*t(-?$jOfERFxy1w`4z`WJUG+q%A$=>b&e1M0C$3t|yVB7A! zUskVLu0@?V=#8&kpHoxwZ^?D#P1O;ifR$D0M&$m7kvBLvI0OXT%A#+(x-3DA*)7QQ z0oH%EHM{^^2l!=ALvulJPe1%zsxXLnUK0}^ zJL%?+HJwnv=q)ul1HlsgS~=^9AOIJyfG?Yk=RcoPvZzh@Hacm43hW*mz7i-Fy=EOD zkK0&mrM^FjP0NWW)Ns;6Q1mxnwjk$AauLjycC*WHCciIGMW?uj%3h)khsJ*!g68Jb z&P_o6Y#|}M4%cW@#Gb!P)dd6ua2a)+n%H=G@B5-jk2GKZ+ArpL%g%n0YTG6uDd_aoa8UIXgScS^kbiK+p`VI0zM2RYhgVZ6g8%i>I)#*yz}Py+xolil<`= zJZy<-+0FHKW@>7x@6B#OFtN|9n5m)NB8W!qcc+8nIbzAl?=rnk`+-jW?WfEfBL^nV z?eT91Y^np;C5+%?lQfggi&TOzw2&EHU+;xFYhz<$V0Rw(=QFJfSF4$0O9jFcuwFO+ z%(om}K@#nIphk+#L#8fiV!lp zyq{Ln%`=!Gy7;7~CWg*ZkedshfCRNY+*s}@=*`!dYn0*8ZI4as_U%peXEijWxo@Ne|X{!Ise5*!9C@%Ev;)t>`C(KXnIo8+e0- zDs%k-!8Knd^#W)*aC_(iGepdu7gji7-3FEA?GGLnm;?}WqPvHQ0(-h9-w-+H;s?YtLNcQcksOc6Alw*$pi{eIDG@ke~_shr`B>%!1Sdt;G@X!Ov7khTgu9;SA#>eaEQu#*i1*#{Qdobh-Rjz705Z3oRs9{hFnbo(+YEfx@nYlTG%ZCUojUfLH{Or;moqany*)jAH#<3^ISM7y zI!#XI`@hS;QzkJ#fe6jW&cVT`)gYfOf3VzoTUNh3Ff`P%o#9!Sof+M6akwxzILLo{ zQ2q7mv*KSy%w;;QH$cFetsZRNHncP}eBe?#x2w%coA*_?h}UzraxGQWgy?7mRn;w4 zxm$h~PaSGC$|iSt zY9dltYmEnSXe?x2VZ)zHDaqM4YDs$U@9!t$>ANrydxK4656O1=!+i25J^V=+K0Xh{ zDkWwrh<>=bUMKgLD{4i`-QC^e4&`6YXqE9GB1hae zEsFa#Ea#)e#!;TLp(p1KfTj|;hUUW?trU6St-| zHZ)uTAgfhlhLt)Dpx$XtBwrJFhedf@ztP^|VV+4)`UiUhfCs0ir&HOjVg360`jCjd z{sMo09wgxckBG?ie20~d&Ha)$71`nX!|>1iTKBuF6P;b-zo_9u>D=`-HF3$3e*qY8 zE|S~SREP}&d)b3#Y`fByKDs>_>t8r62oDRh1mbbKbQ$5gb72cje)QCLa^?2RWzS>a z)In5B0C*2X_a;8JSu_ZQPp|9EL~V3zB}0XOHB?a6yIJWO3$S);YwLf}%(RmQiCkVB zv6Gm@<4a42ZJkPTytNk7G+R9WMM?$B0MZP_A!F?B@1KpQj*q5sR-27~2eAhrnn~bJ zyKc_+f!{qlJ(Z@21%L?5mdNYx&nKG$>Bodc?Gz?8+t1_m?y%$5%M3>8s;8vl8$J&J z2{-n^oo1}-C44=vc|lwdHRb||%BJ%3_W)SBvat*_&mXwnqq97Ih(P^G8uFq+tNix- z;0Z7T<4V100WpSJDPLJl4MQ*<$QNLc!O_v4BUVxZ5_C(5>?3BaKD}V^LAW)+;p&i#;`6 zZ{Sx5^I<^v0udu8!zNGB+{5FB_i~X1e(P;pK<|e2TvfNXI5iIs4+qCg^PEb+#^1Fr zfN`S+H_K^S{!K_-E;^2axiD6fzBGZi4{dLM1m1kHCFnA_DV;;H>}EB0G}V@%4s zxa2_uWE0e3V)MTF7Z(>d%6Dr;#qABypiS%fZ{R^-q154#;o(iY4QY5eDm;Md8AY%lw8SYHe^|>Z5BYXt5z)(|6$yk`g)9+c~a8Ms;Vjw*Z@jEuLm4}Qx+4c zIKPvV(-i~foE{S)zt5srL?pUw+K2ax6eN97{@GUWyP04$)pG}*+cFw;r751A4ZGl$ z8K64^KStsS-1HhTgFIzu|Cz_#l|2S7f)M7Pfwv@Y?eDHfzOAvtfdt*#+Z*`g=;&xT z58PZ@X$mE8Z{FwW?~vE~UQrH}FINVT+#9gskEV0uzI-WMrpalBhS+u7{`jo|wb|h0 z?2N$XMHJTXEU}>|xoC^HuI@^uVXuaU28C?eYeGVvu_YM_B}d0IfVS6ia3$X6z&591 z@39*6e!?-?+rvOdmk}2SDJozHx~#F7^Ytn}0sYKaQStQjWYlf~io+V>noMdKnURqJ zsGR(K3X34SIMYCrp=T`TTWJ!p6u|L=bteA;OzF;z&o%H2ry!yvRtA+O91+)r*6++T?@76aqxVgDczJ!qUg>;WTjbp$S z>>V8Bu4D(EdIhoMZ@r%^wqi~OH)?8XD#T;K<6UdDxV`7&%Wy{`(D95~>-McMxwKnY zeREL08xn#{I3a{-RD+3!hw}XL{htA&aJQjvxbNd5S?GEI|ArRQ*2c%4YONMB@72vL z8aVg7B?~-KJ4u?N#s2L}Lz$$L0a3s@1#rAKqC;lU=|%Prm-xHoV$6*5bZ;EEH*fV2 z_49j@p`rcP73S~)J&>Dbb0^+X=fPe~w-lZLW@hKc2~QEu>(@^}TR+uJNQg?bo{1E^ zn-?%+AmeNSVRT6i6$M2dJpYz_!S|@fDgL(sYhAi(*rmsPBynxv8 zzl!_v<%?2*tVG@fVE+6`c20p*CF2235W!HvlTX4z%uz|Y9gg{^FN*nV2D~Z_Gko#^6CRI&6(2pUn}&+20Ys6M)Ks<7Pw&lC z*psDqa7dHlBS?@P8V^?5+lz{*-@P*c=pPIn^B3OXvTL|5D-LjqVYXNTLh)00RvmpZ)9QnG%i)8 z4L-^j{nFRh*J9w{<7)#}mY3Hp=AHeYHegG}1FjGi6_uJ%GEZ^${HC*0FyQA85ZTw* z?WSG;%lEl+i|tp|)wN}M{bd}x*mbYpuiJo}oLod$IE~Y>=M2w2gC#$3ZU)P04OMbC z?MHb=-TY&@*0p+9hs#JDH7bG@NiBiHpLp52Q5rAXZl^*%jcf%Dr{D&gf`XGVg@tlM zGIHXt?%LYc{12L^qVA6gi`9zPdNfh!wehz7Wn%9wuRUo^1gsB#evOo5JNjmQGl88P zh1;o*gjpa)#qNez3IO zze<$$pV0~HvUg3@25R-L5wq{-FVIuAzurzuJq#HUaU+`K{cY}*3hXbZ-BbP!E2Aol z!#|{mo!JAY=lCJb?O}p4p=rLNxothi76rnTZT&L122l#EbbR>Q*s0FLjysXhVQ&4+ z`kUMCxr@|aM-soT=Mc19T98ncdT?XTb;mf;x*+V;ygt&!wj}tudc&eri+awk?;M7- zPn_(-ha?UAGoVneEsVZr!yJi4!{T;!Op(1H$(i~AU(TBi7^UWB&(1&;V2GFHE8(37 zh}r)F@>ocBDy-OpZ!j~@=h?HZr^^FU$p~1Xle;)&PnlBH8#gX;!-ch-Viitq!@=jN->T`MlE|TQ?Fp#RMLekhxrRUgJQd%g12zr>DPzVSO0!_BluWh0pwpv%l_kMH?bN=XmNNrGf0;HIB-ID z8^n)xY%Il9RTE~cPb%)vcn8u*Awr*3L)2EG`qNLN?)xZ6Y_Y3b*4 zd*#=VUNxsxoptovq#NHC3Th?wCCeSO3MKUGJ+EFCPX{H)Oi_Yt?Y` zn|Hb@BW(OK9-jM&zRoS}CZn9eLWYGsSSZ<`V&g)q30YlAb%GvpO)#3KgP?TDe<=bV z(z#^I|B}~6nJ=k32LD0%K7J?MC!5r_{RNtnUzkOQtk^XxX5fVXD)uTxOqsbpupblZ#V=rkYG^k3>=h6Qxcs@$(9y z>SwROcFP@j8cwoXv}+mbnZ#ZD<>l-UT zwetZmX{WhHT_BhFl(6*Sny3!ZN8%1$`HFO_ylLCj6W=9czn5AwNX<3UPuXNi^zC9( zfcfU!*QJ{{^Cj10X`p~te58hM%_!{k$g2Xm?=*GhFp9$w!&+_D1dhLw4wE?Juj8%| zpF=y&D!us?x{MCq$l7cRI@f)7*TUTc^csHsmk;;jHR66y+aFaT=%d=Xmz#VMm@HnEaH*Wa+L z(-1U-Eh_^--rh0@)&bW;S`$i=Lt)#nPz~Gf26h60DB4K-jUIq*BL0%FzXkS?i0DKozUq}b)fBfJ2P7IOWF2E(Ku*f_`^CKXkT8Iafu3?uA@ITa88^K z8!;=4yg#?q)g@M$F#QPvScGV+PQ}c=rG>@!98v=O5w3sCb`*@Wb@YSX?Chjd=@<2a zMH^#`5T^0>yPT?jn%7o4He4;{O617KyPf@3I~JDEkMQI9`h(X@^=~Pp5~R+57cS+m z@?^<6$1;n?IF-MC-SqqoA3SElG!g`bdhroJS^AL%3;KKLoRh@aw8S5#{+gcni$mh8 zB1F|S`BWaEeHu0Y#<>f7VTZE|#Z(#AqC8hTOtcYDAjcr{69`(m`O(f>`Lb||r5f9{ z{tY6uI?=mTQsf6vjwCqe5sju%0?Gq4++8NgHk$6yYnZ=fA|W{{38gZ7s`nT7GaO=f zbIbHg%1rd#c^#O4AImwIU?Jh4Kp04C>V;CGr0IOP;f&e9T!Pd4tcY&W!ppNws9qLrhFkD#R=T!T2i`M%ms-T9mgmMd;K1OS=Rh??{ z#a&b!X(WiGw)R$>9r}pDy%w4$1@mDN93Aswp8rxjVN6v0)^2 zL67JtnCwj|no*7Ewy2%GiNCvdU2S$;W66wJ&g_91%N&2$UUIF!x7rVI$NAs20L@;( zLXe-?1RMc0d)H1EPuQnMm{)IyaEZF%DW23;qdXj5vQJ=1^Kd^xF>Icsk~F$Sfy|ux z^>;!+8^spebO!R;NX=`HtEIG_pajw;w|<`{Qz-RCC*s&X(%;`20R4Xe9DY}G-wX-U zYcw6LHtrwb@cF&IP`bn5=Nf;`>^EY{$1P`U`cTMF6Mgq7&-l34z?@F$4{;p7$rV!j z9&e0Ay*Gx4eu4YlzA`zzNifiQ#qD4xwJ0gu^W%t0V^o)>9h?U%Nn?UySE3E+?H_O- zb}yf?w`RYh5QTyMIn7nrJ2!+{jk7yFGm*Ji%u2{-i7iLBB+ z5_!w~r8*ZWP*fluf`MfB-U#x=%h$D=-TS&*9<5c+dD&;zuqS482v@~U27M!4eIrAb z+P*q>jykKg{0kl$%PgEGR8hgT_;4-^M>u)FR2~f`dLNXlGA$en!j=>1};) z3B!8grUm)QPQ0aPlRFVj)eH4YkVC`LIX6sgKNd2Gg6h!D$DG2ss zRY>v5RVF$uZ4#y3Y3nGxnbcTF55!hMW(|#@?ibYMSvM zE^3yhtn2O_^bn6XC*Okj@)owWWN;=F)h4xr?n7ga{zTkX^8GhmbGG6vW9yMmEMgw!ARy7>aFTXq#C%s>h@?q5HjaewmX-t~aTK z)&jrE?Rkk90e!MAJ)6wTK*HHP35Co@&arK)-g)(+{jlz$Dc0P~R}St$S#F%Qd4dev zuPU%8tLIwO*U1qLB8M%~UH-Z)GRcv+iFnMK6dm;YjjM*og7 zfq{+VBO5UezvHeVO=Z}2i;xTQA=yluN&||Y2Q6aQRb@utkN&x-1G}>!KxbgWf3cDn zLAfk*yZIt6l)2W^!eQ&ATYG=|J$^wJrX$q4By$O|#{#12i1pV_3ewcY&q25-1fnTf(oA*I@))qw!< zDqIsAtfy?ceu8UCWSE!z&M~DrMV33V7BeryI50@yhqqp?^0Jgft)GlVeLAzf#Qt~l zUxYlTjNul)z|e5cHDhslo!fQ zd3552Z5cFlY~I;=KLtf4)CjhGfPRm;amFnP5^@?qA?v!X@e%K;1WARu)QM#NF$d17 zHoEs)VkpTrgvnq0;nsaBvK5_$A?M{hwFt3z`;}hh^yulEyQM zwl`6Q+EX=N-_(Vo&`pm}ruEq>`2?~*iP|%ri=@cHuaQuPwPLr>(eGtK za#q5drWSg%tlb~`?@d+@r+VR>W}5MbY{u^P8}|aLDOd@M5&SUDM2NYHjC2l5iV!uP zTi*_;HwMjTmT24DyG%-IzsxJsjtI7Rqv}_2rDrUI{85e+`jjA4X|q_<#76^=6h7c{ z35Uw-GZr@2SIvehtn+2A&AQ8D6pc1@&}FVjD_Cqe2=B z|7k(}%46oC$M@1A3~}P7Zw{4=J3dZay?7AUbFUu{QfHzsl6HRD=M(Tge4{){<+?)X zv3g4rch3;!0#d67T}tT(ufE1usd|HI8@0#Hk@`B!X?of1>}2x~9(~RXgyxw7>?F?% z+1Dntfqa(rLIUUPh{`<`Z$3&=K}Nh6+ZJ0lbER?J>Lx0PLg{;xq~)q!p=s4d=T;_E z3(SR9pPw7w-noI|Jj`DlXhBS3%UXXwj&bW|>|_Pv8En0JndV;9@Vy&?qVW(A&I}5_ zA_|`S#P8R#GVZYIJ{XL=FlaLwNclHCgUhyAm#o&PagY5}=_ks?hr4CZf!lJreX6}rm;wPv>(tVW;um016^FeIIpHDb@tW(yW1)WR#sp?wGadBpLF$gBkDRa?$ znuBOh(&Sxqp3w$pT#w*HWNBv2?gJq7iJ41ye$I5JRjn%cL4LWGg4yN84Q}g}iUa&6 zCsnz}M(VnUV^#GH4FlH-DoMe~p`MbGOU_b_D(ZqVEY9t)gMMcBA-{ZjI z`d)obXAcezjwUqrFS>Q!TBZ%|pOc*oo>IA5-oEJ675Mu19L+Qg z+()TF|2y=F_AI(VTHJa7G2{QJd_3?*D|IDpLQAPfy1uC5e0U`7WP3;E}>* zlKVynI(zq?sSI$8=zLI`u^%^f?jq(toV}ZT4yn3Pr6EBdzI*J4gbh$EaM&^-5YSnq zXJjl-Oki`>*d~HPQ2|kse+>@pj)kJILN#RDb2nTVmiQw%sbNpy6Wyc^H@FLC23$oJ zA_Igq2{QA)*-gDm?sj)Fzs{!+8!nSJ5{`d8AO1hT2`mn&5v|Q1@Gl~gAd7#UmP{VI z;jRC5!3~7pk<}BkG!*{52C#U(&oMBFfnFlJ%9Cvc2cxgm6N5zK&(^?Nk6ytvz_5KNrKpyjvRF5^2Ok_w zGFa86!QOV&FAe;vwnW1daB_A&TxzZ_DH-%m2ZnGD&U=MS;)Ao8nkm3LhE11W9I_|J zQz+%rz4V*3;Oi}=8i~m52Tx{k4xX~R*^JeUl+TZQ)(~hRq$HZHk-YQS3+Gt_ zd1HI@-zY78Jt&>ZLi2vpO>N^NJgJr zx>N)s?rIqkz{n6)JgsQTwp&1+O{WGPspz&fB8l_N4|_wa(r>@UTabJJY0&WN=FdR+ z&{Gt3MB)aoEtV)L<4x1Pz-P_0A$!reZB&K9so7#wN?#3k_~BCJzoLitDivwa=Z*Lo z=j47GHiL(T)N-`x0#nOWO*Pv+@ZklcVAkRE|0mI=3o~>*(59rGIJxex!PaEIvyI3N zfmYAb8`#1gV_=;#|CJ2H604I&lz+MzrD-C&@Bi*e@fyvewGEsHa&a|fXRlR#3nD{a zE!c!*dc0$vRB8m3Ctc*Niu$t`L%q|Ht8S}d};9Qq~c@DLrxa6Er6VozjBlmf`+rI*ycE*8Gr+ki5Qx=uQXupzWMDlKx*$iqG zMVnpaDFzsa>-pe6Y0IG+#h&-@rpsk0z2_QO-7h|+g<<|lui6;%vZQ;gU5J`GA^$hd zW}M=-rDKz#MofvF_hmsQ1{wLB!aVxBTb5YO?;}?4AgfNAIH64H!55GtZniXag!3|b zHtAK-uHcwilDFJ;WE_7$f@lQgw79hb^`FhoEyX^=K-NODz`~A7^a6X>eI4@vrdI+y z9coez^EW@?vjb2d`GUBYDs#Wdkl*;)kAHPn&=dO=kjlYtrquZu^xJd>Mn2D@SG5 ze3fio)tsRDI|!0^W0e;R=d=|pB{^llS459pG!d^@aP+o_eqb)MT)8BVE8eVtos@$& z8v(l54EqB?@tk)CweI|$=JlU#U38HJO)EdtCMaY+Km6Ja$rp|Li7L@hE0pAYwBQK2 zkfDj*+!~}$F?Hspw$d`v7eveR-KwY)FmDs`B803mX;_T$`U8t~Gnwm!)6k-D};nKfC%X*Mjj-ubvEO*}*+4}HJR@We5o2ek4zBOdp z;(gKWo0y7C|INtGbGWoOTUhY$c<;jl-zA;HhL_o7bwmnTCRhXv?dK2ca;iVTL7O@%mAa%)=9xCb4&|dm_JT@NRzYCKwQusHs)p`&8bMB^;9{r?F3fp zUu~OEE`mNsy-D+L#(&~1$CIU#tC(tkADBo;PJPRskl({Y7VLw%3G zIAq;>HClx(ik%8@-}=e7YnO+Q`QSghAfQ>RyZkJydSe)EVD^uPnd|~N@m3< zQahq4CG6+YY&xqy6tg*$RGZ2)Mbo{sb1o(v)a83so}06oFjix{0$i$8;f|(Rp4XWc z`Pst`wN}q8>86d*0CSAQA!3+%-gWWr9@<{DbwOOvNjO}MS^NF4N0dCQ<48)&Feas3 zCOe_Gt7HjTw}mq^^tZb*A(#N_lMeB(+f!_8QC9qpBL!hQAZVSvQCBqJM0ZIx@N$H2 zQ%HLwvLjw?!#&LOgFf!16oQswvo2gbe)GW8+t}ZhJsz?QUsOG3rb&9ANIPXZY%Ni9uK z?emROI1wN96s}2Z`T``ZLhmItBC;u}W<`8W^7-V|Bx@vy!QgE;g$3dYuYbc&u}(tH zV}x#=8_k7fwu!QggM_!k5-@(6s#G?WzDfx4*$2N{_jRuN=4Q{PG0N$Z@Rn~q=Pr~2 z>gEwr?rYtH%(0;$Hh%G)((h#Sc=HylvUQ)tXoFDR>!qfYm|I2XQ#^xVy|p;YowIPx zS1}3oC5eSVm*+=Bq$$Z@w)XZV|>39&g9D^y;9Pc!E(ZeM4?S8iAt(Xo< zOpx+3EdIBM9r#&wg2t5SQ<2$ha>`j)196I4Db%tlo}Cu5SdmX}jFSvxo+Dm6l^deDoZgZ=RFcTb zjFTWV6!i)o?M=E4Th@9%te6&)BmZZ1#(JSyv?(51W%OX7?B4xQliVL!?yLWG^NiG% zCzD8@8Sr{5eA+joUy#mh)?JQheatmmC$$+PCEce5oxeyIexSwitLsbWiISnbu^mn`wJe=a zmn}+0V`mWq7k`>2nLUL4YA0CI-3J^sxz`OpGnTaXLglCVVpTF!Nn{0z-S+sksC!bdH%~+_8g_;n?~7n2uaG2G+*8 z;IBp`A&jr*2kl1CL2mcSfdxG8U^d=&G9w;__aH!uqb}bir0SsNH(8^0Dxc?=K^BC zIeYDkoEFk)$`o-M3ogSd%*`MEv9i}ES|ulopMr(o&4_=+D?TvdWt5O`iK|my7RLqp zXOUN$sl4rTL#IG%Op=)BFQ2cBs~IUorNn$&e{0dTh+X?e5|3DG58%v1jgn$Szxm?) zA#BUU_)|g+ExA5NVKjC#2@>US|3Unjp;xFMOR8)IvL|BhhJ1d@PlC%tvUw$oYEq|@ zMo%zuK4O+zt48)m`yE8l?U=l$Nj-`cs?6o{MS9kE^D`FX$gA1I@@U)N{vQWgG_Z3P zA1X;hFz~RX92ARNT$fhbt@2wef&{Z?4?4pBk;^74z7v^Q!)MP*ivZnRD;!visa<~y!B&5 zo`KtA81n~n~ch%_ip9#*&~+IgXqOmF2~Et-u|m zjt8&kH=GV3&P167CIp(f5mAZk##eg03L>8g&g0^YxP6VL`bvx7p7bNHr5!@_o@R~{6SLZ?jQZfpf;9{N%=a`72CV* zt4Gf*xkuZL;zLSl3R{`P@8dv?r4Z^bNg=9XT!GN}Dg(G8dI!Y<#SA#*q~}BNLzl?k zsYN9tX*3*ShATy-8dD`mp2f2Zr3{BWyssR*qkgfOIA0@e5vI&lM7}Q(&FGI^>yGig zRGh#N*qMDSxFMB?OR?T4v@|?HHuMpl*sZO$wbHb-olNjG2C?Fjb zSH+go0ykQuk-pvUMD0U|3(ni5ic>`4mD?^&exS{g_SrG9_Ul3&nXi%Fv~x)fO<3Q1 z^6N8Va8{P>eH!l+nG06+_E{u2oEy=uj7Qz#SXlVp6^={Ewc%CCq~`y}yirQPksYOX zj%WyBBngR}y5A%`z1b=PrZzXx-6x6n+SuGeDxsOSv|3N1taxI=B-?M_3jbBD);rRD zEs^tmowXol;|%LiZ`Ha+ob*VMLMw#+7L$;TnjA@YS@Ks3u2YDj0bI@7JPXN9E+s)s zIOXpK(lWUA$=q(xnCxTjj1#T%l{jyW4@bUtZs`wryr0#TdcM8fKN)a_@{56iNv%5K z&=d4SvXV-NR8G1Lh-XWq%qxy|-iBc8?C%>^enJe;Z$QU;4T?UOEe342N>fu(N8Cy{ zA&Ym+V>XPw^1?1#t%W%Y#<_sWWA_k$?(49 zn=Fueyyk}>reum`O3#}w%lF5n_g;b?9#XdAg>BQR!7d^O_RmiEf=Kv4k^R9^^UQxm zk{D2-Z!&S3&zo2+&`1gj;t!9Gh-`!qKYsf3$-v;+{%rdL4;aa`C#RBP+@FPH0YeQc-+FY#$2lOA58p`%j`1$4Ls znWnQ8U_P;wprSS~C@6wK-=y*rD9&EC7{2Y~dCP#EA~%n&e}XVcx?pkPQDxQV*PB$~ zy_)U{nn~v`Wne!%&$?gLMH%IFF_adT@$ABaY$M+5DxfW(q5zTylo2&9VpTqC-nMJh znwaxP2oXPd^bi2lD^v?lp8|+z3)4@ z|CaSZt}O8y8}*5XGBdZejf{*W7Lj%Q^yzi#@Kep6YQQqi)`Cg?$8?8!8TBu$lzG<2 zDN774C!n6eWipQf&dKG-^O$(R!_R$i`DTB*wN=#c_922%L&sozzc$zE>A8hw*47bW zVcb58wC*i@lne~zwbQ9x`8qgT;ds<<9yJX&7%3Saq$JZ7nLoL5x}SXUU6e+t>NhIB z{X^RG?%lcP!*wJt6icjgdmxk6wo?s4GK-+@tkXFYJ`P_e|2}Wad=+V z3NM2l+fYj0RgcwF`!7e<@LJ@ zE>O4hfnl$rVcM))d+gr3=Ith1z|yNb+V76mX;Q%g%%PDaQ&~6 z6I7f=OMt>^&}0Ovx%fc|B>%&e;h#VLpzSv|myDmEzv6b_CDl-d;iJDMc;c@DPiH?RxDvIi?Z##1C4_CoTCKK5>VAW7x=gd+onpg^Fh6>=rz0tAG7O^fG_w zc_I=?NRgkoRImzxrz!|=J2Gr+J5O(bfa!lP=NU76ns^iz{4=mM;K3ySXAb{SkN=xF2=7637+;kvDQ3UX&^z`(kcwUXa+E%aa!k-jGKon>g5G^vZ|5Hiv|C==b z&u;vuV*;X>|JO&{1qccTFCac`m%tFb&R!$aBKvV-E|MR#9 z)&u^2K=4AsVE1i>xA4>480m08&z1#f;h~5vYHeLIV}pfLiH*pG=FQ=-B^h%r8(2EDg*}N=nMx`=06*J7}BRbcl_O1tqVV8X8{~(tbX1 z)qP3tNT}#YSQX_m@N9%ci9ttPc6K&bx=oxUbI?ycGaDNUiPOE3is#Rt|4-!}tKO4_ z;c*M+FXMAxzUB9QJnOySIGTf{n6%SmwOBoI16v8|uy5@M*5CY3U2UTlLu9X|nb~U_ zM?}OYJv9uppIO4XE^zPVr#_gxBs2OWvhpMAyns>rx9le_dzki;yT;$5CjDrI3tp$U zaL;SEh53_u%heq&3LEhk+62W2s*A=&1qB85^j1OLZtw!8tpU?(@fc}I$^Q;_2xZ#Q zpH*8rNKMUUfqH;13sIF4`O@lCX0!keJ+CM4OVV3(cKtQd*uJTkd}4qL)ex73x2vp+ zCUuz(jUDSw%pVIxTjs>%AJbs&q}t-ZWj}i zIC@i&SZeG(q=5$U9I?m?UFRBorq{BWe4sB>%@x%BIXi=HkqrM%p6?*>oS3CifZ3QM z0YrIt#p(El;M2TTQ`(~dk8&!h>l8quV*=(DF7+zo!9lx4-hdfzA<~P0UH_)rvHZTh z=rIv`#TACVo9^BZrnu@C;hPq9DFFLXZAlJ#Q8fk%%YEz zf&wwfeTWqBMn$zEoDLC<9h674!gSCfVk!kWDnb_hY4i(5_5VfPTZTm$eeI*8s3;-` z5+bETNq46(ba!{RbcZ4^q;!XL*U&8`4bsxm4blx~^LyX-ob$h~^X2@{r}KOOu7{a< zp4rddYp->$d)@1;E*YkL*h`RK&=1NU^$<0338q@(%)f%O|J8}dPITWfKfPM8LFjMn zh-bF{F3K$@S&`jyo#-MuCmZtrH8?xXUY4FPI)ZP(K$D5dmsKmTJ04EL$TtLPODR<==Ep#`mQgMN%bj*GwguIG z;6aPCEfhYaLS{D0q70mVahgn}K`B)fk!^T6Q5`lDIONF-(@LG~ft(E62qKq_&l)MDY(#$H51LypuXo2K{Cvs;t{LT9}MUc!T`i1Rlw*qaH@C$fsm5jyK5Euu?sy@ z-&B7@m4mPneJ0b_^pY6kBzziq%~!8uF(7}JvBFfoA$X1rMeUzCjfay(Xc~B1ETMGP z(@6+`D2*MLHpW#88nc$vshv^WlCdzLqxWPTp=gwBNoUViz+%DG}{@1A0-FoyRe zC7gdktoR%s_7?L~5OL*>0+(~a7O5O0^3Q){kf#4W%o@;rlj)m}uw+xr9axXvN3Nt^yR53z zd;mpOC?3h#@D|W!5+X)#JWV}Fde;6*#NxMjHv1?CaE{~iGOP9Z4vUsOXwy3 zwqxV_!?uJ;S@x%cy2E!{^~Y1i>2E)UF(N_AZFTq=mwj!foPDn`H)f>6-*u@^gPlFg zofBgiNann?dBgLR*W|p46^-w=1Y4?}C-(?UP)U$n;6G6?Rv|4~l_DuXY4^Otq(>b2 z)Ei}2n)u#?HAVeJ$+mEMQHiun&?_yC`xsy`rKMc00Y6$$HgfHqCZrzW^u=roe$q^Y zW6Z_H;Z*QkEaq2`7_FDjNkXssiQMhL4Yg zz47$vW(xk*!AR&?&qb6J_@gMuhC&HaLzA7a>k4XFcRf>8)_N%Mt@H~|8yPHzekL4rAx2Rd0Vxk4z0|pf$heBzY0@)Cd!BH;ik`cXr zYuJm!1dB`nC^>Hp>svSTQwv)y8-j_?9Rl< zEVQ$_PCG?g{@XPTLs?{HOfWgQ_nf~&|CrRI&o|BAFn$PlMW{_$u84>_hCuSO8Q$K1 z!h)#1soD5;4E;bu%}Fn*dmg>u%^4}b%Sb{JA-}m_;d(+ZR%wEV@`@Xpz(PI;mI_zC}*`ZN*a>3#9g{#U$Rn`KYxH33yer+Y{H}`PhIXVhv0B{o!a8s-v^8izrPmecccZi)~5`zgcOjMQ+wQ$h`@B9Lwh40G-Iy` zpfX=Hr=;58r+P08lV1G+dAFwq4%dz<7=WdzvYU-$f$Lq9V2j}jrtdJ$aW8rMsH6tM zNvfG2PCj~N^q#UN0Ks}h^_4ne^vl&;6y|pC&tA&M4dJYULl-nLlzWelo?~+H} z8AsSDGSy8PyAib2d_Ga0UngrAlypp{Zh$mcu+@8HWz-)zlqik!=x@O)QGFs(&Og~d zOJS`Y8)4%DY6Ky(*(m;k?y~|e{Sj1Hwsgzqx`z_RSJhm*kVwI3VW?iZ^<(Cu74_WqdDIsXJu{55A!?$?OPvjEsm5YFU zjQ~ffI5csH!czI2No4ek!5@*fGO7|?O4nSJQ#oUXw?^fIJowd>l4E zk1MVB6$FXVz2iVg0URDuWYoNRNy)X_!{V&1H!$ZETTA?0~Si(1QBq*6u0L{Y4$k3y1bOb=CX zBWzn*=im2l>(4d6W*(i}VKV(dR;3S@*;cc3q3iRrJ9@hX+DM4pdC;gy*=%-jpaNt& zCMuIkSCe1j?N^e!o_^(VF7vB>IT?(hhqDj$U-rKv1jO+0N{>Mn7SC3|v zv*Q+3EITLua<-q|vn^J;Ba#E%}9r#fxS!F85tqje}W zdGG_{HgU)*=DE^>?8r?6J~AGKxMI+E`?_-ao})LvfAQj5SP996_g#>q<4KAr)5tjw z{RZdLUjbaH*D(c?Q|@%k4$3&kr+S7OS+jfj?9(YN28E9gj&kg)Wh!ELQAk05M{fWq5~0_B-=4-yn=G6p1} zqW16p0LedPz2e^yzo(6kwZj*!i+E%EmCZfrx6wC^gu-+6(w)1_yZJ&&#bgQZI@gA0 zpdO`$d5B?9A@0bf4P)C_TLV@34lSw}1HBz%$H|MXpnAPY$F_a@{n{2Y1mYqPOHI;t zs4v#k@a)MEa^<|mQC?B2WfB~I<=2RUgz;tzEAX8JE6ZyB_sBi~pxCxtt{)Hc6DJL} zDS2#I88wT-Vkn77qM=L3QG zt|{0mir8%L+bRBF=Qi?H-z+Y(CeND44DsyXNfZ%+#bgx~ICmyJ#fcn*RpBTkMtw+7 zroZXt_u>lt=y`;Tv3JAH@48kW@o2pb9bbpj$w~2UNR()rf!FKuXbn_&e;e$322s;a znVN?!O>32Fi?5PH74Xq_z)-n6l()JE^~ZQAcAS_D$!Qqglus`5wg{N9+0Il2O$tyc zV$v}nV(1r&??2rJ5vRW0JlMla6nC_SKvZ5%`3fM~^8_(fW2s;A^?lerNVL7g*+g-O zqZ_rcUJ}pc{-J9`tjkuGs+4PF#>j2$b407T8SrzpOt;qinx2H*=ko5jlU>K%d3G%% ze8T(K7UFxIKRY?DZkA~KcYCm{!pQy=YNUu_F-2o)&!TU5k=*sx5Q=0$o0f_0s==ln zTbaL7uJRv)w0J}?KR)E=?S@rp$pNW0VYeMYH(vhOhbKA}i93Ws@B9w1hoU?t$J^hS z2jg14yzaNMM~%!aY*Q=$V*?(aC21@rp2k3bZeKqcRKi2ga(#So1UB^yqK9qAHkFhl zl*T^$wd~ITx#ngm;TN|VLKQ_dOIPn_E+_1zZc%6^dl~ot2K-1*e&b0gW*<;KK~X@kqces6OvvB^2)>svmrqQm?9k+ zMAg4X!=Z5tTIHl0d$YXvsqt$@1^aHd;Cd=Eg(>wsS6dWnP<#j^YP1j))X=~sVaTgg zajo%B}n1CD*rgn(y z2T*`AS$cSA=Pwxf@D89(wX(7jR4Ux`dH@2koB}bn#%A9T5OyJyPKtex@t>39<1te^ zgTuqJj>7vN`~^Qef82n01bNTt%99rQp97}4nx^-b97tlU9FjIb`+fDmY#k$~paA#@ z5Oz{EPCu)$o&`#n6ciM1v?GlL7?2>vPM|pY>-*h56^5+F%pD9sMB|@(GdLGi+J96$ zqlY7G=kCrmQVoHqX#m)vT~y%u>PkdJ#2LJUi`?AQ$ml^o58OO}oWNTU;_fB8jUS%3 zH#ilYRUtuC5|@^iV5(oPuCFP`$+1v`Ra8`fcvnx4Sf*u_6>5i(si~>4@!-%k|hFiN5w~AV$}Cwwd)k{QLL3ygZ6*6$r#mLQ=93)EKt!QBs~?=N|o+grmB; zI#c4nl%q^szmHdcb~z0<8(UnqDfXwL4+(x>U!+DwEuXsc5}|i6I5;?%aUuD}%s07> z?VLU{ytv!2`v$}$zyb8ya`N(+QKOTRlKxzE8d_ONVRZq4Hc(a!n_9CeSuD4!p0D6| zI}B9)lnazl{4-lyeZq>7UqZxeq08kaxXDC*uJP|9s)I8mS3{C*x9~>{c zGom-?^dJ%AI3i`X{q6gf-&l%k^v(`*5C;d`7w?rAn%>B9Lez=`eH;X%IiT74VVbFJ zQcKfPONRukg^wWne*V)0)Uk)m)h1-d4D?NWRF0A`+cW43!biNbka7i_pb53lBsg{W0Ry`3|| zxjNp}x~nX}?}vHG{_;(rLMEH?`ME>xrRMXJzMPO+hH!2(rG)Ihak_`|dxbJ0{ghB= zQjeci5m725?0q*NPs>R;aQ5TFKZAop4G2WCZTU~R9HuTEwnZ<$&5ak5 zZ#KU@vx!hf(VxWZ5K_XM&tHZuEE$pYyQCpvC_RSo@_qb8&ZJGQ zIn9J9Fx|fCQot>47jG}rGIu`n|DEiIM0|$p!@CRj`g92H&VhL|*GP?S1;3-Pz5+Uc zfZ(K=DjUT2#2APzg2k7Uqkm#8UTeDuP^`3+6ar#mSx4=C9Pm;Bjkf_{mGt)YHF#a0 z0nMe|T^bB&Sy_q(ov1i)q@~^cL_*Kb$q5S$1<9_U%!Eye`43N+A->;w*6jTSmkwPY zir-hm;+`11CFkaD1Y(V?t&0!Vi_7IPu&VE|fhL@xdQD>^exzqNOn&#^fS8DAp~6rU zsOFsROuf<0GVWLf=_+y8e{INk0hfce-hN6g9+@8>6#KxsL+88D%iKX&J(%K7pckgu z>U|5;FCsUCi-|Hn0v>XiZYv)>J^hH2IuW=pfv?s;6b>l;iLGH%1TTXt5WD{U0ulpc z4Rrr8r(z8CTHZim6E#SQBt+;vE2QVo~NDQ%QRLJar6$Kl?|Lg)mF>!GLdM#7A zd3j@ivi;w?1|}sdD>g3OB62}NK~SmSfA)(YKv~ScC^_Q4>EQP~tA*C`T?hJHAj_^T zNLQ;2+4AD&1e_;+3>J4JdHrxB2nE%9`L(GUEsyglea)AlJ}2!{HK;xuuUDi+sHfyg z(B`w*MQzkq6GW)H6HgCSkEYVgJa+y0gBm@5YD_~Qy%Hs}v?Nh@97C`FkUCDX%V&g& zsdU*XA2*_;o>mXaKrxX~76xz5gGN~;osQiu4%p>3M;<3OK0y_>V8K@BtCpaa? zS7slVi{^I`w_WeCn=?{O#IH$3vqE*7QuIe;^oQ?zD3Ac(tv~3raHxF?T6;)KUUaLY zL{=cqn-F`|5$mA_J?Qpv8C^);oB@zE>`O&%3_-WG4AjEL!GZM`x~27ec4_JFf7X*W znfqh$B{&X=y)zhBY}Gckq5M0))j1_$UoB{~%zMT@h&E5J(*Q?1>7(fI3+tTW16$@~ ztCkKWb~P;P5kXb!Dh-sfae5v@-}eQ`%tfPS)l5+R)eRV!R1he1l=!#2X|uF3TT$^O zTH~n_qlZ8x&l^PYP&I%3=ptV$e&rnECOa|}tPRA1A} z`WU$7FS{`?w%XrSa)mQLK`2|#<$YN0K%x4LJ-XI^+t7a3zmHAuY`?BY6)(8IhiFo# zHIHzsNECt0oaL!?zpF|-pH>!Ui{-DgVTwD{qVIA=|6*W_`0f{x^Lk|sajWE&?Y_sA zWD+vWMOL+YSVngelgF7g(0TTI4J##FU? zE8WHIvoQ&Trp=tWHlbeV%EYo9pY!ViLC+RK=q0l6DYBzuPK#&;HEDO3cYg+xrt8!> zVP*P$aXFTM?UXo}kZ(#kmV58OQhf+W&;qk+*%6g2|7TDHoKtEA)dKM&y@lJ%HEuGi zzj(>&*)$*B4P^WK!<^|cjBTdCV^yeh;&S?OKMCC?$Z3$f{#_)hY5N+3@!d*=M(Ui) z{N>78;L8x6uUctBou0e1x}}pAtSt9D5(iw@yDo#sF3)s^l+3ZI_EgiXYFp-J2&X>4 zjT+*082QLw#{7uo&!(N;DJQBv>@k7BKWWeFO?%ZZSXCzV5OA)7xwzt5V%=h0$gXOB zArpvTSv^{RZ0H|gTglZc1v6^x#D`{`3~?sUx(?~lRU$|0XlrNnFKI3lX@f8@S|%@~ zXT7QEK^$@DeB0vHI%#Y8rG(@x{k(Q+i8Mct`$-@LAv+9eP;HE@@6u&5@zj1P`fQS2 zb1yvv1}o^xYb_;%Lf7tWFGv(OEo{9iZ%2Pf5jjZc(=0IGcPuryXWZsZW-antctxLM z$`@+2mXQt#G03)F40apAR_i|_?yg05eB9fZB}u0ylq};lv4Nv-p_uE|O(@ve|8zFi zGvu_R-1-WM3(v=nFOXY?8w>l_*Vw|yTgSW~1t?laT6)Elv4?rfso!{kI_Y|{?G=KP ze)v9_NNUYx!Oc`((tSeg2#48iR+hl}oZor&u3kws=R;I)&9`B!9qU@Qs)nF^KPCPG zr8gB!oEY-!Z~W0QF-EGH%h+1TURS%!@z}TL8^@`KP}iuIJ)yEYI}tU;(MDrbucwK0 z*#h6>V1fxo>YU2w)+P>XrjW(OCG-x}e|eUl-(Njf_5`ArVWU|RoBxxeHD|!;_dnX) z-yr2M)KK3c5;%=BS~x+I@P*Jx#W`k8DrHbQd>+SdVw35dpm0py^cHc|>ot7?^uVuY z{yXU8OTeo?+X+OAHCw|E(pUf0ido3-?DuV$&-t22xL7Qi;@pY&rLNXlx^{4S{k3>< z>ww1YxX*f3a?jG-CN(!HdtDIAW7wSWQ>-zoH_ zCG)wwQ8H)Bf>zFwY$9~Fj4+Szng+)~wkLasJ2957(;YqV5BHEQ)MX&1l4TmVTcv28=>m`yOA~JuM^4msP6*cCc?w43!Hsa ztQMCB*83c2j9aHNn2|0fZkKHOEdRp=aC+oh9m0=o-)sM+BBPYyti_$#6bv7Ee8rcs z%e>l)GQ)LEDcbs!jEouO+1+fkBFZ$jeQ0AW_I0UWT&xC1S1Jt!=75O?et}dk;lMlX z$P{>wM@7L|8GR58Ye(ZktrJhzfEoX*U1?u@_uRbboq*XHvuwQCTPSoYF<#fpG66c( zqJH&yA?O7ZiaErdXuUMW2!Rwg?M+}O2{6}98j(Po6N@&}ZAWIl+uG(=CX^-bTLjb` zAiMR*+akDnU0x$~9bH1?Wl%HCZ_cESymwqJMqjPBsgqn{esKB~^L4kgxyhz9HG zk+SCCkc9y}7A(wOKIfudnFqKiyh!8BAO)DYA}6#n!CuS(L{vw&gkainp46nze{Xgs_=bb==H20-%QMHZ6+PS%we)3@0U5U zz3mz~!|$i=m{zd8_P=k6(r+rQEWyCVbM;vmcO0U9)~MzZ9-{e zhh*I5I$SE=iZ)0kc0KnaD$D1cbIm4o_{9FQqmRAcY;FZF%o~ebk#6|C{_mAA z{(Ma)?3|ol3dLq|ImzS9f;kB1>;|qQdtlJfyqw=nXjtmh6!^ZOe;D-dDEdkDe7!OA zvev{e09H0NrM}mS>K0{t9f-YiS&tqFpburk668t-8h9ll!uXTeiDT-WU9=ZRA{xjo z75&mcUzg-BxjmD`Q~3~ILejwQ*Lc448WPb26mwHIsfu5SpYWEQ>XAdCBeMrB-BT|Y zfyRe`LQ-O){#$Z`W<53a|H3&Gg25vNIQ9_`76uWzq$4OO0)8;?PhnF~od?Go2ApBl zk61yaT~zUrkq?rBn;M-jfqNez24Z;B$~lk0_9G)>IC)KLA^ZWdof#5JjiA z4$KY;&S9bWzkj|7z&Br4xBm<6Fv12=!~cSIh?RqA1gPKN+<2atm(^L%)|{T5$;gn8 zDHr}H0>CFAzIg6)%e8=t;eRoNIfEo(W+pl1-AQ2CK zJqW7agDXNvpMa5ty#Mg$EMz~hF9J^!z`?%`tpk8d$rvo)vyaV<9e8wjW>QM=Sj zOGr>VWC9(I(r+YSm!aN5{tS0@Q2K1|fBU>+9<r%D_Dv-s0^&!ds)7fFAz@ zGr?<}5gS8(c+Ej^IHuFV_a@W6gvTIxnUM?wgHf|pOQ$s4v%7QRoUkb#eo_rQGk2|4 zzD4)#Q07eQ)Y3^+l^EXA!#hLk+u@P@fC5UW&(Er#tQ&D0BNI(ryiM5 zPg{L%ViyzhomOnfEsf5!r`}DcD;gaG62^xB5_ zn-oYd4L!ZBjm`f0U~*9DbKgbKQKZ&-HZDGXYIZhxvw_C1%CG+XM$>j-PHc6F)!0V| zkD4>c^-xSxPgcQO8edA!ZhVD^3~IbaM;1B7-G@jd4Tg4D1iQRA3vaaP+Mmucfx}@N z-I2$?;btez{k+H%+(AcarGMt#kNW0`UI`$;lpq3-0}vsm8Nq(^+yP(_Kv>@un2;b7 zOG)koQMYb&-Aa{OmvQ{aOw@j+0vL=1A3>pLpRFX0;jv#bzJW3SnTy>5FkEF_4-7R1 zTX4q-+3x??2TowQeTwP(H$x5R4w2`AE0Pphopcxl#AD?!EQh$n^Ld~BnO4RA*P18X zTKo4=(+FqCot&Q=ZnKIF&dq80__PXUJm|7xYR9{Zb0=(YCWU{~!XD^x@-brM z10JJ}L(`q^V%=-ctql5tQIdrso|BZg5B4sp_?N+P@J*HO%i(Hdmu)nB0{Ys{x*wdC za)g^)ro{6_Ii+U3#glt+7#M>`xZGK$rLIdP3qK~9I(Z5`*v9NkSi*-&gmK67~iAj_%c$JwYF_kIv_HL4x$U zihR0?X|gOkJU(n3jWgH#{4PPTqtOl#=~d%VKE$H8Ahu_edR19y7(($&DMKI}?#|cz zx`>vBYsd?ViIG#7ez9lf4U1JcRD$5$%%@c}U^X*_Wj%G@bO*xc;4rZjEh zZx}d<>Wua(2EpzeEn%uv$6;gIP>dW))b4?+@;8);cRe99j!@`1GOhH%Ar!+nZk%8w zFrCEgeBUGpZju_$jk`z0=@NcU=C0FQ`PAjG7m{rOITsfOUCzY{1H7gIeqc zoWm$b1`jt;?$*zh?v{x<(lC-%3w}%8)xBkU9Un>MN*~0jHq$OTp`?jg+z05gW(m?2 zuIb%==-=7mkC*HBhnf4I0^nwoD}NXM3PbH{@76SvrqBu%#Lp5AtFI_D?i&hj9U?_8 z&rDjH6ZM%XvzrGPM((!gZ&!0(iZ@zN|LikVy8ce!=Db`I?@ho|z^tu(SAu)aOJfLc z^3x`)nVKzIx=*Sm6S`ijqMiNd#M#Ee%{=eo)wH8~pq_&%Fxo~C+#A!+{1%!y=s_IL zyH6m)al8^g{M{{Ke!vy?Y4Xg1jmIqgbx_ao3!h%^7>uB|ZsN*eOGQ+ZKDV*IPis%v z`2J-vc;>cz@-9}PkR|IIQSrW~xONqty+02Cw=Q-Pj=9I>y6WOEe>1UV?U6o>mqXOs z7{~Q9*bu8Bi7P7r8_#s};5%Z}fO7G3!J2K-#f!CQWYpOE-juUucdeds5vxBs5tRr2 zr-&48k832^SabjwDajfUTSQ4D>E)I+xfWA-21QPDEU@k#z8;~UnTNII)@<@kd9%63 z#(c!=mC*I9P+GH9lx>)~Z)iD{P>Ts}+S~k2PUEAs--sir@-Ju4`~!U>EU^Yw^h*7< zatb@A03Cv^(sGpEmx%sOmOA{~{MP_4!PJB1e_7<$6tGqAoo_di`~^3os+&@qkCC>R zjB)Vm{0NGPq-08O{)X&~?t7xTHq(fD|LeMIy_K{x(|bxC_fg!TKIVcxJ)p#yYp3FV z_*}K{b4FH0#h4Im!E!Hb(SsEldy8E+dbNGNt=ael8fp8=T=vb#rYWz`hemhpz54_6 zftF|R<(aKts=9nLbJ&;n2`)dW5faKj*rg%|qf|FZ0Ay;|AsbFmT%K|v-%&wKWNEhi zt|coPL%!jxJodU*lI%`#+p4)f$fJbo7k0#Z{Y0|iIccivV#(PE^`n8{af+p=JW!u% zvvSUlqV=2x#gE8#cVB<8xFVOBYf9? z@OE5wQlmR}*@&)7M{aIRMHhG7Qyuaxb$K!m5HonUwN$Krd!os1H}~<5Y=y+JN}Mm^ zdD^5hfA8Qd&$9k1W~5)lv#w148*bZI^)|5&23s!2mp1Zk{NR|va|zGMJ^B19^5PP2 zXE9Ku_43RaS%e=+Av-DJ+VIIqhq|@m()vm3}WBri{`rp68?u@rJ-_0Vk zB2z03u_w<2;>Mu#>X{hHDXfta0GF9mz$f3c^0?$G8uv>0@YmB>bRFx~9L|#_=Ph<7 z`)MRtWvFK|W>&v=-}TOSGH_|&^1nZfvB!JC&Y#@M(KpX8SWM&k#+l9X&U<$^zNU9G zu3H+`xvg7BnP|^)*V7h5bVp9^O_rxgTor=k!j19)tB6UZc%Q zieJrS1Zzz@0Y^PKXqc~l1(Tfb+UW1W{rcGeRpMnl3?zI6G9N;{s3`mEvpsJ5<-VOs zm9rjVg5$+FlgXW{9|c<*ZC{M@Zf{EheL5=&V3nEIT8d-C%`w_K$4$SEzl&-%fC;53 z!^N_W`+d``h<8Xm^F3i@*4K@b`;Jwru^Ou^t6O1k z%)Hx->7A)KDTP#4R_;ud=}w}!I2TdglxaCuvrpE%i7tt;{parP{|~Xcb2$?~gg6>3 zW_J}{{_JsEE7U+Iyl{r-b1+v5^CmCq z|05SaYvR0FoI)08_JAG9JNfx*NU6k+SoD%X?Pms-?ltM?gAJ$|*v7YTXppGJChQu@ zX+0T&PKDoPRPvp;h-zzREX@{6PIOMst(I$WFn0~CgG372;$#yH1!+zeWQKuAIyK^C z*FcphSVz{@er}T}o1B-|%bv+xB{Uik7`l*??6w=JDYggGSP4v@yHPnl!_W2X#|$%X zPNXJB5w{ETr+$T#$)!X^!t@P`{$X4{TCBcVdkdw)v`XDU-^{NxZj4=Nzk0?J7ZLp} z!UAay9VDedN0oPkUG0 z_Nq7Pw(C?lEo4pKHEh>Ws~8ZMzX_?}CnkR6G}A);;S@(m+_-P4S=!fX_i9WUY30%b zyd>1at+ou{@VSuE!JnJI93_!Pkrrsg%ny3W9Jk#NlbNc>diLS>80Xom|0LM!e z^9i$0-(swn=z$JTuwcHuu@F*eKE2bdg>;M9O=Im#j&vJI;(WA1dzOMLsuG# zp;~j=hCNG~nJwGYZf$y3O%M4W9D>j>;@`a=t#m?K#M~}}HEU-Fse@0A(1}Pad?5a{ z(_ZVzQzP8HsWp{6s>+5p|4K!l=$$5|r4ixdNB6H0$&RnF9mtg38X599@Ix}eDS*ZO zCpr^rih7^0f<--tBO*WkE5|)K%3e$ut3*E^%v&z|@#JC|06|uoJrT{*R8e6g#+II^N!?5t&Yhl7^1N zL$5ThvVL-$KqvFw2x`&-2wWdMwdTrJstnVGK}IUX(tw~?9yHRlsCEZ{1R$l1I6X?J z)$e!)(y)%?is`zTPmbG5ck^)ql5LO93>@+{w$NkyzI|Z&{AxC9mpDOX9iRViL?q?Ed8M!#UYCo2A7`sXZm6=SQ3mT|nVU?U?hZyqYOP{4}FDzCmJA()P{{XeZO^ ze!30fj?5w?0az6XoI&AMOH0ctm#);;ry>`MAH?Q;KcZObL1PmI7n9-K&WV(IrhOZ`!-D7kPto zg34d!QEI3S&nk+-0-g5FrYNUNmP}ENP5j#Jxau@(OGVXe;O1D4xB=?;ttTnB4ot*qldgK?aKngVUX$VWwt=F$RolUf&paCsyB*L=27qfof zK76q^N}>WVuNwTX%J}D$&nZ@l#02g55<(swCT3fT z`AiEo>*@6i)>#Y77EYX2*tTJuPJzi-CMPoQWTD+#pc04z7>4p>uf>YbD*I<^ZfkM_ zu-AwSupo{X1 z#b4mY8+sD87)9fSoMvZSQ!@ZR@vV747YJ58nioI^J-{mhJ!e5PLGqDc$%tLhsFMk> z!zwGm2H*oiYS8}_Hnk~JeE;uY`;B&B!ih-;U0{Bp$=?{E{66c-nO>Kzk%$#X_7TOA zJiDjyYbZ^6wM2^xubuvJOp=KJ+8XlKU_*QCz0{)$6W{I+=K^85tz=i<5NMXSHP1pO zHc?Upo*>WMrdQ6Yc+CBg>rNDM@6SplhGA^jK!9BU2cRth77$=JfR;!pX=%THGS4Xj z4S9f{O3KQD*#gEK8~}PrjdoN$^TY%@8oY;{S)GI|J~&+3?4 zh`RyeXFXRJsh!fP6Coi#y4xP0#njOB-m*PqDx{1XD?ASUq|y&)Q<{-6&k zlRI0%v3-r`M3(0 zDOFA)=koZ(WVA?I#IXkif{H4ao~v|%dHD<0zt=jwr?kh30Y)u+)OI7cMlG6_W=osP zRD96pH{3FLVRccOc8;%W-7HbHreDIw^ecfblg!V}W%^A^ii6LL6T1^y-#}DI)1Is3 zKBJ$-*FVy*4#`%^+y%*!a2u!sk4gr;UJ%d5tcEQDrX+Xe85z~%l~Fwg`n>4Gdyo~P zLm=nrtA2^oAN!4_KhCPh1^pqN-JBggS65Xv>aQ{lOk8E51op^lWW@GNSRSug^ zswMj6^a2S5(RtINn$OX-XAJr#GllEd@60jZE#5*6D!rTzxqR%zd-ULg3zD$a%UwZL zRbq@tm(Jz^K5qc`qx2#>_+A@WZs0QN%*t!17pM5Rua*4r4_je6zH1I4dVf7_N_6^^ zD9gP;0sAZov3c2-`TLa+nkj`3vRBs*8^X%u?~4vZs1*qbit4-|kFCktb|82}TX)e< zCO(AZDB-xOmtOBPE^a;Z=`-3>^IiiF{&5s}g8yPwZ8u6Xg7>&>p!{eM(Nn{M@k-ls zXYhNaXX?)^nd|A0+w2Fl=h`rel5tlxXTf|PM{31@T8Gn4p!)MK?Si`vq{cTL$VqSGeL;ZzFf#6cp!n2!@+-=op1BG%B&V-sKYgVmoM z$9`CN4xLJfNc$V8Q$D_BB=5YeDK#luyE}&aI*5HZ)!HTMKadV7YCYZ{J2kN( z9GWLRe^_i)#$H9Q?opQ^vgcmAHlA3jTh(|>EtyUM#hl-m8IBrh&}|em<%k5;#Zy6W zvyD4L{yE|`wN?vjrEeGf3fS2l1G#X!bI{Jr_t#8GuNFaO}AWQ-vAAC>@k&(3Mz zIS&m96thvL)ef0(9MLQ)!^3w!e*EQB`M#%IDpGW})JvEtV1!TetS7P5TY<0Sw0b|$ zDmG=w!+Soqn)2V!L$;j^lQ;c%-|n8RtX~pIUSzPDj{YgvoO-NCQ7ogjqupgJXi_0A zo}?G<5?T*-0ED~_NEgIW2Wi>1o>(Nu>%W!93KDBo7XieKB`7qi|8aB6zR$jWJoQ?7 zRSnl#U(@B+%2CgIW5iL+2fsg=44W?+r8qpvlR}L*uY3jG;Z@*QRt2EE5`s0KO`Z!I zNKi@Ml2vGQSW@t4mrVY0c+5;03{wKJ7z==yeJ0Iz(6Hk3_;12`idy0D`Qz32eUor@ zz%m0R){(5FCJg_>1?V*b;mgjXur8scjSc7yukiXC*lrd#8cKd-zgTzeP6&dF6&zPf zXy5#K+5Ij}2`$ZSE6}tFhGmIWU%TfSZ_zY;TD+!Kr9QcR%g=)|8bkx)mmy?|$j?jJ zr^H&Y!r%i!y2Iw?DU#Q{Vk#GzpW?(}22+V+Hqf;%DAs0;>H5t=g1xQ$c50GoKAe~K z{{{?0hFtaTqtWXZIYu{o=1NB_vYdvPNmB2Sfi4W(jP6$+5=6`!*_B4`;4g31}lSAnLrhLqhTJ>9&aDr^N_-AhGCO<*`^)6d8)x4Z{3&*iurnPHT)KsYrBo zu2UJ)_tjw##A94gjoOWoK)BfJXPHv&ND#HBARpBPI{H~om43NO;2%f7O z8)d-V1I_2Wl|Pe_Ni)z3dH*N3!b#t}(fnznERNVy?DE86W^!_G)&UOxw&dToyK19TNkpaB8IkL1)9LeJ z*H-rnwb+1*xGV3>zY%Aq{6*HK{8yzny*@rh{6a89)B#(*_o2!PI-mWkxyyLalY|w_ zGvKuXsKl3S z#(g|j<6C?e5mB^sWA$&bF(Us>(;JqAkhx!X#I3(gJhLUBr4lb=(`=#9Y#1T@czTyo*1&P#4)tU&x|t;Q-7@163hW6lDlH(e&l#9bNWn zpYR;SBfZ!^;5q*MDUedIOA@>cNVgzYWeao@4yc^JlS(s$WZAgbhE9nH37Q?>33bt6 z$QQcbqYFL@z$-aK_vAh%z6=OtX1-BXpWpgTQz@APfVF3({V&tewX#@uu0rGEi8-A= z=h2G@>;I7i#4T7S19QAgi?=w9JC_^`ObzZJvE8c;7m~?sOzvS9-m(!UiM^>9+_6{~ z>}f3PCG?U$**V^Gqxy|W9Dv9B?{AC3_K0f!xULGPQi*D+n~GnLsScAG!$A|u4JLPF#OUX`&kXZtoltdhiW`~33z zg{=iKnTU#9o$&hg^OoMlP~uW`%axsYMp9iCato7=Rib!)yzt-wJut^}6N}?^wFdmi?X z;Lu;L0Eef@6mMBMF`d01X!5yR9Oq_~fbC4@Q`;jmBt}v@^do$(@o0r$p<`7gLyXAj3pR3CxmfiN( zzLP&aSno6BCxsq7Ej`>`NRlWCrTtA@I)Usw`H%sK#hBpLayXtkH5GXQ(L0^4+UOP5 zL3!q27Do{eo<&`Eg81t2pNsOTuA=a8U2GK#^}7@C>---s(f3LD>Sa5`nlk5@kraKH zL?K7ctz+)y&1;_{xr=d=Y;O-W3mg2lHN$_YT5Cs3t>E-WdQkx)ng2^u=yj1-^>MgP z!;B=^7d@QI@XBU@3Fp7Zlt0*t8-gC|oOk1<;L?rH9EY6o*B%5dDqKwwO6$5Zqi0%T zkW2zPizrI8Gcr9{WXv*s{KR#Es|e$1;zKT!j~Ue%3#L9DfmWVqDV-CwO~ zw!D3fwt)Xflx|oL4hJrAtIH=cW8tnNNnE=?@a#hX(FZ$;2K}zrN8M<{^8WnXpxMs2 z$nk&S`&6uI4rDd0(6n~)vC~!4^X|!<%)y;v$5Y?nW;0;~-R}P;3Cg9Lm@XOoCI7R85Wfq$n_*^VIZJ3eR^K2Tv%7WhJQ6~!Pq|Z&9 zoP=JnUhd~*vpWR%zjS=yUC-B+0UVRp65-tWC)#XD8b9j+$6Twq?4L-kUk>?V2pbkWhvK1cS-e zFN0O^OIRJ5t~@qc&wiiBCDv1++}od+lp4I(cnbD09RGt5^jjOD%a)C&Wk(!{>%g}*-JE-JMt|F}F84fk{t zSPcm-Pyc)u)BCQ{rPI5ivb~+vieJZ)#83!OF78AW2iWOy-XmCmzqGZ@q5UVWyk{zz z6RC>>8Ln7cG6{wvh4U(JGI_^weCw~I6YE6y)5VN2=gC1rHm(7-jjzYV;G>PS<+LJ2 zHjB|qN*fvkJ8ISAIW9lX43N)edOW$G@br21K-R0eC;%MzR6tS!d3}zmk){VL_zIs* z3$*J^Q`sdFZ_x`E2IOGMSPTqhJ1y*CdhbJrKvq)eQ!prcBM$VY4%WP^YG|9*Dw=zq zYA~M_Pia9jz{NjeSP@K8k%F zzEaJL{PlzFUyhDZU8zo#?KYpo2aw!ruuNKX9WEeo;@k!TF zOwxhcHY!BEPOwj{3@x3&KCvh>cDZ1315*8ua!QPW&ND;3Dl^*QYof^j>Yl*B2fI{MmU{wFl_R`I@Q@O zPvROEp~hu=MsR7})cjYV(Gr!@GvxnZ?JcA7YPxN~CrC)p0Kqj72rj{$7Y!cV-QC?G z1PJa9K|+w=?(XjH?!g^y@qYJ=9{uC?>2bRGlROOS*|lp|t-aP zM}Trprmp$EsSE$^@tZT~!}j^ytqJ%&paf^7R9Dc<%xu!LySsbQ>Kb$q_AFlL2RUza z)HnjFkN>veezcN(;grsMUByKi)h^i!hm=sEq-vq=Q$*!#ZN z;FnZ=ClepUk<>j|$<4u+Lls7l0U+yGRQ9sLrDAIqDt?-krX~_WNfisI^8^(g>=pFp z7u!@w-c&}p!co~3-(GJ~#$48?KV(qli^ou2nSKN3Nl*wExHZm;hzl< zZ6v3rN?t8$MtAzFS}uwvlzja#HRF|MUW!Y9-E-JaP6BF9UJ$<8E(5ZP4|*fFAEdnD zN|xLT5(~{K@S{(SkAw`o7i49X^&c2WOg!Zyvz+;Q{_)OJAV-X9V{@kY+zx%7;#cug ziuF@e5lC*ZhrhrmZSrpcbX#80?4*i5wB1mCxxMOBtk;m-D|RRN>+@6Wkl1ErH~JN8 z%Cqkh;hp8T2m?6v!4FFq$)#4+2GeC9f{6~Vp{r%*2YAGoN`7c6C)oM&H_~Y>1{6S!?C-iytZ&)*ysQy94 ziyHRth;_zAP9&ZttU@OVZrt!zsKS*gqaw*Eeo#Zl$(e#jdtn`QJ`!)SXS zmqMHs?zNLll7PUB70X=y;NeNXl@nT2MP2puOkOwz@xz0DaG}2jw19N+k1tyX?@@xg zyXwPD$I9kmN$1v#8Kd+Eg``vJx(02L7C;s*O##$JABViu-i%$=Ovwk(F=Umn;RRWj zq2F5X&)G(me14x0)kK5`a{4hp4U!*z z3j!kFxc-oz8QaUu5`IK{8%r9p94P6hF56*S&R=x27PipUojB!7=SGs{cO;HR*AP_i zzNnyLVKr)!g?P~oe<}R>>?C%{jD*RUQ5%IdQ0$jYjmnV%!2`)saIsAo0p8&+OW{i2jztx+&6*^l%o}U#*2rV81WFEscVzczlGTX0pGh6IJgQ1c|iQ z%f*`eZY#SA@JXXBHp5AeB-RWjL41mM>=*o%g7l&{U#cTYFEEgNOX`VlxDPYbK_(HW z-h=a@f{U+_CKhuazIX13D<8hdXO&$`-F?x!PC2B#-4;}w(B>LI^V@;iXL9{l$YQLbub|=f(;n&R9PZ?ARsf&^J>kesj@&9(uU($1aa8i*X z$YDFcES&8h#WMdkU=7C`MWrkPEI7Y;w9Cph=5#^0cAFE~82?{WjYqhUAD_)Vz20!Y zq)c|G(&gl9sMrFT6^$PAbzm=y!Z!dmc?-2-MGt(7q{~_jcu2JP8El2Mc)|;tbFDHa zX`}W>FS5$N6k`_Ls&CDUm618Zb0{y^m+jy2mMSO|2 zhhBM-1v_G9zCGUT6yd~4h5eQ~^e$fjM>Bj;1+1>!q$t-xx%N8Jy~BRF#!zJTlST0_ zaUgghi!9+=I0iVq;&!kV%_@Rp#ko~B+wpf#pxiz*$U0N-wcVui&E1!5XTyV`*M=sk z&I@KkLTl(h+m)hdsHb5=w!e#^KuAO}8@>*flPcIHXp7AF8DT z%R@=fBUNoh^0}@6j|N}#Bl~hXp9Q!6AQdb}xcbqO0o$!wUfFGlz809a9#q;YJK^md z^g!L9O!Kx3>=VmF6EaeGjFK^166Y7|ZN1cKm;zr`z&D8akN4a)#)cONu$hzPru!Hf zUe%~^KpV~hrCyrK#To$CQO$?KP_Kp|+VJ{6@rnNji2Hvxq};1ZViEXvKN8+TAf+OI zJQ!poCI7f2fvJs0rKNeEz-t5HPC`Ph|FD)f(yx&8L;tO2{Xepv|GP{654hFkyki(D zyx-%j;~5#XF2ZX;%kujIv+S8muTR=~+laU%&0Pjn=#29nNTT8@xJ{!bymMqCl=Yjx z&;tQXxp?I5RC(Dw?ij_RS!aD_aUk4w9s5|nB@`gH+w%7=qsn3F6b=UB!y-A}_6In( zvHc{h@Z2)EQk&Bd;D7GBovog8&P8_2^#wM?KOSc4#Rmz=sw&y%SUd%!hj-W12T?qK z4FGgD+%rk@fq|+r^KHkbmA;s#-ZJmshVla;!z@$J7t#FvKYmYkMp|S)ta)zr_g+iZ z^4k2(w)L<8A^;uWRYWbG`#kNXNQH_TlRduut+~@fQt0Etb4voXd~gtjXv6cZuwIf$ z@7m#J+79RPb8_y(KO)UF+q2y%m_?mXnN zTUS^6{%ycAjJ2=?PtDNf2%{;r5erwF6KC9BNbEf(EoIye;`?$C; zy)=y}HaYD~#q$JgpiFR6u|<4x#-oe%qZir@1Bhzhdna7akhBzdwThPngiuV&gM%7P z7Y43x-NHX<5V79`Y*AdPQf(okYI+X1ojqpF3(^3*+d+MVs7ezx_=}Q}*DnQWA?7{K z=clfeije7olEO!<<5u*w@xJU8=})d*)ZaY-P^_kH$&JP9p$Xm!6J7i60NH&*wx?s1 zh*OgAMONa@Z2VQK(i)9FtI1NJ~ByweltGwtR@z ziGj|F!goXHrmk!r4`ocQ=?${vq`z*W!QH8}Qbdnb87<6*?xfaS%hC7Z0XKXp%d1U7 zlb0(ij+jKTo};r4;d9z(md#ES34muy%cqgAE8pjGSoKLwjgMgZU{femqbM%$c)$U* z&I1Kj<^gb=9blM}eNPWH; z4*8SBm8)uC4Cc*w09>V~J54TmZv}R}Ilgul4yz!u8eQ*KV;4aUGg8duLKQ%2B%YFt zOqe_dh*OlPV|hCM0fH2LePaK-dtn%498~+|fX;o>M2(Z;IoGsk<4bqr3k!Jr4umx! z@7K77hl0lVg6_vt(w4@wC`J`Aw}dTHPQ%@>lERMo_<|Tbo(vvE*KNMQ8}q0&M|_}4 z%s8r|#bN?N0LOApPFL}?>Yy9~VBJ7KB>Ol9)U^XiSwMuJnPKDR_Q)`=l>-+?0HhnC z3DSWghxp2CBxMV!9kO8-d>w~aJ}wZmkHi(!9hYt?K)1(kMvi^e1Gz} zM(~~%h~Q2s2)lxWEGtDg%W%tLr7Ot>a}U2ZAY3asTc*z+T54?Ta$#-o(AXOf@I`03HNa#L* z5uFf%H!{M7euLG-&u}w~A;Aujd$x&6sFtfpG<@Y95n)Iy@{ud0JIJ@8$k7N}BziNf zykZKmm13L30eQ`9aL?>WPf61vE`wP@+jHJ2A@IYucTmJEEG&Svke#i+tAN}9xG$l{zTns8D|$BJ=@6|%!aAMy|G70G$nC2f z@rL~Z_gjJK-S+TCWkg}8cKHEOg8l6TD?dhsu<~Pg^xf*nfTX*YG9|P5q$ze*ed;@b zUY!b~p*v6{OtO-c)|B3-fmV?`dD+m6z9xVM)NR0Xi44fxL@_e@8bd9T>0UROqg)0d zcDS9BgiTR2s4DWf{#`9-Oa@u@SLD(#8>)A_b-zm98e_12H36}qM&fjArM_FVBKrL} zp@;0VX?}uz#oRfJKoVc3lHOujn4?R4>yji#CuuG39&@fgU*`a_e zo;h~oJc5Pm{YpYe1l77|{7ga1#y9LXm&nVPecj=mpuJRU>#s|`Y0@i3FgRc2F5Xh4 zBTu>_kI`f_SIa$%(Z5n(Qc#Ul_iI`%%}>$V!*k@vbB!$3=dDg=|6^18S!v$eT-5>o zOzNurtb}wPrf5psC87r5wD7dt$t~!}M(z?#Ns)&I3NVl(`%26Abn^ zR*#y9!xESrT5?*GRgmzSPWa$iFj;;E<2mn#*nTwcMhwX63wx6g8e@Ui56R5_t20w90I%px3~4lm6K% zC~oBGLNu5ae@Px3-^o}?c@gtK90aWI-qQO87BDU|Yi%-yWX*Fp##;lHMQxd-O+|rC z`t}0RF4aS>tak*sCNbdQKGu2OS97P%9WB(v)KjIr6L^z^2(;E;>@r}}zOdBj7}3F% zWB8w|%pYfp`}pL8lfXD3JN3hcKwTc^H|)$C-ut^7hn7>8`Hvb?gj>uMn(M8!Qf>!|{? zi-8LwzoA!}7*r1HdPJ{|Ms&(jnEj}i=|kRq{sH(ppPi2vd&|m8GOk68Y%~DXs=;on zA3I|{@yoFeUFmF%kRfXI?2-C<(cDHsIZ#y7!l7|O59dN=Cx8>UpR+0JUl01CP{ljE z#=1A5kMe;zFq(Zp;}5EIGn*t8w3&5Ep-adT6vz7zmem6h(l=?<(Kc$KeX!s2SWhas zv)}O*dS3DmMw+Mr4l^&u#ERUxh-c4ITI2S0Sl1FE7T@C1#CvQuLAS`ZRF(L;dO5yb z_jkWbDn(b!(#;&-;Anz|n`&ix!UmbHD!{}fGwOHGRTu|qv)q1kW?-8W_lwG9UHD@z zjU=xY7gEq;f0&D?9A~&%F&3GPD$RnJ@Yd}7HBv^cNuie;64s79d$fkjnM*P^hEy76 zit3TDDleQ3dBf8Cieny6w}?I1?~F7(eMEo+g;4vygV&` zGXmzZmXe?ox{jxLL`?T%iVTtuG0UJPZ6t%Q#pQGzQ1yW=J)N6oa7^@6`Vrz9;Z}}+89SRW_Su};`i1H z{JN}WbaYU3zf0XNevzZqu(qhk8)Bu#I*D1-dFy>#IK*1W%j5K^?39QzqpY#j#F!^t z)DHd(+j*eX$K3UxYFssc9Vs z3&7i`*4Xn8VnV)-HNw-eJASuiZyI!v^uj;hYOh0@T>g^7V&2F5ULjB3`9_vGf%HiV zo3#;;8oQd+kNybEC+V>nB_$=zK>s;o$a;{7y0ic~X(AE_Zf2bs43WF^!tt-EIP(kD zzc4YBrphcg}keWOAi;?dR#Rg1T*^1*wU)<*rTbPr!}nEQ~9m?TH4*pGrnX? zd*tA?pK8--RryZ5-Js=LM1P-#<1pNvtyQtjb8BW}RmnmX7&Y^Lb5f_FydSjFddDi4 z$Z#lw1C);gpEX}Y_%8@eV<41zLv|8n-_tPFfc;MoY&(m8>sT_IiU#S{nKK^O;4a^& zawCG6NSOC4iaH~RgsD`4fz^P}*mN7RT;ToQd2nX)cO` zgM;&1kH<&Dw4R(!>kTcmI`C?+NLvtHb+*7v;>P;JsYJkL}Kp93bDj$ z4cS?psP0f?$e&iZ83y~@<_W6N66TCpwkG3Z@gq2`6groSz{X+!s7(EQ#`3$XnrjW zG*DX>HO^cF&T-gOLxs|*)Be!QW#y}AZbstVf&@7z97yjPu-? zXPHmnM$UMNMmC-awfw;!o~vK zUxB=x(8!W5nKwh>5FYdDL8`wRndp1r+j_K=4!L#yS{#KPa(63ZE=%? zx3?N68Z?9tsV@AIM`fQ}J~JSCS~_BuE>I_*VUzNUdc?qx7FWhtp;(2Wl3Rt*G;FuhUV~65gAp>y1&A z%b3Q#YUX_i*}bvxHn%w^wIs~`9#R}YG+0WRf$pYLu1p_x&Q5$m9jWfy7hu$C+p1Hx zaO9yGJcKjHAMsA$SgIWErM`}rU{^$;a1&-E5$^q_nHwD|q|J$4RU+=KlH@yS;EUr9 zbC0}Af#Z)MZR16oA{=0rb({lx_zz=V{_RiWLH}ypDFn&ZrHdpTP31opf3Az@hSgiy z@KMWX>|jNrG%NRRKd~g?Lo?94OEPF$Dx)ooOZhWmVpHz)eEQY%tXvrW&8G#pbm%%B zEf{~*DrV^Re0sQeu^o5lQG2}bNCNVy)E)`j5f+?)o7pKY1#@7*W4yh1$sw;?X4Iz zyj^wuhv}hU%x|cU6#h>zA^vw^ww*e@HP+3bnedrXF=*|D)}HGnZhd5&1cZx+f~kZO zce=tS2|?uZt_9k$qTjJ8&c}Cj1JA}wyn!;c`!mgFO6$0=kkxqVdEQf#89eRjN?3`( za`oVvB;183^?Ev}beRR=rfo4+_b9i5-sr%n2kqBa1z(NVX&#du);4F6E$yCfIo#rJpK&gONNX5$mHde&ulYQ@h+x*yJmkNR^NO%Fe4*t7{Tt z;Zrq?h2>e2SD(V`yUyagFLFK)-LHLeUU5>_FJEYDkw18m7nmj;6wQ4-r;w zbbU|X=58gF!U^;HB~h79nzQRG-=vl=Z4jMUSVO${h|OmMv=D-YTv5d7!yfxPqd)f?72>=WL# zD1|y1yPX@E6J0lp=Hi@>%i3x&MV;%rp%t$_d<)IYp<`TOtGi-UC`&XfF0Nd#&nm#_ z*pT>p`eVBQk*-`@o@Cj|SG|bM*ksgm5xUBiuJ-7}Bmv+z}a@D8^!_Vz*@a%182-xX{!OjmGQ5aloYBlL|wm!~J ze^?K^)PF&QPNKS%wzhRxB|crX*!q-N%w0|!LHK>Vot?wvhnHG^Kh%WJ==YX7G_gD}FbSk$!hCyYm++rRfq^r_2)LT6O>UQhFRP)JDa&xMyx=BAX0~LqZGvq zx>_e?b_KhY>91vufAg{gPdH9h=GKBd-(e-b=`f^9EcEhTZ5H`qJpTSilAhlOA#7H` zHnCGUhB|Ll=wQ(%g0Y`Gcfq!)V={)oak(K0OOfi`ECs=a{BLO~&})vyiT9Ns@cZA0 zQR(9jiamzD{6EQpA`!$sksk%xBmu(k5f$DDL<%2+zcRc;3}WB@Gk_!;e-PYZ5@%d{ zItUyD&G#qs!DpO~$B5%&tL4TdlP5Fq=bIlGJnt@me-ys-B+8Kp^bFIJlR4|}KZ58X zTGY&JABY(CSUrO;1F{Z4CL+5*=H+6pgXugCB_-B}SA6|#_-|m?!?Sw^Ujhzspo6)= zPWP@hFzHalj2YdbKb8_yO`yO)o$rhgbuFR2Oi>R&BEI~qKpaA!`73L8R>LG{=>wAf zK)N;WxHi4J*aI#olkxC-;jxqJ>#M8pSYeK~IQq(Z$i_@Qz%vIw`SL&x zJ2y8c=PSK27}R$N7A%2Y`}pJ}B7c`4P@y|f$#4McUHHzwbr%#r)@KQ#z_;TdY@qj* zGtxJP?{vLfEZZv8-vUu$PoZ23U1_JjlPQ~A@UpqGAoIo_Z?Vh=`>Eo_rhY)V1si^!;yh`9?xCuO#lTm|_4W@3U= zZ2X46Q}PA;S?Ac54F`NH0JJz{U{F2WQkk0Jy~F z*47ySF(8fL{)`-m7o!6t1yWfp=U<|(rFs_3|7_NYBK4Z#bRI6CBMN9SWxUh7aOa0} z6+44T8<%dPo+Ds-z5aGP!)ai*4TJ4^bvUQ*_HzuyC_un=tvvv2gowNJ7vli8J6)uX zMX#km@m}98Ru*|1{PAZ*(l_n9$MInDVz0qQk`@z7;;`SICD4oY=b)uM>W`!Tuj8#z zXGca$Yx(c-<~E=H!%xicUp9gGT_TJ5^z4y07(_<)3lMf3uEk#LPy{%mpc1HT;h6&N z9Ei(%8p6LLHzl{epkE`u zn84l+qjjV4-Q=IwzWHCD>8@Wp0}v7|Yg;Q>A{U2>##gEO#uacUkhfWiC|F0snbTHTMyYQl94+PiV;yp2 zz2Agbm^i{1EF)3Be+`!-+aNCRCYaUN%)Ce2rE+*P;h_ybbbRfywtxm11@Z-9Qq2(8 zA1eFGrbr#Wo9YIH1kR)BurtT-Jsg_1>2gPiEfY$3T7wv|@z!GuOVSJ4EQX-rrKEEM z*Y;1uf!wl4qtB4|CX=E``(PP{;hI-q={~BR!JYF&YP6WS%NFVv+xK_bIEU&WlpWcm z{2Y?sY%1kdAvUtBY5J*4d@722Q)wI)@~YK&xUg5@HCx^BwPv{b1BRzn1m7KP*XN?o zM}+j>!4A!derP-7npW{nCusYTH3J6$pj1xOimXX!$wC-UalJeQ64qyhQarGwesqhv zP)s-f^Iex$A9emDz<`bN*DfWf#2`IS%P~|CpYqoFLgLD|&ubq{+r}L*AUQ?M@d@5@ z%LtIatW%Th*)p{2sV}xG^P@LIh{rjq*B_K}n*gP3n4e=*n{3ztYEQD+YO)WBRLiUt z$|hCGK~=v7=pa4?KO4EZ+!VXk%Q!4wc2L?T%67w)+&!BX23#BcX9*>W94*97ZwhEn5cx(|)LZZj1ZNYqff$SZ;Gm_yQLwK#dqjwP#`TpB5|yv z#r{>DTcCZbItGe)PeX+K=PKmJb0=`YegDejW6Ix2KJ7>%3(jA;9?%OX}*zZWS zm@L%tGHhC~+16mnvMccJ90JZ{$;Kf;yeDv%C7!qzJXgEq1J`!7z5=w6)oNmH*84rl z;oA{MW$%$38R(B=&Xu|QGyMtA+(a$HhJs5}T#hKDrnYCY2Gk;MQs; zcW;euaVqL}KQ;V#JsdO39U42esZ>Uvf@FXd_mQ>#0I&6h@TPGLWoo zwy{c{_B%5>2E#DA@Y289MtX?yvEzr&^~W2Jj2y9)`>lN2ew6^q@A*%X#Z!`XCWXYw&gsx zvP~AC+b~rbFX(-IK>#{s_+m79aU{;re1U^%9&WveGqq6gE;L0YvxYGJrgrSYJMZ`~ zKrv&@{Ms8+BOM;@M8DZZ_P0JuN(eS2dZ+WP&fJi13A8V(=+u+G`M`==!0NdX@uZme z!xn>C7*M*em7P5`Q|si_ItwXiH@ zm9hTg$DwHcl>QGRl27N1GKb6Czcrp~OvPJ?3Ss%>3bUFEnEgg&GBB;CO5zZ@G!!00 zOf~a^(@(!RE2p3ELSO!w?ocV%fZV996t(=KXU$W6bKKpbeL-p{<~8O<27L;&&lmqq zOc-KA)=zVlAOx3+-c!V81IzPSz7CfjHF>JJFg_9?p-v5MtCJj_zcf+4^00pBBL=!k z$M@i!oW$c^oyvcPa&`}!ZRNn4>SdJVt5buM)v)a_?Q3BO~Vh@kE2>i7+TKzwI4F$f8s{mFnx(qjCCp_?; zPxklSfM#1qTch{$Q>{5UHp>?qmivoaR0IJ**a(Bz%uSpsW$7(&QyveeJ!@xW9x~}( zcYBA27FBAGT|l<{eqjZaPK7rFfi&~9$3$eO3jHN6-yD4C>S zqnT!a)HGX+;CtPiieZb~bZ=pD9eF1@*zJA3L*Yyt7Lq`E-N7`%jqa7NKqVnAe(1!7(H6s< zIM+_3iip#OwTQl*7Tj|9xiHia*0OqHZp4x<6n{mq)&Uip4wJ0y$fDCxx5L@7U~)8N z@9s>qVjm@4yQS*O5R}g(J<#2KUtg{%KJ$dzW9ut{(0!T3r}7)hCWLR0B65_!q4-+2(C zJa_G(TP<%iq#F0lplGFdE}DG9FPv~#xQ;JIBG`=IecY7)V4kMbrc>ZHaV;pkWo0~s zH$?4auvDA+Pk|lXdF-z%vLR+jD@&d-)vGVs_~uc$J5Z29ItIBg)(_~y%F4=FY6mx) z|3F^xThBm7XOD7&IF9vTC1!O|4Yz`K4@z1H#TY?uSb$wq zyW%=X8R~h0fUWw3Mnv#Pw)w^trm$p*h}~e~)$4rkqVL`EQX1vHwyR zu7QPIxs2y2E>MdNC@f!5SJTAx?phtJD0|)CulvA7R-N0x8+8gu4l{Febq;&ularGl zlnzc#R;tXEvwne=8YMuKNW}?7}N`wb^D>lg-@~k>4b;W(iYK zIvf-EoYZzzy2P*+X)|si%>|ABAPQO0-KwW~LEs7s)Ntre5(KP?@{4UgN5e2bJce)M ztlbiMvczLvK(D4CPiM5UoQTr#&i?!-sp6kP@ZqE7lGqnC&}$*u`wa{f2)&< z*Tighmd(;uZ!wo{%)e5P?xNY7WW(*MuiQ=7G+!1C4-cb%l16Me3qZj7D7JreMBp^k zyBPPJ!Vw{eY1w*eKRIX6tEE9bTKXgUJHWEWbTRF%r72( zhl#Z{%JpDW$N&V02RMN?4b#)pwJC^-8qzZd6StfxaH?1VwB+t&qZ>ceGW(R!H0jgw za@Cru=95u(9&O{3llwuxqp-ZXeQWRgg{Tvb@|^o|33^)aiuuba{;RyP@d32S@yB|( zvRT#LXr>@3-}*NZaOotW*C^QI%gb~Trj9fB@NAbasTcN0L+Oi94#|oxtCeV-xkMSj z#(jG7f{-F^k5vKl#dlx9cYp2u>|7_A8Xr&ILVq3jIx?@}Tre1fJ)iRfQat-Qyq3D+ z_wHt=)fvgQb!2N9HAK@k$&5G>fzaN`of6`w+>HrkZBL9kg?xqPhqHk-h1c)izy z;w~`sh%Dp1f60uhyjXU)xFeUuwDjT~ot*j;h}cq$N79R?%$BUKdt2HNQTxEYU4!56 zmE{Im%xBPZSvtM`0Q>!*LAfr=tt&#kx<(`WL;dDjmI75EA$5wOD#Wcx3)%XY9ESo! z4=rO27p`kVpA{SI%GNQD076q!6DZhCN=hP(*a08UK4M12 zNFfoH+HaEJnKo$9LBJ6PS?RNZ4|hB=g!1JXE@x-ILbA(CKqmc z+tTU{6{zXE2;{uPf9-$$8WMVZ?;bjV=X~hcpD{N`D!lc)!BJ*=-`s0Ek50f=8BtC} zdY-{$=64TCP&Z?wTw<}FbnethDadPls53vYdj37TBNw%9IZWY@Qr#5!JNfM%{7FHH z`}M`@v5rnWmX&)QKUW4UvE~{Imsx}d!NN!iHN}eRU==bi+wB9qghWf>8uph2-lFH7 z8o_I`*@ZX!h7WiW%Nmfs-UjX)KiQmCP@?B8DvCKUjH@A!C|Aw3P!QlE3qH&Bc4E}j zBI*EyPKM`l^8TcSz6L5rImPB2Zy8V=yjVeZlPK?6(DMZBhe|ceP0u$g^`I|wI=~~I zhA-L@0a9Xe1b|S*-_mKxpr5eG5rdpv0(cppX^KqUPNN*8$Xpe|KbgqyNn@y++P{W~ zsMlA}{I02b(#G|~0e5S#lvJ(X^AAxgX8Z|bL1U4Wp(HJXX5*zbI5L|-Ki@{+sTHf2 zNS!w?p-DOwjj0)Z^+PolL80z9*d3r6!d24a7JPtTw;smrcTSpY&c3`yzKs8Nd6P!mes5+(>eQd65nlo;Ei>G9M$oWu+Z*G3cxDJtV9^H4B!AAxQ?pQwp* zAA$|AIH2#+wboDE`4$WNd{o(N(-F)OSt!6}ffM_j9B>)~J5U zo$_(T7CvI6y4zMX0PX+t*BK6F_x>;lm4*thY(($h0KJPvS{-HerL}T(L~irb zRp|TI<3qmAv&J(8UDdb=Fp%Z%$pq4EGik=bycwC{2*gKoED~MSo@czB)Adv*EEy-L z=ihhJRxJdF@TA%~ybqVNKhA5k4f)n=sqDW0=v=5ina}J)U(|co!dfs)Fn0>*uZe|H zQo0bztaypKxpLd;fh%d_Q+@{Xn#Wp=g#BXAU%4#jwu;HU-h@9C`yAW#Sv@5JwK)SD z!fcTWcwV?UIyw$HCmMgQixGF%{R`ES1p4AKl6zmr${7z3)PKye+!fg5=v*HL1d4rz|$+ZlJBm!#BtR z!uXpnS32RH+s1`fuE?HH5DO~*LS;Cp+NKG(COISrA?ujcO< zVf)2*KbAyce8lAQckYCPsPh(iM`4U3~22w;O5Tv+}@765|bNZ+7 z@AjyxOUQD}hOA?;o=)w<_&9{Z0It-hvkr64EjULWHcG=srbs{JV`DZe8k)L>ku74Q z^!WK9st_=b?i?WRZUpZS)Y`|gAHtR9&i9_=1y5{tH>l2Mr~*c=8qjG7$H|4FTIicz|nx(SV^ zrL~o2z!0UWEk`8z$NiL4WEIesTqrb}`srLjC>O))J zHNz`jhku{1tL?uQp1l*Xba54279aA!gmefzk+61dk2_Kkq6~K5nxzam-DEhpzzIe%h z3RWi9o$qEuwMG|?uyaE$a;^ZCN8mMy~WoGy1~@Ld($xS@=CQ_-dKX>q%S6~XhZy#ADi*n|)z`zL8>SF3THQF8+ znEBlNTu|hA)=ZZ%N@UO4VI>WY4q2T)0J4f{2I`y)R=BuR@)i1@4NDP;pOl$03YaV> zK0{(Msh566Ee_G5K|~sc)Z$4A=RixVHIu z5$sf+X5mQtCWU$o&)~s_-6-2W!|ze_eqs|Ae`sbZJOrB_G}J9U>`&;jaUn{0th|}q zo0Nk%O6LNuW1oYq&B0r~np`dhYdxU_a9ENi;HFEP>fIWAVIj^F#(rjks=BYC8TO@e z9aBp#l?jPYE%ATsNR)IMT8|ntKoYwGQkn^vJj2HS{tkeD4ar`vr2Hi1;C=Y|=kQei zxZg~HoK58Bw#o8<mmsg&*o6mXHmCA|mAbloew!!Vy>7%d?z}ZlJnPLUM!k5g3qx zhg*(y?}bUN;u|RUD0wL2hP)dg%y*SI?u@PZAhT?w+{x-^?%V~p*e|ufnw%T3jBulN zt;{jsw#k$4dw;YYHD%k)N0&V^wHi{R-u*k>VQ#>k>eB8mh@jY$3*XZ2ExY9}&KN~4 z?Y?$Wy}f!qHjDs?i0BOOlH6R=-4mpJ8kU|95=g)C{x-^=TYc}ksjG&=5X8GfuMAS9Ooq9R7lQ8^O0G^0p=YJ_`ur(Q4%=eH{4T zhrX81==H>6oB0Dz01=V!yWXD&uKE|Z0Fm-o1;6c;$Yz2md>75@naAbD%Uu*qPX5@WE{hwz^@-XYtFuF@a3_~<)m*mN;5?alzuKIbs~OF{CM zO@_iKomYT7@*Y3>JX2KAE*wSd@M=l%vMjdL`gE4i*@3Jcey^OqyjltV#?zUJE9Cv( z9;cFHERR!MqlisbyMuviUQ`yPD+cv0p7@V<4eQIMV!LN3Eo$vU8*jqn()#6kC_n!L z7zSG)mx^*n6FrF?u&a`|+a?(ubE2aE=!yyH*86kFND>i;1i^|nGSd*~-2`c{ZoghO zA{^6NP4*2ZQk-#MO8|A5kOto9>}Q%=jZxyO89QThjai-q}lu{HILm zL@bOJwk^@M7_RUGV`-zl?{+r)ZTeC^#KBdt<>y$G5PyUbga~|QbOZuoT{!1t< zKpqlEUQcK>c@y%I_$2usq@w?eQt`is;yixfz@k-S-v2kbNgfR72`{=xcf2o&*G)!1 zqAgpv1&Q0Ofdo;{43HW5a)v!ouj|(R|3ugL|30n%|MK#=e7oqi?trq2iA4NDaGBpI z$y!#k9NHNLtZQ~|SqVT_6odYI5<8omRU@XjllN%(8{>rzgPd7cu%M7G)nv7>Rh)LUbIj`|+Ul14>(77mczZtb+U^ys5JevEC4)ti#6)LEJW2j+?636d zW=V8@m`o*L3iP_18ve^=S+#au>o^+{NVVT#Gs zA!{M7Ca^-Ubp6qYH4TT*DS2Ft#f z`K6Mpn2Y3G^zvNC;p`dYH*}|9yJt0C(|5hyX0PoTGL$KqG@zPQcv&C1_m*FS^jbFK z6!w`RIu(uGm99Y_x-{C2Z{fs6C4)BOq^;<*6^2?3UPSxQ$m|*|e;39SG)^dal$u4Fg92yn zE-N^Ev5;1(s`|NlM40jr&{Z)cXV!R zPSdsiQk~G+(=+Vcszy8z7v!euXKcuyg{v46mT%K)>iVA`UzGI;d0r?}W#rO)AlXyQ zY%2Fb%r1sn77>zo&6 z|NY&=T7d4OB~#s&7#<%)cmk>dqYq-q9~q^ zSaEoYUrD3V{_5{Nw~k5|DejRzJ|yRWmc^}E)vNmt-CbXCRuIz+$d|%;6BeAXfCRB$ zFzR4vR;fpVYzG8QTigE4FTAbm06~^Z>T8QJh51(m))^YlZa@JT=o}dhVjuV6+kjl+)zuY1pF=xTrC32N*5FxK zd<%tK1%x<_N3LRqvb&h%6s}c7J8Bftn2e>tkEm!M@tclf$zyRjIHvnu__)Q7^CRxY7;4K@p(@zO z+&`CYk7r!*5}15uUHypOoK(i;vR%WsSOry>Yd_*`U5+MRTTrJZP2YsX$GEH+V*2c! z96E4uF>iH=zka8FfL9NJwD7cr)Z=6Nv|I{u+>m8*l{@@jiB4SRzi&T$d`l504$1I- za+Tz80X(Lc0=>I~64Dir|LY$Zkf(}!i!fxj)7Kdo!bI+^;@j1{w*Is2fv6OK(ywkn z*XD__F~-Aw!1H6mMg;h*1pZ(LV$pl&SiQ?>vxEJZzW+trTZUEHc3qeEZnzkFsE`tIn8n zj4{V_HS5w)M5@$Q(m6AqZ%HTCQ!|kzaH}06xoRY7r(54``N1<(5Y|#_p~Hy;g^N@Z zcuy2^b^6mh`i=mFTSkdwP9@h_!8k~k-EaEYpQ!4b1S*V|kWRPU+M+;NEG;z*GgeQY zbfm}omN(Wg7j3R<2?6}3=b>N4(VWBdTurKvZ|>u zn$wr_tTFnG0Or;LBJ;aBT(s+(rmUk0?9?Sb@EQK_nKx|Fa$9C9nS~Sxt>Hq2eq;qi zy$U^?bM=044%4i)Q7eD`^fu685mV9;wz&G}YrVmur;EEcrsUsC#-24_;mZU$pDUS; zecH=d$NIuLVRQ)Y0KXN7e@I*#pVM0{PYT^8rIcxz1Js3Ho@$9sZr@_Z^nq%@`5b3h znFBF?U6sBz(^VjtX|)$?3_Tp=o-_x=q!kO76Lnt@Q>w+SFYCqE)$uFICdu&-a3J*2RnbI6_$=3sJfd-yt@hnnh2c{ zAR9o#^$jNP2>O>0~-PD>!8Vt3`6pT7Vr_Q>++j4TGj^PS3& zNm|83OLih6REKyfMG0+(88M8VXuoJvSOVHcY~MfpKvrkZ>E;4n=y%4FFMt}G+FGiM zT9Y}{Tn5-LwXtUMwLNmz&O`9Iqw4q|sc9*9r{AJwQ|(u+(?BoIe|O4p6Bu~iCJyud zA{(Jwt9Me%wa@oAk*hX*}Cc@Om^c^H>9ACQ+o;sx_6*VCn$XC9@Q|Dq6S{wsxu8KL)ENKt3i_!Wxz#Wl$wTl=xll_fpH z7Sc~Hl479`R!foE-an=*_&rR%<4^_h6M)t5p!MGOPe15C*ElbB^#~V>iI*Qk8b$wy z^;0=tVog%5rm45BGF^{cRaJppvhq**@zD{eY}~IX)T?Jl{WR@9WI^X3VpH=$h@Yme z^2BQF@iZUxv~^zPp?zP6R2&Xqu83V*izHM!X}U5 z8-m4pSluXhrVc!r9z!idQy5ShJT4{D%cV$**i@iW*TynCMzR4b2{1|r=JP;K9lLvZ z0GvC!Jz@%29dv~Jfy62!csCgTgjjFW-0`{NUQFc(KU^Tl7xqIJK83D{PY^5VdVp^T z?It+nrllt(-KXeB62dJ#D;bFvs&t87h@SqmiCd_37h4qv%O9rGE|! zSoi+>cs~dbelLs6fMh1!{dZ`OEbzGpe49X>SVBp*@h5n6MwZC-*+_w41Bm{iMle9X zxxw4d_jXsWp9yK1EG}aC2giGz0Q&c#8qL)*m})A&uG8SJwPVV5pZ&x5U4SxcQlFDi zyOzt1i^DYt*vJyI*#d5{60Yg~?ivgyU`Y@ucxCS8b^R4qg%0uxLOX<_hrV7A3OJt35irHk5LKowl`IEez_U_3H%kzNoE`N=SG_BE$Ji=e+}>!@DqGXw3ZMXf@S&p*{$6&-sO4Pk3{u% zdsu0hZ-IWjP^GAyO&!h0@6TjIBhD9WY&_d2(nGUn)=QLZ>JLSzD%Ki2BA@^-d)%;g zj*Y5IZ_X!)plyI(QTa|N4Tq@qzzU-^XT*jTce9WO(SJ)|?ni6pB-!`}4+XTMblr}_ zh(o`khmy;D`80AN;Z*d&PF1hdu|-^rUl_*2pvuXSEWJr4xMoW%%lC0N^t(PT7#_W3 zb(U3e^5gP+%^O|k-o-yNWGsV}Sv`!bcjdZlA#gvr-YyiG@06GT(Aq(q{&StNnvqkY zWXn$~EuAlmwp!0)i#0ZOqy#P+K9%6er>lUvoVvzjtPlMxanGlV)P?uVi+y%KQ5(R4 z>c{VJ8MB79cA>hgf2IQxqE03sN|{&HLN#N%R=tN+h;J0re=zI__+79@K8w3sCSrFY zcy}j%Z?ilfcsH{~Y!xWJ^^{$UJ~($qzT(Yh9$17?F)(i<$X5@zuD(!KgD4PG2yCpc zf7D_|e^Xl1oNPfBBD-u*)7&j95vl#`Zpf{r()aetKrJrWXR(vUBJ`doVlRDG=f)^y zD_SUlP|P=RGTXq8jg;z!s>sX4te|h#a_-uMO)8vFw5ey%V=FpA?79I&+G6_;4GBTwMsp^;X3gpB`}ZR-93EHA zfLna^;>(4VjOf;K&Y%O~#_n0&Rk7;oiM8U7Bk6f*R40*y^C84ir|vk^z!~m4wqL)I)ht%EsD>)L8?p~ zsnq6Prt-P%Ni7Qjwt;RG2wjQ&%v;x9rLo4v?I-cMcnywbexKEkq7!>4O20_Uc$|Fl zX*`vE?rZBZk}e9LAy7Vn@{Q0rO^KsRZxFN-hA2sqg4MtEYO;*fYp;BJJR~w0l)Cr= zbx4QGX^dCYURyCMr~>*~cUNxErbj@rgr~J!XpZ{j7>;;}nzo1`Qm7!9Z)1&`AU(%4 zzH{~UO!h%3UHMx-Q@jUtnI{}52G-juCb)Qe>Qd8P(84wg%T*}!z)%jqQ`0_tHyEjj z^)=+#x`3JDHx42vr&Z@idoeqmhPy)hPHBiMUT$TE>snp0%UzjN`iE3Xg5A@tmR8Y4 zi@zhyuv36TbbR4}o-cqFQ~i5gJ0=Ho^Zi0w4z)LBojr>C;} z4f0(UGTrlJD|2`8ffXd20)PhP;M+HLuzq|%djGI$J=JcMMjR^aRULb#6PwEB9D2!E z%||Qsd!nW1!mV&0wi|jDnXZIVTDapT0JYRKmn>NMp7ES*Wxg4YK`N+KO=fRe_|#=o zP&tFdl|pz^E3mZxjqeVOcjLJJFDF?Ij)fzX(bVEIAX%k?ww; zYc1}D$YBAeokD|U>L<&BGETb@Ts`4>{C`S2e{P4%q+bl@tpOLz3Vnz3 z;iYWhUz;}f=qUdDC2{WWzp3YZ?m&TqSU1YCn(-mw+iTu+vCWoTag)>M~aq4dS$!gwP9wNLr7`H=!P4{j~EP-1k^-nOhmxv5m^d%}A0D%4eN=V38f`nKn`|G$q`Kb9d3NQkFO4I+5bQIxQllY3cS38A-sd zeLShPr`n`sD19?S>x$O-LG%})Guh9C>SY77p&%OZ8^LD$j}}(HG2RqEpokKG@t+D# zx=M8N;dz;G?C&xilfP!ciM|11(~z&Ywl0)^TNheedER@B>X$Rv{0Ks2*~uXk_AG^y zGqdjPvhbpw*41dv4TOBozwfC@a8c&Jfa&Rvdv2Rg|z-iVZ zwUz}Hx{7+JeRa2OOzSCl1M0?#b-xGMJ0?h+MHviC$9|jnj!#4nZ}a4#5`zu+Ou*%7 zI61m42K2od{Dt^-!sTs^bmW}V_qfX_zm&|yMK2@k$pGgIpN<-BWR9B1`Ksz()!!Uy zYI8+vXLS)tN%HUh7JUbUV$Jv^lG8&yJY28}e=ghId4&M4*<_JRpiuOLRu~62&tG2Y z`}<0_djp#VkF*t`Xqpd{sE49GJu?C7@5MtI_^1YcXH(=({#n?oNtC_`PqyLGpv%)Z zI`ND)N+_y|mvo~d3USdKwqJ1fjyW7}=py=U(U9KVTfBJA-7>Nxw*4y2CFK{lq^uF8 zI)QN@8ZE-d3}}&zGBjElyFE_0LZ~b+ov=~)YDz6StF}eR$_z#9J>sg3#kIA-hIqBnDfFQ-SBY@^hGv#NfhHw@97g4l;iwpYPD4BknXY?ANVfPcFzLk%Zpa(6?H+ z%|@S%`HsYyW6P5G|D|-*h5ewqJm;KQG=*`-xbn^7T<=m^dZ>R-Po6OpH;?$^$n2!v zb^USe7ZWXCuVqY2H|}+(!IB=*kWqi8q3;~RElCZgY$^kuj-L0th*|-n|N4w;DzEgc z)xe6nL9wEF+HopZepr##7j?9>G%`2dxNEq9>+`sVrOToi{9DMr{Mv4zvW#re@8=fx zc53S5xm%S#GG}p-a4^Kj!Y9P|ZEwc6BzKya^lTCxU!J0Dyua+$8byWLXA_^8Ccy=) zz%KIM)A`>~38n|B&OZMx@uhw*ROartfRcHYp@w;+aOM@CwE)MNX~k01s!K0B8-DX) zX5l-;rBU)bo&v@i6hFC*{H$Khf&POM@>!dJB-q)8w#Hz%LR|l?{>C3^l*|s(3XSn) zgRpEDfm!Pnyt~GsFSD3rj$PgqyBH*#u?j=J5e4Mf^HweHrF9MKJqBwfTgr$d`QL=~ z8f*Ahb_kC@$P_#xxHv+rRJ;Cxrk&YhmG})Z<=hvKDdq`GwVYCg zV?^+Ex~4ef-r~qc>Ghn5JyWf@V=Mz{IvyWRe}#+8y=LFaM^ge4{;;1YR1z0~c0#R* zttdYy>9qIMD^H9}W&Sz5yKT^N1BzJ)#}JFzmYe9UFV5d7VR(q!y1R7Gdk$@3i=zN0 z=wY(?bcymLZfJ(?$>DuX`b$ep&G5%iP~q7y5&E96pWpYlV;a^hCOh<;UVC8?o~zfg zlv3kSe+iZ4|8!QTIy!JQe7M8u^_4%F^`sGXI-wkRWf45XCHq6ghAKvt$@=FZica;x zUtL@{VQa74ONX3Iy(O*4RFte``R1JQG{xJeB1O?f45m2-e*NS9IzI+_I`{9{YBY>W zITbc-qTUE9p-dXttleI?xx@%$FWQ!EB%zufeL}h7Yd|8evhfU)L@LeJ^8=@3x>Hq6 z^iC;NI5~P_n0i{x@&&(h>aW`8GPKZ)bW4ld<%?-~-&6b=U5(vz5}#w>y-3fxznso} zNQ4OKXHUB-O5S-~Cr3a~@wwwUOPdb&Kz8#i^XeY=_S7Mwt;;@}YEfL(JS?hoPua4F zL8+a)b{WrQZsUjf02_xr`?&Txh&=axrH;=FK%Sg!iIS(AveN@Qe-Zi6kYM(6mP7~=dryQuXsQ){NH(ntXsc~pVxYL+i{MIzpe^}Ey<3;)!;NQ$ z)J|gP=JakF5=$|E>7VRWP3}y6yg%fYS8rTZ$Xi@Wu?~h4F^_%Q=qkkNaXnNc@Cg?U z(QR+$5R`dj=Ttqz>T`@mQ@^T-mux{%te9^Nvh^K<9}GxMzj2t&@72CBVH2sf`2>^2 zO`G+i{$vIy{5}fu2f`PsavME1w(crsB*zP3Pv~jEoHaOUvb=5&Iv9N79}=3 zTE=LZ!OICla8t(n^mZ|GsKfr{X`(}rNY30(_ih9svn807JI<+*W4p{;a%Q(@JryxC z$r}0%(RBATf8iX;J>g$U;^!;)Lrk38O}21B721ylF;jo5O82U|mNbSq zT${<$e}6QaCn2ycLp|I8xR7k6QR0OKw&^4G%iB4iqM~AlzG^mkU~pzBn3(Jg49K0< z^LP?AH#VL;c`~L&_3*6%;J?8BkX6k$DbT~yQ!`T`yH@7moV=`o5Yy9>_n(j4`nD<- z{4#hr+4EPSZ-Hd_*4J-?F=_|_Hx%^kbnx!VYDs8M3ZES9{G5EbX$@=DId|cd@$Vjsj+HK4Y;TQIxBVb`6ZAx0i!!<<3$E&HR zfTnaM!Y{s#qUP`<)aTxUx?GDS-<1zY$N}%{F2U^?nWI4dURc<-dA!)-L)2~%Paf?! zxbc%drVG1%M^0WIY~d=($^*m0X3m;C4_EsBtKC1zOBm# zOtcTLF>`jbjvw^Nqv%TDv1B%a`U~n2e~3V5O>mH(Js_6oni8Xs*$EI=cK^=;@1ze)r5h#=S?!TF2X!{D&e zw|SWubJ(@3*+Kw*q=8Lw_pr_we)$9%D=bl?f^j32b!w?zD#k3KzxyzZq%bxO9tPR} z(Wv_WFd+Z8AAPr!q$9}~kN@o1yBx)+cU7kNV&F!r(h;By4G$wd`Tzv&)vcagfr&3Z zMMp<}|NgREeSjIQzXA;A#1+9=@Evi?69-u@6j6U01J{aZ0v)rX5 zK*PYq1Y-*vi6skP3V0|f7lK9~04dG+_atFpWCSw=ydAD9MnAnTZR9}2Y5f8@Aa|QR zF7#mm2n7bI1XI%1UKgbc9FS^iLlaAF2<+!nCobr8n&IchT=&wdphe zdkHbJu#kKiWnAjb@>9K`?+Iey%P8cB^=d?be%ydMjVOmznf>9Gt4i~`!a7b%;U9hw z`IW9WpfB+-0fSV@%hs}#%fSl6$0H?=6V?7So{3rVSP#t~g!%f_e36A_#(aol8^_q6)K^fxo{zvQt) z`JuKAg0n}SmG-8#i}QC^x_2KGZtOfq%)@;US)i}45;TGjyE$7>62rd+ks9M0rMLYE zl?H2z^KkEuzH6#wW!h)K>{@41me+9lTd6hELnY4hW?iSg2eZ2Ym@to_I(fJF(?%B? z{TG~^WkSMpu+=I?mg;%gsF1C@FEvWkx?7RHj|?mYY+0lrTV40lGA{u)jR!Xg9>$_T zLdY0#JVvGeJd2T2u*=29i#oH$z;2+oQk0DQwXc`5#=K*dYpj~goJ(=#%$zLcYO9V< z9$kxrw*0;q|0OPRqGk@-a6^p1AqfQ>RD2yHtx!7pS6=&jv(XILnKG}XYmh2)&l0IJ z4NNEZ&5~TZNwcqYx1joIWkHG5<10>LVKD~7{PW@N77T={$QOR;M~q>mudo=#^S>HBn#kmzEYa_!Zyt zk7~oL2ycGSqe2^sl#svMFLWqF@k!OjWjqE)i-XBC5oAho?9gvgCU4$+P^WL=n;IKa ze1Vl=!Ry2&tqWg|Kk$eCbv8w9N@#Q19RiF4jjvfvU4jvwtV7UV>$kTPf+tFTa zGJX@2T?c(~!49XX&FthCUt9KtT_%{F@>>&0Lk?Yr+6SLX715!eBTLGmN`E_pdwuW?uyJv5VCfqkKprOgec>$#eeGYz z#l(`eLqGQeBAb$nHzdaO|ZHCI)Czkiw z)ssEmcl|N=)eF89Ra_}wMCN_4SRa=$=#19oJ%N6@T-FXCd-GSFC(;ha+)vHSYiVda zf`sLBqpNeza%6kUOq5Eo?DqVOM@06;pv!>1!?3|DKdR=cF(JgGvx;mp1V zlIRe9LrcT=iEkZhjKgdy&oyM5du738Y+0q3H0dF~A3yS;IQMjx^>e#9^y3+yzlQ4= z2~YP=)X@F~;=7!j91u0V&SecT(MTUw)70c;jb3p@)}TKR;8}=B$)KC}OMR~1B8%`o zd)$UHfxHifN%CHmpz6sK+n+PUmj&J0>5G+NR@Gn76tNm_tAC97Tw57~ezS`$tvn4( zjw#HVci}}Srzr8ePdxGkr5Emv3$yE9c7d$x!rk>8eXJOI#B2$1c77zDtY_y|uB5e~K)U#g%W4 zdFtVJZzn`RU}_vu#*PC)rA$l}nfj{x9)}l3hdQ%SLQ8ir>W=Fw6$cd^w85JrZ{Dkw zOf&Xc{o^dEQaAx$c#tPpR!-F!sSUaY)@VxxZG0*(b>p+%B~-}sXuUIWseHx{Zm&otOE1exG3x9>HY{)u`$suro&c|m33C-gpl55^OL9In|1yPn${R0_n=(qm5ucnhm_Cmb!^73QDntW;6tj#~Y z?^jx|l#!h(a9{{xsS(zvDUm#LN`@{8;&8Z_f|u^C#02Qf@(iex$AT?Bv^*A@#f4buF>-I12iV zS1R@vyS>_`eJ!+pLo>V9|A^6&yPEA`1EBjARAtb-`(VD5iM0w72k((rG5(Xktw&Ru zUSW{-k^ht9RcH>Be9sk>b0=y^2yp}*>}CZoQ6b0~Ps?l8qlxk*_^wFxxY+*nCV;()=iSTQL^Tv@zc`IC)$gfue}1rrl(<_y~5`c z*k)71cO|_#+>r=RweQlVAqYoq9|5GoXS^9S%mgCIK8krIHG-*~Q>28eo_EHC3LzJoC5JD)K1sQx%TO$`N#F?QoT!GU)hNXSP11ikcKUX@Ds9G z*b$@JboOcZq&^j?2RT$>e*ULcwX=o%zG%2fn&Fe)hH znosWMYEL}t;<7J*aA{<*a@RAHy6l^-t3jwSZBImhp2HO4F|X6L0LsuYqb zTkeTj3<1^Tc|$8Ec=e@4UXg#Y{?GaiTMVfZ?rsq>3?*vh9v@D#1z7uAhD~KlBHLUF z2R&(b-g#qnNnaR!(nI>>1v-5HbbfW>@%G7X`5f_~P(pjRQbFYk6Eg^BSHMn$DR}Xs zb+qYay>?o9x)6U#jI`Xb?==(MGMvTY$t-V;GifkRW>7vg41JASu72gKFk6p7B`;KW z;L5!W+X+;lXYkWYmh>`;E)t6tcdI?c=$4dunZy`B|_o-|IJ9<*tp!I}8%oap~W*cab>@gvh_~0pmA3JiH0ZQ&CKTUln^dH%%)R ziUrD4F%IWOrU`Gg#7r9L_q)GbDkAzTXaXEoq09TRJU8FFyxysnW7>020cGncR-4c* zdOOUEgU_(PzA;DZQJTjowNoYL61Y4`z?Qx==8;XWt#Fa^v7t+JG|mH1HKCx-fxZ5>?1h#{L$1RDeg2ZO}!==hit zJ9LEyq;aH?)L7~sq8rw6gKYyuvk|0%6C^tez14{q-MhVK(e}x&4+)M*PL8|Ky@Qd1 z&?k+2)PDH)U05FXX1HPZEM=1G4(^9zkNzLV<?6*!pNhjnlQ`2T z3%Z-DR3VMlW|Djsat@?weMO&RMr=mklG(eYJG+uT9a3A&?4YuDeWe+;V76OO!xdvH zaibJdov;??oTp%Bi>oBnF#{HjtG>`TkUdY?SS|?_NnO<>Vuq)C^)qXMUBDyJuvE>v zmAal|gFK$r!>oHE?QXpi{G`E_~$K#E>=T^7 zN;k$5U1Y_AI+7z*AU#K!qzz=PCEJXRd|Qx>~L$03}m!$Puqo-F0g z;O=6S*R0VSL8Fz5r`1?sd^sJoADTMiXPA?YiZQ?a{9*VlU~we&V^xPp{&D|P9mI9~ z{hF`%Ab6Rv`8s@Zr;r+^ttM)IsG^*TGlah;HKp|&J%fD)hEzaWG}+Y&dxNvMTz(^R z68jul@_N3bnFl_sy$qc@?1Bw!?<{}mDR_M79<4ih(~TICALBqkoC*C>g)2}dAH_J3J5|7Y$M@w^#%-p#cSAIRA1Kw zm~9o^VACzrZKlNzWqt3i$`s!qztD*|3VyTC@%ywCdccq34F1ni^8YO;`9A~fQB76x zbwIXV75@OYEmDt>4FFZW1ryE#N=Dk**_lYkf7`~-!;^T%3Ie>$y{OR^17f3pF#iAf z8~+2@>oaSb1WNx$3-AC4{{M&39!)hq>V3*uE!BH{_;bj;*%yyC=}Sw68Uay6Y%RBael4Rcpf6507*HtSF)mme=*(zL9{zsT5gA>?GJhVmt+TgYUGz5 zu)cGCr=Qs}r`02J@iB|bv%2v|Y;}1KLV{?gvPftJDe~nHk@UBY3jT!0G`pBwgvbyi zqSV-0zVl(vmI@ptAo2;4>bA3`Wf#v1K4*qqqhmQeSMfcmq%Er3d?(GGbhH(+ z(Ud01dE{IwIapm*Fs`blSC=!yF?bp`L{YHm=2(1g)yGD0@m0!fq6dNO4LBm7BSk{N z_|$R3T1ho)Ze97U7UCoSyS;_WHqec4mI8g5jrN0!k1rvlzon*ABmeF4=a?=p^kcI= zD}pzn&i%-4xB;%pPpW2u)?iCy+V374k8tKv+p4+GrDj)6^sn*msOc(RK}F5Fxy?X% z{*e888h)fu4RR0~^z+C4#i2Ozt=O`2la>eM#ec!u<{5X*dU6<%wc zrz-aWID+Q0ZFZrvctkPu^MH`xL_B>^`y3wXLCd8sibKi`i->fHusC=%!MPVv-@Z43 zB=>oh1nVjmBuQJO(R6LrBXRKl@mEi(v|?Y3C!R@gVZ;VEBcU;`I-e@7iY5s}$le@o zt3OG-^v8hEt|}6b<3*7=#(zN&TAbWic6i!N=c$_0dNL?2kex2Ko)%;~bqXo|AHd1R zh<}5VyfuJNquB)TpB1e#wtwhuy5HaVO!B}A?)@O7og_>4NAk~PD*D7La#RWTml5@w zAo(r8)z`$A=do^ z-{GHM)l^I-Xb>0+y$TCnw-HpX>`H*)qvc4Mz(EVu3G6&~IPeIz_GaEkcv%Cp1ZRz0klsnT6R!`~@0EUBz83aHMDOYMcHb>I$mK|c-I4ZEK8$WN*R&LEJ#mzW7m0S4R0JW zYaxKeWAsJoyEY1V2AXOOy5ZuhCXrHxpEl&b&M`>-OhTB!=K$ZkvCHKpm5^ZMJOgubF=4CQ0YR~nt!#a6Y`ITyTJ{Q@(w5m*iK zp>JR2C~7Duyud>}_ySx;o*^TcCt)B4T4~p^gQK6Nc^pVrcIJES%_l9en(vetz7*rM)FTD{e>ZY zoxY6>={M_&=O`9h`ke%Cw1<9K`haknh2-fs>L$GvC3`x|(aEsWwooSMq zWh;iVs-%e^S39ds_yg@{Ernw<6P8Wm)8hXPY*nbkCjpxu4*&wetD~cXjjg@Xnp-d( z69QPi5$yNlA77Q=T%UqSL#DiHx5-y6>pQxK6(Vn=$WZ= zkU%DI!u|#*gybj!sf5lcx7PFvKhCahAOK|Vw=BWM`H_5gOb(VdnQkP}@nQcdd@gTO z>9^S6M%U(E8d31rDxRB3+Vc6XORt1qY-BCz=7teIa%vCEq|3$tG3e-n1nJY#z|UdM zu=J{$=Qw*Nz{`{;KO-Zfsma^ioW_Gz0=Ptb9}jY23Y?4pFRkx2E}Z=l`crqkbawgG zQjmiAXMh&jO^^v>1frO!k&Rwe;XP?ezd4FYy}v$>c%Htuxk&A2#y^*lIpiF{MQ^t$ z`OJ!pB|jqwmFYYmGXp`teRi00e{srdb@v_jFgKrG%N;&LELr;A7c|;R<%3<4tjK=B z{k*hSkLhT}1())lro>Q68pz>ARUz?NGO>wN+k9lGG{F@&dJHxP2r%wjX$k;!6%U{_ z^CU6l644-iNv&K2=wJ(Z58=hP=c(aedy$cui#I~h=OIYj$Igr0?kiFu*ZiD2PN8}C zGQc!`Hl*H4()NsT>9q5i^l=2+6#jwlj?!{Kb~BN%GZJ~W?W~1j`pGfmASAPx*&Mgn ztOthv)>k)(lkE-QO#HPO?*V+RdQJZi92*sSWvpq7P`Q~^E95w=kluTKuZHTs85p_M z?1c^qFL-ZGk_F~&Z;hJTI;&jjpH97x!2Y>Q7{B`p`dJYMEpYFLF4+g=wO@QBBe-ZFP z-F0%=^7#0eZs{`(-?}(68&?WGafYQi7Z#-F=k2<;+6*ZeT3$*1?Ix3Q!uwCJ1@NyhNGg)@m)37(i<7Fx7Aarl`ammgejYf{re#ju-$$+_wGi8 zpX99@2-k7qB#@z1Q2jVQxXXX+ci)GOR++zeK1MN@C|}CVw=MQUJH2sM!d$51e)6sS zUCl1OUD5OTNMCtWXi3@!9@=eE_y zNwnsI0$yinx{crS2ON9e;;6-Lf7e>hmz}(KG#DMCJvTSYTHu4Miw+x?Z5EWWR6-XI zZn2dBiR&2qKjW}~b{SAO&sMEzvYBEP$)!~ZUW)??9FOfhWxVl|SHk6TNQ`RjvDQHY z3b)4P(}z)Ek6(P|DD?KHoa4hIs}7PWQT%MQf9BrJ)O%t)89cI>g)_dlWVKM`jf1+* z8EF0OsF=U;>xi|AXGnAXl_+4Ke_JoN+zv&re@Rb}>QyV#1p<>q8vg!&nC9DkUsTgo zok0-G{0C3{Z+aLF*cUt8*sSw}z8w7rp=6@91}L1)^up|~aIE*P9tMJqzR{cko~kdi z+D6b$6-8%^q*tXu`|KOnMnjB83IGgk*&2Tc`b+=@+BT+Cx2^Qf-ANVzq-lG2>QSF3 zW6lCaYMnNwo6@Sd!X;XdxTmtCKH<@e? z5EB=@pT_u*k?Q=5c;cu=;b6=z79kr&MS;+lo&NxvSKebw+<0>g_08rkLdH;O_kWf#~{`I4qo14|u)o@jM(F)Let-jMo_MoYu zeYzCmrQWXhqmZCEttSmq!0tJY*wRPw5q@|07#_Zp}f7{#JpDQ!L z!0A%#fDAuBt;fx6ENxTy?;L18s3G#GIpYxv>7uZ<*qV&poW^%$((>eXd06?}!l69z zo^7S81ew$r1O0-Nm1c{ScdAP+$x4&vcxw^JXd zRIySftb}etrojeNbt++5dVaRCcE0{nOD3N}WKQREi8Y_uW2yAzCdv;lD7I_23nn)>%QN2a6Z z3WA4EtFX>XjzLTN1|_#O^E(qQPb<+)^Vt9 z+VY%4`DwA9uSMG<*mU^@u&xJ5<+K?$gS6heEe1(dJLJ}~7p79To9)z?ZxxwoYu9?G zh&+>zsf5_sE&ncBG-Zz?PFT zMIkN&1$S|fj{7&tYp$UI`DFghlGSgl2W5eD-9dfhoBc0RXx9p z-Sn4lyGtFMQ&HkmkzKl?z{F%+7gK5T?VZn6mN}b_6NRK~7AwKlG4E!Y6KUY1Ifk?s zjrRt&C$?nxz5#I$ZGTGLkZwb|mk!2!dEXF)D<9-CXIcG0t}JZA_+!BLZf7fz`u*Z^ zH`TP3ievp*jon1Lnhrc4t@D*5`46@gc>}smy`BZZ%Z7*$b4-mCbk5D5+~rve9t*nM z*F{L13lZ!I??rpGIcwEl*uq2`vEn8hSrh4jyo@#$l+>-So1FR?x>*3O z2E2TIeSv?u0g&$jxx-YWBJz4u@!ix)yNVl4=lCE8qUVjy5?pi6sIZ=g+s8BcU@AYj z>37Po4xw+rc6I#5H2^pIL-Ez~^d!d4IPuHMsrDkbzDg)C z{%z>T&lwlB_Re((>&ir*N|rARb6GNs$E3iIrbtm)Sy^2@UxDVInAf*8eIC`m+6#9b zE8qnLbYa*C>p9treXp`!KTfdMcLl@(JbV$(={c zF;M?;c0OrQ$-gj4#F-fRU}@i|z~`GR$*A<1@IE!1&>}f{xQ^qgi`F%?BMI-)tExB7 z&*F>ty*ojO-Gvm+Oei6Z(@l?2NyMAUMM}|*#cWShGFF{lwLlr*c5V72o5vpfX~c`k z=NR^7->zG=v7JzKw6&Wo#&dzFu53CC9T++bmm)ajbeaM*>sQcmGYh;+T5WEjZ!1a) zNzownlH#1boEE3NmM>aGzMxfw%%9sYX zLOSyoE|UN>=mFmLh3{VVyS{wZ*wiO?CO9dDw*-UR!bkc(fBp=7 zHbAfTsm7N);r$yG6|BT|;xs%!3$3aEUhm(35SVd`G?@im+`-=l1O%LxqKeGY4Ym=Z zA$A&~c{$6EnzI9CO+uf=>hWb)cQ?oLK~FV=Og`%Wn{YaGUoS#|k(PnsF_a>gBEFE^ z+gy&k{=KQG3CK7HV$(PD(~^@r-N z#&5{FPEM*sGT+%HBA4LDO5q|S{J`OZnVOnD`rt2>qj~W8g)=p$?E<%eKq~li;sM`Q zy#nRmmnX08;-%@5fQlf_+@SkSehLFqwbnaB636^BR}8V)AM<9KOv}!XA=^N zl+IHa0z=^R`WA?@3Fl~tSj)97y}h+_6eV-Us^Y^(G->JSe<=^;_xARJ@aVMku}IO^ z^mM(4j5jTfPl)p6>uGC@0agoo2e6uf0)Jpt0zi>0oW7@h#6Y%d(jWKGsdkm*qh$2W zgTMJ|2wCZl#*>e~*W>k;DonzrcYR>h(|{Llxx3m$)1eas!xVht%oD8Q#F)W9QJbJbv*%lK?d9c!0-k?yf$U|xO{LwJM7EJ%bQe6 zM+Ke&MNBVq!uvQndivu0{J);f3)Umyxva(~HIYU}Mu101=Cq-uqkEX3;2iMIp1gcT zBP1l0Rmpe|-KPIz9(F_VOw3_Wk8uu6ErfLS_wU>6>RSJ{qqbfz9%yQ5!L1bJrOnma zEzkXN_;qu`&-ml5hV*|?t55IokyEq{a3sO~JUG9;z5D*~dKn?!@gSxqTx5Qn*@%yZ zXu>-C>(?(&wO5(%(5PR|0ROH(;EI*DUqx=aM_xYtD}R3?ziru4NlzC8{P_D=7ZKi= zs{s9Vw&Hd$IuSwep=a~WT_wjwZBhZA)2n2-pPQhau3)*kHURo!r5s|}`b-yEwaV4` zg@n@2+9Sl*z%xuXA8fz5@!klI<9WUG^s=OO$#b`)wyq2GFN?fEc0U=FV8D}po@X^U zJRAnHZ+fyyLX*h|Nc}U0t=-DnH@`G8h;bwO49;U>-*;3e#c9NlWX~6wtM#dnOmQyTzs}VJ(Oye zXfZzZZ0P~DGgVdBgUNI<7kL0`t!oxl!67fJzDpQ`gKWCpMJ8>YBm^5cDsLS}X2FDQ zh%}6V8ygej=v1#7IdL<}#N?OJGO{~+dcWQWh+PBy3Z4N2Y1IkDjySf&=* zp-!SMYlS!t_4fxV(@bf5F;IjWrHBuaO5DuL@%sSN5~?Hjr_X~1Xgda$!-;fb7FeP_ zh2#U>IBILJroL`T`_76Hc#r`@z@J~KSO!2K)OA<6xGx+`7ix+2OWfejR)@GO)%qCRobQuK1=m`@$09ksB7IT_e9R^D)D`3okrbnV;Vo&hJ zZ3p-3>+5p9H|Rjy96&~k|K+UVviqf4Fc0WBc$nI|@1~)F)8oZ+69?#n;owN@=43;D z2hy=iFQ(j=$AK79oR1qq2Uz;KBv;QuCVm+sG_3&q#usdyhj65bh{ zF8IA~p1|@?K|uk8GXI_c!~}5L90SbQ2tb&lzAJj`Bgl{nFgQXoZ-9^hg|OO4R2=jf z6X1cYpO7ZGS~g{L_A6MkoxzjIk2%= zThj@-Wwie+TQ~t0^EBKO;$Y5yuiR}{@6u(Qs9(;@{)YV42Xug$riI$`QtA~G~epb;n_DA(Ai zT%`&F4whoF)MBU+N%UiUT(V5r6Ht;nKDM|Z0?bR>5BPUkL}MnanHovg2 zu(TAQXR#s-)SvbD3l=j*$cJ7vq`%NEC3hJNKqpHk|NJ>)ayvPR;O6b?`>uO_dSF^2 zRiqe5RH$g7pn&=v1z6O8Yj-zq>zSE3sw~P;L{~RQFi*Z>;lB!youQ#2{fE%BH9*aw zY0^BueF7k4|Cn-~^udej(&7;Q|91qG&XX4}ngc?^&Q33nM)Uv9e*sqNQBhG$2`c|nd{XZ;5KFC zru_T;Ko$22dRVrXnlAh1B1B;0Esg;-#K9aAUOF`zNMbB=q_YnSb>$hx{*( zj1ocl=R*M_wJ&|`5b1w6U`=c|od-U?;`Q|r{QwD*!1-eX4gq|{(82vWaSQ}U852~0 zaY&TsAXpF)ELyyJ1pR-^P4w1fOa~8y@1S^W0Wz#e5fTL^D)4x5ND`EA;&oT7@Ipfg zKEUGvJLodU(}ANz4jRUbBM?EyyGJ&k?BEaMcMw#0Zuiy8WJE^@3TiUhKRtF>N~c$# zL$gMRp#Vp$Qyq8!ZbH;2p5iY~2S}lA+FCC^-VypuG%DzF5?>mW&A=EO3w6F3%UeUh{atm$9u5g`b@=wEA04zOG3)26#eh*X>Tn<9e-$1!# zwoDZ50i*xbW^_|}g;!(mntLv^e0q}*UHL0{IIjuP@40xpKK*=^ zLIL1`&?QW4S&`vBj~Oa#e$-#eW%|083KxYA`SbA0VPtLAeuwPs|FX%<+!554@5%T% zhCCAvM8v_rS*r%a#LdQ3&0%R$Hv(q5(c`A*>wYs6%Ym!d34PA9xX23&@?Z&(h9)9y z-3@()kO?2p1sF-=P#}3gccB;%qyC3^fj4dn^P4kF0>k6%d~_TM8KczFmD5!!AF*^A z#AiQgA|f{agyTc1Kw~xOx7lcAbXRvg*c@VWZnn}K&!N!|~1Bke%ky@{0(DASWz>tB+3>`L=K5V!A{>FyghIi&iKjjyJ z#6f}SmgtnJ8-3QLcHep<=|iTy1z&?!&yhZ#^)fY{s6in8=g8eHKl`Ve;3zB8iSm!vzt?F>HE$;& zJ#OHmG#{hpbm6|{{cf&B@O6aQN1jJi8YLumFPVfr?tcmvTo6#S9^)C)02BmBCP+~u zm!<31w`a?x5avnKV0jJ{0CoF)aG%+~&@%I#G4{1ku!4S_2$gBtI7owkZ)?iWkhUtNm%K%ou?S)&MxR|bjXPiX=N%R}mCEaL0}$vxIJ?PSjR&qVOCBL)FiAC3N5G48`oC&8#+sq7dE*MH zg$<{93t<$G=)L;P=cYw9a8MshFG~-hETnIEezm4>A(eYcrUiZ(Hw5TKbmOM(-qa^wn;NVD-Fd zVhnoNtZZ+o#J-OMClP&UpZ6sTnM zhJ2mi%A3Q9-brbz>^HyjBk&EDw~^wULjc*|XIEEL_^8~4v}}gb>v1u7vcu?dz0>FY zsd+1xs@=H!hr{`T&Y-F@ufIqi)-rN^TirUjq+xw9h^fTe`pV%ysjvK9GTIshl2;|P zv?-fM1J#NnzqZ8KMnD~1{IwIQd4(yc7wC9ojKzKSbNF*7!dT2IXznAdn4Ygwo)EncT_9+@oJrrzVk@sHClhE%5rvR-p zDU#_Z2|YF2NL1;3NA^wNl0U2U%H?l|-hW`PCHZ8F)^0r;AFES_i&-?AJwe^2UQopx z4m%6T)VO#52Hf5Q2PXgtZYmR8++b6+^GdoJ^)Lh#z;zWTQ3sDN$H4^lO~&{-zY7K` zm~0$)HZZ)V3eRtKpLm&J(3~PETY-2|Jzsjj+;gkT0Q4&rUS|G8SssRDM0i(^J|3S8iWEsFpTjv;0%dW?yVYl+||e zF}tt;1Ja&QwD@KySF33=X(P`L2-u%K=IDiK5rd&HGSV6)WSHk#^9`8pI^OK z&!2&fs49}p45LgAe+#MmT^5t$Kjw0f)sY|mz&bcd2Y=Q(ucgJ{Py@9#2A?Sly zt2<>pw(v9D44w&Ro-8+MeGePUyGy<c_ftD<$`{?$SEICcMd%8Zvp57h z#hN%7F+Ft^E$3AMIX%8y5>sqLOV7R=FCOa-_XW|WT?qdF(JWDKa~~@rc|YFdQYf>@ z)ssZ(4jDV3YBFxH8wN~WK)<`MOeYE;|1!EZ<>37|`=WT?`U&Ju&;tg_rB=B4q>!QO zUbNC{(sQcVa}XvbLj%WDu(Xb1S*G-1hy431u5qsSZ^uUZ%Y9j~rRxx0RzmJDjVasx zNi$IGIxoQqJ&eB=M711a3%J#Ls7>D3d%#Z&Ux`hk}WL%)Y!7_@UzDi+*x4u=A zgFFoD?nbxpHY%z0RA^?TBfHQ^RvB~BKTpvK5MgJ9ZRd-BN&0b9nD0$(J*l^<-1=8w zN3oj>apR8$!6YbH+8smnS7=%)sYG?s1llSEX&{5njy^i=4dQ-X@U9o~Z7mE~jlAqSQFl^F zRWn;Fkz1jb@C+k>KuxiBKy~EFQCX#HkS% z0g!t(r7v@@YDmYgH>5SW0G?HADp2}`gHp&Y-o7(9$p3I2J}=xw`_a9yg@D2KrR%2u`wT7?}gSRN!33T+~sRA@$u=P^;D_qxmo#p}Y-ruDH6Sk2SUM~{1hdXos zGJKiUAe{8!uv))RE~6>4@#x}sjW zgY-O%By*gd@e>mnFFJ4BnwHu7=Tu?N}D$Cr2CnM=igm*e?rle~G}4Kk4P0s&dh^73-X?|8Vl5U^<% zgDH!f9T&8I?}!TN+LDcG?r2L}iNz&kh{7mDftVOW5mIrfawg%D^%!a@f{taqbajKM<~S|9)N1`9^NvdD;u7ur$#-^GNBGn+G`}3YNRvbSQ_%0wTA+ zi(@49U->97dJoIQqgc6~v{kbU`+C^k)OixyY|}sqpT2=(Tw>J#)$6=vWk>6C^k#Uj zI^lV!1l0}JA@j3OTy*#|Ft_uIm*Pzltt_u?uNw;D7BA%9TAywJeBkQc=F3d2R|N?n2`fG zm@t{}YR6^f>~XyJF3B+6!sxPz0~wK(K1o1X8qnID95bR1m@`BuB;yH%mQl=1qDvSS zS{3%Rcpj8gFoJ2n2DhYfs|Qy~CK}lR%+m`~Sl%yPzd>^UP96ct%mSn7pU z78NjDSUB4ZMaH7@GKJ%Q5b-~y&!}!V$-NuOTFdk;EJfAffaxa)t1b+E8ZRXhBnlj| z<`{#fh|mvToQx7AQdY7c!_o*>Z{vFBBOST zRy(VjmBXo3Vhh(w>$CodR$F`1)wMMG+G&l&A%NaVy_B9MlB}KhwLgIrKH~kpsc7`r zR6q&n-akjN8}3iK>ABzaUTC84y`1Qfo5>blD3RS77v}b8}xoI`Xk+DlZ^ORvb{|%Vw%`39BlUNPnuk}ugHWpYwkNC zmpQP)bx(Y6mgC#uLFZ!6NdsaB)Ahe?KFXcMPRwg`cp84&(IW1jlWy^&GUD^OUHp;f zRB*87_>NOh5`(Ye>g_h7?IBC)E$j?cE}*E=TKfK>;coHIYt3q zh=hwjgpzN%6m}G~d9c_peq;#3JzCADt;t z@U(%&r2#DH_E0(W;Gr?=|6VZj(V+HJbSm}X`y%u(7iEeX^k#;FNcg1M|8T2^A4p35 zsJZ;%*U?2C>t=8nU`JtdzUjwz)NoFH%jdN{*g2l`1|gI&PeZXqBeY5Q$|J*++C3bV zdnvB=47Ssqvjk%!tF`>tAMdZo&iSe1;-{*pm)==-7Yqnw>He(~PX+nI_qo)8GmD=N z7TW*?78U>WYf@4QYP)>>!C!WRzr5)I)tzc(t$OifWu>`40{m-#-w<2pG@IHIgMirx z3I2`s&Dr6MP)vGWnv;W}ruxSrV-?ZYmDc0D@H%ev4z5=UJ7O{tJ%?#6vnn&f5RfpsO5=avX5)a@qCu*V|3q z9C&(dR=#_IUi(d`M4bFfnf-S-+DXO)a#TovP-yTZg-SX~e;iXG>uDsIet5Jn$cPRP zDI`DM4?lTYRth}vp*lw+y{bzd+D)N=76=n|>@N@y`~`lrJ`-s9>h^(`SC;LKXFk!0 z7_=p*Nk`X9B#;fm76dB{V;jl$3E-G0Ll_AgB@x$A?(b3siQ+(EgMpbO1;9}hh!8Dz z3u020_RGSg7Ph(Fe_{RiaLx%a#sLYC<45TI^>Wlo((SMK@5}^7!QvHos36e>IJPcY zsAPd$npPCpiUAO$0if@{JhWWzNf5<5a381;{5Hm#!>!p!*2F&c*> z0e@r&8TE&igA3Jm%+FF~a`nD~;u(S&ERBgTh9m zvdj+hz>;D?;J)X#jT(}Bpo+o)g?~3@ijl?^qr^n%7w8>T4z|buq4evse4nd`0}-J> zlAzTsgW!p!2h!J}3QeDw;2I{*uUmB@2}b}cX2qgJ=Ct;IJ9=%%M%$HC6X3Nx_*1m z$+lrXT;0&&TBV-GZ-#-eDBu;V?~E)-G@{C&Ng$-R2ADS_0{-sYsY(e_rQ z^QIyDvQ5!SruZqUWYS2UG2vUwxxLiLwqbVt!7un&Mka^UwRz5j?v3t>hN{;zzTKpy zhl7~r`dxbbwp0K1^&E=5=W<7-jrF#gGxd~tESxp)R^XFT=fQ9Tt^CdIE|DcgbvTU!9SPEMf zg@L6a7cSguxEGP*MFQo6M6rePttorS#$bWoUTh!14xwWPDpRSS=MFlw(hetjWn<0E znn_1|&M&G#+FE#hPK-0?^v*4Kn|~ZSI#(`|1B9wJY~?b&^tryCX3@YxYHGEEj8Vh0 zzPkO5c{y=y_BaXZ2CM?_b8%@M=-=rg2ekGs+2Yxjk;{_xUJwurK+Aj!yH8&Q>^Kg( zb0hBw!((gZT@zCz_HnaPi&;g15ipaoiQT=VCtDz?7^2kIDTxaIx+oK0h zrqdf=HGNF3o!`Z!!KTuZA?da2ncjTc%(-Uq@6#E@?i|U(?)`TrylFx_p#BHQ*1-)`o7@5($xdSlw?33IN@|Sfc;7(^Lrf<48n+Lq$^SBdpxRS@ZX=_ z!}hOg@nh7a{=NFabvz(_vch_!&1-S>QY!Qu@5yoX^4qNNeAwJc1kvwQ+2IYZP~32v z8~r>>QCu6vlc5vm7666a=;ugyurn~o^j-Aylwj^!+Yhbr_XJ1h=6dt9i)~g+r}s+j za)8lxF57nTLm&bY0~Ql?{!PS=OrIGuesfoBH}61=_!d4*EvCPJ%GMYb+IX}tI1cUI zBG)S5ANqvA3LoF3CpG=(%-x6E`nR(GuGJ=2!RByhXQ}zASxxw_%`a=8XEzD6K8H~- zB#)m3E#}Xl-yUCD+Gs4od45hA6np-)l5%PWga{yziVD!uLqSD_9ViV0#k!+wAbU_` zlKeH)77QggMAdp1HJ^D{05aYmHp(OkiP%W#Bz)dL^af}4La%_gyKVbyt!~T$T9lz{ zhHaQEfC>6zALr64m|21-euN{hH}ENb$pf7H3q;5`EY=yUj~o0g-E*|Ns&ajL&Sp$X zpN1W4E+e4h&Up}J-9wlY!@iQ$4z|Vp-RzCET1ev}b~6Uct{l0oy{&;S(t`akK?jT4 z2@cQF226ef0ReHcV;bgbpS60>JRy1*FRRxS#`b{LA{@>!&Hw|%zwL(ci}E*1Sy@v3mUfhRISx_1z)$zeI27yZ zWJW}JCN8YcjL`i}r*xoDTai98aBN{iW>%D8>;T)Z@ZbVqB>+7{NYl!7EKGsAm>> z(h6tOU$VF_&hB`etUtteyX%br^yO`=rM=g;j6;i{4Gklx%IJ=>&9fG}B`lb@=iC&w zs(OZJ2AC}_Kdr=(`6QJ(7MQ0#8K&$&%9F)lFXZQXW9&V9lS2|2R9v+23<*EA(;b$X zhj`b=Ue=u9__rzuIj=8HdaU-PP5bhH?ro}Z81Xl35})aow;#@=8>)0{<&>Vd^vY>c zzq)#UnO~Z45#l;p+e#?gqU6?;ZC@vdHG9eTg8+P#VaNV2AbmuUIA+dvGQIOOIT>ub z*@+WPBOC`c3n>sTMlM*7(FP3~g^pGfRVkkJc)|Yj#oNpffx0ZDG~4yFitX>=zyv z-tz{qprhl51vaij?zzOD^FE!GQ@htr^XUU)GHd{%=(D*PKms2NCLQiIEiRD`VHrEi z%j{JwWr;`=WD8-Eq&5i@z=pcZAhwf6j*Zk0j$59W(~yA%69|A)p$(xG3KsZ;Fl^*t z85g2O>d*_>tC`CT%`}P1Mg!;qAkj$(e^;(zVKq+z$A(8WhlDge7P4br%WUD^zI-rc zz$kvf)cOXkre+vYV54Wagw?~J=Z$zVy&w>@*6?J_0f7$EmgBB^zo<^sT{4wZ&78Y$ zMBn{>dR6z}_GRsX3*@)jUsN!q^plCaW4%Et3D^*M-T8)0$MIjGAlug?>6BObN8yT< zG(PlAuBtxQyUFOr(0X~0)l|L|1w}HFfgx()jS`@x?R4m7&~oQ~cx&{RX_QK0y=| z5JENZmef(uhYexdFl3-1f&?g_PWZ$sDo{+E)46>_7Eqy22OeL5Aa~8}+@boH&*-xv zqd(rKl8&!tZtShR+~}+LXX$Pm%D}pm7~j5`>J$C)!1i-mozB_D;qT17@}dFLKlgHW zI_$1rx!!X%SsPc?kAEP#9hgomy!Q=#Kf4AzcK2^dvnoRN(@(BJuk`9O6Q+yI%ki8h z=fpm}vaQnhUk8t=v3N4Kpbj9W==57-Fz9H2?mp})lXZMKkwo}8)taAkiGjBSb9`xQ zG3LD|`L`4|dja+;iwfz`Eo4()aA~`u!{_GDPb8Wk19i=tuj;DgwJX`4REH9ZAj5)h8q4pTg>^t%h5< zx_FxIM&pOCS)D{LysKv~_*t(IOCVk}d(v)Op9`*wgxqS=dJ=b{?@DqDEk&VR-SEM|?)p5qgaqT&^IFz^h z49em;-CAiZ0^9s^o z+)ssluXlwj4JGiPY$_=4Ab0jwG5iRFpp*z6Aqc%fh7ylzy&CuNoJl&Xo<$*LCY67q z>;nZhE`^Z1zf%VX;~mLRuJk(kJdNk7n@jTovP+Bc(NEpN{HZrz66le& z*#HmKo!-2~YN1gHm)jgqI^|Ik`0!Cu-iML>henUAvJh#$R=8pk028wEhu_iA;&>v4 z!Y$~lL;S*2HUY1XImZAt5F}JdWqEVxIeZDy8F_Vr-(zq~G+JNEQEmdBoC4i4GT$hF zoLVHmv95l>?ao3zYt=B(#5J@Zmx(c-23Segz_TCCC`z~I;a%E%HK~KJ;heAOQKd9V z+kL+CK3rZpJR`~jvaaB$GH1`1|vWwF4%&ejLsM zEg0q^t~nmr>2wWyxXqf z3a~QU3ZhS_1=bXSAOQ@b(Qs*6-u(!2p>{HG1xag|AgW%nQ<%ac32Mt=mh*+j!EhAV zz5%PIA(N*8(n+ffZXO&DI*T2Jl|+AZvnS*dj8=k2{PQJrIiK_NV&hNY;EFamH(_8J-4J?OxwYw54WtEl79%_K z*|96r^d|KJqI`4O8A?qJwyhoaWT4C0oMDbGj?sx`W9F8X%MoDhS^WE1Z)7;{A_a-; zQ)Qu_*F_4$TYxEL?JGEGy6WXv!+SO-cWQTK2OSpONqW#^VIbr!!DlQvvqKs-Q&ooUUIc&Z^}An#%OrIJQo+osN=i=euTR{L?Ehs?Qjm#* z{bBv7V4+DeglBK$J`O{?Xr$nxP56WI9vh#|S+SsPPAN%n2_nYoSByvXoSEIh*os9- z6Z5FvQWFjJf5({>`n?~!TkIq}tNEs_@;L&TpRfk(sAE~PqiXAD#r-fwA3H~iFd7J= zzl&s~+UX~#KW05h# zTS&=!E<8GznJKzH4Gk`YGN1WejBjV@b9Cy{4><}gjhYClTV;?uAk#ZK zn(YwvFK-p*q=kaQo1O*=MhQS6#>-i`+7_aqE?Kc;X=pnA{T>;ru8nku-VjnvrJ_tP z3X2LA4n*(bGkJd4Q8+1VGY!%oQCc$t$+qIlTk1By8x7Auls>`T#X>z6#XYK7$Q3JYoZ zqL}O$YW*2l$Rf-4TK%)=2WnnL_DR@?cXcoe!=4LqYoaqy|Mz>?W?3^5(PWAPS!a)& z@1NVD@K!;_=^Gskn)2dXetJ=%h60sDc7PC+=(R9DQrCz#m zTAo^}?J<)1K|@%c{86b$ZrMs z3+GnQCbz>h(&LtPNyk)VwOxBE)^v9kNcQwzr5_ZMfPvvv_l!AprUS3WsEH9^$V4Ds}|Cf{`WHzlb8BT*n zVd|#}9})C>`>FZ!)ElXSCgL{5coPj=ilAYS{I^;Hq)O3Pth%fF61X;!Hkx2w9NBg9 z|0W@sK@_t;coH+FvCQfp+@8Brm0Bo36QWET8?Qi4%J&J*%Yzl-8 zVGyO@AE?0V_%dEp!SDN2a=7^%Z5$UlQ32UTgcb>(pnxJ==x5wZ5?)vrTUan44?tZPU>$mag5m6*$-!a_9z+c19J!IP*y08u4qaC z78MTlJ7j=3Q38=T5gIBPcs%S}dAZ6XZ*8@!QI*Ykm&yMA6I4ij-|wZDlVSSlEk_ii zf`QbRH#G*G&UL??g%7uz)t4pGcyUATvi{20rqWSOb$R_dbzun%$j#dSMmCEZp-5S& n$)1ykmz9;7IlVpR^#e@r$DTm*`o1_oqX$Wd$O=~r>IeQma<9)q literal 0 HcmV?d00001 diff --git a/docs/synchronise-chat-logs-cn.md b/docs/synchronise-chat-logs-cn.md new file mode 100644 index 000000000..52d79268f --- /dev/null +++ b/docs/synchronise-chat-logs-cn.md @@ -0,0 +1,31 @@ +# 同步聊天记录(upStash) +## 准备工作 +- GitHub账号 +- 拥有自己搭建过的ChatGPT-Next-Web的服务器 +- [UpStash](https://upstash.com) + +## 开始教程 +1. 注册UpStash账号 +2. 创建数据库 + + ![注册登录](./images/upstash-1.png) + + ![创建数据库](./images/upstash-2.png) + + ![选择服务器](./images/upstash-3.png) + +3. 找到REST API,分别复制UPSTASH_REDIS_REST_URL和UPSTASH_REDIS_REST_TOKEN(⚠切记⚠:不要泄露Token!) + + ![复制](./images/upstash-4.png) + +4. UPSTASH_REDIS_REST_URL和UPSTASH_REDIS_REST_TOKEN复制到你的同步配置,点击**检查可用性** + + ![同步1](./images/upstash-5.png) + + 如果没什么问题,那就成功了 + + ![同步可用性完成的样子](./images/upstash-6.png) + +5. Success! + + ![好耶~!](./images/upstash-7.png) diff --git a/docs/synchronise-chat-logs-en.md b/docs/synchronise-chat-logs-en.md new file mode 100644 index 000000000..04d056071 --- /dev/null +++ b/docs/synchronise-chat-logs-en.md @@ -0,0 +1,31 @@ +# Synchronize Chat Logs with UpStash +## Prerequisites +- GitHub account +- Your own ChatGPT-Next-Web server set up +- [UpStash](https://upstash.com) + +## Getting Started +1. Register for an UpStash account. +2. Create a database. + + ![Register and Login](./images/upstash-1.png) + + ![Create Database](./images/upstash-2.png) + + ![Select Server](./images/upstash-3.png) + +3. Find the REST API and copy UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN (⚠Important⚠: Do not share your token!) + + ![Copy](./images/upstash-4.png) + +4. Copy UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN into your synchronization configuration, then click **Check Availability**. + + ![Synchronize 1](./images/upstash-5.png) + + If everything is in order, you've successfully completed this step. + + ![Sync Availability Check Completed](./images/upstash-6.png) + +5. Success! + + ![Great job~!](./images/upstash-7.png) \ No newline at end of file diff --git a/docs/synchronise-chat-logs-es.md b/docs/synchronise-chat-logs-es.md new file mode 100644 index 000000000..40135f1f6 --- /dev/null +++ b/docs/synchronise-chat-logs-es.md @@ -0,0 +1,31 @@ +# Sincronizzare i Log delle Chat con UpStash +## Prerequisiti +- Account GitHub +- Server ChatGPT-Next-Web di propria configurazione +- [UpStash](https://upstash.com) + +## Per iniziare +1. Registrarsi per un account UpStash. +2. Creare un database. + + ![Registrarsi ed Accedere](./images/upstash-1.png) + + ![Creare un Database](./images/upstash-2.png) + + ![Selezionare il Server](./images/upstash-3.png) + +3. Trovare l'API REST e copiare UPSTASH_REDIS_REST_URL e UPSTASH_REDIS_REST_TOKEN (⚠Importante⚠: Non condividere il token!) + + ![Copia](./images/upstash-4.png) + +4. Copiare UPSTASH_REDIS_REST_URL e UPSTASH_REDIS_REST_TOKEN nella configurazione di sincronizzazione, quindi fare clic su **Verifica la Disponibilità**. + + ![Sincronizzazione 1](./images/upstash-5.png) + + Se tutto è in ordine, hai completato con successo questa fase. + + ![Verifica la Disponibilità della Sincronizzazione Completata](./images/upstash-6.png) + +5. Successo! + + ![Ottimo lavoro~!](./images/upstash-7.png) \ No newline at end of file diff --git a/docs/synchronise-chat-logs-ja.md b/docs/synchronise-chat-logs-ja.md new file mode 100644 index 000000000..ba75110fe --- /dev/null +++ b/docs/synchronise-chat-logs-ja.md @@ -0,0 +1,31 @@ +# UpStashを使用してチャットログを同期する +## 事前準備 +- GitHubアカウント +- 自分自身でChatGPT-Next-Webのサーバーをセットアップしていること +- [UpStash](https://upstash.com) + +## 始める +1. UpStashアカウントを登録します。 +2. データベースを作成します。 + + ![登録とログイン](./images/upstash-1.png) + + ![データベースの作成](./images/upstash-2.png) + + ![サーバーの選択](./images/upstash-3.png) + +3. REST APIを見つけ、UPSTASH_REDIS_REST_URLとUPSTASH_REDIS_REST_TOKENをコピーします(⚠重要⚠:トークンを共有しないでください!) + + ![コピー](./images/upstash-4.png) + +4. UPSTASH_REDIS_REST_URLとUPSTASH_REDIS_REST_TOKENを同期設定にコピーし、次に「可用性を確認」をクリックします。 + + ![同期1](./images/upstash-5.png) + + すべてが正常であれば、このステップは成功です。 + + ![同期可用性チェックが完了しました](./images/upstash-6.png) + +5. 成功! + + ![お疲れ様でした~!](./images/upstash-7.png) \ No newline at end of file diff --git a/docs/synchronise-chat-logs-ko.md b/docs/synchronise-chat-logs-ko.md new file mode 100644 index 000000000..88e6e2dda --- /dev/null +++ b/docs/synchronise-chat-logs-ko.md @@ -0,0 +1,31 @@ +# UpStash를 사용하여 채팅 기록 동기화 +## 사전 준비물 +- GitHub 계정 +- 자체 ChatGPT-Next-Web 서버 설정 +- [UpStash](https://upstash.com) + +## 시작하기 +1. UpStash 계정 등록 +2. 데이터베이스 생성 + + ![등록 및 로그인](./images/upstash-1.png) + + ![데이터베이스 생성](./images/upstash-2.png) + + ![서버 선택](./images/upstash-3.png) + +3. REST API를 찾아 UPSTASH_REDIS_REST_URL 및 UPSTASH_REDIS_REST_TOKEN을 복사합니다 (⚠주의⚠: 토큰을 공유하지 마십시오!) + + ![복사](./images/upstash-4.png) + +4. UPSTASH_REDIS_REST_URL 및 UPSTASH_REDIS_REST_TOKEN을 동기화 구성에 복사한 다음 **가용성 확인**을 클릭합니다. + + ![동기화 1](./images/upstash-5.png) + + 모든 것이 정상인 경우,이 단계를 성공적으로 완료했습니다. + + ![동기화 가용성 확인 완료](./images/upstash-6.png) + +5. 성공! + + ![잘 했어요~!](./images/upstash-7.png) \ No newline at end of file From 986d34fb3e3ba4a871f1f94609a3d1cae6b6b91b Mon Sep 17 00:00:00 2001 From: Surav Shrestha Date: Thu, 12 Oct 2023 21:22:35 +0545 Subject: [PATCH 104/108] docs: fix typo in app/masks/en.ts --- app/masks/en.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/masks/en.ts b/app/masks/en.ts index 1ab40d59b..ed130351f 100644 --- a/app/masks/en.ts +++ b/app/masks/en.ts @@ -35,7 +35,7 @@ export const EN_MASKS: BuiltinMask[] = [ id: "prompt-improve-0", role: "user", content: - 'Read all of the instructions below and once you understand them say "Shall we begin:"\n \nI want you to become my Prompt Creator. Your goal is to help me craft the best possible prompt for my needs. The prompt will be used by you, ChatGPT. You will follow the following process:\nYour first response will be to ask me what the prompt should be about. I will provide my answer, but we will need to improve it through continual iterations by going through the next steps.\n \nBased on my input, you will generate 3 sections.\n \nRevised Prompt (provide your rewritten prompt. it should be clear, concise, and easily understood by you)\nSuggestions (provide 3 suggestions on what details to include in the prompt to improve it)\nQuestions (ask the 3 most relevant questions pertaining to what additional information is needed from me to improve the prompt)\n \nAt the end of these sections give me a reminder of my options which are:\n \nOption 1: Read the output and provide more info or answer one or more of the questions\nOption 2: Type "Use this prompt" and I will submit this as a query for you\nOption 3: Type "Restart" to restart this process from the beginning\nOption 4: Type "Quit" to end this script and go back to a regular ChatGPT session\n \nIf I type "Option 2", "2" or "Use this prompt" then we have finsihed and you should use the Revised Prompt as a prompt to generate my request\nIf I type "option 3", "3" or "Restart" then forget the latest Revised Prompt and restart this process\nIf I type "Option 4", "4" or "Quit" then finish this process and revert back to your general mode of operation\n\n\nWe will continue this iterative process with me providing additional information to you and you updating the prompt in the Revised Prompt section until it is complete.', + 'Read all of the instructions below and once you understand them say "Shall we begin:"\n \nI want you to become my Prompt Creator. Your goal is to help me craft the best possible prompt for my needs. The prompt will be used by you, ChatGPT. You will follow the following process:\nYour first response will be to ask me what the prompt should be about. I will provide my answer, but we will need to improve it through continual iterations by going through the next steps.\n \nBased on my input, you will generate 3 sections.\n \nRevised Prompt (provide your rewritten prompt. it should be clear, concise, and easily understood by you)\nSuggestions (provide 3 suggestions on what details to include in the prompt to improve it)\nQuestions (ask the 3 most relevant questions pertaining to what additional information is needed from me to improve the prompt)\n \nAt the end of these sections give me a reminder of my options which are:\n \nOption 1: Read the output and provide more info or answer one or more of the questions\nOption 2: Type "Use this prompt" and I will submit this as a query for you\nOption 3: Type "Restart" to restart this process from the beginning\nOption 4: Type "Quit" to end this script and go back to a regular ChatGPT session\n \nIf I type "Option 2", "2" or "Use this prompt" then we have finished and you should use the Revised Prompt as a prompt to generate my request\nIf I type "option 3", "3" or "Restart" then forget the latest Revised Prompt and restart this process\nIf I type "Option 4", "4" or "Quit" then finish this process and revert back to your general mode of operation\n\n\nWe will continue this iterative process with me providing additional information to you and you updating the prompt in the Revised Prompt section until it is complete.', date: "", }, { From 0dcbfd746e00b722d5b378f760684428f022e5ff Mon Sep 17 00:00:00 2001 From: Jesse <43443357+jessegpt@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:41:00 +0400 Subject: [PATCH 105/108] fix serviceWorker cache --- public/serviceWorker.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/public/serviceWorker.js b/public/serviceWorker.js index f5a24b701..2e1d60b98 100644 --- a/public/serviceWorker.js +++ b/public/serviceWorker.js @@ -1,15 +1,21 @@ const CHATGPT_NEXT_WEB_CACHE = "chatgpt-next-web-cache"; +importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.0.0/workbox-sw.js'); + self.addEventListener("activate", function (event) { console.log("ServiceWorker activated."); }); -self.addEventListener("install", function (event) { - event.waitUntil( - caches.open(CHATGPT_NEXT_WEB_CACHE).then(function (cache) { - return cache.addAll([]); - }), - ); +workbox.core.clientsClaim(); +self.addEventListener("message", (event) => { + if (event.data && event.data.type === "SKIP_WAITING") { + self.skipWaiting(); + } }); -self.addEventListener("fetch", (e) => {}); +workbox.routing.registerRoute( + new RegExp('/*'), + new workbox.strategies.StaleWhileRevalidate({ + cacheName: CHATGPT_NEXT_WEB_CACHE + }) +); From f5a2ce52aa1cdbf0c6fe8f9b1e053e0cd75651c2 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Sat, 14 Oct 2023 15:22:41 +0900 Subject: [PATCH 106/108] Fix typo in README.md passsword -> password --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3fe76f4f0..fbb87a5ce 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ Your openai api key. ### `CODE` (optional) -Access passsword, separated by comma. +Access password, separated by comma. ### `BASE_URL` (optional) From 55bcf78efe0ab600abf67df0bd47dd62c8432b6c Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Sun, 15 Oct 2023 20:08:08 +0800 Subject: [PATCH 107/108] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3fe76f4f0..76d928b63 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,7 @@ If you want to add a new translation, read this [document](./docs/translation.md [@AnsonHyq](https://github.com/AnsonHyq) [@synwith](https://github.com/synwith) [@piksonGit](https://github.com/piksonGit) +[@ouyangzhiping](https://github.com/ouyangzhiping) ### Contributor From 65c4a0c319c2264dcd5236d944fe7f541ef16441 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 16 Oct 2023 11:52:45 +0800 Subject: [PATCH 108/108] feat: close #3031 user can set larger font size --- app/components/settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 8ed6b7738..795469a96 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -753,7 +753,7 @@ export function Settings() { title={`${config.fontSize ?? 14}px`} value={config.fontSize} min="12" - max="18" + max="40" step="1" onChange={(e) => updateConfig(