From 50f08124d76cf69b776032573ee7f59659426b39 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Mon, 29 Jul 2024 17:14:49 +0800 Subject: [PATCH] feat: add dark and light theme --- package.json | 1 + src/main/config.ts | 15 ++ src/main/index.ts | 17 +- src/preload/index.d.ts | 1 + src/preload/index.ts | 3 +- src/renderer/index.html | 4 +- src/renderer/src/App.tsx | 40 ++-- .../src/assets/fonts/icon-fonts/iconfont.css | 14 +- .../src/assets/fonts/icon-fonts/iconfont.ttf | Bin 3992 -> 4372 bytes .../src/assets/fonts/icon-fonts/iconfont.woff | Bin 2444 -> 2708 bytes .../assets/fonts/icon-fonts/iconfont.woff2 | Bin 1952 -> 2204 bytes src/renderer/src/assets/styles/index.scss | 74 ++++++- src/renderer/src/assets/styles/markdown.scss | 10 +- src/renderer/src/assets/styles/scrollbar.scss | 4 +- src/renderer/src/components/app/Sidebar.tsx | 8 +- src/renderer/src/config/antd.ts | 26 --- src/renderer/src/hooks/useSettings.ts | 10 +- src/renderer/src/i18n/index.ts | 12 +- src/renderer/src/init.ts | 5 +- src/renderer/src/pages/apps/AppsPage.tsx | 10 +- src/renderer/src/pages/home/HomePage.tsx | 17 ++ .../src/pages/home/components/Assistants.tsx | 10 +- .../src/pages/home/components/CodeBlock.tsx | 31 ++- .../src/pages/home/components/Inputbar.tsx | 4 +- .../src/pages/home/components/Message.tsx | 41 ++-- .../home/components/NavigationCenter.tsx | 14 +- .../pages/home/components/RightSidebar.tsx | 4 +- .../src/pages/home/components/TopicsTab.tsx | 5 +- .../src/pages/settings/GeneralSettings.tsx | 18 +- .../src/pages/settings/ProviderSettings.tsx | 17 +- .../src/pages/settings/SettingsPage.tsx | 9 +- .../settings/components/EditModelsPopup.tsx | 7 +- .../settings/components/ProviderSetting.tsx | 14 +- src/renderer/src/providers/AntdProvider.tsx | 37 ++++ src/renderer/src/providers/ThemeProvider.tsx | 43 ++++ src/renderer/src/store/index.ts | 2 +- src/renderer/src/store/migrate.ts | 9 + src/renderer/src/store/settings.ts | 16 +- src/renderer/src/utils/index.ts | 11 +- yarn.lock | 207 +++++++++++++++++- 40 files changed, 611 insertions(+), 159 deletions(-) create mode 100644 src/main/config.ts delete mode 100644 src/renderer/src/config/antd.ts create mode 100644 src/renderer/src/providers/AntdProvider.tsx create mode 100644 src/renderer/src/providers/ThemeProvider.tsx diff --git a/package.json b/package.json index 9cfa617f..f4369a7d 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@electron-toolkit/utils": "^3.0.0", "@sentry/electron": "^5.2.0", "electron-log": "^5.1.5", + "electron-store": "^8.2.0", "electron-updater": "^6.1.7", "electron-window-state": "^5.0.3" }, diff --git a/src/main/config.ts b/src/main/config.ts new file mode 100644 index 00000000..9219d16d --- /dev/null +++ b/src/main/config.ts @@ -0,0 +1,15 @@ +import Store from 'electron-store' + +export const appConfig = new Store() + +export const titleBarOverlayDark = { + height: 41, + color: '#1f1f1f', + symbolColor: '#ffffff' +} + +export const titleBarOverlayLight = { + height: 41, + color: '#f8f8f8', + symbolColor: '#000000' +} diff --git a/src/main/index.ts b/src/main/index.ts index e9a3ed82..96190795 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -5,8 +5,9 @@ import installExtension, { REDUX_DEVTOOLS } from 'electron-devtools-installer' import windowStateKeeper from 'electron-window-state' import { join } from 'path' import icon from '../../resources/icon.png?asset' -import AppUpdater from './updater' +import { appConfig, titleBarOverlayDark, titleBarOverlayLight } from './config' import { saveFile } from './event' +import AppUpdater from './updater' function createWindow() { // Load the previous state with fallback to defaults @@ -15,6 +16,8 @@ function createWindow() { defaultHeight: 670 }) + const theme = appConfig.get('theme') || 'light' + // Create the browser window. const mainWindow = new BrowserWindow({ x: mainWindowState.x, @@ -26,11 +29,7 @@ function createWindow() { show: true, autoHideMenuBar: true, titleBarStyle: 'hidden', - titleBarOverlay: { - height: 41, - color: '#1f1f1f', - symbolColor: '#eee' - }, + titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight, trafficLightPosition: { x: 8, y: 12 }, ...(process.platform === 'linux' ? { icon } : {}), webPreferences: { @@ -118,6 +117,12 @@ app.whenReady().then(() => { ipcMain.handle('save-file', saveFile) + ipcMain.handle('set-theme', (_, theme: 'light' | 'dark') => { + appConfig.set('theme', theme) + mainWindow?.setTitleBarOverlay && + mainWindow.setTitleBarOverlay(theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight) + }) + // 触发检查更新(此方法用于被渲染线程调用,例如页面点击检查更新按钮来调用此方法) ipcMain.handle('check-for-update', async () => { autoUpdater.logger?.info('触发检查更新') diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index a08c333c..598056eb 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -12,6 +12,7 @@ declare global { openWebsite: (url: string) => void setProxy: (proxy: string | undefined) => void saveFile: (path: string, content: string) => void + setTheme: (theme: 'light' | 'dark') => void } } } diff --git a/src/preload/index.ts b/src/preload/index.ts index 8f1cb7bf..1b3a34f7 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -7,7 +7,8 @@ const api = { checkForUpdate: () => ipcRenderer.invoke('check-for-update'), openWebsite: (url: string) => ipcRenderer.invoke('open-website', url), setProxy: (proxy: string) => ipcRenderer.invoke('set-proxy', proxy), - saveFile: (path: string, content: string) => ipcRenderer.invoke('save-file', path, content) + saveFile: (path: string, content: string) => ipcRenderer.invoke('save-file', path, content), + setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke('set-theme', theme) } // Use `contextBridge` APIs to expose Electron APIs to diff --git a/src/renderer/index.html b/src/renderer/index.html index b425090a..8c6fbec9 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -4,13 +4,11 @@ Cherry Studio - - - +
diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 4e6ede0f..010e014e 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1,35 +1,37 @@ import store, { persistor } from '@renderer/store' -import { ConfigProvider } from 'antd' import { Provider } from 'react-redux' import { HashRouter, Route, Routes } from 'react-router-dom' import { PersistGate } from 'redux-persist/integration/react' import Sidebar from './components/app/Sidebar' import TopViewContainer from './components/TopView' -import { AntdThemeConfig, getAntdLocale } from './config/antd' import AppsPage from './pages/apps/AppsPage' import HomePage from './pages/home/HomePage' import SettingsPage from './pages/settings/SettingsPage' import TranslatePage from './pages/translate/TranslatePage' +import AntdProvider from './providers/AntdProvider' +import { ThemeProvider } from './providers/ThemeProvider' function App(): JSX.Element { return ( - - - - - - - - } /> - } /> - } /> - } /> - - - - - - + + + + + + + + + } /> + } /> + } /> + } /> + + + + + + + ) } diff --git a/src/renderer/src/assets/fonts/icon-fonts/iconfont.css b/src/renderer/src/assets/fonts/icon-fonts/iconfont.css index 098b60a9..95abc881 100644 --- a/src/renderer/src/assets/fonts/icon-fonts/iconfont.css +++ b/src/renderer/src/assets/fonts/icon-fonts/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 4563475 */ - src: url('iconfont.woff2?t=1722099305424') format('woff2'), - url('iconfont.woff?t=1722099305424') format('woff'), - url('iconfont.ttf?t=1722099305424') format('truetype'); + src: url('iconfont.woff2?t=1722242729348') format('woff2'), + url('iconfont.woff?t=1722242729348') format('woff'), + url('iconfont.ttf?t=1722242729348') format('truetype'); } .iconfont { @@ -13,6 +13,14 @@ -moz-osx-font-smoothing: grayscale; } +.icon-dark1:before { + content: "\e72f"; +} + +.icon-theme-light:before { + content: "\e6b7"; +} + .icon-translate_line:before { content: "\e7de"; } diff --git a/src/renderer/src/assets/fonts/icon-fonts/iconfont.ttf b/src/renderer/src/assets/fonts/icon-fonts/iconfont.ttf index 64e9a4e360b19475e3b6efe0464e752e4ef4b3d2..5ea01e3541ccddf30476e0a8fb03934c0ed1f56a 100644 GIT binary patch delta 896 zcmZuuUr19?82`R|ue)>Cw#7E*lgj)v6}vi}8|CIpyCAWtMNYG%R!%2vDYc0~xIIdO zq>JbwiU=$cB$1H9hp>m9>am9ik&0c#k_dVzXzM$>L=SZizwdnKJKy*He&?LqRW}_s zTGKUmO&io)`thZGMjY4MJ@!k(_QO z2LL~lX^u}^h#1b7eWUt5+E*kZ)00>Q%XCitq54uJ5j8*XJpnKZ#5XoMapgeL|LML6 zV2)G5(!xf~Q3;S-f;Ihe|E$UP9VAMo@!_@D_;)R21mrWc*8|6*G+3Pk_rQ>K;Qk_~ z&zd<)|4K0o@Cf*#*G#K{a#uhAx+s)`nIgKJEjO53S6kFUbzWUmw|4e+1BC*>|Kr(J zw6!4B{yXX2KqN^>{S73JhZ4{cpDw$~{mj1LO4DUbE6f%xFfBQo2C9VIpa$vzxpfWf zf@+Hfb_bPm7>EJXc@3&U&}kzYLHN87o7*~UZ;ia9Y!Qcq{uBJ?~u{mg>98+H6qKdqb#fr`a{n$8GIZL z$mF3o=XIAN>shXV1m} delta 521 zcmZvXy-Nc@5XIl@UCiZFJT-xUf}$1{7O6x;5K;UCQUxp|_>mNe2DH#BX|)K^q!1eg zOB+E%gdl=l8m&@T1hL2(6hXnlD!%gs6`bYA&dko6*(nX|^%Wx7i_WPxKsm%tq&Anw z_1z(0MgS|5T#7FapS|7yxF@4YCj9rt7X30AN~YI0hUftD?8d3pg}A?Jo7DF*4yNN9 z8R@|>-@}aiSK{d<=V;;tXglP*L}qoZef|1&I|_7IEGX=h2d4uNx+L~G**4!X@s3tD zq&-xsji0-jGsVa;wxHZB&D8~P0IN74FU7Fx&hCHZfD()^Z-lqa!hM)XvbqZnGpZh{ z$8=2R^{%cxJ~d{VP2j(rJLH!M^?wiGhmZfU(876!f_ZwS+9jL$R&mLC$b*%r$2_Qk zrtjizqw^l@1HJ3Pj?gs^cbCT}5B?vG84qsd*`+v=lQ$Jr6=T*MJ1zM>>|j8Y7-EV~ PTEs7w1jI)1+?o3XHIsDl diff --git a/src/renderer/src/assets/fonts/icon-fonts/iconfont.woff b/src/renderer/src/assets/fonts/icon-fonts/iconfont.woff index 44baa8be302ee8de0a55aace28f2e8013b729d42..d20c02b0ad95b76fb56ede814b88ea9e2afcba71 100644 GIT binary patch delta 2201 zcmV;K2xj+;6OpNKmY&$F8}}lHYmDMPiSamVE_OKZ~y=RAOHXWBnRLF zi)d|hcmMzhkN^Mx4*&oFEGz&30Bmn#VE_OKpa1{>7ytkO7z$zx@@-*wZ~y=ZxBvhE z9RL6TAORmL0IP0cZDjxe2-pAs0YCr%0%s7TJg{(Yb94Xz3K#$Y0DJ%d0H!_Up$C%) z0fT>%!41MN3`KuvnnJ~a3rvC3B|1cgVE|4&bP7&fyG@UAs}51VLk-bU;eh#}e{wvP;er{Ql!?Rjx^cHWSt4Id zm8S00uyMr)1J-_;Do^6}8t$nsUt=S#Emw_t=D_@cj47H|J}XU?VBKi_XZn97O(OxN z&_VPvdK0~at|63=C8dn>io}K4YKu6lQuu7ij_9)40%zD#Jc(#P+v>u|u+&(`sPTbLKQ$8sl``6AZ?K za#3Ox{E$h{rfrVE&x#4Q@ZEnF!Zf{Ob976#{_py2u~@s!_)RM`rJ`I5%E3Bl76z&{ z&_=_IRI7P|L2oVH3hH4_5Sk(o+f2q1rG#u28-{6(%kG%l;a*s9x4Wb6k1t-tH5N9_ z!ll`i@}Qh%4QB8Ln@v+O{1Djtb=sr|Wl$dQA~A6Ki( zhu=i5Qhu{9`T z2BFPte67AY{7Fp|=!zhU!jrln;`sYHa1oa0UQdzYjP|qb`&~qkuH|DFsbBKcyc~+!q5{LQZgH#!S2ov7N|ZpQ^~Q zR?e^o0Y7QffE_K~Y*ap4TWPTiKfKZ5weuY=xoA7-@EV9QfB=sO+86*tG+6}|oMt>^ zthwQ^Sp5#dsJeevI@J;M;wE!KHZzF}CM= zj@y9eGGrLTEl@CT3bA!{2!SZ(buqAYNx)4laDYd6>7IYB5OnqY($L7r(9-#WVhI98qOa-qUi~_vukPB{I9I8Ye$Y7kKyp-VDR%DijI^^rxzbDo-UP(IjhAb zcD%iJwEEIp%rH{O)#nzAulIFd$hdN>u{u}J6^tgkSa%l4+X8w2Kv(EEs-t(&b##Nh zp^yha5a@r^i!<2OQmU-*vJ+YyuPPiUD3CfQh`bX62ctNGhg%9-*KViH$i5%n|cECQf@@ zNQ8f>)dsGl&38-8h{+LEj)lT**Ga4Oq{|%%NmBBKWT4d{g(7a}DU0<4bOd8ka^%&G zRe@NKRoKeli2nkG55`mg0C=2ZU}Rum0Ai8)!|&qxZN4&avoL_bqqFRq2>O2m3nOy_ zkjufq1QG=RBPI#N0C=2ZU}RumZusB85W|y+2MtXF0CD*Qb9kI%VPIfjArb=s1WW)e z00000001-qmH|`&rUDiMhyv&XU<2F)&Ul<-U}Rum;AWW5z{LOpOhC*9X8#BC82}mJ z0m}dYv)>0t0d~SL5JhKdYuZMn7wN(i$!LboU`$FfAbNT&Zd~}_FW!d-4SX5lKOP(0 zVT&E^(P57Rj_C0KU7&J1c@w_#9CZ;pTA@@U8K0vjcB0jCc}}8%>t`1#l8e5kD*5qy b3^Y>+S0U?ZUAKta&0J+OO$)#q>?kO$`jqJK delta 1925 zcmV;02YUFF6^s)UcTYw}00961000S$01E&B000k|krYdRmH+?(3b_$M@@H&$W&i*J zAOHXbEC2uqa60GOplD@bWB>pMOaK4?F8}}lHYm06&uD06VE_OJd;kCdAOHXWBnRLF ziD+$fcmMzgoB#j-4gdfEBqRU;0Bmn#VE_OJtN;K26aWAK6bIG{1Z`n>Z~y=YzyJUM z9{>OVAORaH0IP0cZDjxe2jBnz0YCr%0%s7TJg{(Yb94Xz2_OIf0BisN0FcAbic*sZ z0fT>$%?-jZ428cmp{)=Hgv1t%(IGMdM<(f^;>2)0)~z~3v12a$Y=EOLl3!xUdJotE zQMo7y!h?X-UDZ(Z$iC=_tM(_3s#Mc?%95p*LB@Gqx^07B+D8SQe?QRZ4izo6zQ(dc z{WbT0btw1k)wSlXqkb`E$I_GRsE0`J*nfW+9Q73$9ZPgx9ZP-b9MTh&$TZ*p0C=3O zRm*Q1MHrvQ?v9gPqNxk+ujpGMSQc6>3Q)HK>?}}C-Dut>@NT{gh zhQtMgdH|_CRH%oz2P9q>Bsg&Bja=Fb{{ccOpdK5CZ`NsD1A-#4JDTq?-+Z&bneTr$ zix@(iKhiI#g=92=4x^XR+vq*?0YX__Gpe{~Xi@^pNnF7Nr)XeZljIoaF>uBT4g};e zEgJCZlUOq>Z0Z55Njj5Z8kLgRsbgD7;uH<&W{K=vId;@8c5c%rrD96a+2 zOV#PpM76Z={7bJvK&fOOJ@)Z8$?Fsq?W4;px1~Ev`{r^J6S=v4x41HqD^80Mp8H?OSHAo&L7G{BWteQ$YlzJ3|ZrS4@DHkRFq#r z>S)KO#eE&$8zEn3RMkL>cLtO4ZW7upWH#E5r$1^bGF_7uMSj$l6?}g$?oQ$zf8AtS z{C~oSpN7Pq2K!9-@WlbDj|N!(BftTw&HyW~Gbpgldr)T<8b>yogG$q%P|&6G6Z!$g zBWa)|U`hgZx87=eC-OLA8hW~q8TKF+C7l+qqtBZy<&%xIKD+RPo1Lok-H@;_%Tkowiub1H|=qq>c;va_X-e>Wfv5NI?);zZ-k1Lp zfcl*Kj^3vYlt2!uqy1X2h8%*uaCY;Z}yO)X1MkB>NMcdkBZ8tLo=N zf;OtN=T{!CoS&^$Y*7d(qwnsWTX^9e?r4&tDs)a&w#b+`d; z0dRj$*XVxKM(=;4>*yw+HUS5eAkb?P=W(DfOx<9*A1sNPh6Dl{K$m2N`7v-Xg_F46 z7ho0Y@cRKm!p6Ygh&RLJ+*DvTlLADVB#@OEX4&M#H6gI<)QOWKkf9t?SQZ!oe1M-5 z@IY}l4>pnBXo_N!i4>0)35$4->~@00KSB>3CL?|lbasCq@xqQ4$FU%Knu5^uh{A9r z$sIQ7a3~c&6a?u&Fn%(X*=2-xTH&mfNu5$dixT)3gYi?TjAewZond2FCUsH`FZO<) zTAG|(I>WnE`b4vNqKW;}iJ8Q-Bzx(>8i5Z?WTP!u#}93!70o6DiILht>XoIv+h?{< zv&aZ}u}XhDJW%F=m@!QJ-nN`gP|*XSWF&H1$t$^rsi)(KVBoAMo(%+(aZSrTmy7iW zw0JV;KPQN1U_ec4xyGwoUj^M}y2-x`j`$Crd4Ps^oMT{QU|;~^h>!L>@%%Pl8Ms*( zK;Y4-YcCP>{{|LD<^~{_gMkSo3II=h3@iY6oMV%72p35L0CD#Oad@0#VPIfjA%Fn_ z^Z+FQ00000006!L5CMPz@&Zx<$^%9OICz|6U}Rum;AEK3z`+0lOhC*9gbWP-!F&b) z8GHf9v)2em0cL|h3`G-b9mhrSBA!W7Co}<>kx4-8>09Z>MIZdd`|zNF9|Qa+VuLqq z@s1q^>@ng153iJMCFgn7%t6$=)MFG)Y~MYpN~!uDP0D^fB2ARiBIG@7+Zl0tS}=Ck L`~vs@YbGLBkC9|i diff --git a/src/renderer/src/assets/fonts/icon-fonts/iconfont.woff2 b/src/renderer/src/assets/fonts/icon-fonts/iconfont.woff2 index c2e0fb69b7ec596bf6d949c15503b142daca9e07..fcf48bfc87c8e5eb462a56ace334e7cfadcd07bd 100644 GIT binary patch delta 2199 zcmV;I2x#}951bJgcTYw#00961000P_01E&B000pb000P2kr*C-HVTs*kR1Uw0we=0 z3m5UTui*t-s-qDylXTGg-U|Rq69*NuhxFP^SzrjK+QtU zg4R=Ui&QLy2?_px!qDz%==PL7F@-htuT%fq>*QRf8z)a}cN2QZZT$ZmsB3-|NnljPAqPOW}7!+p~Uy+_xszhkC=awxP&AcMZHlvhtMd6 zn9*%ACFrbWgTxNatc^u2rY%uV>6C{pDtqSB)6a(a`B@6B z3+wdbvLfLK^2&;0VgfGF8O5P?!O4=yQ2NJ5bsZdk83rPj)$%?#!dIbHh3i^Hys;S~ zTl5Hz5rAtXUK++@b^NF>l_#%ybzOv*XDOyBdA+(~S;R18U1vUaaabH%f@+!Ll(Jd` z$hV&ek&05@3R8=6DKB0E$R$$g%)a}Qme{2T3>0`7yjjkd=cRGQBMOjcCRYtIh=2W} znhMN+i~Qu!2naQ%L-pfEuS-!bBH#D(CZSTNMH4wi2=~1&~B7vUnB@YlBEPDN0S zo*o?f<*m0jl8W6T4EQ#IJB+#~%+;?ja;`A2E8)od6T@!>{#%7Vcwk7xcy3OsOq? z_ccQ^=uj=GB}^47fpwcohh%HWPGSv9Vp14~mgZft%xQJ(%54Rb=JUxQb0=Kq{7mzX zU+2ge$RWj6!^N;{4HWI_yMmIhKc-Vkgo&jB3D?IGSl&loMQO$YVI~fv)JMMb(=6+M zi@S*9Xu?m$N z%crwtHM!(^g2{MiDK(S+u#n2zQ(4LED5uJ|Rp&743ds3)Tu1Hoe#n|@ z9$jq6G~HPQYuj59YHhEDMV<7u#T;9I#Z=k&c-d6NZ6cvWmA$=rh%Ehod-qf~fqVOUtHU-RbK2c)ZD>uKTi{J4Za^sdH8D_7&_7KCZfb_d zeA1iOe{rU_Zx6`LrLku)0&SS8&izE4Mf|o>tKD97{B?y=jwx8BS*DZ)uz2x*Js6W! z%2aas)@AA7N|{sy_u>0<{4!kt^PPyLuqmh;5@8W z1fhCU&WqP@0Fyp>bKQr7dpFy#qL!_T@!wC^?RhXWr!wwc?SSo(InaGgxY#zqv;GB6bklFmE39Z zUL+DqRDI#3-JqeiHzrkoLG`7mR`%8O#lvB1wmhY`9^?M^5Qo2fFDaTDod>Me znl38KX?gMU3ZjDPZv5|d8vn8GzzjC`+Gl6efvqRuJ*Zyb$NI;a z81_?&EzTSm_%T(r6zr-*8j6MpVrY&?++12Nf!4Eo`gdCE=F?XC`AhoY*e1<0GxUZkdWjjB;ncQ4&2q*<@ zQ`UfmvPJI^(*r(~gL+4+_S0i>IVlhu0S%U+7qWby9O7Cq zl_137oanIO9gEvRtJQ%!MTZ$05WyqltSKzy+72NM4jX7Cf>Ljuinm0we<@ z3lsnZAO(bH2Z0b9XAL6}c7t*XME1{wOpfkLCf6$_%v|<{gR z!@nPMbz*TNG?&zW2#FH^=5MCb5Ya}6qEXZvm5_IYkkDb!So!YV`x z!&JWo1OOV@Dv|Y#?O242>^E^VB1z!PCxA4a7&&Z6KPU_ZBn>nI3bCx~nGz5yAO*<9 z z9H3%I4+S)rE5T3zYGFnc`Azq&>AUB5+4oi7-~I4YbA2G7+!0zLpgs+dLkbcK$UuNv z{+}mP}qbTkOJSEWFR421{o9(u7V6Q2;ZRw2;hgGYJdWo>)BsY zp!CmB1CoG$d;?Hx;fU%(pByBlu8x8NTT67hB+5+FSu$-{ura-0ba+^pMA61ZDD}BQ zZm@CyZF3WZ)ot4gh*uskjNM~(kDzUP#eHe)K0w@O_tg5XK5VkOlqg!Sx4G!+rL|g{ zBW@@MXn~AUQrzxBC) z-6y7|@~TBD0<-ThrfxSTs$T=8_Iz|VC#5D;6rNH;s0m5G_#8E%B8rF>Bzb(U<#Hg} zl0E4B_y!9ddeEX+*yqo1rk=smMntG9GkvfexZc8R0h7R!C(I^%tfHrk>xoHs63X4SZVZt4j} zc&D0Rg=HxlECKFpz-$P(>lASh!^~ZGZ7Ofvo@(@%Qnwj@+qCJtS~=kOmtfDM#mcYs z?RQU&QxM}C>teDLTZZIm9~qg4XR5fBimM}2Ak(}j=s)@vJr1rQXPUj29qn_ zMl>mV{qvf+34>J?k!{K1x=A&~Wu;{`MU!hwh{$&3sv$k_UAGIH+-Y!s{K~iCpI+0k-#8`qe>6R7YQ*-ihyW0HL>;5j!ot9K zD6|%rphWO-jne>t8b|(rQFv+mRCVYw<)&ImWqYC3A5}qpLd*X-%s+C$r@Q`Ug)@x< z3XpX`m<)+a-&2A`CtAYP14_1A#2iqp#1<%gBmmkS1ISbswRh8sG@V)UqezpMl8_?` zD2`|Z35cnsQZUE`8Hg37G}1Jj&p=}=C=e1QfJAdqN+8U>QW7D5I0F=jw?G2o^HM4F zzJmp2!!a|v82-cod5mM^ zmQJb>B2y0^+WoNW2r5&9UUylwp7&XUxnp*o`q72as$V$(_iSD~4RK)2|Gh6v*UoZB z?4OQBVTk2}qn$%|2kfU{=+xmxa^;pzROliQuz)@meV97z+yH^f@7AE#ojkx-&-P-V z*j2DwUev#60p4Aw&U2LGt7*G?dOLmnLX_~}Amu(n$pg3nf0w$>hUoLM_81y#$CMk4?K6xf)? diff --git a/src/renderer/src/assets/styles/index.scss b/src/renderer/src/assets/styles/index.scss index 211f849f..ee07999c 100644 --- a/src/renderer/src/assets/styles/index.scss +++ b/src/renderer/src/assets/styles/index.scss @@ -2,11 +2,6 @@ @import './markdown.scss'; @import './scrollbar.scss'; -// @font-face { -// font-family: 'Playwrite'; -// src: url(../fonts/Playwrite.ttf) format('truetype'); -// } - :root { --color-white: #ffffff; --color-white-soft: #f8f8f8; @@ -28,17 +23,22 @@ --color-background-soft: var(--color-black-soft); --color-background-mute: var(--color-black-mute); - --color-primary: #00b96b; - --color-primary-soft: #00b96b99; - --color-primary-mute: #00b96b33; + --color-primary: #135200; + --color-primary-soft: #13520099; + --color-primary-mute: #13520033; --color-text: var(--color-text-1); --color-icon: #ffffff99; --color-icon-white: #ffffff; --color-border: #ffffff20; --color-error: #f44336; + --color-code-background: #323232; + --color-scrollbar-thumb: rgba(255, 255, 255, 0.15); + --color-scrollbar-thumb-hover: rgba(255, 255, 255, 0.3); --navbar-background: #1f1f1f; + --sidebar-background: #1f1f1f; + --navbar-height: 42px; --sidebar-width: 55px; --assistants-width: 245px; @@ -48,6 +48,44 @@ --input-bar-height: 125px; } +body[theme-mode='light'] { + --color-white: #ffffff; + --color-white-soft: #f8f8f8; + --color-white-mute: #efefef; + + --color-black: #1b1b1f; + --color-black-soft: #262626; + --color-black-mute: #363636; + + --color-gray-1: #8e8e93; + --color-gray-2: #aeaeb2; + --color-gray-3: #c7c7cc; + + --color-text-1: rgba(0, 0, 0, 1); + --color-text-2: rgba(0, 0, 0, 0.6); + --color-text-3: rgba(0, 0, 0, 0.38); + + --color-background: #ffffff; + --color-background-soft: var(--color-white-soft); + --color-background-mute: var(--color-white-mute); + + --color-primary: #00b96b; + --color-primary-soft: #00b96b99; + --color-primary-mute: #00b96b33; + + --color-text: var(--color-text-1); + --color-icon: #00000099; + --color-icon-white: #000000; + --color-border: #00000028; + --color-error: #f44336; + --color-code-background: #e3e3e3; + --color-scrollbar-thumb: rgba(0, 0, 0, 0.15); + --color-scrollbar-thumb-hover: rgba(0, 0, 0, 0.3); + + --navbar-background: #f8f8f8; + --sidebar-background: #f8f8f8; +} + *, *::before, *::after { @@ -68,8 +106,18 @@ body { line-height: 1.6; overflow: hidden; background-size: cover; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', - 'Droid Sans', 'Helvetica Neue', sans-serif; + font-family: + -apple-system, + BlinkMacSystemFont, + 'Microsoft YaHei', + 'Segoe UI', + Roboto, + Oxygen, + Ubuntu, + Cantarell, + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue' sans-serif; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -97,3 +145,9 @@ body, #inputbar .ant-input { resize: none; } + +.chat-nav-dropdown { + .ant-dropdown-menu { + padding-bottom: 12px; + } +} diff --git a/src/renderer/src/assets/styles/markdown.scss b/src/renderer/src/assets/styles/markdown.scss index ba1ae58e..6e2f4a55 100644 --- a/src/renderer/src/assets/styles/markdown.scss +++ b/src/renderer/src/assets/styles/markdown.scss @@ -1,5 +1,5 @@ .markdown { - color: #f1f1f1; + color: var(--color-text); font-size: 15px; line-height: 1.6; user-select: text; @@ -33,44 +33,36 @@ h1 { font-size: 2em; - color: #fff; } h2 { font-size: 1.5em; - color: #fff; } h3 { font-size: 1.2em; - color: #fff; } h4 { font-size: 1em; - color: #fff; } h5 { font-size: 0.9em; - color: #fff; } h6 { font-size: 0.8em; - color: #fff; } p { margin: 1em 0; - color: #fff; } ul, ol { padding-left: 1.5em; margin: 1em 0; - color: #ccc; } li { diff --git a/src/renderer/src/assets/styles/scrollbar.scss b/src/renderer/src/assets/styles/scrollbar.scss index 29a95c94..10865ced 100644 --- a/src/renderer/src/assets/styles/scrollbar.scss +++ b/src/renderer/src/assets/styles/scrollbar.scss @@ -8,8 +8,8 @@ } ::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.15); + background: var(--color-scrollbar-thumb); &:hover { - background: rgba(255, 255, 255, 0.3); + background: var(--color-scrollbar-thumb-hover); } } diff --git a/src/renderer/src/components/app/Sidebar.tsx b/src/renderer/src/components/app/Sidebar.tsx index 3de3e7ca..8502a967 100644 --- a/src/renderer/src/components/app/Sidebar.tsx +++ b/src/renderer/src/components/app/Sidebar.tsx @@ -56,7 +56,7 @@ const Container = styled.div` min-width: var(--sidebar-width); min-height: 100%; -webkit-app-region: drag !important; - background-color: #1f1f1f; + background-color: var(--sidebar-background); border-right: 0.5px solid var(--color-border); padding-top: var(--navbar-height); position: relative; @@ -68,7 +68,7 @@ const AvatarImg = styled.img` height: 28px; background-color: var(--color-background-soft); margin: 5px 0; - margin-top: ${isMac ? '16px' : '7px'}; + margin-top: ${isMac ? '16px' : '9px'}; ` const MainMenus = styled.div` display: flex; @@ -102,7 +102,7 @@ const Icon = styled.div` font-size: 17px; } &:hover { - background-color: #ffffff30; + background-color: var(--color-background-soft); cursor: pointer; .iconfont, .anticon { @@ -110,7 +110,7 @@ const Icon = styled.div` } } &.active { - background-color: #ffffff20; + background-color: var(--color-background-mute); .iconfont, .anticon { color: var(--color-icon-white); diff --git a/src/renderer/src/config/antd.ts b/src/renderer/src/config/antd.ts deleted file mode 100644 index 34c3baa5..00000000 --- a/src/renderer/src/config/antd.ts +++ /dev/null @@ -1,26 +0,0 @@ -import store from '@renderer/store' -import { theme, ThemeConfig } from 'antd' -import zhCN from 'antd/locale/zh_CN' - -export const colorPrimary = '#00b96b' - -export const AntdThemeConfig: ThemeConfig = { - token: { - colorPrimary, - borderRadius: 5 - }, - algorithm: [theme.darkAlgorithm] -} - -export function getAntdLocale() { - const language = store.getState().settings.language - - switch (language) { - case 'zh-CN': - return zhCN - case 'en-US': - return undefined - default: - return zhCN - } -} diff --git a/src/renderer/src/hooks/useSettings.ts b/src/renderer/src/hooks/useSettings.ts index 8720e816..b1fec33d 100644 --- a/src/renderer/src/hooks/useSettings.ts +++ b/src/renderer/src/hooks/useSettings.ts @@ -1,5 +1,10 @@ import { useAppDispatch, useAppSelector } from '@renderer/store' -import { setSendMessageShortcut as _setSendMessageShortcut, SendMessageShortcut } from '@renderer/store/settings' +import { + setSendMessageShortcut as _setSendMessageShortcut, + SendMessageShortcut, + setTheme, + ThemeMode +} from '@renderer/store/settings' export function useSettings() { const settings = useAppSelector((state) => state.settings) @@ -9,6 +14,9 @@ export function useSettings() { ...settings, setSendMessageShortcut(shortcut: SendMessageShortcut) { dispatch(_setSendMessageShortcut(shortcut)) + }, + setTheme(theme: ThemeMode) { + dispatch(setTheme(theme)) } } } diff --git a/src/renderer/src/i18n/index.ts b/src/renderer/src/i18n/index.ts index 0a903840..9ebe395f 100644 --- a/src/renderer/src/i18n/index.ts +++ b/src/renderer/src/i18n/index.ts @@ -156,7 +156,11 @@ const resources = { 'about.feedback.button': 'Feedback', 'about.contact.title': '📧 Contact', 'about.contact.button': 'Email', - 'proxy.title': 'Proxy Address' + 'proxy.title': 'Proxy Address', + 'theme.title': 'Theme', + 'theme.dark': 'Dark', + 'theme.light': 'Light', + 'theme.auto': 'Auto' }, translate: { title: 'Translation', @@ -334,7 +338,11 @@ const resources = { 'about.feedback.button': '反馈', 'about.contact.title': '📧 邮件联系', 'about.contact.button': '邮件', - 'proxy.title': '代理地址' + 'proxy.title': '代理地址', + 'theme.title': '主题', + 'theme.dark': '深色主题', + 'theme.light': '浅色主题', + 'theme.auto': '跟随系统' }, translate: { title: '翻译', diff --git a/src/renderer/src/init.ts b/src/renderer/src/init.ts index 9ce61951..8e70a2b0 100644 --- a/src/renderer/src/init.ts +++ b/src/renderer/src/init.ts @@ -2,6 +2,7 @@ import localforage from 'localforage' import KeyvStorage from '@kangfenmao/keyv-storage' import * as Sentry from '@sentry/electron/renderer' import { isProduction, loadScript } from './utils' +import { ThemeMode } from './store/settings' async function initSentry() { if (await isProduction()) { @@ -21,12 +22,12 @@ async function initSentry() { } } -export async function initMermaid() { +export async function initMermaid(theme: ThemeMode) { if (!window.mermaid) { await loadScript('https://unpkg.com/mermaid@10.9.1/dist/mermaid.min.js') window.mermaid.initialize({ startOnLoad: true, - theme: 'dark', + theme: theme === ThemeMode.dark ? 'dark' : 'default', securityLevel: 'loose' }) window.mermaid.contentLoaded() diff --git a/src/renderer/src/pages/apps/AppsPage.tsx b/src/renderer/src/pages/apps/AppsPage.tsx index 932ec215..eea49ace 100644 --- a/src/renderer/src/pages/apps/AppsPage.tsx +++ b/src/renderer/src/pages/apps/AppsPage.tsx @@ -116,12 +116,16 @@ const AssistantCard = styled.div` display: flex; flex-direction: row; margin-bottom: 16px; - background-color: #111; - border: 0.5px solid #151515; + background-color: var(--color-background-soft); + border: 0.5px solid var(--color-border); border-radius: 10px; padding: 15px; position: relative; cursor: pointer; + transition: all 0.2s ease-in-out; + &:hover { + background-color: var(--color-background-mute); + } ` const EmojiHeader = styled.div` width: 25px; @@ -148,7 +152,7 @@ const AssistantName = styled(Title)` -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; - color: #fff; + color: var(--color-white); font-weight: 900; ` diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx index 04beb829..6dc7fce3 100644 --- a/src/renderer/src/pages/home/HomePage.tsx +++ b/src/renderer/src/pages/home/HomePage.tsx @@ -9,6 +9,8 @@ import { useShowAssistants, useShowRightSidebar } from '@renderer/hooks/useStore import Navigation from './components/NavigationCenter' import { isMac, isWindows } from '@renderer/config/constant' import { Assistant } from '@renderer/types' +import { useTheme } from '@renderer/providers/ThemeProvider' +import { Switch } from 'antd' let _activeAssistant: Assistant @@ -18,6 +20,7 @@ const HomePage: FC = () => { const { rightSidebarShown, toggleRightSidebar } = useShowRightSidebar() const { showAssistants, toggleShowAssistants } = useShowAssistants() const { defaultAssistant } = useDefaultAssistant() + const { theme, toggleTheme } = useTheme() _activeAssistant = activeAssistant @@ -42,6 +45,12 @@ const HomePage: FC = () => { )} + } + unCheckedChildren={} + defaultChecked={theme === 'dark'} + onChange={toggleTheme} + /> @@ -101,4 +110,12 @@ export const NewButton = styled.div` } ` +const ThemeSwitch = styled(Switch)` + -webkit-app-region: none; + margin-right: 8px; + .icon-theme { + font-size: 14px; + } +` + export default HomePage diff --git a/src/renderer/src/pages/home/components/Assistants.tsx b/src/renderer/src/pages/home/components/Assistants.tsx index f6f5e8ee..cc2d8621 100644 --- a/src/renderer/src/pages/home/components/Assistants.tsx +++ b/src/renderer/src/pages/home/components/Assistants.tsx @@ -96,7 +96,9 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs onSwitchAssistant(assistant)} className={assistant.id === activeAssistant?.id ? 'active' : ''}> - {assistant.name || t('assistant.default.name')} + + {assistant.name || t('assistant.default.name')} + @@ -143,13 +145,15 @@ const AssistantItem = styled.div` &.active { background-color: var(--color-background-mute); cursor: pointer; + .name { + font-weight: bolder; + } } ` const AssistantName = styled.div` font-size: 14px; - color: var(--color-text-1); - font-weight: 500; + color: var(--color-text); display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; diff --git a/src/renderer/src/pages/home/components/CodeBlock.tsx b/src/renderer/src/pages/home/components/CodeBlock.tsx index 4d733cba..7b102635 100644 --- a/src/renderer/src/pages/home/components/CodeBlock.tsx +++ b/src/renderer/src/pages/home/components/CodeBlock.tsx @@ -1,11 +1,13 @@ -import React from 'react' -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' -import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism' -import styled from 'styled-components' -import { CopyOutlined } from '@ant-design/icons' -import { useTranslation } from 'react-i18next' -import Mermaid from './Mermaid' +import { CheckOutlined, CopyOutlined } from '@ant-design/icons' import { initMermaid } from '@renderer/init' +import { useTheme } from '@renderer/providers/ThemeProvider' +import { ThemeMode } from '@renderer/store/settings' +import React, { useState } from 'react' +import { useTranslation } from 'react-i18next' +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' +import { atomDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism' +import styled from 'styled-components' +import Mermaid from './Mermaid' interface CodeBlockProps { children: string @@ -15,16 +17,20 @@ interface CodeBlockProps { const CodeBlock: React.FC = ({ children, className, ...rest }) => { const match = /language-(\w+)/.exec(className || '') + const [copied, setCopied] = useState(false) + const { theme } = useTheme() const { t } = useTranslation() const onCopy = () => { navigator.clipboard.writeText(children) window.message.success({ content: t('message.copied'), key: 'copy-code' }) + setCopied(true) + setTimeout(() => setCopied(false), 2000) } if (match && match[1] === 'mermaid') { - initMermaid() + initMermaid(theme) return } @@ -32,12 +38,13 @@ const CodeBlock: React.FC = ({ children, className, ...rest }) =
{'<' + match[1].toUpperCase() + '>'} - + {!copied && } + {copied && } {String(children).replace(/\n$/, '')} @@ -54,10 +61,10 @@ const CodeHeader = styled.div` display: flex; align-items: center; justify-content: space-between; - color: #fff; + color: var(--color-text); font-size: 14px; font-weight: bold; - background-color: #323232; + background-color: var(--color-code-background); height: 40px; padding: 0 10px; border-top-left-radius: 8px; diff --git a/src/renderer/src/pages/home/components/Inputbar.tsx b/src/renderer/src/pages/home/components/Inputbar.tsx index 5263fc23..4a8746ae 100644 --- a/src/renderer/src/pages/home/components/Inputbar.tsx +++ b/src/renderer/src/pages/home/components/Inputbar.tsx @@ -248,7 +248,7 @@ const ToolbarButton = styled(Button)` &:hover { background-color: var(--color-background-soft); .anticon { - color: white; + color: var(--color-text-1); } } ` @@ -260,7 +260,7 @@ const TextCount = styled.div` font-size: 11px; color: var(--color-text-3); z-index: 10; - background-color: #121212; + background-color: var(--color-background-soft); padding: 2px 8px; border-top-left-radius: 7px; user-select: none; diff --git a/src/renderer/src/pages/home/components/Message.tsx b/src/renderer/src/pages/home/components/Message.tsx index 0a046536..e51229b9 100644 --- a/src/renderer/src/pages/home/components/Message.tsx +++ b/src/renderer/src/pages/home/components/Message.tsx @@ -1,16 +1,23 @@ -import { CopyOutlined, DeleteOutlined, EditOutlined, MenuOutlined, SaveOutlined, SyncOutlined } from '@ant-design/icons' -import Logo from '@renderer/assets/images/logo.png' +import { + CheckOutlined, + CopyOutlined, + DeleteOutlined, + EditOutlined, + MenuOutlined, + SaveOutlined, + SyncOutlined +} from '@ant-design/icons' import { getModelLogo } from '@renderer/config/provider' import { useAssistant } from '@renderer/hooks/useAssistant' import useAvatar from '@renderer/hooks/useAvatar' import { useSettings } from '@renderer/hooks/useSettings' import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { Message } from '@renderer/types' -import { firstLetter } from '@renderer/utils' +import { firstLetter, removeLeadingEmoji } from '@renderer/utils' import { Avatar, Dropdown, Tooltip } from 'antd' import dayjs from 'dayjs' import { isEmpty, upperFirst } from 'lodash' -import { FC, useCallback } from 'react' +import { FC, useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Markdown from 'react-markdown' import styled from 'styled-components' @@ -31,6 +38,7 @@ const MessageItem: FC = ({ message, index, showMenu, onDeleteMessage }) = const { assistant } = useAssistant(message.assistantId) const { userName, showMessageDivider, messageFont } = useSettings() const { generating } = useRuntime() + const [copied, setCopied] = useState(false) const isLastMessage = index === 0 const isUserMessage = message.role === 'user' @@ -39,6 +47,8 @@ const MessageItem: FC = ({ message, index, showMenu, onDeleteMessage }) = const onCopy = () => { navigator.clipboard.writeText(message.content) window.message.success({ content: t('message.copied'), key: 'copy-message' }) + setCopied(true) + setTimeout(() => setCopied(false), 2000) } const onDelete = async () => { @@ -105,14 +115,14 @@ const MessageItem: FC = ({ message, index, showMenu, onDeleteMessage }) = {message.role === 'assistant' ? ( - - {firstLetter(message.modelId).toUpperCase()} + + {firstLetter(assistant?.name).toUpperCase()} ) : ( )} - {getUserName()} + {removeLeadingEmoji(getUserName())} {dayjs(message.createdAt).format('MM/DD HH:mm')} @@ -137,25 +147,26 @@ const MessageItem: FC = ({ message, index, showMenu, onDeleteMessage }) = {message.role === 'user' && ( - - + + )} - - + + {!copied && } + {copied && } - - + + {canRegenerate && ( - - + + )} diff --git a/src/renderer/src/pages/home/components/NavigationCenter.tsx b/src/renderer/src/pages/home/components/NavigationCenter.tsx index 7e673650..966c7bd5 100644 --- a/src/renderer/src/pages/home/components/NavigationCenter.tsx +++ b/src/renderer/src/pages/home/components/NavigationCenter.tsx @@ -1,6 +1,5 @@ import { CodeSandboxOutlined } from '@ant-design/icons' import { NavbarCenter } from '@renderer/components/app/Navbar' -import { colorPrimary } from '@renderer/config/antd' import { isMac } from '@renderer/config/constant' import { useAssistant } from '@renderer/hooks/useAssistant' import { useProviders } from '@renderer/hooks/useProvider' @@ -13,6 +12,7 @@ import { useTranslation } from 'react-i18next' import styled from 'styled-components' import { NewButton } from '../HomePage' import { getModelLogo } from '@renderer/config/provider' +import { removeLeadingEmoji } from '@renderer/utils' interface Props { activeAssistant: Assistant @@ -34,7 +34,7 @@ const NavigationCenter: FC = ({ activeAssistant }) => { children: p.models.map((m) => ({ key: m.id, label: upperFirst(m.name), - style: m.id === model?.id ? { color: colorPrimary } : undefined, + style: m.id === model?.id ? { color: 'var(--color-primary)' } : undefined, icon: , onClick: () => setModel(m) })) @@ -47,8 +47,11 @@ const NavigationCenter: FC = ({ activeAssistant }) => { )} - {assistant?.name || t('assistant.default.name')} - + {removeLeadingEmoji(assistant?.name) || t('assistant.default.name')} + {model ? upperFirst(model.name) : t('button.select_model')} @@ -69,13 +72,14 @@ const AssistantName = styled.span` ` const DropdownButton = styled(Button)` - font-size: 10px; + font-size: 11px; border-radius: 15px; padding: 0 8px; ` const ModelName = styled.span` margin-left: -2px; + font-weight: bolder; ` export default NavigationCenter diff --git a/src/renderer/src/pages/home/components/RightSidebar.tsx b/src/renderer/src/pages/home/components/RightSidebar.tsx index e02f930c..de809f3f 100644 --- a/src/renderer/src/pages/home/components/RightSidebar.tsx +++ b/src/renderer/src/pages/home/components/RightSidebar.tsx @@ -90,10 +90,10 @@ const Tab = styled.div` align-items: center; font-size: 13px; cursor: pointer; - color: #8a8a8a; + color: var(--color-text-3); border-bottom: 1px solid transparent; &.active { - color: #bbb; + color: var(--color-text-2); font-weight: 600; } ` diff --git a/src/renderer/src/pages/home/components/TopicsTab.tsx b/src/renderer/src/pages/home/components/TopicsTab.tsx index 4e4f233c..286b0f46 100644 --- a/src/renderer/src/pages/home/components/TopicsTab.tsx +++ b/src/renderer/src/pages/home/components/TopicsTab.tsx @@ -130,7 +130,7 @@ const TopicListItem = styled.div` margin-bottom: 5px; cursor: pointer; border-radius: 5px; - font-size: 13px; + font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -138,7 +138,8 @@ const TopicListItem = styled.div` background-color: var(--color-background-soft); } &.active { - background-color: var(--color-background-soft); + background-color: var(--color-background-mute); + font-weight: bolder; } ` diff --git a/src/renderer/src/pages/settings/GeneralSettings.tsx b/src/renderer/src/pages/settings/GeneralSettings.tsx index 2acd2e2f..dfca3f7e 100644 --- a/src/renderer/src/pages/settings/GeneralSettings.tsx +++ b/src/renderer/src/pages/settings/GeneralSettings.tsx @@ -8,14 +8,14 @@ import useAvatar from '@renderer/hooks/useAvatar' import { useAppDispatch } from '@renderer/store' import { setAvatar } from '@renderer/store/runtime' import { useSettings } from '@renderer/hooks/useSettings' -import { setLanguage, setUserName } from '@renderer/store/settings' +import { setLanguage, setUserName, ThemeMode } from '@renderer/store/settings' import { useTranslation } from 'react-i18next' import { setProxyUrl as _setProxyUrl } from '@renderer/store/settings' import i18n from '@renderer/i18n' const GeneralSettings: FC = () => { const avatar = useAvatar() - const { language, proxyUrl: storeProxyUrl, userName } = useSettings() + const { language, proxyUrl: storeProxyUrl, userName, theme, setTheme } = useSettings() const [proxyUrl, setProxyUrl] = useState(storeProxyUrl) const dispatch = useAppDispatch() const { t } = useTranslation() @@ -53,6 +53,20 @@ const GeneralSettings: FC = () => { /> + + {t('settings.theme.title')} +