From 156013b1e8c9de0dae2707f07cf25e102a951315 Mon Sep 17 00:00:00 2001 From: Victor Tsaran Date: Sat, 30 Aug 2025 17:44:52 -0700 Subject: [PATCH 1/3] Adds accessibility labels and states This adds ARIA labels, states and properties to the Claude Code Session and related flows. The goal is to make sure that screen reader users have a decent experience when interacting with Claude Code inside a session. More work is needed around focus management and element order, but this will hopefully will be added in subsequent commits. --- bun.lockb | Bin 0 -> 188472 bytes src/components/Agents.tsx | 4 +- src/components/CCAgents.tsx | 2 + src/components/ClaudeCodeSession.tsx | 54 ++++++++++++------ src/components/CustomTitlebar.tsx | 20 ++++++- src/components/FilePicker.tsx | 5 +- src/components/FloatingPromptInput.tsx | 19 ++++++ src/components/SessionList.tsx | 24 ++++++++ src/components/Settings.tsx | 15 ++++- src/components/StorageTab.tsx | 6 +- src/components/TabManager.tsx | 15 ++++- src/components/TimelineNavigator.tsx | 14 +++-- src/components/Topbar.tsx | 7 ++- .../claude-code-session/MessageList.tsx | 3 + .../claude-code-session/PromptQueue.tsx | 20 ++++--- .../claude-code-session/SessionHeader.tsx | 12 ++-- src/components/ui/pagination.tsx | 12 ++-- 17 files changed, 181 insertions(+), 51 deletions(-) create mode 100755 bun.lockb diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..4ae2350f30008ba59246c8b2034e4dcdc61924da GIT binary patch literal 188472 zcmeFa1zc2F{5C!+iVX&KAa($PfQf;Komi`=3@M5)Nq)ti5ZC zvBv*-X6{+;x{S^++~5CwfA5@+58r#@dCvEI&$;Ku3{tw3eRO1my-!exy?;nwYoF+l z?0{`>sBiCppkRO7fY69O>L}a3RkLSQA+5_d$19Upxo4xY4|_Z=lYO2$^{n@*TG##h zasOkpMn7d_@~(TN2CBf&Q2+iYjg5|sQiu4dBK+09QOHlr zq*CQnsp4M3W`iw_GUk0FBcu9<`Kv4(kB>98R;{ zP&CR9dm4Fb*p?VXUf74wF9mxZmi13EYyC|?J|FU%VL9HSlHcLzoO(}S5wo~EuxJt& z0kI;mFA+5Q+X?xiuoYnGkE>K~4$JX>F4hg z9fl+#!5~adA^X;jQh>F~bbwa-i z!U@{t=#+YX)oi1J`uImkRt2k$h*0-Ww9&RhKC-7e%pW#P9o3Wdk-q9+|I-LM`MJIFNrTUqY zzhfo)yu5b(4Mv`EpNrsezNf%q3C3y8FWaEV;T5&(vJNcI8#`F`I|}WX7wv*W{nU{? zRjP34aUJ^xN5-g-t|D$+XT_>$#}yV46xpX&q$&b=>NK;}+WN5U*B%y2A%AxPY;RUo>-P(L?Yv2+qv{Is?DqgH$F&2N{)+Qh_z@i1FE}W$XH*90 zdEKY&A)lS;;YauNC83W68CHgypcKMLs z084*fx@i5VU0tg?36}NWj^hT}&Ze zEazcOeeJlSqauRPOjV_PqWBmgiBu z=GwR}!uWX}^=P4;&-SpK2ium~^P?mzuOWW;OH=wD)}ev z{Dy@_M&aH^wX%zLyz^nn3sHxJ2H`%%Khh^UDA-T6v#WM}_l6$#lTKdR@kXgNva0^- z;NYOhC{>@{=m`3Kf(%od%U&dFQeARZ5i^6-)ZDo?}PY}-zP#77FCpwHvZ;pOlFb&Z_2Xr4ZT;_( z-33eD&uGW~mm<&cPk<%AT!_}6cv#L?I4t9R5bc>?49otvglgBL7xJ7Jmke5+B+Mc2 z>uivx|C7SC{`Z6BxN@SL{mjFha$Lz6Kl5u4L+Uh%($3ovSjMpiEa#;(EdAUPt&M9= zM|vi$~F>P&&EEZXKgOWBnL_=r0owKWJ1qUK3_<#J8hOrpUs&!7&CxbL?2$p* z{5Dv|^|!&={-?p_M1Ir|ZG3}aiz2^MsviwYokg&k@q^Ft^oGT)Qd})q_AC7B6&V`h zj6MVXLn8Z!_^RH-Yx}zo%l(CmQymmu=HyvEdB9?rA~cV-0H?z!&<{;fMvV;KWqIz0ZaXL zuxvjGwm59}Y0u9&bS=^U=#?#A1@hWVo%Ha^&BBwCre?ex9yg_Y?;ZEXBmeXMhizT%J#FkhB(d51W5Jo9FKNHte@5=lD;{u)pH!;$>BMs7?tHTNT~x4GtT*TQAQx`p1Mt?Un!zY}!R+o}C1r+cHXKF%5SVCRL< zL%%&-9BSX-R_(4A|6aGRd5#SEy<@6{w>@2^WRZNIw`4E8rCdR)adUjv2G-8HVPNQp znvsX%A8y{z%&f?>PIhj|q3@5|Tu+{xG_^$H`-h23KOdadI`0RkTCcj?UlBfilI^(~ z<7|tK=st5|LgNz)GVkn>dqUF5QibamKisFu*6E(plD40;FYqBgf64l}bF8RXwA-eO zKjnGxs#C|AiznazJI3j+xIGW%R&MB#*l_r>Xpc4V!J}dhAIe^pO`RwXh#D3g46d+Xk%zL@RWbmP!g^U^KD|7dVK zVb1oPOT+!#at%4Va#ycP7ICWhJLR5?EVjcsOAd9duZ>+V-#pc-S%XS(txn9IFihoi zr^xZcOSdLn+;(|llV=Upm#w1qFKQIgzU1=Gwd1$?2Hwb0A}V_Eq4%X{N4VL0#+47K z_t*V#8*7!>U*yb{fSvh^tt(h$WuuF3zAs`2%n3=%5lNwm5vHZ)v~40flOR3>wuwpxwRYy{GzY=s08M zrl1bRT1QRHci7?XyZeLtyIAg?GGbwex>mKFEI*uBbtky&m1WKPAGa;u{K|)E0}EBV zm3`}{!N-o<#5`|MbK--<0>wKOIQ8cKG~4~#iU(GXt9EF}>o(*1cP>?6giF>%Wis`8 zoU_u_&1&m+TQ1amZ}aw9jS1l;)$zGzMn4+(;F8UZwRxRC76|cm%z0n(gA_SLGgdwsWczKgh27B@9kIz-A_JNWUt-7gq{W`mJyD^v6m+0Iq z`#8@|OJ{DFwZFg@n@0OLoS)z|K51tCZpyM()aUFX$obz#Mrzx>OoD>U}J+4Wx6 z8*w?-*{wfgQO$nr+B;ibEeaevcB5;}L%+pNUq0rMbrE;3H|L8-tH&pXzT4Ys!03^g zm+bG^z;R9KZF{Wd+BR~pQRw%MvnO3S-gRQ}m*tPdt+_aJ(=vOp-q)UQll6kvleXR3 zKX6%kEPHmY?XB%OF6VyLq{*y^ikrg+%zc%4$h`|Wj+7bHZ|>MRqXU{&THiVR z(efuB7A=d2-MzfHWuT9x&&h|eyPwr_J{FUCo4T;YwC*{3jkVugxMcIhe5L>CufC&0 z-Rs`JcO8Gm+|9x(aOU0hm0SW2Y#EttVc(FibK_rRHV>b)+jX`2+i|Qqb)#S=)gV>A zxkKFjc2r$aw|mAt7oKOmWs}cr@|_XGhd!7v&TqC`teJ>Qm);r5I6B6Ar!}r3E^}HO zT{C{2I%d(}>o$!Cn^~?;aI>p!9nnU_vHnUshdD17R&8;9)89u>Tu=@S?d3c6GJ9Ho-gIdEwErLoeoT=ds|%>cs_j zzkm0tWuHiwWn1T#iSWrWx<|1`F?}zLZ!xBb>y!!ZpBwD^T&Qct#Oq()^vW{Ty3PH~ zEobED`98>L^_j&(>n2$_nZN1Ox^&@|U5Xta-}C*VQTJYE%-Xihktr<$B8yJ^?c>Dq zFYH>EJ+Nk(b*boJ!Mo(zrdTtdwl%EW3LLTX9A#JN>E_(6!&-M0>rSjMv967cyU-Qu zSm=MdZbM_U%qSP(oiWn;Zj;!A8TSg_@U1a7+}9PsIXGg>=b@7)S^A)i^>>Dwpf0xh_`xXsw`qaD1+y>@zPB|_a)u+E0 zmzanD>9~z=5!B~M*DH5g=6b$Oy<_K{8<8;y8!FZ{^Xbr|$DAQec8c@+_JX}OSBHeO zzL|MI?MnxXZ_TqQ{&k|wiHwihJZsq{B5P#->EZ4<5;9JIQMC2&C4RN9_ITFmmEF>f zkB==pFxx(h@1xeQ6E=39)UV+Cg!@OUUirDUZLNIH6H4tIRKYCBvT?SLF*_&47aZ|y z^v%ILqa%;jxafE+OXU$c;!c_UaIzMcCLc}K_|CKDi9(-8R^>xCg}VqcjxMG^Ly1q{2QD1spM0uxyhU zmSw=vwx8$c4n8}&+3qPhv-{UA*0bch!Xr92ES|ah1$EnNbMIaqT<>@D@eezV8ozQ? z>*8nUPC2*xW4WGh!(%;EiCP{qL!ZThi_go zdiVmDk;hgaIkf5ZwP70`JPKJ^C4Q{uo1*1h3)~x5$f|u;p?0a4Y&V&@aIb*+a`c%NOlsmUBv@_IZY1X>8HZxR{9lCzz%$Gl&)>f;JIkw7HZHjMf;=F}}p04pR#Dp`|1{%O5EJPyV%B2iB0Z~ zQuWz%v3>3vTRif0$ya%fZ_5MI)Wvemdb6uq?>fq-f83}TFCDH##@EySyz8gA!hd^|b z`7d;TIHu!_%*>7ehVww>9T^qAABe9nHOccIP!AQ_BHtRCI9uE zJL(92wXE9yh2NrF-dAeJqFtcGXWwb*e~!c#dcu#?`jnT6_NT$;`6u$i%G;)PEZThq zzbW`){ifD7wKBo)o=v4{FZIvyi}KVs@-orR2`9Xmf7Wv!&?gCgPw<@X75q@}y}=iGL;G);Q>AKW1b+eeV`Y5dhra$-#LH^N zk39NrsQ=TzZ(~IN_rdQ8KKs?zcaBZ8?SYr~EsW@Y4fwred_%`?g$pjnukX62?@Ypf zfAG2g3cj$~R=R%*{(SITNql{Mm-qiC`0n6y-%1O=s-;TR0Q|J>KV!fb>rd{N?vZP2 z#Q1aJL!A)xFY@}1L9`zLzWn}Al3%k|X|e%8T>}eEdi}l+zPF5T==?P*U>tuL_{NO? zZzK6_@xbF_#Q3*>?+8BkJ;KoV-3Pyl)IYg~&R?EFDd#`=xSZ;h#QE1Ad_KQ2udnay zK=4O_&-v5WZ{eKaug61Mlo9>g;6q~h^-t*k$cp~Qf$t;hU*8xA{xk3+Wc(lXE9wQ` z8($*e{R8v*_Dv1JKMH<~5&ZhajJtlC<3o8LsedsxeXQ_52z>edmp)v!5&R?Ii|aqj zc@FB61V0D9T;Oel|GmK%`&U}#FA@9zS^xTK2>;#ir3qd?#rY@7e-s1JZY%hVe_Eek z%&h41}$OqB?Q}E^S7v=i$ zE0j-}e{v0tUm*DI7(aRR-BAD6NPPOswUe0#dL%KVM-3Dph^(#kv*t zM=pr|uY%9#Z}QUOf8&bU`Qx=WD`2Sqlfmcyf!84X-l)O+6X3f^{!o zSg$1dcLCo`ia&{rgFZ>{W5BNpKJ#grzm?$s1U}ba7D>a9{_XJaEUsVV>zjoCao|@+ z|MZ>nuWu6k9pJl55-r@bNFr z`^QU=oxrE#^hx-|{P&V*R~9cH<@?9?IPyBd?+v~?`saN!W2kQu{14za1fTOxz9|2Z zj%Zg0gKr6bE(5sK7W}>7w*p_^^G5h3_&M-PMBIPGyz3hS!EX$HYw*bv8lsQyS;3zU zKG(nC8oGa;0-xg-d_(;&iic0J|4>8UJy7`n34DJ4hy4<~htekrelrYSzJ9q5^+|%i z1bmL49F`dxzn9>*2Vdw3OTF(U(XJ6TPhLN`cjy~?>I(iei7)c{YKZp9;5S77f~W6! zAow-0dGh_A;2V1X8U=m}@VRz`Cdc=qB-);n_+s9K)tB#rix2N##QOgcPt=S4CxI{4 zzleco^F1s0kHB|F|H5xk{yi@1M7t^oKKUZAuMeVq82H@3MBY&TTJU-QnpS=?_&k5p z%C7_GdH(?DGkNk|_uLNJd|LN<881E4^j z8#op8-U9`{2VOqlpJxBbtncELoj=z>7WaQBRta4me=mu4R=9Za{Nee_GI1V%kIOp2 zZwo%(-}3&kD4_2U1b-CxxP^%WRnxbj_wT#G7xxc%6g9a2SKzxz{a?`6dGsUv?@>Qx z{un!bB?NyD_}qU4&(QUkrNO`NzlAPb`EC;Jnt{*jm*`)Vf0v9((QZ2U?%*>oG=9Wo zz2IM!_&HIQ7tmKj@V(r$<7YlC?;j_CkAHD#?f(qOJb%;5&)rb_{!6TTjzga$+SLUg zw?LZxQ{Q`6>I;5f@Ok}bnIZRZf`1eI`ZB&Kr{4FHXjcM@hyF7!%D%@Fb)s!Y@SB5g zCRG@E|8@#|W3K;Q8fp3BzSGe4yA=HT@L$~b8_ItNKJWiUJTi#@{lJR$U2yZl{g-jm zmrH5Ep8!7RPpmyd{XYzTJMhK&*VhNpzfxb*t9E(P*DMQxJ1@L+Q&huZKLx%9n zG}W$uo_9QV4LyJV|9*cv5dMq%_aX+wK*ai2@VS3;-1_c4X!_4wWyQtInD<{D!N)5! z&HG2WE|0&Lg#UBE#}G94|7>sQ{JjMq|NhxO^<6`v|F*dJ^ZwP4*wIhHp9lU=;Im)) zuJ1l9_=C{7ANXSa3?2U+@VS1(^^?BolSKaw@$#6@uW23s4Dj8-7kc{i%{HR_HHk0c zCdz-LBih+x@nQ@4XZ%7(U;aMu@!YNPpRqSI{_nx({foXl;g|5gDqemY6aV4h3;(ID zuOFiSec+4ppIlKc{M2PdyPVj(>&xa}-*qJTEx;Go4_$qwRxbF{z;{Fc+0aPeu~Sd* zpMcNtXHBh>@01C?vDdE}ZU6oJ@SQIIRVDg2-M{@ie)_Hf5x@V}-@lsf-~PRS@^e^y zl2||SIDg$_`=2QPk%nlO1U{eNc>k>L+NHMOSH;6Gwy-o`zvIB?{e#%Ih1J*pecFt^Z=|hJOCp4tzEGw?N;#2g^k#^hhFpTfpc0-?Z-kPr&E>BlQII=>DJi zbHT-5T)*|bccs4YKMZ_cznIrI_HzC{@V#aJi+Nz1?XpZAR%yP^G01-~ld$NO)NLEkwP{^#zL^8S*1LZ2k~9^f}X z|Ma~CVCej<0l%ve{4ChKxPJ(L488wu4?g2ZKDj~%hu=-2-Ei=E{uDrK?g7@{Wu;UK z{&DabKhB?3O7#zq1V0BJ{_qiA90@GT%>zA>;5PxE{)@PY@*hcvcJbhg^+yeTp92Ly z5qxK<|H?n=R8}wedAg`nSV9{A)57lwK0p8C{>yX6Q2)1q?*=~CJ$*8C{l5dB&mZIo z;P8h@jK2X+9>!0cyN2FB35aXW?KHoo*n-?&2{7=B={zW|j9R4tg{;j=Je*cPPhVCCd!1s{)7i;*3ouh&1 z|04K!1r$dP`P_r_NrGQnt=)gfW$Z-xk2FNP5fY#6siE(ExZwW*KF2Tmrqw42ehnY( z{-4(MI}Cj8U!s3~`eqx^|0eKx{xHuqXsG|CeYMYzY2^<9-yQvn{m0P$_k&*te8!K- z(E0lt{94lZGl6et|IU8@-oL4#PZIHu1fTw=b^Lq5cL!hWJ%;+9-CsL?_HRfM{=0+E z{l`oq8#?~s;B)-cqwj|Le-3=EU%@l9f6IWB?+?)jed8d;zbjCC{ovlsz76I74ZfG; zKQ#^Yzf(```wJ02L-{Mfm+xPO^8W(AqvSv5K*R-y-%X-j!yxVdUn01o{JUgSigwSy z_m=f9)`7nKR=u>}Ulr|zuE^`MqTO2X`Tm3b65>42#S`U%{}KFVGQN0^CEDq-g74lt zh0iiW*I&V4?fEbKH#B~2!IzKUQ2qiV`G0|rTeyGXZ)pF``=p%z; z@e2H(^>665cajE@+9;pmk8 zk9`~3zZ!h5UlBh;`Fp_U{kxbqVd>-dlJGwne9k}f>^tlCNU}P?Z_zg;{w&KPYxBKB z!5<4gJ_7kC{)V2vH^Ha>V*Z5g_xfg?Xy?=~#ebpkBd(ku0lx5`@iWx_!{E1+`ETg{ zmoFye{~w|MhW6hTe7u6z%s=@S9Go6W#P2Bhjg8=2^w)m>Tg2Yb@pl2g5&CEB_1$|p zJmLQu@cI13eUod~(DUyh_|?D%m465HA6@3Zmqh<p{vJnOC-|Gd=l;REwDfN| zAm#hxTsuOSdf!W;ojdrB=-&*DS!QVbCW2ode9k}by=e7GqW_2BbN?oXWulzs=fadd`Re(MEpG^{$Dq%L>8Y z3O?U|nxiZ&*WbKjQ|@2Va{cTKKA%6++W&g+LyX`T`&qmG853*%&@+knb(HwbX9Wyh ze|N^E{QZQq@Lk8J{Qnba;m-h{^Pg7#--6%S2!6{6DgXa@TKwMtzLydG=a^_*{QSY^ z`-`;3Zx{G&jNoURWL*5!;Pd=5)PJ5Pcy8B#dv|q!8xQtUjjajGL`&`3%pmDwc@1v> za8GCgkl#$QxL4EYkrVcK!Aysduwyd{|3hJ-|duoz0TnF4cquc`Z zANlYjH*6_pB+Ek^{Gcr@mDBRDjFhKkzZIqON|LQC)$3!~w2IW8mWQ^IwUf%f zXW7JFYDY_doM0KxTCg0at5h$yobP&4xgnPG;UVcZmH4#mueFqyTlUjdD*v9P-yNlP zv^?x2MYgEE&J;tmDBRDtCW}9Tqy5}a`xK?mgjR{ST^m)AKzFO#Yp9}7RZl+ zWzlHKj$sBVHOu%+l-f;_+R^4j`CM4~Jzpx9TgH2tR4%t{zd|bio@Kx5(2h;lOYP;B zMH}#g{cM-&^|5Ta13&1;E?DM&mF#Y2kZ5_h2S4b?0jXSWS#(e;ms=Jk;Ro#@sh*ZP zCnS3omg}4U_Ym!6sr&{k{;6*Ahh(|0JdpCV^!pJk=l?HQRz1TH?i;UQ8IKPv{Kk^^ zw^aT;OI=MTk|iYr$g~+LqQkO26YA-QIV{JO9X1E7mBgdv`YQv=_GMw&UnMDD8J3Bb zd^=dSca`evO7*nl*OT(8S@PXb&-0}fL&*u1bSq;gswu9EVpS?aHr>S=knR?5?|ejP0P+bEUOGQUO2r)J69D%I1n z{SGNl%lPb-@^VYwKB-)8S-v0Toae)^%pZ~3(em(^l$To;osjBJN%ge!=Zuu6rS4@Z zPs{nfF6HHxMK`5#TKaKIvbQCBN2;e~{aq<9x4e&eCDp%%Wq#(vR1$1z@v5jOR^J zSn3s{xMbPBl$57sy^WNoWq)NPTTZGkFV)K}c@%>z3NiE+_Jo; z)UK9fYfJ5DS?>zV{j`Nt{yj@SJf(KD2!*PnWPgICU)`m8TIzelvdB-$2f(sjZx%|H z;|P}Wv^)&M59&us-j2 zNv113#;1?8OqcH_(k;n!`RtNzNv6wpGU=9Nx_l>>Zb_!gcY*1aWV-w;G~JR+m+y?y zEy;BGt~1?|Oqb7q>6T==d?%W2Nv6wpkLi|Vx_q~sZb_zV$amX+*uPAd?~>Ck$#nS+ zJKd5@m*3G(wB#}=~h-qw~9i#whHOmDWq$!kZx6lbR87ZbyP^VnnJox3h6p4r0b%PF2C=dZb^0@ z#qZCjTaxM4OgB*o$#iQeq$_?;AYJ5T{?DE_B zw}C>sZVKr(R7lrdA>Bp_>3S%n+gKspCJN~`RY;e=7nN>Fw(m4gH&F=5bXzE-+fpIj zRto91R!FyvLb`1g(ru@ZuBSq}?G@7Pppb4yg>*Y9q}y2`-JcZF?V^xwSA}%F6w>AI z&8AzDT`#()n<#{2x;+%q^;Sq%t&pydLb|>R>G~<8>#vY*fI_FGo4dH%1}d{tD^FDx^C=A>Dxr=?+pzcd$abLln}D zQ%HBHLb~w^=?+s!cep~jBNWmdsgUj{g>*+Nq&r3--LVSk{;ZJhIE8e_E2KL?A>D}z z=}uBecd|mdQxwwuMIqg(3h7Q$NO!tIx-%5govD!SEQNGuE2KL|A>FwO>CRI~cfLZp z3l!2_sF3a=g>)Ayq`O2R-K7fYE>lQ%xk9=t6w+O(knSplbXO~+yG9}1wF>F3Q%HBc zLb@9i(%q<#?k0tFH_LR*CRUiSrqTh=`~J(ewCrG6t7aBe$EQ)!11XixU zu7FFh_2vGbt`gH6Z67ppyRm|Ul1pt{V0f#p9wNKVf7xzVZ9 zxz2W)e4hVjsrWtYt#aKBaYv`mPAHmjfcL5B4zpt#pRO^dQ9%5@;i?ro>OIbV=JmZB zO`jb9`$O`MYD1RU6h8bpFv-EcQ>6;m?Z4zI;Cyl87Mbogxo)TZy@SFFM3j6QvN~7f zuJdnNl~}PWuD%|Cnf!m4f0H!ME!YEUnqtDh%4KO8#I!?$Cm^`~A>n7r?Jq4o!c-d*?V z^W|N2a^G&}*TQ?lr>rNF3%RtJS*GP>l^6eyS`mjGa$WmjtvAkc8#KqF|A3_@91cyl zxcg_kRYKJ0ZN;k&YtY zboU!ACKHh9Cdi77K9uPu8Z9Ohkm(+f6&rmh(>-Xkm`p&Xn*CGHu_Med&+1r znSf09H(9aKhceyMMvKV=WV&Z$#YP{>bk7^R&4a4 zO!uPEVln}l?j>2V(T6hK%SMaI1Z28bWW`1w%5;A>T1+M&)4eJyHu_Med(CJunSf09 zx~$mfLz(Ukqs3$bGToc9Vxtdby0?rLlL^RlZ_A2}K9uR+FnL2rhCt5 zF`0l&_YYaI(T6hK`$mh&1Z27oWW`1w%5)zZEhZC?={}Mb8+|C#eQdOtOhBglL{@C{ zp-lIw(PAivEhZC?=_bpHjXsp=zB5`(CLq&&FDo|sP^SCA zXfc_9O!uR#*yux$pmD&pJc^GAIfw;8!aXikm-Jr6&rmh)BS3+m`p&X%kT83 zTaxMW??I+plIdntNH>#0x@HRLW>!cyi$c2Q3h8E5NH?28y4e-d&7qKPPK9)HDWscQ zAzcfFbS)Lq&7+Vm|9)?}C1{Dy=lS=5(=Ew#^DCrlrI2m`g>(xlq+3Vz5ox+R$| z|IT{4C5^83ybyo4JzcbA{+3O5f3@dZIfZn~E2LXNA>E4UsjK5#AiSwdz zdg{vjt&*O)GJkCq(zR1a*IpsrstV~kD5UGCkZv`Fbe$B^byi5%MIqhl3hCBRNVld! zy8OGe>6V}+_TSnH>DEz5*Ht0ix(eymQ%JYILb?soQ&)C9c1urPXo>aPP$6A+g>?Bl z6X}*@aqv(`x3NOHO%&2?s*r9og>;)Mq}xIv-IfaJwo*v9wL-dW6w+<0kZwDLbUhW) zZLg4S2ZeMyDx}*6w>upNLQ_p zu8%^xz6$C3DWvPKkZyoNx`7Jm_EbnWNFm)`3hDM%NHx_PltQ}E3hDM$NVlItx-kmr_E$(ZRw3O13h541NOzDzx`P$c9iotKoI<)o z71E7QPhHu2*+Lq&r$6-7yO3j#WtaXN7df zDWp4IA>9cI=}uHgcalQ7lNHjPqLA({3h7Q&NOzh-y3-ZXouQEKOoeo3DWp4FA>BC& z>CRP1cb-DJ^A*xvppfoDg>)Awq`O!l-6aa?E>%c(nL@hD71CXyknT!_bXO^)yILXL zH45pjRY-T8Lb~e}(%qnt?nZ@lHz}mMSs~pm3h8cDNOzk|*X)lmUbQYZD81-PxKpE& zKZn>(8*DYmweHcK-R*xl+UaZaq)!JQMArHJX>6X?kCtU=w0lPJSL*G44Hq@a;dQpu zvWtFv$H(_2ad=;{U9Q_U!@*fw$CbLj_Vd_xm3sH^>soWp@-NQ*C9d6b|Lf(mn-k1V zethn{;lhv+F5M4jueI0eYx#ADCcpXouI}NVug^cenR{afRaTAe4!Lg6Veekqj*U@W zTlnJbfrLittHtwJw6>nHwRoTP>ux*>i}gtAVcp}^xY52F3%6ex^4z;Xn^pO*S|ybI zSib$PrIRL?(&(yGmhg9{T=#vs6G531R^9ZkGwYUD@ZxPdD}GrYF#p7XMYXbQo-wUz zfn0@e)W~E1(aWKj*WjdtGrtTC^DNuG^N`p@r4R4Drx&!oINS2(h%3Vu|Iy9szD0b--s%>~8SB0Gc-!T5 zM67w@?OVI+@VkN;R2en?{wmi^o_+VvWy71VjjC4vQucjqZ4%dc)pa~^>i11w9d=b* z@w)D&kfFVHRlHSp)`(57UtgQNr+3CfHpSZilJAYrC6Cb~9G_`)%{02Z<+?d1U%K?J z(#$QD{H%R;U)}KhW1&nHa^+YXx}jL*w!JSoCU5z&c>Ia;9lFihpQpv(jav_d{n@dk zP51XUSuZu(vG}g@OPTH-x$d(0 z=aU<)@w`{&t<(BfZ-e#^$};pt?usq^vt=(Dlz+m>8^sQ1>CxzDfu4@fUbnv8<-mYS zzq^jxaV)u{8^2?Oe_5%!Pp+Hg^svg0vW$&da%=CwJh$!q4prXk;5=vBhdiH`Bg@T zKTcShC#F=E=MlX3$3M+FOOWg4?|EXb#ST};u!T39Sti&#w#fRpqFvXybyk&XHtp=D zE5~NP=(TS9#V6KvoXzWam9{wHaQtJrJ-)%i+uPTic7FE$`Z6uJY;YpoH#o?ySERvI z6TuV#Qv^&AFh#%=0aFA_5imu-6aiBNOc5|ez!ZW1brIlm@^1|_@1NrS*A31zAX5bX zFNlEYUDf{ulVox+-6OztKc=Hfl|!ZZSS8){DabzDJO9sL-`MZwpHwOfg~wK{t4fty z)(89Ti!vo+iZ1?>*l*)*Dpfw2FaK#|D$p4Le0^M%J(G6cD*60-PIZ$0msM~LCHSjU zc};8Rf7#5MhGU9A`b2=Ai!%wU&87V(_nb@vRH|I2J?Do(!Nh-ttrK~!IW6~$XvW_E(!!grTsU7~# zg~~%Jn+D7FIf1HD*(|9Y{(o;(kmSp3sSJNNL)97mvi~_KV;}f?9V-4Ua`r!8YRA8! z&cC0{vIVeoKM%maEzYtkhj_KzLsSJM)Lq#8$*1_^HKM>6Vr1esn6^`5D znB&O;>IPNZ$Z9*A+DFpP8%C?}4z7z%;u@Z@Y{~iBSMF0<}jDNeG zWkrFeQW^j5I?IXy$vEH`x%yaE99WD4j*)-woMj~dKXBPU|5iE6N&nSyokA zx>coxl)X-LHOIPGp*q$oC>kE!;+$~WxV|_pt{=`97z7Lkh5&KEDh#+LIJE$tn-zgdKxLo`U<(ujiUTEpl7KZZ z4ZP{V3}7ZO3z!Ye1?B_Gfoy1(9moOX1abkn0Smwq$OGgB@&Wk)E1&@ICnn%Ba0oaG z90yJSV}PH58Ne)H4#2(REN~7u4_p8)0uz8kSO}c{BLMe~B!Fv+Yl&-T2e1?1+UHv5 z+TILsJy%COEMdC>UO+dXJJ1902GoEL;0yQx{y=@80pJES1h_Y40nCA{KsF#dkORmG z)IlGvKs{QFyDm@GnQ7y*m~LV<7~0tf>) z9B>2NfkuD_fImZ|@&Y=ejR(*eum`FF4uBny56BN#0R@19Kp~(oPy{Fn6a$I_C4iEE zHE;2@9$90HDE{KtWgn3HZm01ycD1bA)m2HFA6z+SX3 z4_g-4h&fmVI01LSzYZJ*b^tqpUBIuvZeR}(4fF-Vfcii=;39m^3i}+EdoQmKyoT}G z!0XW{U^@Do0n7w=edq_o0R4ej0Dq=J)dlT5fu=w+pgGV2XbH3eY6H%IEl?S-0V)Ay zfeJur;45@q0-iv70Dpo@^%L+ra1}TYTm(+R-xEMEV$cT&0YZUtKy}2T25=MgRRKr9 z32+8#0KE2i0F41&Us?il(f4Q|4%!odjyP@&WCRu>zX(_YECp5qVd&=;c&@-7XtNJx zw{hGY<(FX#0=#eFeL@qU7s_uS-x=UF`~~n5;5E4|&<U>s8t9cpU14{wVl`avOpPt_XWI9C=9Tk_X@mc z5XZDOfHlB-2i`*nl)`aoz*;I}Ui3ptEn413uukp=H8~dg!TSqp(-+=vP@nBN79R83 z#d`+9al&ymz#8B^3GYM5uL_Xw0PsGA_bft-eX*^`(^>=Mh%(l*Uu(&V{%Rs$17M%k z0T;j;V0*^U84&%-`xHJ3{crR1$r=##^5fbl=k?khXb89g4S@PUJ%D2_1yJK#U0vlO z2K1RR68*B=Nv6ZGFn*%m`kOrGvpLWVXbLn2JOHjW&NubgMxJl-P1(2gBG105(F&kW z3&0v+8L#1Dee!(dH6#j%1bPEOKu@3@kP&DD@ce2k<$26KECBEWd;vA!4fFsy0Nnvz z*M9;!0qp@#fOQ>#&eCx=Shiz*7s-0T`T*X5KhO(^0QvyIfbc0C$6)~dVVx*vJ`@N6 z*d~i)$H7hneg?(@F#tL7KrGN77z)GzLx4fRKwtncSUMgJI|>*H3f#efAs&85H+ zU@fo)SPj?!D}fcja$uEotgB!8!r1HpwgcOMt-uz5`#uxv_X58HyMaBDO@vJV_5-3_ z8>y@>>|ucG;}P%&a1@{x+uQ^00_TC_z%hXF=D3RiMFIMD0^nTnn6dZ`I07=u#) z>&RpMS%C3p{Tbjiz&hsHZX<9G5Oc(`TfiOQI&cBF23!Sx2W)`Lz$M@!a78*Mhgvs* z8vy5da%F%9z-J^rFY&qQ1Iph5 ze*sT`9Kd6MF%&W8G5z=xcnUD)qU;%t>6ggUpEm&gc?~d@uYi{TdGzr)Ap9eb{=ERi zc$0A~;!HiBQ@;W405Lw!9nT5!*_QJ|9cr*|_WK@SJVl*YD<4rNUn^M84ECQt)# z1^8^l`+kqst?c~YP5iD2Dk$a0XM({XaqC{_}sx~F+PU{0DQ*i2KWKK zKxcr@1Ra4kKwE&%Z+!0IyzqWq%mMRk^KCgf-yO}x@ubF)k45ee z+x7uC7vJ_HIQ>zVTAz$V4A2jduU9cnd0f~BW78K11-|Vo8fB5de$06UY?#!Ba~Up` z3xC)~97laqCp1Jo+j6`jHsAIu+Hrg$E{x~5bD%3ma-Obu2rY5`>WTq<{MJ9YK4Ux> zV7t`j*pN9AK5>oFFV2-NzvMoAJ4X3=@T2iRx zChQhq1~46%0E`C;0aJlVz%<|&V4{?t0y`NH^=w0I2B^n96P4vA9#~Xpw zz)D~RunO1!tOwQsYk;)?`(QgUzIYrb0Q-S`z+PYvup9Ul*ahqab^zOfnOE=wX)|B6 zA!V=A)zPI}bPaqa=Ei#qJnxVU+U3EJL?UO0)?BXWd zcNt%9n`_Oi8PJZ`!?@D0xDSadVpTTJ(9)hK!EvZzR}Cb4)Zp$bZuIWu=L+>ZTP3BY zp2S)EV%Wy82Rrm9^vN4u(;r)1p8#AToEJbIw5UrW?DLCgvJNKbV{7zof-j+33E=1DX=I19?gRjE9}-zRH^!OnIrs8IC{it+~qpPd9*HqQ1j1{@bV^rU_A z%?Hje7e=4!=+kCb#thx7+12Do2cmre)L4GlFfiAHrlnEiVCO&&cvoB;HLeBjjVokT z!4fr&sBv)CJbPK8W@VN5v7T>=mJ>DFH(>=)v+4DI<#(*m6Q3KPN;DL2lL~J8m{;(Q?1+F-`sKrYEVwE`OcUg6zZBW z@%ooHy`&m=;2RtnLmxeFcD>j2MqCcmNHOqJNBP?ZghaYdnc)7p!M@L^sfN`H-~56C z0?2t3)Az#o7GsK_hF+tek?04{EOE{YkCq*JF?YL+8Tz4wG5!UXBmI3?Xa6cLpS{4r z+U2_D=YI4$N%h$C)%d{5sHtk_8x!m6mLZs*MN^dxF>qo&gO z&f$-iKly-~n%c9GpJOp@>+IH_v8ZN0mTEkO9aItizBuorBCEDIe5G$`zd+O=ZXD1n zaOkyd!pdvQ3OWR#hVz5CeUkcF?>{5==ke!CX*imQX3wOJPP6yNf-^r~(q6;Stn1iN zKZaUm>vHdDWA`D6sHtY>$bMp@@x_V|Kh@DITf7S7wP6gL(T|JvO|E?Bv&>M}72$yo zL4Er0PhzW9baqoc`{S3)`BB56x?nv2j%W&}Ieg>!v&Fqc`IYB}TqVcBj(g%X)Ucn7 zm&4;`G;rk1aYw>PU!sPy@Mz^&i%XZXgk{Wd2PKv$;iob@Pxp>{r1GZ3_F@I^A9-slvsbHlrV|sudNBcH4CEr#$dYT2%?C;nlgohxq&@>*uBi z(n!yvhP(2~QibamKisEDO3g#mltRsk1(|pD$UPw`Wu#fLO%y{-REwOQ|V? z8m`1LC5zX=J>8nsY#2PZq8|J{nqGbMfmhdw&!Y$LqF1re~ub@kof*#;?mCt zr=^U?949F*4{^tBt|!k;npz^IrUq(w=TPKXCp)*~(D%mG3`J--bFK4!aH{pH3-<%* zJZ*_ZkQX(!#YS|WIWeJeN{w!>b^E5fA4Aq?C2lE5x;eUMTQ?^hi;c7JcZ}0taeE%j zO&O_fuhaLO)4f7;@0PmHxjPsSPP;gdHSxitVh-~jRyt30uPWVV;}fWrhFax@9*GTy zKjXDTS|MqfZQZ-2ZnbT=WMa|BJ!`1GY!$VCQObDy{C%PWu^uK2Q#su!avaxFG19a| zG!gyq9ya`s2DcODY|oi8(yOT9UBIqQHx6wzFWoYwM)$e!5*+T=1Hvxd%(Nna_byW0 z%yC+APfR|%y>;(WUt~L!(M9+y<3X{!<$9%!Ff zH2Y{L)bJ^%Pw&{5eYbQvk>Xo~#K~T(SLyN|`FPi9#BA$cy}F~LdmZRL8+CJZ&yVhi z>Q>Y3f$s6>=IHiKHzzIY+6N1VPspvBHK-KV>cs4nIURx;u4{FzuZ>+V-#nF4qkF|q zmpJ8~j4ZapI*aT+WF2aFMc>t{l0}><{!WUT?vd(_o9;V@V^Tj$!~NWH4LQ3qrJuB% z3+cP!b;nJ24AQdR{-^rUeFmrRc+yv`Po(BHLA7*i(#36;C-Q!j&-Hw}f(t#bjg2DO zmt5Ytc6`bc*bUTFKuuzn__3w)Kbe!VsxsrEV2zrKZoV&K2h8EyB56-7i<+{i*_prC zx`IVkHcF{!h8mu88*7!>U*yacK8Z`5K-6&e_lzqaQ17q%yc?8iMx&-2Y8D@QUwU?g z8=v%~8r@e6-8+TunfsqwJO3y9raL-m*^zW#Y149U>-H^u?@)B_mb&i`(lQ?1=XUz` zqx(L7I4(PWn+Nx0kBbzaR@7c?~vcp>kgdE06m z6gknVn|jx;vo)XPXy0ECh8myr#tdB_(`0rXzKx)o6F#d%4OimQ=R58Xm=eAdH7IAz zOsVF0hCEkWuX*$lHMr8#gXL1qjG3E)IuvUig_>$Ecz<0ZmG`|{(Uw>LZ{MI>W)L;H z-}e^8<(&5v?d~n_J=JGJM~!cecu}27KjKZjZncF_XV$CuWIi{uMq>>yKF!W zV^Hp4XFI1l@%)rWIve+*1~s9{7M@y#fy z=Fz|hmuzON&8y*P&XN~WKNG@Bs^fFb!b($>vtYrco28hp!TqaBa!{%yqrE5}t!sR1X_R~_tc z6Z5=5&4~~A>@0cBnx5(~f7{~CuY8y`u#nVGRZTxpL4EvH{aq|~PZ_b0D_-&%9M(ik z%y-z~?z{Vg|JBboUOQUbS|ik|MEJ%zy%Sva%Ccttk7vx_fEt92;|Wto^;A{6m3`}{ z!N*X8el&4I%{OYR@!|w88RNFXQmw!Um#mA*Wa_0=V?Y0m!CtAYdpOW88gspoC@^qn;6h*_JbPV(BROBLqlG-8P~sasT2>6;s%y#&L0be_&Vmq zQ=53p_TP0NYUbfKm1{Jox7F2b7azY$;XE&|eRumV?n{$4C%xYyI*!^;x<=vj!t2gB zuklH1Gg<%2vynRz*1;6iFiN)T<8zg&eV`<2@HtZrRLw;V-=Pd&=zYo6#+);UbKcGw zpLqV~6PwLwi+`z{!J@{lq7=6;HjVagI6nbu_za9{bx=d!#(8#HI&;IU{VBesdY)n} z>hhW&aR1jgd}hRrr|N%q7g3|TpW*P0J8Zi#m)4i)+$_5$2Kb~&iorMgC_bIzYD{gU zHTWs1?$4Q0vQMu_)kfEvhklElzFbq|$eLnM%LcWzcecD* z6gYM)YNXFx{~OV=Qrn)eTsv#ex5;|J>q%R1q__JKsNuRE^T@i0yVslZsFCi{{_DYD ze56MYQhDbsjpuKu;qff)NZgu>GdC^cvzb(r-&T9wo^<7S*NMenmQSg%M@=4Z8aS>g zy={*b-`Po=`lzATtpyXJ2#axs&cG%rN6r>(>5(%hDq!9;K*BAL*%_u1vg{eBPrhYPgf5eJY=q zaC=1xP)7$x!8Vx{QE_wl09oAryAlUuC35BhLL>U9L3)*W$h`|Wj__S4u5#=rl^96Z z1>JW~y6@69U^8WO#-A~Fv+xR>iGHMr9zjh$)YNx$sC(V}_pYds`nif4yjG11^s)3g z`7m}jYH*S=2)aLw{S%yA;4E4e5xaYNaZ7Tv)2;iH2i>2W>HaJfmlDk#m-wEL_LFbU z!hg>>evZzXv_w>Q40Ml2x7TU$U<@vheA3%oxMcIhe5GY8UiWP4em-3YHJ-tREv9wP z*=sEK18KHbqlW9?Sv}`tF`2iiQ6rrtX_=o?&JwAg|JGFElnoMJUGKa)_y z6_VLJe9~^$)mS0is<3QS_i=vliji;b5O=>FRac~VpnK-hvWunVIrzV;O02i^^;-8z z^fm0M8sWzGnJsmFwyXP1Nh)^^(lsjq)5n$gV8S@R*>16Bm>;P|_vf0rS4atbnU&X) zF1<69adeFLPFcOF+->MQ>&023o0G~N%Kz8imB7hW6#WS|1W15{BU~$&9KOw7IY0u- z5eP?!BnA*LVRm+Acd|3{Ci7;JYzP4nNPrLJJ|qwh5fDK@B1Zr@C4zvc91_4NqDYWi zKTwR{zpDGa*YD_lv%~(xpTCdqGdr)VtGc?ny1Kf%x?5`evrzXYa1f15i1A${PK@s& zAu+y-gv9tRiPQ#HtMs<25e?Dz#^|Vd(0*baF=JP<7X04FuX&(puMd9pWwdsCb34s) za=8Mc4G}Z^%68lAJaq-la>%`gEhtNJhduk3=gzYl5=*Ei?xkz@W4c6rmKsE+s%DQWx`oSfCd3(f=yO2WO z<}>o|ZLiEcWBxmJZ;z`M@a~n8-IZ4S(UTzIpvdWu62DrK=e2}94KV4 zWS2_U(8V)J2CZNLBHbMjs_FlJFuCZAz2Pn&QgsUXE^u(j(pSHK-B!y#288CPuoNA@ z*%CO9{_cM+Eq=Rwy%9rP`xceK;mEr=@{=Sn=dz`0zM9WELwi5YcYOaB z*D-JCCpbwJH=jei<=E7<2aDOq2PfbD!coB!IIp=H(9176{)wyAUG)0< zYi%HF3W(mX(vKyLtb*!${U2mazx~YLzx~Dcnx}v_DN((5tEU!gtGH)PJVdPXy)fY` z+a2C@^mvrRk*%;4>57xZO2)2_+;T(H7itASq`#y0qV*$Fj3&xiwVrFetYhoLZ5lIs zm#wY$-}Lp_`cPq!wqAYbrc=sOXa*)d@7*M1shZqxJgC%-t8PEg2R-w3rB8OWOI=L`Cl)6Y-$KSGT8Ls!q7cJc&TCwIp={n|!M zYbVY^^>YkO^fP?Ec>jP7zx)kclUK89=>t2xd};|J(?aU?t#Ri*7E&v;V*EW)%!9k8 z&zyPXsgv#j4oMV{tpK4J%j8YoU6#K49>`!J&Y_VF84U<|o#!2yFFtX?OC%ksqfu`| z0#f?|hkV#f<<$P;?HwKjj+D+JfRJx_+3fV%lmC1K;?fpMYxz|`XpVm9w5GKle{j`{ zfXMz6J$d5d%mmI3z}aHaITwBTjhl8tInwg!J%>g-Aw4yHpWAV=sOgLEpLpNeH~)n8 z&7^oNK&a*RKjtTMZ!1q-28h&ay~ouM2S0EHIF>0FL{{3UN7}Z2kyix}Ge)>(|&>G5;zkQ`=v%4!-_|nmP8OpK zyU>MA*Ov6Jf3xuG_g)JQq{i#pM-N&26}c9Da>u8AUs`eis7ZU>h>xV#1N{oE-tT=8 zypbNPf92C{*DosegE#3BH)5~#UbKD%P~Sd!>FE7YeVgjrN8d~IeIZ5@6aA1zxYpaM z&wq9pBT?B;|KC@}Ip5~UexsC*eg#l3QN51F%#5V%ZA3@)9K_fiS`@>7JHlQMhYhKJ z1Z-(&1k~F%{aC-t z|G$44J)!JJ&qU9byK9iKr}cUEKDdzkrVCH#f@(el!{Sfxe?2>!n%W1UpN`^XKx0myj731R)ZViyXvGH!(_teiCL zh6Cu7Htxabz^>lYphk0<_UiHx&u#fd^7&{}ueF`#OFUz7@8}plMmOd}MP=A&cVi-*@)?<(p4#vi5uaTJ&FKjThcib5@LhzHK;pc7&30 z(gJ7dpAI}{vkQ9X`8Y$475>{+mwu`L)m#7hmJh+1^Fj?J?SFN;ebfEUd`N-Rda$DC zr0#t>^Xs3iyW%zU#{P7%K=tYP`^FC(d&wO$0HIZP$U5g_=U}zbx##@Tw>tBk#jmYHYth&j!3{Ow znr3>vpDZ|So&A>`3W?JA1IUGd&`$Bs*4yjMp{G5&>pIxsXy^IqgU1Pd*wEhhr*9qo zo#i|`Wa%`b2Vo6?tlOIwENGh15#0+V_Zr)}slR`80xx?z_EfS~)3L~jCw=G%Kc ze|_b-zB^B32=ooN&72_Ch_BwZ<-TA2Idl9vw7bkvZW@WAFI3ZLQ2ck(K3C+ve&BN# zf!a8<4`hApi6U}--=9P;LMW2io&2d@ES3vf``W8$rETyX#6z>#zGOs_q+1cLiN z{^IP;wJqAmkmEuzU&@!Er_OrkpOU$JAKK%m^#jO&AoH;`v*exe%ZE&VYj=r*`V{-n zh0*7J@{inoa~@oJix26?%Nlv7n)=|f+g}*9;0uUCbk}>$yWXAJQQd>fp>3vAxCb*1>vg!F(k`6P(UqV^HtW(KA94K# z;~t}USZFoWcbvXW^)_*jnL^(_*(}?)rsL@=K&=(F3S9pd5b8hU4}bEVwkt=GtmO`2 zHC;}3;ckQ#pL7hp*!tCD%r*KJZb&*w#K>GZ^6s}roq2zs;Ef_A?+1kXT>tyaURt^T z{bG-j?&3)8k`_289hMoo)7jHs1`c$WsKxRI_321G3!IUl_PaGt-?n?lCYJ+;F4aJ5 z>u_2O98|HPN0Aoxdvg!o@|>^I>SP;T7K5ACU@bQTWFX^@zO{=LHrJ`!TF37Fz!zSj zQHHoiP4)d$-*Pd%k&FoXT;8#I3U*g&?ism1HczRIK{?d30C^WRrBP;e@v$q9`_s`+ zNeJ5K6F@cvWa~HAG#~Np8m*zowY2p!V*h#B8*d)>`kGtTtTRLkC)HI+XR;0=^hdn3 z-hGe$iQ;alpQ4aH$l6VFEg+l|bHci$&v^8R8!I!n zr2fN(B4q=DV>YK9dcyBgXFhe-#XjU*K(+$p>P-vJEk1ntg+AmuK)wLTb>HnS|8)7h z%>{(~kVgU89FV{7*8A7l^AAA;5tRc5QqKdjB_KaI^sa;7{Nm6R1i`gifUK5~7jNG2 z^#?j`!HEOHL1;*7NRL<_y>Qn{fBgJm2OKLPFtDj@0HOXf=8lbqw(nHJE-6(MqE58| zG6Im3Pg(hF&xj|!FUldvA%N@#$dJEQ)?f5-=Sl&Au}l>J*%6RiCs)sYZ_E>~`Z&>e zzCDqj{zLF4TQT?Eui-;nNOZ09T+qk#FJ^z+g+$AtlMUpM>T4RsaXA=V-t-dvcX6QC zQ9TFxR%it0R+y?LwBXcZ*B*Ps>}l8!ru`byou>hzzHsx=vojNi{cVxZQPTKE%4q~= za5>P+x{-1QR}=Mp;q&M1TYbX5ZFA;v2W`LZj9;_u6*4tja4DLlaq@$yj{cOFHR~*feO2YQ)rSlu$&<@o7|9itH{V#v*R$qJHmyl2I zx!~p(cI~^;hir(s2aQbEU%lhJrB_`8Pl)Pc*7(8I1AToO=|7FsXK<}cBV6nCKu>LO zTVc177y;8quUzu`mly62jVF%_UO-ejZgf+0ZNr7k2DQzgRX0!B{<$R={t|OdP~%%U zPX~m2ir=h%-k*+Lbly#X&{_Z-1U+PMHKY;mN#8#De)=OD>%^$(%G&h@|7*pVGyeTO zOV68LtD>##dNq2vJysTX>HO)OZBHCBbGMPArj6P}y+1m*zNKC|df!s7iJ!mMIk@A` z=kF)#^-VvX&Ydl067#3$*1urpX1G7Z9nJJ}nMUR%=c62&XFaseke{xqRYZ6YdN_6R4<*YDnds`pEL29ansLJ#vi>msITeZ z>ZraPy+pr;)tKSvbK4G|_VF2aUccDy+n>M2>&L7-xF$<+)*Y6yD3iqhfV?jXA&SJg9&F%S#ihZ zM;^xzUKrEYG}?crfy!7=c>;fFrg!!aJBAqkJnU-h3nGU+XazMl@RJs4bl(|ba9%@aFA_gfzx zF!riP;SKWU7SwT!}1jT-J;BJ#_aSlbFjXdm) zV>Y>P7dlfsPQ3CnYQC7U+`Rmxx2``uH;3Y$5SjE$HVuZb=WsuNNcJO5V{?0x27$} z?YLk%A_fFbYw8+6Xg+t^ng_RPx#28CT3mz_iY;4)znkOlS%>X+#PuI7#{L}Pv>+Vl z37O-27Yu*+^wa{1OQQfL2q7Ay`D^6e3se>_6jX;DbjZqK6aGRHWoqM6mjFT%-ExaR z9=ZGCNe}ywYXR8`knJxxyp*}-qPc#g>^*=`bjHRPPWlC{_{=I2m+;$d4S+hi2Q;Pth9gaVKW&YZer(bcm4~eZ0t5s|{ ztOv0Wv|M|<3>atR>0;;Ml>4Wh_T`5cDJ>V)1Gi^cEwQm|9I4a;YP4&0__BYkdhNtj z)BXC)1BB-2t7n~j)`t6xUFt&GQ;Pwih=Gm;=Ux86f;(SxA#JIf0imAtwfWzD;IAWJ zyUm5Pr&a($8h_~T3lsNz`0jgMNJlDGZmhj80B0C*4xBQ5%H;Dd66XN{X-mB!A)`Jn z{^jN)cA}f%xP3a}<;D{I2sos@tKa<2cczVglp=qr2cw_Hz8b+T*OM(d{iRImq)}7G zEjeZ=&K<7zO`)~39J>U^E45FhYsQ4FZ-FL)8r!NEi-eULvJ?%;8rn;JFW5_{Z|kL_ z*Q(f>3f>T4mM%D{<_GU-KCo@~U!p#U_a`Cfxz@E@o3<59{U`cLmK#5+kEYXcFh!UM%^Ir2&*Ni$F)Do?W zPUG7n*5B>*xIIeKOIo$kX-)kTyiqUt>2_PJx$4iuwsv`IOAT8jqSRO4lD*;TUoQQz z3u#SRfRMemFMoE}!o^3@DjUXGgea!=24q7(-h1qn@{$*SPV1J4!=%2T?m?Y zvE~ne@VLEq$^wK&jIX?V?TyE;SRf*%d8H9;oi3!iv!50o{paKp=FJ2SB3sE)=ru7c zgW*Fi1AVG{BlbG{il#qioqw*lQM_%V4_A$x`>k$@-o#zW(9K%`*#YIvxb{n{uUq-< z*L}!SfQ$m9T6=K(V|y;##fQ8B2(8p_`^o#Ko%{1`j`1Nw{zq8yX~nOd=6rDe#Xh77 zkS5?f{eyM4`O>RHKk^}+fM8a6+DG@c_f2}`AFF&w1`y&cGx?EA(%Wrvg%9}#AT*Y| z`lCa(IDU)0uJj?-0YYQR?-!+tcRc^qO?}A2fDi|-{bc!ew=a5lk`GB8wH73iN_EP= z8|~{v3@oI(vW0MB*)H3lA5NyyY^w*4^_9aQCtJv7s*zNF=`EaE zO?O2&ui9xRf?mY|=xV+U3FixQ^QCSa=OGaXzDZiWnb5q3 zk`k8kiUKHdJ6*sf)pn(tsa0c8?Q*sR2}Mzz-fRIybK}!Vut0;uFR7;IS-rSamoL@@ zg<5{DWV_gHWxMlLyMo%}t*UMHR*Qv90Vgf2Iq6D1UCLywbgc@W^3{I2Bny6!l`fQf z)6K29N*Z@#TSdE?w@ZZAmPwc9q@6-7lkd(7RiYD7y><_-FO%A07sU@!EQ>Ttd=}G{ zKGGNQ6=k`Sr5sjIuIM%lG^^5+b-&O|z13>j*=IB=)`PPhmHy^Z8Mg&D+m)Wt^3BiD zmix=10Q0fU)gC~y7I|(Ky&*}R-Ets}tbrJ_2vUMo#4f`vB1#3@2CZyqPCnOfKv%PY ziu!5M)2=NbQ^g6Bav_a@!tR=7kPTm80Al>t zA~~=Ih|eO{v^J_Di7RnlF)2sR_Rd~`rI+dz)m~@3W*1RWjgu; zQ5Nh{PZmlAJ3l-%XsO4>p9RFc4oP-nl3bEoHF8Qs{TC$!;uWhJZdI{9r+MBs6B2Jg~z_Jjk+~6-eGuKA15)icz71yaon!C~<{KYHW1a zgw{tl?*xgV7xA?w31Tp{00^4EEN13Vwp$7ARJY%_aG-KoNG6jFQAue2QVpI_K2rxF zr@z=`7YyA@VF#1?LI~odOYo#2Yb;@eLA5TPj{;i1_#Gc9AR(@j0)()W zoyibG86lACms@l<-1mGbO;gJ#kXJOL$+(rhp!7GF)ZAVyAjb7; zT12y|=^l$*!TLK^04-2I?pgux0EHK2IY2)OZje4pDwZ@u8l=k25=)gK4N_G!A(plR zud4(e5kQImV&JFMC-dyPNRySK1L}1zt%VDLBdd-|Mbq@lU4&9fO>!&zzTk~Uv^o`# zR{+AR4q{U74x|1+MyRw5Z5Q&TKC6J#mPLZ*o~J2 z;TlT;oa>U<5j46IXk$Z3fqb7ydal+P3Ujl3rgFl)z))T|YklSVb0XPa0}1||LYd0M z_Rb)hTlWxFK>i_^=z>8qL3KBEEE68d%mjy^u;-FwgToZ0PRAts^xP#zVP8!A%l$Bs zkr)mnu(ce(WRqGbAnT|I0$`tG6OX~Fx=kDLY($7ZpoNdcekPrmz!cRKMp6;ciL5(OiGftlps1G{MXJFrfsH``q+ z7?%XRJfPwIwlGDfz#X&K6|4{Ng9#R%2;Vz6)tApYyEA|Bp0)p#FF~ATo8Iv96IqbT zC%swif6^n(!LJG)XjTXr#3$c)u7Gp*C#!6dYRJq!nm-7bOXD}K1seYZ9$nrEDKo=Z z@G$iXFApeqzgd@)c9X#{KlLq&7O#GDTvkE@1CJi)7HIUV-imUY$+ zO~+H(ATFMo<%L&DA@7}E*_Hy9aI;KF0c1u|ppsD6q9F=X)J4!x66)0m4#5}L{IgzB z2^k1P@w;A3dJA6Q(XYDr4D}R&O#-VhdQyiEqWjp4oHY9S$T7M|_x+$7_<2a%3=JR5 zC^+#HG^zjk$gz65cS5i5(G@ijEGP-SO#J+;7YSiDhu~sTcrooX8 z(g-GCcZDwEHIFC3u@f|c2^f^{U_H^mUQQm%-pz>W;Rr5+NW)pFokG|!p%hx1#2XE- zPLalV=FIHJ*Hq2ArIbO zc7IwB23?Jw&<24Lz((MkZG932rLMqNzTigE=wsG_&VbxV&_aS1$DnicoI!$$asXBN z;u0@q@$!c9W_a@Na<}_-2Jq3MQk+Ie|!j$Zt** zuBcTs(9x@K6hz7g#o<dIF0@JP)SQ*$b0C9Y4@K*b<^P&7b6ouXdN+Tr878fb||56}+5 z8l)UglAQJ=E_=P+0vi5@A4en|76Q?ZZ`yLQP;<=wy4~nr8c=!9MyS7taJMTo0A(vx znp+E>8RIJNA4MtXqaNcCz-l~z#g`Ov8^;(Z#wEk-yH-WdBkf#MBqIN1yPGsnkTBy! zCYvA*1Y<%jP=d5h_l9c zF(Anr7woz0XreIpCn|^oyh(`OScGwjs;CT87OIK!stF9|(zQYrnEaQ8n$+vO38kD( zQnW>)(ztwyAyeg4q;Sbel@&v{yD$qP!31k>ST;e!(@}&O&4Pm<9Vy-W1A1Cd{lY(ZWF|nrOjym9b-;wNg*I zJQ`fqGSymz!WR*Pltpcdn5vYrYOv?P0<+!R;6RD~bW`hE2KBiB5&wf{s!1J3Q!x+< z*A=Ql0n=v5#`JtbO498Z1px;!sdd%KHA#pDL@m{eMpPSlb3(VMuX zz_Eyfz@BuHA&48lK`|~FlR?+Y=^G}lNkK0znfc@18PGUV3`1K*@`AclQnI(iVbXvf zAmO7EYSP>@g_Kixt5-!V9a1C3yUuZjlA0Yy&Z1im|`WA`j!Q~+*p z{w7ES)9U3SVG>slKYoKmrqJNb0IiG1P%rU^0MORL>Z0tAUjBdsqH)PQP$ucDd3Xb5 zbB4Ogh@VD~m}4g%EQciLS@@sc$yyK#rNCH^k;|9C1zE8!J6>+X8q^&Qm^{x5>i!2T zY};7bY3F9UES!+WqHw3ofI3yOq=4Sh;c=an3i6HSBM5-{(>TP zBrnyg$E{JI`bou(r44pCaMa)+mbe0MRWgS5C)+KLd7~`l3+*7|7IE&EeSfP90_&?H zunkT~PZ6s#96W0v9?6R$B5B;&da4%R`t z<=b1b<5L$k)Pk?)MaldZJj?P77%BrmR6kXYlPRDwK0a+^DD^d9(`5+2qdyk)gCs-I zjZSOjO0;(2{zNBCUboWSm;gFD5c;MxBXnDcdi{c-K5915RzI0uQvKW|QsBBj(T>t9 zHIy57V+3dji|E}U!A=)D%W8f%a1HizPCh$VE=cgo6wZ>@OcT0DUM`atG<8ihUf zCZ3on3J+O$h3=6^Y)bZEgG3OZ4F+XtLBzfqWDv;D;1cG1-?=G8czKuoft?fRsCEte4D7Tw#3FEdx7-2$eHD#=zMkP43@x##(H^lV|E zdR{8eR+4HaEEWhz(}fjaIhe`>Z#}}$jNz<4IS-pQ30z(G%oA9x+xp48;i?iocO%&d z_?m;4vn_NIa!gIefl&jQi8#>6TKZEd=+cdr+n~i_5c0J-B*_M?A!-U>+tvDrt8lu3${26Ez|eZg1+5TM0e5 zG@{mJb-|wY&931@4OT0#3u$a&-gD3(aurP(Sh4FKr5KQLrjv{a$8QcmtT3=fXb4W$EO$t>Z+^A8mOg6 zV!86dVppbYb&P|CRx`acfpNzex#W%4)-mK@O<$2y8utas&Fd24X%V2EF^S&G13tXp z5Kgk~?Xv9xP0oPm{^Xfil2GG$4Tu3*yh_y7*q$Ij#`|5D7ZD>>(1=L|AIbZ9YI~7f zl;&e@lKc@@;nH(~?SH`YbJu-6MT0r1OUsZCw0xoCTeD9pe7?Gf!W@d+0n%P7`-e$- zAgkw3EBq?^X#?q|!rJ;Yq&nx#ozyn`*S9ruYvKxh+p1;J|*EHBIPtu870*#lzq zlk3ubmv;|~8ohd?HvCh!oRL?eEf1g*%>$p%7f3mPOOy*e@1jYj2pOh!Qz zu!qq*kzrm1~bpw)$}~Gp203VmOwTu80tY zq3Y+RYEg83@?P;Z4Z5L3QTdl_~Du1C+p{Vp_$N=xmk&tv2Yhyj@EOD)S4UHCsn~@=&2;QdWB>v214VX7yV)s05};~@L5v10 zzYvv{Qlfz@ZSA2Wl61r`cv%$Ti8T%q7a!UrLuS>2J;$c@ddFMf`wih3)3ziA2`vs^tqNlOuD0fc!=JMt%S) z(CGqIc^l*sn;(=_bcMW$l4{r4jDrmS)fs;QR7)BV`G67h%2LTG1sf<=)6|*pFFf21 zGzCd9@1)Zi2NvBb=1ci9#sUKY`APtY$X|Z!Aa_b=!)V<7PMy;{iWU3NqsyQpey~5A zq?W|re6qB_l0O+Q$%4A|l6X`__VAPNGQBqL&<2+H!DS_x$ry2hfFfKpl)EOUw|WuB z0W{e1E51d#SjJ)i9h-uK1fx*KV4lTl0j{{DgZX^Hd@~=+dZ=Euve{Oq<-DdMuPBjFU$PCMMZS4%ZT7!b!BwCpT1kaDXS9 zs}-!ANwKTxGF?&wQu0?_KC40-n93Jg8D=&~p*!Q+5QMz;WcP|yPL(2>nMMI>a5Wic z_Qdc&07GkQaj5bt4QhL`wqzxgMszPf!$o#=mZD&A?N~SIB0O}d;J1@b<3-n*Ogdm516&u}&C6)}1QO+fw z-55AgtPnQ-Wi}GcPmnG!WeLzETKYy)%6phlE?bmzcQ=le((4LA)WDEqYG(tQQhZ>n z@^~@O4KfEx!2~woN&U;ODv40sZiITX2~LV1^4wIQdcV0pCY9PneTaNH@q>pA9%`{E zUZCZ2<8AGJCQ$G{{Q6=gj_*K8q!i2uYQY2^xw$S_l){!+mr)J9JfP$K22<|ZrG6|! zZkmt^Jo?4rXMq5OfF?>aD1x6!kdPQ?3dI>!CS_*7L&*qo$``s<@&)RVhfuIij`vMK zNvu)G_fEL0iXqc`-v#TQxbY=FCmW`Li94Krdf_7%c~m81<5H7(k!Mw4hl+Vj+v#Gj zLb^Zt&P%>KTnZk9!J)TkY6YQ%kyO}gx+y!FuBn1kup>OuQy|1X&99hIyzhdWSv+~T zi#iStychcMpb8crN*qV&1=Wck-a&V4k}Hu_=E%ChGNTB05(RR!6TLE$6o#I_sn_JV zbzQ9iH+|BANSL;#8y;0Jh1GnZ^r$^NQuNUfSE^!q)YL>!tz+~KwRpTR>Mad31Iy9PLCGgz8L+yIvWsw|46fgz5*%6ovk3V?Hy9Bmh-jva6A z#U52Rfq6|@v58Q_J4*cAgy=?P{$xstH=cwPKv*K5D>h8rz`|nY3N`|caMPFnC>9>s z3K4-*XU`2MvhKi|bm1W>GN*BH+Ek58cX0W#nw~N3%m*`aRiJ;~>c{|9jEeTeL3N;n zhN)2n1Jo#?VQM(-?uj!%RRuIeT@Gy?QTffZk|yS={AovvwZWsrUrZ;_#>eWKM~KD> z@-o=`cB**+F*R~1D+uzi!r?_L7)G^Otgt3boHWB;lSnydAdbu zpcX7o$IFF9K>SZ59U$V$cmyL5L0`;bYh1JiIyG;^k~tC z5MS&_?!|V5*{axcXgJ8(8Dn^}3C-g{r6qVtoKc6WHbSOKM)-nA{$(0UEIK()PumSX~%U_|_zGBAMQoDrBp6 ziNvQ4gY@lSOE;6|>p8u7i{XNxO3R_`bva!{bZoGu(&+wbp8X3=tKbl&RJ~aEV?IO5z7& z57fgT^Cw@d^;lBUKy%zON+Eo z>^}Ky;Ee+y_;xkW@jtN2NkKB^&c$yx1C9UEPJ)ce=*$;;WTLu$tt4YNa!$UCQ|t8w zsU%=}EX0vz^-QwMN&zZM9$__6sVK=jNEJ;8n0I$@`RQHZY-nLOOIq5LgkZd{il4EMtWfF4g?`1=CRB1Ts?f0tq~? z;%Z_o8qPqvKku0CmsN6smhwdsuTNfl)XJ+ORfd~CsaJ`*OQCw`@*90TdF+dO!5oCj zi2ipUG)Eg!&>C4Gai=9uVZ~3GkFO7NCUM}7+BvLy_0FIkES8IDKUoIYdB4~t6ZB0-bOR(Ig}^r& z6Wij69Wufv=qOyH3n4g9*mNmEI-^vPl45MRm@Toe>4^tha2PF^jHgj>7r%G+xy+1) zpl9Yy${!*l5qvIP5`xo2pF&hZ)LI&1!W<_&99b=KWvuM*^1wq7#OJB0mdbrd2J4@U zj0(w8<46k@6P*)Lxj629@*>(oR;o_A0VQ!;QfUI&wxC828lmTr!DeP;Ip$3-DIM(8 zLbE_t{1Cjc>X{@P;LTG$pE)l{&`azPG-(U>X5xu0j9AACbpM3m#jTq#P_c%GO^R29 zrO%_ZR#2g&!D8-AvJ(2CuBx%>bCXGm{HQSBHXsu``2~xljg~w*G?sAiY)J((7$=ya zXw&Nqer}>;jN?#r=i?vncx2=s?VJ?9v!C1^>l> zBSi*?)3dPTBzAyKO4UO_G7c_w|lNfGA>CLPg~bjUZ`ws!Ek`hao|M%?Meg zSq@%WF_2<7@cA#t|I{e}dCfRb0;%vm>3xBWFcGf<8N|vi{QW%bf5Lu?uDj zDl?%)3YO#s)F6_f7+c|!j{%A@f-YPUFCbtVQ%wh%_6JM5Otz>C`)(LS9D0wfP^n=7 z4Dm~b=JxV{A@4UPWi&}N>wPrTlnhN0mdYQBR6l9%A%hFq*tNH`w6u;yJEgpMM5=FHgKuP^%n=jf6 zPYap##;m^}EIUnDvj^1is8h-QLah2o^Rhwtc!&Or~rCJEr!qK4iHU<`* zIhin2E_VP|1L0FeVVEe8L2*T@;9|`wXweBdo9@c@=HjzZmGlH~^?zoLsnYp0Y93c> zArrwr!3_o^%vcyzwoN$MZ>#~+_P7p^_EH<7ELubnn4}#^{|waK#tM*j6F&o8G?T9< z0pbD3XDdM}T9~{{{>-$Y=P{&_+7M-RjZ%_TlRgVgv{#TKHK1pB2CU26{ODkkX3!Qj zL|14m(GAN~rukH9%x-GNvwM|%87~dy=_G7{o;6~Co6HBS;OZlkMNEQ_AtnE-POVL= zPCU5#n%}O)DL2W#8|_ { - diff --git a/src/components/CCAgents.tsx b/src/components/CCAgents.tsx index f72b154ee..8cda1a8d5 100644 --- a/src/components/CCAgents.tsx +++ b/src/components/CCAgents.tsx @@ -469,6 +469,7 @@ export const CCAgents: React.FC = ({ onBack, className }) => { variant="outline" onClick={() => setCurrentPage(p => Math.max(1, p - 1))} disabled={currentPage === 1} + aria-label={`Go to previous page (page ${currentPage - 1} of ${totalPages})`} > Previous @@ -480,6 +481,7 @@ export const CCAgents: React.FC = ({ onBack, className }) => { variant="outline" onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))} disabled={currentPage === totalPages} + aria-label={`Go to next page (page ${currentPage + 1} of ${totalPages})`} > Next diff --git a/src/components/ClaudeCodeSession.tsx b/src/components/ClaudeCodeSession.tsx index a9b9e5891..05f33bf97 100644 --- a/src/components/ClaudeCodeSession.tsx +++ b/src/components/ClaudeCodeSession.tsx @@ -1333,10 +1333,13 @@ export const ClaudeCodeSession: React.FC = ({ animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: 20 }} className="fixed bottom-24 left-1/2 -translate-x-1/2 z-30 w-full max-w-3xl px-4" + role="region" + aria-label="Prompt queue" + aria-live="polite" >
-
+
Queued Prompts ({queuedPrompts.length})
@@ -1344,21 +1347,28 @@ export const ClaudeCodeSession: React.FC = ({ whileTap={{ scale: 0.97 }} transition={{ duration: 0.15 }} > -
- {!queuedPromptsCollapsed && queuedPrompts.map((queuedPrompt, index) => ( - + {!queuedPromptsCollapsed && ( +
    + {queuedPrompts.map((queuedPrompt, index) => ( +
    #{index + 1} @@ -1377,12 +1387,15 @@ export const ClaudeCodeSession: React.FC = ({ size="icon" className="h-6 w-6 flex-shrink-0" onClick={() => setQueuedPrompts(prev => prev.filter(p => p.id !== queuedPrompt.id))} + aria-label={`Remove queued prompt: ${queuedPrompt.prompt.slice(0, 50)}${queuedPrompt.prompt.length > 50 ? '...' : ''}`} > - - ))} + + ))} +
+ )}
)} @@ -1431,7 +1444,8 @@ export const ClaudeCodeSession: React.FC = ({ }} className="px-3 py-2 hover:bg-accent rounded-none" > - + @@ -1464,7 +1478,8 @@ export const ClaudeCodeSession: React.FC = ({ }} className="px-3 py-2 hover:bg-accent rounded-none" > - + @@ -1496,6 +1511,9 @@ export const ClaudeCodeSession: React.FC = ({ size="icon" onClick={() => setShowTimeline(!showTimeline)} className="h-9 w-9 text-muted-foreground hover:text-foreground" + aria-label="Session timeline" + aria-haspopup="dialog" + aria-expanded={showTimeline} > @@ -1514,6 +1532,8 @@ export const ClaudeCodeSession: React.FC = ({ variant="ghost" size="icon" className="h-9 w-9 text-muted-foreground hover:text-foreground" + aria-label="Copy Conversation" + aria-haspopup="menu" > @@ -1556,6 +1576,8 @@ export const ClaudeCodeSession: React.FC = ({ size="icon" onClick={() => setShowSettings(!showSettings)} className="h-8 w-8 text-muted-foreground hover:text-foreground" + aria-label="Checkpoint Settings" + aria-haspopup="dialog" > diff --git a/src/components/CustomTitlebar.tsx b/src/components/CustomTitlebar.tsx index 3342959b0..ac3e3dfd6 100644 --- a/src/components/CustomTitlebar.tsx +++ b/src/components/CustomTitlebar.tsx @@ -91,6 +91,7 @@ export const CustomTitlebar: React.FC = ({ }} className="group relative w-3 h-3 rounded-full bg-red-500 hover:bg-red-600 transition-all duration-200 flex items-center justify-center tauri-no-drag" title="Close" + aria-label="Close window" > {isHovered && ( @@ -105,6 +106,7 @@ export const CustomTitlebar: React.FC = ({ }} className="group relative w-3 h-3 rounded-full bg-yellow-500 hover:bg-yellow-600 transition-all duration-200 flex items-center justify-center tauri-no-drag" title="Minimize" + aria-label="Minimize window" > {isHovered && ( @@ -119,6 +121,7 @@ export const CustomTitlebar: React.FC = ({ }} className="group relative w-3 h-3 rounded-full bg-green-500 hover:bg-green-600 transition-all duration-200 flex items-center justify-center tauri-no-drag" title="Maximize" + aria-label="Maximize window" > {isHovered && ( @@ -146,6 +149,7 @@ export const CustomTitlebar: React.FC = ({ whileTap={{ scale: 0.97 }} transition={{ duration: 0.15 }} className="p-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors tauri-no-drag" + aria-label="Agents manager" > @@ -159,6 +163,7 @@ export const CustomTitlebar: React.FC = ({ whileTap={{ scale: 0.97 }} transition={{ duration: 0.15 }} className="p-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors tauri-no-drag" + aria-label="Usage dashboard" > @@ -178,6 +183,7 @@ export const CustomTitlebar: React.FC = ({ whileTap={{ scale: 0.97 }} transition={{ duration: 0.15 }} className="p-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors tauri-no-drag" + aria-label="Settings" > @@ -192,13 +198,19 @@ export const CustomTitlebar: React.FC = ({ whileTap={{ scale: 0.97 }} transition={{ duration: 0.15 }} className="p-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors flex items-center gap-1" + aria-label="More options" + aria-expanded={isDropdownOpen} > {isDropdownOpen && ( -
+
{onClaudeClick && ( @@ -910,6 +911,9 @@ const FloatingPromptInputInner = ( size="sm" onClick={() => setModelPickerOpen(!modelPickerOpen)} className="gap-2" + aria-label={`Select model, currently ${selectedModelData.name}`} + aria-expanded={modelPickerOpen} + aria-haspopup="listbox" > {selectedModelData.icon} @@ -931,6 +935,9 @@ const FloatingPromptInputInner = ( "hover:bg-accent", selectedModel === model.id && "bg-accent" )} + role="option" + aria-selected={selectedModel === model.id} + aria-label={`Select ${model.name} model`} >
@@ -965,6 +972,9 @@ const FloatingPromptInputInner = ( size="sm" onClick={() => setThinkingModePickerOpen(!thinkingModePickerOpen)} className="gap-2" + aria-label={`Select thinking mode: ${THINKING_MODES.find(m => m.id === selectedThinkingMode)?.name} - ${THINKING_MODES.find(m => m.id === selectedThinkingMode)?.description}`} + aria-expanded={!thinkingModePickerOpen} + aria-haspopup="listbox" > m.id === selectedThinkingMode)?.color}> {THINKING_MODES.find(m => m.id === selectedThinkingMode)?.icon} @@ -1029,6 +1039,7 @@ const FloatingPromptInputInner = ( disabled={!prompt.trim() || disabled} size="default" className="min-w-[60px]" + aria-label={isLoading ? "Cancel current request" : "Send message"} > {isLoading ? (
@@ -1083,6 +1094,8 @@ const FloatingPromptInputInner = ( size="sm" disabled={disabled} className="h-9 px-2 hover:bg-accent/50 gap-1" + aria-label={`Select model: ${selectedModelData.name} - ${selectedModelData.description}`} + aria-haspopup="menu" > {selectedModelData.icon} @@ -1114,6 +1127,7 @@ const FloatingPromptInputInner = ( "hover:bg-accent", selectedModel === model.id && "bg-accent" )} + aria-label={`Select ${model.name} model: ${model.description}`} >
@@ -1149,6 +1163,8 @@ const FloatingPromptInputInner = ( size="sm" disabled={disabled} className="h-9 px-2 hover:bg-accent/50 gap-1" + aria-label={`Select thinking mode: ${THINKING_MODES.find(m => m.id === selectedThinkingMode)?.name} - ${THINKING_MODES.find(m => m.id === selectedThinkingMode)?.description}`} + aria-haspopup="menu" > m.id === selectedThinkingMode)?.color}> {THINKING_MODES.find(m => m.id === selectedThinkingMode)?.icon} @@ -1180,6 +1196,7 @@ const FloatingPromptInputInner = ( "hover:bg-accent", selectedThinkingMode === mode.id && "bg-accent" )} + aria-label={`Select ${mode.name} thinking mode: ${mode.description}`} > {mode.icon} @@ -1245,6 +1262,7 @@ const FloatingPromptInputInner = ( onClick={() => setIsExpanded(true)} disabled={disabled} className="h-8 w-8 hover:bg-accent/50 transition-colors" + aria-label="Expand prompt editor" > @@ -1265,6 +1283,7 @@ const FloatingPromptInputInner = ( "h-8 w-8 transition-all", prompt.trim() && !isLoading && "shadow-sm" )} + aria-label={isLoading ? "Stop generation" : "Send message"} > {isLoading ? ( diff --git a/src/components/SessionList.tsx b/src/components/SessionList.tsx index 9575c015c..f94dd4150 100644 --- a/src/components/SessionList.tsx +++ b/src/components/SessionList.tsx @@ -105,6 +105,20 @@ export const SessionList: React.FC = ({ "p-3 hover:bg-accent/50 transition-all duration-200 cursor-pointer group h-full", session.todo_data && "bg-primary/5" )} + role="button" + tabIndex={0} + aria-label={`Open session from ${session.message_timestamp + ? new Date(session.message_timestamp).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric' + }) + : new Date(session.created_at * 1000).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric' + }) + }${session.first_message ? `. First message: ${truncateText(getFirstLine(session.first_message), 60)}` : '. No messages yet'}${session.todo_data ? '. Contains todo items.' : ''}`} onClick={() => { // Emit a special event for Claude Code session navigation const event = new CustomEvent('claude-session-selected', { @@ -113,6 +127,16 @@ export const SessionList: React.FC = ({ window.dispatchEvent(event); onSessionClick?.(session); }} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + const event = new CustomEvent('claude-session-selected', { + detail: { session, projectPath } + }); + window.dispatchEvent(event); + onSessionClick?.(session); + } + }} >
diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 06d338a0c..b66bdbbf7 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -387,8 +387,9 @@ export const Settings: React.FC = ({ {/* Content */} {loading ? ( -
+
+ Loading settings...
) : (
@@ -633,7 +634,7 @@ export const Settings: React.FC = ({
-

+

How long to retain chat transcripts locally (default: 30 days)

@@ -648,6 +649,7 @@ export const Settings: React.FC = ({ updateSetting("cleanupPeriodDays", value); }} className="w-24" + aria-describedby="cleanup-description" />
@@ -792,6 +794,7 @@ export const Settings: React.FC = ({ size="sm" onClick={() => addPermissionRule("allow")} className="gap-2 hover:border-green-500/50 hover:text-green-500" + aria-label="Add new allow rule" > Add Rule @@ -816,12 +819,14 @@ export const Settings: React.FC = ({ value={rule.value} onChange={(e) => updatePermissionRule("allow", rule.id, e.target.value)} className="flex-1" + aria-label="Allow rule pattern" /> @@ -840,6 +845,7 @@ export const Settings: React.FC = ({ size="sm" onClick={() => addPermissionRule("deny")} className="gap-2 hover:border-red-500/50 hover:text-red-500" + aria-label="Add new deny rule" > Add Rule @@ -864,12 +870,14 @@ export const Settings: React.FC = ({ value={rule.value} onChange={(e) => updatePermissionRule("deny", rule.id, e.target.value)} className="flex-1" + aria-label="Deny rule pattern" /> @@ -935,6 +943,7 @@ export const Settings: React.FC = ({ value={envVar.key} onChange={(e) => updateEnvVar(envVar.id, "key", e.target.value)} className="flex-1 font-mono text-sm" + aria-label="Environment variable key" /> = = ({ value={envVar.value} onChange={(e) => updateEnvVar(envVar.id, "value", e.target.value)} className="flex-1 font-mono text-sm" + aria-label="Environment variable value" /> diff --git a/src/components/StorageTab.tsx b/src/components/StorageTab.tsx index aa4071cb3..be887ca03 100644 --- a/src/components/StorageTab.tsx +++ b/src/components/StorageTab.tsx @@ -529,8 +529,9 @@ export const StorageTab: React.FC = () => { onClick={() => loadTableData(currentPage - 1)} disabled={currentPage === 1} className="h-7 text-xs" + aria-label={`Go to previous page (page ${currentPage - 1} of ${tableData?.total_pages || 1})`} > - +
diff --git a/src/components/TabManager.tsx b/src/components/TabManager.tsx index 5f63badba..31285a6e3 100644 --- a/src/components/TabManager.tsx +++ b/src/components/TabManager.tsx @@ -75,6 +75,10 @@ const TabItem: React.FC = ({ tab, isActive, onClose, onClick, isDr isDragging && "bg-card border-primary/50 shadow-sm z-50", "min-w-[120px] max-w-[220px] h-8 px-3" )} + role="tab" + aria-selected={isActive} + aria-label={`${tab.title} ${tab.type} tab`} + tabIndex={isActive ? 0 : -1} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} onClick={() => onClick(tab.id)} @@ -103,6 +107,7 @@ const TabItem: React.FC = ({ tab, isActive, onClose, onClick, isDr )}
@@ -120,9 +125,10 @@ const TabItem: React.FC = ({ tab, isActive, onClose, onClick, isDr (isHovered || isActive) ? "opacity-100" : "opacity-0" )} title={`Close ${tab.title}`} + aria-label={`Close ${tab.title} tab`} tabIndex={-1} > - +
@@ -395,6 +405,7 @@ export const TabManager: React.FC = ({ className }) => { "bg-background/80 backdrop-blur-sm shadow-sm border border-border/50" )} title="Scroll tabs right" + aria-label="Scroll tabs to the right" > diff --git a/src/components/TimelineNavigator.tsx b/src/components/TimelineNavigator.tsx index 2cc0ce154..2fd149ded 100644 --- a/src/components/TimelineNavigator.tsx +++ b/src/components/TimelineNavigator.tsx @@ -281,11 +281,12 @@ export const TimelineNavigator: React.FC = ({ size="icon" className="h-6 w-6 -ml-1" onClick={() => toggleNodeExpansion(node.checkpoint.id)} + aria-label={isExpanded ? "Collapse checkpoint" : "Expand checkpoint"} > {isExpanded ? ( - + ) : ( - + )} )} @@ -348,8 +349,9 @@ export const TimelineNavigator: React.FC = ({ e.stopPropagation(); handleRestoreCheckpoint(node.checkpoint); }} + aria-label="Restore to this checkpoint" > - + Restore to this checkpoint @@ -367,8 +369,9 @@ export const TimelineNavigator: React.FC = ({ e.stopPropagation(); handleFork(node.checkpoint); }} + aria-label="Fork from this checkpoint" > - + Fork from this checkpoint @@ -386,8 +389,9 @@ export const TimelineNavigator: React.FC = ({ e.stopPropagation(); handleCompare(node.checkpoint); }} + aria-label="Compare with another checkpoint" > - + Compare with another checkpoint diff --git a/src/components/Topbar.tsx b/src/components/Topbar.tsx index eeb5bc743..caea5da96 100644 --- a/src/components/Topbar.tsx +++ b/src/components/Topbar.tsx @@ -85,7 +85,7 @@ export const Topbar: React.FC = ({ const StatusIndicator = () => { if (checking) { return ( -
+
Checking...
@@ -100,8 +100,11 @@ export const Topbar: React.FC = ({ size="sm" className="h-auto py-1 px-2 hover:bg-accent" onClick={onSettingsClick} + aria-label={versionStatus.is_installed + ? `Claude Code is installed, version ${versionStatus.version || 'unknown'}. Click to open settings.` + : "Claude Code not found. Click to configure installation."} > -
+
= React.memo(({ animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="sticky bottom-0 left-0 right-0 p-2 bg-gradient-to-t from-background to-transparent" + role="status" + aria-live="polite" + aria-label="Claude is processing your request" >
diff --git a/src/components/claude-code-session/PromptQueue.tsx b/src/components/claude-code-session/PromptQueue.tsx index b2b2f546e..cdff4326f 100644 --- a/src/components/claude-code-session/PromptQueue.tsx +++ b/src/components/claude-code-session/PromptQueue.tsx @@ -30,20 +30,23 @@ export const PromptQueue: React.FC = React.memo(({ animate={{ opacity: 1, height: 'auto' }} exit={{ opacity: 0, height: 0 }} className={cn("border-t bg-muted/20", className)} + role="region" + aria-label="Prompt queue" + aria-live="polite" >
- Queued Prompts + Queued Prompts {queuedPrompts.length}
-
+
    {queuedPrompts.map((queuedPrompt, index) => ( - = React.memo(({ >
    {queuedPrompt.model === "opus" ? ( - + ) : ( - + )}
    @@ -71,13 +74,14 @@ export const PromptQueue: React.FC = React.memo(({ size="icon" className="h-6 w-6 flex-shrink-0" onClick={() => onRemove(queuedPrompt.id)} + aria-label={`Remove queued prompt: ${queuedPrompt.prompt.slice(0, 50)}${queuedPrompt.prompt.length > 50 ? '...' : ''}`} > - +
    + ))}
    -
+
); diff --git a/src/components/claude-code-session/SessionHeader.tsx b/src/components/claude-code-session/SessionHeader.tsx index b48a24823..968679460 100644 --- a/src/components/claude-code-session/SessionHeader.tsx +++ b/src/components/claude-code-session/SessionHeader.tsx @@ -64,6 +64,7 @@ export const SessionHeader: React.FC = React.memo(({ size="icon" onClick={onBack} className="h-8 w-8" + aria-label="Go back to session list" > @@ -107,8 +108,8 @@ export const SessionHeader: React.FC = React.memo(({ open={copyPopoverOpen} onOpenChange={setCopyPopoverOpen} trigger={ - } content={ @@ -143,14 +144,15 @@ export const SessionHeader: React.FC = React.memo(({ "h-8 w-8 transition-colors", showTimeline && "bg-accent text-accent-foreground" )} + aria-label={showTimeline ? "Hide timeline" : "Show timeline"} > - +