From e11633310c913fc15cc570878850c237201805fd Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Sat, 9 Nov 2024 08:39:10 +0800 Subject: [PATCH] feat: add the ability to display the application in tray #297 --- build/tray_icon_dark.png | Bin 0 -> 2131 bytes build/tray_icon_light.png | Bin 0 -> 1789 bytes src/main/electron.d.ts | 9 ++ src/main/index.ts | 32 +++-- src/main/ipc.ts | 4 +- src/main/resources/icon.ico | Bin 0 -> 361102 bytes src/main/services/TrayService.ts | 68 ++++++++++ src/main/services/WindowService.ts | 194 +++++++++++++++++++++++++++++ src/main/window.ts | 132 -------------------- 9 files changed, 288 insertions(+), 151 deletions(-) create mode 100644 build/tray_icon_dark.png create mode 100644 build/tray_icon_light.png create mode 100644 src/main/electron.d.ts create mode 100644 src/main/resources/icon.ico create mode 100644 src/main/services/TrayService.ts create mode 100644 src/main/services/WindowService.ts delete mode 100644 src/main/window.ts diff --git a/build/tray_icon_dark.png b/build/tray_icon_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..0ee6086a0d10f44b6a71a327ffc09526c0dc2e90 GIT binary patch literal 2131 zcmV-Z2(0&sP)4l|C zQ)wm<_{b=bq{yr((@8Dwi0M!No z{#ff=&)IjMv-jGMliwMJd-huE_xpYKUVE+ed+f^a@G!3=h?p+m8sLM#7~prn3RU|^ z(Yh+HB=REWP~Z>18zW6m1G81_jw1C{ixidzB4#Xb3!{kx9147{Xq{t=7FdUfISBX& z@CKj{Sf^_Dwi$8_@a{J4#}#LtLn-7%%rU?nz>J2=4~v*nRqdlO{TBnv62v}RoP`dB zPkRwl0e(g^ae$e?JtAgSr2pH1wJAj3P>h8yql|Z6fH${jn}NFla{xrlbYMMjK!VK6 zRqcI+S?Ey)`AG@wQ-Qlg%sk*1z+nmfZwJ0!gkez>zSW7C@xV^t)dh%C;O(mR-v!7T zg~Fe_s@(&8sQ^*W0}IL`4khjLs&*~#g`8p*s@jK&u%{JDJLIU^)xf8Lp$x+>Rkgn= z*rZV?^@OKtuLl+aF9i&{6}Yqm0Rt>lt zI2>39d`#8G8X{uGc%7#J-M~>auN^M|j{tuJehq9;wJ)aOlt^C0yareWe1_zsYa_M; z*8<;Dwf~8cdpK|%u&71*L|{4ad0<+A-T|DW)T3(KdV+1qiv!*_8rBP;rWRjT%ftvbY#7cmoXF60?Glt-}!SgC4T?)iR-Uk$7%Q18va*{XIh z+AxZ|h?xXz1Wqc@fI4mmKB{V8h&G6rak#UXUW_!IA+^RmNgSxNvd`F)A65xmq8c=`tK7czgbrMJ5 z_>Pdjpu+|{{D0D0#{~eDTCHmiaDNU#gSc~c2u=n54E&4i&ea~0UF3E+p$g2;FmN~U zSzxlNorpVrt^!6KQ)6&1ZohvY;l%dmy4ceTYz4G%M;o! zBdp$FTNStcgkY(Nxx_CpBM>p;M9e+Fxj-dmTot%b#Qebjejrfq0ugg@T!V-?o#f(% zC?2TPYF(cKeiS$mT!P_dT$t)e;A~)e%CU2B@A2jgde2a`x72_59c3Hg>(Qc0t=2UK zw}q8fpA&=4z&zZIOMAoWcn(*Y^*Dft`7&^2pp3o1^s1`e4g4~2a9+&zVeOw}7<(|y z?)SSg$XKsx`|Iy*PX+;bP~YRP!##yiED$l>{_m+7#`l1CEL3eTE_m1}40^p!;f}&t zu+sm(t8K5*xJlLC56xcHg}^fz4GBeW!{6To{*gh(heXU2TzP6*JPUlW{$qr^cQ_W3 zt(YJL!!!gje75FHU{p9e0poDq=ncdtKcg4|miXBQMEq%0`x{(Zs!a5#+P;Y1Ufhij z#a!<@EW({W+hK*O-OzYp)Y;xwQ=9|DCK+>LqpH0qu0hqV1Fi~`H_MN`4Hq1Vdwq5Q zr>ojE(T0|}IuUa+a3jfea%nLPysq(lAER>$ZUIXh&3#JMj^y)v@$$*QTfB^|z};~f zB1leDPQoon=hK{y&VoKwJ2R=1h}nvNqA4(-YA0tksI5z@Y6n#9a-fErlH0Q?BK?9E zvhU}L_XkyG_$0$DT<&WoZgET-lz0J{;)@hp$sdo~j?&89rD{WR_e}^o&-?0$t^OY( zW-f3-%Hap%>hjuUhR!7{!t-Q6-%-e0kQ5R&0=QDd#Abl~^=}2r%T=M7*OS|V+hfyW zJg(yUYT&1U#f7;)i7Qu4Dm{3A|p_+N^_fO?$66OF*Is z+?$6u6jOw_I=M?J_)cB{MUcM@mvrnTdV$ar1q)ZTip z?2;m6rNB2-Z6PzQ$im|*aG#Wwi5p3d-={`lW$alLH{s%Ubqo_j)jpYCU002ov JPDHLkV1nGH2K@j4 literal 0 HcmV?d00001 diff --git a/build/tray_icon_light.png b/build/tray_icon_light.png new file mode 100644 index 0000000000000000000000000000000000000000..71181c0d84b9fa8a3d2831172e12780acfaff44e GIT binary patch literal 1789 zcmV3)5T{F6>+W?t35YF>Bst9sQ);vhRtk@TLVc1fL*mPs0z23JNB2N8|~z6Q=L z_wg+4#kdx{V5NnL6{b5fziO}z*=At&{<~u3&0g+em^fU%RHpz z{5imN1{@5bv+=B5#Bz2S+pO4Fuwlxjs z2L?E?UD6WI3fXEzXBHl ztqJHVLK=^{cHprD^!yFnNJ@@Fde8ZJz#}nr+zqTwz?PCobCEL(co680Ao~>XQL^R~ zqY}&8JAgNULqm@L5V#pg-LOin?HhnbEEtxn_hk7%sj zEuNi;EpLyJ^qiztxnZlM$0XI3(0j6^_sjg=ThbIse>n2CdiEX@33BqCG&3qs&O)TVNZ}*XHd6r^w|nI17x3 zAh;74k0lnpffIm7fqz2CuE3I*YjAhG9hmCz)p-2lJ$qa+0!d39qh*UMG4Wfd)JyuU zjO>rHT0N5HIr8pS@gp*K4Djr7!BQk`a1P0j=Yltct<(v z+mMm8m!9YwgcdB14qKEw7TAY>`FSlcFr=P+p02hsW3s^S*ewwToms*=4)8N>)z}@B zuDiPgtId>m9L%nt9duT?2Aop$w53snN2>#s4cD4ByFt3$8kJCiAq_YZm zhsb(uU2&JBI)&R<>5?=upV#l1`9K6CTrk6j6G@{Z=$hxr-k{(o)`UXNQmdu0N~fZ_ zqy%5?nBl1Ji#*vY<$wEZlWnjT@s(>%xExrBrLajrz=>E|Tna~H0W54acPnsm-seLw zyU+J@tOAw;9sU-TpNT%QHFM#1bQG)!;L07V9CWsZ)aiQ!rL=9pL|_0g6Dt-(h5x@_ zjB=gXg@{$}ckRi^V5#M$z}dh!!WShvu*=f83sUyXHTJ%imgHQxCd?|awHkCl$& z4?x^Lyiz?oRXFas3-dpkCg?7GeXZvl5E zrO*cpu?FQtj83xaT3EZsJ3#{@;$7_J!^CDQ>6Y_OZ02Vqtam2%#)eDM6O1@(V;S@>6)}`Ow)N}?cd|%UZ7m>$yKLmU!`6%$pDuC*JBCHSy(FC3ww06V9g3& f0n5B|>W2LXPn4eDcO`%j00000NkvXXu0mjf&zDrB literal 0 HcmV?d00001 diff --git a/src/main/electron.d.ts b/src/main/electron.d.ts new file mode 100644 index 00000000..877df783 --- /dev/null +++ b/src/main/electron.d.ts @@ -0,0 +1,9 @@ +declare global { + namespace Electron { + interface App { + isQuitting: boolean + } + } +} + +export {} diff --git a/src/main/index.ts b/src/main/index.ts index 35de52f8..668a9f9a 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -4,8 +4,9 @@ import installExtension, { REDUX_DEVTOOLS } from 'electron-devtools-installer' import { registerIpc } from './ipc' import { registerZoomShortcut } from './services/ShortcutService' +import { TrayService } from './services/TrayService' +import { windowService } from './services/WindowService' import { updateUserDataPath } from './utils/upgrade' -import { createMainWindow } from './window' // Check for single instance lock if (!app.requestSingleInstanceLock()) { @@ -21,21 +22,19 @@ app.whenReady().then(async () => { // Set app user model id for windows electronApp.setAppUserModelId(import.meta.env.VITE_MAIN_BUNDLE_ID || 'com.kangfenmao.CherryStudio') - // Default open or close DevTools by F12 in development - // and ignore CommandOrControl + R in production. - // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils - app.on('browser-window-created', (_, window) => { - optimizer.watchWindowShortcuts(window) - }) + 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) createMainWindow() + if (BrowserWindow.getAllWindows().length === 0) { + windowService.createMainWindow() + } else { + windowService.showMainWindow() + } }) - const mainWindow = createMainWindow() - registerZoomShortcut(mainWindow) registerIpc(mainWindow, app) @@ -56,13 +55,12 @@ app.on('second-instance', () => { } }) -// Quit when all windows are closed, except on macOS. There, it's common -// for applications and their menu bar to stay active until the user quits -// explicitly with Cmd + Q. -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { - app.quit() - } +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 diff --git a/src/main/ipc.ts b/src/main/ipc.ts index b69ac368..89d3900b 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -9,8 +9,8 @@ import BackupManager from './services/BackupManager' import { configManager } from './services/ConfigManager' import { ExportService } from './services/ExportService' import FileStorage from './services/FileStorage' +import { windowService } from './services/WindowService' import { compress, decompress } from './utils/zip' -import { createMinappWindow } from './window' const fileManager = new FileStorage() const backupManager = new BackupManager() @@ -79,7 +79,7 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { // minapp ipcMain.handle('minapp', (_, args) => { - createMinappWindow({ + windowService.createMinappWindow({ url: args.url, parent: mainWindow, windowOptions: { diff --git a/src/main/resources/icon.ico b/src/main/resources/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..07f1f670cb236807c3b7963e0a58015bac3dc8e2 GIT binary patch literal 361102 zcmeIb2b3Gxbs)+akL0l}?>NY^rCqPJ|F3oY^}Q?aS}S{3mUh-F@7g*#EARhbvi@qm zq=}rsOb$8ZFb>0z!%Pmznc*b-jJ-T2Ck92*(^l!c6gR+i(c-Yn-exA|}y4!-#tk=OCGyTD58QmMV**tTm7&R#YK!(M;g5AFROuw}=1@czij;b0m{ zH|A^%!lkSFVBFgEc4+D7girQcq2R_)Fzk&t{Ls*uZJA zUW=rE>(((?wJIb(#uLe=rL`lNhSIs6bz!Kh(ZT!6T)})cerSUZ-Bal9e+=6&AL3#0 zVmCZ4?+HIoX}!)`Nf;fw2OG=(9CkhVk1*k=l6;2J`kgf%1i}%ptKzTWg9rZ`EWGpE zaK7;u5pjZNJppIULj({*ujv%n#!I1oU>AIF|6gJH>ZoTX(|#W@@-o;+yP6S8<1x7P`c7F^$y*Cb* z`iB!fKbhaoa_?aGBQMq`F`(Qt2-{EF;8!n)>OAIe>ppS91Lsi8bd(?EUG3@hBS0+p zT?A*$cd|V2LMO-miWI@SuMnW_rrE zeL^P*HRc{Te8m#Z>#bQnXdLf`R#z7+et#kye*T_GGCj+;+uZ?TIC9YfR38gv@%C&V zbl7yz?dpJ~E8XF^i+7E1rqBeZuA4#U=#0v1z}*f5E(F2|XRevTX@&XI^^vm{ICfz; zB7XmIYxsWOp^2Hq;r;kMbRMHHD$Nr7KL!n{WA`-_i;u2ewgH4RHLl8SYnv+C<8W${reR0h7HM z7B88S*X{cQ;ruA?QEaCe)HY4!{|>gR=F#S0-0hM9xONL|y|O1<{@i&U7#?qe5l15| zd*2lfzjLoYoFCmKhd>n^;A1{4BNj~1$uvPsS8%D2({D16WME5Z(BG& z%G-%;oe<8R4Ykco#zK7Rlo5^{Glyvv#?kejJtGl$QNF@8v}5oU=UiHI)cw!X_*OETiy&_ zcXzhJt?L+nZ4la9+9mU#e0?&&i*4kYhM&Vbh0nmO!e^nXf19jylsC57enGn}*`8l) zdO4W){5!uH#0&2H7W5CFmwZ-~7v6gJWR^R^CLwN?<`GBk9Zxk*A z^RDNdl$`rbU@Fg!?DdPx?B-iwN|d75vc)R-E54zvDUV z>%4T_=eT*;XEz`5*#iiWCIl!i%JZnt@zOEBQ}V6uynpN)XZ&Ne2%s|5p7)P`LnwwT zKI=Cw`>ZAe$OK?{CMuI?xal@MhyAfy1jsPdQrX2`(@S@;ADAo@xlC}r+X82GR=8y| zC7qtiqp}}(hi)Q(T(~=CfQ?5S@T#EyMeob2cDdk&-Y7et$?|Sicm~W*JOdyHicNZ$ z^R72$JNE|mQ3~2kvht$xsO%a~ZvX)j!bC4DT{qR=VC|n6zcbed6=VI0^9z+3Xu!Ux z94M%kZvUz8Ag1BKRjaH#soa){E@+hjyN-n3w+ygW8cESiNbgFGKOmaZE!_ zp7c!TL}yHBcj@5L10!tMZiktI{wI}j{Mv9hT~Tdl+=0%o4y6f~K; zVEG4A{XdpB!&^SKh0~q58ripAylet$276%JC*!bX&p1?e_lBPpmlf=9Q$Jn+z4i`x ze^u;xE#7Sxv(!Ug5@IK=P&fY)gnC{JZ1^^PpLnB>)v3L z?H@a94(3PWS*&crG*+g!N0(*6cLgyB`|6bTDUOe^XR7gCg5FCSJ3FE1-atedi{Ep@ zurrij|8Q%RURruO!uhcL*gFctBbZ*S&&D=g_}!zcza2`;`(VJ-3Pr_{<-fn&6@Jcy z?>J&UTDv;J@vOXYeAf`eQXEqg%5FKnt30mkhJx!uIMx=jwRhg}!u_(|i03HH5quAM zTGItvwnfUHXt9hnMwMm9cM&mMxH$FhD5OJteFeuL7YW`&#qq-XUAsr4%As0G{r4L>K#!|Skuscp!cmscVB=Z%hj0giHY)( z^ByUFrlx-f%)R~V!S^wGw_N++e+Rp*O!m8_Fh6{63y9v`;=bF}V%f8cz7Q^($|v~b z>3_lU6TTOU^QN-R$&`P%=10NuW*0pN54%>wmPdaPrlkPi;giXW%EtG*o5`fnTQ0$| z+8;yX;GSSvBcr#2@5m&BIVcw&j@`@6kIF`P2|@V&ne=s!=>)8M@TYLL;eREWw)Q1( zH$I2u*QQHD3h8UXUK4y5#I)Z)Fr|=3lKh6{Ol3v90|p$`L_;RzfMrxil+E5n!F~TZ zaG_4nE)dKJfh5NQq{a>?56bIiFz<$5N_zhuOy5%*`M1b6a;hCg^2fpFw?|M!8J6;py5)jSzz2@gjy@sEO*Nmid zUo>WfPwwD)g^yjZV3h~vFZaT-EpFIx$p%*k&9T;>q!doHKlBcsK!CKtGX(o?;QEL+ zBgYKb`vPH_6W*}j0mWnb^ves;eB!Zppbf{{Xv{5TD0dCQnq84&O+vcG*K-zo;f_I{ za(Rqaq8-=&IM|w@D#7}pWUe>jytkOfMECQSc%gi3Ah;GOnX-sh!Sjw<9B)hq&R4{q zUrxls)6VWS8DPtT3O+(og+#t-3`a580zpOrhFEA`yTqR8=4Mmw#AxZgPt9xJXN>w$&uc_e*g z{dPzAIjJ%dg>~S1k7S_L(FIo@8DQtZF_`~u44Sstqw+j;OL`uD$qIMWv&qmSTsN5L z`pSI+%$gh97MT1u?yyI!Eu?(U-In(A_mF;8ZrivHTE}$ZdlnuK_u@EvBIv<2FnbQg zUN^+(X81K+vsvBO6V6v>?}TNmBgZb;vm3Ueu8`)fd&h%g(=<-Kev2KNhr7b*^ z9CYEj(Kt}n(3fx=Q;IGMt8DLqqVm{v6v?`-ryE|wHJMU*96382mo}o?fO7@$V9ole z^)^yv#0z`U+zX4B#vbq8zGn=EoUKvg=`7FJalEt3+7XvNBhGQeg4TgfNx4w|G#9V~ z%in**0(0j_*F*C#>`Bwq`Xxpa!*k|&L1$_Ymc4aXZ2Ky>qmM}+(aqKkOJS*b7%HFi z{-~xmoOWQO6+YTJMZ^3B9;j)<`IO1E$c$!&X|AEZQx{I(V{QZDm&FV5?tP=-=cH(4 z^DR>N^abhpE?V0x^?c_*J2ZA6UAP8~;t!y1gtV~h?KlS|PJi~2bXgy)b3{EWOiS}Z zvEcMM={kGUW?RfOR>xF+pC6K@|KoilQRy#V3$6QRJTkmytt0ALA-!zfxD>v1Te|Kb z{daV?hu4me+8g2I856vPa~d?Cv>n%%51Lxwe2n>`w{R_CpT0Gm-ZIt{v8IpFPI$+z z(eQIpG>YfQg!gS79g_N|d}+Pn#mjmqx-$Ul(4K^4()HSPcBsPnGI2P&pO5x__1X~J zD#E=2D`UU=Tq)2;&8-P(wBtNqJfJm+tjuI_XIDGSnd?b8J+1NYA83tBAKNz&3*WyV zJr^mLkGA%9xN$?;2Ib`O>1WpP{)AZc3FrJ`UGKoPlyb@wJ~My*1lVk-TU@`nUc$bF z&q`e{STF%*bCcxUq>xUjIZAQZD6RwDu)!vq2lCHmTm#DT8Adu*3f9F+(JKs_i#FHY z)fP_A@{z*g^{%n*9pm+I@`Neod@3t{-C8^J_q2xBy}EGCDCJ3OVGkY{j#~e~(lNYc z^B5TQ&EfR1=waa=%sUwv>TiZ~XAJPc8avFIrz z& z@?-5AN;TYrbG66o{_imDZ2q!Ue<0@Gqg2bHbiz63bgw5ImGF7>-$&5S=AoAq|2~*U zZYo+H(JOaOH_>x$`xDqx`8`S684n+peG^>vM~am>Ns}WHZF1sEb?-;fPIJ$+zMaue z_+a%9m873&Z<9k`qG#@OEQ+A><0pR;l=ZdNw<5xchobiRO7TFnpGZWToOp-rA?$wg zpJ4v&-$4KUpWtyknWV8AK7IOMBY2pH&v?;SSJU(OoL zTs1Ql$WIBHlF1`De@An8K4VOdzF3(F4D&=7O|46PG-5n(k#c%2Xhr11MZ;Qr!K zzw1XQea>CDhNt|1&uKvHK>&?_=YY>aG?f#LMC<2)i7%$WYD5y;U-bg6?^%O;$xX+6 zHpnfon@;$UcE9t52&QD-oc7!P>6Cxmc+x)(xeUYu@gkG$85g|P=WuV^-gCHCE1v-O z(Ct0%vqr2X6w3tHP(63qKXU$}e+2RouK2Czh(|Hs>>l^{?Y)Y73iBNHGJb{ls&Dw8 z@Vp$D4nVvLdB=T~FW?$GV{WapOKV-Ak$7e@xa&2q$)z=VO3+F?6aQsC(--j!6jL#t z5tS*pW-`N($`ROidmQ%M8i&sxj>37J1@2DJ9@|XOVKfo%rQV?*-S_H2Z76Z-;gcd< z8-wc}*_gH%zwwSgxL5f4kV$oVLi`T>sLVUGs|?p1stIT7hhf%2UrfHma-eliJI{~9 z1NV@sbP?~5y@TbrUJ=xS&u)%Jof#zNH`e{Kjc#}_KBO9L$aC2fPrsqk(+_IEfx;2l zI*%oTrR(ubGUtFQ^bzmXc)lG1p0A`VTyHT1&+&=HS2E#S58D){Z<1e6I%`~GrLA`L z!h)rqv~|y9MC)R1wCK}b6Dvh8t#wrz&fJ$?)5!Qq1+U!XQk+iWy9t5L_firsUoUTM z1g)DFpYIh*He>^3g9D1vNqjfs8Iwvv&1i2zzo#{7N3L0*7T51Kxx3+3y&mQ-PV6ry z3WpV?mH2MMGbNOS+YOOtOGxSSRFOHV%%_H4c;ij!^^Po$ZJ&)trAwD~T2oAC(4~W0 zon25=Hv|VSSYhRc*lUi64?35Fo|h_*)=o2CrSKX&H$*C3s`OJ{Iy}=ORcOI=&4=-f zK01?6ie9O(g-a%4mUrZm^tw8h&k8)NHC0+;r4yeyDEGd!sUPOzdQ3)}TukfmT1N2v zLW%PZ>Fkqu`K;UG2&bza=n1YpKXc0jk30Lq;Zpg=3yZggJ67EFGQI42TZ*5(xMp3P zcUen+!Zk#D4~Nnn#Pb>Hj6`AHJ3bkME9oNuE0eq8^Z4BV?30NURoEIXC!>-Y8H za_a}W;cYx;F;PC(O7(CV&$*2C+)k81GQ7~U1GrXPF0`3+@HU?B8LRwc!ryz}9W1vW z*YYo4leoV?{e+I;PFRHXN4#v_m#e@%9J^yz?$71uR4_3MJq z)82u2`BL2Z>&Ccy3xs^d+G8LNKUWaj?}YTs!Sh@;d}M<~3HaLe$tYAd$KHn^%=5x^ zLwK(N?QIaoNnI~n;R-)5=0kjM!ZXU{0P!SMmN4w}MH3hubY@g2&mufymUJh4hOX(% z*iJ(`oIEeR4qy2EzRyR(`BAU_iCEWkMi)DCU#J`5by;O^-2E6*d9VH;{vMAe82OO+dExzSJZmjhT4A`< z-VLcR+Cw7DUwnQ1)YLZ%gz1geX1I)dEoi^U-cLrLw5$(C@vLZi<|8~SS)7mXp7wtT z^J3Sny4d?iSo%j#dL+}bJQx=4`G|MDCV{Qe-kYl0u82J)owyfg;lk+ljrQS;;NG00 zan7J-d|W9QipbM2+zcyM#kU(u+uhd|kuDZ*!gIOfU7t9WxUQx;VkheP!X*RH9uNAa z1mi=D=`6ZO74q6YvV(IM3~=PAIe4a;I4|)%?ExZwLySBradhJ?$+R z8E*tSZ%)qh$vkr$_hSfYk9Qqw|BY1m#*K;l0CMT1Gw-O3GjYyomz$UHIeNeDA81Lq zzliZC+_RJDx}&3Au|6>ApUSi1S@H7{)a63{5?!A@ZA`c~DG{IX;tQ}oN<@R4_^Vfk z!gSWv=@gOcYW#-4_;~cU0OIi@wf8Ae*io#92RC!M;yWpL--G=o=L0&*C}f_)V#WxIdEiztOp->v69x?THJ0w<0p# z*!V--bMTk2vhtyVT^L@;+Mok6{^*fA^WsMDSIH=Yk9Q zX4m95$(+xfJRh~Y@NDNFC9z*jV&7i$zF4XJdhnZEbOyQ0RxX*xnTG#2N(QqEe+}IB z$CA%Wm63Fq19^AVe-YDV@+@yjerVtGj>q4Lkin-<|5ozZr1_NCyBy>hd8aktxVLhR zLV2b|uM_u*(>L6+k7vrifX^D^KQGVDb0iNVo@F22 z{}8kRSKUA52<_?HgsV1U{|oU?zks85Z~n`mR&xJSkbh_Oa{*V~`Mi~ZqwXB;RgSs; zjbwm(i=V@9E99XaRPV*_Da7TS_-8--UM1BB`A{bs@f-?Ce`mbL!uWlQ7XaHvv>8(d z_qZ#yZysqz+9x_9>iJl_#tR2e{TIPr^&`x07w*3=M{GcOcr+vvq=jfg+FVE@(TX(3 z_q?$s>;AivOei^yhyUo<`3)k@16A7A0EduR5)Z_ z1|)kt2j+X3wt)))$5-)NU8gnly)Grc{gsP(oxTuoW~5y>9kBiMlz+^M=kLI20r2~5 z%VW%XIxzlIwd#V4F#d&${;@q8{mz|MzrUApx-R-{d(NP)l(YjpJL`p${?WVV{dk@U z2hGdignzUc&jd?*e1vRhZ@~5C%YJKhKAszv&pciAkK$l&?U&Q32k=a_7q0n6tF`=n zP)=Ly@3)ffNQbG|z~u?^7x25Qck%2z;E=mAARUq}leGanD{&8gV;4B&gAA;DlF|P~ zpXI05QTH73M+T%*IW}4cJx5+u=pA1O1 z@$`>p$9}I6zw6E+e`P>Ama>5ooJ+dnH9x$K`sa|pG9Vq3uEoEHzTp?|qVDf{jR=Gp z@cSuQU-X_A-d*c~)q7m9<08)E*I1#*X3SezkgiGR z%mzxm`nd>@Z*a?Egxv+>uwaD;ewpB$BetKNVLF#@*=9E!xIYTTPD9?wrqpYgJEi}D zdk?%rkS}mUZ-fn>JJLGKli2~`nX~+``|>yx+YNamqx;^$d!+wHzxhiKy+a-Z2*Kba z&b|9m=Md~TXM2fLi}*m%NWwELQ_-)SG&A4!LqWSKC*?-^e&p>#-S<7j@5Mka z!l66Xr2F-9^qR~wZ{T+iZgd)RQhw;~Wfh+O?x(2#5cDA?(Ek1TDs#%;pGro*a-MyA zjt@!7m`u0kFJ50-AM+)PAONZ$q8<@VIa;JMpmG8N!rW5>DSS^7^ z`?zAX58lLYiDpB$a?7Oe+2x8niOxC%rN0xXR(_veO#i!^a`l+bb2ppxIU~~s)PF8O zt)my-O5%HL>F6)rwCi>_azdu0cluUjF2IHI#NTO1S8qzCr+z?%xj!f5O5aD#1z5i` z{x@|}(cNn}W=!9BlyfF|viTp!erhhrRmpF}s+CuRvj<+EsZ76*d~?UaaVY5=ghu>E zEzw+W?}6*p23WN*@%Lxs+QFJFPQ|}ztA>1AkbkYi-PS=x`uybUcUy+y@=M<$rtde) z)pM-p=$pSyF73;<6~B|LbvTJSi6v*b;jg|PfYQzZS-c*k1BHoud`m#-gKhq7$+Wr7Z)nX*aIhLqAPar=}85d8fi7_2E(}kEPjv+L}_isnD8g znl98o1D)WS!qmQh+|>tbKT`g^p2?r#^;_*h9+Tlm=jw#lu+PZyNCh7_YYodb6Y@-k zKc@3;cL!t|Ivt&G;EYw3aTh5)&6)3oE_Y{Ad{*_#`-ZDjc`WSsmEpu?PGnyGnbxPUbE2wa~F7F7Jl=c z|)JSO6=#e(1K zRA8>Dv|%79(_nd6_URj@xZ{}gE%xo7j6vI|F02c+_0xxX&jggW_QA|7ui5$xeJl~L zI$I~K-jMz{FzK=t?TE_jMcwcIT>cowlK0)vG8{^qh~9ML``zuJHq`X=z+3Mq|2^wsXLe8(7*-#*mSrX9+B3w+_?iEue5uO#1=EL-IQ-B@Qh4|R0Z0RyQ20T%*c1`6JH zT04Pck&3R=NHY`P>Dq^NQ@jfJKYT{HwPkw`jt0wU9PENuQ{0cXW%qbUHwx+_O5c>X zia&tNw4mE{SBN)#5zPsuaeh8?lw>Z zdQhfw@ZCm^J~I6b$&_Ts_&$10nfX4(2jxe3_u1Ov<(JbNL!vyl>>3Z#%4k*!>)oxO z2AsOA-St}3Hz)rjzB`9I;0=Y=mXNNTOBECs9(3-LGy51=H9XYLB-{S05V^scW>x=1LA>2KzOrMA8_06|@ zu=^AI?#ML*T)Jt11IH||D2?$1YU{g9?LqzT`&_yGB*)L0r~F7D%MN`9y#9K6^TE(eEPI;1&3$&rl2n<=(3keJ&L$xJvAQZ1ylvN5M)*5%CjIfL zuA%m%Xf->U;qAB68yDHSLs|J)(f_TeHv-asI`<}9bK2vbXD=ERqbD9e2acrIPoci2 zh2)l`{MN2duYKIG(WaPu80{m-2LZE#RPW8>&1uh}UAm?Z(xL=C@%W)WMk@6aOG9l8 zqcItIZTu*`^#$ucv@0qfM!OQ&YHxy9(_F)S=;&~mKP702mB*3e$*;*^x+hG0lD)^u zUn+bQeQZgZNO4+Fihpy)Yh3+`_J;fb$dV-oz?YSitKl|Q_?T1 zJUOzXXT}wVD+TGlSCeetU+#q7f!3Jxjo2FD;Nj%@@YJ>#EzQ9^t>caG+H2{(s}h}b zc1R)GKs-5CR(Au1Ig&kH+^vCoDsTu+hE)gvQ>k* z8SbJFkS96TO$dXE{mlb+Jo&y=oNew3%Opk=wz znlHyBzg5*;X|*S6>Rg!jwd;e5>xgtta!>=BnmaXH2T(ss$n&~&%Jl<&&^E6wp> zrZXk+v18^ao{ws@&r0nm=~yitshw{{Mf&F`DXDvU|Lf>zkCNxuSOdKOe)?nAO43U0 zlwRK)MXTC6Q#${1=hB~RQA)1VCdJ~fudfx}c_+R3E2Ze9@q_w$T})bI@sNCY9k{T5 zA)vG_r}FnsM?LKSEWP=5CFHtfi5o1#jdA&HZo>UwX|7>PmsXm`dT_s2HGM?un{=)& zIPCTCSr%>Jz4u(8A8MAxXG48waO^oP<@ z8w{Jt@8#QgXGa?>SdiXY)@1en7S4B6J?%=0&SZF!de)6H$uvB{KH;nkw6j!?o<3y+ zhpj#(9*0d$uyw2ab9<82xA6H@D;?0++maHU>F93)_0NFvVb_G!yf+)0VBa3AYIVU% zT!+$%ZF#!_W||Ah5AzJn)(`7E)9Wm(niyzH=Xptw3)hYYi@_FTgDW#gJn1` z{GhZK+}PG6;-PQ&3Y=l|5WC|~<% zJ(QO8!AG0Mlb++H_UMB(cDQw807mdVL=K(mKT|8BBir|NFwoNi_wM$=u_GqfhIIqA zjZ2oeVDVx%EXQ?0n>LQaf&IflIkva7gWE3Dd1Cro^@m~6-G3I;J?Vc|;jckW|L#dS zBu>wCGLCgZ%~Ks*x}b->*p{uwc?7C|7QgF;rTD%cVc;}ztzO&2IMh??O-$Fj0^lgDTMZX@Udu9V`?*A(o z8-F;hY`}|tKONvf*BY2z^ju8cGaJ}b`F-qKy1&};rfj`4{gbUc>HR2g<&v-Z7V_oAI{Xa2+cTXei>Z&;gYSsKJoEdy zM#O9G-g_KXaHRGhQ`SARl@+D`0@q!YOv)v9`^k3|(>=3+(+&SCFXSVoV?XLWAHX*L0Jc2(cZ%zt z*}y}5f6NCtN$}v62+Jv6ec0FAS@9oL(>>Y1+}pnnora@1B^McbCEZ(OJf~^CYx{Sq zs(V%kEG_v%+~0Xy)3QiXmlnUPt~<#zn&bai?f;du{#TX$2^6%vm2|qz<=?{jrV7o; zAT=GL?p@UnQ%kFze#4$8|2Zi=u6y_muul5%iMpR8m6qy)qqYAtPxN11=66;f&2{}B ztCR2lAC>=G7#}YU>uZeW)z{q3mu zD_jd$7Nxh5(L3<|y|2p3gY^aG6n!E0`-6_^#eQe~4{}}q)c@aI5&yk^U*&(ncKdOZ z?iugh#uG5__HQI*12i_o(&r)$I2(QtaCLkMY4Ctp=zajr<(#bl zDR>-}L0!e;=TY~1)W7_BLYhx@+R7trAYT3?V-3Z70`874&A?y4UVV?~V!-IkwR@tw zRsT6$Y=y`>cWw()zgs`Otbw1NFq z|2;=_kGe*kGyR9^fZ9t+$uA%B={KK)1$Vv_(*_pZeF4uBEXYm$yJ{~Bbsy4yj|p+w!xL= z*WqgO8@NwkNAO)DXL&;%ldh$H;BNe0&hjOAf8jjD?h}E%=6h1QXJPm*aGJ|6w)}Eh zHmF;s=U6!4uKJ^Zv(}Q+eDbeL{-<33c~%FYPGjj?Dm*a$bPuvKH)LOfl^iVHZaU%+vqYHkBLY6GY{aMrvK zOTTi$1KxXI<~ERv`bWQQxc6l_I!^QqxN5K-DCccKuDn;EuE15BmfwqQB4Ee9r5lr_pc#yp;so-z>zp8Bt%9n1xL)jhNU54Quw?7$P0DajUPoC#f~TLyHL zehDV(e*mufCE%<&itrF{uNm5e1%cNS(e)(B#1fJb%8F!0vLhM#?g!WDrmGXF^nXTy z&mcTY=<9Tczvn_I&;G9moX>yi_k8D|&-s)6{)trwe9k?a{qDj8erFk8Hy{ik*bsaO zz=6vkjt!8Fkgbr-G>{DiZ3%6PY>RA+Z0&~M{(PRfQBJV)6egb87w~**z27nWrr&+y zpwH2a0Ef`838Q`{fv(v-kLU7H#Xg$J2FVup23&tZwwV)7lw(95@=v_5*FW*|<9^r4 z!#;=kP|)AWr#WyLXvzl6$Nlb;Xsa)g&E}Y!X^PzAe%tf=1MZiO`kV!aeRl5=^mX)o zAk3~YeDa!M9?#{Y8T;@a^*IVY4@|s7wyY`V(l9Z{{O)fa_Ph2T_1Q-efZ)g^7GLvt zE+4T?2yI0x_JMSal1-CsYuNR&M%rqC8O470-yQSYi;khMM}tiwO!yp+kCqvi&y-9w zWFKC85!pJ~{B-}33@-ZJ|8UZ0uQ=|r0S7LF>0$$9^JM$ewH`R`AN>#KefCm(HwyVZ zVFpwe2*cSukC&DUm(P^#NZE~sz@tSUpeWwSrH%vuaK|g)9*&W^|h1!(ZW-x zUk+Rb+y>&?z)Al|;mv^kYxz(IZ1s=*<7xl66#+PK8E_krZ3EUV=sUSu2b}SaK35PJ z-G0VD1{}ByxDBLc16Td_?c__jR1aK)@jp1_x7|OBzR!WnfZITNHbA~~27M|Q>VSiR z(Qh8{k9D2%j{*lS18xJ#*Z}#|0pF;m*AHIsTEBb2KRR+AeV+l|2d^1s;S95IhFLhn zESzB$&M*sSn1wUU!Wm}a46|^CSvbQioM9HuFbijxg)_{;8D`-Ovv7u4IKwQQVHVCX z3ul;xGt9ynX5kF8aE4hp!z`R(7S1pWXPAXE%)%LF;S95IhFLhnESzB$&M*sSn1wUU z!Wm}a46|^CSvbQioM9HuFbijxg)_{;8D`-Ovv7u4IKwQQFv7@rpY^+1><5(kO+UQo zA8}mpk06~RfY)IV-t%}aAE7;voe1x_eMHJbLOzblfa4#uR0rVR=N}`T6GETo*HiT$ zmj#!F!Zt9mH&FXyEz|*xe&-J_q3ia!#rMWKNF1s z=MS@42VC}zd>6ri035gsxD9072FUlaG9GX)VEpD4--z{!&k7+JK}8&hk?jRO3tU4W%z)jC zF^><)NGun2&*j7ALpH$dL5!2Jk0Srrb~XC|$Gql$B>FgT8E_lWrVSkR4y&?fsM>G) z+I64BiU8Mr!vf&$S0T`sP7*)eU?|07z?QOkA3B)&um422teNs z-ZRYOxqO5+!0duyVH{nvaE5uj()MGm35=(Gp5Qg#@WETCqnLntiy24hcs!R6x1ZQH zprU`^T>Zgx`TzwE{kIFfCg8wjz-=HmZ2)~ZrL)C@-{kR`ON-F|IdBC@PX6@qv`f@Vw0VO!!cgJh4U^dI~ zRQ<Sl9;0w-eO?C0^6sVw7133^6c|=kg&N;PxSH2io+H5}#>r zJpV5a3_V}sHH;zvhy0Mi9j6g4^$o+3$`ROea~w9Ga=_ZpoUmf28%KR_}4ahld@obmyW`j77N_8nxNQ|AF|*!Z5S=~4?Q2N4yf>1Ub=_A&mnhZ zK>m8@@d#`@>VSo-Jn-rp0r=%FA!Yd0%K&fB_rc1YF8KKRI9$*TL-9oJ%4fRUHu-og z|G(!o7LrV;!}`yiaI)44 z#crw#vd2HknU|l8{EY=t{$J`JcmZYUy+5f-A*O5LJ;_{n&*LNIkqP;fxD0T(d<2$a z|DD=sIex3gGgKFBJ#B|;L#AN6A+!r72W}skw-2EnMDq8R`UYMQ*8zpT&Y!dABJohr z*YA574)HSe3=3zN$4|+IWWnUZaC-8&J8pnI1!KWJxf=eQYM#{ZScUHh7utp=?S=8l z_-2@eGt9ynX5kF8aE4hp!z`R(7S1pWXPAXE%)%LF;S95IhFLhnESzB$&M*sSn1wUU z!Wm}a46|^CSvbQioM9HuFbii`4<)|dpNswfp>OEq1GH5R+LS?wLm%|}nX~*^YwP9I z4Zr#o!1ApwxU4gCThgvSofP{20E|EL&^u&801n!c!2{0_9DQVkSqprc_Iq*NK<^|Q z_B-IV#lUS!dwyjipJ#QzQ=j2m550q^Ck|SY!A-ppmTz|Dl;4Z%ftm04;b6%KJoIqe z(vodeq0ck_zvk1=E<@kvpb;6MuaM8Y{-(D3`eI*?cfSJP32qM?xQ%JV&&cPQ|3C5S zkC!7|9I`Kid*ef}c8@cs+x%Gdz*}>CaJFuk+nEM@t-`B6!Ti76JJ9?HY2uJw8QknQ z!n~#4y!3rmC(t~>KAgWO$370Xx2(#bf&9PKH~jo#^!>-Ek07RNDd6$aa$&McQ$80P z%|P$U%un(ie~4p)+PCmB4tFNiQZcl4<%8D&6cxWF=TYlU=Q}f?qf7`d8cn5y+1bsOS=+EJ63g`E# z%H!ArOGei0YR&f!T#1QyP)sDALU zaE5t&MEw~@J}1h^_p`Tlm-%3}@iMFf%ItzVAZ&YNOU$kq=JqAmz9RiPT7J0J4}HJ? z+6=F}I{o_mvFe&tI6wH<#r(Y=63LIpr)PhY z^4o*!(+laEL*`^~qtgJd&zwGEeX)FA9KPvuJ5)|&P7l-F=E?V~y!~ZQJ$=9-Gcveu z9)wxApI&VH{66#^wEx-&x5bS4V}Dtdr>_A4IH)g!N>@L;kMFwN*JF=)(E5PO^+s-w zneoYnYENH30&q}Y27Atqao?A57SY?dPVnLI0Jll?{gQm&9`gNOk-YhRWSy9vJPMi& z@bb%iyiac5XU#SzR8I)&GdH12lZs|#MTFM7khZyFQe@j)(o>sAs>2=71}eujy?7-B^w$uDeBNeZ4CK_{eZ_u){`>rvkv7J z0Uq>oVHn=?c!3O(kk9o-1N;)t!;`XW9wuKdt3Gm0*<=#(585ra-}wG3^mTq6DXTQ) zvEswrKD$dZbrja2H=Ff}>L#L1OHa&Ovz@+-lMDwm*&cesSHiW~-CisfxbZ zfO#_z)&~?u_biUxvuD^nkDrne$!5bYJMZ@^=>K${@V%bFpdB*XV|K|fvq2s|)t<@m zPbN2xr+hdaS(F?4`Ix_gzR&ssJCBSvuZ1tt#rT-Erg@FOK|$$4Azi-SFwT-01ti`g#E7F7m?S6%(*|9gAy`WvT{PRJ~V5t7xtYVh1)Gd z=tGgZZ*+IVBYi)dE-}N(jZRJ11=p$#++G#(*JjLzgEBIx9PZQP9^W_L_QA1R!%%PU zk*(|EjzL)Yp*Ghpe|&sYHXq!!6WewR)&WvLvXcsDVLU!kK35(aHP`m9+~9&I#y&|s zCe$DH365jEp|x{Lm#lOL`AJAWw_i!UNBVEqqXHRb;w?X^Lp zL#RukJ}F#yA6vgSYxSBsuav%(Qa9EkoBoi!$L4Q3$G4&lw0aN-GvN1AvXCnul-WuR zt@mHQ-2u(6?hqde=<`Ok9$vw-`Lr@FSll`|r6UFS3){Y2eKTLl@#ouweBd>^7h@(L zaX7o5?(r?|E_i#EHqP~(_pS%(Mtg$(p`?BeU$JP$|IgepE6yL;Fq4-U=d*niuN=~o zLCtWlCdSNZ4zI9oNELmyxVqrIRoXbecrT6ta+^)hW=VeS6FLOK;%j~%DVzB6xQG2w zE%c4oZgHrp&%#=5tZ&+|!!EMj`1;}Y9ZNps{~Xel!PQ52)`yDg-sI*@{+TW>@zS@s zb+CAO_SZPPhjRe&=;U$Sr&Y57^nqGS4-_{K!i9TAIC#+lt8vYYTpv~IIa=S-ihXp| z_?~Phj%Z*UVD@~k>U^p#M=j*vfjZ?7Uj}V19X#mlhvV0W;lrJFn6tnOztZ4Y{+n^W zFB7`F-_f7-y6z2J2i)e2ueVIdBG=34OyK|1HJ)s%2>a4I4~)U=dETtsyPQ75v4Y_! z|5rnQ&9*L}Ie0lVsrAgO`0Zq(Lk<3fKAr#hccKhAFd0-1^ulMStvTcKLjSt*(2y}* z(z}MR>`Y(NcLJM6$^W_SrXvF#$}b4+NzCrWIM3tnxAnuuT{a-Q%Cs#jRo;yXeMpyL z-O;_m`kS}NlT|sho;cFHq#CaCqFx(A0KOf8K_Fi0H z9J@LkTZanjuidVL*IxH$wf(2}|2CU$ieCl!lk|f;&A%J@>PA2Bc6Wp@T?_B?EWQi* zxpdC}Z{c|tV*knY`z?FMQFoJiWcpM=f0ezxS(hD+|93jcZbkN*=k^=)O-20^?S*V5 z7LYx~iVNCU?!-5kx?sb${5_X@Hjc^Zf<7V8i3<9=a3}lwoaf-2OsLHg@G51e@%YEM zo_O&&X5W&2nutFsndMlz>rC`7N{~-UJ5W%k>oz;G-u}P0!ksjq^@eV^ zQfh#`M@C>R@~{lo5H4Bnh7}*U;G^9(IDEkZB)<;(WPeyenW?RlRP*ma9`hNxT^(?; zz?@g_`SI$J54YP@)n`>-k0xaKA@<|s*lm}y18zJTg4KAI5zQIIlO2nrcSEWps``6m zl_7@?{!HfU8J(jOcI_XNrE{kHT*tKk??F?)YIeI7#~8FAqfd^FB;@fv%7OX`snSd5 z9_>9m3aul$gk|NbfA?bhpO4Tvu7eFYua~Osb2;7OWp3yk?^Il`w=0J~GdHLu+jHkW?^@A4UJH~XddPDa2<0P^xHRHGM8;2fz z_fbkePrG|HHD^M79I{a{e>z(yY}#Q{yxxBe*E`=V*GJU>;_{i_Fq{kVL}R{y4%@vo z8a=mGO@Eil`?KRCis-ejzZ>S@emki$vJpo8@^Byz~e)Ro(fgVSPs`^*UuGGx;LZLA&{kGto>4L@C-|sJ!(USMwl01EKRQ+$S(7va8 zH3O36=cXUEBQLRGAXq=-K)kCR_8idG`MW}!&DJ&9>r-8COdZG5Zx{BZR;+QU)}E#+ zlYNIr!u&A$?>rgI*x1C(xBOw5vhrj3koM!-VF3L<4CqJUYhgIM)@c0M8z#-!X{z?E zbh=e*ozU-`)T>;zR=&O;$qR4#rOyH{hB(PI-cEREADp1;FJz!==Z{{?62{YlGUrP;ksXI zXN2FKxt5)}fBj~AWWCCKK!$vy+E|u0)BYtaHwUlV%sQa8pi;KN!=}FHxoVwr%KsDS zxw4}dXuMmC@>}>h7JoGu*Vcx>bw*5l6oV;R|E!T)PG=UB>_iUShe&MyK%@5M6 zz4(t*nClVhH(#!4T~J+bw?Nm@`t7lG0FAF{mG3Ke4G}!tug&gRWV8>lvb}ebChhr0 zWIYm1-=M1v2nHrGyBA|E#$Ug$jkBn=>MLTpsGls>E4ydgw4ZrW~(Yoj&Y z-9YV`nlz-7_x8PGQS>icp^f>8(^q5Hty=e`Awkck67>CAZ8+O}#xeURY}a z+O-qEX`AxvyM=np*K0c4w0oo@O3&0zX{8?>OZK-P4Mx%3(ANdO_{G#5N367&3Mcv2 z;X9PHZ)?YYg!Jj%t&mGl+18_}IzLmkr9?Tj|DF6_tna%O+HU)o;u#%7&bBDJ8~b$d zixm1nO2{%+9^3bfMbRmiH@{E4&e^0is?uj84W19p)gxmt0JuT#@~|B}i9 zNuTO8YpefHx~DxtP5oVx^lH)X4cPwY3N-cWG;#KfmVG=PZS!%Rj9AwdIHvYXTpJKC zT{h#^ZL)(N>nJhZbU)~5gPCt?Zw_exQL7}qQatc5Y28Z6<=_d6X6-baJ|eel8aENk zbqCI=$xWZ>=V&gnzN;&WW+4wNR%@^C^5$DU=o@Q~OSh1RY+f6&9t{H4vEgeLCd65e zAGEc>tl4~>Z#q7B_Np;L$K96pte@i_kDp_>7Ab6#G3@WqNo}?XH%f-4XeU34L4P9t z)Q@jLIuTp~%&sY%0ShC{u320L(w9_Zcb#WEJI#eSTL0VF(-qR)r2Y!-=r!B+FI|rF zeKu;71a`^nOkjUcntC+J!`ct+Q?xU>85Yo=h`)@+6J0`JvKP{wrT859DSnVSff^E4;fzd;1k=9I(^aF~x(>Z`idA{=za~X@ppb z^uqi!dObMa3iM8;Ro`NCQ5*X{{QexAhUU zit8=$eE~ktbf6_nbct!z+Px`(&ubyRyf*van~G&u zgJZ4mw51!4oi)S!44+ZKXr?hTqDyFdMGv$&=fC&!kqA2F*xwMoCoFzXdwqb9wvU4x z`h|KCUuPmcm0@x<2Wbsm)4dq77k}iGS(EiU`F(X;*QBfx%SUf-fg8m`FbBWW#e7q3 zOk?OF8YAi3^O^ScPpsWw2lC};+e>VpkL&TfBr?D6p|;Ff{%>KQb&8)z`A74Zt#~R| z+d%KGOr~1EbCB*c#_L|s?V9xezgqLf2Q%Ecrwmp9PKwNtL{x(W+rDd96YY>Ui(iE zT7k+D>sM!N$2HX2+2^}x@dW4$?NM}!=}vTS!ut5*PqfvqxpYIHHeZSO&gb}A>|bZn zzvGofW4aHY_9>@N^vx6JOsec>*nijpCP%dIldasZ?9+@)X-!A_K!;*7HQ+nerY+jp zAGUknh+@2{;pf9>50TI$y3cO>&Ei4L*y`EKMwE^6^0|#;aZ1f^yoPq4zC=N>lA&`_MKdO>0@p5ac0i+fzj47$tUq3wi|Yz9DmXIq?X=-K2Jct&#t9#w&J&C zXq+sO-9M`7$%uUF+I6t!(-GPGT3*BRu($3Qhl;u$7Vxd_Np{txZ`Z7UNy%DEVJooRP2`(u$J5w!Jl|47Uiz^XcvRg3r_P&T z$L>-1V7&uK7Hifz(BH@4D9(wNKI(%3OKaGNWYK|sF>k&n>oVPfbK|n;P0%jI%j3>{ zZLAruZthZ@*LZTs**LV-`3tf?Pj&9HA#e0&9BEQGmc-sse%37b!|GZWP$$(n^CCz{^8cFualv5Iz7g=NS{!MzX+LO z-(elDDN5v@YRBKWGnhN}6H5;)oMCaljD<6-K)eC_!1R8rwtnK}Pv61OkG4$dPeJ}7 zY?bB9umbXv%D-!hWy+6I#F$n~L^#vA~dZe$U~2YuJVuuR`BqVcD=RR;G{!3KIgO zRTw6`Rw;f6$F^vknAM}%zzgxr{IIPGc@|!)#D7=^3g+L|lYRgHV4Xc<_Em5z``?H< za{>LIbSShL;kAPLl`JDp{v$|-R>7d5_Ft~t#q2kWxG2Iv-!|7XL01f4g`LTbzE7=i z>8c)Tn{Zv3Eo2vB-3j%^^vJ^I>e^4B$iLhtb8bmJKX#1%ix8ge& zqf2|ZK|~wfGaz5hZalpws%?jI^FMpuD3A@43&X5lW3pr649ALZgxkgaFcZHYFPDyZ z&r#pLy1q-Gx9IZEUCjQR7~`P``{W)O4tDPS<4>x2KA`~|D%ouIB`<>^M9x>aHphy((a=5S6-o= zZ)OC~`VYPzN79+9d`%-wuu=niMwz|s!M@g%zfSR(Dt|05rP5PAS_aE#6VRD^OrF`s zWUr5&^rS7bQG7o=cu2jq=~P$U#X3MrxBWvcnmYgQ?YDhUk9B|)t;vKLEzPiA1N(i1 zHd=6fC~dxykwHE_yGA4DI554^n&it@^e}2qt?nE$w!oIH>VLoX|%wLkcP&V&Xo8`MF#nr=Hw~uo$E|G{s8Nxx<(2A ziqWUCjy1x~!hwu_$8`Su2`~;fN7>-MPpq2r6_V*kIKSE2(Gf*&EZZjDPMtPoeVwS# z=h-!#|8E^{l+8;l9@PxDp{>RODmPXfg=Z?hx>h@905jb(Ol#Swe{$p2An1B{?;g& z8qCd5@w6NEe{Rvl`4Cb(P#DeUFnLSiO!<#g*|`)(?Iyh=#*(v^!s)yc>RT_tZ&H%K zQy)Cr^brj`IKL>=x!%;G3EC3LhU!;pM;GDuN=T-&XM3`~Co~bwEWWU4AVTIs`Sa`e zeG9RCKHj5^IcuidT*F6>nxkxS!-v{ACqLKZnTTE*BO27VM9Exi*Zf{yJ@80Fb5@CT zuf=%!&c-OdiwfCp5c)~!NWYxUZW@A0vKXpR*};hBsl+ns~^M$^&B zbWclj0YZHmhqbX6C?EAwjB~G<^bet4*>$4wL(kPm*aSJu&cU$$wYbyZBpOup=1jI$jtrYBeKw{IWg_L@Q+AohQzZ@Z%&)@a~NfNcA2y5$Gg zS@gDr{h!H~IRavQ1<*f@tDBQU88aBbNdcHwvwtH(U`x{)Erm7TJ&`%zQ@M`qBm9?g=aI~jC0p~ zJwRfg7uxgr^M(k0iuKoz<5pVQYb~T*>({q#4MfN`+x{V^{CK{d3vmw~^zuGH5`EUX zI$e~FiuGJjkp1uFXx-Pv^sUAD6Fcq&7t500$L~KVAs?F47VCrGNB0Y6&GN$dcw!qR zJ0^SPGK`j?luW3<)YH?-eLumwf)c(im515f1hmIpN;f=g%7zagHp{lrK3r!-d+xaW zrsNk(7PoKr%a*H@{qnH%`v?sUojI|WHI@yj9Zq}El5L~P%6j;GK!SIxy?d?P_p{XQ zi|b7qe^J|yay9=02ZmF!(^IF6T&6MVfE6q78>SXr{A9}Cna?x-XZ3*)hx!Inwj_+xUivX? zvo=lRI)Kj4ymfOx`G+(QVxP|Kd+G=5*gpN%vr?bG z_-5l1(>5$#06Ys=&^~`A_t`Erm+2(4#`>$Vb1Cy~S!uOSspW6CKHh-Zlxca95aE#R-bX1W?F~Ssk~tv!^)?=D}6J( z2)`%hazwWGnbheZ&VzrrA%DIjKy&*~E4!wh-%ntlk$`N6VWA!6Q~ZPbz4#rO+-|pM zj&q>5C2XhJ)Q=0_2X5gz7L7k-s}GcJxinw;=_eMjm>cun{)PU{>}I-QE9&|1A#={1 zBShm-WeW8hP`{NIX5n_e7O_qYdbY3Pp#=UAL;4QwZbl3<4SYp>9!$i zrlPe_EKj1XwYdW}Z5-G9{-H%!mzL06sZD!zsZu)T z`N-3)uDvbTc4^^zox*yA+WQ;V24Q?eIlm{Ha5<{sXx&fXox*2eN5yx+>8Mh)4xloc z8aiOx)-lc1t;?3Wf`0E9uUDMsT=qB5ty8buvfzVsvib@pZuTTOv5iF%Sk1FmWD7ve@_dX z#P6SH^Srj#Uh~5){B}fbwGQ0&P@BN$Q(C@a{~rIX<4|<7AI$oupx>yW51Z{V?0)k72;V3FpH=uQJm_ApTDioF6WiOGr#jgGnI-sb zbhYbLTCY#_Xz}enu$mf?w^~*CQ$t_s=gDOfU&ggCfn#mGU9Cai-23qeEPmIW_P8OP zud{xg9ZsDvLKUvr8?!X1ZkuD{4`J)$??m}N>jTWY{Tt9>IH5Wne|}idch!$ zzl^|w1ruq%C%p5HH#ir07S|@#R_VZo?@Zk1r`j}WJ175Vb$}SNwBm4fKON(V_u8Pp zcC@xbIgZ5_Tp0?k7x@h5k9O@Gfo)sHU@Nve+qaFvo{z0?5YOK@cg6sB3j2e0sqb$N zjyZ+o9?CDI8zxtVm6NY!v;;mZ`!_LtpVa~H-}@SvNAHU207j1(D?uOSiGX$N2+qm0 zHnqcpd%bYw(hwXwVuDZip-gdncI%`}cc4sZj)B&7UpQ-kyV$nZVV|Df+o(>Nk}0E& zg-K!djF>NWKON$QywbH42<5=zqh*vxK1{|^vSV?ShdS}bkwW<3!Jj7deO3o-dGwdy zv_BEhlZZbaKUIHA=}_zPk)oN0#rDVCk-RASKwB!$y_We*D@GKN}F6VVX%sv^nQ6+4Y=b?@rwHuDXyLW##Mc)t23C=0{ zb!Z+s$m@VSt^>Tt8;AUoLBrr@u;9*br{(+XJzz=kAAoV>CbtppC)@^dzy_-Nb^y)e zG5=Kpe^~bC_m+sf8LhoS6iNpKFhi{z?`BlD(d^J{y0$mBYY32;dKD71Go)n z&<5^yE`wP`&#B`3tPZ$^dxeszkNC6NZ;O0r%N%l723~BFueZ*@^}D~Os_#=BFuUjr zP&crb+Y9#@ZUY*y0rLF|O|PQgKdZX$vpxXLDGXVza{X&S|GeGK-F7=&-Fa}o?uoj8 z%9!6X|0lfm!8gE$dkNCzfv4A+{nnsP31ZR3iev&xzh5M$p6*lJzFxLKIO9dJ64^* z<3sg(cH~2S?(L8NN)x_Mb-=8`=iq7IHbEW0?IWA^F;#Xl_9=dr>5$dsJU+7i&yIXX z$4X#h*}v6{@3TGttuySyeFjr9%8q<^`9gmc`mFGpo)PPZg)z+Hh3VL}Fph<>bPNlh zp)eNCFprPc0h6UrHY}_bhDVBmV{Xjnw1!ueejUc}4ACjR7$02!+PD82@ME3eM?WPX z9n!rR^Z0DYrxpyje|Kf+ztmj2FRp`jRs0Q}lk_yoZkQc$`^kp=O!=|2{E4QiXPB<0 zaD1AnZi`oE@c4A(Q;YrT3$U!@k2LT5tS&g$_^WUqAY@nR@K1Kh)2r`ak?nt|FTlct zIHU_6FO-S!T3PvY8jr)`;@{61-={i&)*zPS9t3F{QC2?DW$^q%ZM@JXh1XK@p)lby z;%gqC4f(X-p8JJ&{#j1>KI;R_yZxJ>GoB3g3**}(*&4U6Y}nUSnL+vcME7y(1g@_s zS&8dPW$c;8(sR}KSsn2Hy?+kY(K`}$#O+6I`$;l?@_l>!G*WiX!_<)3grgGHKm4!%XJ&~-6?rHFV(He3*TpT0PRm;W69jc zlDCHp$c%iyAp_;`x*|bcL4J}iZ9nsW+Lw4ww*qyasNb(i-JLo^U&=)u%lxjULhk?B_y4NC?W+4C`97`L zFKS;DwAE7GN-R_Tvjg5M{tB2!ZsnqXvXgx&LS1|4ZSbDBor-V^w6Fl#M^^TBDf% zQ-6M7_+nD!lMS3}{H0{vOJyGcz}^~XcrKVwF~ z*)Th`Jk+Lf`sF`3dpO2i^+*kQu z)1s5n%W?94LO##@-|uSv7RfLm>W{E{F=p}>hqHSgAJPYthZrZ4PrKm=e!KhklJ>tX zkN!QL!*wt0Q{r;jeWLOyzR!G4v2O^+st;(NquX8)w!38LBO2NBVl0QhX`dc_p7}pa z3_OGKFvXGylP`~#wmCKAL+5^Nd;A@&0}_wN(KkQOHv9s8>9M3wq)Ok@-mSs)g0cKJ z{qSdDX!uH)W^P}Zu`jd-6Zt&zf5HJ*%?Yv-W-IKP-80>=dmbN>6_bY=^6@#U@q2r_ zgXjG(xbr1ZUGQtL6wd}bS^qPjcLp`er#!DT&rG@>l1#nOZkU>zWHu$_MNRn%%M+F< zyw1z=T{S0#z8~~|OU2KbJfyH#CgNv!{8W7)mJQvfD<2oO?E}LXpmpeTXvh6Dv|hlA z-``t)Y# z9N(X(lYH#s58#6bf2w#LK<5Qo@NDzxBufqO>Lp(n`+u+x;Htf<0s6Q-OumO{Nf$%I zm(!Y;ka{Ore(zrd*8pgV&%CF_QC%SA`$7MAH@%ei{A9w5VJ5PLKZ#>_pIPf%nGf#Ns_N7wa(q$`$ zO=aIoxenM-@z=P{_zBmKO8SZAzvSDA{GZ}6eNn7DcsTDHkPXl}!!3{gGU+;CH-0D3 zVSC7ZKf}IHJ}t-hgZ}Ss`#bLE+|Lu)zyz+{z0&-KtaAfs{Su8GcyWJGqI`M0^0o)= zw*MyA{{c#HJz#YO*MstU$VZu4MhfA2>nwaH_%|^B_HSXI;MamcKEJvAFW?sX{pjdj z?)RB*^FuO6UnZYU<`gRnC*TbsF^v$k#{5ieUoJ7Uc3)SKe~u3+~2$NXGZ+ zSpdL3Kz*s4{P{E4*D==t*MX!BpbwKTr}6_&!?z{#%vL{!m&@BzZUaf#0E|BQb}GKl z(%>1v2YDU9+ZJvEIcNjuyUdSM#o+1w3hoWIa{cF^{&`!OR9l%>HjX|^zMHDw3)A_X zRWIk2Y`9-cU%!|px<{XVMd-unTmwACvA~8xUI*|#0Jni0umM+1A^EJ5Kfk;CYp8qv z9xzSX5ANqV;OEk|XeD1&()U?j0eAh6xn1O}UGP4?w7=w+T-^2l$oy3e9B|cb=XC(D z1Go)n&<4;q)$n;?p5Ur}E?{|lpX*^30?>AI)K|iMr~jsfW8;cH!~F% z@ZO_&L0$*&woyxMV?NU|5ooygKQiUta>@ZuFZBcXy8y2P^4agSz?&m5(W8lWUtBMM ztLD3SKI{nZ2WGS%s0EwkG-ROO$8UIzfU7pE@B3n(7vF>T+BXBP`+RON1NOk{*=bbI zqECVEnymAC#Xgzo`+yzS2bdo5HA1`&;Qaz6Y`{`+ADnd=o!?Kl&cJ@ka{+e~Un{JH z{&^nLw;n+s0#{W=*7c?9^HS+yqV>nPPRPRhs?(^i%Ihm7>MLu&-J^(skCcNhHA-M@lRu2S~|6DokigS&gS}a*^$)_-&3{^?50}gWntU4E!DHril5t7z=eJ zj-z`XKjjNtKGCw!ihUp*ML@Qmk(XLF~ws>8jQ5!G?ZS?8&pOekzs=sNOZkQN&20HG33ntFb26xACw1Xy% z+5&G6MRPcLsHdi&4U#Q-(I&zCjFx>TUugrc?s;(B{0=y4e**T0tI*E&pk4|Qmm$<6 q^ds01_ { + const newIconPath = nativeTheme.shouldUseDarkColors ? iconLight : iconDark + tray.setImage(newIconPath) + }) + } else if (process.platform === 'darwin') { + const image = nativeImage.createFromPath(iconPath) + const resizedImage = image.resize({ width: 16, height: 16 }) + resizedImage.setTemplateImage(true) + tray.setImage(resizedImage) + } else if (process.platform === 'linux') { + const image = nativeImage.createFromPath(iconPath) + const resizedImage = image.resize({ width: 24, height: 24 }) + tray.setImage(resizedImage) + nativeTheme.on('updated', () => { + const newIconPath = nativeTheme.shouldUseDarkColors ? iconLight : iconDark + const newImage = nativeImage.createFromPath(newIconPath) + const newResizedImage = newImage.resize({ width: 24, height: 24 }) + tray.setImage(newResizedImage) + }) + } + + this.tray = tray + + const contextMenu = Menu.buildFromTemplate([ + { + label: '显示窗口', + click: () => windowService.showMainWindow() + }, + { + label: '退出', + click: () => this.quit() + } + ]) + + this.tray.setToolTip('Cherry Studio') + + this.tray.on('right-click', () => { + this.tray?.popUpContextMenu(contextMenu) + }) + + this.tray.on('click', () => { + windowService.showMainWindow() + }) + } + + private quit() { + app.quit() + } +} diff --git a/src/main/services/WindowService.ts b/src/main/services/WindowService.ts new file mode 100644 index 00000000..63ef5fde --- /dev/null +++ b/src/main/services/WindowService.ts @@ -0,0 +1,194 @@ +import { is } from '@electron-toolkit/utils' +import { app, BrowserWindow, Menu, MenuItem, shell } from 'electron' +import windowStateKeeper from 'electron-window-state' +import { join } from 'path' + +import icon from '../../../build/icon.png?asset' +import { titleBarOverlayDark, titleBarOverlayLight } from '../config' +import { locales } from '../utils/locales' +import { configManager } from './ConfigManager' + +export class WindowService { + private static instance: WindowService | null = null + private mainWindow: BrowserWindow | null = null + + public static getInstance(): WindowService { + if (!WindowService.instance) { + WindowService.instance = new WindowService() + } + return WindowService.instance + } + + public createMainWindow(): BrowserWindow { + if (this.mainWindow && !this.mainWindow.isDestroyed()) { + return this.mainWindow + } + + const mainWindowState = windowStateKeeper({ + defaultWidth: 1080, + defaultHeight: 670 + }) + + const theme = configManager.getTheme() + const isMac = process.platform === 'darwin' + + this.mainWindow = new BrowserWindow({ + x: mainWindowState.x, + y: mainWindowState.y, + width: mainWindowState.width, + height: mainWindowState.height, + minWidth: 1080, + minHeight: 600, + show: true, + autoHideMenuBar: true, + transparent: isMac, + vibrancy: 'fullscreen-ui', + visualEffectState: 'active', + titleBarStyle: 'hidden', + titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight, + backgroundColor: isMac ? undefined : theme === 'dark' ? '#181818' : '#FFFFFF', + trafficLightPosition: { x: 8, y: 12 }, + ...(process.platform === 'linux' ? { icon } : {}), + webPreferences: { + preload: join(__dirname, '../preload/index.js'), + sandbox: false, + webSecurity: false, + webviewTag: true + } + }) + + this.setupMainWindow(this.mainWindow, mainWindowState) + return this.mainWindow + } + + public createMinappWindow({ + url, + parent, + windowOptions + }: { + url: string + parent?: BrowserWindow + windowOptions?: Electron.BrowserWindowConstructorOptions + }): BrowserWindow { + const width = windowOptions?.width || 1000 + const height = windowOptions?.height || 680 + + const minappWindow = new BrowserWindow({ + width, + height, + autoHideMenuBar: true, + title: 'Cherry Studio', + ...windowOptions, + parent, + webPreferences: { + preload: join(__dirname, '../preload/minapp.js'), + sandbox: false, + contextIsolation: false + } + }) + + minappWindow.loadURL(url) + return minappWindow + } + + private setupMainWindow(mainWindow: BrowserWindow, mainWindowState: any) { + mainWindowState.manage(mainWindow) + + this.setupContextMenu(mainWindow) + this.setupWindowEvents(mainWindow) + this.setupWebContentsHandlers(mainWindow) + this.setupWindowLifecycleEvents(mainWindow) + this.loadMainWindowContent(mainWindow) + } + + private setupContextMenu(mainWindow: BrowserWindow) { + mainWindow.webContents.on('context-menu', () => { + const locale = locales[configManager.getLanguage()] + const { common } = locale.translation + + const menu = new Menu() + 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() + }) + } + + private setupWindowEvents(mainWindow: BrowserWindow) { + mainWindow.on('ready-to-show', () => { + mainWindow.show() + }) + } + + private setupWebContentsHandlers(mainWindow: BrowserWindow) { + mainWindow.webContents.on('will-navigate', (event, url) => { + event.preventDefault() + shell.openExternal(url) + }) + + mainWindow.webContents.setWindowOpenHandler((details) => { + shell.openExternal(details.url) + return { action: 'deny' } + }) + + this.setupWebRequestHeaders(mainWindow) + } + + private setupWebRequestHeaders(mainWindow: BrowserWindow) { + mainWindow.webContents.session.webRequest.onHeadersReceived({ urls: ['*://*/*'] }, (details, callback) => { + if (details.responseHeaders?.['X-Frame-Options']) { + delete details.responseHeaders['X-Frame-Options'] + } + if (details.responseHeaders?.['x-frame-options']) { + delete details.responseHeaders['x-frame-options'] + } + if (details.responseHeaders?.['Content-Security-Policy']) { + delete details.responseHeaders['Content-Security-Policy'] + } + if (details.responseHeaders?.['content-security-policy']) { + delete details.responseHeaders['content-security-policy'] + } + callback({ cancel: false, responseHeaders: details.responseHeaders }) + }) + } + + private loadMainWindowContent(mainWindow: BrowserWindow) { + if (is.dev && process.env['ELECTRON_RENDERER_URL']) { + mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) + } else { + mainWindow.loadFile(join(__dirname, '../renderer/index.html')) + } + } + + public getMainWindow(): BrowserWindow | null { + return this.mainWindow + } + + private setupWindowLifecycleEvents(mainWindow: BrowserWindow) { + mainWindow.on('close', (event) => { + if (!app.isQuitting) { + event.preventDefault() + mainWindow.hide() + } + }) + + mainWindow.on('minimize', (event) => { + event.preventDefault() + mainWindow.hide() + }) + } + + public showMainWindow() { + if (this.mainWindow) { + if (this.mainWindow.isMinimized()) { + this.mainWindow.restore() + } + this.mainWindow.show() + this.mainWindow.focus() + } else { + this.createMainWindow() + } + } +} + +export const windowService = WindowService.getInstance() diff --git a/src/main/window.ts b/src/main/window.ts deleted file mode 100644 index eb678396..00000000 --- a/src/main/window.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { is } from '@electron-toolkit/utils' -import { BrowserWindow, Menu, MenuItem, shell } from 'electron' -import windowStateKeeper from 'electron-window-state' -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 - const mainWindowState = windowStateKeeper({ - defaultWidth: 1080, - defaultHeight: 670 - }) - - const theme = configManager.getTheme() - - // Create the browser window. - const isMac = process.platform === 'darwin' - - const mainWindow = new BrowserWindow({ - x: mainWindowState.x, - y: mainWindowState.y, - width: mainWindowState.width, - height: mainWindowState.height, - minWidth: 1080, - minHeight: 600, - show: true, - autoHideMenuBar: true, - transparent: isMac, - vibrancy: 'fullscreen-ui', - visualEffectState: 'active', - titleBarStyle: 'hidden', - titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight, - backgroundColor: isMac ? undefined : theme === 'dark' ? '#181818' : '#FFFFFF', - trafficLightPosition: { x: 8, y: 12 }, - ...(process.platform === 'linux' ? { icon } : {}), - webPreferences: { - preload: join(__dirname, '../preload/index.js'), - sandbox: false, - webSecurity: false, - webviewTag: true - // devTools: !app.isPackaged, - } - }) - - 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: common.copy, role: 'copy' })) - menu.append(new MenuItem({ label: common.paste, role: 'paste' })) - menu.append(new MenuItem({ label: common.cut, role: 'cut' })) - menu.popup() - }) - - mainWindow.on('ready-to-show', () => { - mainWindow.show() - }) - - mainWindow.webContents.on('will-navigate', (event, url) => { - event.preventDefault() - shell.openExternal(url) - }) - - mainWindow.webContents.setWindowOpenHandler((details) => { - shell.openExternal(details.url) - return { action: 'deny' } - }) - - mainWindow.webContents.session.webRequest.onHeadersReceived({ urls: ['*://*/*'] }, (details, callback) => { - if (details.responseHeaders?.['X-Frame-Options']) { - delete details.responseHeaders['X-Frame-Options'] - } - if (details.responseHeaders?.['x-frame-options']) { - delete details.responseHeaders['x-frame-options'] - } - if (details.responseHeaders?.['Content-Security-Policy']) { - delete details.responseHeaders['Content-Security-Policy'] - } - if (details.responseHeaders?.['content-security-policy']) { - delete details.responseHeaders['content-security-policy'] - } - callback({ cancel: false, responseHeaders: details.responseHeaders }) - }) - - // HMR for renderer base on electron-vite cli. - // Load the remote URL for development or the local html file for production. - if (is.dev && process.env['ELECTRON_RENDERER_URL']) { - mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) - } else { - mainWindow.loadFile(join(__dirname, '../renderer/index.html')) - } - - return mainWindow -} - -export function createMinappWindow({ - url, - parent, - windowOptions -}: { - url: string - parent?: BrowserWindow - windowOptions?: Electron.BrowserWindowConstructorOptions -}) { - const width = windowOptions?.width || 1000 - const height = windowOptions?.height || 680 - - const minappWindow = new BrowserWindow({ - width, - height, - autoHideMenuBar: true, - title: 'Cherry Studio', - ...windowOptions, - parent, - webPreferences: { - preload: join(__dirname, '../preload/minapp.js'), - sandbox: false, - contextIsolation: false - } - }) - - minappWindow.loadURL(url) - - return minappWindow -}