From 760b88d6f1f3df73f9f8da23e22c1666c1ea6413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E5=AE=8F=E5=87=AF?= Date: Thu, 16 Jul 2026 00:06:40 +0800 Subject: [PATCH] feat: refresh workspace UI and agent visuals --- frontend-web/public/agent-avatar.png | Bin 0 -> 54551 bytes frontend-web/public/cm-assistant-logo.png | Bin 0 -> 34186 bytes frontend-web/public/cm-assistant-logo.svg | 28 ++ .../workspace/agents/agent-brand-icon.tsx | 90 ++++++ .../workspace/agents/agent-card.tsx | 273 +++++++++++++----- .../workspace/agents/agent-detail-panel.tsx | 6 +- .../workspace/agents/agent-gallery.tsx | 224 ++++++++------ .../components/workspace/cm-chat-sidebar.tsx | 204 +++++++++++++ .../components/workspace/compact-welcome.tsx | 33 +-- .../workspace/custom-prompt-menu.tsx | 17 +- .../src/components/workspace/input-box.tsx | 257 +++++++++++++---- .../workspace/messages/message-group.tsx | 47 ++- .../settings/skill-settings-page.tsx | 203 +++++++++---- .../workspace/skill-display-sources.tsx | 63 +++- .../workspace/user-identity-badge.tsx | 38 ++- .../workspace/workspace-container.tsx | 27 +- .../workspace/workspace-sidebar.tsx | 162 +++-------- frontend-web/src/pages/AgentsPage.tsx | 20 +- frontend-web/src/pages/ChatPage.tsx | 54 ++-- frontend-web/src/pages/ChatsPage.tsx | 2 - frontend-web/src/pages/PageRoutes.tsx | 4 +- frontend-web/src/pages/WorkspaceRoutes.tsx | 40 +-- frontend-web/src/styles/globals.css | 6 +- offline-backend-20260512/backend/CLAUDE.md | 2 +- offline-backend-20260512/backend/README.md | 2 +- 25 files changed, 1236 insertions(+), 566 deletions(-) create mode 100644 frontend-web/public/agent-avatar.png create mode 100644 frontend-web/public/cm-assistant-logo.png create mode 100644 frontend-web/public/cm-assistant-logo.svg create mode 100644 frontend-web/src/components/workspace/agents/agent-brand-icon.tsx create mode 100644 frontend-web/src/components/workspace/cm-chat-sidebar.tsx diff --git a/frontend-web/public/agent-avatar.png b/frontend-web/public/agent-avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..511ec5c4ee568e4907eb02c5bba6b591a0e85359 GIT binary patch literal 54551 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 t4bHp@ zKcY4k2wsp1Nj72PfEer00Gh*vkx35KAAg2lV@6SnL&AJNKqfYakxJFIh!vbl z{>ZJ6ZJP!PtE;l zf5Q6(BoQOY;^1umlI2sAZ`;~!=Ul-@b$bJzArw{al{UC6R=P&g%+f;1LGXTz1dN0& zxBzNu8|;4K@LdD1rW7z47G*5GOAXxO^U0lGD-{gqH$v?s(+Y#lFz+}X94 zihW9F4<)&ZmqM;y?7FVCRRjyU)E8y)1`{;o=og7I@hkCJX?MwNU`m@| zsK46!0f9VngR!ZwsvZ6#1F_{CEcxK;uM2O2>k9rtoIoRCsmELOhfrT#s1{m0rUP3{ zf#)ZfThtJ{s5#cZ;YKJJZHxgo#mK{Ch`$|uC&1`U{Y$7sikk+Hf~tdgTo|n_O|#1| z)iE?Xg8a8Hl8RK#7$J+Gsg#Qf*H(F?BB=}=X>n?;neKkF{g(AdOn&YyZ|SGces(pd zhcW|DcVBx+4+zMHmp&cQlD9f#93F)BE>1o#Iz7lsBG_7KHX&NN{r0=#w1ttF1br(=w}oD zm)DFlv}4@9>1Ld-V+dQJPUpH&|5^vI?eeT4i+i7z{WX=a;4ZE)iqV zr|{n4U6X*SSh)3*dab#tekJcqnN0+rS{^5Ln1@7`7PA6M3zr|s-6xN>iUvM{-= z(uCxRNFWYHY_Ow%C@>*7BKl>KAyQXiz-g%*)t7P$z%%9>dg!BFw^eLt+U-aJT$Zr@ zB}-OV%!YL2yAW9l&ka6%eLXNR0R4v{(NI-I7zoX=f7Y}xJmWmdz~H;q2}p=m1F}Y7 zl)NL>A3#`-E;D8JF&H2lB+D`+StZG5Rk!!)b~mLmJ!DYxj#4p_WE5nySgaL2hLxl; ziHyk@Pd%oKA8I_UsoB|GESA=q$L*bNe?jhJfmc1G7=T**cW+(1Wc37nbMo~w@v9q2 zxv&~`WOaKI$7q{jdfbpUybiDA>o{wVgGxM-w?Gyk?Qu!woN*;)59dh+qSSYl+Of+R zU$2A}mVmgcER-3aDev#X9~h>tH8Xx1H4X+X52?%Gz!adekP)&BJin`YLaHJ_$k>gx zYx=djD2=Dc+?tn0EBK_if%ts>&672LW&GQ+8uA3a*}NzGCn$_G$r}hUi;%VfdoJORl6^e}5%c@6F1ClO5#%EB^{I{h^ozjhm z>jdW#MkWn~IPv5akq>uRw+t0#+3)0eMYQ~myD${ui^B7m2^w69;y zWAX0(bbo?=v1|IteBRd_+ukTU`#b~@t{A}z?_31>i zIb;tQL2q{dqap;$*%#u+xO9~;s5XQyd8VB)r2vUR;sI01&k<|68P>#g7F1hVlok^U z)rRGHy4bZ)r;e4z1L%Eb3(-7=0Qmu>z)l!q8q z!<<+jJjn^VB>N}0M<>@Nq(A@`?_diKUQF#!s;Xc(iiK>)4O&7ofQHiFwyzI4nPj7V zW&er(OuqfTw+B?8k*Ypt+h1BcRevLI(VX-8HG&L@RTqO=_l-K^3+P%kDKNCkx@30Z zXJ2t5{t(Q95>q<>%%GHxAmIXrmPMn|EZ+i>3)KmuTCd%oJy!T0(H*BmIGX+pk#R@} z=z$Nqb~#!BE_uFwSu@X3k|`!S$HgNMQsZR6L>gh|{^+J7rB_FhNO)m+X5MA66u)H- zbvaKJ%_Mqo{P3H{_l*RC2H|qtHSvd(MAn|!oHSeK7r%Vs)by`?CTa-O_jRz()-CgI zSUpvLW7hkH_yNL|V-=#kiXYKXmm7&eA11M6;i-`03bjYc*k=y`3mV3rV*9TsEhE!Eg@ zHEKm0V@?=lsHLs!yuq0)-HlEGbzF2Rir)jI(XUwGB-ZGq|Br6 z_#yHLdBXzvOAufNOur`o@icGjJ{$kCCW8#Q*jO~!J7B)FgUZnD86~46nl=?J&tUiE zA|zTjU>y_Y+FVfg3ioBWQwK$MC7w>JepWSmOXun7bT!?Wo49ZG8n~|zKzgB_Y4`Ih z%k|ZVhizYJUac`?|B4VJ;wj!{}YKiS@)BV=h>Ixa1{4g>m<5SO5mj0Hpzw8~E&d zjWrQt@X;``)NMSX1`U*aFqa^Y4zmXH28>wu8#tQx{bY&O_g34Vk{p5iL@=l4-`fvR z>*nsc`8TYduK&^nb@?fu)_QX2QiVykc`P7UNg7sJwhRrEHd7{FE7A{frOupL+j8Me zJuu`~`{A=At4@p%NWXZEOXVn=V8u!ufY`2A=*9E$NK{{fs;rTMIF2HX!CqpL+w+&* zYB>gX1GCV?A=nfYd+5SA+^_WL&&K{M>vbt^<1ZeT>=Dti!3CgaM_68v5Kg5!CtdoJ z$86qqslbc3h)4ikRD}woOAZaWK;-)TwAK-I$#q=oVr!>etZp=4zJGr4T~a@wdB8IO z32^B8PxV*UOZAT*93|Uuw($V%=-@(Znluo(s=|L?I0JSq(-W<+D*2V()19yv9X1n; zYWBT^!-eW#%P$)FV!M8o2PnB}kiX&LbpDi`l)hl>jNKY!3{ar{4V*yU9~fa#Qry%7 zKpHr9<-7Sg&=1s$af~qY*Y|f$!A0;L?@XQ_O}c^%Jaw3a`B3`?cuMSf`dtwWMVEov z1pMLAZor0nF;hnP1fxHAQDYEtkAo|#$V`3T>>PG&wM0MEf3&~uW%RQ9#4AwUCw2YQ z>c72cD=zDmUS9aU!pBnT2E$9dV1SVd3vDjDl^4L_B2TK)LcCWq;p7?adT@vtW)HcM z-5vVH`;z6f6GRx^zPiC+eYsDZTT3|&-ccuL9N0jczK}=9$7Al}b5(NtuP}nSO^{Z& zcJu3a%tg=&P<-3e`c>|kaAqHpcp&~C8JoQ@aH0?x76hyRo|?wNE_XA+0~QH;1~`!j znVz<5c_;Wj)`w!Pjy}VmfA;iUb+=LtkuK;SCCcEm#DMCt&vhQ!3n`U;`)v>1-y;DNQs9 z3@IQ3ETOLbh6Le3j1$H_;41?sLqt@KtTE~L(zH%)!odnmI2H}mD(0b3OnHo;!m)!l zgFd<5H1pdQFIc{M`YZP~6R7Xa(q4VvPY;&6|8UT9HfFZRXPy6;1v*AM#n)2bDnDm^ zj#A|ww)ki0H;z0h$4H8cpZfVC9@adjEOpdaT#NSbthyQx4w7O_1#MK z##FVAjEQP0ASqS9)x&DL>JKj1CBS2l&r50-FE zN_FCbn-5``rkes9@E@(|aLN|9>7pKe=hp%NG$sL@Wem_OoJUnC1VH(P79)=f-*jy~ zRffT4X2aH(SzXa=TBpx>W6u2QO#JMW**(m`QF#Ln7~_KZH~~Y}oxtLp##;qdY`Dr? z%4@|VLkt>~DQZ!ka!?WYO70+UpYA$CKp+^a5D$cqM^s|W+GXM$gz)^yYPIlcCs+q8R-iF`Wc-)r_oQ&aWy z)z3Of*KAJG#wO;SF?GQMrR>c?t6K?Nb4BtnHQ4 zZFO$`jOB+d|M|V83QX=Tpa1S{{SU8g)SG8H+?OkOj2=3>5sZaF#Pcprae76{=JUA} zRIC!COV;!FAQ6?pwuvVawY#r0)_>4!7!W7&1Xst( z%3<*ik*-`3=eC`e{`s%mN+15zAuSGBJ>pPEj*6`4=x9M(+XwWTKRHi7|M#y;1vI+O z3DJgT2&Uw`=f*`l0;oAgzV7$;ct5=yT&D4rZ~^#Tg^@v*hqnpspj0pH4b_NbF=SO$ zNkuxCXCyFsC}P661&gat*D;QTqMEWjW8BqZ-qObAbhf)TeK`@mknW|Uz85O|wEczt ziS-)2Xlt+S+~+^Imik=yXBb6c7nK3w>12Za;TqeFla#oUNf+5n=*#%!TQOfM|i`0H#LVuQq8u1p(r_`c@h0EBh+%Du{uZKu#Ph za{?g43LdM?mQBOTvJ18U7=OZH^5=FI?dp2{g8fgw71vN`RxU5?{~Cw z*wXHPM|%hJga&xOgNBo0EXp`PJ4J33>%DmWfh!bpIlVm$g48wwgezywUAHFMg}G%l z#~H4}{uo9+IIz?g=1>p{3YZs>2s;0~zEUlLQG-B?9`)G~RYK3{*p7@Rc3X#pca%R` zuU<;`(s7S50MSu8cKfZLsQ=RTPRpf_lPHOE4$B23rHx&HNFxvz7`Fb(iuU)#F7jA$}z0~FG~ZPYxt?ApaR|13a`+kgqkHkr;uWc<@4n$WT=%rDbF}u z4?Mw`imy?I5u2QBUIFX-@aLG$^7I)+Xb zYgdb_u!Q$<%>5h;76v>iGqgk>C=V9m>7!bJL=};kiDA$1Q(6z{08vdsWanQ!ny|OW zx*aOKmTq8biz<7VSk)XbAT)f2VF3YDLgBFk1&5@^?JubB)+Ya~zoB1#U9KZ|N3Gnq z41lHo3tw2Z^X>jCme=a%?CiB2rHwqtE3%l*tCNLcRjPMc+xaedm~iB5$!1&Dg0Ihe zu7rMRxU4}$O91j5Ra?|LZzu)8{vbWGmh*pc0Yo<*UY7_^^?rymD2-57=|^#d8LFba zBPQnP_#wr5*t3Qb^UrhM((JIM#ZgD?yrpi@QOCsIwp4Xp`peaj7Ylbmn(Cw3kNU1U zK4F4ARNKc3xd$K=@N(GgRyE1T*N~w4-fb*=!eSICqB8HMhW_fn@O5=S90++_of`uN zoHLN3OZ7cu9_)<*^YnKyTZKJpoQ{KipswnJ{YAI2Q9t(JY4kTo|g?PP9#t^uz}Py*S;p46hv%` zVJQ8}73S}){fyi^8$CfaF%?AGw}k}C67U-hSvJ48l0jfN_?t7I)U~#wdT~V5wQS-i z_SW_t|4S*U)U|VJ+k^C(x8NRic?r|X3KY=qgqy;V=Wu@LvCXC!dEt zZ#K%Pk;(#2WHvo;$JdKCJumKi)#SU9?np2h{WnYU+-QQ?jKwdrbOdYND>2C!aURz^AHX z0kX!q(w>>TGF47@I+CFojA3zvm_DWp14DSjS&UPPb6GM+`~rq9Mge;VUAMm8TzBRP*Zd8s70}#Y z5J2C*s&9^{`{$Ab6Y#1A=cEhM34PfrM|2%?#68)-tt1ezZ7WOf&8H!Mz6{+=6oN74 zVoF-4n&Ot}}^9k)O8rp9gI%pd@>Zi1Q#QJSTr`9K#c}Sr^HV_)~G?gGSPE%8K zoNb|dKYT9nLrMH@l#8|fyx^)aJn#Z#sp$*^Sw&I}Q6)oHZ$IMU2x?87{i57Z8c$Z4 zd=1K*!#g?i7~z73THV!a_6T4^{DD&s_Ss%LtN&5|+J22m;GRGM)!mo=)E(VUpI4or zH*FuZ9SateOm?l3kVaySKupv5MxUh@abb;$_U8;^i-FclwBjv>s@ZBPLPISmMSc_g z8fB}Wm+$DZG|(XT74aRe2}DsCUf&pi`~uhd!be7d8I;;nH-X`9K76nbyRIAh>EFJY ze(MdNq}g;vi>{-^Vou#+A@I1OZ+!lv=sUjhVRVr6s@$&nrX7CkNI4So7^+ym(NsMM zZBBbG*sr=C;`>=dogyop8Xo{3!xBM&%HVTa%mxNa5he}|1A^in62q#~{QEK^CQbu2 zROGc3%Og}a8dI}ijo4C^mfNmtPi;(^{d0@2TRT1d_5Ox_QeA)dLWJ)D22izITkR{? zHtKIWpQHW+nndE%@g3)e$BhI$3mVF0y?p0i)uF4R9G5}U(xi=xrmR;LI)<}UWSH5{ z$RJ7vfE!|f!LMLjF+0$uy9y+;Z|dU}Il-Wqam@cCI;Sth&GiX=?8c8!H$UXXMB6(% zw6(oU^TR`W=-k#k>9#&I-Zy{saW;)%$kwx>w3tl?5NRl^ic((2%h{XG zgO43_RNh$(k@kE!4_S)JM0@QgFxhaEHhhdbIzObM$)uv?44kYVX=^gskFY#EI<>TS zjW7)jU(WN`uB}b#{q6RTPn>AJ^qwdkHo2QI|6PA`|J1`7yyqE!uYh*|Nh~il zIDk;gfk4AOOS4S;{)0&Zom`k`?2VBg(6mY%@25gY=jOU72JFFz5@9SZc(CY8e**Wn zj%itPgt0>Wn5LlrQE8}>P+tkP1Y}waA?uzF4mw&}U8dP&i6+g2ws%%&X*Qvw!vk7f zU81c$@nwrmMfGTd-b#=#kB)GPjXLa|h1Hn>-Vs>tukT-&N^RiSz<9Y)QB)H2IvOQs zgyCvL{^w-q=nffrWpm+uGPw_hWwU&6p`&x!wjFhKoop@L$VN2aoHejDo~!;(rWkdVId=cU=J@q^q4QvciVxYbLzD8Vxg4)?JU0F<8u)N`7@;0xAlY z6C|8O$k`LX$5GqSe33aQ8(`CROfp`JMN3?=CD+)`Oe1R=nJ_bdc6CPkZO;|y?2J}i zZ(9QGzn)cOl#UV#MrDfU$gBh^j`a76B zb5ALe=9xDrt|h1oce%GF*DI`4<}%hdh|l-2LMaF`#@umQPFoCbJ$p@3*{L+nO3P=a+|xOq~<6Xf(*#PUOwAWCzU zo940%YqE+wMi5OX8Vj)j-cMOt;$hG>FibcExRvi-L@&lwp(Wt}1s6x(_T9=-J)57H z{!KY`pEH28t#Mm--!^Y0VA723chcXF1D#qNUT1_R8+fwMKS`y)f+JxCxCO31BvB(I zn|;5_qw}C6K^nU&X;AC}?g?{m3rXOxp$diPDp7R-rdXZyzWHCaeW+wizsq^OCMWJ> zm!CeHK~k#r!_ks$*6)oCN+lph1cyg+nlIYC7lPtUsS=dbg4CuW4|tx0#`5Fxvnk%A zE<{9Y=%NLxGQvl6ttunf4`L#i>*rx%$yg79&9r^{+DDh6xqd+vurI}Q>Naga!u;OJ zHJvUhyiLVX*1kYw70<;bmRD!P3oh%nBTG;9{>qV}J!^C9Zv_vF@`VL9(r zTZ4_ezC(yI&MY#iNCNHM2&eN3LRzJ#N9o!kF(Ygb*7~Bzh|N~e`M3ZRN==oP zk^v{Dyd%59!XMT&7K@etsKGTHZs4vD4qA2zK6-Dl_)BZm^j9y{7vWnXhiepIZOw~ zov*8+i)dqZRte}1rljZu#`Mm9VW^*RlM&9`%T^l1- zw3IX9rz$_yZH zlWQGtVXC;tD`5W^#Vt?s{NA}Tc7QgZG@g&M=URY=pl>EkRkz*2`a$!A>Wb1fb-H%~r?2aOXJyzI^igXZwn;S?oNnI%B+O7ePj#_Yd|YO#QFgdE2h9 zHOqUm_Am3<*S+kn+w|^|3h=z=W#`-csQb>zN-nR00S{xM9;2MfW9-GT1%B;RmKg+I zwl@xN-Y63?H+Ven!C_0tmsRzefb#rv3p!-_$XDfw&I#Hl_W(sIuFianZWM# zm8DLDN__UXuzKgu%=BVWkD-B)1q^mGKjm|z%agY=T+Hd)`gO>s%!q^&k~lbqvlUOQ z)pYgANkUCh2^_d}_L`V$Qt3#GrG{aSxes(%9Bwip-5L;ALDC!kCSO%H{!m=zpYgT><0xC~^ zN+Xv0vIKXlMl(_o>nd1e`efgfQ?a@EC$DBWVNgQa9?#%7`q6zbwNBe9g{UKv356^SB8b zF;9TQb5@Voc5+e}0V67txMDE1nE^E?j}uB&VBj*&rq=Z+JYWh!Hl`w!qB@Am6hIHA z5>+{HEli&FP=2pia+E9^rqDxx;hFGTihu2?U0Z4D`EvEYrXOA~4jhj=?uyQk;D1|n z-;j!^)UFo3u5-}gY}-qmv*dip`NwhATg*)F@0tE0V-l`vPlFhZ5~<(^H4y4E?OGwG z)g?9-Hncipbpfx_maE|s1hq2OGw6-q;TkhFAfO>{Kcfe~(F}wTC91((s=}%_DCqnn zb2v&H#;lc70Bfr#sR7l5p0V@s!>1=T;4Xv)-`NY3Ua@x-{e|`QW|^&zjKN|c&w!6R zmU56Dk}w8_Ke2N2H3>e!TwzOQC=J9Jy^;_N)FJDscJL@g+-l9ayQ0D1Kk91p+H;OaxG(rPjg( z_(kDb;Z&ixsXQ|0OQ;TyZ=~6WFkbdiB)!;K&TQ6na+OU`kQfn%GK{&Q6dg8Te3i(M z5v1+cl-sfyqb#q-+?GBW1JTPjc31+`ap-+;dJrhmuCF##b$a$CGJ*Fv1IRsWyBA8m zIN87m2A<0z+tn_ja8Sz^v`9g`M)s;pZWK$qgmqTLdMEmDp$C%0pQxL0s=9y??AoS9 z2uj0RLoUWOWPN|yu+(poPMV|a@-;S?zzlN8Y!{M%O8J@>CH8v&oMLhz=r0Zo{WnDs z#(=CwBI@+z?5Jx{zRS&4%g`8-cOkgqycxN`XEry9pEXM{Z}8%fCJGVH-kIy=q&k8$@x_hR(0|5#m)U5lY&z5#f+L7M6!$ay&{J>o5Oh8f0E()# zgRvHZyCO?}zXn<5F-XxeXz;Klb37~9cCJn8`kgEnc83f= zw+!MN&pt~V<~|9FQ#fc~0&Y$BYf z*$G>SXywHndgLNOCA`?KIFSo!sz2Wof=ZQTEaX$Yrod#B>BxMUy#R||+87trk?~{d zzTN={Mn+ zu-w>EgGDrP#1?!ia4A06f^lHHw44a3l%^kI{&~5EOAjrGB!#(5{3}fSo08Ze6?gN3 z@R@YK@=!V&v`*Kk%Tm=2FJ|Jx1{M;+SDO?xtLw^9m1<1d2ZMtnGc4i>5 ztdJFN^Az9!p>s!EL=OU-e36)hgub8(mWj!GVpD7kRG6hPZeU1qB$k{CwDi<8L}yM+ zQV>ycAL=~gkcDN`>%**qkrMdm*a21y!vP3RbERy0WPo%7CLEJIv>wUo z0>@S!EU4=0>I=jRaHpyU*LTYOceX##*Y)S_A02Z~c+7|+V{AMT2B!by1;@oIu}DSP zh&{?fzW^}8F1vOK-lrVizeVvcR|-eru5iJ}Gn*1I%{sqaGBDP`0@Jpl-x(gHP*){@ zGDJZXZZO1v9NG8+pHoOT*9GD;U23`%!kG7=D=>dc$t;60&JVLuHM@id<|tb8Vpq^= zF!n{(VG}3UV1{5OW@L87y@CWd{FdVdayG6O`FHl7CnyLfp-Q4+z|rE(SK9=+}RSHnIC2S ze{hwrQ|o8>JJwaRlc+1Dq8BN>Q6U2OV+Udy)-%9C8!Ji52vW)uV>C_IVekksr9zrS zSMXwDu8y!6iLanHN6i>t4{C}50EF8xP9j3`PyP_QOsJu(v1h9fU2B2K`U~d8hcFIF z#Lwqpz*$qmbn=S?Av-Wx-n%BWDEccv&V-Fsd|j_{Y{ZQOZo?cGfv0pUW0cTfdC!1C%DKSDgWU`J;0zO{FxMCC z)>mk`ADu~Siwar0Eyza|r)enS zhG8{BAyOVmwlTfb-SfOS{@u$405_9M%(VZ~D z$B{jZzWPW@0}+14>nt-3C)_i?($ecI+E}U5B4UYx$kZoXBf`oBA%;sC2wND)r0gDD z?T&cj3m2Y`LHviF92#5)lVI>+(vZmlZCBib;B9 zD(e=!J7PpGY(cu#_&&CAI8!QSrzG#5ssrBov97;>UYcd6*6^Ua$^cCNe@nmG*Yu== zv?cP0Vu6xoj97I7tvp!q67nHi|Amc7?LV*?TA&VqHW)B3fN7li7)hNM6MVOca-PYl zyqhZ+8XS#YX7m)I>MK@Uvh+!dyDV-=m{tnN!$n|@ew^pjSFSUks^lQom$4aFihRRP zzrNU2Ck!~woiZ4Yge*+a@I?7m#AN6-X?~QELe?b5!n2>Ab0#>7FKs(kGG$;;Mp^)| ztq<&VlCtHtKIAo=PW9PUN+h~ zvLk@NOMDez5)fqfakE^Uf)}Yle}vbNEAHd(1xjqv#Mg)$Y@};he;37zOK~5}J32U$ z(pZV`p&r~QM}@^8L*ZGDu0oV9eA56*suiwj>p$RA`2$`+K+f@=0KS|7rlFRFlNdpr z%b+byYg%5aX*$UjiTY&xMZ|^^rVTERSVk7Iz6>aV1%tBkSDSEIH zvefrPeI|~lw<>4)i(mn}!PU2nK(b-EnK)`L-Z0PwhRvH>)ffVJ-ekS;NG{s0-CUcj z@1I|MArbu=y)Mylcb6H!Ti==)Ks%+UF3qSpIPAIfx8)kA-~%fdtwk|#wZQ>m9}{lO zcg+SsuKMdVb_~xdjXqu&5)hY9Mxc~pT>*XRqj++7u`JJW+NmzOC>=^70wmzGVZ`NY z(yIXN@dllT`5*5!pWa!Dfg&Lvt4m>iIq)W2573jsb%`IcJ;K2CzzmU;bTSrPo%rymr|rEq6(Hq;DQhb=El)Y5SIQD919b^7ItEHAW^B}_+b#L?iPZ`1 zAI@`tnL-CKR#KrF*#aq^>4jzD56B?mdU9rz4ZeBKxDQh^j0A-W4p`g9X@}4CpJ973 zqJC2MRIS#Z%M9SH8*e>k5#0d;c-FJ>8+3j5EUu?kzIt9To>z{G!IC7ODmpOL;%W;~cgQF~GG+z=whCm|(s_od9AB;mGK7EexgG?9YK$`>nQ&7F z@G^$)xrIp(uKQ$$EaoK%5s{J|<3A+i;Y9`V0!ZvU z3QTiAyos?(?EYNzaVX=9c1D>&Xl41%HxCi@fQ0t6QzySTue=7`Xi%<;g)^z;V}g30@uW?(k`TC$0*uC|C{6xdlzCGnNawRuaeo-Dr2! zZonAlkeMv?Po}rfq^hbpT?zw`go0VOMW4kDvN0h`vaBBigd*UN3^jzp%d#9lc3fc^9&fTxAm2m4j3uXom^M6 zzEshwX*x=Vow%>z)Nj;sX~?+l_*MX}qhf^{-omGcx_0Qs^)_=>~X7XmaQ zx2O&f&F9olmg>{=B)W=-J{dbZ+V$~c|Cavo{Rw*KyzQ2|jKd13a{SiD;hjrQfRka9e-^Y__O(KOgd>qWU{#6_((|OTbwBB&YjrAg`MYmBOTT=>ZSi=PHJ~`c|vE_GUH)*XGSwYG11sn9FZac34kJCywOWyd5E;5urb#j zo=DMbfqkm>YqK+4!}dAfvDEamlC1u-6cqCZZ#_$Y@b>fcjL+Jn@BYh=ps)K2rxH=@ z9xx-QlFD?;*(18*6fy|G!5D4N10T4qx#$nS5;ln&R$FBwimWAS?X~v!MkqUrT^m>K2E>;mUA?zdfHr@ z*!#0CY%YW-IL04u(TYElVmCUb`88Aj2-NN~fs8`ipo3FIQ zfia;qKYh*UMp5J-3>Y7U9A{*0ALHIJw<}fs6Y1`R0(Vn-7{ZXk)bE23`tOq6MMBX} zNVi1klNVK)f#$}j60(Kg-EUX^YI56jy?AS40J?A@C5-jKAs-?WeueGb1)bknP~CW1 z>Do#~KmO~Vq_6rX|C4_APtVawi7-rCWMy%0Nh_H_l~N@eaYF=_#YVt5kK~OHR=>V* zKtTYLaR`ItIqoQYuZqwN^+~Xcw1+|~ZCol@a%_7!xk~igZ@i7Z=AXQa{-4+0N~hLU zHf2I#M0If3LVZQ${L%f2>IkrAn-h-ED9LanEI*&9`_I0c*?`?sa)Sf2M=;`HN21;;?hep{l-uSZ zDXQq$O991$@ggJWKi;*SO&K?tg z-X)!%&(?2dEdTJQK1~1QCqGQ>qD@h041JRhnl^UR&`{rb*W&4+h?lFsSYzKQ7UhG! zLBZz?l?_&O63|ec7A*UoB{$4Z$mP3LNGXB=237`MY2RhqO!8KFJa}AAt~GRUctkJx z5AUOY{<9yU&6OtQ`D<@Ky){qX0$T?**Y3I-5QI^vqn?$epv+Mw_Lz)>>vU8_(g8uV zSRzLucCRe}s};;g&V0s*eT+il<3nDD{LGbt`@VX3e?z}am%;%0pS^LJdb&0>q+KgH z4M^O`Nr5m`x|vSu(49Z(zV!ZL8PtVhkopfHzY>C6;t4&)fa%HyziXCE`SWa2CzlL3 zYol~TL5Mfn*NP4f`%L_GhPbS2vikD3U-4o3x&M9(o!XoxM#Dic+El(fZ|{hm)%pjy zw_)&u(Jt3H1O};SLi;VKA@mj`|KP8^x3Ov?7&UrnuBJ;)VEieb(}~~zVk}5Rd%lSC z0zP6-z@(zn8x8%~uYQ95?vH;sG1BDw7FRJN0W7iQ&x90ZVG1EI&0_o60N14X==)JV zVUQkQ#h5^#0SJ*qsVwhgs)Ly&lwlW{7xpfWKry}oE(vR|7*f2xN{Mw<^)Px~F6evL z5y00xX4=*LBNwdBj^>)EKNN|5tRZIz?iJ7)c^dG6h|{HQz8i)Ldtvw&q_sQ%H{A?6 zaoGkNrUYra)}c@hp|ZnG#Qpr0CD(;r)>l0Qn4|k%{KN03Uw{4Wbmr8|!h5wpsNxtZ zDfWm{DRJyX=_2Ll$|{IVb3_C1r$$B>Bxa;ror54T_U*rTCWzV;^i-XHt0Sj_V^{Flc&xEqB4LtqxH9S5cP9^ROh-M*b9w<=wSV6sAi zFmwRn&4v^UZ-6eN5IJY!ng&&*2RvjXB6C;?3xD%xXmsOZL4DIyD~C58K7uZV0qpM5 zI#>QSSA_*dAoK_KOIT5^7KCLqpuxO*P7oYg*UpS9I4bdOc$AHV!B%@#!RqbOe2(=& zJHUeMvEX`xENXy#9KdmYn|;_h)|(j*hZ6sTpZoy*)*pR}&TQs*|9qPp_)rhgg`ZmUyA9-39;8LY$JN+NStr5JX19ao6!dS?$BPhrqXoOr zL>Cflj+&y?&n_uzW5OCkS_vw2=%N~fBb>1D z?`VxKg#k>ipp;0Mnfb^HDTG8Cer-+1B)l-jvgae!Z38!nmAnelDn7*^UHC5Ww}5(M zij%>!w2yoC4V!ex9mUImb0Fi$(4Qmx82U4hWIzA!y!AZ&pTBf7oj9>fU6&Jzj9Z(a z0Zt^5l>srSWYkKs<@RPKtvx!jgzN!$IS2%{} z++S#w;Jd~E4sW98uzoB_f8z&mSJM&;t8{$5c;0AL2`d8rkr@pZ0cZ@a2F{;r1Qcxo zp>Xm|zFmp5^h$tX(b~;d>~h|p1e@=TMi_UNJw-8C2gnbg&gX@5(b|B_P~bMd)j z6@gQFZ{M2JKm3m$^-4|^rKGCt6+nCjtBXITcoXa{q?T8XJWzs;m(S3YZxvTVL7@o5 z87?q1d+|uThVG1~6w1U1Y5*QFgl+bme7-(YAJC;Rfa*lI?(k{k0vAy^f-Q}8>5fpi z0)que#Z&+Y6hg>mL+jQ~Xw+Hhpi~A&2f`v@Gpyf$R zKlWcfkrH}RWg*!Jgtw6gdExIj(q|>8ytzx4)ComOIJVkN>zJvJ2+@yu#K`)NQZzSa zrjmB)z{>d_h#L z{3f`ztRk#NL1QX-lS}wA02HIV_^P&T>F{VlM@REQx6Q;IT$9E7sqW9ItQw}ex?Iyw z|MqQk`}vNR*rJG16hfSx;AL|W>5)o2U4S|;d9f+1%=h^HzCnrBIq=eEZt$}4K`sQ6 zu82*65@jPVu6?u1odIKhM~)b0u?MTh7@LVlpYFiPTy2uclYhBh#Me@reaE#Kcr7dv%Fu1y0|-L2qpAY(FI_kKT4EUW`IR>+ zjEL5^J32<-xAZdqjC)eZfGVR(U0ti^m&5?-zJ8_%NkGEt(=9gBM5KJ|JB-BB+l_JI zMd=25k{U)(Nw!tw0}=Ez(e;?qGvOzWKOhoaC^<<)C*2k!OaeoH?H_MZ{jpmP>A$@G zHdBK4X{-kvN!N=Pd$-Ym^bd^rSw zO~HXVDjE|jnlqs=qygIETnkE%8Nrt_;N@=|ev28{qGXa0O2nFIX~{V_@k38Hh5##J}k1AeDs5)?=j$+~PNfGJXB?ucqfc z^CUgt5$kD-vX9-mPk;KJ3-sDIU!eDY;*d_Pc1c=xQa{0${?Kld7M^Cz>cR(AF8>w{ zgYF#Dn`Axo6@7ZArzbvQoxbu5PSY1W`BbX-b8xhv4}Wrp{@`sF=y(3~cDk^AL?=jcwPN#X;#G$YVh(j_*6<+XEnbr)ENOR#K#BU|8!Sle|+-r;CKhb>lx zrEx|32XmA929^r7*lr#o_1qFO6Vd;rg(Uso_whaYvk#t6Nk1vU2PA!%6o5tAf0Y0Z z6-5Q9xpv*0zUnVrP5Wt74~RZr{t_TkiPEz1*2}zJ{%onRi{gZM47>G6eg=1sk~a+fRjBtu1XA~ zWm-$ojoBFr(&y#$?5a{G;NxTjY1hlhg0a}>L)Ld1N$|L2X`R;;ng!H2ZA(!Ylg`}$ zB>)4%{v!}zX1>5uT6akWsAv)VtHE^z3tTKFj^-n54y0hz>sef?r@20)4}bt4)&G(D zv0{p1TA0L?q;u4z$yAB2|Cms6aYk79;*(JrSMmma053At4_9V2z5N3_bYY9D+j{hy zLhE3w_aO{SCa;?;O}Q_;crWa>^nHKrarB%2>Z$a!>y}fgaArK|-=a+kMGL7h@PB;k zb@U7W^l4PpQ>k*JD_=SZ>bz5w&l^7%U;eE} z(&>$y;h6d*{hKdZnjam}Ge2vCe)ET(Mc?yRA5G`?xZo*A->5+&bWz4xLvv$vTPi%O z3%=`UdD_zlKe0B2laH!%U73d$4L}&#oB|c_X>6T#@O@U#>`LY8ksu?FZtLjNzS4eAc$`fkx zL^@|-{g4+-_8D`r6rw-7aW8$yi#Gehk*PSYiSL`A3-xrWTn~?vT>k2Fucm+d9gj&y z9400;0e)zi{m!jh8RQTS$AxSDrM~gTAC>fX(j>zixAE`4=Q{ejFFHf#cYA7N zay;I^a7REzrq?3lN?pt-WPfj;-u%9;wAo0Oq?tjRApzElY21Y|vy;teRUqg;?i(|P z{llEzjjP?&IuJYsb7j@j9xn{uVKYPr0!JB(5{zpI$VDSeBjk^SruLLO*!=JKP+)3K zrMT;h?JG3)U?Xtsw8Onpx|+vKjB@4HLlLT%#*^?=A^dD8;fy?m?77@lp1h8WZE7%u z%w~Ruw(N)obym1M5o5a`djCyFR81#VBqXaH#+nNG<5J9kQC{WgJJ-rPb9#k-=sO>0 z(O_&{&&;lvfi$rh_OpTW+rRo6`d_~As=WDy5ERA&*dRKtwssLfspRxgQ7;M;cS9F; z4(P9b$!Yp)&%IKt@VS5HH?^g}?^Gv=uj~4UzWtGD!xc8NdATg@8YJ()xYz|&y+{Xu z$9YbF{?V=MaY(xjmGWM0fP||+bAkZ6v>~m(hE-vfIl(e_kw$%?7K(<4ccTEbi)4Nv zw8PI9b8Id8h5gt$6JR4KV4yGG**E~E6hw?h$T5MZ^s|?GX7G*x(zJ!5Z9%4-S!qLp zhrhUeh24hi`4WMZcG{8B+j$Ln`7uwTDGr2g=)*63`@HBl6!_+|ixf_rTf~hx~Q>l zsLBDeNEY(GrrB(XzUylrE{5UC{%M4o`q4GT*YB-blf`BBvjHyE<90P3>vU!P7w_0U6cvuz=(Rv11U zRXPjKfxHe6s48?*xyg}dTpnZV<=F3GF8SHw0*1@SLzUujJU|$6IpjbHsmOv5zqm@f zVCz?}OMO(u(`xSj>gt zC<8dZyGW2cZ)pZ4$endZ$qL0A^4p7!nf4GM`toO<&VFdw$< z{_HbUO{RH+Da$N%-?+Xr`q-U>#I7Qt)04@B&h0Gd;3(T*vO5$oqSnv1K}TrmQ9eB| zfV4)SFI<|PQDj~UZkND>owTfN{2YO*3Kb*`B$LG)6;%awS}}atKL)0#AAO>1*?QLo zxZ*CP12=URK*pYZRSZjRY0t4kVT|l|h1((GtI&a*OH0%h__)LK@KHr%Vy+_Ua)fW+iEG6d=0bq`yml~r~LIx%QTw9F{L3j~Dk#N>u$R*w$myrn$bJ{(mI*1HkY z5JvY9qO9rwCNm}50C;}V>mkcAx#1D2mrP5;du&U5WhSD6M4t< ze>?-|`t}@4moDKtH7bqQ%(w1ZrWiiCU=|SA;I1iOfkXABP~6g`9=|O@3Z%C)1C{hy z@@LIFPqG>TrO7D3d{zoc&QWe@qYDl{J?iM_@QA9O1FhV9pyjdHoo7-X=FPI>6JyXa zS`%7YUP)VH$>2K*DH4Kk)IIwyKlkLugxUqiZ>jairLjZ$G6K@Da-38QW|F-VxR|N; zLi-krggftcbwzVTxPQF!GJ7`6(4~vZ2amx<#A{5$ zR2Ag*q6Xl*${Xs!3D1ua&n6v{1Z?M_f2nW=s3LB~h8B^j@OUy4Xlgn3E2HE{jX3D(glEK>Bc$W%G6$>>9!=XEos?LBAesc-zS#K^#) zT1YWm;M;o~cYm0&1uURT=E=Pf$0T7WBH{5;eKAFL7AP~lYyRCcC6%G-vXrswGLg51 z{vXwW{1%Z!ef;6|MhMyGC(5#rNhKsxGT_k4i03TQ)$ow{C{qXTbUJWP6W1doLacD3 zXg7QkqJd=~Rp71|*f1R4JV-D?0TvbQLO=0TCU!>vx+L}cXpc~3#JsOVylNW%7PoHVMjr%Bb(*47@q|KodcEYX`?Fp|t%bfIS>;zw>fqA7bN%yR&% zEDmmt#8hStU~TBsVI`-g*|eq)erhjms=@BQ@pcOap-PPS)<5{ky|nEayHKU$zTK0w zVP4>oxOYQ}Vk4=0E?DX6mB}4>ugvVCYG->n2psQpjR51~a_yxF#cfnRZ<(=1REC4B z{Ru)71fjC`C{%}%dl*CypGyXaLXcgugf;K(3ImW@(Yd4JVVX|o5DjP&5JvA1*`S?DoqW#MH|vERZLUvfeRY{S4yMFJ z4UKb8?P~u7>RVg^5@_yN*S7RKZ~nBQ2#k)z8TNn=MP2#D-}vtH^!`uo(#nh(K#8Md zvCDn_+Bk18srJDWOL$kG4}E-x{_I1$0+YdXZt8&c)h7Z2zJB*@7vz1o<9X>N1F8Z? zZB9@@>0%E3?pY<7BrlA{?nAtgt|R~yFcLr{T{dM{`x*XAOaQLon0fB&-VA=jb?hA< zD8hgQOTiV_wKJ}WlH*O7j-A`+x~^wi)uNkUbZ@|mg$XFx0bg^@779l|H5BboX({!! zUgSQR-xVYVvNS8Xq{QHo+PR9v4`}C9`pe={DU<>~%o}jAVabh`;EADb-nWiubG1o+ z`vvg^mTC#dEyO(ZQ-{GJsf?wJA@^ltZJGY-8@B1jn~#!&wZ#YHm+T54xv)6JkNx_m zXfZ$XU6o{N)z2_MNEm9ZQ-ww;5qWCx%Ndmo{p4@nl7_hL+U$CAr6`PlUK*KEu$BIO zpV+7W_9y3QV{IZ~)BbEVk_p`dkt$mj3%8!@+Z5+<<>_TwTgiy09xx0~-OX#q!-f(hZs%HDXD7Ro_opg9f*( z0<~tdhR*G@^aDTjiS%>!pEHpa%+U0co({zY_Vd4gJH6)3+jMemmJC{`E{(8n7+{z` z=_jJ0oY$Y{onzP8i1#c1<97O)*L<30(@C#*e*rId7yYT0sy@HvSNUU(8RN@(1$ot!`5@6EZxkpmdag<$?Gnp2=XWy7=~g$NtiEZ%dme*khg3 zqr@Km8aYy)N38(sx?R*;fU)3>^3PGJT8ne!?l!+g4lN)mv zC~NZ$jFvqC`uTGnwL*P6lm-&km`}?>E)6-QbYY2la;){%+m_a+ z2lV&;{d?#aewUd5Z+Mc^t#wRud_8~f1j!J6`;UH@e)El=rjslAw>)haqAPzah^mxm zs2eDgs_Ds(-AKP{NiUr{9a|Gh$Iwm>`oAGX*tj!?n&T4H*buPy7*57M#~O*pX$Q{A zp{bxE7IP(k9qbm60NS(Lejy8|nAnww?C+^KU7?1A^$(8<0u~N=HQ=*gl8;U8m3=Y`H)>4Mllxy^+r* zrd6oG+C3|ZIzZIe*8*9>Gjg;h@pelW=#uIITQM?e@ZC$;hM>?f?z73=JN zBFDodH|Eu3fIg-$Z2Rv-c6=wz)KQI+4L28>VgFF7zp_7*(F%hYSYob zd)0^Nm;dl4`l>IwlD_mQC+MmZH60zb^!`sB&>y~QoBrS}pQio&1)W}-X31a5JR5)6 z&ej4jhb7E2g34=#D?2^n+BJIGV^`%GY1t5f$0KC)QaH;^2$1~KD9-2#%wyo;>Tcik zMS)R!TJ@-$W~)np$yL&6eTL3iwpBPM#`S+3k|^g>jVhd!b$FtA<0UbGYH#w<_LNV7 zb)?`rc~D?5tO56v9_NYF9C&wo=?FV4L?X!(tZ-ijO1M8VCo}SJ`mUtvs!>hp0$o25 z&gEwmTIry+950&3T)Rw9e$*2E(L49(#QM}Aqn5WSLuB)i;CtqFal!L_%mg;pW@)Vd z;LW$uZ@>BW6n)s#O-gU>7K;>=b9$ZAfQ8A~fI%uSSm+JGgcmI$m!*FJ43z;ivZWTk z+Z89Kw6im(pZ(37=s*4Un`u&a)Xo=4Wnoomb7ev&)*4#0ZG^}$Z??j0%)^j{d(_f@ zaM02hK53Iqta2)HE)ePl(Y^>!5;m+_gjd<19Q>0RORlAWhZu^Bm=Vy9Y(1gqL8*<# z&GL0r-r0FngQ3t?N%TmIXjJy=;raZZql@nicq{{G9!c+<&y5@-{CAok?c~5GqLRb{ zQ9!@MCnVo=Zn$HF01sBq9JrA^#^F878GhsJt5&^EOKQ7S7|X@y#Vw<5ka(B_jReg^ z%Gr1<{a*Lhv! zBUhBwOG+-OI*1$loUlwMh4-=BfU7uS_7#z9;iAnLpt=s2Ts@oSg_UEx)!ieOUN;T+ z)6fzNh#hyG;Z3g^z5k-5f@>Wn$5coT+fQb$_Rq8%Mq2;%bE;%4o-KI;@+|G6jAc;sDv1w9pW70?#MMyP!3xhE<2 zpWT06H-gb3f6sXzAxq5|NBE!St3Y;Zv5M_^dcUbw#fryRhhPKHSQFlZVc_=jw*rc& z4;Mt!hfn{QE`_UbqCAsLMqPC8A2ZfD*nL zec-0jJ)3gX*_Eao9=Lw8ShpqM^gttB=`Qpf`Tl{j5ZDDMd!0$m_tF|Er`ruvHd1QOofHT54+ z1GYB6#bz=JVPasAW6ulk0`#Qo*66Qae?^jnDF#5LYFI>aM!X*Srln@u$y(6lpX@Zs zNQP0(>THr!9E9fzS3D@Lh*VW1lM6c7V3ulNIg4&*84;$1_eg0AXVMU0R(bo$w5}Fi zcXqj>_tGUXfLGF9)l~0e_p)XbjOPEVg&o%Rm;f5(Z$SlcYjmz&t6WqUuN%v3VSv7u zE4}IyGD+;jkt_6fzWy3IzvD(n zqd#rWEW6g@;7tKdCrB5KuvDOg62(&`^nZAtpI20%#FQD6UxVALX=fCc8$dH=<#-)P z%@~2Pd7FzUm1X@8P)cU5Ifvic1FpzM#lyCyfAXTo(1}$xEMoACc2fGI)vCdOZ0d-C zEx}X3ymELm8wB%yk7*0Cfaai$fe{R(wCOaulKPSBd`$B2IwK3%7Gh>_iQs_@S-&%L z?2h`~o8L5BG>G|A#3Qbdsd4Rx=yLr~Y#_kaDv z>GM8&owj!7sltz2;F~xys5;5KrTl+=kz&_bi$Z%s(D z_k+beDyA*t54WT8-$U9r=uD%2=>qxTfrvu87+`ZU==OAeYmv&Hebe)=N>ZC648y!@ zdphnqzv#t~i_PFi%GcC>kx?R}!>douXl9-)5(7xoxdL7Tq(4Z;n zi?{11RS+FBv_6pj$c_bfHKF(Yn14%>A|Q&gKe#V%PTR#?3nP- zx6~{;@$nj5b+IE%A!<0b-atb7vT=Qsk4IjyoW36{3Qj7CANq2d&t1lC0gsdK>K@X` z$=CBH6+ixWADb2{qXN$y)ILI~vl1gI6@uH%-$)porO$hcbEtv@;Ka3Ud_*a+0#^kl z5R;blv(G=y#E_ulB%^|+|5eo?j;W7<2Qc-*VCRRtN5YhT_&cskMMW_;o(b017_PxM z0&4K{HK6b1#bVg_w?FS`eYv5BU$N{0qFj60X0Q|W<46D`zR`u|vtS%Rcmo%;LH)of z#JMjIDY{i1^|!wK0f{M6KHEEE3GNt-|hucvCle*AdWD#*o9dzqN@8aE^?7<_(mbD(uo<>kf8xEL+-#W zEy7TTevB>SHIOSGaU+tCFasbMYdbHBl-)-)Z5>whqc8a^deS48(m31tt8$)!xp*yf z*9R^YE(*379b@+p@xcuHXhF0*tLSmpaE?K)eW6~Dd=Usf9b<*=W}pLv7ofWeI>}sQ z_o5j-qLB2ppyNHHa-+Mj53AXq(oEO0$vfx{j*BCJ8NEx`2Sk^Q;SgQU;KPJTjaRur zP0!`{dN=rF;s@$C+6TZ`3Led`2foSuVYql^V@gkb%zDb;3(YY-tA*P_vyb#)7e~uNnKqAWnXsF2E50Ync zh_1<1fuY$aKPXY|;>QKCSnbpH?&b#7XQ0Ru=qzf90AN6$zwRECe+Jn&Sf|b+!ltOj z7USAXB1Z5$OzNJ_ZuRu?zx_md(U)D7DhlU419OYXlLLGtcSV^TVvp>2ERjK*Tz+{y z@T0FjF{P(GdYzV*BxBLq^BfOccPNkWoMTZqJ9w9^)B6X zw-`WwL?1mk=(Z>958)+PzPyu!pZ8fFSrFwBJpskmZh5)}HWl?H7cEIuqS&iUJPhp^ zLO$iu8}zW#oHWzXocDpVdT-8K9K5PBp>8QyKo$t2H3jnTsa$y1)Bp9iA5Gu)*B(xv zJ~tPAPkJo6y!FzAgfgvCPR82r;eA1saK_+=>AV)o3TlDTG`zG8LNSKI1e6!#N5MeB z&QU(2#e<}U_)U4yFkg}70lVLCJG-F2|JNT$|NNUCkpr)^5Q#CbSPa&fYJmD4H{whp zt7&_O98=NL(uAe{5`E62)@WrZ@9-QnB%6z&_*25v`LOy2gBb%4fT^!xsl&5|N2U`% zSb7_by>`2<>+0aBTeK5;JKe!?(Nw|JU!~hFd?S5&X^l2gsX1FIK4~e*#tea=UIvJc zoy0|*uVKAzE{id$I%2&I7Gd{64~Ga3cS-SpvoVK}#-p!Zr8Ao|y0AM>VX7$E%losQ zE#Gv4G}2E_a=R&31dN-EI{xiPypV3Zv$xE+q$CMUwod6(xa)+h9cQ&T#*KBLuT z-r*5luQE1b@HE19zjl5zaZBUcR|-*Fn`y+=x}3sX*lBD@4MXh*#y^P6(Khv+yN>Gs zH1*-Z;_RuT`bN5gqRfD05Www$p99`fSsUQqTuH@q3 zyjpZ4uwsSKa*cz_Vg%v5s#yA~hJCIsHz|pR>&Yjb5?LDtrr^R#@Q*hE%~o8K*$D+? zw^8zYOE=g}^|EhyY|0n-`#FH*dxXCoB5KJ7_ZLX=}Hq+0u-D z?E9~y@Ayy!EZgVbM2T{Wf`(9Vh%3u@NGBp2>%KH;#ylVn9fcHUAhoV_gNCFIv^~A}f4P=k_ljrG zbDnX6Zo9Cc#k{3yqh+pRgqXdxy6{p>PWK|zFkO%l4Vb?C99qqli;8#wU3B?@y17LO zPhG?WzqAs9@o1=nV3S+~gk=zHb`O5jw82o<(e39J^pwY+rq}++GZOLZR&1FT^W1SL zH0*)uDoOFK_E*;#P?w zr*eV~Rd%yvd#c}_T={pP<{$gxvz{eS=;&>^ct$Tfp1}jbf~(RWWBpkoxI0AdDy-*e z`a=G-w;#}!3{t4lV_vnPUBJ!+f4@tjOt=-z=Mc~$n@$Y>xq(v4NV&Z%b7GAr&qOAJ z%Z9>%BZ8miK`TbPUCT(jq5hka-N>H)(6jDoMazO1aESKDQ z_LX?G+_?XrYq{RPNx}TwwB4JQ6H~Vy&X4{BXAb^TZooq7;7AY<*iw95 zI?1Eq6M#qF;mNkm2=x{Z{_@t#5tnxTJ=e z8a)&`?0>X>Tz4hCXWnEh@Z89qJwLNX3UW}c_9&|tg7RJN=AAs zgp-LAS`XP(sQ>bL-ac$&xuT5~*Vw$D4jJ(md-oy#&&!+l?Bi%aIM=Bpt1J5WZHM$@ zzxpxy>n%B%nT}VJtiFTUOf?*>wkX$+FD-m*6r;S^W=5xC0%nE(O^RK1v{VNZnCq7~&!5&@a-UZx>nBX-=HiLCCC_#`a&hoIS9iS{~Z%QK06LSBH_~2YUoQ0gVVZCTU^Vk zjf)FM2p<6q41(AV?eP5arYf$^`U4pV`_arqg=ArXRwOZ`Lya>|I)s$UVgj{;G$Hf++%yY{h9Uk`rFR$w2P{$CKj%Gmku97 zX}qum6CKQTGsfe(r;7rEk$lwi0+29>V$hTT+`yBc%jv6EY}R6BM$T-55UuLcuS}0& z>4gXKB&R*=Ou`89i0+vgK|VhGseO9;2e#>LAKao3eDa7saoZ7{+dibDBec1owOpmX zdr5*E<^^YIvS9$6N(x|L21tvCa3+ElYb+s_@AJybXuC|maW>s_N^8qRSDu>D6CSZj zpZD1(>GPksMvs5kGRQLF^n1tIcG0yZ!x> zCnjHhM+U$;`uv7Fq|4|D^(xvKb@3 zWI36^priy35+hirwQry=qP2##nxi?<%92D1BJURQ5u@)M_8PR*iwma1iuwu>`Xk`woNq@IHuq0HTv8hu`mz=ZM3cdEH^ud4@fcV<`&hq zd2A2oTt|R6bt&!>FGGmT)M+h<-xx+Q_ZZ+5#wZ0g73G)0l%#F=Lf*xI2?zU`u2WdE zT!@tmrd0HYav2Z$-C_Xg=4EQ$vU`x^N}~b^IjeRK4I23`(1ZoSln~pcIunLnWA*6@ z-OlI57>F*{Y`o!#)dgDG_MNzOVwf-)d8{nyVku#=r1d(B@sMFniUF7pxIBGK8HlMu zZeDc4_tG_7V3YgF5)fq?6U6!o&)sp=oC?fY*e@=A9iG3~aZ&km*RGQ&zi?MVNV#T% zsURICuS~AjhzV8skJ?o&&v z&BG7(Q!qBLbN5H;uGGk#V-G;87_^q)3~?3l7$u9eO+Ho8)-al+VeRAGt1mH!b{~a;_-WXKu0Eb&!?dz5&^rFMusN1KE zRuQV@*!r+*Z#5S1poH(qKPhQPY?jU>F4{*~tfe7LM!*&Xz&h!xE?oX)|FBINeO4hB zm`FxBs%wL*MO~Hp9;sPn2tt%bgIc$6thiOPH_3N2mZP>o7^S4lsw5d!XX+~Q{vedow~YwIiP6-OLM!)4 z_C=f)KU=*eGmL3kH@DD#`aC5lvpMAIoU-73eB!&R$OJnI^s7_~@Y?`o7}Z@&p~%;{ zNd&IY*a_L5Jr!iZl58Pr)EBnt=W<~ZDqt@NA?VsS%U_ofkT1%r65vy^Wh01-K~0Y;vr4B%*lINwf4)ZWX#^m+)ojqrW)NLK8XD`M zPA!gw3ZXgYi;8v+TUuXfazRVru<5T<=?#w!SLAofXQH^wYYfl&geJn^%*51;*aC2Y zdIQI!p+OMU$P~b$wlUJV6N%|WAPd|sLWxzre^i@FRm%-(O4MN6$v{UKI$(V4vjIy` z7v)FKJBTkjcAn>S??{Y)_M9HZSJp1%VVDsP-qrI76td~PxTv5KwAh(^hYk6q*ak0< zH=t0yGsh#q6@!<~dDHAh?{*gdXJNtqJd<)LjpRl^ zY2i=Nxia_*UmFvmlo6@T1+D5nqlm7Y?W!6U$B2o(qMd`5b`R?mq!kCI$`*lwp*y2? z0dK`?;Cw)QWdMQnloP>naG0ubgD9ba(l*{+xx!TF_Wjbzq&nPhKXGcKxvr|JIo-+Q zqC^Y_hqd(85&hvT?Pba4>x{f< zK5=p8weEVQMuzcueyQDqe5Z!{+ScOojJ|vpKx2K3ZYY6|HZvTysk;}alG-^RUl)ln zEsLVX5?_%3aEWML10N%hMWQm;XGv<3PFY{o^=8kK`xM4&d+D>((bLf3P<2P22PNWj z-97kS_HVSYwbr%0;ysFY&wX~69QHYv1mn2>o^}r;x=`0<;QxKGP+e%KpaC9f--q!R zMKhwZq!MxVyQ`U~z7>zRd5Q8fqRM;Gw$O8c2>b7| z8sg9K&+-&oki~Y=;=*#~;+v9~s}yus^cX1A^9Aa;@4C3L9V9fPE2ssS7qS!b7%}C3 zNGP-tI4~I->rg&T-c$AYvGK7t%%r1RwI9dGa@%<{yo`Ik2}tEV|z#z9av* zv)8(fM3^=j?+7F1P9-6ZK-X?IS!zQKr173qs(gLh}R^0VbxIg>XGK| zJ_FEL;*XuBH}36sH?A%-iKeK9@;ZNfcq0uD)j?Lsd8 zQzfTDSceMQKA=7SV5?vBU0IR*j0WYi=m+n!hB8e!bpQ{(7kk;1Mf#e_ihv=a=FGwub zX_WVs7y^Gke{XxXNRu92sqB6|>Qaa~2V_N3a zj!l^&k8VC-5D`6JgqD+crt2rOx;ekIIJ??UeqVk$HunEgjDWd=eLtzH>S$-D|Mlew zeeXWH1O1xudq+AhH<)n;qs{m;s{pBP8W>`9@;&;_Ie|we>VS#*poKF6c85L~B9RxW zIZ3FZo&9VOWNUS>e(3^%n)Abw9&%35jdCaT+2WX3m(;3{tOtspgziJT`5Bpb%F!M* ztXbQ`IaBFv*MqG_e+uI#QW~MSn+*JY(Bz|$nm(4;u80F=$~L#dYsQW+rkvqeb+o0&wC#*{?PiR=WtbFZ$G`!PW~XVg1Z3!?-B#x<8?&MXMI+6*i_ZeuP}D73L23+ z=XU_uBgBIdZzwtiiRp4Od6Eq%HZQIUz@XW4M)}^*umzaAShy~+O$V!LI=?4Ohl_rw zE*f-n(AANUicKP9>kjKT?nrCI8SWn`K$5KPk#m;i(>F2ph~UEg=BGwSQM`km@jbzb2AkZavj z4B)m`=RQxT^?%+yXxoaFD=QoTiwzh+S{NodA0QUh7OHiwgA#!uK>dbqku(M)A?QAhFcb=8L6@q(mm$U3hs-YYY3K1AxdgA zV&T2Uh=6c%Ld^)H5XPj;AxlC>OGa!WW^-E+VdT9N<#)BPBg4?GNs+)&QiJQ%7+m@* z;j2ZJp6HQ?$I@;tY!BpKx+6t38qQi^sH#W#|BD335kxoEJIEtYyybxO)snTq>K(bW) zkK1Fg>GBvmkB|q7*?$U{qj0va-IkpRD^Yg>A+Co-ll-$IDmDfy1d8Yp3r!PX0MO>~1{0 z(L9-m4#Ylo?;!xahm#DZP5m>H;_b0m1@|1~E}9l>XbW(G5WgB=)aA!nqXsE0L=w0u z==mk#=VrZs5IG!Rw6A=G*qLE)Go7S<_SQz(uy~+q5YAxCi7(arnI5MJ;_g~Vp?xqY+ zhLVZEJcrbim#Dp4w>GP2I`4m@sw&?9ueqzlKQbzJ4gs*9vHP+9;r;3EgKb?ead8Kz zv*Rt7Mi%A5lG)@$E3ciXD}p#Yj|=z~VwXks%ZxM`ds2FlY71^DWE{ zgTmUFp(Mzn!4{eu?}NX?V6w&z*AtzQL%c$~2&J}5N<)8o4WqFn9I9mT=P#UL$$iuW zE9`(FH9S{|6om5R_Bz3>tUN$=aw=nv<>2S?-Xi(>Av!Zb7YvQ=4bx|owt;~Qu&Vmy z3H9BD#WUBhUV6u!&HlUF3_v=Qe2rV@+n--MQU8q#+ly2xY-~VMw*ypy9NYi^U0MeC zEBoki8E?+pFPgVQ_jt?Ca9kb?d+?hXsN|$plGSU1#{yvf(Y&R)?`U&Xat16rH%9nt zW*>}ML`YWd+MkC79`hVWf)KtW-?cgujJXk(d=G^z^H_R&j)q|PLsND-JVR~@%))aK zZ7v6V0S&QvQP{PH3WjB}T-A$@&SQ~NtJ&AzOdR&7R*_!R03EwV#z2)Zay>$vNvR$fY4-NIKRUTlKR3&nyH5Of zH4b3d(W?E4`J#T27YNZs$e*FAE9gne>ICG2{LlAmq?Aj`d=n39uC*)%le<(P84Xz@@#n_j{@*O{%Vpf)i@B*swU=VPtAGTnm~V3DalH12&)a z<(}8Gu2Ec6SUG^vgMlV z-}!7rBGQg2ShT7!m@Yel#t~V`&>$WN5-~Dd?*~3VU0a+Q^bh)g63|$W5NWE6lq}`4 zHMdy)O`o>nL9(-usaSa&_YXSSn{zu^=TGCCV81e{_y$^NG@~&bi7GF+PyGZ<-C5EJ7{f=|@iMNQ(?Ep6|$ zv_Egt<|MimnpF=%_t3>A0Y4xGBAm8&RdnSzz!^5Wt5AJVBEVdE1F$3dYH2`-M8@;2 zWz(5pc$>DhrM%sk8T6#+i6-qBpy8c-7cBK#>Zqor*#z?Q^P??3y*PAUtx$)oj!qdi z45ry~N_7OvlclkN-;&>7Dlwb4zVs@Al4Rp%LID`{XpsVEx@hJ z0BtoNTrI2oIRgfJuz9EyMkVSaDbVB>= zih@gAWpZK8wOb~U)i~!wJDm`nT%Xd)ObVokzh43E z@lP0y2_I2GvQ7_i{FUrsB=FqWoo-tgD8#D>;+$|Fc!y!$x?GoTu1%1i=i`AZQH7-! zu+4oO487f->i$i6gSh_O+M;<9t6Y_z+BsO{sxET9!ubSNW0+C35+%KSQRt-Zt87_A1yvDMzBUC1 znai*rFpgZ5)Kv|I*rjw*XQYMZ8VW#(DcB1bSg=9JdH%y*0{+Kkz)!5yw6etZnOq&| z>`t59RlHyZkzLSNg^sE>8WPotG@L*=SO}+Pm0Br z`1yQaWkjd_LPTapLVHgi1@!yTix3C^-pXp*SYi^r0&YQI2#O-+rK9?qjG8$M~no?g)=-fV~9f7y@ zWLqqjz?_$EH169$EB;A~GF3#%dkLy^)u$l*bZP5S=$VPbM{C>@xDuEwS_&KBT7p$j`bZJnQ&8UT3#k+B)C>qK1}cQ>s~g()++Zp(F~9t~K@I zuzS_gqpEjnKpx%A<8C2<+|NX|+}kU554-pF9i5u9)4u9!mB5C=&_ZvPlzaik2e@%? zcnwYzZz93}f;2?^-H-$N^s%s{{tl`^aSs~f^c_k=pY}~Rzfk>-zjO89!})@moLieV z2}u)Ovo!Nn<2E5MtQBKohEJ7r0M)(U&ZJO4n@lsVJha~xuay6Yn&X!#=|#rt#$VX7 z3JPIZ-@sO2$zjP0!<&CQYjh+!B|_P1Gl=S8w}FID!fYKwhO* zMg~k18H~M~-Q#r~rjshUfz5mcy9(#w!McX9N^Ri5Rc>JIk_u&|Mv<4NPWv(h&yvzze~Tgz9J|flB5Oh!xDUX+N8|1q%ZSMKpk8D=d_qF zQX$e|On{41UEFDFG0>xfgoeePev^o_OcXLY3WWt(@cB$gzWD7P(QnOoY0}&!x+il1 zWR`vzm;j`pebiTF!OdHMXO(G@>?GW;*TlZ zR_7g_Q!av_dg_wMB$IZ5PO%}%^mHA%7YF;~##7leRaX-(Rqa^e1>nG+&GkvWztz5R z^(FMSY`4Ap#6LDJmvm@C;Lgo-C7tNr+gJTbc5T(N0pK9XCTNJD9e~t{upFYHpeZ#K zaF}r7k`}`*AkCr!i(2xoW}76_-DmlWeTJeNW*^Y+20@1&g7iJHz*M5sP>E*bF$&fhDk4s3QoxXgTaFy55ksxt}u7NY!GN62RxlWBNIX!1rvAeh=M&J*1t^9 z8Y5u*NFxVDs=K5RHIdu1=K<3!;9bfMP{2~kqRN?MsV13wPE`&0uE^7nJWLMev5dn- zM@$2;7&dXSW)~+`o5{|0_sb_9-Q1(ZUv`b|5s$z&o!OamA0VO=?D}FhpL8A!gOcmT{_7m72JVnUOx`4VfP(i z06H&bd<2PUu1vJ*8x*dng;)jE?fn_CAV;z#OC_0XD}Xd$>=XGrAP7=~po|rnBQvk1 zsiG5na}Xr3E`uRl)dd?v%pMsYBK^BrRdti|^QSLeyY%Pzb??6M?;f)O3_Jpx)$O!T z{~_4|sN!fBTfN2=#c-ov$p+jNDDDZv>ecq@=;`E*+*NXU}#E2B6w7KnDuVdzg5qdwoF&guvz`{4fI2a0VOKN z38;jv2mkOMh%dl6gDOjKi%&4S0Q&v~X2Wm#jQm+65_Ak=U)397_lrk_Q_pj0Pn`K; zTmuq{c__m*QxcvUdG#wK1dG9u+e;gK*oFC@u|yWQ@H}PCaYq1cG4aZr2e3G` z+SK#y?pKx{w)~!Z%JM%p9{0#$MG4<=^UBT>u6ai@?XEjGNEJA;`j#$=XgZw=p}Hk5 zP^k@&tnf=KMCAH?93PN{o(F`;W5ZuhQpsmIz1Px)O~MwXuH}QF1Kao^2ZCso9sgZ6 z>X2we^At#%9^-D~2RyG(Sqh7KlpaOKSkM=IF`8bg4WYOiGY$-}>&PW?S%|Givt zvx|SRzS{iU!C}vJePwSHNS-Jb6UIYFJ3YYl!uaG5&I_5PZ`b5K=6z%pYD&(%;xf_e z@`T7_3^oP@W>kUEL3)%T>m<@mv)xbyNw-19m_9=*6?6OuKiUF}_^|Fpq=UMlj~PlG zRf`Y-K@CQXo&?co-n+6QQ87WDUJ*T0;+AoCa`9Ttin6AzC zf)-t;#Zrh=Sfm&i#Lq`5g=JL)AeoYvcnslPQ8h)MYEYD*x^Vu@3Ew0>xCQgFOuI3+ zCzhMZ)~(%tSbb9Uk-Ho7f1e-#J=9QfVY~gqwe|W-E^IH_s;ZiSI*FJGC7&XKV#No2 zxEMr5S=`it6%X+XQZ%5=Q6cTo>tN}g!*b#(q16h`#vlM0A&og~CK7i2(WIBc$Oq%$ z@2vw7)eh@wyf|d#qv_8VDV_libu6MNxO7n>%esi&EmWjHWFFufS_@vcf(V!AKp)Y(muWPN|{xh$L%RojzFbAZak*#_tuKQa+}v#3pyhLyUppm`dZ4*XatuZ*Bi&^_0m@e?vd1uD|D{|6pkD4HR(h zw)W>vTv7k6vs;U{uImQVe4W5hCylz0!76mNgJ230SR^?A&bl@u0hi{WU_dOkZj)vG zMxm<&MikS+K&AHKV+n~>u`j+{KeC<_U$SCF?B@U-gmtF1lt4ite{zyJmVrVI(y zDQ6OW)l~horgpMjKV|u<>iw5m>+e2y1D1#AD^*Gq+NOW9w#0_Op23@HzCnNY+vHj4rv%(U0acavsBq@612G zSJHm5f8LJ5^%Mna`m@~|(vZO|S-hu7Z;<+^n zc|EX8SBtmk8c55-%L_mQupUiS@pXdyFy4|I1VPuHSgq^DcKJ&CZh^Tfc5b=cqi<#VCZnI*#a~s!qo%}_Q}_(x*}iY8t!s(0;QacB4A2v z6&9F8K0b~tvT>K=O5Dp3)201gaC|(Q69pC8h4iE*`j}e4KL57u*4C!=;nw2)C*Co6 zGCePAl=p_=@4jXL2{a#^r^nE0_h*Za){c%kP7?r3S!BHh8LhA@5@+NAuJLDn$r(##3SfLR?yqqNd9jrGf2;~D*@G4l>C7N3%rfvO zh?-&UL~zFiJs&)i#n_=iHKO^*90x>=_5hS_)~@KupgxqefJj-1Uw>DEwj?OK2JV&d zvTIGEbF6>{o~bJiQ3f!dO7jykA#$LP=J#jj==B#Z0u<~777?iZaR4d<>jqLJx5Dhj z9~Nt~rs+Pl`0|yZ_-OVL%VX3N_Zr!;8p}rg>D*R9-)wa zTtqg(Di3b`kxTlb(_x(jei0dB950ZQ#j_AA;NA9I3U@0!= zyP_85=tE}`!~S7)9_Fkw`;-FUYUsfU5-De{uMC{W6k{cPYW2rFqRuxFE z8J$aJR^8%?&B^5aO^Y8p^|0yp-dDu0eZNl*4P-cXVe#6H&E_v&P*p&Q0udRw(R_XS z5>zx0LEI3Q=euMP$|~b}g1Y0cPKpv~m>@3pkW-mJ(kWE8cM9%FWdgR}Ub@Kp>rAPH z;pL0?;kis+RvQE~s3ckc)u^r zXvQTwG7I}vZwavjxhGv$l+{LpHCpY{`?ISrx%&W?YnAE}mE}jsj@CH&0H<`HfFR{@ z0;Df(*LUlyP2C^#f41_L`b+3}smgD6Z$tmj1OrH_!1NduQL%%Hh0Y3ui3P zUaA35L?qLVQyS{hw(!L-o6ZykI?}aWPe%)`EDY!al7>`%CEKA!_(6sp61+-5B~q8{ zzR74Ld??-?+>}f75F&)Rt8(mU@<>3^?!0gfvia_ZWZ8y6S)?^#=} z(~CN^JEkB5ux)3Go}3Z>CeAME0=5MNz!$NBC+}-mM4pLAVJMj0ebGt{E7Lz?Ra*JM zE|a!B$;Fdz^qJa0w0wJfdoC`L3w)?G9jxe(6~_reDLodfY6aKb&i6la;skx$*{ya_*VP1dQ5UkxXCY~FknbKP78215%0nESgHcx&oBE^>iYiK#slS@No++vVLb!Zr9ou~AezfQCqqt_1g7g83M3Y@xhP*k#35C9ag+SEvDFM5xBMBHaK2ha4KG z!xoS!JRW%lflCVXOZ#bf??J425!WAdSBI_kGk%ifta2;@4G6wf1DP3S?X|=utAq-} zmESc!18L0Z(3usaK#fTKd{<^}PMFztki)rkoGK_+=eCSIzQ*1xRzMiE^h)uI_qEKc z@V=$>!pTb=EzWE-ldbdZ_nkO3dBwez=6kYR)i@i-R-?fwgm<{ z_+1HXut{h6>gFQEw^nzYx9in>yxj_zxVsxMsoK-i&66}T2Ux-~MEe1A>$+}nW^*#R zaISsz$DIFkx5DjhH;0o*#W>`X!!{iW-q{ZtAiLV&WkN006p&29In zWl3<-g^Wh4f+%ae)+^ZNVjzx^Fx=J$(hXV6w4|Q1S>E-Jv{$Pk4xb~d+#qsj)0jz-hf4(+s+YSDIJ&aZ%6j>Kb_D%(;GMs;r_^t_Lh+)%H zbHP_?*H$N!{hjU|{X3d(e^uW%^s-Ca()&S!0Qh+6OZ)m|FQfkE57Cty5AXhHI;lTv zXHWA5e6{fZIx*w;x{s1|Ai?A)KfAQ^^jvvN{v&g)MLn_1S2$2*7$AX&V$fqSDoNqU z>p5269XpVrMSpnEa>B(WY{qYjTAaMS@l;~H+#@W+<`=zm;A4o#-EaUY3PWC0YvA{S z*%@3P=q5hNe5WQZ8RCI;Qkf3oPb{h)1Qeuh;=xvFFy28p$?a_PTV9^j)x6!JbIp_2 z9$9_j-p=xSz%YQ|8vI!QdCOOKzd!G2^=Qt)P*vVt2p9$w zuj>|hIHyJ?S*9(k1YpEVhYC)6Mt-J#jtfY3C?<-hhWiAbGf?Fd`%di0L@c&+=7AA{SK`H(eSSh)HGuCj$my!Cd77QPm)L zouNG-zDul_6o%8TbyTG)RNwX8WLou06WTjE-+uMxRkJ_(Od9<@V}U>yJ9scS2;$_U zsyFZN)n7Yn>Sof^Y%uL}V2_Jm+i^vtey%ZFN;+t}%pOESG2)lTg4D`Ca~7c0l!t&9 zUGR~Zby0d$OjxK28ZpSmFEmK0`m7hq9E-K{zq*--CaxF)2wPuyyY^3lfMgD&F{3_f z!-8$J>aYYHX}cPnqtDjOIb=I1d#jj?Q;ugG7ko(_vrPEVS3qZogwig_>{Rwm3?bUn zb-K!gfVX9{4_ZquP>tvaY&0crfzLfxrKzdkT&bJGTl&8v#DAX$``qslfF5cTIeYWs zC8w@#UU6Z!ZQCwEB|&Q<{Sg(U8}om{cM%OrpXLQfzUhKbn7bxcX>e(v`vHPIpg;x2 znABE*eG;Uhj4MOpqUM-Vg}x^;B_w85)tPG`6lYa z;W~iPTKPur5xap|=SRY5!*|_QTaig%8p7~{`&8<+rtQ&lRG`^!fX{1YBC1PvsQVF& zzjmLj4=_vmtK-~&B`_KN>uSEfR!_IL+P`=D#Nh2PL67cmyE?NfX~A z^^X^@GL;Ze0+`e!;~3-z%EreU%*P{oT7+)wHXI+V>lmJ+j|nQshDgCy?3AH8sE`@|DhHs5t_tDO_|QzU$`4;vf{5qPjTeN_p3 zlvr#p;hlf;RS)v1Q0DoGDMx^rfSmJw=X<53voRVY418Q@7NHCp@SM~fS-_nN@6qM} z3(`Q4Ji}723zm;oZbdo;0vEjrTDNIN12SDt%89JG0HP*Asfj(&daB)y1Z1h`UD(9umgf;39sQzbvsQ4{DKv)rl#7b~|T z+bA{ca(h;O7>Adlb)V1jW|6unX%QJO;2`>^2VH`<^C~m3CuQ&qwjbMxD<71i0N0Db z3{jQ-q&Of!bFpVeLJZFZAGurhW3W=xv!P{hFJjtFa_^xLVc^LKU5-u4TUEyYqORp` zreL`}vv8$#n04Ub(hXee+BEzJA$#P}%1}#Q>BEye#cFR&`rj?JGAn z>TgO&fCGi_y7(VCAYLF85CtEvF~See=nErDad}i1vG{7dP`lrCG(WT1Ot%lZU))@2UZg4T_gCV-Uo!x#EX@AW zo!$Ny)>i37XSWvfs;ZJYfGAE&H6hj>SpQW$2gZ83E{( z@E!(}<4TCSXkbVXz+^9J#2%UeNj%VOVKswMD+)VDKE|n}4GHUw?k#u0ne8RuYwd@z z+~NW5>(?7f7iJtGvCDKZHQ4Sg#C>3tfGKWrZRZhm@W zGTocgE7xY#H$M=F|9;N^N+xh&r+ww-TK!E)9U!t24q5+-kQU2)mPT~D-4ji`dqo&5ibNEh5NHqV>IgH)~&#e2_RBYLy|o$*c6}_uy98*?yL*oj|P_k>f#qWN)p7k z%AX1(Gt$G-0DBxz89o~jg+2;{ova-xeJ;<@r0FD~hXF$ryz9Kkn5H3oL__9cjZBbA z&94}R!GPJ+rEJ6b6%70L7QeW;Hhn-5{{w~r43NMr^H*+OHDQ}T+fiS0pnydQ4sQ&r z@z_}@3VM`3mOHs%(_^;?Ujk;J@E5Ky2rcpn_5k!Q2Rtx%h?YRph)G35CH*^G4@QxT z9FZ#ei}ZuPYiwqSt+Ho8^d`b>@J_^3Xx|^f6$v*@hRjAAM*u}S(5BK9xE=iU6^yfa z8mx_hre^{~x;a0y(M-2@7r(f<{=g&t2NVMs*aUX^f4sCt|7v%qZ5M4(I z0C)})HS$IH$5*f^dT8DDkmEZJ4fUq9^!R`{`#VJK;TS;O0wu)t3y>D;kU-qJ!`cyP zKd4>e(iJ<68;M4J-Y3LSS7B2ylFpWRwagpA>HTIrjmcoziyC)UhIz~2Y#2su2}|S? z#SxAnL(;}eSdQGlwjQdgl-7H2zI)}$spbKr^FN>&zyJwsFTQtUz4_6t-HsNEuB#h% z0NZuJaK(qq5nR6#0F*^ zMQxFLgTBB~a^{~<1ZU)+z{7yS0Im2lp*Wl6VVGwF$JBHkl+^NDnE)iuGBr+AB11LW zInZ?ErLFzQLx=(wd^qdO-~bJMSPw8=#KhlmhRWJn)3oQif4z3P`A4!Q>48T4 z_sc+_J34rflO%mhKdm;Ze}DdCi+1^%=7;A^wQ_jawsl?QeS1s7MBT428lh`08?EG? z!l##oitD2q{9_He1u|bhQq+grO#7tEg3DWnLm)~S77Io?U0>EMU~K9^uowZ9xbR(* zrh>AR_S6F@4v+WYD4A&7Fe4Ug0ip_!1WI&UsJ`pyUTqhn9fj#AQ6i84Nq_8m(O1!T z2~4}vUV1%)DK0SN={zvGG z+j;kATydrE`m`~4h?EPWLg0-}tyHBxv+bW zKn8uTmLAVVIXrVQdq}bvuTA<#ODs;Asv9<^ih%HajM&R^Y0;aLe+P92n4zN|j|A-c zZn3%9Oqw};{OBWxU#Y~;>+gX~{112rU`*hKescOr)w|BUyZ(y(J^JsbHz(7os<{q> zRAxFJ=y>Iz{7tiA0fqWP6haY&&m{{if&4&c6F{rbXUMv>XF1+hVq!+%smLB)lp((= zEcpqJ7IHw5^{dpwZP7cZ&-M@a9mY&$a9-x{5{g*FRp%%tt2k*xRoKG-gLCvceM~Tu z=tY~uan%U_t~?zYr_og`UP0Zd&B-e z-(M4Z9&#!G9#~{y>umdPmQU5+e|SK2c(iC6u?yr2eYTJDvAgjsTCa0e98kg$g@zRb zUqbU>h6lYiUg%FB)B8(RL2prMHF%wnX^e?KBdESKl9Vb3GBNoN-foO>P{|0#1N9j| zQbGDFmw+HAG;nC5kD36mexIo`I)8fGP=%@N{y43CW!rXLGij=gwYoaE(EarK$@)7J zYrP*8^SCFD2Mq(@L#(kJ7SLX}Y4I(~XPSRkP3gq;cDtx6njjMx!ax5_KN!KJ3-ymD zoJ1-FjS|npebPsuCO7hVF;j@@Lu4U@5@0rl4x`$P_o%-+{C-?%oIUZCDK##Y)rJp|g+132)hx@0mT-*J}<>mU>=e8G|vy{R{AYhsb2zy9W9{XNi>(`*UEA}h8G`-DuO(3Vz;SXv6f)$UWk7uLkANuW0 zV5=SGr60mwM>uPyuckM`-k z2e;1uw-b+1PHaq?<)um0cfG+kOMWpt zt7PF~LFJ^KF(f-ok+qF2V9$DAkW#)3&ST?BxNZ{wsmn&TG5ig%IsjZuxdk_b+8*q; zq=&l;3^et~!z>S8toF%3<`Um+7&>b5Kh@96a^^+M7Vg=nmst5O$WnAZ1_4fGodO#Z zy#oz8_);o=hqx{=?nwKkxt9h35$IHxCA5-$M!k=z*12chB|TP_1STI*$qV6wMr-;{c=_i;&nmIa1(u>lC|Hlw}^grNk}- zX)W)l^&ZNioi@IJcHjVSVc+(Bdt!4k>5k}pzt#TW%9WF!kp4fY^!hD^=u6hR(K6mfID)k@qDgYiliA?;g z5^P*s-MYF~ea+tO^mnFJcmC97!`^|m>)IR}rwEn*V+(jZ(Zr#o`sv1L!OMXwOhy^$ z#ONDfXx=-h3*cJJ_tqo7$9RKG7&&w8ty;=<15YAOu%s^4}P@ z;A7=q3~^{?W|*h=Iba1dDq>}pq5?7~YNpDV3X%`s&aLlx($_beW>(Ss@V54!{`yA! zB|`kEYVSjV_#YAo;J7@3{agCS)$8<%W|h9?a87h^uvoC`I{k|Gz~>-!Fd+zR02sJq zsmBP@O)UoLE0PvT+}p^7NTXe?kfu=53?-b~0EX|PYR)m^6r`uaePLD*3T?d}!XzWi z@C%@rbcq@lF=kjj9DvgYhyynI7*BnURn?!`Xqtol?)=iC{@;IflYaVzl+odXC9vnN9uG+d zzz6zuZ~C+Ct2Q6A{(to=^v%mNnr`j1%pB4d7*0q)Y`VZhri)AbvV0cC9C#8D`kx8k8l|;v8oDJBBd?rFSU7N`aOOQpU3(4j>?8FJOY@ zu_M71(TwHaGT13lICdNe(}Mbn`i$0|Xy+KQSQ=haT672X8{FJ!PafJ7jahiN(-Yh2 zbTZjoW~A4>yPnrSuyUsQZNBY>8*Z3ffBp3jNvV&=L!JSYo&x^9bFTl^YS#Vp)z$jZ zTL(n#QM=%MCaXH-BD(=A7z<)%Tl#qbWU2(`_jHLvXJ4ow(FCgzqvwmc+lP6 z9M(Vb$zP*aJoa0v1F`V;4`r#3$7Kd^j9T!iKis2&;@7{c$7KdEa_~l_*I%EFLOZwg*EA>TyF2Q>b9Gi`tWC;hmlSnrTDdJ*VzF*Ou!mcMpk<4j1e%=2d0!6lk1Nl#u@n*B+J^&&fcs99b-d z;W7D0@E|usiLzrSwoD>8?)^tHB}Wm2UaeJt%AzM_4#TJwVT1{c*eC)&$y~B)CX;4; zg=lBHZD;f5Ki3QTk)^AvKa)!yv@!hm)Nz>s+(kxk>#hB@8yoc9RMB@VFVU6z3!?o! z_8O$P*0gn?l5Vt~qbQSFGbU75&WWYV|R>?6QIHE*_T|z}@s1-1xIME?@QakNK*$>At(J==p0a zb+vQE)@RN$NC2L%9arX6H7HT%TtnUl+ee~HIs z25>hR!Rv^c>mh>daHoHI+jTFVH}yBJtO;%JBi^q^=0a(j;R@kg3;EB-)TI3_Ma(Ti>vf}Q}i+-t^fbT*_ryQ*#`YipEt zg5BNkZmZk+H>jbXTV1XG2rj-1_3!3!nE~8Ghybn3=Wgsz&aR^8w_W$Y)fGK=ZLMBA z>RCl-Ije0U;8u#0Ky;w#(It7uBA2DXOn<3lue!0x9YYSsp$YKjd9pG>b|}JOq0JWl z>>cYMC<-4E5K7+iH*K*;bv0RCt!b$u+S%(4>Y8q-5BuL}&mH{Q`eWB`K5o1(AD4by zW&rnm905aqLk0a_|ERr+zN%W#i|5pR{^oj}e%#&Tx^s&*dkq+4Q$6mdhjSM*;E#9! ziu{^^O(36l16!~^_#;biUSK;IxKm!@(qG;~8AF$K`eam@PMVb!q8Zn6-0k|Nrazrg z{TtnaerHp^l!jv0kt9 z`TaRtv)Oh?t?1i~4N{l^CK^fO!`Ob?O=n2Fqh5rO&qZ^E3$G~ARcvuq1~!eY^NmH*bb3|2=^xIWW|8r8;|9x>tzqx)YyYS>WHNB2%dfsJM-o15PW&ro{(Z95> z>1Bx-1b=jYd;f_2DLt?4=`Z$8_gt#$N3E}BsmXZ=M~A$>K-XqtcnU#I+4-fnpq$8o z)Gx1%zH~WN)CSZfrXm;-Z4w4e0`Vv2#J6z*bN1#{)nv9*(`=fzp*mU+?e4a>HdXcd zSyjJwd4pb4Rn;f_At}~Rb^FWVy7%&NnE`wTRD^PI_c!#*7oJE@?q=bm+;n)IJH zoivYPXv`BdXTssYqj^Vtmzjc?(fH>K21Yy$Aq7|i28tN|+^>?Mc(x%FFi-;bc*Cm7 zVBRB^>gjY+&6bE}!XI|`lPmF)iv_)NdAWXVvrX^Z{lJBHocyAbXJK5DHgK8f@B8C2 z1Nh7%gH*97{B}dXy!+&%C(%;#w5qS3+7W$GKkFY$eRIvms$8Rt;V8T67xSb-FwG{$ z;vm4Olhd11C~eAejsZN%0J3flNzAe0S+Y}1CzGmfh?ZxWYV}@5E{ntV(^X3!=-cW& zP1C=9zSq6+#A5oM>QNQ50(_?4dRfwc29L`O;C^suUOLzf1_>*D->>eTqbJly^yF@} zee9yGK9BnTQPmP%+b;Tz<>kp5x3WAnLZ*ebB0~I-T@jpWi#odC{A7f$Yg%T}&4=`8 z!itA9b9j_LA0D*3bxqqu)%*JeotriNo7=O z9G4lu{gffR`qfmw-~~26qw(w+eD@n$A3D5x_3Gx4d);J}j=C@E*6Y6C@2;Lr=&6fo z%^O@*bx%#V(>=YOHWPNwMdiLCR!I6qyQtRdck~st!~wT^)b-2s$NiKJ>Q8r7y+LnS zozdRjjqN8kpR{yyRaINZ^3;{X5v07*qoM6N<$f=-Oz$^ZZW literal 0 HcmV?d00001 diff --git a/frontend-web/public/cm-assistant-logo.png b/frontend-web/public/cm-assistant-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9051a83a5f9bf1b34121ab752318215446e03ea5 GIT binary patch literal 34186 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 YUvlbQ|KF4O5(5x;y85}Sb4q9e04* + CM 助手 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend-web/src/components/workspace/agents/agent-brand-icon.tsx b/frontend-web/src/components/workspace/agents/agent-brand-icon.tsx new file mode 100644 index 00000000..4a82bf61 --- /dev/null +++ b/frontend-web/src/components/workspace/agents/agent-brand-icon.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { BotIcon, SparklesIcon } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +type AgentBrandIconProps = { + className?: string; + size?: "nav" | "sm" | "md" | "lg"; +}; + +const SIZE_STYLES = { + nav: { + frame: "size-[22px] rounded-md", + bot: "size-3.5", + badge: "-right-1 -top-1 size-2.5", + sparkle: "size-1.5", + }, + sm: { + frame: "size-10 rounded-full", + bot: "size-[18px]", + badge: "-right-1 -top-1 size-3.5", + sparkle: "size-2", + }, + md: { + frame: "size-12 rounded-full", + bot: "size-6", + badge: "-right-1 -top-1 size-4", + sparkle: "size-2.5", + }, + lg: { + frame: "size-16 rounded-full", + bot: "size-7", + badge: "-right-1 -top-1 size-[18px]", + sparkle: "size-3", + }, +} as const; + +export function AgentBrandIcon({ + className, + size = "md", +}: AgentBrandIconProps) { + const styles = SIZE_STYLES[size]; + const showBadge = size === "nav"; + + if (size !== "nav") { + return ( + + ); + } + + return ( + + ); +} diff --git a/frontend-web/src/components/workspace/agents/agent-card.tsx b/frontend-web/src/components/workspace/agents/agent-card.tsx index 006f2811..62870b9a 100644 --- a/frontend-web/src/components/workspace/agents/agent-card.tsx +++ b/frontend-web/src/components/workspace/agents/agent-card.tsx @@ -1,13 +1,13 @@ "use client"; -import { - BotIcon, +import { ClockIcon, CopyIcon, EyeOffIcon, GlobeIcon, LockIcon, - MessageSquareIcon, + MessageSquareIcon, + MoreHorizontalIcon, PinIcon, ShieldIcon, SparklesIcon, @@ -28,7 +28,7 @@ import { } from "react"; import { toast } from "sonner"; -import { Badge } from "@/components/ui/badge"; +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, @@ -38,14 +38,21 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { - Dialog, +import { + Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, -} from "@/components/ui/dialog"; +} from "@/components/ui/dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { Select, @@ -59,7 +66,8 @@ import { Textarea } from "@/components/ui/textarea"; import { usePublishSquares } from "@/core/system-settings/hooks"; import { PublishSquarePickerDialog } from "@/components/workspace/publish-square-picker-dialog"; import { SkillDetailDialog } from "@/components/workspace/skills/skill-detail-dialog"; -import { AgentQuestionEditor } from "@/components/workspace/agents/agent-question-editor"; +import { AgentQuestionEditor } from "@/components/workspace/agents/agent-question-editor"; +import { AgentBrandIcon } from "@/components/workspace/agents/agent-brand-icon"; import { SkillTransferSelector } from "@/components/workspace/agents/skill-transfer-selector"; import { TagChipList } from "@/components/workspace/tags/tag-chip"; import { TagPickerDialog } from "@/components/workspace/tags/tag-picker-dialog"; @@ -548,10 +556,10 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar 内置 )} - {isOwned && ( - 我的 @@ -559,7 +567,7 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar {isFromOthers && ( 来自他人 @@ -568,7 +576,7 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar {(isOwned || isAdmin) && !isBuiltin && agent.published && !agent.is_private && ( @@ -663,8 +671,12 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar const skillBadgeItems = [ ...(agent.tool_groups ?? []).map((group) => ({ key: `tg:${group}`, - node: ( - + node: ( + {desensitize(group)} ), @@ -674,8 +686,8 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar node: ( { e.stopPropagation(); @@ -823,80 +835,181 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar ); - const chatButton = ( - - ); + ); + + const cardStatusBadge = agent.is_private ? ( + + + 私密 + + ) : agent.published || isBuiltin ? ( + + + 已发布 + + ) : ( + + + 运行中 + + ); + + const updatedAtText = formatDateTime(agent.updated_at ?? agent.created_at); + const cardActionsMenu = ( + + + + + + {canFavorite && ( + void handleToggleFavorite()}> + + {isFavorite ? "取消收藏" : "收藏到我的"} + + )} + void handleTogglePin()}> + + {isPinned ? "取消置顶" : "置顶"} + + {isAdmin && ( + void handleToggleFeatured()}> + + {agent.featured_order != null ? "取消推荐" : "设为推荐"} + + )} + setTagPickerOpen(true)}> + + {t.adminTags.manageTags} + + {isOwned && ( + void handleTogglePublished()}> + {agent.published ? : } + {agent.published ? "取消发布" : "发布"} + + )} + {(isOwned || isAdmin) && ( + void handleTogglePrivate()}> + + {agent.is_private ? "移出私密广场" : "放入私密广场"} + + )} + + void handleDuplicate()}> + + 复制智能体 + + {canEdit && ( + setEditOpen(true)}> + + 编辑智能体 + + )} + {canDelete && ( + <> + + setDeleteOpen(true)} + > + + {t.agents.delete} + + + )} + + + ); - const cardLayout = ( - - -
-
-
- -
-
- - {desensitize(agent.name)} - - {agent.model && ( - - {desensitize(agent.model)} - - )} -
-
-
- {statusBadges} -
-
- {agent.description && ( - - {desensitize(agent.description)} - - )} - {publisherNode &&
{publisherNode}
} - {createdAtNode &&
{createdAtNode}
} -
- - {(agent.tags?.length ?? 0) > 0 && ( - - + const cardLayout = ( + + + {agent.featured_order != null ? ( + <> +
+ + + 推荐 + + {cardActionsMenu} +
+ + + ) : ( +
+ + {cardActionsMenu} +
+ )} + + {desensitize(agent.name)} + + {agent.description && ( + + {desensitize(agent.description)} + + )} +
+ + {(agent.tags?.length ?? 0) > 0 && ( + + )} {hasSkillsOrTools && ( - - - - )} - - - {showActionRow && ( -
- {actionButtons} -
- )} - {chatButton} -
+ + + + )} + + +
+ {cardStatusBadge} + + {updatedAtText ? `更新于 ${updatedAtText}` : "最近更新"} + {publisherLabel ? ` · 由 ${publisherLabel} 更新` : isBuiltin ? " · 由系统更新" : ""} + +
+ {chatButton} +
); const listLayout = ( -
-
- -
+
+
-
- -
+
{editing && draft ? ( ("mine"); const [search, setSearch] = useState(""); - const [tagIds, setTagIds] = useState([]); const [sort, setSort] = useState("time_desc"); + const [statusFilter, setStatusFilter] = useState("all"); const { data: publishSquares } = usePublishSquares(); // Each configured publish square (发布广场) becomes its own browse tab; the // backend always returns at least one (the built-in default). @@ -53,10 +61,9 @@ export function AgentGallery() { // frontend's desensitize map, so typing the displayed abbreviation (e.g. // "TS") still matches the stored raw name (e.g. "态势分析"). Whenever the // filters change, every tab returns to its first page. - const tagKey = tagIds.join(","); useEffect(() => { setPages({}); - }, [search, tagKey, sort, publisher?.id]); + }, [search, sort, publisher?.id, statusFilter]); // If the active square tab disappears (admin removed that square), fall back // to "我的" so the gallery never points at a dead tab. @@ -108,7 +115,6 @@ export function AgentGallery() { } = useAgentsPaginated({ scope: mainScope, search, - tagIds, sort, page: mainPage, pageSize: PAGE_SIZE, @@ -126,7 +132,6 @@ export function AgentGallery() { error: privateError, } = usePrivateAgentsPaginated(privatePassword, { search, - tagIds, sort, page: pages.private ?? 1, pageSize: PAGE_SIZE, @@ -177,21 +182,22 @@ export function AgentGallery() { const handlePublisherClick = (p: { id: string; label: string }) => setPublisher(p); - const renderGrid = (list: Agent[]) => - viewMode === "list" ? ( -
- {list.map((agent) => ( - - ))} -
- ) : ( -
- {list.map((agent) => ( + const filterByStatus = (agent: Agent) => { + if (statusFilter === "published") return agent.published && !agent.is_private; + if (statusFilter === "draft") return !agent.published && !agent.is_private && !agent.builtin; + if (statusFilter === "private") return agent.is_private; + if (statusFilter === "builtin") return agent.builtin; + return true; + }; + + const renderGrid = (list: Agent[]) => { + const visibleAgents = list.filter(filterByStatus); + if (visibleAgents.length === 0 && list.length > 0) { + return renderEmpty("当前状态筛选下没有智能体。"); + } + return ( +
+ {visibleAgents.map((agent) => ( ); + }; /** Loading / empty / (grid + pagination) for one paginated tab. */ const renderListBody = (opts: { @@ -264,10 +271,8 @@ export function AgentGallery() { }; const renderEmpty = (description: string, showNewButton = false) => ( -
-
- -
+
+

{t.agents.emptyTitle}

@@ -276,8 +281,7 @@ export function AgentGallery() {

{showNewButton && (
@@ -384,43 +393,99 @@ export function AgentGallery() { value={activeTab} onValueChange={(v) => { setActiveTab(v); - // Each tab starts from a clean slate: switching tabs clears the - // keyword / tag / sort / publisher filters instead of carrying them - // over. The TagSearchBar's own input box is reset via its key below. + // Each tab starts from a clean slate instead of carrying filters over. setSearch(""); - setTagIds([]); setSort("time_desc"); + setStatusFilter("all"); setPublisher(null); }} className="flex min-h-0 flex-1 flex-col overflow-hidden" > -
-
- - 我的 +
+
+ + + 我的 + {squareTabs.map((sq) => ( - + {sq.name} ))} {privateFeatureAvailable && ( - + 私密广场 )} {isAdmin && ( - + 内置智能体 )} -
- - 共 {activeTotal} 个 - - + {showSearchBar && ( +
+
+ + setSearch(event.target.value)} + placeholder="搜索智能体名称或描述" + className="h-12 rounded-xl border-slate-200 bg-white pl-11 text-sm shadow-none placeholder:text-slate-400 focus-visible:border-blue-400 focus-visible:ring-blue-100 dark:border-slate-700 dark:bg-slate-900" + /> + {(activeTab === "private" ? isPrivateFetching : mainFetching) && ( + + )} +
+ + {activeTab === "private" && privatePassword && (
+
+ )}
- {showSearchBar && ( - setSort(v as AgentSort)}> - - - - - - 最新创建 - 最早创建 - 最近更新 - 最早更新 - 名称 A-Z - 名称 Z-A - - - } - /> - )} {publisher && ( -
+
筛选发布者:
)} +
+ 共 {activeTotal} 个智能体 +
{squareTabs.map((sq) => ( {renderListBody({ list: mainAgents, @@ -500,7 +536,7 @@ export function AgentGallery() { ))} - + {renderListBody({ list: mainAgents, total: mainTotal, @@ -513,7 +549,7 @@ export function AgentGallery() { {isAdmin && ( - + {renderListBody({ list: mainAgents, total: mainTotal, @@ -528,7 +564,7 @@ export function AgentGallery() { {privateFeatureAvailable && ( {renderPrivateTab()} diff --git a/frontend-web/src/components/workspace/cm-chat-sidebar.tsx b/frontend-web/src/components/workspace/cm-chat-sidebar.tsx new file mode 100644 index 00000000..6aa80501 --- /dev/null +++ b/frontend-web/src/components/workspace/cm-chat-sidebar.tsx @@ -0,0 +1,204 @@ +"use client"; + +import { + BookOpenCheck, + Brain, + CalendarClock, + MessageSquare, + MessageSquarePlus, + PanelLeftClose, + PenLine, + Settings, + Sparkles, + UsersRound, +} from "lucide-react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import type { MouseEvent } from "react"; +import { useNavigate } from "react-router-dom"; + +import { cn } from "@/lib/utils"; + +import { AgentBrandIcon } from "./agents/agent-brand-icon"; +import { RecentChatList } from "./recent-chat-list"; +import { WorkspaceNavMenu } from "./workspace-nav-menu"; + +const NEW_CHAT_PATH = "/page/workspace/chats/new"; + +type NavItem = { + label: string; + href: string; + icon: typeof MessageSquare; + match: string; +}; + +const NAV_ITEMS: NavItem[] = [ + { label: "新建对话", href: NEW_CHAT_PATH, icon: MessageSquarePlus, match: NEW_CHAT_PATH }, + { label: "对话", href: "/page/workspace/chats", icon: MessageSquare, match: "/page/workspace/chats" }, + { label: "智能体", href: "/page/workspace/agents", icon: Sparkles, match: "/page/workspace/agents" }, + { label: "技能", href: "/page/workspace/skills", icon: Sparkles, match: "/page/workspace/skills" }, + { label: "记忆", href: "/page/workspace/memory", icon: Brain, match: "/page/workspace/memory" }, + { label: "AI 写作", href: "/page/workspace/ai-writing", icon: PenLine, match: "/page/workspace/ai-writing" }, + { + label: "多智能体规划", + href: "/page/workspace/roundtable/planning", + icon: UsersRound, + match: "/page/workspace/roundtable", + }, + { + label: "定时任务", + href: "/page/workspace/scheduled/tasks", + icon: CalendarClock, + match: "/page/workspace/scheduled", + }, + { label: "问答梳理", href: "/page/workspace/knowledge", icon: BookOpenCheck, match: "/page/workspace/knowledge" }, +] as const; + +export function CmChatSidebar({ + onCollapse, + collapsed = false, +}: { + onCollapse?: () => void; + collapsed?: boolean; +}) { + const pathname = usePathname(); + const navigate = useNavigate(); + + const handleNewChat = (event: MouseEvent) => { + if (event.metaKey || event.ctrlKey || event.shiftKey || event.button === 1) return; + event.preventDefault(); + navigate(NEW_CHAT_PATH, { replace: true, state: { resetAt: Date.now() } }); + }; + + if (collapsed) { + return ( + + ); + } + + return ( +
+
+ + CM 助手 + CM 助手 + + +
+ + + +
+ +
+ +
+ +
+
+ ); +} diff --git a/frontend-web/src/components/workspace/compact-welcome.tsx b/frontend-web/src/components/workspace/compact-welcome.tsx index 9bd26eb3..1607c6f6 100644 --- a/frontend-web/src/components/workspace/compact-welcome.tsx +++ b/frontend-web/src/components/workspace/compact-welcome.tsx @@ -11,38 +11,15 @@ export function CompactWelcome({ className }: { className?: string }) { return (
- {/* CM logo:浅底圆环 + 主色渐变圆角方块 */} -
-
- - CM - -
-
- -

- 欢迎使用CM助手 +

+ 你好,欢迎使用 CM 助手

-

- 基于先进深度推理模型,为您提供高效的市场分析、专业报告撰写、调研设计与协同脑暴支持。 +

+ 我可以帮你写作、问答、分析和规划各类内容,让工作更高效

); diff --git a/frontend-web/src/components/workspace/custom-prompt-menu.tsx b/frontend-web/src/components/workspace/custom-prompt-menu.tsx index d6cd72b7..8d168b48 100644 --- a/frontend-web/src/components/workspace/custom-prompt-menu.tsx +++ b/frontend-web/src/components/workspace/custom-prompt-menu.tsx @@ -46,10 +46,12 @@ export function CustomPromptBar({ onInsert, className, currentAgentId, + compact = false, }: { onInsert: (content: string) => void; className?: string; currentAgentId?: string | null; + compact?: boolean; }) { const allPrompts = useCustomPrompts(); const prompts = allPrompts.filter( @@ -81,7 +83,13 @@ export function CustomPromptBar({ return ( <> -
+
{prompts.length > 0 && ( <> {overflowing && ( @@ -111,6 +119,7 @@ export function CustomPromptBar({ title={prompt.content} className={cn( "border-border/60 bg-muted/40 text-foreground/80 hover:bg-muted hover:text-foreground hover:border-border max-w-[200px] shrink-0 truncate rounded-full border px-3 py-1 text-xs font-medium shadow-sm transition-colors", + compact && "border-slate-200 bg-white px-3.5 py-1.5 text-[12px] text-slate-600 shadow-none hover:border-blue-200 hover:bg-blue-50/60 hover:text-blue-700", "dark:border-slate-600/70 dark:bg-slate-700/40 dark:text-slate-200 dark:hover:bg-slate-700", )} > @@ -139,7 +148,11 @@ export function CustomPromptBar({ type="button" variant="ghost" size="icon" - className="text-muted-foreground hover:text-foreground hover:bg-muted/70 h-7 w-7 shrink-0 rounded-full" + className={cn( + "text-muted-foreground hover:text-foreground hover:bg-muted/70 h-7 w-7 shrink-0 rounded-full", + compact && + "h-9 w-9 rounded-xl border border-slate-200 bg-white text-slate-600 shadow-[0_6px_18px_-14px_rgba(15,23,42,0.4)] hover:border-blue-200 hover:bg-blue-50 hover:text-blue-700 dark:border-slate-700 dark:bg-slate-900", + )} title="管理常用提示词" aria-label="管理常用提示词" onClick={() => setManageOpen(true)} diff --git a/frontend-web/src/components/workspace/input-box.tsx b/frontend-web/src/components/workspace/input-box.tsx index 2bed313f..e63eea63 100644 --- a/frontend-web/src/components/workspace/input-box.tsx +++ b/frontend-web/src/components/workspace/input-box.tsx @@ -1,16 +1,20 @@ "use client"; import type { ChatStatus } from "ai"; -import { - BrainCircuitIcon, - BookMarkedIcon, - CheckIcon, - FileTextIcon, - GraduationCapIcon, - LightbulbIcon, - PaperclipIcon, - PlusIcon, - ShrinkIcon, +import { + BarChart3Icon, + BrainCircuitIcon, + BookMarkedIcon, + CheckIcon, + ClipboardCheckIcon, + FileTextIcon, + GraduationCapIcon, + LightbulbIcon, + MessageCircleQuestionIcon, + PaperclipIcon, + PlusIcon, + RefreshCwIcon, + ShrinkIcon, SparklesIcon, RocketIcon, XIcon, @@ -102,7 +106,58 @@ import { useThread } from "./messages/context"; import { ModeHoverGuide } from "./mode-hover-guide"; import { Tooltip } from "./tooltip"; -type InputMode = "flash" | "thinking" | "pro" | "ultra"; +type InputMode = "flash" | "thinking" | "pro" | "ultra"; + +const COMPACT_QUICK_STARTS = [ + { + id: "report", + title: "撰写报告", + content: "请帮我撰写一份结构清晰的专业报告,先向我确认主题、受众和篇幅要求。", + icon: FileTextIcon, + }, + { + id: "knowledge", + title: "知识问答", + content: "请基于已有资料回答我的问题,并明确区分已知事实、推断和需要补充的信息。", + icon: MessageCircleQuestionIcon, + }, + { + id: "planning", + title: "任务规划", + content: "请把我的目标拆解为可执行的任务计划,列出优先级、里程碑和风险。", + icon: ClipboardCheckIcon, + }, + { + id: "analysis", + title: "数据分析", + content: "请帮我分析数据中的关键趋势、异常和可能原因,并给出下一步验证建议。", + icon: BarChart3Icon, + }, + { + id: "solution", + title: "方案策划", + content: "请围绕我的目标设计一套完整方案,包含背景、目标、实施步骤、资源需求和风险预案。", + icon: LightbulbIcon, + }, + { + id: "summary", + title: "内容总结", + content: "请提炼我提供内容的核心观点、关键结论和待办事项,并按重要程度整理。", + icon: BookMarkedIcon, + }, + { + id: "research", + title: "调研提纲", + content: "请根据我的调研主题生成一份结构完整的调研提纲,覆盖对象、问题、方法和输出形式。", + icon: GraduationCapIcon, + }, + { + id: "polish", + title: "文案润色", + content: "请在不改变原意的前提下润色我提供的内容,使表达更清晰、专业、简洁。", + icon: SparklesIcon, + }, +] as const; function getResolvedMode( @@ -210,17 +265,41 @@ export function InputBox({ const lastGeneratedForAiIdRef = useRef(null); const wasStreamingRef = useRef(false); - const [confirmOpen, setConfirmOpen] = useState(false); + const [confirmOpen, setConfirmOpen] = useState(false); + const [compactPromptOrder, setCompactPromptOrder] = useState(0); const [pendingSuggestion, setPendingSuggestion] = useState( null, ); const { questions: recommendedQuestions } = useRecommendedQuestions(); const userPreferences = useUserPreferences(); - const showAdminRecommendedQuestions = + const showAdminRecommendedQuestions = !hideAdminRecommendedQuestions && !userPreferences.hide_admin_prompt_prefix && - recommendedQuestions.length > 0; + recommendedQuestions.length > 0; + const compactQuickStarts = useMemo(() => { + if (!hideAdminRecommendedQuestions && recommendedQuestions.length > 0) { + return recommendedQuestions.map((configured, index) => { + const fallback = + COMPACT_QUICK_STARTS[index % COMPACT_QUICK_STARTS.length]!; + return { + ...fallback, + id: configured.id, + title: desensitize(configured.title) || `提示词 ${index + 1}`, + content: configured.content || fallback.content, + }; + }); + } + return [...COMPACT_QUICK_STARTS]; + }, [hideAdminRecommendedQuestions, recommendedQuestions]); + const orderedCompactQuickStarts = useMemo(() => { + if (compactQuickStarts.length < 2) return compactQuickStarts; + const offset = compactPromptOrder % compactQuickStarts.length; + return [ + ...compactQuickStarts.slice(offset), + ...compactQuickStarts.slice(0, offset), + ]; + }, [compactPromptOrder, compactQuickStarts]); useEffect(() => { if (models.length === 0) { @@ -252,10 +331,11 @@ export function InputBox({ const resolvedModelName = selectedModel?.name; - const supportThinking = useMemo( - () => selectedModel?.supports_thinking ?? false, - [selectedModel], - ); + const supportThinking = useMemo( + () => selectedModel?.supports_thinking ?? false, + [selectedModel], + ); + const effectiveMode = getResolvedMode(context.mode, supportThinking); const supportReasoningEffort = useMemo( () => selectedModel?.supports_reasoning_effort ?? false, @@ -529,14 +609,68 @@ export function InputBox({ }, [context.model_name, disabled, isMock, status, thread.messages, threadId]); return ( -
+
{extraHeader && (
{extraHeader}
)} - {isNewThread && searchParams.get("mode") !== "skill" && showAdminRecommendedQuestions && ( + {isNewThread && searchParams.get("mode") !== "skill" && compact && ( +
+
+ + +
+
+ {orderedCompactQuickStarts.map((item) => { + return ( + + ); + })} +
+
+ )} + + {isNewThread && searchParams.get("mode") !== "skill" && !compact && showAdminRecommendedQuestions && (
{recommendedQuestions.map((q) => ( )} - ); + ); + + const iconStyle = skill.name.includes("web") || skill.name.includes("search") + ? { + Icon: GlobeIcon, + className: "bg-emerald-600 text-white shadow-[0_6px_16px_rgba(22,163,74,0.22)]", + } + : skill.name.includes("doc") || skill.name.includes("file") + ? { + Icon: FileArchive, + className: "bg-violet-600 text-white shadow-[0_6px_16px_rgba(124,58,237,0.22)]", + } + : skill.source === "agent" + ? { + Icon: WorkflowIcon, + className: "bg-[#2166e8] text-white shadow-[0_6px_16px_rgba(37,99,235,0.22)]", + } + : { + Icon: Upload, + className: "bg-amber-500 text-white shadow-[0_6px_16px_rgba(245,158,11,0.22)]", + }; + const SkillIcon = iconStyle.Icon; + const activityAt = formatDateTime(skill.last_activity_at ?? skill.created_at); + const actionArea = ( +
event.stopPropagation()}> + {actionButtons} +
+ ); return ( <> - {isCard ? ( -
-
setDetailOpen(true)} - > - {badgeGroup} -

- {desensitize(skill.description ?? "")} -

- {metaNodes} -
-
- {actionButtons} -
-
- ) : ( - - setDetailOpen(true)} - > - {badgeGroup} - - {desensitize(skill.description ?? "")} - - {metaNodes} - - {actionButtons} - - )} + {isCard ? ( +
+
setDetailOpen(true)} + > +
+
+ +
+
+ {badgeGroup} +

+ {desensitize(skill.description ?? "")} +

+
+
+
+ + + {skill.enabled ? "已启用" : "未启用"} + + {skill.use_count ? `调用 ${skill.use_count} 次` : "暂无调用"} + {activityAt && 更新于 {activityAt}} +
+ {(skill.tags?.length ?? 0) > 0 && } +
+
+ {actionArea} +
+
+ ) : ( +
+
+
setDetailOpen(true)}> +
+ +
+
+ {badgeGroup} +

+ {desensitize(skill.description ?? "")} +

+ {!isBuiltin && skill.owner_username && ( + + )} +
+
+
+ {(skill.tags?.length ?? 0) > 0 ? ( + + ) : ( + 未设置标签 + )} +
+
+ 使用次数 + {skill.use_count ? `${skill.use_count} 次` : "暂无调用"} +
+
+ + + {skill.enabled ? "已启用" : "未启用"} + + {activityAt &&

更新于 {activityAt}

} +
+
+ {actionArea} +
+
+
+ )} diff --git a/frontend-web/src/components/workspace/skill-display-sources.tsx b/frontend-web/src/components/workspace/skill-display-sources.tsx index 3993f022..731d171a 100644 --- a/frontend-web/src/components/workspace/skill-display-sources.tsx +++ b/frontend-web/src/components/workspace/skill-display-sources.tsx @@ -53,6 +53,7 @@ export function SourceCard({ referenceTarget?: boolean; }) { const [open, setOpen] = useState(false); + const [showOriginal, setShowOriginal] = useState(false); const [context, setContext] = useState(null); const [contextLoading, setContextLoading] = useState(false); const [contextError, setContextError] = useState(null); @@ -81,7 +82,7 @@ export function SourceCard({ const previewTarget = parseWeKnoraPreviewUrl(source.url); useEffect(() => { - if (!open || !previewTarget) { + if (!open || !showOriginal || !previewTarget) { return; } let cancelled = false; @@ -115,7 +116,7 @@ export function SourceCard({ return () => { cancelled = true; }; - }, [open, previewTarget?.chunkId, previewTarget?.knowledgeBaseId]); + }, [open, showOriginal, previewTarget?.chunkId, previewTarget?.knowledgeBaseId]); return ( <> @@ -128,7 +129,10 @@ export function SourceCard({ highlighted && "border-amber-300 bg-amber-50/80 shadow-[0_0_0_2px_rgba(251,191,36,0.14)]", )} - onClick={() => setOpen(true)} + onClick={() => { + setShowOriginal(false); + setOpen(true); + }} >
{showReferenceNumber && ( @@ -168,7 +172,15 @@ export function SourceCard({ )}
- + { + setOpen(nextOpen); + if (!nextOpen) { + setShowOriginal(false); + } + }} + >
@@ -205,7 +217,27 @@ export function SourceCard({ )}
- {previewTarget ? ( + {previewTarget && ( +
+
+ + {showOriginal + ? "正在查看命中文段及其前后上下文" + : "当前为检索命中的片段,可继续查看原文上下文"} + + +
+
+ )} + {previewTarget && showOriginal ? ( ; }; +export function isWeKnoraSourcePreviewUrl(url?: string): boolean { + return parseWeKnoraPreviewUrl(url) !== null; +} + function parseWeKnoraPreviewUrl(url?: string): WeKnoraPreviewTarget | null { - if (!url?.startsWith("/weknora-source-preview?")) { + const normalizedUrl = url + ?.trim() + .replace(/^[\u200B-\u200D\uFEFF]+/, ""); + const marker = "/weknora-source-preview?"; + const start = normalizedUrl?.indexOf(marker) ?? -1; + if (start < 0) { return null; } - const queryIndex = url.indexOf("?"); - const params = new URLSearchParams(url.slice(queryIndex + 1)); + // Some MCP tool results wrap the relative pointer in formatting characters + // or append a newline. Keep accepting that safe internal path, but never + // treat a normal external URL as an original-document preview. + const previewUrl = normalizedUrl! + .slice(start) + .split(/[\r\n\s]/, 1)[0]; + const queryIndex = previewUrl.indexOf("?"); + const params = new URLSearchParams(previewUrl.slice(queryIndex + 1)); const knowledgeBaseId = params.get("knowledge_base_id")?.trim(); const chunkId = params.get("chunk_id")?.trim(); return knowledgeBaseId && chunkId ? { knowledgeBaseId, chunkId } : null; diff --git a/frontend-web/src/components/workspace/user-identity-badge.tsx b/frontend-web/src/components/workspace/user-identity-badge.tsx index 9e007a37..a3101b56 100644 --- a/frontend-web/src/components/workspace/user-identity-badge.tsx +++ b/frontend-web/src/components/workspace/user-identity-badge.tsx @@ -1,6 +1,6 @@ "use client"; -import { User } from "lucide-react"; +import { UserRound } from "lucide-react"; import { useLiveAccount } from "@/core/auth/use-live-account"; import { useMyPosition } from "@/core/positions"; @@ -15,7 +15,13 @@ import { cn } from "@/lib/utils"; * 配色按当前内容区前景色(foreground / muted-foreground),适配聊天页浅底顶栏, * 而非 Header 的白字贴 banner 方案。 */ -export function UserIdentityBadge({ className }: { className?: string }) { +export function UserIdentityBadge({ + className, + variant = "plain", +}: { + className?: string; + variant?: "plain" | "topbar"; +}) { // userInfo belongs to external-app route parameters and can legitimately // outlive a login switch. The visible account name must instead match the // settings page's current auth identity. @@ -26,13 +32,33 @@ export function UserIdentityBadge({ className }: { className?: string }) { return (
-
- +
+ + {variant === "topbar" && ( + + )}
-
+

{displayName}

diff --git a/frontend-web/src/components/workspace/workspace-container.tsx b/frontend-web/src/components/workspace/workspace-container.tsx index 329449b4..6ca12e0c 100644 --- a/frontend-web/src/components/workspace/workspace-container.tsx +++ b/frontend-web/src/components/workspace/workspace-container.tsx @@ -12,8 +12,10 @@ import { BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumb"; -import { useI18n } from "@/core/i18n/hooks"; -import { cn } from "@/lib/utils"; +import { useI18n } from "@/core/i18n/hooks"; +import { cn } from "@/lib/utils"; +import { ThemeSwitcher } from "@/components/workspace/appearance-controls"; +import { UserIdentityBadge } from "@/components/workspace/user-identity-badge"; export function WorkspaceContainer({ className, @@ -93,9 +95,15 @@ export function WorkspaceHeader({
- {rightActions ? ( -
{rightActions}
- ) : null} +
+ {rightActions} + + ); } @@ -123,7 +131,8 @@ function nameOfSegment( t: ReturnType["t"], ) { if (!segment) return t.common.home; - if (segment === "workspace") return t.breadcrumb.workspace; - if (segment === "chats") return t.breadcrumb.chats; - return segment[0]?.toUpperCase() + segment.slice(1); -} + if (segment === "workspace") return t.breadcrumb.workspace; + if (segment === "chats") return t.breadcrumb.chats; + if (segment === "agents") return t.agents.title; + return segment[0]?.toUpperCase() + segment.slice(1); +} diff --git a/frontend-web/src/components/workspace/workspace-sidebar.tsx b/frontend-web/src/components/workspace/workspace-sidebar.tsx index 39206d90..b8db3b9d 100644 --- a/frontend-web/src/components/workspace/workspace-sidebar.tsx +++ b/frontend-web/src/components/workspace/workspace-sidebar.tsx @@ -1,118 +1,44 @@ -"use client"; - -import { - Sidebar, - SidebarHeader, - SidebarContent, - SidebarFooter, - SidebarRail, - useSidebar, -} from "@/components/ui/sidebar"; -import { useTheme } from "@/contexts/ThemeContext"; -import { cn } from "@/lib/utils"; -import { usePathname } from "next/navigation"; - -import { GeneralQaSidebar } from "./general-qa-sidebar"; -import { RecentChatList } from "./recent-chat-list"; -import { RecentRoundtableList } from "./recent-roundtable-list"; -import { RecentWritingList } from "./recent-writing-list"; -import { WorkspaceHeader } from "./workspace-header"; -import { WorkspaceNavChatList } from "./workspace-nav-chat-list"; -import { WorkspaceNavMenu } from "./workspace-nav-menu"; - -export function WorkspaceSidebar({ - compactChrome = false, - className, - ...props -}: React.ComponentProps & { compactChrome?: boolean }) { - const { open: isSidebarOpen } = useSidebar(); - const { theme } = useTheme(); - const pathname = usePathname(); - const useWhiteSidebar = compactChrome && theme === "light"; - // 通用问答(全量模式)左栏:新建回答 → 常用智能体 → 聊天记录(需求 0616 #1)。 - // 仅在通用问答(/page/workspace/chats*)下启用三块式布局;点击常用智能体后是 - // 「原地嵌入」(停留在 /chats/new?agent=...,仅右侧问答区换成智能体对话),左侧栏不变。 - const isGeneralQa = pathname.startsWith("/page/workspace/chats"); - - // 紧凑模式:用「外层定宽容器 + transition-[width]」做展开/收起过渡动画。 - // collapsible="none" 的 Sidebar 是普通 in-flow 定宽块(非 absolute,故不受本布局对 - // sidebar-container 的 absolute/left-0 覆盖影响),外层 overflow-hidden 把它从 - // --sidebar-width 裁到 0、聊天区随宽度平滑回流;收起后整列不接收指针。 - if (compactChrome) { - return ( -
- - - {isGeneralQa ? ( - // 全量版通用问答(/page/workspace/chats*):新建对话侧栏独立成 - // GeneralQaSidebar(按 0616 样式参考图重画),与其余 workspace 页解耦。 - - ) : ( - <> - - - - )} - - - -
- ); - } - - return ( - <> - - {!compactChrome && ( - - - - )} - - - {isSidebarOpen && } - {/* 「多智能体会商」「最近写作」与「智能体/技能」等菜单项保持一致的显隐: - 仅在非紧凑模式(!compactChrome)下展示,紧凑模式(有外层页面侧栏时)一并隐藏。 */} - {isSidebarOpen && !compactChrome && } - {isSidebarOpen && !compactChrome && } - - {!compactChrome && ( - - - - )} - - - - ); -} +"use client"; + +import { + Sidebar, + SidebarContent, + useSidebar, +} from "@/components/ui/sidebar"; +import { cn } from "@/lib/utils"; + +import { CmChatSidebar } from "./cm-chat-sidebar"; + +export function WorkspaceSidebar({ + className, + compactChrome: _compactChrome, + ...props +}: React.ComponentProps & { compactChrome?: boolean }) { + const { open: isSidebarOpen, toggleSidebar } = useSidebar(); + + return ( +
+ + + + + +
+ ); +} diff --git a/frontend-web/src/pages/AgentsPage.tsx b/frontend-web/src/pages/AgentsPage.tsx index 412dfc02..bfc436ac 100644 --- a/frontend-web/src/pages/AgentsPage.tsx +++ b/frontend-web/src/pages/AgentsPage.tsx @@ -1,5 +1,15 @@ -import { AgentGallery } from "@/components/workspace/agents/agent-gallery"; - -export default function AgentsPage() { - return ; -} +import { AgentGallery } from "@/components/workspace/agents/agent-gallery"; +import { + WorkspaceBody, + WorkspaceContainer, +} from "@/components/workspace/workspace-container"; + +export default function AgentsPage() { + return ( + + + + + + ); +} diff --git a/frontend-web/src/pages/ChatPage.tsx b/frontend-web/src/pages/ChatPage.tsx index 8ee81291..b6271b65 100644 --- a/frontend-web/src/pages/ChatPage.tsx +++ b/frontend-web/src/pages/ChatPage.tsx @@ -35,7 +35,6 @@ import { TokenUsageIndicator } from "@/components/workspace/token-usage-indicato import { UserIdentityBadge } from "@/components/workspace/user-identity-badge"; import { Welcome } from "@/components/workspace/welcome"; import { CompactWelcome } from "@/components/workspace/compact-welcome"; -import { readSidebarEnabled } from "@/core/page-layout/sidebar-enabled"; import { isChatsIframeWorkspacePath } from "@/core/page-layout/chats-iframe-route"; import { useSidebar } from "@/components/ui/sidebar"; import { Button } from "@/components/ui/button"; @@ -51,7 +50,6 @@ import { useThreadSettings, } from "@/core/settings"; import { useThreadStream } from "@/core/threads/hooks"; -import { useLayoutMode } from "@/core/system-settings/hooks"; import { useRouteBase } from "@/core/route-base"; import { textOfMessage } from "@/core/threads/utils"; import { OptType, writeYLog } from "@/core/log/y-log"; @@ -103,8 +101,6 @@ export default function ChatPage() { const { open: historySidebarOpen, toggleSidebar: toggleHistorySidebar } = useSidebar(); const { t } = useI18n(); - // 有效界面模式:用户自选 > 管理员系统默认 > (下方再回退会话级 sidebarEnabled) - const [effectiveLayoutMode] = useLayoutMode(); const [showFollowups, setShowFollowups] = useState(false); const [hasStartedConversation, setHasStartedConversation] = useState(false); const { threadId, setThreadId, isNewThread, setIsNewThread, isMock } = @@ -444,20 +440,8 @@ export default function ChatPage() { MESSAGE_LIST_FOLLOWUPS_EXTRA_PADDING_BOTTOM : undefined; const isLandingState = isNewThread && !hasStartedConversation; - // 紧凑模式(正常 App,有外层页面侧栏)落地态:用 CM 参考图样式重画; - // 判定与 PageRoutes 的 compactWorkspaceSidebar(pageSidebarAllowed) 保持一致 —— - // 优先看外观设置的「全量/简洁」,未设置过才回退到会话级 sidebarEnabled。否则用户显式 - // 选「全量」时 PageRoutes 已挂出顶栏+侧栏,这里却还按旧 sidebarEnabled 判定,导致深蓝 - // 主题底图(workspace-chat-bg)不生效。非紧凑(嵌入/standalone)保持原样。 - const layoutMode = effectiveLayoutMode; - const chromeByPreference = - layoutMode === "full" - ? true - : layoutMode === "compact" - ? false - : readSidebarEnabled(); - const compactChrome = - chromeByPreference && !isChatsIframeWorkspacePath(pathname); + // 正常工作区统一使用新版侧栏和落地页;iframe 专用路由继续沿用嵌入布局。 + const compactChrome = !isChatsIframeWorkspacePath(pathname); const compactLanding = isLandingState && compactChrome; return ( @@ -470,16 +454,12 @@ export default function ChatPage() { compactChrome && "workspace-chat-bg", )} > -
+ {(!isLandingState || compactChrome) && ( +
- + {!isLandingState && ( + + )}
{/* 仅在收起态显示「展开历史」钮;展开后由侧栏内(新对话右侧)的收起钮负责收起。 */} @@ -507,7 +487,16 @@ export default function ChatPage() { 与全量模式 Header 右上角保持一致。全量模式已有顶栏则不重复展示。 */} {/* 简洁模式无全局顶栏:把换肤 + 界面模式切换搬到页面级顶栏右上角, 与全量模式 Header 右上角保持一致(content 配色贴浅底顶栏)。 */} - {!compactChrome && ( + {compactChrome ? ( + <> + +
+ + + ) : ( <> @@ -521,6 +510,7 @@ export default function ChatPage() { )}
+ )}
{/* @@ -556,7 +546,7 @@ export default function ChatPage() { "pointer-events-none absolute z-30 flex justify-center px-4", // 紧凑落地态:整块垂直+水平居中于对话区 compactLanding - ? "inset-0 items-center" + ? "inset-0 items-start overflow-y-auto pt-[clamp(82px,16vh,146px)] pb-12" : "right-0 bottom-0 left-0", )} > @@ -568,7 +558,7 @@ export default function ChatPage() { !compactLanding && "-translate-y-[calc(50vh-96px)]", compactLanding - ? "max-w-(--container-width-xl)" + ? "max-w-[1024px]" : isLandingState ? "max-w-(--container-width-md)" : "max-w-(--container-width-lg)", @@ -587,7 +577,7 @@ export default function ChatPage() {
{isLandingState && mounted && (compactLanding ? ( - + ) : ( ))} {mounted ? ( -
+
-
) : null diff --git a/frontend-web/src/pages/PageRoutes.tsx b/frontend-web/src/pages/PageRoutes.tsx index 208f82bc..5bfd2c96 100644 --- a/frontend-web/src/pages/PageRoutes.tsx +++ b/frontend-web/src/pages/PageRoutes.tsx @@ -61,8 +61,10 @@ function PageLayout() { // (界面模式非全量、或会话级 sidebar 未置位),导致整页没有顶栏/侧栏。故对该路由**强制全局 chrome** // (仍尊重 embed=1 嵌入态:外部 iframe 套用时仍隐藏)。 const forceChromeForDashboardAgent = location.pathname.includes("/strategy/dashboard-agent"); + const isWorkspacePage = location.pathname.startsWith("/page/workspace"); const pageSidebarAllowed = (forceChromeForDashboardAgent || chromeByPreference) && + !isWorkspacePage && !isChatsIframeWorkspacePath(location.pathname) && !embedActive && // `?hideChrome=1` 显式去 chrome:优先级高于上面对 dashboard-agent 的强制全局 chrome。 @@ -114,7 +116,7 @@ function PageLayout() { pageSidebarCollapsed={collapsed} pageSidebarHidden={!pageSidebarAllowed} headerHidden={!pageSidebarAllowed} - compactWorkspaceSidebar={pageSidebarAllowed} + compactWorkspaceSidebar={pageSidebarAllowed || isWorkspacePage} pageSidebarDark={isDark} /> } diff --git a/frontend-web/src/pages/WorkspaceRoutes.tsx b/frontend-web/src/pages/WorkspaceRoutes.tsx index 96a989dd..be415952 100644 --- a/frontend-web/src/pages/WorkspaceRoutes.tsx +++ b/frontend-web/src/pages/WorkspaceRoutes.tsx @@ -11,7 +11,6 @@ import { ThemeSwitcher, } from '@/components/workspace/appearance-controls'; import { WorkspaceSidebar } from '@/components/workspace/workspace-sidebar'; -import { UserIdentityBadge } from '@/components/workspace/user-identity-badge'; import { CommandPalette } from '@/components/workspace/command-palette'; import { Toaster } from 'sonner'; import { SubtasksProvider } from '@/core/tasks/context'; @@ -79,28 +78,6 @@ function isInIframe(): boolean { } } -function shouldShowCompactTopActions(pathname: string): boolean { - return ( - pathname === '/page/workspace/agents' || - pathname === '/page/workspace/skills' || - pathname === '/page/workspace/memory' || - pathname === '/page/workspace/knowledge' || - pathname.startsWith('/page/workspace/knowledge/') - ); -} - -function CompactWorkspaceTopActions() { - return ( -
- - - -
- -
- ); -} - function CompactAppearanceActions() { return ( <> @@ -137,11 +114,6 @@ export function WorkspaceLayout({ }) { // 全量模式下通用问答(/page/workspace/chats*)的历史侧栏默认展开;其余紧凑页 // (如智能体对话,为聊天区更宽而默认收起)维持原默认。 - const pathname = useLocation().pathname; - const isGeneralQa = pathname.startsWith('/page/workspace/chats'); - const showCompactTopActions = - headerHidden && shouldShowCompactTopActions(pathname); - const pageSidebarWidth = pageSidebarHidden ? '0rem' : pageSidebarDark @@ -164,12 +136,13 @@ export function WorkspaceLayout({ { '--page-sidebar-width': pageSidebarWidth, '--header-height': headerHidden ? '0rem' : '4rem', + '--sidebar-width': '18.5rem', } as CSSProperties } key={String(compactWorkspaceSidebar)} data-page-sidebar-collapsed={pageSidebarCollapsed} defaultOpen={ - defaultHistorySidebarOpen ?? (isGeneralQa || !compactWorkspaceSidebar) + defaultHistorySidebarOpen ?? true } > {!workspaceSidebarHidden && ( @@ -178,14 +151,7 @@ export function WorkspaceLayout({ - {showCompactTopActions ? ( - <> - -
{children}
- - ) : ( - children - )} +
{children}
diff --git a/frontend-web/src/styles/globals.css b/frontend-web/src/styles/globals.css index 097e616c..ce84a433 100644 --- a/frontend-web/src/styles/globals.css +++ b/frontend-web/src/styles/globals.css @@ -1290,9 +1290,9 @@ html.lightblue .tiptap-writing-editor .ProseMirror ::selection { * agent surfaces on classic rgba()/hex colours instead. * -------------------------------------------------------------------------- */ .agent-avatar { - background-color: #feeded; - color: #cf1322; - box-shadow: inset 0 0 0 1px rgba(190, 35, 35, 0.15); + background-color: #edf4ff; + color: #1f5fdb; + box-shadow: inset 0 0 0 1px rgba(59, 130, 246, 0.16); } .dark .agent-avatar { diff --git a/offline-backend-20260512/backend/CLAUDE.md b/offline-backend-20260512/backend/CLAUDE.md index f3d11696..6ffbe232 100644 --- a/offline-backend-20260512/backend/CLAUDE.md +++ b/offline-backend-20260512/backend/CLAUDE.md @@ -846,7 +846,7 @@ Both can be modified at runtime via Gateway API endpoints or `DeerFlowClient` me ### WeKnora Retrieval Skill -`skills/custom/weknora-knowledge-search/` is a read-only integration for a WeKnora tenant. The Skill itself only directs the model to two MCP tools, `weknora_search` and `weknora_list_knowledge_bases`; the credential remains inside the stdio MCP subprocess (`scripts/weknora_retrieval_mcp.py`). Its retrieval core groups document/FAQ knowledge bases by `embedding_model_id` and calls WeKnora's multi-KB `hybrid-search` API once per group, preventing invalid cross-embedding score comparisons. It does not discard a KB merely because the legacy list API reports `chunk_count=0`; hybrid-search decides its actual readiness. Wiki pages are opt-in and use their dedicated Wiki search route. In explicit DeerFlow sharing mode, `WEKNORA_DEERFLOW_SHARE_TOKEN_FILE` selects WeKnora's narrow `/api/v1/deerflow/*` capability API: only owner-published document/FAQ knowledge bases are discoverable and searchable, while Wiki is skipped so the skill cannot fall back to a user bearer-token endpoint. Shared document hits include a credential-free source-preview URL. The frontend source card recognizes it and opens a context dialog; its call to authenticated Gateway route `/api/weknora/source-context` is proxied to WeKnora's token-gated `/api/v1/deerflow/knowledge-bases/:id/chunks/:chunk_id/context`, returning only the matching chunk and two neighbours on either side. Never expose the share token to the browser or add it to a URL. Configure through the bundled `templates/extensions_config.fragment.json`; otherwise prefer `WEKNORA_API_KEY_FILE` for scoped-key deployments and `WEKNORA_BEARER_TOKEN_FILE` for legacy deployments. `WEKNORA_ALLOWED_KB_IDS` remains an optional allow-list. Tests: `tests/test_weknora_knowledge_search_skill.py`, `tests/test_weknora_source_preview.py`. +`skills/custom/weknora-knowledge-search/` is a read-only integration for a WeKnora tenant. The Skill itself only directs the model to two MCP tools, `weknora_search` and `weknora_list_knowledge_bases`; the credential remains inside the stdio MCP subprocess (`scripts/weknora_retrieval_mcp.py`). Its retrieval core groups document/FAQ knowledge bases by `embedding_model_id` and calls WeKnora's multi-KB `hybrid-search` API once per group, preventing invalid cross-embedding score comparisons. It does not discard a KB merely because the legacy list API reports `chunk_count=0`; hybrid-search decides its actual readiness. Wiki pages are opt-in and use their dedicated Wiki search route. In explicit DeerFlow sharing mode, `WEKNORA_DEERFLOW_SHARE_TOKEN_FILE` selects WeKnora's narrow `/api/v1/deerflow/*` capability API: only owner-published document/FAQ knowledge bases are discoverable and searchable, while Wiki is skipped so the skill cannot fall back to a user bearer-token endpoint. Shared document hits include a credential-free source-preview URL. Both the configured Skill source-card path and the generic MCP-result fallback recognize this URL; the source-information dialog never renders it as text and instead exposes a “查看原文” button. The button makes the authenticated Gateway call to `/api/weknora/source-context`, which then proxies to WeKnora's token-gated `/api/v1/deerflow/knowledge-bases/:id/chunks/:chunk_id/context` and returns only the matching chunk and two neighbours on either side. Never expose the share token to the browser or add it to a URL. Configure through the bundled `templates/extensions_config.fragment.json`; otherwise prefer `WEKNORA_API_KEY_FILE` for scoped-key deployments and `WEKNORA_BEARER_TOKEN_FILE` for legacy deployments. `WEKNORA_ALLOWED_KB_IDS` remains an optional allow-list. Tests: `tests/test_weknora_knowledge_search_skill.py`, `tests/test_weknora_source_preview.py`. ### Embedded Client (`packages/harness/deerflow/client.py`) diff --git a/offline-backend-20260512/backend/README.md b/offline-backend-20260512/backend/README.md index 7e64f109..c00d3fa9 100644 --- a/offline-backend-20260512/backend/README.md +++ b/offline-backend-20260512/backend/README.md @@ -78,7 +78,7 @@ Per-thread isolated execution with virtual path translation: - **Virtual paths**: `/mnt/user-data/{workspace,uploads,outputs}` → thread-specific physical directories - **Skills path**: `/mnt/skills` → `deer-flow/skills/` directory - **Skills loading**: Recursively discovers nested `SKILL.md` files under `skills/{public,custom}` and preserves nested container paths -- **WeKnora retrieval skill**: `skills/custom/weknora-knowledge-search` pairs a concise Skill with a stdio MCP server. It retrieves tenant-scoped WeKnora document/FAQ passages through the public API, groups multi-KB calls by embedding model, optionally searches Wiki pages, and can use the local reranker. It does not reject a KB solely because the legacy list API reports `chunk_count=0`; hybrid-search remains the source of truth. For WeKnora's explicit DeerFlow sharing mode, configure `WEKNORA_DEERFLOW_SHARE_TOKEN_FILE`: the MCP uses the restricted `/api/v1/deerflow/*` capability API and can only list/search knowledge bases whose owner enabled sharing; it never receives a user bearer token. Shared document results now contain a credential-free `url` pointer; the source card's “查看原文” dialog calls DeerFlow's authenticated `/api/weknora/source-context` proxy, which retrieves the hit plus two adjacent chunks while keeping the share token server-side. Shared mode currently excludes Wiki pages. Otherwise use `WEKNORA_API_KEY_FILE` for scoped-key deployments or `WEKNORA_BEARER_TOKEN_FILE` for legacy deployments; never put any credential in `SKILL.md` or a chat prompt. +- **WeKnora retrieval skill**: `skills/custom/weknora-knowledge-search` pairs a concise Skill with a stdio MCP server. It retrieves tenant-scoped WeKnora document/FAQ passages through the public API, groups multi-KB calls by embedding model, optionally searches Wiki pages, and can use the local reranker. It does not reject a KB solely because the legacy list API reports `chunk_count=0`; hybrid-search remains the source of truth. For WeKnora's explicit DeerFlow sharing mode, configure `WEKNORA_DEERFLOW_SHARE_TOKEN_FILE`: the MCP uses the restricted `/api/v1/deerflow/*` capability API and can only list/search knowledge bases whose owner enabled sharing; it never receives a user bearer token. Shared document results contain a credential-free `url` pointer, which is never rendered as text: both configured Skill source cards and the generic MCP-result fallback recognize it and provide a “查看原文” button. Selecting it calls DeerFlow's authenticated `/api/weknora/source-context` proxy and displays the hit plus two adjacent chunks while keeping the share token server-side. Shared mode currently excludes Wiki pages. Otherwise use `WEKNORA_API_KEY_FILE` for scoped-key deployments or `WEKNORA_BEARER_TOKEN_FILE` for legacy deployments; never put any credential in `SKILL.md` or a chat prompt. - **File-write safety**: `str_replace` serializes read-modify-write per `(sandbox.id, path)` so isolated sandboxes keep concurrency even when virtual paths match - **Tools**: `bash`, `ls`, `read_file`, `write_file`, `str_replace` (`bash` is disabled by default when using `LocalSandboxProvider`; use `AioSandboxProvider` for isolated shell access)