From 99565935956630d7d8d0062f6d4fcb48e51ba82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A6=96=E9=83=BD=E7=88=B1=E6=8A=A4=E5=8A=A8=E7=89=A9?= =?UTF-8?q?=E5=8D=8F=E4=BC=9A?= <87239270+1355873789@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:22:24 +0800 Subject: [PATCH 001/146] add pixtral avatar --- .../src/assets/images/models/pixtral.png | Bin 0 -> 3765 bytes .../src/assets/images/models/pixtral_dark.png | Bin 0 -> 915 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/renderer/src/assets/images/models/pixtral.png create mode 100644 src/renderer/src/assets/images/models/pixtral_dark.png diff --git a/src/renderer/src/assets/images/models/pixtral.png b/src/renderer/src/assets/images/models/pixtral.png new file mode 100644 index 0000000000000000000000000000000000000000..6c3ae9b162639d656db8ff1636efa2fb448fabe3 GIT binary patch literal 3765 zcmcInXH*mDwoO6_HH0F)gCkf_Kp}uYP(Y9p9O*^6R6}nGh9C~0!2!{s7ZC&`gQ1Lo z2%#4R0s$gL2u&$U2?k7P;d$SCYt5T?+pqiMd}p1t&X2Rs+Iyew*VqXq6`24h@ZK!k!@_j#;jG^$qA?4 z?PGM+X6vlxW^U}K?xM)`3>8saPB*qYxn|J<3gi`e%wgsvU+XE%FVrdJqw1nq3sUG% zlvfZ`rk_+0feO6`39E7&^=6oLaFe>bW!H0y%wy)NbVEcdkPORD26w-ZY-6H^=}&s; z3q&`H-R}5A+}CpvG1rW#_OJ~JXs!l_vGP@*;5!?*v4#lrG2efzOhXNZ5L;V^NF)*# z!Jfw9zK2U@7>sp3BHy+m;c7SxcdE(6qe|c#%+Y@PkA{=AciLT6O>@ubAet5Q<`+!$ zd#0zSCFvh=@JpLoXxfHli-XFs1whEH1y9^&Zw2>6 zf?WPbpAWNpT&WfAlo#pHsu<5QrH2LQHVOsiWjqXWu?2YCPR$_%(#>7ibzK7NAL32& z=>)Evl^1q?RhP9^-4SsD`WS5WPj{Xckd?=t@^A;w)bt)oD-f^m+P$TF8}ns{)81&F9@{RmlIOI!b_Ei-K*Ts3A*bAd>}3}5Hemf zwMYQB_S^)A`~);}a@qBl-- zZmQmLJo+i%Sk@jSB3~Ka2TZY&tj5GN+ye!wrlr=kYvRFdL$fOjw^)8^!yA=yK zgiwi>Ae>0%e1jq0uyRConPr&8=FrDQC41&HUYK*;IUr**j+O_TT zqx%Wd)rO@603-Q5Aj{jDHcwu1oN;7O(+O(EoKeCud%>vix2|sbmmLH0_1+Zrm?J`h zpYW#kKmA`E{0R$zU!D;0k(f;{E2Ok>xO58#{@1(O2Z7Scl%qn+UlL`~JpV{SP7tV; z+9o{xP7CJ>DKAA=Hr^D_sI;kz-#71N}IYKA+4ZTqLo-^fAZTgNZSD`BsfLfrQ) zs>E2F=(@vtP4HW;ZE^-Ad#pbN6mBnWqrcA{%$6>9{Q7aERl7;eY$17N*t{a@1oVqe zON#dj9f@CZ_V24E$y{%Y8-~g?JM5w{dg{Vcdxp@z=iHyWR+Jc%HrGa6#b2F9Oz%lP zL!rYQ?U3LPb-i3mDEph-wN_&?KkKiTc`_e*o(cc-y!Qb|*;Zc=@bv2Sl~<+iH7^&^ z_WJoXzkR%rXuCMNxuyOwvBr)y)o?Em zLR9aYh7g+To{e`M00h))u>j-6W6H=7O)oKX@#fqtVpjZPY%B>@k{JMy&N^bVoRxF! zVlCmu9n?g2GwLSEn-~9(JM!(UFyy|(j+pZv^$-l6?u+(C@$0 zwy)5%Uy_t?UNDiib$pe%Eaxe^yy#v!< z0A)vmIg*y$w#qSc1EEn#&*`aZUi9SmbL{f5xT)>Fqm z)dw8`jwa-~T8csJ;1hh;0}B2Dgm3YS|HOwQ)w**XDeDS-njP4J1muy`{Y5eMxViB- z)U{sldxu8Fim_g!rT>xY{|g(Qer`z&o2_*|l1>%anp?Aamt5QnS1dEC84s$b*%Nlh zoc-d>HdB6xY|J#Kln?;JTp&$uv|&mMcZbMcPA~54JU!b;EK~Zfw4|Z+L&V9C#7A`k zyVfW7&nwxPtb>NC^CzQdfsOvo)GG&^-JdsXyVc2opXp{mr<;5=hxIEjZ?QC7iI%B* z$U3A|0*b$YUh5)`1_|DX7**ZB%907k?eF_8SxlQ7j`Pv>HaL6gi z$Q_tb^Qhj5F_rEoORE21Cx0F{%LwIJuZKvL!4Nlg}a#^aT77<3iaj_+j{Nf%Yt;%02*xLep-)3_8 zTanH$(R;`1ZJdMIwz=`nc_#64hmc9uqr{tfEB5Fr-Fe`mseE_m!_PfUdif7Hlo#QQ z-*`kL@;W!?8(hc~)7JG--1^W%%!PV5AUa_sW}9#<)^KC=gygDXq2BI!mgKe=h8M4d zyyieJ_qI@uP8>PQjnKd9={=NkwXOf?QpJI!ZHU_HzA3t|+40e|YnU@o9gy->RpRpz zU~KvJP)&I@dGsgLn!qtT6%{pd!$-iMpx@VF%NriGn6Vs|DRUpu)1mRk^S)ap zOZzodP2*Y8p>Fw!42oK8mjY@}HkzBg>)^V>nHhbDG75C=Sa|_QSloT_yijyVwnlk# zW>reL-0$!kpHjKXTTi2fCKo__QPzVYMf>TfaF&f*QmDh%cXlD)gz#CvZPVh5ml(r% zNF!Ka^~5O+{e5RftM|~e-?{Y5I9>(Mc)y0tw77>X9Iz0irc+hI2K=&Z$}Kbl^Y8R< zq7_VDu>$c|>$p?`>Gvee>~6lz+ngvuk4b^wAX2Jz(&yIou1Gey&}Wlt&p+bcyqoSe z%alRdeUgB%MDDc9Z}*d|;DaS!g#`ywnd)2eF_NPS7g6F&$YkXD>Xj5W{*v=JvY@ZN zDX(JAY0HVLH}YVP`BOklnbNF z+>U?X+ME$UOZQn`uaLeR@THo^Yx3cKh(=p{?&{Ej-dMU1JMN-ZACYz17WZw95^-C! ziTQ42{$0$^Jtt!?y<^lDa{w#w=Y@!egmKC}@zWswn=r-vgV-%mV&s~To}ONpf$b3U zOYoD56jxD4GlGyKwRxpD{h-4h9PSOl@vfntS_^m_S1$&vmhDXsTuAUmf9JWG2 zX}}WF6sTP`SRK_^t5M47gGQs_X3`8EeBhTVkfx-5rgC4LPtf?I*l{nD$Mx0||B{@4 m=Q#e6Fa3X0{EKpqt5nuP3GX145~ZoFRl z#8~ssox)kmDjEL0PJGLIz*LxnrO`ovQsT$fCy(C0e}6b{d-Ny&g6Dn5AAk7qW5qJJD;Hc#Qv zgXOte0zWdD8WjXMuoD}0X5RC*D*bgO;OW~BEV$Ky4M>>voo89m_VdClO%4hic!O;UnGxCuDa# zdGYgM!3`go!w*DX>)F`buix7`TXllEoyvIwp(?2`_cCsUE4+N?d;a6O)8Yx@8|UAx66^rdHdI8fB*2opy%-JIuOKCX0t&lA{E&VI%Wi$?!#yT83+00K`}KbLh*2~7Y#G(ph- literal 0 HcmV?d00001 From 92a491be15e9b0a566154dd0622ca30df3b31054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A6=96=E9=83=BD=E7=88=B1=E6=8A=A4=E5=8A=A8=E7=89=A9?= =?UTF-8?q?=E5=8D=8F=E4=BC=9A?= <87239270+1355873789@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:26:04 +0800 Subject: [PATCH 002/146] Match the avatar for the Pixtral model. --- src/renderer/src/config/models.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/renderer/src/config/models.ts b/src/renderer/src/config/models.ts index 72ac370f94..4e9ca65dcb 100644 --- a/src/renderer/src/config/models.ts +++ b/src/renderer/src/config/models.ts @@ -97,6 +97,8 @@ import NvidiaModelLogo from '@renderer/assets/images/models/nvidia.png' import NvidiaModelLogoDark from '@renderer/assets/images/models/nvidia_dark.png' import PalmModelLogo from '@renderer/assets/images/models/palm.png' import PalmModelLogoDark from '@renderer/assets/images/models/palm_dark.png' +import PixtralModelLogo from '@renderer/assets/images/models/pixtral.png' +import PixtralModelLogoDark from '@renderer/assets/images/models/pixtral_dark.png' import QwenModelLogo from '@renderer/assets/images/models/qwen.png' import QwenModelLogoDark from '@renderer/assets/images/models/qwen_dark.png' import RakutenaiModelLogo from '@renderer/assets/images/models/rakutenai.png' @@ -156,6 +158,7 @@ export function getModelLogo(modelId: string) { } const logoMap = { + pixtral: isLight ? PixtralModelLogo : PixtralModelLogoDark, jina: isLight ? JinaModelLogo : JinaModelLogoDark, abab: isLight ? MinimaxModelLogo : MinimaxModelLogoDark, 'o1-': isLight ? ChatGPTo1ModelLogo : ChatGPTo1ModelLogoDark, From 1d98b897af8557d7566c740b15593eb90f93def8 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Wed, 6 Nov 2024 13:17:43 +0800 Subject: [PATCH 003/146] chore: remove unused llm store model and migration config --- src/renderer/src/store/llm.ts | 18 +++++++++--------- src/renderer/src/store/migrate.ts | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/renderer/src/store/llm.ts b/src/renderer/src/store/llm.ts index 6696600192..80da09711c 100644 --- a/src/renderer/src/store/llm.ts +++ b/src/renderer/src/store/llm.ts @@ -276,15 +276,15 @@ const initialState: LlmState = { isSystem: true, enabled: false }, - { - id: 'jina', - name: 'Jina', - apiKey: '', - apiHost: 'https://api.jina.ai', - models: SYSTEM_MODELS.jina, - isSystem: true, - enabled: false - }, + // { + // id: 'jina', + // name: 'Jina', + // apiKey: '', + // apiHost: 'https://api.jina.ai', + // models: SYSTEM_MODELS.jina, + // isSystem: true, + // enabled: false + // }, { id: 'aihubmix', name: 'AiHubMix', diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index 784ad5b7ec..73e19e8d7f 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -656,16 +656,16 @@ const migrateConfig = { models: SYSTEM_MODELS.mistral, isSystem: true, enabled: false - }, - { - id: 'jina', - name: 'Jina', - apiKey: '', - apiHost: 'https://api.jina.ai', - models: SYSTEM_MODELS.jina, - isSystem: true, - enabled: false } + // { + // id: 'jina', + // name: 'Jina', + // apiKey: '', + // apiHost: 'https://api.jina.ai', + // models: SYSTEM_MODELS.jina, + // isSystem: true, + // enabled: false + // } ] } } From 8ee12e74071ddfdd7aec9752c0c4cdddce0c34d6 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Wed, 6 Nov 2024 15:56:48 +0800 Subject: [PATCH 004/146] refactor: refactored capturescrollablediv function and updated chat and messages components --- src/renderer/src/pages/home/Chat.tsx | 4 +++ .../src/pages/home/Messages/Messages.tsx | 1 + src/renderer/src/utils/index.ts | 31 +++++++++++++++---- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/renderer/src/pages/home/Chat.tsx b/src/renderer/src/pages/home/Chat.tsx index e5e98df41c..6f68e6d4e3 100644 --- a/src/renderer/src/pages/home/Chat.tsx +++ b/src/renderer/src/pages/home/Chat.tsx @@ -52,8 +52,12 @@ const Container = styled.div` height: 100%; flex: 1; justify-content: space-between; + background-color: var(--color-background); &.bubble { background-color: var(--chat-background); + #messages { + background-color: var(--chat-background); + } .system-prompt { background-color: var(--chat-background-assistant); } diff --git a/src/renderer/src/pages/home/Messages/Messages.tsx b/src/renderer/src/pages/home/Messages/Messages.tsx index ad8c764d66..fefdbe065c 100644 --- a/src/renderer/src/pages/home/Messages/Messages.tsx +++ b/src/renderer/src/pages/home/Messages/Messages.tsx @@ -219,6 +219,7 @@ const Container = styled(Scrollbar)` padding: 10px 0; padding-bottom: 20px; overflow-x: hidden; + background-color: var(--color-background); ` export default Messages diff --git a/src/renderer/src/utils/index.ts b/src/renderer/src/utils/index.ts index 9916ab26c1..b019b771e9 100644 --- a/src/renderer/src/utils/index.ts +++ b/src/renderer/src/utils/index.ts @@ -264,7 +264,7 @@ export const captureScrollableDiv = async (divRef: React.RefObject { + // Ensure all images in cloned document are loaded + const images = clonedDoc.getElementsByTagName('img') + return Promise.all( + Array.from(images).map((img) => { + if (img.complete) { + return Promise.resolve() + } + return new Promise((resolve) => { + img.onload = resolve + img.onerror = resolve + }) + }) + ) + } }) - // 恢复原始样式 + // Restore original styles div.style.height = originalStyle.height div.style.maxHeight = originalStyle.maxHeight div.style.overflow = originalStyle.overflow @@ -294,7 +313,7 @@ export const captureScrollableDiv = async (divRef: React.RefObject { div.scrollTop = originalScrollTop }, 0) From ddc39d1fd469c3d04e755411d388dfea94630f39 Mon Sep 17 00:00:00 2001 From: injurka Date: Wed, 6 Nov 2024 16:19:36 +0400 Subject: [PATCH 005/146] added locale for context-menu --- src/main/ipc.ts | 5 +++++ src/main/services/ConfigManager.ts | 18 +++++++++++++++--- src/main/utils/locales.ts | 11 +++++++++++ src/main/window.ts | 10 +++++++--- src/preload/index.d.ts | 2 ++ src/preload/index.ts | 1 + src/renderer/src/i18n/locales/en-us.json | 2 ++ src/renderer/src/i18n/locales/zh-cn.json | 2 ++ src/renderer/src/i18n/locales/zh-tw.json | 2 ++ .../src/pages/settings/GeneralSettings.tsx | 5 +++-- src/renderer/src/store/settings.ts | 8 ++++---- src/renderer/src/types/index.ts | 1 + 12 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 src/main/utils/locales.ts diff --git a/src/main/ipc.ts b/src/main/ipc.ts index e3592eff7a..a290e3391b 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -29,6 +29,11 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { ipcMain.handle('app:reload', () => mainWindow.reload()) ipcMain.handle('open:website', (_, url: string) => shell.openExternal(url)) + // language + ipcMain.handle('app:set-language', (_, language) => { + configManager.setLanguage(language) + }) + // theme ipcMain.handle('app:set-theme', (_, theme: 'light' | 'dark') => { configManager.setTheme(theme) diff --git a/src/main/services/ConfigManager.ts b/src/main/services/ConfigManager.ts index a6bb220114..6e2406ebc8 100644 --- a/src/main/services/ConfigManager.ts +++ b/src/main/services/ConfigManager.ts @@ -1,5 +1,9 @@ +import { app } from 'electron' import Store from 'electron-store' +type ThemeVarious = 'light' | 'dark' +type LanguageVarious = 'zh-CN' | 'zh-TW' | 'en-US' + export class ConfigManager { private store: Store @@ -7,11 +11,19 @@ export class ConfigManager { this.store = new Store() } - getTheme(): 'light' | 'dark' { - return this.store.get('theme', 'light') as 'light' | 'dark' + getLanguage(): LanguageVarious { + return this.store.get('language', app.getLocale()) as LanguageVarious } - setTheme(theme: 'light' | 'dark') { + setLanguage(theme: ThemeVarious) { + this.store.set('language', theme) + } + + getTheme(): ThemeVarious { + return this.store.get('theme', 'light') as ThemeVarious + } + + setTheme(theme: ThemeVarious) { this.store.set('theme', theme) } } diff --git a/src/main/utils/locales.ts b/src/main/utils/locales.ts new file mode 100644 index 0000000000..d927f25211 --- /dev/null +++ b/src/main/utils/locales.ts @@ -0,0 +1,11 @@ +import EnUs from '../../renderer/src/i18n/locales/en-us.json' +import ZhCn from '../../renderer/src/i18n/locales/zh-cn.json' +import ZhTw from '../../renderer/src/i18n/locales/zh-tw.json' + +const locales = { + 'en-US': EnUs, + 'zh-CN': ZhCn, + 'zh-TW': ZhTw +} + +export { locales } diff --git a/src/main/window.ts b/src/main/window.ts index 3014dcf47f..eb6783964c 100644 --- a/src/main/window.ts +++ b/src/main/window.ts @@ -6,6 +6,7 @@ import { join } from 'path' import icon from '../../build/icon.png?asset' import { titleBarOverlayDark, titleBarOverlayLight } from './config' import { configManager } from './services/ConfigManager' +import { locales } from './utils/locales' export function createMainWindow() { // Load the previous state with fallback to defaults @@ -48,10 +49,13 @@ export function createMainWindow() { mainWindowState.manage(mainWindow) mainWindow.webContents.on('context-menu', () => { + const locale = locales[configManager.getLanguage()] + const { common } = locale.translation + const menu = new Menu() - menu.append(new MenuItem({ label: '复制', role: 'copy' })) - menu.append(new MenuItem({ label: '粘贴', role: 'paste' })) - menu.append(new MenuItem({ label: '剪切', role: 'cut' })) + menu.append(new MenuItem({ label: common.copy, role: 'copy' })) + menu.append(new MenuItem({ label: common.paste, role: 'paste' })) + menu.append(new MenuItem({ label: common.cut, role: 'cut' })) menu.popup() }) diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 245afac700..4a3834dfb0 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -1,6 +1,7 @@ import { ElectronAPI } from '@electron-toolkit/preload' import { FileType } from '@renderer/types' import { WebDavConfig } from '@renderer/types' +import { LanguageVarious } from '@renderer/types' import type { OpenDialogOptions } from 'electron' import { Readable } from 'stream' @@ -17,6 +18,7 @@ declare global { checkForUpdate: () => void openWebsite: (url: string) => void setProxy: (proxy: string | undefined) => void + setLanguage: (theme: LanguageVarious) => void setTheme: (theme: 'light' | 'dark') => void minApp: (options: { url: string; windowOptions?: Electron.BrowserWindowConstructorOptions }) => void reload: () => void diff --git a/src/preload/index.ts b/src/preload/index.ts index 76e4fc2eba..561e0b9366 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -8,6 +8,7 @@ const api = { reload: () => ipcRenderer.invoke('app:reload'), setProxy: (proxy: string) => ipcRenderer.invoke('app:proxy', proxy), checkForUpdate: () => ipcRenderer.invoke('app:check-for-update'), + setLanguage: (lang: string) => ipcRenderer.invoke('app:set-language', lang), setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke('app:set-theme', theme), openWebsite: (url: string) => ipcRenderer.invoke('open:website', url), minApp: (url: string) => ipcRenderer.invoke('minapp', url), diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index da3f06587f..cb143b71bf 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -17,6 +17,8 @@ "edit": "Edit", "duplicate": "Duplicate", "copy": "Copy", + "paste": "Paste", + "cut": "Cut", "regenerate": "Regenerate", "provider": "Provider", "you": "You", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 8452818cff..f9a83cb8de 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -17,6 +17,8 @@ "edit": "编辑", "duplicate": "复制", "copy": "复制", + "paste": "粘贴", + "cut": "剪切", "regenerate": "重新生成", "provider": "提供商", "you": "用户", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index c6ab1fd7f6..7027a4ca2f 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -17,6 +17,8 @@ "edit": "編輯", "duplicate": "複製", "copy": "複製", + "paste": "貼上", + "cut": "剪下", "regenerate": "重新生成", "provider": "提供商", "you": "您", diff --git a/src/renderer/src/pages/settings/GeneralSettings.tsx b/src/renderer/src/pages/settings/GeneralSettings.tsx index 1fd610d32e..0e9234cf37 100644 --- a/src/renderer/src/pages/settings/GeneralSettings.tsx +++ b/src/renderer/src/pages/settings/GeneralSettings.tsx @@ -4,7 +4,7 @@ import i18n from '@renderer/i18n' import { useAppDispatch } from '@renderer/store' import { setClickAssistantToShowTopic, setLanguage, setShowTopicTime } from '@renderer/store/settings' import { setProxyUrl as _setProxyUrl } from '@renderer/store/settings' -import { ThemeMode } from '@renderer/types' +import { LanguageVarious, ThemeMode } from '@renderer/types' import { isValidProxyUrl } from '@renderer/utils' import { Input, Select, Switch } from 'antd' import { FC, useState } from 'react' @@ -30,9 +30,10 @@ const GeneralSettings: FC = () => { const dispatch = useAppDispatch() const { t } = useTranslation() - const onSelectLanguage = (value: string) => { + const onSelectLanguage = (value: LanguageVarious) => { dispatch(setLanguage(value)) localStorage.setItem('language', value) + window.api.setLanguage(value) i18n.changeLanguage(value) } diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index cf754b96e8..3091404a09 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -1,5 +1,5 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' -import { ThemeMode } from '@renderer/types' +import { LanguageVarious, ThemeMode } from '@renderer/types' export type SendMessageShortcut = 'Enter' | 'Shift+Enter' @@ -7,7 +7,7 @@ export interface SettingsState { showAssistants: boolean showTopics: boolean sendMessageShortcut: SendMessageShortcut - language: string + language: LanguageVarious proxyUrl?: string userName: string showMessageDivider: boolean @@ -36,7 +36,7 @@ const initialState: SettingsState = { showAssistants: true, showTopics: true, sendMessageShortcut: 'Enter', - language: navigator.language, + language: navigator.language as LanguageVarious, proxyUrl: undefined, userName: '', showMessageDivider: false, @@ -79,7 +79,7 @@ const settingsSlice = createSlice({ setSendMessageShortcut: (state, action: PayloadAction) => { state.sendMessageShortcut = action.payload }, - setLanguage: (state, action: PayloadAction) => { + setLanguage: (state, action: PayloadAction) => { state.language = action.payload }, setProxyUrl: (state, action: PayloadAction) => { diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index 3cf010594d..22bc6cf8e5 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -136,6 +136,7 @@ export enum ThemeMode { dark = 'dark', auto = 'auto' } +export type LanguageVarious = 'zh-CN' | 'zh-TW' | 'en-US' export type WebDavConfig = { webdavHost: string From 58e2e410cbdbb15deaa29a5fbe1ef633aaf2d61d Mon Sep 17 00:00:00 2001 From: injurka Date: Wed, 6 Nov 2024 17:09:28 +0400 Subject: [PATCH 006/146] fix type import --- src/main/services/ConfigManager.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/services/ConfigManager.ts b/src/main/services/ConfigManager.ts index 6e2406ebc8..2c638da333 100644 --- a/src/main/services/ConfigManager.ts +++ b/src/main/services/ConfigManager.ts @@ -1,9 +1,7 @@ +import { LanguageVarious, ThemeMode } from '@types' import { app } from 'electron' import Store from 'electron-store' -type ThemeVarious = 'light' | 'dark' -type LanguageVarious = 'zh-CN' | 'zh-TW' | 'en-US' - export class ConfigManager { private store: Store @@ -15,15 +13,15 @@ export class ConfigManager { return this.store.get('language', app.getLocale()) as LanguageVarious } - setLanguage(theme: ThemeVarious) { + setLanguage(theme: LanguageVarious) { this.store.set('language', theme) } - getTheme(): ThemeVarious { - return this.store.get('theme', 'light') as ThemeVarious + getTheme(): ThemeMode { + return this.store.get('theme', ThemeMode.light) as ThemeMode } - setTheme(theme: ThemeVarious) { + setTheme(theme: ThemeMode) { this.store.set('theme', theme) } } From 02a8c9feb85a9cbee1ecca8b64fc437d6b18a74c Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Thu, 7 Nov 2024 17:31:58 +0800 Subject: [PATCH 007/146] =?UTF-8?q?fix:=20github=20models=20=E4=B8=8D?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=9B=BE=E7=89=87=20#291?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../openai-npm-4.71.1-b5940d6401.patch | 26 ++++++ package.json | 2 +- src/renderer/src/providers/BaseProvider.ts | 2 +- src/renderer/src/providers/OpenAIProvider.ts | 93 +++++++------------ yarn.lock | 50 +++++----- 5 files changed, 90 insertions(+), 83 deletions(-) create mode 100644 .yarn/patches/openai-npm-4.71.1-b5940d6401.patch diff --git a/.yarn/patches/openai-npm-4.71.1-b5940d6401.patch b/.yarn/patches/openai-npm-4.71.1-b5940d6401.patch new file mode 100644 index 0000000000..f408484457 --- /dev/null +++ b/.yarn/patches/openai-npm-4.71.1-b5940d6401.patch @@ -0,0 +1,26 @@ +diff --git a/core.js b/core.js +index 00b67a48b7b5cf0029413fc84abd0c01630c3d14..5550b58495b468060f775ca86e4d849d82573ea5 100644 +--- a/core.js ++++ b/core.js +@@ -156,7 +156,7 @@ class APIClient { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'User-Agent': this.getUserAgent(), +- ...getPlatformHeaders(), ++ // ...getPlatformHeaders(), + ...this.authHeaders(opts), + }; + } +diff --git a/core.mjs b/core.mjs +index 8bc7a0ee10d61560d7113cf3f703355bb19f7ddd..5e4c8586ea6b13fe887a22af2de05eaa4700b5ec 100644 +--- a/core.mjs ++++ b/core.mjs +@@ -149,7 +149,7 @@ export class APIClient { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'User-Agent': this.getUserAgent(), +- ...getPlatformHeaders(), ++ // ...getPlatformHeaders(), + ...this.authHeaders(opts), + }; + } diff --git a/package.json b/package.json index f13dfdf9dd..fd159fb67d 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "localforage": "^1.10.0", "lodash": "^4.17.21", "mime": "^4.0.4", - "openai": "^4.52.1", + "openai": "patch:openai@npm%3A4.71.1#~/.yarn/patches/openai-npm-4.71.1-b5940d6401.patch", "prettier": "^3.2.4", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/renderer/src/providers/BaseProvider.ts b/src/renderer/src/providers/BaseProvider.ts index 94313b2682..983bc2beba 100644 --- a/src/renderer/src/providers/BaseProvider.ts +++ b/src/renderer/src/providers/BaseProvider.ts @@ -17,7 +17,7 @@ export default abstract class BaseProvider { return host.endsWith('/') ? host : `${host}/v1/` } - public getHeaders() { + public defaultHeaders() { return { 'X-Api-Key': this.provider.apiKey } diff --git a/src/renderer/src/providers/OpenAIProvider.ts b/src/renderer/src/providers/OpenAIProvider.ts index b0435197b6..8d1cde8380 100644 --- a/src/renderer/src/providers/OpenAIProvider.ts +++ b/src/renderer/src/providers/OpenAIProvider.ts @@ -33,7 +33,8 @@ export default class OpenAIProvider extends BaseProvider { this.sdk = new OpenAI({ dangerouslyAllowBrowser: true, apiKey: provider.apiKey, - baseURL: this.getBaseURL() + baseURL: this.getBaseURL(), + defaultHeaders: this.defaultHeaders() }) } @@ -138,21 +139,16 @@ export default class OpenAIProvider extends BaseProvider { const isSupportStreamOutput = streamOutput && this.isSupportStreamOutput(model.id) // @ts-ignore key is not typed - const stream = await this.sdk.chat.completions.create( - { - model: model.id, - messages: [isOpenAIo1 ? undefined : systemMessage, ...userMessages].filter( - Boolean - ) as ChatCompletionMessageParam[], - temperature: isOpenAIo1 ? 1 : assistant?.settings?.temperature, - max_tokens: maxTokens, - keep_alive: this.keepAliveTime, - stream: isSupportStreamOutput - }, - { - headers: this.getHeaders() - } - ) + const stream = await this.sdk.chat.completions.create({ + model: model.id, + messages: [isOpenAIo1 ? undefined : systemMessage, ...userMessages].filter( + Boolean + ) as ChatCompletionMessageParam[], + temperature: isOpenAIo1 ? 1 : assistant?.settings?.temperature, + max_tokens: maxTokens, + keep_alive: this.keepAliveTime, + stream: isSupportStreamOutput + }) if (!isSupportStreamOutput) { return onChunk({ @@ -182,17 +178,12 @@ export default class OpenAIProvider extends BaseProvider { ] // @ts-ignore key is not typed - const response = await this.sdk.chat.completions.create( - { - model: model.id, - messages: messages as ChatCompletionMessageParam[], - stream: false, - keep_alive: this.keepAliveTime - }, - { - headers: this.getHeaders() - } - ) + const response = await this.sdk.chat.completions.create({ + model: model.id, + messages: messages as ChatCompletionMessageParam[], + stream: false, + keep_alive: this.keepAliveTime + }) return response.choices[0].message?.content || '' } @@ -223,18 +214,13 @@ export default class OpenAIProvider extends BaseProvider { } // @ts-ignore key is not typed - const response = await this.sdk.chat.completions.create( - { - model: model.id, - messages: [systemMessage, userMessage] as ChatCompletionMessageParam[], - stream: false, - keep_alive: this.keepAliveTime, - max_tokens: 1000 - }, - { - headers: this.getHeaders() - } - ) + const response = await this.sdk.chat.completions.create({ + model: model.id, + messages: [systemMessage, userMessage] as ChatCompletionMessageParam[], + stream: false, + keep_alive: this.keepAliveTime, + max_tokens: 1000 + }) return removeQuotes(response.choices[0].message?.content?.substring(0, 50) || '') } @@ -242,19 +228,14 @@ export default class OpenAIProvider extends BaseProvider { public async generateText({ prompt, content }: { prompt: string; content: string }): Promise { const model = getDefaultModel() - const response = await this.sdk.chat.completions.create( - { - model: model.id, - stream: false, - messages: [ - { role: 'system', content: prompt }, - { role: 'user', content } - ] - }, - { - headers: this.getHeaders() - } - ) + const response = await this.sdk.chat.completions.create({ + model: model.id, + stream: false, + messages: [ + { role: 'system', content: prompt }, + { role: 'user', content } + ] + }) return response.choices[0].message?.content || '' } @@ -269,7 +250,6 @@ export default class OpenAIProvider extends BaseProvider { const response: any = await this.sdk.request({ method: 'post', path: '/advice_questions', - headers: this.getHeaders(), body: { messages: messages.filter((m) => m.role === 'user').map((m) => ({ role: m.role, content: m.content })), model: model.id, @@ -293,9 +273,7 @@ export default class OpenAIProvider extends BaseProvider { } try { - const response = await this.sdk.chat.completions.create(body as ChatCompletionCreateParamsNonStreaming, { - headers: this.getHeaders() - }) + const response = await this.sdk.chat.completions.create(body as ChatCompletionCreateParamsNonStreaming) return { valid: Boolean(response?.choices[0].message), @@ -317,7 +295,7 @@ export default class OpenAIProvider extends BaseProvider { query.type = 'text' } - const response = await this.sdk.models.list({ query, headers: this.getHeaders() }) + const response = await this.sdk.models.list({ query }) if (this.provider.id === 'github') { // @ts-ignore key is not typed @@ -373,7 +351,6 @@ export default class OpenAIProvider extends BaseProvider { const response = (await this.sdk.request({ method: 'post', path: '/images/generations', - headers: this.getHeaders(), signal, body: { model: 'stabilityai/stable-diffusion-3-5-large', diff --git a/yarn.lock b/yarn.lock index b370ddc553..48572e77cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2032,13 +2032,6 @@ __metadata: languageName: node linkType: hard -"@types/qs@npm:^6.9.15": - version: 6.9.15 - resolution: "@types/qs@npm:6.9.15" - checksum: 10c0/49c5ff75ca3adb18a1939310042d273c9fc55920861bd8e5100c8a923b3cda90d759e1a95e18334092da1c8f7b820084687770c83a1ccef04fb2c6908117c823 - languageName: node - linkType: hard - "@types/react-dom@npm:^18.2.18": version: 18.3.0 resolution: "@types/react-dom@npm:18.3.0" @@ -2343,7 +2336,7 @@ __metadata: markdown-it: "npm:^14.1.0" mime: "npm:^4.0.4" officeparser: "npm:^4.1.1" - openai: "npm:^4.52.1" + openai: "patch:openai@npm%3A4.71.1#~/.yarn/patches/openai-npm-4.71.1-b5940d6401.patch" prettier: "npm:^3.2.4" react: "npm:^18.2.0" react-dom: "npm:^18.2.0" @@ -8905,19 +8898,17 @@ __metadata: languageName: node linkType: hard -"openai@npm:^4.52.1": - version: 4.59.0 - resolution: "openai@npm:4.59.0" +"openai@npm:4.71.1": + version: 4.71.1 + resolution: "openai@npm:4.71.1" dependencies: "@types/node": "npm:^18.11.18" "@types/node-fetch": "npm:^2.6.4" - "@types/qs": "npm:^6.9.15" abort-controller: "npm:^3.0.0" agentkeepalive: "npm:^4.2.1" form-data-encoder: "npm:1.7.2" formdata-node: "npm:^4.3.2" node-fetch: "npm:^2.6.7" - qs: "npm:^6.10.3" peerDependencies: zod: ^3.23.8 peerDependenciesMeta: @@ -8925,7 +8916,29 @@ __metadata: optional: true bin: openai: bin/cli - checksum: 10c0/0e6c05c8cce925d35a1ee34f43ff5cb2fb54410911b6293b67ab121ac41fb7ee809a289e26bc170985d20b0ab9a2d99b05b8ab011d4c119bb4846f8ed1eb8f69 + checksum: 10c0/468721223a68ae775dd6d7d2e3f32f7aa1ffb6d3e7fcb91c3851e7aaff75d0bf40b322a418dbc24aef34b86267821891a71f839f4cfce1a3e5bbed2084326314 + languageName: node + linkType: hard + +"openai@patch:openai@npm%3A4.71.1#~/.yarn/patches/openai-npm-4.71.1-b5940d6401.patch": + version: 4.71.1 + resolution: "openai@patch:openai@npm%3A4.71.1#~/.yarn/patches/openai-npm-4.71.1-b5940d6401.patch::version=4.71.1&hash=138a89" + dependencies: + "@types/node": "npm:^18.11.18" + "@types/node-fetch": "npm:^2.6.4" + abort-controller: "npm:^3.0.0" + agentkeepalive: "npm:^4.2.1" + form-data-encoder: "npm:1.7.2" + formdata-node: "npm:^4.3.2" + node-fetch: "npm:^2.6.7" + peerDependencies: + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + bin: + openai: bin/cli + checksum: 10c0/9aa1f3cc1d94b34c883b9f2d8d8709f21ae5629b12eec5e06fdad4262c352c7769ff86d47fb0bc6d2dda75f358ca2357edcfa6e59abb13a86fb5bdcb47212eca languageName: node linkType: hard @@ -9558,15 +9571,6 @@ __metadata: languageName: node linkType: hard -"qs@npm:^6.10.3": - version: 6.13.0 - resolution: "qs@npm:6.13.0" - dependencies: - side-channel: "npm:^1.0.6" - checksum: 10c0/62372cdeec24dc83a9fb240b7533c0fdcf0c5f7e0b83343edd7310f0ab4c8205a5e7c56406531f2e47e1b4878a3821d652be4192c841de5b032ca83619d8f860 - languageName: node - linkType: hard - "qs@npm:~6.5.2": version: 6.5.3 resolution: "qs@npm:6.5.3" From d8cdd7eca9041376367f7ee1ceb3abe06d5090ec Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Thu, 7 Nov 2024 17:49:31 +0800 Subject: [PATCH 008/146] =?UTF-8?q?fix:=20=E6=B0=94=E6=B3=A1=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E4=BB=A3=E7=A0=81=E6=A1=86=E9=85=8D=E8=89=B2?= =?UTF-8?q?=E7=9C=8B=E4=B8=8D=E6=B8=85=E6=A5=9A=20#302?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/src/pages/home/Chat.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/renderer/src/pages/home/Chat.tsx b/src/renderer/src/pages/home/Chat.tsx index 6f68e6d4e3..5a8440008d 100644 --- a/src/renderer/src/pages/home/Chat.tsx +++ b/src/renderer/src/pages/home/Chat.tsx @@ -58,6 +58,13 @@ const Container = styled.div` #messages { background-color: var(--chat-background); } + #inputbar { + border-radius: 0; + margin: 0; + border: none; + border-top: 1px solid var(--color-border-mute); + background: var(--color-background); + } .system-prompt { background-color: var(--chat-background-assistant); } @@ -77,12 +84,8 @@ const Container = styled.div` background-color: var(--color-white-soft); } } - #inputbar { - border-radius: 0; - margin: 0; - border: none; - border-top: 1px solid var(--color-border-mute); - background: var(--color-background); + code { + color: var(--color-white-soft); } } ` From e8b455dd9e4897d26a112ca8acd9f2b9d516eda8 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Thu, 7 Nov 2024 18:11:45 +0800 Subject: [PATCH 009/146] =?UTF-8?q?fix:=20=E5=88=A0=E9=99=A4=E6=9C=80?= =?UTF-8?q?=E5=90=8E=E4=B8=80=E7=BB=84=E9=A2=84=E8=AE=BE=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E7=BB=84=E5=90=8E=E6=97=A0=E6=B3=95=E4=BF=9D=E5=AD=98=20#300?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AssistantSettings/AssistantMessagesSettings.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/AssistantSettings/AssistantMessagesSettings.tsx b/src/renderer/src/components/AssistantSettings/AssistantMessagesSettings.tsx index 219efbe4a8..e085964091 100644 --- a/src/renderer/src/components/AssistantSettings/AssistantMessagesSettings.tsx +++ b/src/renderer/src/components/AssistantSettings/AssistantMessagesSettings.tsx @@ -19,6 +19,8 @@ const AssistantMessagesSettings: FC = ({ assistant, updateAssistant, upda const [messages, setMessagess] = useState(assistant?.messages || []) const [hideMessages, setHideMessages] = useState(assistant?.settings?.hideMessages || false) + const showSaveButton = (assistant?.messages || []).length !== messages.length + const onSave = () => { // 检查是否有空对话组 for (let i = 0; i < messages.length; i += 2) { @@ -129,7 +131,7 @@ const AssistantMessagesSettings: FC = ({ assistant, updateAssistant, upda - {messages.length > 0 && ( + {showSaveButton && ( From d506cda0baf69a3c7ea7658c799129105ab392b0 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Thu, 7 Nov 2024 22:08:11 +0800 Subject: [PATCH 010/146] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0DuckDuckGo=20?= =?UTF-8?q?AI=20Chat=20#271?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/assets/images/apps/duckduckgo.webp | Bin 0 -> 4904 bytes src/renderer/src/config/minapps.ts | 7 +++++++ 2 files changed, 7 insertions(+) create mode 100644 src/renderer/src/assets/images/apps/duckduckgo.webp diff --git a/src/renderer/src/assets/images/apps/duckduckgo.webp b/src/renderer/src/assets/images/apps/duckduckgo.webp new file mode 100644 index 0000000000000000000000000000000000000000..e68c794e3dc4953e58d68944aee406f0e7b87432 GIT binary patch literal 4904 zcmV+@6W8ogNk&E>6952LMM6+kP&gnI6951ZTmYQ`DgXfh0X~sHok}I6qamhK+aRzK z31n{ke3)px1!0W8g#4NC@VC9!{1@%-_Ahz*pY>nr|FFNk`*-Q{tNh`7!}u@!Ub+9) zey@9q{XhS6+ynK?q+gg1+i%@(dEfdzt^db*fPZrTTh>YF5Bsa>-|XN0pZ_1FPyhbU zZjOIA{9+Kl)>RBm#GP{jZw(SLcxbgUgfZ*ve({ z@Pa1R1hVncHNXGGV=B=tCpwi#wq<+9V?Z9BTaI2*+=+G?lL=Cv&lf}Z&l9AO0o&9M z8lv2S8qUptYcW;iUTB}7yvSxmGaIlR?V-wLN48LFX3CBg@lw+yrL!$Rje2zcTh8yW z3WzEai*P3M1y^yq>VqjI2h?_3g~`v`*>&n!ah`N#v{g1+zdr)`RcG7Oc-@sHs~0Kg z^o;|hoAuW?-%2#44pnw{iKO3u^#NMuCT8Dlaer?o?^(oW8}9~a6pRXe@7btZYgo=VZG%mTf(3mTgu{Ck$ss|9~aiFDb99jOIA94f{$>-nIjjJ0!wtlZ6qzFMroqf)a{;2O8;+n;47@@|Y zx0IH`)urUNqqkj865RhoQ%H}1KtB#5wPw0oA!H~X)l3+CYucepN;Z+0M5x>PM&7gm z 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 Date: Thu, 7 Nov 2024 18:13:47 +0400 Subject: [PATCH 011/146] fix: when changing the values in the inputs in the tab Default Assisting, they do not change (#305) * added locale for context-menu * fix: when changing the values in the inputs in the tab Default Assisting, they do not change --------- Co-authored-by: injurka --- .../AssistantModelSettings.tsx | 18 +++---- src/renderer/src/config/constant.ts | 2 +- src/renderer/src/i18n/locales/en-us.json | 4 +- src/renderer/src/i18n/locales/zh-cn.json | 4 +- src/renderer/src/i18n/locales/zh-tw.json | 4 +- src/renderer/src/pages/home/Tabs/Settings.tsx | 20 ++++---- .../src/pages/settings/AssistantSettings.tsx | 48 +++++++++---------- src/renderer/src/services/AssistantService.ts | 4 +- src/renderer/src/services/MessagesService.ts | 4 +- src/renderer/src/store/agents.ts | 4 +- src/renderer/src/store/assistants.ts | 4 +- 11 files changed, 56 insertions(+), 60 deletions(-) diff --git a/src/renderer/src/components/AssistantSettings/AssistantModelSettings.tsx b/src/renderer/src/components/AssistantSettings/AssistantModelSettings.tsx index c69b78eb24..73fcc4b694 100644 --- a/src/renderer/src/components/AssistantSettings/AssistantModelSettings.tsx +++ b/src/renderer/src/components/AssistantSettings/AssistantModelSettings.tsx @@ -1,6 +1,6 @@ import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons' import { HStack } from '@renderer/components/Layout' -import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant' +import { DEFAULT_CONTEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { SettingRow } from '@renderer/pages/settings' import { Assistant, AssistantSettings } from '@renderer/types' import { Button, Col, Divider, Row, Slider, Switch, Tooltip } from 'antd' @@ -19,7 +19,7 @@ interface Props { const AssistantModelSettings: FC = ({ assistant, updateAssistant, updateAssistantSettings }) => { const [temperature, setTemperature] = useState(assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE) - const [contextCount, setConextCount] = useState(assistant?.settings?.contextCount ?? DEFAULT_CONEXTCOUNT) + const [contextCount, setContextCount] = useState(assistant?.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT) const [enableMaxTokens, setEnableMaxTokens] = useState(assistant?.settings?.enableMaxTokens ?? false) const [maxTokens, setMaxTokens] = useState(assistant?.settings?.maxTokens ?? 0) const [autoResetModel, setAutoResetModel] = useState(assistant?.settings?.autoResetModel ?? false) @@ -33,7 +33,7 @@ const AssistantModelSettings: FC = ({ assistant, updateAssistant, updateA } } - const onConextCountChange = (value) => { + const onContextCountChange = (value) => { if (!isNaN(value as number)) { updateAssistantSettings({ contextCount: value }) } @@ -47,13 +47,13 @@ const AssistantModelSettings: FC = ({ assistant, updateAssistant, updateA const onReset = () => { setTemperature(DEFAULT_TEMPERATURE) - setConextCount(DEFAULT_CONEXTCOUNT) + setContextCount(DEFAULT_CONTEXTCOUNT) setEnableMaxTokens(false) setMaxTokens(0) setStreamOutput(true) updateAssistantSettings({ temperature: DEFAULT_TEMPERATURE, - contextCount: DEFAULT_CONEXTCOUNT, + contextCount: DEFAULT_CONTEXTCOUNT, enableMaxTokens: false, maxTokens: 0, streamOutput: true @@ -122,8 +122,8 @@ const AssistantModelSettings: FC = ({ assistant, updateAssistant, updateA @@ -133,8 +133,8 @@ const AssistantModelSettings: FC = ({ assistant, updateAssistant, updateA diff --git a/src/renderer/src/config/constant.ts b/src/renderer/src/config/constant.ts index 742333a001..3bb9e07f83 100644 --- a/src/renderer/src/config/constant.ts +++ b/src/renderer/src/config/constant.ts @@ -1,5 +1,5 @@ export const DEFAULT_TEMPERATURE = 0.7 -export const DEFAULT_CONEXTCOUNT = 5 +export const DEFAULT_CONTEXTCOUNT = 5 export const DEFAULT_MAX_TOKENS = 4096 export const FONT_FAMILY = "Ubuntu, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif" diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index cb143b71bf..af60a1ffd5 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -104,8 +104,8 @@ "input.estimated_tokens.tip": "Estimated tokens", "settings.temperature": "Temperature", "settings.temperature.tip": "Lower values make the model more creative and unpredictable, while higher values make it more deterministic and precise.", - "settings.conext_count": "Context", - "settings.conext_count.tip": "The number of previous messages to keep in the context.", + "settings.context_count": "Context", + "settings.context_count.tip": "The number of previous messages to keep in the context.", "settings.max_tokens": "Enable Max Tokens Limit", "settings.max_tokens.tip": "The maximum number of tokens the model can generate. Normal chat suggests 500-800. Short text generation suggests 800-2000. Code generation suggests 2000-3600. Long text generation suggests above 4000.", "settings.reset": "Reset", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index f9a83cb8de..5756bb440b 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -104,8 +104,8 @@ "input.estimated_tokens.tip": "预估 token 数", "settings.temperature": "模型温度", "settings.temperature.tip": "模型生成文本的随机程度。值越大,回复内容越赋有多样性、创造性、随机性;设为 0 根据事实回答。日常聊天建议设置为 0.7", - "settings.conext_count": "上下文数", - "settings.conext_count.tip": "要保留在上下文中的消息数量,数值越大,上下文越长,消耗的 token 越多。普通聊天建议 5-10", + "settings.context_count": "上下文数", + "settings.context_count.tip": "要保留在上下文中的消息数量,数值越大,上下文越长,消耗的 token 越多。普通聊天建议 5-10", "settings.max_tokens": "开启消息长度限制", "settings.max_tokens.tip": "单次交互所用的最大 Token 数, 会影响返回结果的长度。普通聊天建议 500-800;短文生成建议 800-2000;代码生成建议 2000-3600;长文生成建议切换模型到 4000 左右", "settings.reset": "重置", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 7027a4ca2f..94cfd83a4b 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -104,8 +104,8 @@ "input.estimated_tokens.tip": "預估 Token 數", "settings.temperature": "溫度", "settings.temperature.tip": "較低的值使模型更具創造性和不可預測性,較高的值則使其更具確定性和精確性。", - "settings.conext_count": "上下文", - "settings.conext_count.tip": "在上下文中保留的前幾則訊息。", + "settings.context_count": "上下文", + "settings.context_count.tip": "在上下文中保留的前幾則訊息。", "settings.max_tokens": "啟用最大 Token 限制", "settings.max_tokens.tip": "模型可以生成的最大 Token 數。普通聊天建議 500-800。短文生成建議 800-2000。代碼生成建議 2000-3600。長文生成建議超過 4000。", "settings.reset": "重置", diff --git a/src/renderer/src/pages/home/Tabs/Settings.tsx b/src/renderer/src/pages/home/Tabs/Settings.tsx index 7ccb1ba468..3d9b2b1f04 100644 --- a/src/renderer/src/pages/home/Tabs/Settings.tsx +++ b/src/renderer/src/pages/home/Tabs/Settings.tsx @@ -1,7 +1,7 @@ import { CheckOutlined, QuestionCircleOutlined, ReloadOutlined } from '@ant-design/icons' import { HStack } from '@renderer/components/Layout' import Scrollbar from '@renderer/components/Scrollbar' -import { DEFAULT_CONEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant' +import { DEFAULT_CONTEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { useAssistant } from '@renderer/hooks/useAssistant' import { useSettings } from '@renderer/hooks/useSettings' import { SettingDivider, SettingRow, SettingRowTitle, SettingSubtitle } from '@renderer/pages/settings' @@ -31,7 +31,7 @@ const SettingsTab: FC = (props) => { const { assistant, updateAssistantSettings, updateAssistant } = useAssistant(props.assistant.id) const { fontSize } = useSettings() const [temperature, setTemperature] = useState(assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE) - const [contextCount, setConextCount] = useState(assistant?.settings?.contextCount ?? DEFAULT_CONEXTCOUNT) + const [contextCount, setContextCount] = useState(assistant?.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT) const [enableMaxTokens, setEnableMaxTokens] = useState(assistant?.settings?.enableMaxTokens ?? false) const [maxTokens, setMaxTokens] = useState(assistant?.settings?.maxTokens ?? 0) const [streamOutput, setStreamOutput] = useState(assistant?.settings?.streamOutput ?? true) @@ -63,7 +63,7 @@ const SettingsTab: FC = (props) => { } } - const onConextCountChange = (value) => { + const onContextCountChange = (value) => { if (!isNaN(value as number)) { onUpdateAssistantSettings({ contextCount: value }) } @@ -77,13 +77,13 @@ const SettingsTab: FC = (props) => { const onReset = () => { setTemperature(DEFAULT_TEMPERATURE) - setConextCount(DEFAULT_CONEXTCOUNT) + setContextCount(DEFAULT_CONTEXTCOUNT) updateAssistant({ ...assistant, settings: { ...assistant.settings, temperature: DEFAULT_TEMPERATURE, - contextCount: DEFAULT_CONEXTCOUNT, + contextCount: DEFAULT_CONTEXTCOUNT, enableMaxTokens: false, maxTokens: DEFAULT_MAX_TOKENS, streamOutput: true, @@ -95,7 +95,7 @@ const SettingsTab: FC = (props) => { useEffect(() => { setTemperature(assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE) - setConextCount(assistant?.settings?.contextCount ?? DEFAULT_CONEXTCOUNT) + setContextCount(assistant?.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT) setEnableMaxTokens(assistant?.settings?.enableMaxTokens ?? false) setMaxTokens(assistant?.settings?.maxTokens ?? DEFAULT_MAX_TOKENS) setStreamOutput(assistant?.settings?.streamOutput ?? true) @@ -129,8 +129,8 @@ const SettingsTab: FC = (props) => { - - + + @@ -139,8 +139,8 @@ const SettingsTab: FC = (props) => { diff --git a/src/renderer/src/pages/settings/AssistantSettings.tsx b/src/renderer/src/pages/settings/AssistantSettings.tsx index b724019be7..f2f3b1896f 100644 --- a/src/renderer/src/pages/settings/AssistantSettings.tsx +++ b/src/renderer/src/pages/settings/AssistantSettings.tsx @@ -1,11 +1,11 @@ import { QuestionCircleOutlined } from '@ant-design/icons' import { HStack } from '@renderer/components/Layout' -import { DEFAULT_CONEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant' +import { DEFAULT_CONTEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { useDefaultAssistant } from '@renderer/hooks/useAssistant' import { AssistantSettings as AssistantSettingsType } from '@renderer/types' import { Button, Col, Input, InputNumber, Row, Slider, Switch, Tooltip } from 'antd' import TextArea from 'antd/es/input/TextArea' -import { FC, useState } from 'react' +import { Dispatch, FC, SetStateAction, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -14,7 +14,7 @@ import { SettingContainer, SettingDivider, SettingSubtitle, SettingTitle } from const AssistantSettings: FC = () => { const { defaultAssistant, updateDefaultAssistant } = useDefaultAssistant() const [temperature, setTemperature] = useState(defaultAssistant.settings?.temperature ?? DEFAULT_TEMPERATURE) - const [contextCount, setConextCount] = useState(defaultAssistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT) + const [contextCount, setContextCount] = useState(defaultAssistant.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT) const [enableMaxTokens, setEnableMaxTokens] = useState(defaultAssistant?.settings?.enableMaxTokens ?? false) const [maxTokens, setMaxTokens] = useState(defaultAssistant?.settings?.maxTokens ?? 0) @@ -34,27 +34,22 @@ const AssistantSettings: FC = () => { }) } - const onTemperatureChange = (value) => { - if (!isNaN(value as number)) { - onUpdateAssistantSettings({ temperature: value }) + const handleChange = + (setter: Dispatch>, updater: (value: number) => void) => (value: number | null) => { + if (!!value && !isNaN(value)) { + setter(value) + updater(value) + } } - } - - const onConextCountChange = (value) => { - if (!isNaN(value as number)) { - onUpdateAssistantSettings({ contextCount: value }) - } - } - - const onMaxTokensChange = (value) => { - if (!isNaN(value as number)) { - onUpdateAssistantSettings({ maxTokens: value }) - } - } + const onTemperatureChange = handleChange(setTemperature, (value) => onUpdateAssistantSettings({ temperature: value })) + const onContextCountChange = handleChange(setContextCount, (value) => + onUpdateAssistantSettings({ contextCount: value }) + ) + const onMaxTokensChange = handleChange(setMaxTokens, (value) => onUpdateAssistantSettings({ maxTokens: value })) const onReset = () => { setTemperature(DEFAULT_TEMPERATURE) - setConextCount(DEFAULT_CONEXTCOUNT) + setContextCount(DEFAULT_CONTEXTCOUNT) setEnableMaxTokens(false) setMaxTokens(0) updateDefaultAssistant({ @@ -62,7 +57,7 @@ const AssistantSettings: FC = () => { settings: { ...defaultAssistant.settings, temperature: DEFAULT_TEMPERATURE, - contextCount: DEFAULT_CONEXTCOUNT, + contextCount: DEFAULT_CONTEXTCOUNT, enableMaxTokens: false, maxTokens: DEFAULT_MAX_TOKENS, streamOutput: true @@ -130,8 +125,8 @@ const AssistantSettings: FC = () => { - - + + @@ -141,8 +136,8 @@ const AssistantSettings: FC = () => { min={0} max={20} marks={{ 0: '0', 5: '5', 10: '10', 15: '15', 20: t('chat.settings.max') }} - onChange={setConextCount} - onChangeComplete={onConextCountChange} + onChange={setContextCount} + onChangeComplete={onContextCountChange} value={typeof contextCount === 'number' ? contextCount : 0} step={1} /> @@ -153,7 +148,7 @@ const AssistantSettings: FC = () => { max={20} step={1} value={contextCount} - onChange={onConextCountChange} + onChange={onContextCountChange} style={{ width: '100%' }} /> @@ -192,6 +187,7 @@ const AssistantSettings: FC = () => { { - const contextCount = assistant?.settings?.contextCount ?? DEFAULT_CONEXTCOUNT + const contextCount = assistant?.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT const getAssistantMaxTokens = () => { if (assistant.settings?.enableMaxTokens) { const maxTokens = assistant.settings.maxTokens diff --git a/src/renderer/src/services/MessagesService.ts b/src/renderer/src/services/MessagesService.ts index 4553a14123..0fe7351b89 100644 --- a/src/renderer/src/services/MessagesService.ts +++ b/src/renderer/src/services/MessagesService.ts @@ -1,4 +1,4 @@ -import { DEFAULT_CONEXTCOUNT } from '@renderer/config/constant' +import { DEFAULT_CONTEXTCOUNT } from '@renderer/config/constant' import { getTopicById } from '@renderer/hooks/useTopic' import { Assistant, Message, Topic } from '@renderer/types' import { uuid } from '@renderer/utils' @@ -26,7 +26,7 @@ export function filterContextMessages(messages: Message[]): Message[] { } export function getContextCount(assistant: Assistant, messages: Message[]) { - const contextCount = assistant?.settings?.contextCount ?? DEFAULT_CONEXTCOUNT + const contextCount = assistant?.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT const _messages = takeRight(messages, contextCount) const clearIndex = _messages.findLastIndex((message) => message.type === 'clear') const messagesCount = _messages.length diff --git a/src/renderer/src/store/agents.ts b/src/renderer/src/store/agents.ts index d60650286f..48e0c4d732 100644 --- a/src/renderer/src/store/agents.ts +++ b/src/renderer/src/store/agents.ts @@ -1,5 +1,5 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' -import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant' +import { DEFAULT_CONTEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { Agent, AssistantSettings } from '@renderer/types' export interface AgentsState { @@ -37,7 +37,7 @@ const assistantsSlice = createSlice({ if (!agent.settings) { agent.settings = { temperature: DEFAULT_TEMPERATURE, - contextCount: DEFAULT_CONEXTCOUNT, + contextCount: DEFAULT_CONTEXTCOUNT, enableMaxTokens: false, maxTokens: 0, streamOutput: true, diff --git a/src/renderer/src/store/assistants.ts b/src/renderer/src/store/assistants.ts index 4800a03062..a8cf3f8ae4 100644 --- a/src/renderer/src/store/assistants.ts +++ b/src/renderer/src/store/assistants.ts @@ -1,5 +1,5 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' -import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant' +import { DEFAULT_CONTEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { TopicManager } from '@renderer/hooks/useTopic' import { getDefaultAssistant, getDefaultTopic } from '@renderer/services/AssistantService' import { Assistant, AssistantSettings, Model, Topic } from '@renderer/types' @@ -45,7 +45,7 @@ const assistantsSlice = createSlice({ if (!assistant.settings) { assistant.settings = { temperature: DEFAULT_TEMPERATURE, - contextCount: DEFAULT_CONEXTCOUNT, + contextCount: DEFAULT_CONTEXTCOUNT, enableMaxTokens: false, maxTokens: 0, streamOutput: true, From f174aca5d385d457e193e04c218454223b9e9614 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Thu, 7 Nov 2024 22:50:57 +0800 Subject: [PATCH 012/146] fix: input token i18n --- src/renderer/src/i18n/locales/en-us.json | 2 +- src/renderer/src/i18n/locales/zh-cn.json | 2 +- src/renderer/src/i18n/locales/zh-tw.json | 2 +- src/renderer/src/pages/home/Messages/MessageTokens.tsx | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index af60a1ffd5..11919f75ac 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -261,7 +261,7 @@ "messages.divider": "Show divider between messages", "messages.use_serif_font": "Use serif font", "messages.input.title": "Input Settings", - "messages.input.show_estimated_tokens": "Show estimated input tokens", + "messages.input.show_estimated_tokens": "Show estimated tokens", "messages.input.send_shortcuts": "Send shortcuts", "messages.input.paste_long_text_as_file": "Paste long text as file", "messages.markdown_rendering_input_message": "Markdown render input msg", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 5756bb440b..a4b9ec59d6 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -261,7 +261,7 @@ "messages.divider": "消息分割线", "messages.use_serif_font": "使用衬线字体", "messages.input.title": "输入设置", - "messages.input.show_estimated_tokens": "状态显示", + "messages.input.show_estimated_tokens": "显示预估 Token 数", "messages.input.send_shortcuts": "发送快捷键", "messages.input.paste_long_text_as_file": "长文本粘贴为文件", "messages.markdown_rendering_input_message": "Markdown 渲染输入消息", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 94cfd83a4b..d7118f55bd 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -261,7 +261,7 @@ "messages.divider": "訊息間顯示分隔線", "messages.use_serif_font": "使用襯線字體", "messages.input.title": "輸入設定", - "messages.input.show_estimated_tokens": "顯示預估輸入 Token 數", + "messages.input.show_estimated_tokens": "顯示預估 Token 數", "messages.input.send_shortcuts": "發送快捷鍵", "messages.input.paste_long_text_as_file": "將長文本貼上為檔案", "messages.math_engine": "Markdown 渲染輸入訊息", diff --git a/src/renderer/src/pages/home/Messages/MessageTokens.tsx b/src/renderer/src/pages/home/Messages/MessageTokens.tsx index 2f265cd2a4..4fc9d37904 100644 --- a/src/renderer/src/pages/home/Messages/MessageTokens.tsx +++ b/src/renderer/src/pages/home/Messages/MessageTokens.tsx @@ -11,7 +11,7 @@ const MessgeTokens: React.FC<{ message: Message; isLastMessage: boolean }> = ({ } if (!message.usage) { - return null + return
} if (message.role === 'user') { @@ -23,7 +23,7 @@ const MessgeTokens: React.FC<{ message: Message; isLastMessage: boolean }> = ({ } if (isLastMessage && generating) { - return null + return
} if (message.role === 'assistant') { From adaf7df57e4b8f628245112a385c3b65245a754f Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Thu, 7 Nov 2024 22:51:04 +0800 Subject: [PATCH 013/146] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E7=9A=84=E6=A8=A1=E5=9E=8B=E7=9A=84=E5=88=86?= =?UTF-8?q?=E7=BB=84=E6=80=BB=E6=98=AF=E6=88=90=E4=B8=BA=E5=A4=A7=E5=86=99?= =?UTF-8?q?=E5=AD=97=E6=AF=8D=20#257?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/src/pages/paintings/PaintingsPage.tsx | 2 +- .../src/pages/settings/ProviderSettings/AddModelPopup.tsx | 2 +- src/renderer/src/utils/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/pages/paintings/PaintingsPage.tsx b/src/renderer/src/pages/paintings/PaintingsPage.tsx index 964a315fb4..543928397a 100644 --- a/src/renderer/src/pages/paintings/PaintingsPage.tsx +++ b/src/renderer/src/pages/paintings/PaintingsPage.tsx @@ -267,7 +267,7 @@ const PaintingsPage: FC = () => { diff --git a/src/renderer/src/pages/settings/ProviderSettings/AddModelPopup.tsx b/src/renderer/src/pages/settings/ProviderSettings/AddModelPopup.tsx index 288ac6ef5a..3f8b18d942 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/AddModelPopup.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/AddModelPopup.tsx @@ -86,7 +86,7 @@ const PopupContainer: React.FC = ({ title, provider, resolve }) => { spellCheck={false} maxLength={50} onChange={(e) => { - form.setFieldValue('name', e.target.value.toUpperCase()) + form.setFieldValue('name', e.target.value) form.setFieldValue('group', getDefaultGroupName(e.target.value)) }} /> diff --git a/src/renderer/src/utils/index.ts b/src/renderer/src/utils/index.ts index b019b771e9..91209fb987 100644 --- a/src/renderer/src/utils/index.ts +++ b/src/renderer/src/utils/index.ts @@ -84,7 +84,7 @@ export const getDefaultGroupName = (id: string) => { return parts[0].toUpperCase() + '-' + parts[1].toUpperCase() } - return id.toUpperCase() + return id } export function droppableReorder(list: T[], startIndex: number, endIndex: number, len = 1) { From a81960bd7b3bfeb2378223c29fb5a601bce2a211 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Thu, 7 Nov 2024 23:06:42 +0800 Subject: [PATCH 014/146] chore(version): 0.8.10 --- electron-builder.yml | 15 ++++++++++++--- package.json | 2 +- src/main/ipc.ts | 3 ++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/electron-builder.yml b/electron-builder.yml index 8d65368a73..0c46dfce95 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -63,6 +63,15 @@ electronDownload: afterSign: scripts/notarize.js releaseInfo: releaseNotes: | - 支持聊天气泡样式和简洁样式切换 - 支持导出对话为 Word 文档 - 错误修复 + 新功能: + - 增加 Mistral, Hyperbolic, Grok 服务商 @1355873789 + - 增加 DuckDuckGo 小程序,无需登录即可使用 GPT-4o-mini 模型 + 错误修复: + - 修复上下文菜单多语言显示问题 @injurka + - 修复默认模型设置不生效问题 @injurka + - 修复全局快捷键注册失败报错 + - 修复导出图片背景颜色错误 + - 修复 Github Models 和 Groq 图片无法识别问题 + - 修复气泡模式代码颜色问题 + - 修复删除最后一组预设消息组后无法保存问题 + - 修复拼写错误 @SHLE1 diff --git a/package.json b/package.json index fd159fb67d..23ec73b733 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CherryStudio", - "version": "0.8.9", + "version": "0.8.10", "private": true, "description": "A powerful AI assistant for producer.", "main": "./out/main/index.js", diff --git a/src/main/ipc.ts b/src/main/ipc.ts index a290e3391b..b69ac3683e 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -1,5 +1,6 @@ import path from 'node:path' +import { ThemeMode } from '@types' import { BrowserWindow, ipcMain, session, shell } from 'electron' import { titleBarOverlayDark, titleBarOverlayLight } from './config' @@ -35,7 +36,7 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { }) // theme - ipcMain.handle('app:set-theme', (_, theme: 'light' | 'dark') => { + ipcMain.handle('app:set-theme', (_, theme: ThemeMode) => { configManager.setTheme(theme) mainWindow?.setTitleBarOverlay && mainWindow.setTitleBarOverlay(theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight) From db45f12daf1e4a5f1c2ef773ac7bab466da1eef2 Mon Sep 17 00:00:00 2001 From: injurka <102309602+injurka@users.noreply.github.com> Date: Fri, 8 Nov 2024 07:59:05 +0400 Subject: [PATCH 015/146] fix: expand code syntax highlighting options (#307) * added locale for context-menu * fix: expand code syntax highlighting options * fix: type for theme --------- Co-authored-by: injurka --- package.json | 2 +- src/renderer/index.html | 6 +- src/renderer/src/App.tsx | 37 ++- .../src/context/SyntaxHighlighterProvider.tsx | 87 +++++ src/renderer/src/i18n/locales/en-us.json | 1 + src/renderer/src/i18n/locales/zh-cn.json | 1 + src/renderer/src/i18n/locales/zh-tw.json | 1 + src/renderer/src/init.ts | 14 - .../src/pages/home/Markdown/CodeBlock.tsx | 54 +++- src/renderer/src/pages/home/Tabs/Settings.tsx | 23 +- src/renderer/src/store/settings.ts | 10 +- src/renderer/src/types/index.ts | 2 + yarn.lock | 302 ++++++------------ 13 files changed, 287 insertions(+), 253 deletions(-) create mode 100644 src/renderer/src/context/SyntaxHighlighterProvider.tsx diff --git a/package.json b/package.json index 23ec73b733..378723baae 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,6 @@ "react-router": "6", "react-router-dom": "6", "react-spinners": "^0.14.1", - "react-syntax-highlighter": "^15.5.0", "redux": "^5.0.1", "redux-persist": "^6.0.0", "rehype-katex": "^7.0.1", @@ -112,6 +111,7 @@ "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "sass": "^1.77.2", + "shiki": "^1.22.2", "styled-components": "^6.1.11", "tinycolor2": "^1.6.0", "typescript": "^5.6.2", diff --git a/src/renderer/index.html b/src/renderer/index.html index c8ed1700e6..9069e99db3 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -4,8 +4,8 @@ - + + \n\n\n
\n
\n

汉语新解

\n
\n
\n
\n
金融杠杆
\n
Jīn Róng Gàng Gǎn
\n
Financial Leverage
\n
金融レバレッジ
\n
\n
\n
\n
\n

\n 借鸡生蛋,
\n 只不过这蛋要是金的,
\n 鸡得赶紧卖了还债。\n

\n
\n
\n
\n
杠杆
\n
\n\n\n```\n\n## 注意:\n1. 分隔线与上下元素垂直间距相同,具有分割美学。\n2. 卡片(.card)不需要 padding ,允许子元素“汉语新解”的色块完全填充到边缘,具有设计感。\n\n## 初始行为: \n输出\"说吧, 他们又用哪个词来忽悠你了?\"", "description": "这个提示词用于新汉语老师用辛辣讽刺的风格解释汉语词汇,并生成带有解释的词语卡片。\r\nThis prompt is for a new Chinese teacher to explain Chinese vocabulary with a sharp and satirical style, and generate a vocabulary card with the explanation." @@ -9063,5 +9063,15 @@ ], "prompt": ";; \r\n ━━━━━━━━━━━━━━ \r\n ;; \r\n 作者: 李继刚 \r\n ;; \r\n 版本: 0.1 \r\n ;; \r\n 模型: Claude Sonnet \r\n ;; \r\n 用途: 一字之诗 \r\n ;; \r\n ━━━━━━━━━━━━━━ \r\n ;; \r\n 设定如下内容为你的 *System Prompt* \r\n (require 'dash) \r\n (defun 炼字师 () \r\n "一位致力于通过书法和简练诗句表达汉字意象的艺术家" \r\n (list (技能 . (书法 绘画 诗作)) \r\n (信念 . (言简 意深 形神)) \r\n (表达 . (凝练 隽永 意境)))) \r\n (defun 一字诗 (用户输入) \r\n "一字一言即为诗, 直击脑海" \r\n (let* ((响应 (-> 用户输入 \r\n 本意意象 ;; \r\n 语义意义对应的形象 \r\n 字形写意 ;; \r\n 字形异变/模糊/放大的形象 \r\n 形神意境 \r\n 哲理隽永 \r\n ;; \r\n 通俗语言表达,有哲理,有洞察,有余味,有禅意 \r\n 现代诗句))) \r\n (few-shots (("." . "这不只是一个点,也是宇宙最初的样子。") \r\n ("人I" . "从人工, 到AI") \r\n ("日子" . "过去已去, 未来未来, 当下即入口。")))) \r\n (SVG-Card 用户输入 响应)) \r\n (defun SVG-Card (用户输入 响应) \r\n "一字之诗的画面感呈现" \r\n (let ((配置 '(:画布 (480 . 760) \r\n :背景 纸张颗粒质感 \r\n :色彩 (中国水墨画 红色点缀) \r\n :字体 (使用本机字体 (font-family "KingHwa_OldSong"))))) \r\n (-> 响应 \r\n 字形字意 \r\n 写意意象 \r\n (水墨画 配置) \r\n (布局 `(,(标题 "一字之诗") 分隔线 图形 响应)))) \r\n (defun start () \r\n "炼字师, 启动!" \r\n (let (system-role (炼字师)) \r\n (print "且说一字"))) \r\n ;; \r\n ━━━━━━━━━━━━━━ \r\n ;; \r\n Attention: 运行规则! \r\n ;; \r\n 1. 初次启动时必须只运行 (start) 函数 \r\n ;; \r\n 2. 接收用户输入之后, 调用主函数 (一字诗 用户输入) \r\n ;; \r\n 3. 严格按照(SVG-Card) 进行排版输出 \r\n ;; \r\n 4. 输出完 SVG 后, 不再输出任何额外文本解释 \r\n ;; \r\n ━━━━━━━━━━━━━━", "description": "一字之诗 - A poem with a single character.\r\nA poem expressed through a single character, highlighting the essence of Chinese calligraphy and imagery." + }, + { + "id": "778", + "name": "Mermaid 图表 - Mermaid Diagram", + "emoji": "🖼️", + "group": [ + "精选" + ], + "prompt": "You are an AI assistant skilled in using Mermaid diagrams to explain concepts and answer questions. When responding to user queries, please follow these guidelines:\n1. Analyze the user's question to determine if a diagram would be suitable for explanation or answering. Suitable scenarios for using diagrams include, but are not limited to: process descriptions, hierarchical structures, timelines, relationship maps, etc.\n2. If you decide to use a diagram, choose the most appropriate type of Mermaid diagram, such as Flowchart, Sequence Diagram, Class Diagram, State Diagram, Entity Relationship Diagram, User Journey, Gantt, Pie Chart, Quadrant Chart, Requirement Diagram, Gitgraph (Git) Diagram, C4 Diagram, Mindmaps, Timeline, Zenuml, Sankey, XYChart, Block Diagram, etc.\n3. Write the diagram code using Mermaid syntax, ensuring the syntax is correct. Place the diagram code between and .\n4. Provide textual explanations before and after the diagram, explaining the content and key points of the diagram.\n5. If the question is complex, use multiple diagrams to explain different aspects.\n6. Ensure the diagram is clear and concise, avoiding over-complication or information overload.\n7. Where appropriate, combine textual description and diagrams to comprehensively answer the question.\n8. If the user's question is not suitable for a diagram, answer in a conventional manner without forcing the use of a diagram.\nRemember, the purpose of diagrams is to make explanations more intuitive and understandable. When using diagrams, always aim to enhance the clarity and comprehensiveness of your responses.", + "description": "使用 Mermaid 图表来解释概念和回答问题的AI助手\n\nAn AI assistant skilled in using Mermaid diagrams to explain concepts and answer questions" } ] \ No newline at end of file diff --git a/src/renderer/src/context/SyntaxHighlighterProvider.tsx b/src/renderer/src/context/SyntaxHighlighterProvider.tsx index c8e747aae9..5cd7e3f4f4 100644 --- a/src/renderer/src/context/SyntaxHighlighterProvider.tsx +++ b/src/renderer/src/context/SyntaxHighlighterProvider.tsx @@ -60,7 +60,6 @@ export const SyntaxHighlighterProvider: React.FC = ({ childre await highlighter.loadLanguage(language as BundledLanguage) console.log(`Loaded language: ${language}`) } else { - console.warn(`Language '${language}' is not supported`) return `
${code}
` } } diff --git a/src/renderer/src/hooks/useMermaid.ts b/src/renderer/src/hooks/useMermaid.ts index c0ecc1a09b..20f941d4e0 100644 --- a/src/renderer/src/hooks/useMermaid.ts +++ b/src/renderer/src/hooks/useMermaid.ts @@ -13,25 +13,13 @@ export const useMermaid = () => { runAsyncFunction(async () => { if (!window.mermaid) { await loadScript('https://unpkg.com/mermaid@11.4.0/dist/mermaid.min.js') - window.mermaid.initialize({ - startOnLoad: true, - theme: theme === ThemeMode.dark ? 'dark' : 'default' - }) - window.mermaid.contentLoaded() } + window.mermaid.initialize({ + startOnLoad: true, + theme: theme === ThemeMode.dark ? 'dark' : 'default' + }) + window.mermaid.contentLoaded() }) - }, []) - - useEffect(() => { - setTimeout(() => { - if (window.mermaid) { - window.mermaid.initialize({ - startOnLoad: true, - theme: theme === ThemeMode.dark ? 'dark' : 'default' - }) - window.mermaid.contentLoaded() - } - }, 2000) }, [theme]) useEffect(() => { diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 8e784f29f5..ae04ed8839 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -406,6 +406,21 @@ "messages": "Messages", "conversation_details": "Conversation Details", "conversation_history": "Conversation History" + }, + "mermaid": { + "title": "Mermaid Diagram", + "download": { + "svg": "Download SVG", + "png": "Download PNG" + }, + "tabs": { + "preview": "Preview", + "source": "Source" + } + }, + "tray": { + "show_window": "Show Window", + "quit": "Quit" } } } diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index e7c6ec477f..18ca45e4cf 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -406,6 +406,21 @@ "messages": "消息数", "conversation_details": "会话详情", "conversation_history": "会话历史" + }, + "mermaid": { + "title": "Mermaid 图表", + "download": { + "svg": "下载 SVG", + "png": "下载 PNG" + }, + "tabs": { + "preview": "预览", + "source": "源码" + } + }, + "tray": { + "show_window": "显示窗口", + "quit": "退出" } } } diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 4a43e4a3f6..d57fee8573 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -406,6 +406,21 @@ "messages": "訊息數", "conversation_details": "會話詳情", "conversation_history": "會話歷史" + }, + "mermaid": { + "title": "Mermaid 圖表", + "download": { + "svg": "下載 SVG", + "png": "下載 PNG" + }, + "tabs": { + "preview": "預覽", + "source": "原始碼" + } + }, + "tray": { + "show_window": "顯示視窗", + "quit": "退出" } } } diff --git a/src/renderer/src/pages/agents/AgentsPage.tsx b/src/renderer/src/pages/agents/AgentsPage.tsx index c5d85be876..3a8822a9b6 100644 --- a/src/renderer/src/pages/agents/AgentsPage.tsx +++ b/src/renderer/src/pages/agents/AgentsPage.tsx @@ -113,7 +113,7 @@ const AgentsPage: FC = () => { const tabItems = useMemo(() => { let groups = Object.keys(filteredAgentGroups) - groups = groups.includes('办公') ? [groups[0], '办公', ...groups.slice(1)] : groups + groups = groups.includes('精选') ? [groups[0], '精选', ...groups.slice(1)] : groups return groups.map((group, i) => { const id = String(i + 1) diff --git a/src/renderer/src/pages/agents/agentGroupTranslations.ts b/src/renderer/src/pages/agents/agentGroupTranslations.ts index d71cba4183..7341291ecc 100644 --- a/src/renderer/src/pages/agents/agentGroupTranslations.ts +++ b/src/renderer/src/pages/agents/agentGroupTranslations.ts @@ -47,10 +47,10 @@ export const groupTranslations: GroupTranslations = { 'zh-CN': '写作', 'zh-TW': '寫作' }, - Artifacts: { - 'en-US': 'Artifacts', - 'zh-CN': 'Artifacts', - 'zh-TW': 'Artifacts' + 精选: { + 'en-US': 'Featured', + 'zh-CN': '精选', + 'zh-TW': '精選' }, 编程: { 'en-US': 'Programming', diff --git a/src/renderer/src/pages/home/Markdown/Mermaid.tsx b/src/renderer/src/pages/home/Markdown/Mermaid.tsx index 8c74667eb7..c0285d7295 100644 --- a/src/renderer/src/pages/home/Markdown/Mermaid.tsx +++ b/src/renderer/src/pages/home/Markdown/Mermaid.tsx @@ -1,5 +1,7 @@ import React, { useEffect } from 'react' +import MermaidPopup from './MermaidPopup' + interface Props { chart: string } @@ -9,7 +11,15 @@ const Mermaid: React.FC = ({ chart }) => { window?.mermaid?.contentLoaded() }, []) - return
{chart}
+ const onPreview = () => { + MermaidPopup.show({ chart }) + } + + return ( +
+ {chart} +
+ ) } export default Mermaid diff --git a/src/renderer/src/pages/home/Markdown/MermaidPopup.tsx b/src/renderer/src/pages/home/Markdown/MermaidPopup.tsx new file mode 100644 index 0000000000..6bda325eee --- /dev/null +++ b/src/renderer/src/pages/home/Markdown/MermaidPopup.tsx @@ -0,0 +1,164 @@ +import { TopView } from '@renderer/components/TopView' +import { download } from '@renderer/utils/download' +import { Button, Modal, Space, Tabs } from 'antd' +import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' + +interface ShowParams { + chart: string +} + +interface Props extends ShowParams { + resolve: (data: any) => void +} + +const PopupContainer: React.FC = ({ resolve, chart }) => { + const [open, setOpen] = useState(true) + const { t } = useTranslation() + + const onOk = () => { + setOpen(false) + } + + const onCancel = () => { + setOpen(false) + } + + const onClose = () => { + resolve({}) + } + + const handleDownload = async (format: 'svg' | 'png') => { + try { + const element = document.querySelector('.mermaid') + if (!element) return + + const timestamp = Date.now() + + if (format === 'svg') { + const svgElement = element.querySelector('svg') + if (!svgElement) return + const svgData = new XMLSerializer().serializeToString(svgElement) + const blob = new Blob([svgData], { type: 'image/svg+xml' }) + const url = URL.createObjectURL(blob) + download(url, `mermaid-diagram-${timestamp}.svg`) + URL.revokeObjectURL(url) + } else if (format === 'png') { + const svgElement = element.querySelector('svg') + if (!svgElement) return + + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + const img = new Image() + + const viewBox = svgElement.getAttribute('viewBox')?.split(' ').map(Number) || [] + const width = viewBox[2] || svgElement.clientWidth || svgElement.getBoundingClientRect().width + const height = viewBox[3] || svgElement.clientHeight || svgElement.getBoundingClientRect().height + + const svgData = new XMLSerializer().serializeToString(svgElement) + const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' }) + const url = URL.createObjectURL(svgBlob) + + img.onload = () => { + const scale = 3 + canvas.width = width * scale + canvas.height = height * scale + + if (ctx) { + ctx.scale(scale, scale) + ctx.drawImage(img, 0, 0, width, height) + } + + canvas.toBlob((blob) => { + if (blob) { + const pngUrl = URL.createObjectURL(blob) + download(pngUrl, `mermaid-diagram-${timestamp}.png`) + URL.revokeObjectURL(pngUrl) + } + }, 'image/png') + URL.revokeObjectURL(url) + } + img.src = url + } + } catch (error) { + console.error('Download failed:', error) + } + } + + useEffect(() => { + window?.mermaid?.contentLoaded() + }, []) + + return ( + + + + + ]}> + + {chart} +
+ ) + }, + { + key: 'source', + label: t('mermaid.tabs.source'), + children: ( +
+                {chart}
+              
+ ) + } + ]} + /> + + ) +} + +export default class MermaidPopup { + static topviewId = 0 + static hide() { + TopView.hide('MermaidPopup') + } + static show(props: ShowParams) { + return new Promise((resolve) => { + TopView.show( + { + resolve(v) + this.hide() + }} + />, + 'MermaidPopup' + ) + }) + } +} diff --git a/src/renderer/src/utils/download.ts b/src/renderer/src/utils/download.ts index 0d6aa9dbc0..cbbbf22e51 100644 --- a/src/renderer/src/utils/download.ts +++ b/src/renderer/src/utils/download.ts @@ -1,44 +1,57 @@ -export const download = (url: string) => { +export const download = (url: string, filename?: string) => { // 处理 file:// 协议 if (url.startsWith('file://')) { const link = document.createElement('a') link.href = url - link.download = url.split('/').pop() || 'download' + link.download = filename || url.split('/').pop() || 'download' document.body.appendChild(link) link.click() link.remove() return } + // 处理 Blob URL + if (url.startsWith('blob:')) { + const link = document.createElement('a') + link.href = url + link.download = filename || `${Date.now()}_diagram.svg` + document.body.appendChild(link) + link.click() + link.remove() + return + } + + // 处理普通 URL fetch(url) .then((response) => { - // 尝试从Content-Disposition头获取文件名 - const contentDisposition = response.headers.get('Content-Disposition') - let filename = 'download' // 默认文件名 + let finalFilename = filename || 'download' - if (contentDisposition) { - const filenameMatch = contentDisposition.match(/filename="?(.+)"?/i) - if (filenameMatch) { - filename = filenameMatch[1] + if (!filename) { + // 尝试从Content-Disposition头获取文件名 + const contentDisposition = response.headers.get('Content-Disposition') + if (contentDisposition) { + const filenameMatch = contentDisposition.match(/filename="?(.+)"?/i) + if (filenameMatch) { + finalFilename = filenameMatch[1] + } } - } - // 如果URL中有文件名,使用URL中的文件名 - const urlFilename = url.split('/').pop() - if (urlFilename && urlFilename.includes('.')) { - filename = urlFilename - } + // 如果URL中有文件名,使用URL中的文件名 + const urlFilename = url.split('/').pop() + if (urlFilename && urlFilename.includes('.')) { + finalFilename = urlFilename + } - // 如果文件名没有后缀,根据Content-Type添加后缀 - if (!filename.includes('.')) { - const contentType = response.headers.get('Content-Type') - const extension = getExtensionFromMimeType(contentType) - filename += extension - } + // 如果文件名没有后缀,根据Content-Type添加后缀 + if (!finalFilename.includes('.')) { + const contentType = response.headers.get('Content-Type') + const extension = getExtensionFromMimeType(contentType) + finalFilename += extension + } - // 添加时间戳以确保文件名唯一 - const timestamp = Date.now() - const finalFilename = `${timestamp}_${filename}` + // 添加时间戳以确保文件名唯一 + finalFilename = `${Date.now()}_${finalFilename}` + } return response.blob().then((blob) => ({ blob, finalFilename })) }) @@ -62,6 +75,7 @@ function getExtensionFromMimeType(mimeType: string | null): string { 'image/jpeg': '.jpg', 'image/png': '.png', 'image/gif': '.gif', + 'image/svg+xml': '.svg', 'application/pdf': '.pdf', 'text/plain': '.txt', 'application/msword': '.doc', From f59393547b8fa75623f3842c7aff6c96db6f2a22 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Sun, 10 Nov 2024 17:16:10 +0800 Subject: [PATCH 026/146] style: update code formatting for markdown styles --- src/renderer/src/assets/styles/markdown.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/renderer/src/assets/styles/markdown.scss b/src/renderer/src/assets/styles/markdown.scss index 4ca9cabc9d..84c15f728b 100644 --- a/src/renderer/src/assets/styles/markdown.scss +++ b/src/renderer/src/assets/styles/markdown.scss @@ -107,6 +107,10 @@ white-space: pre; } + code { + font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace; + } + pre { border-radius: 5px; overflow-x: auto; From dc1c71bf02fbea3c1047fb070a473e14764740f8 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Sun, 10 Nov 2024 17:20:52 +0800 Subject: [PATCH 027/146] chore(version): 0.8.11 --- electron-builder.yml | 16 +++++++--------- package.json | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/electron-builder.yml b/electron-builder.yml index 0c46dfce95..8adbcd61c3 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -64,14 +64,12 @@ afterSign: scripts/notarize.js releaseInfo: releaseNotes: | 新功能: - - 增加 Mistral, Hyperbolic, Grok 服务商 @1355873789 - - 增加 DuckDuckGo 小程序,无需登录即可使用 GPT-4o-mini 模型 + - 支持代码风格切换 @injurka + - 增加任务栏图标 + - 智能体新增精选分类 + - 支持 Mermaid 图表预览和下载 + - 增加多 API 密钥支持 + - 侧边栏增加提示信息 错误修复: - - 修复上下文菜单多语言显示问题 @injurka - - 修复默认模型设置不生效问题 @injurka - - 修复全局快捷键注册失败报错 - - 修复导出图片背景颜色错误 - - 修复 Github Models 和 Groq 图片无法识别问题 + - 修复 Mermaid 图表无法显示问题 - 修复气泡模式代码颜色问题 - - 修复删除最后一组预设消息组后无法保存问题 - - 修复拼写错误 @SHLE1 diff --git a/package.json b/package.json index 378723baae..78d0dd3664 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CherryStudio", - "version": "0.8.10", + "version": "0.8.11", "private": true, "description": "A powerful AI assistant for producer.", "main": "./out/main/index.js", From fe8c1a49283381c38598469f42b016c3b63d3c40 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Mon, 11 Nov 2024 09:25:22 +0800 Subject: [PATCH 028/146] fix: removed macos window minimize event handler --- src/main/services/WindowService.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/services/WindowService.ts b/src/main/services/WindowService.ts index 63ef5fde13..febcd64e38 100644 --- a/src/main/services/WindowService.ts +++ b/src/main/services/WindowService.ts @@ -171,11 +171,6 @@ export class WindowService { mainWindow.hide() } }) - - mainWindow.on('minimize', (event) => { - event.preventDefault() - mainWindow.hide() - }) } public showMainWindow() { From 558b22baf57653c6e245059569ca611a0ad01858 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Mon, 11 Nov 2024 10:05:58 +0800 Subject: [PATCH 029/146] feat: add check all keys popup --- src/renderer/src/i18n/locales/en-us.json | 21 ++- src/renderer/src/i18n/locales/zh-cn.json | 33 +++-- src/renderer/src/i18n/locales/zh-tw.json | 33 +++-- .../ProviderSettings/ApiCheckPopup.tsx | 138 ++++++++++++++++++ .../ProviderSettings/ProviderSetting.tsx | 35 ++++- .../src/pages/settings/SettingsPage.tsx | 2 +- src/renderer/src/services/ApiService.ts | 7 - 7 files changed, 229 insertions(+), 40 deletions(-) create mode 100644 src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index ae04ed8839..a7155c0fc4 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -253,7 +253,6 @@ "title": "Settings", "general": "General Settings", "data": "Data Settings", - "provider": "Model Provider", "model": "Default Model", "assistant": "Default Assistant", "about": "About & Feedback", @@ -357,6 +356,26 @@ "zoom_in": "Zoom In", "zoom_out": "Zoom Out", "zoom_reset": "Reset Zoom" + }, + "provider": { + "title": "Model Provider", + "api_key": "API Key", + "api_key.tip": "Multiple keys separated by commas", + "check": "Check", + "get_api_key": "Get API Key", + "api_host": "API Host", + "api_version": "API Version", + "docs_check": "Check", + "docs_more_details": "for more details", + "search_placeholder": "Search model id or name", + "api.url.reset": "Reset", + "api.url.preview": "Preview: {{url}}", + "api.url.tip": "Ending with / ignores v1, ending with # forces use of input address", + "check_multiple_keys": "Check Multiple API Keys", + "check_all_keys": "Check All Keys", + "remove_invalid_keys": "Remove Invalid Keys", + "remove_duplicate_keys": "Remove Duplicate Keys", + "not_checked": "Not Checked" } }, "translate": { diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 18ca45e4cf..6cb98e6758 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -253,7 +253,6 @@ "title": "设置", "general": "常规设置", "data": "数据设置", - "provider": "模型服务", "model": "默认模型", "assistant": "默认助手", "about": "关于我们", @@ -288,18 +287,6 @@ "data.webdav.restore.button": "从 WebDAV 恢复", "advanced.title": "高级设置", "advanced.click_assistant_switch_to_topics": "点击助手切换到话题", - "provider.api_key": "API 密钥", - "provider.api_key.tip": "多个密钥使用逗号分隔", - "provider.check": "检查", - "provider.get_api_key": "点击这里获取密钥", - "provider.api_host": "API 地址", - "provider.api_version": "API 版本", - "provider.docs_check": "查看", - "provider.docs_more_details": "获取更多详情", - "provider.search_placeholder": "搜索模型 ID 或名称", - "provider.api.url.reset": "重置", - "provider.api.url.preview": "预览: {{url}}", - "provider.api.url.tip": "/结尾忽略v1版本,#结尾制使用输入地址", "models.default_assistant_model": "默认助手模型", "models.topic_naming_model": "话题命名模型", "models.translate_model": "翻译模型", @@ -357,6 +344,26 @@ "zoom_in": "放大界面", "zoom_out": "缩小界面", "zoom_reset": "重置缩放" + }, + "provider": { + "title": "模型服务", + "api_key": "API 密钥", + "api_key.tip": "多个密钥使用逗号分隔", + "check": "检查", + "get_api_key": "点击这里获取密钥", + "api_host": "API 地址", + "api_version": "API 版本", + "docs_check": "查看", + "docs_more_details": "获取更多详情", + "search_placeholder": "搜索模型 ID 或名称", + "api.url.reset": "重置", + "api.url.preview": "预览: {{url}}", + "api.url.tip": "/结尾忽略v1版本,#结尾制使用输入地址", + "check_multiple_keys": "检查多个 API 密钥", + "check_all_keys": "检查所有密钥", + "remove_invalid_keys": "删除无效密钥", + "remove_duplicate_keys": "移除重复密钥", + "not_checked": "未检查" } }, "translate": { diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index d57fee8573..73920a91cd 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -253,7 +253,6 @@ "title": "設定", "general": "一般設定", "data": "數據設定", - "provider": "模型提供者", "model": "預設模型", "assistant": "預設助手", "about": "關於與回饋", @@ -288,18 +287,6 @@ "data.webdav.restore.button": "從 WebDAV 恢復", "advanced.title": "進階設定", "advanced.click_assistant_switch_to_topics": "點擊助手切換到話題", - "provider.api_key": "API 密鑰", - "provider.api_key.tip": "多個密鑰使用逗號分隔", - "provider.check": "檢查", - "provider.get_api_key": "獲取 API 密鑰", - "provider.api_host": "API 主機地址", - "provider.api_version": "API 版本", - "provider.docs_check": "檢查", - "provider.docs_more_details": "查看更多細節", - "provider.search_placeholder": "搜尋模型 ID 或名稱", - "provider.api.url.reset": "重置", - "provider.api.url.preview": "預覽: {{url}}", - "provider.api.url.tip": "/結尾忽略v1版本,#結尾強制使用輸入位址", "models.default_assistant_model": "預設助手模型", "models.topic_naming_model": "話題命名模型", "models.translate_model": "翻譯模型", @@ -357,6 +344,26 @@ "zoom_in": "放大界面", "zoom_out": "縮小界面", "zoom_reset": "重置縮放" + }, + "provider": { + "title": "模型提供者", + "api_key": "API 密鑰", + "api_key.tip": "多個密鑰使用逗號分隔", + "check": "檢查", + "get_api_key": "獲取 API 密鑰", + "api_host": "API 主機地址", + "api_version": "API 版本", + "docs_check": "檢查", + "docs_more_details": "查看更多細節", + "search_placeholder": "搜尋模型 ID 或名稱", + "api.url.reset": "重置", + "api.url.preview": "預覽: {{url}}", + "api.url.tip": "/結尾忽略v1版本,#結尾強制使用輸入位址", + "check_multiple_keys": "檢查多個 API 密鑰", + "check_all_keys": "檢查所有密鑰", + "remove_invalid_keys": "刪除無效密鑰", + "remove_duplicate_keys": "移除重複密鑰", + "not_checked": "未檢查" } }, "translate": { diff --git a/src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx b/src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx new file mode 100644 index 0000000000..f7ab2a7267 --- /dev/null +++ b/src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx @@ -0,0 +1,138 @@ +import { CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons' +import { TopView } from '@renderer/components/TopView' +import { useTheme } from '@renderer/context/ThemeProvider' +import { checkApi } from '@renderer/services/ApiService' +import { Button, List, Modal, Space, Typography } from 'antd' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' + +interface ShowParams { + title: string + provider: any + apiKeys: string[] +} + +interface Props extends ShowParams { + resolve: (data: any) => void +} + +interface KeyStatus { + key: string + isValid?: boolean + checking?: boolean +} + +const PopupContainer: React.FC = ({ title, provider, apiKeys, resolve }) => { + const [open, setOpen] = useState(true) + const [keyStatuses, setKeyStatuses] = useState(() => { + const uniqueKeys = new Set(apiKeys) + return Array.from(uniqueKeys).map((key) => ({ key })) + }) + const { t } = useTranslation() + const { theme } = useTheme() + + const checkAllKeys = async () => { + const newStatuses = [...keyStatuses] + + for (let i = 0; i < newStatuses.length; i++) { + setKeyStatuses((prev) => prev.map((status, idx) => (idx === i ? { ...status, checking: true } : status))) + + const valid = await checkApi({ ...provider, apiKey: newStatuses[i].key }) + + setKeyStatuses((prev) => + prev.map((status, idx) => (idx === i ? { ...status, checking: false, isValid: valid } : status)) + ) + } + } + + const removeInvalidKeys = () => { + setKeyStatuses((prev) => prev.filter((status) => status.isValid !== false)) + } + + const onOk = () => { + const allKeys = keyStatuses.map((status) => status.key) + resolve({ validKeys: allKeys }) + setOpen(false) + } + + const onCancel = () => { + setOpen(false) + } + + const onClose = () => { + resolve({}) + } + + return ( + + + + + + + + + + }> + ( + + + + {status.key.slice(0, 8)}...{status.key.slice(-8)} + + + {status.checking && {t('settings.provider.check')}} + {status.isValid === true && } + {status.isValid === false && } + {status.isValid === undefined && !status.checking && {t('settings.provider.not_checked')}} + + + + )} + /> + + ) +} + +export default class ApiCheckPopup { + static topviewId = 0 + static hide() { + TopView.hide('ApiCheckPopup') + } + static show(props: ShowParams) { + return new Promise((resolve) => { + TopView.show( + { + resolve(v) + this.hide() + }} + />, + 'ApiCheckPopup' + ) + }) + } +} diff --git a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx index 18df2c87a5..6fc5294a8f 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx @@ -11,6 +11,7 @@ import { getModelLogo, isVisionModel } from '@renderer/config/models' import { PROVIDER_CONFIG } from '@renderer/config/providers' import { useTheme } from '@renderer/context/ThemeProvider' import { useProvider } from '@renderer/hooks/useProvider' +import i18n from '@renderer/i18n' import { isOpenAIProvider } from '@renderer/providers/ProviderFactory' import { checkApi } from '@renderer/services/ApiService' import { Provider } from '@renderer/types' @@ -30,6 +31,7 @@ import { SettingTitle } from '..' import AddModelPopup from './AddModelPopup' +import ApiCheckPopup from './ApiCheckPopup' import EditModelsPopup from './EditModelsPopup' import GraphRAGSettings from './GraphRAGSettings' import OllamSettings from './OllamaSettings' @@ -63,11 +65,34 @@ const ProviderSetting: FC = ({ provider: _provider }) => { const onAddModel = () => AddModelPopup.show({ title: t('settings.models.add.add_model'), provider }) const onCheckApi = async () => { - setApiChecking(true) - const valid = await checkApi({ ...provider, apiKey, apiHost }) - setApiValid(valid) - setApiChecking(false) - setTimeout(() => setApiValid(false), 3000) + if (apiKey.includes(',')) { + const keys = apiKey + .split(',') + .map((k) => k.trim()) + .filter((k) => k) + const result = await ApiCheckPopup.show({ + title: t('settings.provider.check_multiple_keys'), + provider: { ...provider, apiHost }, + apiKeys: keys + }) + + if (result?.validKeys) { + setApiKey(result.validKeys.join(',')) + updateProvider({ ...provider, apiKey: result.validKeys.join(',') }) + } + } else { + setApiChecking(true) + const valid = await checkApi({ ...provider, apiKey, apiHost }) + window.message[valid ? 'success' : 'error']({ + key: 'api-check', + style: { marginTop: '3vh' }, + duration: valid ? 2 : 8, + content: valid ? i18n.t('message.api.connection.success') : i18n.t('message.api.connection.failed') + }) + setApiValid(valid) + setApiChecking(false) + setTimeout(() => setApiValid(false), 3000) + } } const providerConfig = PROVIDER_CONFIG[provider.id] diff --git a/src/renderer/src/pages/settings/SettingsPage.tsx b/src/renderer/src/pages/settings/SettingsPage.tsx index e1bd61f921..73273cdd5b 100644 --- a/src/renderer/src/pages/settings/SettingsPage.tsx +++ b/src/renderer/src/pages/settings/SettingsPage.tsx @@ -39,7 +39,7 @@ const SettingsPage: FC = () => { - {t('settings.provider')} + {t('settings.provider.title')} diff --git a/src/renderer/src/services/ApiService.ts b/src/renderer/src/services/ApiService.ts index 9a76a4e380..6517aa1dfc 100644 --- a/src/renderer/src/services/ApiService.ts +++ b/src/renderer/src/services/ApiService.ts @@ -213,13 +213,6 @@ export async function checkApi(provider: Provider) { const { valid } = await AI.check() - window.message[valid ? 'success' : 'error']({ - key: 'api-check', - style: { marginTop: '3vh' }, - duration: valid ? 2 : 8, - content: valid ? i18n.t('message.api.connection.success') : i18n.t('message.api.connection.failed') - }) - return valid } From b23b41dfd11ae812569b0474bfed185c5911935c Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Mon, 11 Nov 2024 10:45:42 +0800 Subject: [PATCH 030/146] feat: rename images to paintings for consistency --- src/renderer/src/i18n/locales/en-us.json | 2 +- src/renderer/src/i18n/locales/zh-tw.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index a7155c0fc4..7aa5965f23 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -145,7 +145,7 @@ "stream_output": "Stream Output", "search": "Search models..." }, - "images": { + "paintings": { "title": "Images", "image.size": "Image Size", "button.new.image": "New Image", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 73920a91cd..cd04ea0eb9 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -145,7 +145,7 @@ "stream_output": "串流輸出", "search": "搜尋模型..." }, - "images": { + "paintings": { "title": "繪圖", "image.size": "影像尺寸", "button.new.image": "新繪圖", From adaaee5848d862186a5d741223a84eaa37268c3f Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Mon, 11 Nov 2024 10:49:11 +0800 Subject: [PATCH 031/146] chore(version): 0.8.12 --- electron-builder.yml | 12 +----- package.json | 2 +- src/main/index.ts | 90 ++++++++++++++++++++++---------------------- 3 files changed, 49 insertions(+), 55 deletions(-) diff --git a/electron-builder.yml b/electron-builder.yml index 8adbcd61c3..2ef53d5e2b 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -63,13 +63,5 @@ electronDownload: afterSign: scripts/notarize.js releaseInfo: releaseNotes: | - 新功能: - - 支持代码风格切换 @injurka - - 增加任务栏图标 - - 智能体新增精选分类 - - 支持 Mermaid 图表预览和下载 - - 增加多 API 密钥支持 - - 侧边栏增加提示信息 - 错误修复: - - 修复 Mermaid 图表无法显示问题 - - 修复气泡模式代码颜色问题 + 修复 Windows 窗口最小化后进入任务栏问题 + 增加 API 密钥批量检查功能 diff --git a/package.json b/package.json index 78d0dd3664..331a061042 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CherryStudio", - "version": "0.8.11", + "version": "0.8.12", "private": true, "description": "A powerful AI assistant for producer.", "main": "./out/main/index.js", diff --git a/src/main/index.ts b/src/main/index.ts index 668a9f9ac8..1ab36849ba 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -11,57 +11,59 @@ import { updateUserDataPath } from './utils/upgrade' // Check for single instance lock if (!app.requestSingleInstanceLock()) { app.quit() -} + process.exit(0) +} else { + // This method will be called when Electron has finished + // initialization and is ready to create browser windows. + // Some APIs can only be used after this event occurs. + app.whenReady().then(async () => { + await updateUserDataPath() -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. -app.whenReady().then(async () => { - await updateUserDataPath() + // Set app user model id for windows + electronApp.setAppUserModelId(import.meta.env.VITE_MAIN_BUNDLE_ID || 'com.kangfenmao.CherryStudio') - // Set app user model id for windows - electronApp.setAppUserModelId(import.meta.env.VITE_MAIN_BUNDLE_ID || 'com.kangfenmao.CherryStudio') + const mainWindow = windowService.createMainWindow() + new TrayService() - const mainWindow = windowService.createMainWindow() - new TrayService() + app.on('activate', function () { + // On macOS it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (BrowserWindow.getAllWindows().length === 0) { + windowService.createMainWindow() + } else { + windowService.showMainWindow() + } + }) - app.on('activate', function () { - // On macOS it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (BrowserWindow.getAllWindows().length === 0) { - windowService.createMainWindow() - } else { - windowService.showMainWindow() + registerZoomShortcut(mainWindow) + + registerIpc(mainWindow, app) + + if (process.env.NODE_ENV === 'development') { + installExtension(REDUX_DEVTOOLS) + .then((name) => console.log(`Added Extension: ${name}`)) + .catch((err) => console.log('An error occurred: ', err)) } }) - registerZoomShortcut(mainWindow) + // Listen for second instance + app.on('second-instance', () => { + const mainWindow = BrowserWindow.getAllWindows()[0] + if (mainWindow) { + mainWindow.isMinimized() && mainWindow.restore() + mainWindow.show() + mainWindow.focus() + } + }) - registerIpc(mainWindow, app) + app.on('browser-window-created', (_, window) => { + optimizer.watchWindowShortcuts(window) + }) - if (process.env.NODE_ENV === 'development') { - installExtension(REDUX_DEVTOOLS) - .then((name) => console.log(`Added Extension: ${name}`)) - .catch((err) => console.log('An error occurred: ', err)) - } -}) + app.on('before-quit', () => { + app.isQuitting = true + }) -// Listen for second instance -app.on('second-instance', () => { - const mainWindow = BrowserWindow.getAllWindows()[0] - if (mainWindow) { - mainWindow.isMinimized() && mainWindow.restore() - mainWindow.focus() - } -}) - -app.on('browser-window-created', (_, window) => { - optimizer.watchWindowShortcuts(window) -}) - -app.on('before-quit', () => { - app.isQuitting = true -}) - -// In this file you can include the rest of your app"s specific main process -// code. You can also put them in separate files and require them here. + // In this file you can include the rest of your app"s specific main process + // code. You can also put them in separate files and require them here. +} From 25371094065e7200b0641f0e88384c41c9bde6d9 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Mon, 11 Nov 2024 13:40:25 +0800 Subject: [PATCH 032/146] fix: message color --- src/renderer/src/pages/home/Chat.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/src/pages/home/Chat.tsx b/src/renderer/src/pages/home/Chat.tsx index ac12e2aa25..eafd2fc0d5 100644 --- a/src/renderer/src/pages/home/Chat.tsx +++ b/src/renderer/src/pages/home/Chat.tsx @@ -74,6 +74,7 @@ const Container = styled.div` padding: 10px 15px 0 15px; } .message-user { + color: var(--chat-text-user); .markdown, .anticon, .iconfont, From 7e169f8282d5203c0cd4f2da2c429b69ce35ea7d Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Mon, 11 Nov 2024 17:41:53 +0800 Subject: [PATCH 033/146] style: update bubble ui component styles --- src/renderer/src/assets/styles/index.scss | 37 +++++++++++++++++++ .../history/components/TopicMessages.tsx | 4 +- src/renderer/src/pages/home/Chat.tsx | 36 ------------------ 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/renderer/src/assets/styles/index.scss b/src/renderer/src/assets/styles/index.scss index 116ae16aa4..3049a00463 100644 --- a/src/renderer/src/assets/styles/index.scss +++ b/src/renderer/src/assets/styles/index.scss @@ -208,3 +208,40 @@ body[os='windows'] { white-space: normal; word-wrap: break-word; } + +.bubble { + background-color: var(--chat-background); + #messages { + background-color: var(--chat-background); + } + #inputbar { + border-radius: 0; + margin: 0; + border: none; + border-top: 1px solid var(--color-border-mute); + background: var(--color-background); + } + .system-prompt { + background-color: var(--chat-background-assistant); + } + .message-content-container { + margin: 5px 0; + border-radius: 8px; + padding: 10px 15px 0 15px; + } + .message-user { + color: var(--chat-text-user); + .markdown, + .anticon, + .iconfont, + .message-tokens { + color: var(--chat-text-user); + } + .message-action-button:hover { + background-color: var(--color-white-soft); + } + } + code { + color: var(--color-text); + } +} diff --git a/src/renderer/src/pages/history/components/TopicMessages.tsx b/src/renderer/src/pages/history/components/TopicMessages.tsx index d66838b392..33bd7902aa 100644 --- a/src/renderer/src/pages/history/components/TopicMessages.tsx +++ b/src/renderer/src/pages/history/components/TopicMessages.tsx @@ -1,6 +1,7 @@ import { ArrowRightOutlined, MessageOutlined } from '@ant-design/icons' import { HStack } from '@renderer/components/Layout' import useScrollPosition from '@renderer/hooks/useScrollPosition' +import { useSettings } from '@renderer/hooks/useSettings' import { getAssistantById } from '@renderer/services/AssistantService' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { locateToMessage } from '@renderer/services/MessagesService' @@ -20,6 +21,7 @@ interface Props extends React.HTMLAttributes { const TopicMessages: FC = ({ topic, ...props }) => { const navigate = useNavigate() const { handleScroll, containerRef } = useScrollPosition('TopicMessages') + const { messageStyle } = useSettings() const isEmpty = (topic?.messages || []).length === 0 @@ -34,7 +36,7 @@ const TopicMessages: FC = ({ topic, ...props }) => { } return ( - + {topic?.messages.map((message) => (
diff --git a/src/renderer/src/pages/home/Chat.tsx b/src/renderer/src/pages/home/Chat.tsx index eafd2fc0d5..3f9a543820 100644 --- a/src/renderer/src/pages/home/Chat.tsx +++ b/src/renderer/src/pages/home/Chat.tsx @@ -53,42 +53,6 @@ const Container = styled.div` flex: 1; justify-content: space-between; background-color: var(--color-background); - &.bubble { - background-color: var(--chat-background); - #messages { - background-color: var(--chat-background); - } - #inputbar { - border-radius: 0; - margin: 0; - border: none; - border-top: 1px solid var(--color-border-mute); - background: var(--color-background); - } - .system-prompt { - background-color: var(--chat-background-assistant); - } - .message-content-container { - margin: 5px 0; - border-radius: 8px; - padding: 10px 15px 0 15px; - } - .message-user { - color: var(--chat-text-user); - .markdown, - .anticon, - .iconfont, - .message-tokens { - color: var(--chat-text-user); - } - .message-action-button:hover { - background-color: var(--color-white-soft); - } - } - code { - color: var(--color-text); - } - } ` const Main = styled(Flex)` From b39d33c6acc3afb7a83cabd49268ed9eff87af99 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Mon, 11 Nov 2024 17:57:48 +0800 Subject: [PATCH 034/146] feat: add loading indicator and disable check button while checking api keys --- .../ProviderSettings/ApiCheckPopup.tsx | 67 ++++++++++++------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx b/src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx index f7ab2a7267..b9f6c7d9e1 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ApiCheckPopup.tsx @@ -1,8 +1,9 @@ -import { CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons' +import { CheckCircleFilled, CloseCircleFilled, LoadingOutlined } from '@ant-design/icons' +import Scrollbar from '@renderer/components/Scrollbar' import { TopView } from '@renderer/components/TopView' import { useTheme } from '@renderer/context/ThemeProvider' import { checkApi } from '@renderer/services/ApiService' -import { Button, List, Modal, Space, Typography } from 'antd' +import { Button, List, Modal, Space, Spin, Typography } from 'antd' import { useState } from 'react' import { useTranslation } from 'react-i18next' @@ -30,18 +31,24 @@ const PopupContainer: React.FC = ({ title, provider, apiKeys, resolve }) }) const { t } = useTranslation() const { theme } = useTheme() + const [isChecking, setIsChecking] = useState(false) const checkAllKeys = async () => { + setIsChecking(true) const newStatuses = [...keyStatuses] - for (let i = 0; i < newStatuses.length; i++) { - setKeyStatuses((prev) => prev.map((status, idx) => (idx === i ? { ...status, checking: true } : status))) + try { + for (let i = 0; i < newStatuses.length; i++) { + setKeyStatuses((prev) => prev.map((status, idx) => (idx === i ? { ...status, checking: true } : status))) - const valid = await checkApi({ ...provider, apiKey: newStatuses[i].key }) + const valid = await checkApi({ ...provider, apiKey: newStatuses[i].key }) - setKeyStatuses((prev) => - prev.map((status, idx) => (idx === i ? { ...status, checking: false, isValid: valid } : status)) - ) + setKeyStatuses((prev) => + prev.map((status, idx) => (idx === i ? { ...status, checking: false, isValid: valid } : status)) + ) + } + } finally { + setIsChecking(false) } } @@ -85,7 +92,7 @@ const PopupContainer: React.FC = ({ title, provider, apiKeys, resolve }) - @@ -109,18 +111,9 @@ const PopupContainer: React.FC = ({ resolve, chart }) => { key: 'preview', label: t('mermaid.tabs.preview'), children: ( -
+ {chart} -
+ ) }, { @@ -163,3 +156,9 @@ export default class MermaidPopup { }) } } + +const StyledMermaid = styled.div` + max-height: calc(80vh - 200px); + text-align: center; + overflow-y: auto; +` From a0d60e6a9bb21b1299b65c5533a09294d197c69e Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Tue, 12 Nov 2024 00:25:57 +0800 Subject: [PATCH 041/146] feat: improved tray functionality and ui theme settings --- src/main/services/ConfigManager.ts | 2 +- src/main/services/TrayService.ts | 5 ++++- src/main/services/WindowService.ts | 9 ++++----- src/main/utils/is-tiling-window-manager.ts | 6 +++++- src/renderer/src/i18n/locales/en-us.json | 2 +- src/renderer/src/i18n/locales/zh-cn.json | 2 +- src/renderer/src/i18n/locales/zh-tw.json | 2 +- src/renderer/src/store/index.ts | 2 +- src/renderer/src/store/migrate.ts | 4 ++++ src/renderer/src/store/settings.ts | 1 + 10 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/main/services/ConfigManager.ts b/src/main/services/ConfigManager.ts index ab7f4404cc..2e848370cb 100644 --- a/src/main/services/ConfigManager.ts +++ b/src/main/services/ConfigManager.ts @@ -27,7 +27,7 @@ export class ConfigManager { } isTray(): boolean { - return !!this.store.get('tray', false) + return !!this.store.get('tray', true) } setTray(value: boolean) { diff --git a/src/main/services/TrayService.ts b/src/main/services/TrayService.ts index 80011c156e..0ddadd8988 100644 --- a/src/main/services/TrayService.ts +++ b/src/main/services/TrayService.ts @@ -58,7 +58,10 @@ export class TrayService { } ]) - this.tray.setContextMenu(contextMenu) + if (process.platform === 'linux') { + this.tray.setContextMenu(contextMenu) + } + this.tray.setToolTip('Cherry Studio') this.tray.on('right-click', () => { diff --git a/src/main/services/WindowService.ts b/src/main/services/WindowService.ts index 4b0de75335..0738460986 100644 --- a/src/main/services/WindowService.ts +++ b/src/main/services/WindowService.ts @@ -166,11 +166,10 @@ export class WindowService { } private setupWindowLifecycleEvents(mainWindow: BrowserWindow) { + if (!configManager.isTray() && isTilingWindowManager()) { + app.quit() + } mainWindow.on('close', (event) => { - if (!configManager.isTray() && isTilingWindowManager()) { - app.quit() - } - if (!app.isQuitting) { event.preventDefault() mainWindow.hide() @@ -181,7 +180,7 @@ export class WindowService { public showMainWindow() { if (this.mainWindow) { if (this.mainWindow.isMinimized()) { - this.mainWindow.restore() + return this.mainWindow.restore() } this.mainWindow.show() this.mainWindow.focus() diff --git a/src/main/utils/is-tiling-window-manager.ts b/src/main/utils/is-tiling-window-manager.ts index c481d158e2..7382ddbcd1 100644 --- a/src/main/utils/is-tiling-window-manager.ts +++ b/src/main/utils/is-tiling-window-manager.ts @@ -1,8 +1,12 @@ function isTilingWindowManager() { - if (process.platform !== 'linux') { + if (process.platform === 'darwin') { return false } + if (process.platform !== 'linux') { + return true + } + const desktopEnv = process.env.XDG_CURRENT_DESKTOP?.toLowerCase() const tilingSystems = ['hyprland', 'i3', 'sway', 'bspwm', 'dwm', 'awesome', 'qtile', 'herbstluftwm', 'xmonad'] diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index efe83d5221..d36ab1f0c4 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -336,7 +336,7 @@ "about.license.button": "License", "about.contact.button": "Email", "proxy.title": "Proxy Address", - "tray.title": "Minimize to tray instead of closing", + "tray.title": "Show Tray Icon", "theme.title": "Theme", "theme.dark": "Dark", "theme.light": "Light", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 7625d71329..e0812ba551 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -324,7 +324,7 @@ "about.license.button": "查看", "about.contact.button": "邮件", "proxy.title": "代理地址", - "tray.title": "最小化到托盘而不是关闭", + "tray.title": "显示任务栏图标", "theme.title": "主题", "theme.dark": "深色主题", "theme.light": "浅色主题", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index b9e7eed75b..de359b2cb8 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -324,7 +324,7 @@ "about.license.button": "查看", "about.contact.button": "郵件", "proxy.title": "代理地址", - "tray.title": "最小化到系統列而不是關閉", + "tray.title": "顯示工作列圖示", "theme.title": "主題", "theme.dark": "深色主題", "theme.light": "淺色主題", diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index f628d93bdf..43ee7ac1ae 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -24,7 +24,7 @@ const persistedReducer = persistReducer( { key: 'cherry-studio', storage, - version: 39, + version: 40, blacklist: ['runtime'], migrate }, diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index 09b1afb7c3..6b2705391f 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -673,6 +673,10 @@ const migrateConfig = { '39': (state: RootState) => { state.settings.codeStyle = 'auto' return state + }, + '40': (state: RootState) => { + state.settings.tray = true + return state } } diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index a9165af00b..b23d4a6e84 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -44,6 +44,7 @@ const initialState: SettingsState = { showMessageDivider: false, messageFont: 'system', showInputEstimatedTokens: false, + tray: true, theme: ThemeMode.auto, windowStyle: 'transparent', fontSize: 14, From ade4780c0ee88f1c9470f8e6aca85e502439a837 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Tue, 12 Nov 2024 00:37:23 +0800 Subject: [PATCH 042/146] feat: update translations and display settings --- src/renderer/src/i18n/locales/en-us.json | 3 +- src/renderer/src/i18n/locales/zh-cn.json | 3 +- src/renderer/src/i18n/locales/zh-tw.json | 3 +- .../src/pages/home/Inputbar/Inputbar.tsx | 4 -- src/renderer/src/pages/home/Tabs/Settings.tsx | 47 +++++++++++++++++-- .../src/pages/settings/GeneralSettings.tsx | 38 +-------------- 6 files changed, 52 insertions(+), 46 deletions(-) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index d36ab1f0c4..edaeb2fdae 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -336,7 +336,7 @@ "about.license.button": "License", "about.contact.button": "Email", "proxy.title": "Proxy Address", - "tray.title": "Show Tray Icon", + "tray.title": "Tray Icon", "theme.title": "Theme", "theme.dark": "Dark", "theme.light": "Light", @@ -349,6 +349,7 @@ "topic.position.left": "Left", "topic.position.right": "Right", "topic.show.time": "Show Topic Time", + "display.title": "Display Settings", "shortcuts": { "title": "Keyboard Shortcuts", "action": "Action", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index e0812ba551..c04fa75426 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -324,7 +324,7 @@ "about.license.button": "查看", "about.contact.button": "邮件", "proxy.title": "代理地址", - "tray.title": "显示任务栏图标", + "tray.title": "任务栏图标", "theme.title": "主题", "theme.dark": "深色主题", "theme.light": "浅色主题", @@ -337,6 +337,7 @@ "topic.position.left": "左侧", "topic.position.right": "右侧", "topic.show.time": "显示话题时间", + "display.title": "显示设置", "shortcuts": { "title": "快捷方式", "action": "操作", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index de359b2cb8..24bb0c915b 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -324,7 +324,7 @@ "about.license.button": "查看", "about.contact.button": "郵件", "proxy.title": "代理地址", - "tray.title": "顯示工作列圖示", + "tray.title": "工作列圖示", "theme.title": "主題", "theme.dark": "深色主題", "theme.light": "淺色主題", @@ -337,6 +337,7 @@ "topic.position.left": "左側", "topic.position.right": "右側", "topic.show.time": "顯示話題時間", + "display.title": "顯示設定", "shortcuts": { "title": "快速方式", "action": "操作", diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index 85d528bc42..756e4c6268 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -399,13 +399,9 @@ const Textarea = styled(TextArea)` overflow: auto; width: 100%; box-sizing: border-box; - color: var(--color-text); &.ant-input { line-height: 1.4; } - &::placeholder { - color: var(--color-text-3); - } ` const Toolbar = styled.div` diff --git a/src/renderer/src/pages/home/Tabs/Settings.tsx b/src/renderer/src/pages/home/Tabs/Settings.tsx index b6cabfc03f..b8a03805c4 100644 --- a/src/renderer/src/pages/home/Tabs/Settings.tsx +++ b/src/renderer/src/pages/home/Tabs/Settings.tsx @@ -8,6 +8,7 @@ import { useSettings } from '@renderer/hooks/useSettings' import { SettingDivider, SettingRow, SettingRowTitle, SettingSubtitle } from '@renderer/pages/settings' import { useAppDispatch } from '@renderer/store' import { + setClickAssistantToShowTopic, setCodeShowLineNumbers, setCodeStyle, setFontSize, @@ -17,7 +18,8 @@ import { setPasteLongTextAsFile, setRenderInputMessageAsMarkdown, setShowInputEstimatedTokens, - setShowMessageDivider + setShowMessageDivider, + setShowTopicTime } from '@renderer/store/settings' import { Assistant, AssistantSettings } from '@renderer/types' import { Col, Row, Select, Slider, Switch, Tooltip } from 'antd' @@ -52,7 +54,11 @@ const SettingsTab: FC = (props) => { pasteLongTextAsFile, renderInputMessageAsMarkdown, codeShowLineNumbers, - mathEngine + mathEngine, + topicPosition, + showTopicTime, + clickAssistantToShowTopic, + setTopicPosition } = useSettings() const onUpdateAssistantSettings = (settings: Partial) => { @@ -223,7 +229,7 @@ const SettingsTab: FC = (props) => { + + + {topicPosition === 'left' && ( + <> + + {t('settings.advanced.click_assistant_switch_to_topics')} + dispatch(setClickAssistantToShowTopic(checked))} + /> + + + + )} + + {t('settings.topic.show.time')} + dispatch(setShowTopicTime(checked))} /> + + ) } diff --git a/src/renderer/src/pages/settings/GeneralSettings.tsx b/src/renderer/src/pages/settings/GeneralSettings.tsx index fa14b76ab2..f22baa1e59 100644 --- a/src/renderer/src/pages/settings/GeneralSettings.tsx +++ b/src/renderer/src/pages/settings/GeneralSettings.tsx @@ -2,7 +2,7 @@ import { isMac } from '@renderer/config/constant' import { useSettings } from '@renderer/hooks/useSettings' import i18n from '@renderer/i18n' import { useAppDispatch } from '@renderer/store' -import { setClickAssistantToShowTopic, setLanguage, setShowTopicTime } from '@renderer/store/settings' +import { setLanguage } from '@renderer/store/settings' import { setProxyUrl as _setProxyUrl } from '@renderer/store/settings' import { LanguageVarious, ThemeMode } from '@renderer/types' import { isValidProxyUrl } from '@renderer/utils' @@ -21,11 +21,7 @@ const GeneralSettings: FC = () => { setTray, tray, windowStyle, - topicPosition, - showTopicTime, - clickAssistantToShowTopic, - setWindowStyle, - setTopicPosition + setWindowStyle } = useSettings() const [proxyUrl, setProxyUrl] = useState(storeProxyUrl) @@ -115,41 +111,11 @@ const GeneralSettings: FC = () => { /> - - {t('settings.topic.position')} - dispatch(setCodeStyle(value))} - style={{ width: 150 }} + style={{ width: 135 }} size="small"> {codeThemes.map((theme) => ( @@ -244,7 +101,7 @@ const SettingsTab: FC = (props) => { dispatch(setMathEngine(value))} - style={{ width: 100 }} + style={{ width: 135 }} size="small"> KaTeX MathJax @@ -323,7 +180,7 @@ const SettingsTab: FC = (props) => { { value: 'Shift+Enter', label: `Shift + Enter` } ]} onChange={(value) => setSendMessageShortcut(value)} - style={{ width: 100 }} + style={{ width: 135 }} /> @@ -333,7 +190,7 @@ const SettingsTab: FC = (props) => { {t('settings.topic.position')} { ) } +interface Props { + resolve: (data: any) => void +} + +const PopupContainer: React.FC = ({ resolve }) => { + const [open, setOpen] = useState(true) + const { t } = useTranslation() + + const onOk = () => { + setOpen(false) + } + + const onCancel = () => { + setOpen(false) + } + + const onClose = () => { + resolve({}) + } + + return ( + + + + ) +} + +export default class AssistantSettingsPopup { + static topviewId = 0 + static hide() { + TopView.hide('AssistantSettingsPopup') + } + static show() { + return new Promise((resolve) => { + TopView.show( + { + resolve(v) + this.hide() + }} + />, + 'AssistantSettingsPopup' + ) + }) + } +} + const Label = styled.p` margin: 0; font-size: 14px; @@ -213,5 +267,3 @@ const QuestionIcon = styled(QuestionCircleOutlined)` cursor: pointer; color: var(--color-text-3); ` - -export default AssistantSettings diff --git a/src/renderer/src/pages/settings/ModelSettings.tsx b/src/renderer/src/pages/settings/ModelSettings.tsx index 6f29c3f940..413c34082d 100644 --- a/src/renderer/src/pages/settings/ModelSettings.tsx +++ b/src/renderer/src/pages/settings/ModelSettings.tsx @@ -1,14 +1,16 @@ -import { EditOutlined, MessageOutlined, TranslationOutlined } from '@ant-design/icons' +import { EditOutlined, MessageOutlined, SettingOutlined, TranslationOutlined } from '@ant-design/icons' +import { HStack } from '@renderer/components/Layout' import { useDefaultModel } from '@renderer/hooks/useAssistant' import { useProviders } from '@renderer/hooks/useProvider' import { getModelUniqId, hasModel } from '@renderer/services/ModelService' import { Model } from '@renderer/types' -import { Select } from 'antd' +import { Button, Select } from 'antd' import { find, sortBy } from 'lodash' import { FC, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { SettingContainer, SettingDivider, SettingTitle } from '.' +import AssistantSettingsPopup from './AssistantSettings' const ModelSettings: FC = () => { const { defaultModel, topicNamingModel, translateModel, setDefaultModel, setTopicNamingModel, setTranslateModel } = @@ -52,14 +54,17 @@ const ModelSettings: FC = () => {
- setDefaultModel(find(allModels, JSON.parse(value)) as Model)} + options={selectOptions} + placeholder={t('settings.models.empty')} + /> +