From 916555238ac85ac354b599001e5f9b6f3f1c1927 Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Mon, 27 Jan 2025 00:27:48 -0800 Subject: [PATCH 01/17] chore: update eslint@9 add: new logo and icon --- .eslintrc.json | 18 ------------------ .vscode/settings.json | 4 +++- eslint.config.mjs | 37 ++++++++++++++++++++++++++++++++++++ jsconfig.json | 4 ++-- media/icon.svg | 1 + media/spec.png | Bin 57981 -> 41081 bytes media/vscodeignore/spec.png | Bin 0 -> 57981 bytes package.json | 33 +++++++++++++++++--------------- vsc-extension-quickstart.md | 15 +++++++++------ 9 files changed, 70 insertions(+), 42 deletions(-) delete mode 100644 .eslintrc.json create mode 100644 eslint.config.mjs create mode 100644 media/icon.svg create mode 100644 media/vscodeignore/spec.png diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index e49787a..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "env": { - "browser": true, - "es6": true, - "node": true - }, - "rules": { - "constructor-super": "warn", - "jsdoc/no-undefined-types": 1, - "no-const-assign": "warn", - "no-this-before-super": "warn", - "no-undef": "warn", - "no-unreachable": "warn", - "no-unused-vars": "warn", - "valid-typeof": "warn" - }, - "plugins": ["jsdoc"] -} diff --git a/.vscode/settings.json b/.vscode/settings.json index 8840ca7..ba0f75f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,8 +6,10 @@ "lanly", "susres", "Typecheck", + "vscodeignore", "webaudio" ], "editor.rulers": [120], - "explorer.sortOrder": "type" + "explorer.sortOrder": "type", + "terminal.integrated.defaultProfile.windows": "Command Prompt" } diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..4abebfa --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,37 @@ +import globals from "globals"; + +export default [{ + files: ["**/*.js"], + languageOptions: { + globals: { + ...globals.commonjs, + ...globals.node, + ...globals.mocha, + ...globals.browser + }, + + ecmaVersion: 2022, + sourceType: "module", + }, + + rules: { + 'comma-dangle': ['error', 'never'], + 'eol-last': ['error', 'always'], + 'max-len': ['error', { code: 120 }], + 'no-throw-literal': 'warn', + 'no-trailing-spaces': 'error', + 'quote-props': ['error', 'as-needed'], + "constructor-super": "warn", + "no-const-assign": "warn", + "no-this-before-super": "warn", + "no-undef": "warn", + "no-unreachable": "warn", + "no-unused-vars": "warn", + "valid-typeof": "warn", + curly: ['error', 'multi-or-nest'], + eqeqeq: 'error', + indent: ['error', 2], + quotes: ['error', 'single', { allowTemplateLiterals: true }], + semi: ['error', 'never'] + }, +}]; \ No newline at end of file diff --git a/jsconfig.json b/jsconfig.json index c08832a..1b5e6b9 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,9 +1,9 @@ { "compilerOptions": { "module": "commonjs", - "target": "es6", + "target": "ESNext", "checkJs": true /* Typecheck .js files. */, - "lib": ["es6", "dom"] + "lib": ["ESNext", "dom"] }, "exclude": ["node_modules", "dist"] } diff --git a/media/icon.svg b/media/icon.svg new file mode 100644 index 0000000..9b4e3a5 --- /dev/null +++ b/media/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/spec.png b/media/spec.png index 5e105ef30193f19d1180a9e8130c2c8c4a9c0ee1..c0e375224726daeb811cf47a6fb2658dcc189348 100644 GIT binary patch literal 41081 zcmV*IKxe;+P)q^x7wq;5A!`O3iX`Q9pF7dg)e&*EwoSDzTYh+8!QckK} zU`-0MdCe_@dphy5B=je{n(utomGAh_Yu|L)T>Uv6AG|_vufP_6{Qd9c*yPbu$7X{= zN2X_+_8&MfoJ=K$x4pb;J%H5!TmT9Kh-|K_qQA3)ZtQBur8yUXMJjblTV^^ep~*4= z740oRS0|pyrnJY~o4$YbCqMIv6Hh)_cUa7E!z%>$zZ}m!{j@ZDc>ku**yy?Gz|2Y6 zxzI)}7#M7gr#sG7MMJ+P=r*C_h@oPSp&<<7Lo)%Sb2)Q1jtR`_okp)0?;9SZ&!yb} zmb{o%g#dsCFVNQm$TDup#)AHfFL~o%U;oJ$j&o5}@e0BH565$lKh~HG&1{NKPnJ})Fo7V~%zp#WgW5~!~W3Arcg?We^ z$fmJ77^*gC3dB|qL6-a$M0oxBA-Y+0Lo%#aUch;KtkMZV;OKx82qiiA0B9e);9~2M-_Im`o;zw!OS-7{D+9KTHF&qoo<2Sv^4S zj#&>%#{^ccU;r>78~AE21JyJV{_e}Mc4feIL37C8h}9n1g` zpcn>T$YgkCu2Q4k==I`+8s6tyecc( zwr#0wrvShWK2T>H0ASA0ar^qA_x;0nzV{z>)VD6YLU4Z~{NbLvyP}h$XT&Eb&Wucs zZ;>LAwQ?ZbcS;tG8zBi{5N7>u-0P9?Og4i<(HM_RO~Y914<`e)nC6sK%W;dlfx1TiMr(&-QiDWwGk7S*>n`I{wzBf2M z*f}x9Y^qLEgAbn?UQX{%)-YNNK(w|X+uDL}2d407Ce1rWMxoa^?j$0dzhMo&U|)V3yPY{jy6CN00`eRl2g}w|3@oVuUn@bWA+tzh2U)P^y81q!J{MV=f+0P!+i9_ z`1JI8IT~K!3dB0jR;gi7lPU$LrFugM0{3=8w6;S5<3kgNaqr7}*l^Z2C98!j1YLc? zI{fPbi)m{Zwz&7AtmBqkwwN0On9x+bI5WcrbCIwmB7EVbb@Z3<+S<=T{^a#wwx=CG z35W3P+#C)h5*$k;tlDB{JSPC$(b9|?hnLY?qWMSFltk6)Q*pn+2gBb8P~JrGt!3>u zec@-ff3Ri)>%{+hg1hgxzx5wII<{%&%R5)j%*?Ev4a^R&Q_`>fup##hYZ5u_yn5vb z%fa8$1oY;G_|FH&@xvFlvu?)&)=)y6I)VkGO~6y5Ez~g1XlqVEKh&Wnl*H~?mF)EFtc*DN(+w*Vz3Wzd-0!%Fuu5BCr9Sz z?Xs-OcJ_9(MwO(jC=a}X{}-}PgR|K z1VL(V!^19tyXOMfmP+w~$w^1;F+(E4<(t;fcd}B6>7bStv#RS@gAba|1EPF>zdZAV z8$bG$Z+^~6eVpQd48iT(zFpqGZ{N@(k3PCCk(gh9cw}T{DwQ5SI5M)9^F-yqLpF~` z)~uxsx?DS971UzoszM@&!9Gw!BX(p``1r^{>>V2ioU7G~&pfh>H>_DsU)J1c)+<@S zeQw}ZEr)sCDCWk@*qcdVe`v0Rt(Anp7f)PE?{&aRnql=V$lr*u_9omMoW(P-7!J(L za1FW6vb|fD57M_9{n(US+OC5VNCLTLf1coo@ME7o@%MjtZ27Tgyomp=2=3tr9&jgv z(_2ELhhCGM9zP{D7hZiht@)nZf4FgccD7$@(v+?;jrFF=uj*Eo33k)zOa5mKJyzHe49oCmC%8Y{b5~b%@<`D>MUn8ZI}jN-DTl_5 zx)5@WZB$mgIX5)|UJpnJ1W3ol`}S}!8neryCM(v|;KPH`P$Ovsa=!F1*0Q|tCj(+9wXIC#NGYcG>pkjkZ?!T`&HhJXK z$oSZ+)4}OY+1bE4Ob3^(&+7hDby;?E`3zKAjcT1`l>HX~^mGOpA_zff69Shn>!%+? zZH-YiXP87fgBv7+{{z)mxq4LY<#n#+mQ-rK9h5P+4Z(B3D{kivt zckO&*|73X8`MTt(=Amv&P!6U=yFotL=z$9YvLGbbz=a3*aw?m(%EFQFpCH0J*R7(j zD&8g4$4UBHuv9j9J(%ff$IYQY`AT4ACIFnvLzcK;<2u})wXiwb)bp4j2U55 zcki8;o_^y`4~~gz-8ra&9UFX?!eQIJ!^{Y3Dgv5{pspckXz&z_DT^SR>yoTWe=r_C zFoH|7aemsP*|LY#P-tr*os;x6p{Vdo%-}koAMYMmhI3lmXt=j`LFy>Ns*UxsmUy$_ zMDc46D>mEeS$R4mp1>LL6t3?d!1=AMv~_TR8hyTci{}?d$LO-i93JvidTH79#S}Cw zaL^zErbkCkthZo0NH^SYgX6^i^!a~y^?R;;@10LS|NMs(RqHI=X8?!iqIk5e37b5E zdM)fd=CtPj^?+^O8_Gio{P@|)&s8=k5TeGJfFaClPMbSZN>#U zK{CmpPTFCv_fq*gQT{FiNT;!eBy8^PgeD2n8oU(Aq@n2*;>MEIcAx9$B2R_Py>!&o zepN$TCX3TMIv_VRKo>-!xe1YYea;_=bQ+K36eRlmbe2jG3+*YdOHMHE_Au^FXYgD$ zgM+MNRxoB88-kZz{mxtKD%1v!WVoH%xBK4tu6NyZU}WUowbC{>HsAxR2kH9cqQ3D@ zCD#;ilcZo$)!-!oF9;eKH&0D+Mo|Fg`?VZl{a`n)ZdL; z(@ESP3Sdtv&C?-Exu@l92iY1Uih{}u?T!cqe6-muN!I4x4W7_S)Ogi(MLx&E%`Pf_OfZ+ak z|NSejy5{QNCexWs<(0R4M-btX^+UK>b(7mpU=^s?&S96%DpBF~F3G^nxpeWq48j2= zi|w-kSk&=Y<8TlFUfI`+Z?rbkSvmVXT7PO($UlNW{XO_uavo2|qj)hM=ZVU_Yc*|< z<GP`&xh1qx< zQ;7sd=I1e~D40-kv@aUvjLU^oHUoe_L=6KyJzw6u<=AP?mM{9@%{Q<4hcABdj|}uL z+^VK%uu?~3_xL2RJBw)wnV5fDTmt|rIUtk66<$Ax8JoRcYHMz? z%C>6T%$N$z@uwP$s!E~?g-6-%YVE7GI$HgQZooSo!iRl+T-`r_bJ{y-Rd+X0ee-BV zVnx#sj>j=O7v^L-4MR5y$w(5yFNEl+rvN)xu5d>V9rSJcr_b(tV^3d4dv7;2G`AqF zsL1AJqMDUABa_SF(O44hfo3{&LGCfnh=t{`@Z0>XoRZDYe(ln+J*$?KbyUq_-)yj= z4sJ=pIqTQplAcz&qN|h6?`+44u2yR9>_A3mOIIc*V1V|AB3yMPul|1Ve=RG$)k;vY zax#zw!$%ShvxcOmBPU4|N+x0Pn&Zmu4@MB_ZK4Zx*|goeN~JB@uxQ|{e9wvtD2$8Z z1Z#ek0g+B)o#e)<&Nd{#&|NacvpHx+g$z@{fjNQK>xCpO`n&r)p1WT6hBwr7@2Q0f z8E)dpsC@f}-n;)V$C9fd0BUXqb+kj12;U6P;%6gAI1q^db;?762mw=h?u5gTQb4X`BDwm39DA%GhR2nCTG3fM)Apbyr}`V&4n1#w7Gcx+}#M+lUR=hmhsG&D9AAHMp`GvD{0x8Hut zQZhE9VukzCyRZEHM~=>)2IPwu*&K{S0+P>zvzyy-Mn?xdtL6~N@78rZb1;A-b79=A zDroL+q7$>$8>a23xG=jVCC2yF9F&ELf(TFz13Pm$4wd;&o-w=%zY#uKtw|+$lzafq zz)&WKE84rTuDy-21TtiqV%aRJZ2qi3R#EWeObCbjTj_O%0M(&NFVxG)qEYL4L@*}6 zkQmsn=HMZLaaDn$mDr)|Dp$4AV{;MQo6DhppoKPAI`LGl$mMOa>R-EdTX8)V&vgTy zR2o~o4Or3Hfw-npQj!o)r7hm5iDLUDNg|(j(aT7drG0<#-fJIPN~UI%5Zo(IbHf2bOoxMJhl{CG1iy*6Jpq zU|KD;GwoJ#JYJ>-U)9%(J3M}BFc#TZTk>Im(^(7{j1zjg5f=n@xhR~?K+}tSqLp%? zLRqvw6u}?;UOL6+f>RdLiH*)F2e+RL%rHY|#$h$javx5oplWr6=di7KR#EV1Gy$Wp ziOy7nZN==`RE@CgP;)Y3fHOG^s0P;bbRr5S6N_0RcWX!_;A`*!3*UkbW9ow+{LrtL zl$i-d-XJs^pvXPHzO~EFXl(#`FbodPA*L#LLt`_}U$vaP?z%k@SThX#bjLn^lac12 z%cv?in``rXmxcqm{J<&rk&LQ2j0A#!iC1s*dhvzUCh9P?rKAiSw+~Kb&@&UlC)+x4 zaYrXk@9CkQj&`fDQR1Fn%X>4P_czC$m6R z8c1cc`0B1he62gj$}zI4W>otgj%>NvELH`h_*8cvF7E8ct9pB>vvp}2J54W6Pz(T} zx$y9kGP0;A0N}Sj`1Z%&6V*GbB?bVF&7(gY$7L;T*xKBRGxCGdDrwD*eQ|UQZ%xkg zZjbfnG;8=CcN@4rt6*U>E&w2;*>c?ZSAzWzt8M8<%AvMxmX;Z+OeNM;Zwn7T1+Pn0ixofHulJjJ*t8+UB#%D zS8xRkz1#=!xKN0}jQjSF;B~nK@AepuUDwLfm?u?7-^1p2@!gz?_URC=Z|}sVon1Jq zy@OWu^;%_F)f}Q5T%v7}BxyyJ>N!S{rF`n|Kl8gSY4$nk4OUe&WHLCts|)P)KxdHZ z^B|fiYQw5|ca;2&Bog?oq9EArp)(8#E)$Ch0AY#oOSgfCaw-ZDl>i{DD%hJ-3Xz|R z-{$}#Tv za@MxGa+I?QCGXT=QSE-4UpEW`BBx?YOEY$>IW!4D8Ltd7n$Ax8G4K!S%yF}jE z!TK5O6-xi*(y${}Y9`sQt2jI}b1W;Gqdv^!a(E;V#^{Ppdaa5QU(>o#XKl;BN%30K zK@eE(^Weoy8Xh)iCYLk&nc7X3#d{4tAA~5Zr9@pF9d}-I@x>M41nPkd0Q1j2^%>W~ z*~+&5Y*sf7h7pP31h)$td%N*Nw+qb-+MiBwARIB9+8lkxL^?y;504heBIOCI|EWh$Bi4`(^`{b@aMjw7S2Kdz$^o zs2UE>1~C_ngHen_ht_qzQzuvKr4=f+Vw;KI)Le6@({P8rG*yUBYujR66g$J9w zUMQN5m7N{fw-|n-W}7rsgDlHM3JisUYXKb7d2;gJZ7)f$nVDZ<)Y=+X?JFrNN|LaR87-RTq@RlYsp~ojpoJSedgRDZjdyE8-ucACJ+IXl zgL>LEwr1xW3}ZTgYdSkH)HjGb=Yr(X4IG-AW7U4UNI5rPo>{H#TVm6SLH?Iaepj*q zV4DYcfeai}4QMKZv_;t|qU11@NleSs;Z&Y=f$8$(0iG1u7#o8m``G5 zE{BhG_rN0rT4f0bQYnOE@p@J}dU14&FYD~!&v&-bTa-mDuhqA$HbApr{D_rBnJ@7S`AzL_S=)x6LaOUB9Ox)zEPt&z`HXGT!d(7Z4kb-5fzVq*ZL8~VanWH1Ps zZ1&g*Ztm!4w^gOBuB|jhB?>9m!hhIDZ@ZGo8iD*IIsAy@T4CDcqhR36VcYyEBS3MLGM!CC0q23 zoQkfQ5I)`4g`c`T=!ivVm)D1*v$OR`hnSuVPC-ih73P2Nt6o_nmX{%y762os`V0o*T z)+`(vS#HFvY1l4>G79%}Vt&`Y$k5O=f62;App{AVs(oa71la{%^c z(wr`1B`;jR8aJsfs)v{X0J?#OR2pZrx1+hK8LA+N<|c&WmXk`=%#)c69!_PDSl&kG zgNM$hu!pXheh#5KwS)#Ng%Do>f-mHq^ zbKNj76OCaq9>>u{0<&!3U@V5oxiD1Cme9(Lp0yPonudwF2%edpqo1oe-f*CPXaE2p z07*naRMqC8WrEO~)zOqn4D8mL`&Sncv&}i8MAyGEVO4Moia8_IW9q ztMJXVYF`NyRmJnuA$p{xfzNYG)NB#et$YlbeQRs2Xyw0e5-Mgj9SQ?EFjRxF-(o_l zUUx#%0>Lf(rrxgZJ6?0qMN5vvE1lZWdg^No_nQkP+07o7Y%^P4mfU0jtf*+psW`o- z8);bv5m^n&V=&$<)*+M4*{x{DoPQyotfJt+ToiZ2<9M{a5p$ACTN62)>}^D2YYWo4 zPNK1q=A5&b002$bu`LkBU2X{nn_bid16_td*hSdk1%4%TJgyXi84&~#f=U* zNjqAa@q^ZO>eq$sVrel3Rqw+|uHvEqbW} z$ILwy##>FHe&Gdkx_ezZURdBV3fkKvFfbaZ+`46grRPawLj#%PQ4OyedhpWMziwZ( zjH*F8yQlNi^9;IqJKwC$8NBQZ7 zfvHFoPt3(|r$@$7w?@~njExvS)#jVirdx_}D zR0>hUfLC@Q#|FmJDU7qhyMv*EC&{9xyAb&M4J+x=bbdfP(L@2&?pT`3^zb}pmKrWYg>Alxp`?S;Ql|y!-C9-?&$T)6Jn^QoM zINsIRi1ADco7qeHvOzvLam@SrO6+&0`&Q~AaHxMI^VU2Ai~ja3&_ z*c~mN0|0b}J{!Un9j)l@Uxr_W0@UnwV{hqM0FIE;H2iqyL7v^XntrIbY|kfm>V#3a zb-Sj(%t31g@ zkx>-P%*;kV`N>Z%Ib@!6=FqYY=jZ6Hg&THkMYWD|s?+6#KM4X_Az`NOJ+h2%^>)!}RazXZ4xF{?QOOrszMO)VOcrN# zbim!v0F{W)+=NKHV5Zf!}1Zc?b7}FF8((U6WHK zdAVg7pWL{b{yrrawWYR;M=FILl5nQC5m&c&P@u_6Q#nQc=tn>Htvah$gmm79;h}Tp z)pwZ7siy;qFV%slU_^+K7KBMfh2oZI+WNGcr4GWXPN@#wb@KkP5=hmV-(guL_fK5C z5}#GwRI=Qgw8({fwhq4*s4A!H2Bbs^n_X^f>gh&UQAqW9DV#{uZ8}J0vv_i54$pP^ zaj}rd=fI3nXvblffuCeEC9LF(q2qR}2j;DG-im?(9gG-~3a>;wT_s7zk<_8W;dG^i0tpUJff+sv7KM zwtCo2Gbcl3_p8;dybiTk(t7j*R`scPcSTb^28WeAR!v5$1}E=vJb^XAC_diXk5{*} z;?%)GtFYvDZ7?NZdg9O#T$+gBeoubqLpA3R1Hin@xJApste2kGHSCGS&F7;N0GzpI zCI3sZG?u72ufjcZdu5ZihiVwO&~VY{-d!hDsa918Uvq2Cz=b6*So}6`BDDKJ=0=R` zI%YKuaa{+%1rL;hBI8(KMQCPXt2h5@#9)_~nUJg$I7_LABltvVzUF>0!^uWZ=Uz~G z6b~8c>ctzodT>@x5A}C-StechUp^rf`)O=8$R9p3!Qb<1RTViQ7vmeOAf_)49gP|L z6qRF%ggJli?P$kM@RWQXM~XnB@>^)B2Fa8k5s3d{MGtkymNw71 znUN(bYKKEp6Kj{05dfKcSI&OT{}8S>Ew%ORW2$8`DDU)pS;M5J!bN$_5Hm2FsNiay zq8gi5yzXpi!CTgBpbx(O_2}9#3`vp@8=uDhXJ5iE_PmS{HO)suAw=irt<>9A#8ofB zSeYy8VSB4iBn2FervQ{XLR%4&O@xU>n4FvKOa@lwB!RpsgM%Sl(%gcT1IzHU;4J(k zXm2pcHM~=Rvi{Qy0}meY0A9a*IllDPt7z*7u7e@V&lzZt6w0=M6~E=?70dW13#?>4W7P~7mo6Lg!Qk?xv;hEU|Gs@<^GPQu07OSiGwe1F zmZRP+6}u$^KgeZDgxee&cqTDl9-4e9?3(2Y(ACN{4y>(J^FIwIY2z^{}`GscbK~RB_FE*aT}ft!%j=y0FaYFF6?yCCwe;OAfR+z zo>{S#R{2u1ABS4~xFtA?7h^H(pPOTczL?b}AM>*Uiaq^T8;0;Nx$57+2~=5N#^Zt@ zg29a63nlfr=U(#HezOw9%-FnX(<28)M$R)oxM<^A{8W)@dYU`If4Eeu_v40!xIxu1B_Y`*YTRsZ6S#8=8ePj zZ#mg^*TJ6)=FP6c>&1LeJAM)i=~&q?cXx8W-i8vsZkxIyrf06}1-kWqP>a3#9i+p%)X#zo>WA7_@w^jsb zD^}1Bb~e3QsCY*+faw6PWPuO#4dR@ZRvPZ_FaLf{Cmq}G*y>r7x$2o%3f*3=$(0Q* z%AE~NIJ|r3R)=*gfeZlsef@{*5`^RP_|*1&{P33bxG~4ntycf+g{LXO_%0Nr3@*mo z(3r7D(>M?gmn{FMtzLy6>K@CF>;b_`0tOh%+-^)(Zd|ogPDB$4nh%EISt#4kjbc3v zUL0yg!ZJw-=I20O(V}blX-;qOA$93RkT1RfZ#r-QZ{D#F6VL9VUrZgsOL~&G9T`D9 zS(h(CEgTd3nU4US;ODliqpzo6$znQ@$(N!?v9sL?12E>IILYflcTXRF=JC)|lE7#( z$#aYL*keK}rvS)vAhm4AwlFn721CW7X+g@Q&H+&O;U3ZrH{5_|I4s|J&pjVpxMw+4 z9}LXlE>mm@J1~HZV0@1hOsaZG__ke2jzB5G~#lk|(}5NS_8e)H%RGLAEjIZ(M6xaT5eu{QW(tG9hk!({9bIeILEG*P-(YQq?XTZ zP1}a1qbZ%it9yE(xm;lDjmhP3@*RnVg!j7}9er~vKS}|V#C}m_LQX8kg?XBl=xg78 z%`ctgZ-xv2?d|PHV8fP}F&mEH|0pUST-8S(O-qmr$_vFnaV9wGGH@$v2q*@K76eQ4GxOsP+wta`RMZj_&Sg#T6E3OqAEBnsYz_E-u}NNU7wKB6t-KWORAG zApjjV4XD6SUoW<_H_>`S#~I{-CIt4ffxS7cI5il|Fqp9?o56vZ%Am04tsTNIHLvX~ zBmiV(!mP&)0$?!kBrEu8AW%z14*+=I>1WeF-hOLosRa$B5R0s1l`fRz1$NS|jwX3e zl0-f#Gy|?k6!Nn#LOi_(BhxeZ=g@vUH#32`XwdgSitKhrZ+tKRvLRNC*lVxO76`I?F zNH$we90Y(@_4VTS{#I(UI3COV0hoY^hDDWZ6pW9=!hERuE^z|Dm!EtpzI)rxNjGT| zOB5nbJTYlug)|J1017l($j8>qu#k@ohP-<(g%QF+YOGf>V5GVgfp|)u8#4 zn+?opYih!|16}kXcHtzoGDvJWmTI4?1(F0ZO^tZO$l>>~IHs~`JR1nILsvW7XV+|A zL*GbS$Nj7ax~20wR}#?s1o{YSk3><<|uz3M~>Fu*h;6hEXCP zn%@grVwu?ggrS}{e%W+CA|X7qc!=7l%E$4xk6ep8UjH__?~FImyG}WcP8b@3TduIB{<#z_l+!dkdvF9d9yr3M#X|g- ze#Tv%Mbpi3F$vh9(C<-Q!MvfNXTgoCqp8W9td{I%nZ{touXY{aH&c!eyRAo)a;62%_w>g=wU;Fg4whgRT|>H1Dhbu;JV=Pxw2QxbOWP9-aA zFdF03^bA}ZH{@>=R-&>MsmLBmCN{A^ENt?>w3}N^zexZD0U{LTx$A5-FN5tPA?8kY{ zKKdI)M%zNjJ*TTzy{3e5Lx;o${0klBbyZ~rZR>ABv7OH!8pWFnhm0i1wsO_lCo`ZH z295z3iTJqyo~%~MiaZJzmz;l2eEPX3X*{d)L^y)^WQy3{1JSAAUNIm}@kk`H4)#`= zAaUuE9r<~MYs=y77O&FW=ZHwlZYqow?3f+Ptr1I$K6 zxgx&=mU^-qDb_zUGv-r#(AIIN5dx(BLFo`?nC-MB~NO>S4C(4%NArCT; zm-T+XFM$V$_|(-aakWdLcjW3m-@O#3PxF{CFs^FwfDq-pCs8z6VM?hczd7ic{E0gM zwd(+nu3m|oU0xbcMG5O$u0t@7Q&$-oNV)8qYGqlWNN}p#w`Z1g?hDM(rA^J~UcL&q z1_Si6+l7OnIW}!ksmhL6JTJjY&d(6_uqATY3b%$O*u-Y6z=3J|11qg;ZD7EFQPPT{ zmXZJxW!rnq#EATgR#```_W0Ad>Y4pC*?S_s z9v{KuQ)4_{y1m6z^NQ&La87hq>CH<317Mw*o;!Smp9kUJ2mATlCJ%i=bEDHj+w2q} zVc>~e4k|AW@UW_2CRmJ=Tx-mk?>S<#UmQ6~Z|G>}8+$tGjak{&R%gC1m5rCg)J)x5 zbc$c;%6GiOcv0pY48Vxby>W*3= z&8SC@ShnIZxJY;=G*YI)jrmN*DhR7p z>Bob`tUyjxF%pX4o=6PObTuO*YjgrjmZ#*DyMx{5X1ruN2Y?7TtYooo+V;GRh3T^7 zy53d1h^(UEkzfR~%RA^I6(AL4FKlMg8ylgXxCzp$ zPr=!%R^UDDgLF1Ek=)rr+~`9%7OOk#xDDzxNs!Krv2cVQoQv@{T2rw*9AQl_b@&uS*tBW|UNf|e-m;<(7cJ|hq26xv_VloDxslFg zog7PM34<9sXXo&TmPT~?gsrbI0Z6$d=nDg(MF={ytpCofXPjBNi`oVyh99}TBhpEY8VV7mBe6U176k8fT!jY=$9m#T$C4pI%mz1=~;YYb2DyA+w;wq6=lw=WGTOq zqpP7J?x7_D(J1st1X#ALSX)V2l(Wh;mS)eAA{JU`HI6M=$g=AS_b(f5XrcTPlu2fi zlphRwOEdIy&H~Ok3nyO_$7#>L2<72zbVtWQ+!33^;kgh8!eP4->QT5jT|tTrG=pg} z7~;ub2={>aYrS1~U3V*eIxXN`a8geQ#*dU7R8!UwRl%-Uys#6z_+YTR3!i9jrgthH zAXef1N$|BIx^)F^N+Xi|2 z?Vw78oV1{lWyW(#hW3Y}HOtB>3a&fnT$0zUDXvE4D@t*fKv9Xo&gOTs^{+}A$17>A zvKHPq?`RN$pt?O!hlhYyZ^g;W2k;KH5$8xPq_+Af>yi*pCd!iAeW(5KAWO9-uli?FfePY*#bC4uU);PD}t$r2MXd0$y&ACoj1C%6pyWUqWz) z_U@5ZKD+IGCGBEhTm}Rf`*jrxGjdISn#&&~UvFms$a`z;49Bpyx0zng5~MQr(rP=1 zDibndf#8U61S+1ND{jxXtp@`*XZ0EyJnM{#!M?~2T6aqY3+c4AW$Daprzm4(ZQn0f z@^E2wTdXSyD$$q?QW2h7hZMEEl%?-Sz;Qy`(K!s z4K$bWumMejEC_xf5N8cyQ765W>6(E&-~79MBmDL?L-@JmA)kq5x7rRO>%3T71~v(o zdH5JPKM%BI_+gx>-YN(wG*? zT#QU6BR8}P>hNkbUNMi4Kl>7{d-z4Vt8G7i5gEfsG-AbW*3*~OzIV;MLa`V^vHV6; zi3rV2e#GW0W0Ssl{SaM{s7>4EFU6@0R%kjt)7yhvTy8XxpgpM+PlrPFDpz>V*aY6x z(axXmX{Rf)ZU_L9QMyehd~|%d!#n|$X>c!n^X1A5q?N=zm?r}>x{iRRLoBQYWT#Gd z))4c5Jbh>smt^Anytn!+E8{7uizj@6pqkJLry0XKXUSN+X(xcmIeaofi8 z=$$8SrS&UTKwhkCx%SVDdKm0-(?V6~%!tk>sNAl&cG&=KOgl3%l!t#Xathp&Gq}#{ z$9o0_advAP4G#=BsZOEZ#mdZtBe?$X7=PZIEBaHK>)JtT>DkxO!WX87JX&1Iuu=+)kacm(bBnngAy%Jv;sss7Z+Zot^aWZLPFi z6^@o9OQC=PB-0oc5;k{tA_<1(^U{1STkY(3OK7@*?V%VR>+oZl3)Cju#ql69yN>qs zd~xmi^^R803WD45;P0;95}5A;fRJS1mRuHb-M~B>P?#~R9kqyDVHX#YGYSRCn!nR==I{u13h$p&RR=YEv$p(vz&sKWEy96cOxT7fFx1Y zxrkH<|y7@MXMTmdq=Tz z@)Uy@KV}6Q049PCfQS+Gd8-9uwc_EcshrEfyT&H)_LgS;%IanG#@M0K?p-4xz%Yt3nji~yEpL%1x^$AAJXfpr3j|*AYFRq7 zsiLrtuP8%WsjJFbUD&B!vEkM%YqEjd>LDn@L+A`g@sH2E0Ohfr^g!nb{v$Yo1JOt! zZe+E7wQH?Yd8s7b+gA3`WfohvtX!?Eo?AjU;F=2HgFT&C)$hfbjgqG9o?#dj_QC>zmpEO+Vi!B3lGw`2FkLePYI_^>H(d(6 z_5!?q*B)H<#7o$=eJ_1y?jZIj<9uXt5_)OH?OL&9a}fY^xLkmPfXvn6l32oE7=aKj zZEnHJfkFH%7$83p_69?&RGPSK7X$z>be-Xnq3H%9i3BuVcPt=Q5S+ie_t`mN;K8zR zbSXo}p2!?kIO5mgA$66LF! zC|z0g!nUC@!m^DdZDx?TbagH-v!ac(K;%9TvRh9AoqQrTkB{Rg4?K>*3kT?yj!`_E z2;$)MG^eV|&DC06?W+&~zI7_Z@9XHH4jX6aS}UD7O$7|Vh{drko5N>%`tTFC8-BN& zwg-Zo%48f*6uPdPnnKvIbEo5gLKOrzeDbTF`(*aJ@Q@2~L0)3UK2_zi^PAZt%Xt0p za{7YqLYF2jcmV)_htS%CU3~+%lMFl+jd3uOL0~RomS?K5Cj@|XX8d~hK^$JWoWJL7 zph46AB?8dz^WtUuwiQ2MJds4`&tq82|#2C_dfQil68{N71x^`d2&3X5pWl#mD=)&?dX!187$w z$+LM=NzFDbPPlo$%WN*Ysz#BEWm^g8F0Xgo=W!4Ab#A zcpz!u-i0|lnO?*2%xuLvc6EsaT8q&$0DwZF@W9OU^b4!OM@ev}A3XTW!Q;m-MaXn& zX>7zdw{4{hLo`VN&;Y%y4UZ;td~ab6N7HL~W?``sYodg>ZeaVCF6?ZoCo`4Cqf5(N z%262!@A|VB&=t4e>Y2QiGH#WR0VQE*deN@tCt_K>uOtFL)Ihj?mUbi0a1}ECd&8pB zRfMV+Oc;+p1>^q5@Z`{O+`M`Mk1a3o@lz93Yh{C@TZdO3=%<_W;kj#`4I%pPCAY}P z@**BuT);p!!?TOwO2?RPprfOmipDBHIL^$eQ>UtmjSH9s0N~bJZ+XG=?A$p@WQp*; z{X6NhN;{hj3~)Aw9s!)$*?~m{;)YK7c-*g^bs_AS92Qtgt>O5>5{<2;Il^tn)5q@a zE%cJtzaE;QgG+_dHH|#kmR(j*Wm}UoF_(uW8$lIjg0ef8?AoPJ)f1`Pl+P*pF5C8` z=WCh<2A5dn@MpMv2Ktunar-Ji)AvP$uEXBe1MA}RvE{6Ncy&W7UYw53kV02lBH9>f1eFLd%vIvYW8vJh~K;^cL#9A75T!&-mXfO=2k_oJML($5J5HlJZ z8^}GSXUpp;g3H9-A~7%adC%0=Hej zDDES$V!q3DJgTwBMH$@vL@8e{hYeL=cV*)DEJ}VLg45Ch^Q?nty69}Yd{Z0Vm~Eil zt!=bir<1LdD(iHh7yofOm&55@ZM3J9xG3yRRFif+L|pa2eK803tgRt!JBSgX+lWyn zX3&ZfcS2KRLm6!XsI#Nvjz7Qj(m6$A4}@(~V=DYw8XNHu)rvJxev z8jnXyDBI*zY-?B&B0@!0((ikT5?;#XD@2wokF~T&$VE~7ZLPL8SeLy9`b%GoH{APE zT=u)8_`{YJy1M>p95=H(J{=lVs=|gIZ;0U)1&(ALqm(IO;AbfZ4S}DR#1e-E-~bDz ziP_Yu@_v9x2xd*A@+-(>GW`IKRP*crTyn_^N2Nmoimc?jc5S6IO)b>FO(5vkrh14b z7(BOxi!*urL)R8uxM>re*V97{$qiMjtZEmWOQoPMEh&jjR>xC$myvpvcLgrBktd~` zGM$U}hNf?TT#SF~IY9fihKe~#sd>sNp7*Ce@SpQvs|4~J&FhTY?1HBhwkZS# zEsJvnQ@JcN0LLfFj2s`SN>i!Cv2XkKVMw2rK)C$$J@l{X%5qyRHWE%{aQ0#fAL{7B zD>^&z{O&I5?>W=fh>C2G@$=s!98uogRSTFS80KZK0w3H8$`o*U zq8s<^x|p87XKy8x%Dv6yi%#d&ng=$bM7c$?vEsS{ z!!9qcR2&!(LU0#ebWsXGSxxhTy*qJrMh~rUQwgGMBqCox>(o5nWz^%HTesoD#wI#r zOBclrLotmbA+lY;ws3T4pf8e$FWrcdDOp(J2;Z} z>Ny64jpI|(VK@N*4s73sd+M9$*5+3FU`IRtY*QoYX8yujpYMT=wzf$C*wWrcU*$OY zzkp19wFK)Z+X@CREaBpG0q^eKis!d&#`#;eP<=81L@ViKs7OA)8yYFIaGIt;H-gxN z5tv7Pu4^8`@OX$wvYa0Rc}^l&TknmEY+l9vt6yJ1xkkE<2juXr-f_>s??G~w77oScfBTZX3L zz3t6Z=P+UnJdb1eK)eZWBklBRo*zzdo7*P=;8ndH<#I-0JJqu>Ov=FN47SfM;fmJH zctL9$F6ii>?#-S|WVP;k9#}5qKqXHTIbbsi0=pQzESs#~84WO(Xk^7f+6Thvi z3?T}Q@~#PATY0CFaVY(jJ#73tYQSwxzSsZT5AL(2Hd;K6jBqe7Of0gAMPZ`VW?3@! z_fS6PD$%N)U)2(3USr&B6(H}QE0%?$%PW3=(Ewa_a1XtDg^St1CD^OSj33;2XXMr$ ziDS>!t&@hP;q5uDs04}Z$alTc%M+%B?pk*s6L zE~$|gxKCHG)@4YgFlXm5Y349tAmZ56hg5CFssZ~3fBbF5mzyj8cc>Yi$Dp$lJ?hC1F0%7FHp>r!eJ9G^7_4SjTtu5GLuV>c^mEp9?tYs~% zrzRN2{1V=wC-I)Gz4%aXA70$Qof5^|k{dM<&{96jM!Mwi?9)8@du4&3JQboYm)SI+ zL@cUZM%9NBQ>ks~w|`QXSwgu_2@jQGN0TDR1DVMrT#LzxG|xgLM<{p}2@3AC<$g2` z^yDEt&MAyLSxnkFOo|+i7BedQm!$xSH^l!$H4G&9Roubhpxnk2^Yi|uo~5X>wUw?( z){|Sti~KKG0QOqyf=bU1rGZ^L`^TCZ8a(G@A2iRoDwmdOSD<2_3}^sY9@WW&k^Pnk#lz~kENBJ% zJSigpPr@NpZk2Mhgd^6g1(mKdi}f8}-cc3HUpXb<{2U(q*4=p8TEmE)#h{(Wkd?-m znGad!ys$axb?x$xwi?b$h9_c-ANu6B8I3R$RFXi(t+}BdpWn3=dwdCu4dF$9X=-BQ zJ6u;+=lJ~c3RqLUQT5H$?qapDq#~OKPtJl;X}q?+8Nb@OmtJ;YKPZ&0!aw4|$|_b* zj1`|J?+#%MY9$hu+GwSM>lHP8?yV7Wk%w>z>gql6-w>!+Qdxm#U`H-3NJLuD+`!U! zOACXh7ms6$3mEIBYQTEU?B79$s6-XoU&xLQg#0$pV0f^Qz=qId3p-%jZ08 z(vE|}^9w9EJS(wAg!k>~qrX@VzC^>n%cWBLqVRQbRlAX~Ykz zT`;swg>&l{8P4)DcyR?}JGia0hu(BxA1MkLx}bc4xq^v_6GfZ8O7;SR>UWQw$!`mj zR(rvs?1dpu9XYFmOGs{Yoig}jGuQDMt`<@?nuwj*yTv8LN@q!sd^A@13wbNoNHEgO02zs? zYIw!|UG&c>&D&Y&r!L(_Amrv3yDOj?K9-i&)(HT(%_j|1tugu&sV>V1ilP>i=NG{R z8((SPf{&khI(4+RsBTm4O$^3w2ZlVY0vWa>Hmxd8{L+*3((3Nle9By(`zAST=aeQq zvw$9J@i>EJH_VXz1gTzT-B!B%|-1VVP;kNMsn9}u`LKaV^ zm;Eu7q0o>_;>}jEU`sVqDKNs)2?5}Nn1jKBcjFj^z=`SdPOG$5?&EIf`Y|o=$_ii$yd${@ z-`KmKc89wR$v^-wwd$=$;NR)Ob`#q)1$u0CK2?F+ACcOZGt>)3 zWuYwXz@g?3-#C;Cp0HidubE|rbJsn%`pPfkft6X$GCj*Co?ct#Wp9LjV$u+}r{K+AFvAWGPfUjJ7{z5pC&IsUG*g@@^x3*#$Tc1JkRJcVPwtD< z^hAzhYggw9TL^r1dYRdh*BjS%eQK_0~P`-1fD~2e0)nc^>%cq zx(dA1MYF(~X@<7)ut*_w13b90@LbpYnZcy{Sd?}f26RnZcN_EPn3Vp?CI&f*Ty}m| z*}vrHQy8Jj;}F5(kpZQMip>U~n;rkn^{0PCkniuJR;erL&DH^VY zaIjS~(B9Y(cDn~=GAZbpjB;}7C4s+8Gu4P1K9fL{=1?xy=s3co6$B8zJNlmCxn2d1 za{7lTrA{DSV2E-?#v^g;821I`~cJDx(0B`D&Q$Qi$fC=Y!&0;DrqJCy9}N= zc?z$bo8>Q6WYpHd{d6`lzGhgMv(^n}c_DDrws>VtnG2^p`iu7V5*2v;W%0L-P+MbiMU6POE5NfqwWOr>2R@Bmk~)-`3W%dq^H9z~6B4+y+znFtiN zHos+yB%+m#;i|Ebe|L^x`ZUJ|-+4D~xZ)G|-q2B`rBYGH3u!#GFwG+q6IClnLY_!- z%ggxO6G!=tdXAU%%FDCc;=6`dZW9a~b!?uP4Gmdi0JOKa2e*ni#e}y%8RWNQI0CM9 zfB?*mkDU>PU*wExZrU^k-)H^f$O*hGx5^JCHs)9=vxlBttHyrVabPyTTsg z?4 zP%AA|_()z8Ad~}3Lql{*K#>^Z14DS|^Vj08v4LW7%o3z*6G!rCNbXUx;iD#=veLcTLIJltGs5>aHe&zQ zE_CalZox?Eam?Ec$8w9!Ffx{fkwPA$3yZECDT7Dv{Ng=3@V<2A)FFO|F~ib;m?$cV zD3^=0#YGa_#4>yg}22J;rp#RM*s-{>C|GFeJ#YY=LL-j8WBR01}oq6N;-`{ z4y_xl>k|pu+}g@@MzI!r-gYoEKaWCi0rtOL?Csr(8$`1FRx5d}$Ph2daBj^DXJsui zVxVFOcSl>>;QZ2(o54#t_`~B9ctdj&zwfji^v?O(*-UwWm3B$@x9$iOs92|q7--Od zmZl~@!6{*tZD_UyVH7n{Dkok~#I|3X_-vu1zzB^5sj%az{3>-_TW3{E;3=i-@1N8T z%>L&m78fyg{Wo#P1HboZAx+`ngl6;6@o|dC3mT^PEW@UT2E4z&mtG-a_(dXyUuSbz zuq>WmTf=0yf+GOgLV?F;s>+$-!J)IY6<=v=qIz>3)vBF403;lSLoUHF`ZS}d(mj!| zt+!{WQX2qRPOae+j~wBDskc3*gGeo<sS%n;M_s(^6%ff9Os7^^ zmz`K6)ZRgyy$-=jP{>doSr8G#2A>hyZA;&dwbDh+EwtaQ%l_;j^h*&4@7&fwXPbIh698%_Y1gh?=P(=qHWdfBopj^wJMIXz zQB@?k?yl~!N{G8qN=?qM5m^=`qKjNDA}&_7$~X>82J&FkGmu!9#UjLhU_O^Eng)DV z0`pm(?5@luIRv{}K3N7-1);h-rl07-z%@eaay%YYmJ0)*Qr@_`o9uKf54^;();;_f zzWtHU;=cJ)5Ymo3Tgc*2c9mxrm%@mvD%jh*m2d0a>Zy2n0eBK%D%p!)j?|DV2h~umJF0&x&8RSqHd$?_@6* z)o`Q7Immv>;4z9$~h;CK0l(Yv6(jao0V#;oING;ru##&Rn#w z4Je?pQ8s7jSScv0@&llCTkb zj1mZ@u#++MJ>UK7aqsfs;;{*awDGi;$LgD&w6u)H&t8qs-Ey0U*c=u(VrBTLnSg!G z&qk^&c;)K}!@xhCwU@r_#HrQMg0i{1(dG*{Ybk|)?drzvWDCVE|R*7ij{J~S?_BEenyvX@N&$V0|%9Xx}2>skBhHOr#vJc89g z6l=Mh$zs7Wkc`C;j~UpKOoC9s z_;@@<&&x+ECtUC>&e=S683ta{)Qr29mvLTw136uteiyBp@Bx75I$m7D=BX$l6J;N+ zV*y*jAU!h|s_m&raP95w4uBH?PD8{CKY!PDd}_5=s4i@k@FQHQO%zPS#`o7&0S00K z5=59P`r1_`$E~pg&9))@L97xxDTFAN?o~}{xg9G!?G=3j2$m97xl{SIrheHk>?_Fo z4k19iF0L-|>)8yZ{`;%A{`U_c?bu~6f5D6qF5vL!IG3_msdluAU}zfNbjDu#Vnzd1 zE^xFyUU=t&lgZ-ThI%{{k72T4qD#|gVgp1bLsny;8Vgnl36*`=g$A49B&b*dn+ZLd zp$uJd`>6=$o_U7s;uRR^mi9JW7q25K1qb;RB4Jyxzl{nR)x9B<9>E6c1E*28rCeu?g}V~gNalUUF&hh!H5@}DDu+E58om5dfR4x9SE zWyK#xBj=lpO-^FugP+DV5BwGxVVB2|b8I|mrunIpCs|4vSM%gnIj#)igFIWBG(_&{eH_5BIFOsdgZY5@L+3z)FYq6!-T2$)R7F)%hRccSmaUJg`Ex<3m`{$c`t`he}T(k>28bL77q3JX?GkqEm zEYMpOmF(TKcT{nj2!uE7+evRJ#H&gSBrn#rYJ}Y5am~j4*>sU{EyW_u**1wqGAOJ~3K~(YbKvCu2j~~@F;W>o} z<%j~Jb{hf4v}41z1i%aeqp`jo8-OyS{m0=^+@Fm0yRBuRNT3+c?+^eY%$yom#>G`d zaLt>V#}udeyq$fxvJfl_tCj!rg1q2{4MGd~4FClQ+>lMfbhkDlm<*gu#5tP}2nnIY z1^`Q|t5`XHLc%RL*~_v|C=VD7es)k9c`ffo3O!pO5?dmE51ta?2E0@V(9>&}|MVB} zg36J-<~0@{~)~K%)NAnm7qG`!(=}PjO(ln<{YPJVAM1?0FKuscxocL2(WaFbsJvPedV7KdrTtRv8Av%Mo>~v7 zscu|~r*^lu;or~LL)WBzTF4saKor~d8xlC-SUBz!anA||%c~xr*=n?>fosE)Q+T7u z@krcfMv1`11^@Mg!>}2!QPjm)ne;)`eO1YDm%RAJ$5HId0KgqBEx4|+iMmbosaG|#C_xvcJnDDJ|&{rEb^s57IU z3zh6xE`(hh)&(BO7wflCiL04n0bjVyhBcN?jE>>8o0|CpyL#yFq#P!-YRwzifkQ@l zTFj4~+M#M!)pFg{-E9*v2>==q-oJAToo?0U@L5fpzyM1P9wHNe%;$^5R$BH?vV(!i zsj#l2WREhW9J5&5!?)weDZk}jJX*g9&86pD6*|)}qqMt2c?yb@h}M0apV-{2V|q4= z>{q^yE5G`653w;A!=iwPC&p!Bs})GsG`#uD{qzG7r_K@!snSBKZD!1<3nhB+3;C^t#nlvj84B$ra0$8za45zbU8FMS`J!P0prS-S|yCVG!0v0G=6nx9*gSCAwo6a4eo( zT-*rmE~xHhhnc^3c#vO%JfDiK@19|%43D_OjGL@1?q8V01M@RDyts(fY&QAJpZzTG ztf~pFp`m^Z0G(eS^pmW1#R?OO!}>6palM&^Nd!$R?x-vnvyBaIq4p?dP6Zy^7YjLP zYir*6Xqr|oWK!s}y1?d{u2D7vV=oPTTC`?T#d3TLdCb@jG~Fn_tF)u}4C&?j6`1&A`#l+&zjsw7-S;1Q(S%fM;Wpn$jZsE~P0h;daYyxu-7+PHmOTAXJ z;5@&)!r945GWHa6cZQ}}sPHKRk<(K9n)9EOOkNG**La1%Xur9Jv*ewz`4439IrdjPoGHxb%pSCO(m~|*;B*72ID&2!g4{B<+hA) zaLlrJEt`#clWb@jp1*%LF3>^GEik%S!wEy+aUD1rPhw;>gUQ83T3${03D@nneA09r z{>q_2{9*SlT+kS!mu3W>Q;IH_ixVC<2#?to=1m8rX<#rM0Zi8=cxq*pn89=Nv-{yQ zepgL!=bwN6AONgsjJ6H2|KZ@@;pR1gdvm$c&PIR`Sg}kT$>ul{w72=8!t`E;yk*hx zkzwq+^u;dL=&UmaKt^l#s%H+BjOYPXVUPo!{>jwvM|EIr#|Cv7F@$z4`3mCAn zsyvLgxJ>y|j9087r@&Ctv|?5* zSMlLGC)Y3vVh8d2eu-Oeyd96G7o;(jRQ}8JDlKHyixB`meMcX@6RW4r4Y=*qs#XVp z`{ND{WB~+pPf}TFOQ?OsR`F~wA00fb@xU0`JTCz$>Lng#HYHq;v1<|3?>rv zbSA^&Gcy&JNd|K&lR@d7DhWRskE5-v6$}tDSl9I({!XhVxU|bxp$+yL)l5q&bgV_)Qz_(~J1P=2qMhPasYj z#kXpFd}@Lw6Z9^2`^xh9wEtzDg?56!J~IrW)}W0-j_CgKIeqO-!<)KziV1 ztrki`NyuxC^ifp4c2x9x(6wMiQz6vO(*8I)J)6bkm0!X24?cjbaLR^k!HlCK&qv1t z4%}f_s<37N=qGlR25vtgXbw)B^5(CK%teO}J8KQT}9{uq<|j@+Fqv%Igm7 zrnj$B=-iXFjOD+Afw?98bz=iIcXT4LxIlGV1P12j*>appOK#}0y8caz)6_8Ze*eui zEnnSTU8AE@Q~1A0$1|`fsMaefL2Pcw`R--1`tuY%5 zQjvQiU|2XfH4|VZJ#XZ!9ewyxUJo^@tBBBZE}g+k*K&AkM<>p2X~lE8yQ#e;GOc6) z2T1Rj;eJB`1JHG2%lGcM!{cRL6T$77|&&KcxE=t-Ua|@ zs;|RU&5g9n=gX_Yv#JShJ7}3(!oS3lxU{7W2aPzL*WE?+i3Cs`m#{9!+3U#`h(Lvc zH@`_u1oxs#F7XuiC>aR(-|DTh4_YMHG#Mc8b9ZgW7c*+Q)O>$|aKBgO zHTit0okNVT3)54hAZdzQCjF>(vCstIEG^@5J&Dum>#(D) z0sGtA$X6hyqHV-goKSIJ;-?Nh?UCbZBDl7;wo(7XO~=9QM~3;eBF9To{VuuVu!pQ_ z0zWOe?=Xs!J8e5Syt3+Q+1vqoCeEw(_tQ&!D&UwXc0vFQB6KDbRgGzNZ4E0Y$30JQ zBLa#!KD=@kfc2O&R;L}OkTi9&%ogxKaY>*=A9HsR253 zX;~?@-c?@@@?>-jtoi_ef@#zE=%{zMDK`u{2Lq^Bo;NJ@o~wxjyo4(6a3locKl&-I z|H4=BXnLW%>jS_kD~Cr`7F;9np9Bm|!$)^+rQId=Y@=*hz=bPfcX=o-@R(0-8?;QE zoSjuE$I5?O8XNKX#)h&O5r(ge&xW+8mWjCO;9xQd76KiX?ce1hSvhHkBLZ;5XBw#4 z$WoU`OoUxKGB%D^EzI-nl3b_KYUMP>wT6w8mH?G#f(RI9f#b!H8~^GC0BA@i@n0=X z)FixU3jxGjZEopLhY>?#eVuCMXYT>??*`Z7Yh@#}OjIQ8Kpv3EIpmrg1>g})+=Zs} zoLDZ0+?T$F&s}>126C(A=L%*Fi98;in&6Z-WnCpcK58{)0C?ePd+wsr{%vfZCmKqbTBh|l1a2~Qr72? z%~WNuonqPsH<^`g7`vkItOz^$`jio$F3c=0;fldgeoMT-_xcF0K#X721E11t3|Ruu zv|{8o0Y`0{kDQteTirk){7rvPfR%LrlB<&~EiLUa15FJL#ak=Dlx+MCJh^mO4b#S6 zw$ZcWh1~15k4;Wu@QVM!XYRWX(~>beCmcL!rt#FtlYV2ia>tIw&I<&>Ukb-_e=WO0 zt-FX#j2ngJDRQ3`0waZdMY-*z`*z|z5p@6}M`|sl zMVc6ZVd%Yb2CtfPjdgZ*j2{~wj$B=BA@J*g{COJimRCvq8{kIqh2t*tEpTRYlu znH9|DrXr+D#8v4!1GvGkF`Hjc+`|kU2g@Ra5}s2s|7_I2u7aq(#)S==2{9s=f)Ud_ z!PX&5a;aZYR$_7SQ&Zja)KnjU(N3o14pe4j!sT8yWcGcVI&*{p0@p+n?bqi%m9@Luo5x+JRxv2>$A5B z8$%Nw`(a=#yL-0aTUvsWPDt*YKLN=7&Kd&4j)OQ694_h?7S?MHhG~7>Ys_E>!O*nz z_j=;QiM=QhT+J-yzCHV96YEJhD@?np&Z(e`ZEt4>u1BJ(yji+L1zoJc_B!u|K6@dP z0er>$RIH)glU$HqRq(@KU_-X)fq~f45>9^Xv$*ybzwi*7DT?;B60!O5mV1p_X)ChP z01z{(OFiy_$}CWgQG7vX{L<$qIhD`hnVH$JP%8jHeIkKRHaB|AKh-`n{A6^KU&6sj z+rml5f&;>;ZDV$^Y+?!%EXv=5;kCs9z|8FIcK3J9JKTYmMCid34B=E-Aa8@scmxL7=HJeYw^SB(Q-%)P*nLJGqXH77aCF%$>m7Hvws)8XpI8Tsb&ZEob(|j zAIf7Ij4>X`n?=Q5$%k{yw&4`hQG{}G@NpWB<4_E-a{Kwkg}v_YnhCD8XWPKGK88WLkFl9q zUh1nH5#XPoQQ%ih++jGFkmNSOz@Tu##_n%4=`%5Sy#=e|TaXRboorbe}~+l5*tx4)l941ou| z_2h6u*chphZLP7c4xemppoWl~r?qNQtGVwRWR(*Z0mX%3!qB-;jFhkGy*svBysG$L zE|=e}!lx>zYu!9NZWN7P!nDykJ&zAGZo&nPO*lJ|r1N)dr$o%7`P$GVIlZq3^2%F{ z+PCcJQX=tA!@#EI$QtKm7@1cFIj%paq-k_~Dt_J2DoRYTsYx9A$S3j1pZvrVzm|1u z92R+3!JC3&(6KOlN@;SgsGG3Lta9U`6aw~T)BbHhDk|BjM5r9@r z$ENxQl|0Wv$))GaAW&soLTZV*@%~he9x-JxY^8mbrl4(SkR`=xBBoLyHT3rNKttlj@$D3HIdt0>9Zb@xfia^tM&C za-cYSIP%Pqo!BVBHS}zq!tBXD z&q>QfU%U?OhK>cx!WP@XfUc7wf;P&Q;a=y(g2g~F(Sm@N*kt3k?4T-8l;fO|PQG0b zbla|K*O<>`!4s1Z`-%!BO)ush(I}`u@#qRAdCn!g#L5arzi)0UA*2>Dg}E%Qf8FuxJ9wWTFItXzDEcojvd1T@BR;5_vjxy z#I|aicr3Td$7g3%#HNCc5L2~%D%(0j;5&x~_?1qECt|hlDaT_1_viB-o$yRPkKyH& zO7=GTSW8nmWx5Q0r*~DTkRPhS{FiZW;43q5%rbd$UDF;QHbc|!kv-e!k`l3LA$SxQ z{^D=Tad6Pl5TBpCv=ZK-km%fcSap6u*l2a#Fql!jLca{<2{CeN0zXbzo*fMcs-67< zaO2|KcH8Rf{LfHMwxv`GOUFmoTln{3#a*(U4FKW~{2sUe`={~!$&rA4?Wt_aUzolc zs6_!1NlYPamGH!galC3_j_)hgPxHTXWrxK0u2cM8uF*d)1cq&k^QIZ4 zqMTV=!h23m@E6)Ou*RWL0RWKE8P^#OrYxu2wJ8|S*fuK^Rv!Fbu)iOlDkyzk%MivQ zM&T4TX%=p6199vpqDVSFV0)UJ6_VZP_+LSpafC3m680`+X0~^p%0sz-F zKRTy7-0A3L8Eb?XCmkE*XdnPs_PO^sMw39_hu_`~=ZF0*Y2!w5_fk77h}*NTsg;m{YZ#T~;(%enj+K*q5#VCQ(W zx?Ce0_J>qKOxJO+uLox}CTX+dpgx(vkZIGgR2EaIHC_s49#%pslf_k!9^qf^=)>ER zF?y>V_ZIIU#sdl97nX%JOMr9@U`Ez)aM&_=JR(H~P^=CAWf&ESzZ#QJ6u8sFCdPGa zVpWpK*Bl3jmX|qKF#X;<<>S+~_2OG1PP$O{s{#v*JaJKx2f}8DlE4ND?y@((+4Y=~&RqZJcSQ>Rq($Q!D z>WR>{sRfHGs}(&24$aLlKlv2lIp>$LbzQmqhHe6*{(%Q^_gB7wUoWo{6*DlE&tNd0 z=7kk0>?ay(*~uc?9_Z`Eha2kX(n74HK{J4S>2JIl`5j&KlSB-+PK+a^={!C+N7+Il zs!uvRHqKXo_={~V*wx;OErv$T0&qx&X#+s9vXLbmjOKGVmCf?fYASLN3B$m@b+%D= zL948k327jou0~Pv9mDZND*(VrB32}KEg!cuHPYuA>y;_qqj0uGahE3oG%)H)8}fz- z4gkg)V#`=THvl{k6By39_Lbs8#)XZXS7A|UWe)X`5L_cYhQ)a)C4fRg%9;2@fkwE{N{J?#k=nfsQd@> zDGV+yxE>0%jP=>LZ4m%IXZJ4rfD+_RjsVLgfMhC%%inLuZIJ7T&9(sY`fo#nLJr`9tz0^#!gJL#{p;l^zbF^OpZ z$!fHyPsSOK_!1VKvTY2lb0C5DuMy!r{k?c@s&>iC2E%l1wghA(W!{FMZF}zw08AQ; zALR38L750xFingWKa312*-7R7?q5n606WaM^XUP84f2?asgZY&8<-J?hB!2|K|`}T zF0S0S`&V-dERz7q)H21x?M^-`fK% zG#6k1Z!j2{d>$tzCowdBiU!BV!iddt9{{?be>=V)5nCl<3A-L9!};fh4aPlI0rKl! zu`C>0U6EPqz=HF_)Ar(@Q(8sWHx=Z-Fu5qdnaoI`SfG5vI~;G?blk?Nvu_p!SU_17 zYzl!v+eWrf@ZX|hCj)XDDy;mEj-0?{JzM$u=4Lu5$#t4WZtx%K90r$6I@@4CH_b{r zO8IvtpU0;kx}T4}{he|`3o-xzANxr}K~!|b<(H%Lxfg(%Tfl26;Lu?_bg~bNg+<;#N4;Pym1D3_9bhz(oYf}?GDvB#m)PfLpZ550H6~B!RV1|Xu*l!G)`*uC{ zHRAzOAFSYs%jODBnb;VNK>|-ND3jb&GZXzEOsQNMo}9uP*4FrKd;91^D-04SdYhIY zW;=){XMtxyaLFC}?z(n*?lWC&t*AG2 zvR6~E<8cg5Pe*+mN*D$|Z|^SbBZvA}LzV@ml1Us{S>wr6hUb@;A?5A}Q*rZhY7Jk0 z>>2*m_HB4ueVqQrTGyZvJJ6V6=sE;5p5NGv-?p}3etB6|@CTG8a{mCZytanrHE#wn zjffiR>evwua(Pp=qp2)-p%c;2G`#iTZu*a$j!*mi50@)voddr-`vzQmhddWjL*+^%>w<5Zom%e(~7K z+L~BdThqdj_ruvc`uM++NxC3!cpqJ0KyM`O>A?N__T$FMQepWE%7+E#$pge=xl0)mgE70bjM-vPBLhI(FtE9`70rze$mDXEi7x0D z_WRjFs2QZQwUs})Yb(8Wnen5zgOQw9(Q=XlbSf+ybubugNErjyt{Ezv7$+1Go9JnLnUvKs=ZZA+s4%VJO)mT;nd75Nns&= zme2pY5~w^8!theE#Ao#N;(J|P^x9>{@mMh5$C71YXkj4~Cj#N`4(y`W6x5o&YWxjl zaOa@go>o_JDdOmk8R$tQv9GPQrevrp=xA;Xcm+k_;)hyx?e7{6CM>U3=9Hncl+_vV zjiIT&4xedipoUV;Q+d}6^OuzWx@~P@3_A|;LLkpTju|5cO~3MWuUmE7Y#16E>c=83 zm(|?=r(+W})>5We0ExLpyvwM=%i1^NV0|Ob@9v`3%E6>ztfHT|!b}s@tlX-;qilTT zzZwy}^0a>ZB;G{(OR;C)b4<@ivjvwt*7M}+qx}2)Jv%*NEMY;H)l6`e>?9n9lgr|Q zL@`upXFP#z-Ch1M`62oPSMY49NQhOoI~rp=U4W>72I-15xGqxiS$;DNjS{cZ@(J^S3jkV{njk8t_vd+4pH4Wu{|02Y_=s&pRj z?(D>c?VIuZjt(5y-dByiC=BG?&A-z{5sv@yk&DcK7Qt2++0Y&P1SvyVPn#g z^L;RmvcnT|^M1U__;s|l;N!YCjHPBj!wUBTfMuE3Ei@Pe5HmUqz0ymMe0Q^{bma4@ zC8((Jq&3EOEHAe(TPWb^r6nkea{?&N(aR3>)B7`C>1v}2sE1%9!;B0BGAwYyHmx_k zE|&A5Bro6!z?f~5uVsOZW#Db_h!1l4DN2# ze#mOu*@X!CeY(#x_JKQY|pWGK;D5+)zQa?KT1d){j7Z@t&Rdaa}W=S6Hvq zdE5}V&lfi~>DV|rp{!?J>X7iJeLLx|3vth+3}YWFY4$3?nh=<^ZPb@iOxJ4xJ6}z# zrR6xdDp`F0mE5#c=Mi$XF?G+1NE&5dvqc*>Juqg%Bm0A5+e3rvBFnwqhr zt{&&Nw8$l5tC7D{3KRL7N^YvumkS;_F<}^Z`spiP5QPe^XljL3qoqo?_&-gn(+EsuWUwcqAq;RIo}V&Wa-I z=F<<{bZN2TRK@$Ng0s#!CkJ2#(qtnlx*2UW>rxn$-E~%1@d_i3293~a#L%Bel3tv- zewDxKcN9U&GF&AkNMxJN)>d47);{`F+9(_2-Bt3N#(Fw7=91@wZOf&V1Ez~D?QOU& zo+JuUJgMwawFvux2}g_vg@p;n!cwXB_HwYuWGyls1Ar|AeR^OFUv9KRC$s{kNR(GK z#_g8RDLI$Lz^pQuw2adW_wHPe!;=u$2*GD`#`n#Fr!4idp)LRCe_!>lVR&yCwsv)% zkdI%yub)1W4f4d*+BBj-0D!|Vas^ye-vAZ@eaSlX_XhG|hN=8FvU61ft`-s(?b*dY z=b0|BiyZ)PCZFfeA0Fm^uCw4oRL!ks;xjRB(kx^pTS865ct+_5|31XH9V3U0vn;CI_}}!wq)rJ6kpEri^I8 zHgN{)C96H7)mL%}wb{is(#8XZrtx`u`tg}F_R`%>l6G4`5A$O&#*ck&M$`E`j?K-5 z*)gFL;s2iAPk&QG_GuTQlqo9D4ENm87~@9;v&a?^03w`lES#FHDXX>%Du)ONX59PC zFu%C4#=mZfPDC3v1^~FlaB#wxPjZxqqk@e)TU+r(U%BdPJY2%$&TqzS3>9p+5{bpY zmJ6Tzu@8OXKYyj_^Vz8o0i1jGxdTr;eVCH*IDfdIjvDg8xgI5SRwI!5+vJ46GTZ2g z8<;Mb=pYRm5vqw|_5lH4jR+TP?}e$yXnb*zmsV3@eU(q@MA*4?3(jt9qQ5m6&#??Z z3XPLJ)=e;Ox4mh!)3$@7xvbxV!vk+PeJ}mn>c$*>qdYRgSk47Q;2VWJY-#Kh`5X=} zEJh{)@)rP5-ZNpW$jWZzbd-wm9@$#G_#!bTO z4wZ8RhN~!Y@5jALAQV|leCp5;A3m^~zFIJ#SCO9YL`>jk`GV(>)A>AxmzPxwLrD`w zc=Kty=uMgRj9Uol-w*zGC`!b3G;bB7P>4Yw%*6GuU}PKzB76z1X?hWBl5BE5V`%-z2J4Pl3^~!xXH1=ZaQoLYmSX!%Zdv8 zrL%VR@#m@_wy1aK65CC2funh=EVvL8a+_NZU46~97i{a>78&XHtO;&YQ`7j>RV0FO z!9Uori5dnrF&=R&fPqy}%o24fmqP^0qarXru;n=T=+RML-nX6pMKVxF;#ERCY%Jq3 z#t(gA{o}bT1{aqiS*^~O+vf72i;}dv8V z#&2VU-xn;H0w9e*&@h|NVW5ylR6JtBF!;U61R)>(PF#petG}Q#ZqXc^%ok-g0${Vi z+UB-n7ndak2r!Yv!QE3uGa_MvJB@5eELnXGpaF&pZE@ErY> zcDOQR@_G8q(?fhj!QLoA1o3wNnqYjxF2Ir=m9`xW=krjoXf954_KrTjDyNp)q6jH0 z??)wA)*0XRdAwvDff3Vm;}XLn9_WJp9elNsr2ec1c`EIJ4lG(gyn+5-dsiMDXL;U# z?|1ELwX5CLecF;N8DC(0aRivbGf;y)B@ix)7D$;SgwQ5*nzoQmj+v%YlQs}?QOa-x z5{_`R2}j7Z#6wLC@`dae3(1ybTe5Z~?XI-i^Sj=sf2>yCUF{{Y1_S!dcx>-?z5DI= ze9!az-rw`QzH^)Kv#9`fP52PtCXLR`vC=RV4%5rMVf=a1DG?>K{Q41>s zK?H%r8;fF666x5)1mX$Lmi2dKU~Kz}Hf$-P8XiE_n@EiCzY6e%ra?0kQ(pifpzHdI z&6oe}ga7&H&x$)az-azN!yNp48(ZtGr3keZ(A@f{&WsaxgF{Bc{18NM1#wUtu z`&IyHlj0jX+Hk*YnGJ7pjVWsg!U#VRbsSaG+ngUz;}{vA$T`quO^tZkQNh}^?AI*i zy#*3q^g&U>pq4()9!;Zsr9lh~pI_6#9*#@MoEw}SW9p!t6?k1{_{5U|hJk=FGj|FgY#OGcv;DWX z-}TpbUAc8@{$-akPzurn(Hf@@C&G51FttCQ2PSpjlQPFM@fbKx&*Xd6IC=wyCbFgx0vxiBbhiF&!DS8Nn^`8HkaLw2P*8WJxl-1e2gQo za6^1qF)^4@hsOzc-_SFS4VmNJ)X|3j$Wf%pGylN>Oip8)Rlzy+jd;>Gj;gvkb~qZP z;q>bi{Jk{8fEF&k0)2hkQhHpSo9<-0pQ%Q8Od-4@0X?b#O`X;0A#9jjb60f#+a3b7VXuqN!ij1d601yY^aYIAuBdiifzoz9%lo!O9Wqvpr z3gO#5AJG2Qo$SYPF>8pK`JLWYINr_3+dT0&`cC(GnR6Y1G2GqW#4ZdktS`wzj4hiS zsy`mZ;CR-4G_xqTT5+E&F+nZv<(mAP;u@BD1Gu-Y29JlPQ7*~MCW+L4%9Ahu&WF4& z)mpck{n%0lmZRV~Ij$HZoU{rU7J-wTaLnL{a!RyKW(*oP$)joGD>kfu;ks+D`~LOY zx6jL?WuhboXK!fg0R}gh;#`@(B1eK&pNO5#dk8=u@uVJ$#IVD{t5nejnPyHDT zS~l#p5Pls~feZ$%KcRfcdEC|&?d%(2v83T*E(ic7v&f-5a~Iv1b6*)4yKPw$E{C`k_Y z>eG*1e9`?s-OV^FNuH6K2kewgycA0)>yx462$|>}9w|8R8kdW0UQ&l^G!vT*8I$06 zOXhe}RpAw78kr6;k1y*YfjPsz%`J88PA=j66b9|2h46m@urIEICm&fNc;hjA5Rd0o ztammy;Z17=Qwk6?CClF!;fI2bp_IHW&N;fn;k@GnfXkP+<2l1xkdZDXM-uEd9H=P6 zvpydN;!zy(`SP}{%Yr$1r6@31EEeW)IB31w&g>+>E=Y)_Oh_1<5HfXRS~o(DD!0e# zaP+NObMF3#rtQ1vl1sa9?CL7IUG_py5`#PR-g|8Dj@#7BR5_=vs~EB&bK6Oo99|5IHf?Fi>wTgH4p6ng%?vC<2VpkasK_BYAo}jNyUvSIl;ZD%PkLYJ38K z7Zfusl;i~TXmJ|!X4P}fe&_avdc05SuQ*mHA zNE4H}pZBCuDT)G%#R6HDp(rvWNrEVfU|@_uL!~WA`R8m6N=6D z&iYLk?74pX_66zGN`aCX902^wHCOEW>aoCvV)xJ0EGGE_O$&s7<#9~SutSW^ENEyN zG&HyD;`H&1;ns7Pv-^w5hXlZIIq@5j@b=^+`XV754~Gl(#m{wJtHlCU)AFt-OgKbS|}_F*M*Zz&uvWc0WkG)){B$saA$y4`rBz9z3EYN0OyR&K-YG6B!|{fL_! zhjop-{s5C?iDX%3vLcftiVzcj7yu$pVZ$_rWmz6+sI47wR#XhT-R_Z=mLt5_AN0K3noY1oUy9YHZom z3vBC(b`)i7GZ&KrLEK=oVnclc9`}i8vRK*s<>i#4mMZ6i$%zn6=pwW2H#@%Mw$10X zBb%}1yKniF5rv|MA(`+}G?pgo_Ngj*C+AGUr@$iNy|X12Cgio~KZ!!QOlP4DgOY#+2*t%L1t?ME9M8U~lPww}7F ztE)io=Ccze^Kbz0#K!ZY*G8qX)P#9H(*()??^ZaTiN$6sq8S5@sc{_ij^`E8b~ZQR zO-lt+XaTvTBqCB3M}Iz;6gY#|v^u5Ib3 zCEPD+7*45();P!BSS(wY?9=1}7;anB!R{}jPh%=n5QeQH0wT?j6o7|w_zg-lx}CX# zPK2PLX+cf*H!f{CWeCF2)oac@Skv6{+lI!*!JE3eif)McNkPdx900^Noc9OpI*0S@TL9FUHeJl5tf<|J0miYtuZ5z!>hXsbI=XK9qU=)X%bx$1qXuoxs=KZv1zp z72+fV>T20hpRXA4Zt9qT+@6qRQN%wuY^)-6ay!qP;lqTG$?dNw9K*46hj2vGP^cyS z+V-W`5kIXzOue284zE>&PXI32=f*glR)Jf|@?%s;xOC*~K$O(rR&Klg-tpasw^e7X)m$uDVlcv^T!Th5Yiv4k z^cy-wPiy^BrABF?WY+f7^SC}Z!2Of>yuBPX)inSBrzEoeNF;BKRSF7Z~|rce@6fmn{8Lxzo94{iX}Loo=`9iY;488Y+BJQBs53e9d)-cRcyXAHV#;_$9ga z$>n7P0Kb$BjK$NvNv~;=*O#_j?;IiW>ZL9Cxvms^>*u1Y=df+z2tEgZRaN78B2+0B z_I{a-hCI2+8WcHqw9eF-06D+6%`xs`c23w6h8O`Nb;C>Rbw6eUPfg8c(PoG5CVrpGkQll3(QvXe%Fg8_G;Bc802P09e6h!88I>{fLvwU&Ua0m>@ zlM-fQ{0il9Ns_`R5Jm-t01-w_4!ui03=D#?f#!yW!OBYa(e}3X zzEvw%ec*IC$8PHCDmqzR#KKT=g9CtVx8AYqNBg>WEO~S9eM%O(IF-hp5(&Rj6P_(e zlxUpexTfW;01z0%mzFiL%OY~lo)2${Mzt?QG{>MF0*ZXak_+Iq-Q zS+(=Vb?bJ2Qt9+h3(kna0l+;EJ@T#luDNpMH;?(RP5nIc0spmNAUd=7cruji)iv@= zh%aba_5i>^ z7*PcHXGjX)3`r3%5}kaLB!vMJ(}u23YkJV;c6#kD*Rf@t%a2&?_P5rYzwX@|ySnB- zbH9j>1SQYmGO^?OtN!@q-to_6+qO^Qcv78FKFn~AgPMvS?^s^%*49{$H*Ica=X2LA z_%O1kY{w1D3|>jVZbL=?Opf`AXOuL<$Y)%c6Y{fYtggb3s;ik(6yX&G1VHd`V48CT zQwse|BzZiLEQPLIRw9IUFX?{&Cbd)98)^!8tT@%EfLv_+h~IAb0ziF;pqW|o+ z&b=$oJ#Sab(xszUY}vBN{C$dW#tjYt?tJXojZb~&?|ymdo+CH6>jDc4gojB(jA%C2 zif|KshECxE>sepFY8m^hP{MRm>GEWYf%%4)h4$;=24WltQG!nhnmRO1gDgsrWeJL` zK$0aU%Q7TMBvBM0i6Q|2Cqzxt^qQtI(zayD30abc8XFr&T3VJ2mszbRJ32c(c84Q) z^&<24nZX$kDO2%3FFvd;B#zB}e zIf5y(PN^h?&E*^`cRBl7J39AsLD+lIC715Gv8(GdrTJULnZd_qa7p<6e&q){cKrEk zufKjn-@w4t04h?qGceq_x}AM5W&x1hV-XSjGB6?wI3=Fe{7so0lNrok#4yaDVT1*% zWvsfs?s#Qw-NACFYuCk>UfS(+yC)Vke~UPi_}C3D6=Pnn@bZf__)C8Y$UK@lmHPKl)vP`W`tVTq+n38_V30m-FTV96Db z+9j4;I^PF>@167h;CPPWXFtr$oqOlb5Ur=9Mn=p)eB;IqG7WVVgBv$)ssmq2ckcjy zBNHyw2mC?grv4gyeodH)HtK22q825G?MFJ}r!8K@4W-|m16Xg>0yt||xyBDqm5 zM}NS>a!0tUbD1wgw%T_ehS}dfEHRKwf87R|rOw`}TtyJ~zmiV#=o%!aO{Gv%Sh9r) zp8M!ZH0#DcT^_luardE$d9}iqCZ3GjvwA!822?pFcw;el;*_GZL+je3GA}W%3^t;N zVa7f}MEzAa!>`)v=eC|rIA7&YOClk1dq1ahK0L`BLbW?Yi$9~{co;@0ZWqkEQgLH^ zlcV%LW{8DBSjK+_hZnWI+NpN-hPw}2q{ecpE8Tr)q}t9=>b6I9xw*OVvS2ARbcwze z=B%lC_9<3y>yNaI>Hb-D$W>cW{dgrEW9ECw+QV=H&BVkbqkwaVTH1)gN(OOsn(*1c zk=Bh(vT8q=oV# z=x$CM!HTQ&{D~EJs3RM4Zooj z%9S4yxMDSp!}ZTUl3>tI3Zu*?np)NPZ5@2oapKu@;O6SO(Ysu@$;vXU{c(5^Jj?Bh zx?Pr(K$)yrgxebss2vz}*ii8ItoC}BH-57I=<8raV^#I( z7u{+#(}o)Jk0)5KKf^NRMq=*497|5-ZT`Zsib($eQ>2d0O#8w+M&a6#)g#U0^B;I- z7jDBLxv0In{juCfAcQZdpPED%)}t}yEs74@?e-o@V20s$5!vir=?oH#3O3%V3o2!A zJ3I#mf&2*usqkrfg{EZv+SLV zi433#FZ!hgng{1H`<}SaGdQ}F`+O>&-WRS7uO-NnjMYtN>CFh78iZa&vHWI?xqhy` zUFyzVlLn37Q-A;dc>z4y-ZWEAT+CWfOBemnE+pkyBR!Y;CqcEVmde^OdAA{P*hX1N zRzYiT^7I;ZNZ{R1A>H==Q7)4~`2)qxC^Y4DMMaUlWskp4i=G+Qbw$IZ#>P^h?_KIe zm6c(x{xdr1ujO0uS$LlpJWe5XgK7FK&JNXY{7(ip_b$$|3)*_Mc)j-YB%1N(F**Ia zhPK{moX+bMBC@rCD?~>gi(ICE{MO6Z3bY`0Kj5)Hk-xCsn(TQHXY4!RR=K- zMGohd6OG1}kKg9eP3NB7*-JxL(io+3Hn=tzva`cJE@6UyoO)+M0xv`TKUVRW4N(I@ z|5z&*G#h`vwz5@@8#n~N{mzx%+eZ?tk&bwBv^Bwa@*t@aQSP~_KCS= zfA672sy*S&GIA+>Q|yRDY{xrAD(l)>=Ex-Z&>7YdI$4l{s}Ctz{rT$4SJUXoh;p){ zdY7QN%8Z?-HH4)g;%cIY86hE?yNnV<#4L(x z0Rg56POi=Ag#|O{4}GpDp42uGAMa#s+eyVLErGd3&7OTac!;GN;}+FISnXud#aK=u zvwpLfM@WI-dl!!VuszG*CSSb4vm&(p?@5qp*PIw_aO^RInovO11-oR z^;W^No5p(pB03FgY|QV@gYLRFH`R_M3?rMVG$NaaaKXU>Urv*ZQXH?*78UVmhQ*c9 zAyo=Jn=L;#VT&Xqo9^BB9KHiOQej}-{;58s@#5D!lZ@>fR|k!Y;RNQ1fNxSu>USGA z6O<~fihN)+dzU--#uw|%ti864_K$g0fJUz}Tm6|u@d5qgVIQ9o-G=&U01@6bZ|{qN zWX-5oc0YKBm4=OJY95}I=uEdY9-0J{QF)LoG!4&L$cW0BC(u0oJBKVX2)4KN;a-OM zUjxngE~87TRJ99w(cU`sSY`R1CwCi73-H8Z%)^Q3HfgtCb07L1A0|Xc8~1MJuQ@nH zccH`GI$j7dp8kZ)o&xV@L)=(F9$gns>D2Ko;C8si?`4=vY>J%RTJ(fb!1~|ztglN1 z9};ew6Bn!L2a)u1>CLTV4ctrR`Fla!pY_l~yPe9RO?W}3x0d^;;G7KWn%)}3)&HtM zYlLQF4jqr+y01EUo)8=^a*^dNg~*-CdW6I3+kOi?RnV3~{QPevh1-xL*s%R-9*FT( zO$EP%263EpN}sU6I-%=qgzweov^3y>`V@q`S+ddgDO}m?TpL)trxsrrV?i*jvOoh@Fx&=-)?km z0s$Zci^1TwmJr||DUfbfh{o^wGUG1ADD43=38%Jz_0&MzjML`xsD_5{K7E3e!YGAC znJ@lcl&CC5)P{$?uP-8OI6N6ZO`Ru>d zDoF@EJ9jW!b!oq1=${W20f+9ozeqSea7dNWxApd2e_yJYAJNBDLEXJF=+ZuTE@toC zAcdmVPwCoN_A{Vev8l$&F{?q2QYX8S0bb#7+iJpx;I3Wile6zxRzHFqk~LZ$nAeiT zO9yZM`tL0J6}-b+!Q8C#dlm=5?qNc+VHt&ea-9h9!qw4J_61!dz3i(m0-Cd-=|8w4 zBf%8#om!e_(bd*3l%kT)Pseo8CC{~zA2c<)oHLaq@ZCR8)=DY-0!XZ=JGanFlGgV< zD}QnCuI_ujPczcI8=MUSzq1+*=`nxk_`FPGvL7K?JmG6=nkp z7De#L^WS-fCAh!)o&va(0JV785bmh0m&*4ohfcEXpw+rE9h;gDXu%IPBd+fI2JdgM zPGrXx03DO@Y5UQODLjiXs9*ZQ;R zW>yrTi{`P8{M%LGZZNUzCy_K&kT7%iVXs~$o_rj;dfOUKeCUqV2XQ)tp%knOYF_>xLX3@`l+vD_z1A9Go;So>W>6OxMC(-u`-q z;MGZ{;?y{rS%LTsRL^Fz39wcnrx>XqLN%seaWU@m!XyxM^eCCqe zEb8J5Euy5$Sg>^EZ`K3S3*sglDAt30=XCRM1bRQMHM9q0^n)mEPWN^$ z)4`in&;0J%%(1RJ!b>Q&39GXG*up5om~!T?d7p{;~Wlz)@l#2MP)b^xAn? z$uIU*ZO3G#DB@#$DB=^IGpDcW9B(ihj!t4w&5mBj&Dm}?UFK-`T21!9A))BUUM7V^~<{=6<1@jc}*ecp3pA& z#?jcAc7J+dohjH=XSl|RD#1^7fey+NKB<$)J@I%W0sHp^r2j^BmuZ^K?~2SD%(C6D-^;zkiuL0J%LyI7!tuVU9X=lZ?Uzd}kZRps=q|LC#^&&~U{%f3Y|Qh==*P_edtG zB=ksffYXrm%+-VUBN(Z5;)g2i?rg_-<^dHgK#pPsntF9!zGtI9MgcEm-e6Oks{8($ z;a@H|C(ctAbS=%!uI&Q(VDY;hzFamrG~_k)CpPm#OOf}(uqo$rBv3hCw{B^!RacYb z!7A*K7^B`-52wh_hk(bKX7ZzoiIUZ=3*Th^Ax1yGmZTX7p5qhPR;GW z?3tu0;qEdpw7*Og{ebd!L>q4r6{R+`M{aVw)JngUnGKN zQ))kKe5H<*e)PXA4A|Bwn#t^YYCu)UX$eGbFC~`>cmRJ&inpz=R09e7IgU^0xfs^P z81MsZ&fD18%YtLuN-b%4DEH?ZBnMo6-*!m3=aSeEpK&{0`& zcm-wn`;h>Ns|w&9$ZY193**PVY1Wp2W}ajJz?DNgi6JR||85N+6n0?}0RkDfnFoA+ z_Ojd4pf$oNl~CzOs4S%E)|=NWyrZt-1H8$};}blLAfkTYgE|2~)>c$V`0u>cq02(j zUX@kpIvUZ08C_km3W zl^dXpPa(E3n7epTX$@Mk%Tovke#mb8nIoD*(!F3h;M!>>*M5+FkrcHTg~%sl*JoJG?FUoqYED zz;6u=;&PI9^D%$&6G6KN=EL)2sR<@5j>dFLB@Vf?4aK%0G91tJF&Q+$+YT0`OSu9c z86Nat9Ul3Pg6;&|+K8A5e^qdpkfl&xGCQsje!JCI;A5aC}YLs}Rm~TnW5{$*Hu(e+qQxD#EM#ti2i&pm%tG(SSsC zlYBwymkpIJq59^`hCas6aJyIDCmb9nnK$_Hb>4}i53h}nx3?`CgApKY!@^t}3fQ4T zHq*|IZ#XL0RR6CVp&~})-`ddNUHgd)>XdO2Avs=<+u@tL_%+Y7k>TM9Q7Wp#DTwHy zmPGUUGivP+xTG0S9{DDR@<&;LO!Ffxfz8_GK{RU$bJ@=?rfr8tCa9?j?UhomUK_gn zUIpxtYv;upa9-#bYL*CC6tTlu8hE_j#GZ8+0OP17NZioTk)U@%;+rTASN?a}`kb#+ zMmz;a=Xad1-zWh0)n3U{lfNbO!L>8;9&koz8&z4Kq~BgSmw8m60=7IxXK6?*4Woke z=ARDT^J0X~lUW_267;w_k%rBkZIUISBf*%*++&FFrd!C4IDbOU`ocTWUY$)vM~Q?j zmQr~|6cpkjsM^~WpF{$Se37rOaCJ}xVl+{Uzws*^8#RqDbCk5mgybvbviW?YlC0(S zc}2)6b?c`crk3SWR0zjoV;i(JL|zu-`$sRf^GrFA8M#{o)JAB z5J)%@FWqt;<2>&ii(32a;A%=yH1VjtnRwcVnwVbh}+NedEpKD106$fzdLuD@+aZ|OOOu+ar7>f>PdB6ouy&{_Dq!2d z_^D{3Ru5moP>ZEg6!eJK(WpvLmkL{7!$X+W|qMR(y;wi|1!rJRGdBbN^a`;?T&egIjn zUrl%}rb=RNUMqG4_6V7FY9|2(^1Gtdloi_&1;6o^(~-7zh5?ZgxP6>+np_)hd6#(A8s4p{lOfe< z3(u~0>xrA+WD0Es4Qf1NzHt#!y5u_ux>HYxk`#JBa~>fS#@SuSd%yHTEnlS*^|e z8027#7^4E6hzvp4OWo$q)54zc#%W(&NavvGp52+Pxo|LJeR#Q(za&Id;zz}vv{X>F zwy)sSho2tsMXwqGUmc6{7$jQ|A^K%&B@4)dqd4u4K}Y9zEeV_Sig%pqU$o*9HfL-o zvgDj7bac^;v=+Sw`Vwx}Af)%RwCo7FJvm$Bls5gYc%w2(o7VRG82}sAtIERY5i?!n z`Kz-tnKrk@ux!wWso8t%0O-VIYNyMG%se!TvHNQz;E&Lg6Gt!gZL~8Lhbv9Jz}>|S zz}XJ8_1=D-QZSM&QyV-!?VunmI(9NG1o}2f0(jpK*TbmL4Yd48x0wyTd~mFtMK_r$ z#>mEn9G_D01#Yd{0PK__^)7`=7o%h!uye+tO|Y~JegCnEq$g?3u5-JPrCa6>GUbvX zQV%0sn!WeI=h(H5%x;*?LK2C_Cqq;r?!g#o%koSr({1ettg0aE9IK;9^4>ED6&C9! zTEz?`dnGl6C-W$B=9d4nT^T0|}>8ITf7E_0%H1A9?ULup!2h>JNPKI~I z1yHvhuS|)QY*v%~a4{rQjZbel`{L?bE(6nc^eWxuE7`1R+o5AJo!Wv|haG4Rk%%Xw z2=&`mPe8kLZ~>V1dcTysUzu^4g0*?IuDk4F|IO`0&`}hU%|b0XlA-?9oS)mERWl!x z40)C;{r4-_s)@mOGdaMJSfAITzua&n;2|#AGu0qm9eF)ARI5(J3i z#bn$t6#A#@y>P~=UH(=q2O zvk&oHy2al{CrN+`TD&QmW@bRWw!zDebq*TsA~LM&?=t&IHWcA_jZQ@;PrI`Dd7rhS zLr7j;f}+|+yHqV!(y(_!4>R~%)D>uI!Ztl+SM)2d3UDiDyPmY+Lgd;Pw~!-aWOr7WJZa&csoaYdVU^wip@6 zEBj-1Wc;+&n+N;o5%{;J=Uk~}E>=(h@D@*E`zgjt&u(d)=0H)y;yF_T>h4eH zW+r6my#lU#;+B{=NgVi3%oi~M-%Ar5D^u|CU|w|XkYbyEF2~rBuJ3stpxHy795|&KFuVFZm1;M);#(XG+F}JEiVg+i7 zDg`v-6EcPd%j^30F{_~YopLm>K`QUEzN?3)HG=%?KnOHLD$aY|1#XO4y^x0#oyi_R zUZbaLC){!A^~k|xF|W<+?8cYHU-?ZVgg^TZ*)|?+)PzuLC&({dY{`U%9*!H9#B_Cw z3E>A9xs{5Nl0#HES(l}0R8ukOOONT3+|55PzSjMt)h^JM(jgl|!V|L}_JMlKJtBi+ zxbR0Jkzxz}uh))?l|p|xBhn%6DCwQ?x0^hY=e+&YN&fSG^!?N{OUtU>?;WIcl=5D^ter+;;kPTKK0LS`pzEL1bel@Z;ESQ z;HbJ5P)g+l;9Iv;@!MOEktHq8l{%2wpFV!#AbG))- zfdS@jwY(N7DK$TRy^*A1e=QGF{iO51#B>Gc{Ar!NludCcnT!Iy@8Lw6RzUTyLZ=!| z4Y(|Ua%VLsLt*a?5eZPn4DfwNz3MuIdUW8Dya!fm7FszB4;(G%B8%TW$h;B%3m$Am z3PBVaeUc_eYQ@bf4iU74f&(x%yQHEg%wmFtEFtbr>mIUTNk=(fW)#g1)IhoCxEXYm z)c#t+b{%RjudyVt{quZKA@|%r)u9J#PKeC(>O4fA>NrwiPy|DGdjlmh(s*`=>^PJ5 z9G`yiT-eOLMf#E-Nc}%aFKD&CuD9G9bx!t|a@_OPP1*Ottag=hy)&<-_?ohx?hgWZ zV7fFjk2B8y5m?MjRuIOXAntriR|@gh_?OhJZ9?=7#0%X5M0I*bAx9w3c4G;}qL0X$ z7&5a-G^4hLKXa@~H+Rsx3u{ptISd$sQj-RjW@R6>g%!V@5F2pxCNx?__KmzrjQjaI z@n6I*wE)*rM%UBgVFuf0gXVED&_DW1JWE87dCy2EC;L3kCo*{WnP|D>WIH8;3D{s$ zk|{e0hJtBoc~oY^N7z&w$1TaH4biadtJ%+=y-#s^YC8z-v4O740TBW1iknu|D1tvR zH6@DAM~B>4*$7bSp@j ze4XBx6iRhKbq)ueem=?@4CoEenrC)hx^u?l6{O)$b?TsBVwDh-u$dVcC(Y=1TzVm_ zCfNx?cq?B}RH4TrcUTOr@Xu1qiY!5x)eSSgdMI4yl%Se<1kAg`o64#J*^HRsv5-9Q zoU`+t)4DTwYS+Bqqhi5`rmuH({$B3ns%3qFmq|{>VXCYwkG2LUlp_U5UqJ=mhWiec zER>ED8vuZrnt<&f3JNx%>Mwy-N_n980Q~8Y`pqp~yKRpbWqD=(A=EC|Ht5x*!pw~H z&->SqiaK3H{e3%Pmit|TC7jIl!R!BNh7IxafLElb=@nyt18msARTUcSnh;57HN9V@VPaOK)1MWPPR&>%@ri|hzel8` zHDxb+QxOuMeez{+#9-I8^x`k5x+1{j-ulf_$-JPbqga1w!K=AW?%E6dctVxvpfKXh zVOXvtBC1IgrVI@&33!{6_lCsFPB(uLw!Wr6rF-?j=1>tPI#H1poR|Su(=Uc5Fwj@_ z7`j^991}cVaa;l!MB3`e{1pZX;Ew={oKqAF-E%dbAMEatSF1<_ZFVra66di*9U%gO zezf|L%GXW>AOFF_zb4lyos&z)e%3#d~-BP+Ojldp04h^JWaJb;=GQw zzxCyZ4<_3#gQZCkZxl`b1^YmQlbrly{L(mu{W{WlbkalO!nK~PlWcs&Ct~WK=SR4X zw~su%>NUN9EwPdq3m#~KpF5mQ#|55N51GdII6AV~-Z{aoq@lqGU?kKPop>HdBu zWJ$5Q75FFlykU?Y-%4xaxHA_flJ;z>WN5^5*LAg`tNO7Ec2%FXf)!i9dXo1sT_w zr}a1BSc$I@S#18Ji}(XV`E_MJla(JXWAoCETLUMud$9p5Q1#_x_UEcbd<51)Czx#A zwp0z23Ye462fd$WQynM|vOY6skc)$F=RSxz3Kc#RNL?GS(wXC0ubvt@6=h=+|x8tvZXv~NKSDk=XrQ|QYOijOCBK3 zM|LAj}qw?WYkddyk>0ir2i zm}TSZLsA7~SB6DeUOFU1?^y#6i}kcR_HPM+42|u$L^NJPTaNJ}N!Ku;=cWcy7ae%`xe!oH6PYOPwIXZkX6S-l94Aej?{% zdz-7BWG=Vz+`E|_t}q#fOFu=T_d>bGS&XvW$ymQJ_;}Xt%8h>NkhYsPuzP}iQj&&0 zE(|#ao0hS1>@SH&A#c+IlaR3#_N3j|$)dCa%9K>%gV zXVl4AVb0U_V2njhNW|d6Kl2)9>j~IlH?lvv{Nqo4QWf@!pMC8JCXtzhj<0{dmTf!` z5b|x+wzBaI z#rkWyD{-!f!HwL!M_xCzMmOlGMxT6|?6(Rcv{|oR)Op~p4A@fI1b)H333KHcCig64 zhA7KK|H_re9ie>FXj!S9B)=Q3&gA#^RLW zC8%*dMAxPAMG~kd5grLg;H;Po<>Fa3(y-mu_3-yFBFBKi5s-dc_iiNOFCM{2HC^eu z$@J>P&rz}EcK3%nvk@ZbMR9Jh*2)yCFHL{epxcwY{HVag{k5OGPi80Wu-`OgYSzTm zeW)LNoteR~vaA4iB0yPZMxT88lrP6J4Xe5TzP#%o-bAjN$be&5pedKudkeQe2M2o9 z-Dd++%?pr_2$h;vc3};r+WN)>1IVX zO>`|b!2bdH;GkJjhfr}}4Tyz#-!)f0gaKlwci9Av^T{2&%X48@nwDGni-gVNP<<2F zZ^{dyIQ%Rn9odVoO$uU!KJ$pc6IR-IzfC*KuIWk}}7E>eYLt%$y1$|M}vX#LB!g^Nc zOpQJ$2-ic!+$AmmM`(#@Mz3Y+AMSHAkxc2HWS`5O?Tpzlg&qtwS-GX)azt?4tCY zfnsEY`^p?BICDKQWbYB!@_Qrv47vT+Ygc+Ms-{FccP9}lx&2&uh)eh6pTIe}BCT__%rBF_+B$xwItS8!I z=C>~;qPC=jfwh*A%JQP*-+79Ng@s;zr=aeHYFQ(h!Y zK4fCal;J+gm2D+-*l&ESP@q+D*Q$44Q&_E}i%$NfU0ju@;AiaP&QgJm%y~ug|LUco z6!-DBg(Y<2@3H@ub#;BijwZmPsxQk{8X0U{!8g!kOIXx@dL``}$CY4GfA8H(y->w@ z-U()W8-yzEinkw5?wC-D^-lzoPDmnSPwz}fqT?nNj_ENz1xE2EJh1<&Ht(-%lx{3} z)#L<#23J=)x8GKZcfhv&sP$!3naR9@aSFg8kC)z@`}A<>_`5(bA|XxglRrO9b&djK z{eiC^s&=m}<+P#Dj`D0-9!sYXC6c2L*MWH@3_x!}a^9g=zzGcO7KCzbgN@z^lCpQu zWQe`<+PCD+1bnCu#L9 z-@hy&aVN*EvVv49;aw6T<1!~i1|!kBq~szQ*9tg_^KY*MgLZnok7$&IaD17f<=q3^ zSAMzXh_r$l)Tv&|y{ev{uPB_tt6#4#HKt-!70TWK+s1WAy0)UNzA<{YrU9oSi|}fX zp3yRKBAg;b_=6)A_m`#exv_Df24*=|-?@E-cNPFFLP5Rhta1L-6J!Sz(8>E=^!sFa zTM-N->!k2WNa4A^SSv7*yq5*aR&_miF&5pe=d`$1KP4c`TA);lwVd?}@GNWo>!*k9 zst&U`bO84-TF3+=C6bn@tcL5s@P*?8%Kj?l=>gr7T5dzW4w#l8TLQ#_b@``e3z7I+ zCcn$2mbN zBs9V55gK-s?Hs^#=Bf-q)^%77Q;=HAAbJF3p{7a+FtA2~KHuUrdR+YEl-qwIr!3ym z{JOZwdjI@-I`W~R`OfQ$qhbTH`WRG=!k3s9byoUAS(od{euOk@V}ox_Xv*XkjX@~L zvBB3}Q9JQEAs8D@b0*47<}wZ2iHQEbiN&E+xN*rf&1ZymD({*xpj0bDA~M)Uzyp}G zvyv)i*0$Uft8l#?41JY8orW-5xDB-pep^n{mH2hy<8v~C@fGK>g1?ZCm;K|(ac%EC z6WTe|tJ&fo@(7enP1oAx?quF{gx;e1Xwa+nt{B8={{)ao_aszHp+Ay)5;Zj5_?bIL zLXUGcBRY&Sh&@ieaBpG{9#T6IQ$T1SZXPV5#D{9K3#ngAhn2(oCfv(@5Kgqh0= zLe-@r?8y6)!pBi?DOtmy~4;zJ&9zu&u zW=DJIXPM$~bg@O44$*q6@>H-6!^+5hoaCoyt)b{p5uKrQ5xJ-I*u$?w7U3dGD{=E8 za|7G^p5iD`U{jPXAJ0zDc(m9Fk3@M1Js74JAv=eH6BGj}4#-e!`jWqz_GiYw>laJJ zAPOV$vxb0>K_wUb_N0A-haA*^y<(ecAZ742e?TpBHF6hzY;xq;0m?AU!68T|asU!> zTpFY&DHZgy0&Z!<4zt!v>vB1hvA?=noJHMNo|)ZxX83fSdD`xE+MJVAJxHTC4kw6O z_q8HI|A#0kOf5!f`4>xxW!tbW3Ht_@p5af~(7YQlOH^lnZgJPnXBI%L8FL>BTIs@B zp~E~piel({z#?0hA>CFOURMI=tB7kULy@D+G!+z$*m_>a+&Dmoocw`ylC-Vd z>%eZYxYyQXVy~|MVai!+n6`?_=S7+$hi>O;k|k9k$j&L}U$MuQ40m;tlPXq)M7&mv zIbn;ptyQgd>}drBTa%7u{p7NmM={7IiiD;>x5?X5asVem<3XA3JJ~6wV0sHatNEk+ z^E;{Zp%RFV5TMC4{@hKL<~rqo6wu`PkXoVl74z?`$pN=6IjE2Nguj}KVC|+eg#r=6 zK`be|$7lMNKvOQ)wVNH18PaeL5u17)N+5Pk-D0xWzDH~{|5~B=q{`eJx7VZ3uOJBKw>nFI2~rS%2x}<2USjf^_8&R5Oq?GtbPw)eu|>hS8m4_e*F|A4#k9cwVxZo zSqD|SVVh*lz`crD=>Eb(Tqu;5em-(KvCKWsl}^rM^|4igMZ#qJ*)D%7Xu?;T%hAWz z0+lA@%8Yn%*#ZiTYJcG9Hx(>J?t7GoC~8e@YUQ{BRaG9lqK_m9VU*g#)Jl#m z>=yml7>6jD!Uylat+DJJ7F`=1`O{KDT?dyL_F#q%5+3htnFMA&F80uW?FWQqa_#zN zGeed<-WFf;1vff+f+d0HmTvm-^v^*h*Da+;3cNo#8O6DYM5chC_fP~v&$M(Y=iUk) z_J^wgCZ1rPM5J)wwsdJ=e$XdupOm<`WgIv&r85qDuobrb7b~|(zD>W)vyo=;YF~QS zQ^#4-te~j%lb5FXC%X(}+q*$%69ZJTYHZA1Pq_=GF+@;*7f!xS_d%|Jm8_-L*BoW9 zhkZ9JSL_`#*(cEfUIrOQd;b{S_9F-MRbZAheZu2&^7ZId_hpxmVu{G39UAU2)~Ka& zY?bN-6X5@bW(WpBFZ!~amgst`LuMpld{vdD1%U6dG#$!qh*(>(EVvVA(tyy%;KPoP zV`+O(gMkj%^LQuFi4w>{Ke7ilfF%@H-`^sU4fq=TGKHovBAu;%=XJe}D?)XxeOjMCK-^zPJwDS7)M1@ME`r5RJ=L`S-G1Fut`GM;AP&?k#B!cC{Prw~2E2Kxi|k z0OJG7z2Sks>F(vZTtAB5*E|cWvH4%h-RcUka7NQjutoN2UdP7Y?P*q7ej3_eWlL{w z{s-lHlPnv|4--GcPv&b~5Qz)97b(N%Yc}OVM9wJCdBAXTx+2^bwRcotreh(7nO~aU zk)5t8Q{9&^0h3ypF3KWjE&W;-nY8AKZQvUHy&~pBCPzPlzcytGMJ3 zsCm6^v-u@7lo2d(-OWq+FxXhh2A`~gt`9F!7sLiZ)8(rxRslH;6W2{A%GT;w>kl9N z&TG?+U;>^~*Ad=P9mLVoYt#%OI{Y|wc2}5al)9B>Kj*N~G&|(TV2hqyuRByy+z-WG z#@cSqLSx4ZrJg!-YM8RPp-n6m=)>7Mbqx)wioXH%V|{$L=a zCY+b%ob>#O|06xhBg%ei?!9yjM^YhM#!YVlCz1O&dJF$PQIfo$DU$~hscl;y2nl{u zr?m+#WO2jyZ=df|Ds~3OC(2q+I<34yn28R`LRFyNrL|wIW0wXg2yaxpJ>=EIanOiJ1UfJE^L?C;P<(4hH!!P z#?prcThSW+Lyc-EFFnFu*dQWovNG913V2Qd>JZ?G2|;l+4HL=ryAH*xfe{%(>l$Sv ze2DaP4+HnQc89Y*ri{GQ zo78Y3;Zj#kIs0Be{^lAOw%*ImAIJtwv%e=rK-g#=S8PHuzO~o?)1@O2NNg3P&eiHy zqvg7P$npyF&}V4&=G0b~9KB>bTJ7}{oj$t9;)46tZ_P3;Sum@NbgsJ3ix9qYRCOjq z!4ReN(li!UH9cGSB-2GlrXJ?Kl7(bFG1NKXpwCxE&W&yiyWR4aubGId#=u7zxM!kq zRyKNOpx@E-&fK4z1*P?axq4H?Pbg0?5pa7icdt zby%v_6bAML5>zn6Fj#BqQQQJ)|Dmucw>cddZbZdRxiOFX{Iv4GFC=c6t%LmbIu?l#2QPnZy?T{urdV^o zL+ct1VyxKelnS{OZFfE^SKgNmrWcxyN#$Mr@z>0pk(k%s{%B1b;cQS|{AAtx`&D@C zY|2m%JgJg(+46sL+2_YN>y1)@adl{uIiM?fdg);p654}W*6{NCBsN99h0hR+6gxiF)sW~bT6N&AM z7(@}09IfDG^G9wWyknZRRQKH(_NkuEds!%9{d*?HWDjRQ|DMTQts#;#>tqTV@NY;# zwPwFh?|dzb@716KAr2u+cnIQsz2;-3BV(z6O$S)JNmMszp!&=MD;mCY=@tj{Do7{mQ7XJ94#Uw7HL=>Z*+pQo!=djD;*Y2|jA{pbvz;J=O)_T_SH z*K6+Z{Z`r^SzzVBK*v(~3AoMxG9+;eQn~olLbGYzp#!~9Z^A7XRpezkn7r#Nc$iUm zD|i0ZySOn!H~nViM54=~27TMX>4BpjyseFJQ0DT=lEagqDu`UpK%Y{AJfko#*96)- z;%`vCfrK`vF}^HM_6IDTvo3iC%{@u_p_)8a$BFq`%=Gncs0d%Lmkv<=uWPS5FPE}u z(d(Vu;Nyiyz*PFI;%>bx>aE_!*X3JQ2?7y;$)Fo+o1K@7EB6W~LT8wP+up?2KIH-1 zba1T3DOCcq1&bn#Z#`Hlp_S3bQCSvQ%B5$UV-m76=DUhlOvZ0Tm! zbTxbJ!S6hhPMxDsgTcrj28YE47r>YXoTOFo@l~?DCfcp}U$sM8TNiz5CKyVS);+n< zF^^opwz>Z!1zSpk4d&fF$roDYTh>b2At(Ru7Jx5y2 zNtQ(xNJKJ-IMT{)lRK77&Aq-Fbm>aJRK(Jenu=~A1BUUov%db+b6}XZ1#MudZ=#%x zRIB!`?CUfX&rXnERnq>+VvI!OdZF1H3muRnZVFkZcq-73V@2gbY7-%o!|tj60u~L} zs_p=FGE!g;6x&z8-{b;T3NNh8fqT5E#d>q#gpy@j81eKJ>3839f@=df58R9joWO_X z=THGzqdzD{rTIfcKS&r1^aKnFCk9Rg1ns~hI2Wf`#~$}Y5tj}W*3WfSm9m;wR9s%i z{H`Fs9cZYckJzA7D=8*)8HpRPctG!RrqMMwsx)``Y(q` zkZhUQ7x$o#Rlc2p4M~6r-1U!x=d`$t}N#``9{%laHmdA%2 zL4$jjYsA0>PcuRsCtjN6)3$jtxEpBoZseEcHoVoWe!MMAFgC9QA@cAe$Djm}p#x=N zyoFEGihVG2zp5*_jW8E^PbaA|!OA!0cesO!S5rH)?O=#tE14zXX;$L~i?))t+1T!; ze9$A+@4^L=*qIZh-os(}9{(K{8tRr>{xOxWlSANr(hDhx+6sf8NO#E50F_>C4!D3A z8G8{Oh!S00F_lSsFr{PMfD2{%KUBR{Kpb7yG#Uef1%kUIxVyW%yX)W(Ah;!Xkip%Z z3GNakxDD>^E<x{W1|l4 z<*XfoOfJFh>S;~W$hW3z#`){bks};r=ADhP>6?~Fw(Ou7^k~u4!%jxqiK@ut@D{9g zj5^iu`^7=nA?vCK(8;iSWGkt5IBA7UFxmIea*EO+ z$`ZoHK{(N^AE?4ON>;x(r{9LnD@I4*KJA}NwqwH(+(62dnF)YCi-3Y8p_TB6XX}j4 z*$+|sY60f8YG1adBen{+9_aO<941cV^i~HyuuN!w1ZC#~+yo)V3 zbt5^T$0E>R;tFG5wWR9@6X_O*TW)eMvaw(c+i@sr^txoF82lclDXHc2h*xl)PGY=$Ok+wq!U$MweM+5jxx@DN zB^=(wbZK+g`W!fIAKLQBoRhGVv$`p?ei%Rk7jRQ$z8&BPW9}I|a;p^Iq75J&9Siu; zwwHUxEV`3AcNCyIHQB|JlEF5)RsB7>*E6Qw@3Y!U+8k}S+ssv{AIAhA29W{awq^f>J@pN56sZ$^}`M|eGXC5Ctu}Mh7cI) zIw`pBjSuu>A9}*X?$gPhiwH~&vTSC4$Q2{Nbqtccx(o z=ay9OUIcJCM=O;3V_uVl8GCS@J1DeMZkrzhr0y31EtFMfGTL_2YCc_GdIkFf9Rk@wF>RG8*}F zN)&<*31(Eo)E)ECNEqMSJ&{Xrm#@Q>{3a{jI&B{1erW1ps~7cbMGHd zxRCI2gv?;5cu2WQ+F~!D>!z5RUHOeUI~>pLqU#xbuAh8Yp?-&$P{g8(tVg-8IP&Rf ztxD?UCFfJww}H$DfIORb5qx^v@M)%v0`9>p22#grMX|m9g(w#sD%q8f>))5@-A&d%XAUE-nou9u86!@f+9kCGzZ zfDJP>t>x?c8Vhfx4$xGGL&johifNTSfh!!u)qQRn54mP9F_XwJ;HlQ6)rd+uBv`YY z#hH+l`w?oDtlg|dX0Q&+STvUUJKE=b~zaatERcKUhEmc8p@ybS`~EQ4;gDc81u$ySm<6KZ`Q zRFoosWpZOL)y87GnnHbFqt!N#h`IA#>|{K^U-xlZCRnow9dM;+s{Z-4y(!K+4QNB9 z*dICBA7!brfCxu83e5QA^GXbqQ-?!RTk#iw{^y@7(&>Qx`9qhe50UQEp{}Wt5n=xm zzHkZE4`;RU{Sa@2ezSX@{>*zHWqrw~Hz_lY(!8{5V{Nv3)65Ey-NI43a%fjUo!sJVHOma?U&6i&K-s@)-W$eB)EEsiI* zZ5BgnwEmWkL`ROHOS{~s3_3TaIT-fc(h*h7D+!N9A#}R14UQ20pg)>85S723XSD2mbdg{tj#FhHQDo$t7L99_Eu|Nst7%g zU6LP!5Dp`Eh?}hKf1llemv+p`2#lo+*&Q<=-Lg4udTXjX{(#fzLI+_)XWXy=~J`O1I;ewIq88ec^ z%D%Co(1?$<F9v(C<)AMK0}_&+dq{fk6;dKw==iHiXXRBes&Pw!_DpySevXv@-v;tZ0-(=N)5fYQ>p!&(MevByM-(;=eV7!e z)n<&IkS1@e;Nej`J^GZk*Or5#xJR}jnnl$SR{^PuFXkX&edT2dfO{V0O_jU>dxjEY ztH0380Px(xyFqg2L?kV6s#!^h3CF@)KuOr)@NMs?{Sc88jmf?gBG@Ur;;nE`O~^JwfbvHcMe9-4+M=bM zGMY<1ns>Lw7eqNU*88kB7lT&uO;}wr@1yN>i&cbad7F4ov&<=A`B$(FOF-;>oR#T+*(I7aI06!RF4{*glLhp3beZU7p(8i+r%(({bgHZ_ zWm+eZmcUZ};AkORP=F<1EmZLX-3$%@xfmjvn;2MM8M79pw9Uu4aLC+Fb~Q@UzP=h&GdS*2#qZqd#Hd6(u)nUCvjuC`1>WIGo{)4PblMyI>Idbo!7(I%r|>* z!uGN=H(io_u`Hk4wY8&~F8$A2kvL!d>TaCq{@|v*$}{SugoH{Xj>q)3+(hKQ@?c+t z!}LM#YiCRKn6}WFrKy>nEgjE?)ah)7u0oZ8fn9^6+!ldY!blxA7eIfUYBCYeC7dJY^weFHWFeIBZmZVJO*=ai6w> z4cLcbVO-YKDez!|0F z1GPI4W7ln(HufS;W!v8fJulNEv^SuW(}k5G@z(n|JPi@5>{Yy}WG2=?E(=(90s&r6 zopvDDT3f@XV45IFidxuz&>$+VZ3-a2tlw&GkBOWTEB~N9T!J{&AJ>Gm&;*kNHlwFz zq3T0s+Ec7nN{F=_2}!JY7=p~vCQ&%FByYC$W0s56OEj9g ztaBnBkoOYTnz*-nO_plHm0{vG;m!6qf|2$a<*mSKS@NUnUt{`wQ?1yD{#qk1@*f0~ z+CjacX3@0uNX@rvy_!YmEH5_fk1>JPXljuQ1Dpy)SmVa=>@>c47n+`Jc7@#(Q- ze`sVO`D@?3KiZ;o6mXCezvClH8vk1<5%S5j?aC$*a(P}EWcZzT7cX+m?zeYw7K7(z z9(|tsHaZGTk8-nWsiO-R2lH}nv$or%Wp1pYKK`e9lMk(TnC`?zfAT0;C+9) z7IeW*vkx1$0C;m#Qbz}f&|&|Y#!<0;R@T*V-%l;T!3aCsn(HeXz?&!o zO2H?f+P#hQybhDYi$wy)2@r#4yrr8N%{A>yKR^HnKazpb3*wYZKk;yaJVdqa1nyj@ zJ{%B=@att?`k);qK!sT@2MDBdKC4Hy%mkyZ4rvV$=)@8F8|$}B@{-HBb`*SWG^jwF zqWa{79m}0-AUd;3_@kvKVk#$n5skxV6>(CeZ6kQrE|+s<0=rNS#4BmKNI#yHk-;KA z4eQUu>)0<*eUH0^hg)>^J2PTGL}jttlgS+V0s!}yb1Fd+6|cv?vzjvpiwMr?V~l;* zqkR^H@82;o9y{{lm;f9<}bEq zKsW%}o7wV|WXx|u_%Sb+Mad~SBjd7i(@!s^2xt$tajTYnq$q8`cid4a6Lo{rz)(=C zT{MYACgMcr*obl0@x~GT^1I^P98XuaN8D=(?B-aB-tKE5+FKpz5C**4SUzZ-3!nQmhL*fjV(d%i}b^p2_iF=PSYdl53dqibl(1P z=w~EzN7(NK(LBE(ykrN)dr661C(h_w^4ugk8cio zB^dg%K2ylIoA#*x3BRUt62bQu0}|s3%#uWZk9cw%GU%}p| z)i-%F@rdjfy$|~401jU;jt(?0rHh)>9#}uXYIg)H3du!JVugL(B9b$qzX&|5{+Vck zUH$n&^WkeEf}{BSZiuy7ax8w{c4iZU3M=7Fu>oYF^?-7FOTOIzDwe4H9LM(0Y*rm`>%2*SnJ# zoMh9JAB`oakmAXJnwZ6Fj8LWWH~puIsQ|EAhDLji)ccxG>ihUKrhYdWz)+GN*fJH^fVvb*dNirp_1=a3A6c#weuQ0wAhCAx&D04-C1`XnO4e zXibRUg#5;+;^&hD6!W?;=aA7zG(crpBS5Q&q8QNrt{g2NIzZ3;`pm)#)%U5`<}baE&hMK3|-cYb2Q{D z97INTEq-XmN(#M=qv;9r(&4p(`fxj_H0vD~$ksB;e_+s)gh`~zrZ*J9G%*lh z3T#lrPX1b!8z7!gD$9{Im4TR_{3j%2%18$AKx6qAw^#fhBeY(V_kPt+T-x!YbTI}l z3qk_LwSPv5sEk0JB-bnyT}l`wS<9gVhj#wG7&%?}vlY%4O>EP8{c3nV56aV2;$<3@ z>a5ydgm1h*Jl{9QYOOq=`Jq2mIF#*LcP)dqDvR&65kKXZs6&`Lxa)GmLyf-AlkfT? zvj?~sZYOT@~&_))G6(1hy+=66j3(9ah4E&G^+rL-OwLX?YC3}DxnTfqyR9% zUPl`Hx(Hn|Dn^yUe6FM7$f%Ya(M}tal-|1n2H39V;)K;`_w%Ne#?`oj_V?B^wqFtw zJ>%9zopPP{223cD5I^8=2f$nQyQY!N6OmFJ`QfeaqHe?RtGKDD##;-D!WB#QhF0HaDfk|RNS1~WNT!*SCFH2xV%-{okJFpM(3;@+KGqra-gD?+ ze2_caNJxuu-(Cx6I~Jq&H;ksqO)ZB7eT?avMRJOeW+v_L$IsX$O4BQA{>pg9+bdur z0KYvI7438RwSO6Meg9^d=mwv#WIv7gxd zORRc#+8(!eE)NC=J*D&Wz2IAVA9<=ge0jA?F&f_#mbfo!`hI{Oeh3ox$0(vBknQ-$ zkjC|7zgb_2N!gp(514MMDP6vL$7Wzvh!kedIsQYc=<4|rEPqO!V>e^|=d#Spe?pK? z09gsp9tb=vm62}G`<1=eirY&PET>qs)rX2{NEH`+Ui)4jNO6?*zgk4l7|ocXt-gH? z`W%$zCm@$jFHxrw)LU`f5m>(j;O--mRc)~nsHtnMnq@NitiYbaB2cLm@%;5%Gf215 zh_QGdVyyutje+gj!z!1UYFV{o8^K^LW>s9paRn|w)-ZVoY{DQ?XqHE%wa6ki<=|5V zjpj#nG{nhLi`Iys9LB?+nx6EL&vV~qZDS+4!4+u=K6x>{kLRZS42RKOI*cg=ZK6Ry zu(LW8?Y;Ak2UIw$e@j&MK45pisilbLe01H~y`tKzr7-V>=Io4ml1kiP)37UznpZmt z>LYOZDP7|$uLrrDuj45fZPClPOyfk${a?mnnMCW5yIoPgFsqF87whO_mi|$4LGTfb z{1$LI8woRieaW*H&Ni^w=?d-|pP;EAr~7|e0N3#Ck8ip^W!xGKq9csygfazCioIpvrO5W8?XpWM1YRZDtdJ9S*fxa|Dy z7L6o)UroW2X_gi9!exe{5ti5c;@aVI_!+6;C4{tbDjI<0bE-0q9$b0#Pb{M~A!|^8 zIjFNxlX5aQQ@JD($3##6rw8^@hTLj>c7tji;1xT|R#UyvacYAtYftb$3=G&+x2cN( ztaUGdwUv%pQw93d7ek_C1F*n+bns7ZD9R>43J|xFasJ=8ugL7EnQsqsSiW} zKn4@~z%zTg!A7O&`8hQ%jag)j${#jMCXvN1v`gs)Nq1WvYScqj+oa-RTzaj%{nlv;;_ZXRH2h2>k{$QSV)q ztlCRYYhDV5UkMNFR)N?v#%ug8>&lrIPZ50J6)#RNW5$lDm4xKl8S;|+p7B<~Uk(=i zcQDXu;_W1?6k!Wb)|kLQyQN!T6xmdG-!Y4r#uYO(B_}>P(AOqEIV{^QdoDp`y$80l zF#0$5s_e3#vDHkKTi%#%Z{6Imqn{U^Qw9p_hgtaZcw>3rvv?8DpsQzObe2qmnRS|6 z1LzB%wPQa4Wf}9NR)Ff;;)loWvVS$%=$nc+S~{9YpVnhi7fI6=J88+QvCRrn)ALYW z6kReZj*-+j_w}!4Y>jnFWsSHeR~YK+d$litU6>x-rYW-ajjfArV>Pa1s!C$WC1k;x zs(;Jp5fJziSD4LllIo9Y#d;46v|Lb0^r18~+QP|(3jkd_i)!obn#9}Tl(a=J&@ojCnP%&;L{57ZTay3;`8=BaJOcQ|F!Wz*(-?Qh7djV z!cxaw`p^qsovMU88a`Dq>mvtKESht`h7iQLpFrFcIkvYx5lAkK=P4A>{;Y1piuGmR zG9wA`=J=IgJsyN9bT(aZo)f`ym3^Cfr8JOcH5^cC7GA{JiAr z0^ZxiE$0Z%!NuUO)MjE6ZLQN>fMxj~9etK6h_^ZI8Wg5Im zkBZyk)suT$_cP;MHh7>l^)V&W;-!{ zJ(cu6Yuo-t8KzK=545%Lu%O8_V!w;)_d2c+vJ^c064H2{`J8;1KI^Z!5nJoo|G;CN z58?9OyHn&p2MmJ+8IBpr&Hk5%fWJA>M6CgR&Mr9lbI_8ATSt$~G8eREwqvT9N=Ws? zu>8m(9-V{@IO#%c3UG&>0-~Kb4_|mICJIgGICRYkBuwIPdZQH*Qkq;6YF(<5Q2u<} zINP~|IQ0|~BF~mB4h9;M^-YEdJHVaT3rJl5TMk=;>W2lB7C-JOLP|L~Q^_g-hapY^ zih>W2%axWE(>jjP-_4a@QFiTAi+MkBvhSsGxX2BekcPeQ?*K$7sR4j(wmDn&$X%88 zV$`~tpH%e6&Y<+dCP4mCpX6-IrL!jdA>AlYmd{8H3Vr(g1dZHSiB%9BGQ6p9~uEDa`ThKT}7ck|}L zitG0%cJ?m6BRWGyO$9wO=I^t7v(MXTkBVLwq?83-D@T8| zK>0~5IL7Zdx3=n_WmsDAukmrOy^j9B91@jstqb_mYyAUE7AzA@vC)G&JJBtxUo@o! z7$hix6jI{AX!8)D4ITcoO`vxAPffQB;8y?D`UC2ag@{DJNqy!UP>=A#3?}Kw%DV$t zmacEYKsQRWojPzp0LFr2CNgDENV|X-C_cHn{l5Jttf+auuU~-{{|1K=@=pjQ$Fb9$@}v? z+}m=T;efNQ3JM%UpVA*bxl$PuE#CwE&&6%FQ1~{cU}WiwX}&Yq3K$yA5ml!0rM?#R zT_bV>$0c&444AOFdJ}k=3zJ@44dl;7jJNy&S{W-FSa`|TpQ&y@OTwA745&e=WMC~T z3Gs-kLX?UE4G|Q*O8~ArxE~;2jr5)w(rF6H;i|h$V!_8Uvxu0kO|NF+U$or}(zyCL zIj?n^pJ?r_#Q$r{1+d}neNX{#kjGPcJvaj_o1|`uL)oPOp6LYE)Vba#;eD5TfZH6_|QaRuFzYU0c3YOCg_U{$M?`Bhup z6zoDhhT9YR75Dj?>WOX-)V)a4GiE6YFEt|&U#RI?7~8dY)cOSsV#79&68J!}b_2M} zQ2hX$j?6luqNmy7{1pX?Q()0G4oAtQr1@iq`ZPY`6mD;2*R*DWS&a=E+XYLsV{zlr z-NbY>hnL}kW!2BMF1R!#gseKz@6QF$@9DDKIM`;;(h3lY_Tr9cY#Dgr`?EZ^=-C57$-(JATO9L0dlc)%GUjk@bs&f#)op=p6Kt2yqRePH$34afe zzr9{;k%dJJgA6g|=FH$A)+9BSzAGcBG!l|gj7tLxuG%$9J5M18y+7D% zjmRKXd@$2jV8v1l4gDx2btIfO_{}`v4`OomsS(jzO3~*b7{eQZTp|lyD=AP@ua!yfaKC(_l8>>UjdznH^yx-YvmSZLxCj^hPf{loN zK}fgo4!y9P4R$XB2~S!+b*HH8$#Po7e|cqTL@i8urVZ~JTzPaQ?G>o<*rDgEN^4sa zh_xLrpC+apKFqWEb&W_*5+k3r8Bhtys^M$BcH*jAXQAOFq7s=8zjdKrbTiwjdDN11VM+rm5QW50uLHV;|h_+6C>7b#6=`Z`ugr?r-( zkK2$_-8jAi*l3C8rbjMwClGa}i zVeWiGTKJK?Z&cy89J?gU2`?h=6KDLSRJ&M*%G9{Ve@`n}+$Tz(msE9Eo06dV zas3RU9xmrc8l)+`IU%^mN1Ea_1ZCu=?fOy#di+ws6LHd;_6NjsGJ#zd8+AF1d_RoC z|HdP6FHYPg=R1xkRS{Z(>ri&o??(R&FDv!cWQ9gBs23_0n2w*vn|n<4e=2X za={m(PfN@ik7>(oA7Onsb>~vh985p6C@WI6m~3CnuXxUf&pb80%*q6et0)xWb7hed zLSv3VoD};Y(A-x2{0uTA8IbYjxgU3Tr#F9%+%wB-7bF4wa#CE~4MD#aJ2+lg#j%$* zswABG0u2@LBU>FvmF)xMrfN01J9nP5Oe$iTMh1x=WWzy}hC#l@Q(p$VIF1lZ#-oyF>Tu_sEue;4yJ8lGt1Fop!JMw59^t{=}86*2NyIS8QHRp zZ37R~?h=>iSE1x7%cOt1LgYW>HO{9lYG>{28>pSH%|KrA@XyE-f#sWe5Dg)c=Is|e z_^U|H(R_uH!25j3epOYB6uy5zsj;u4^i~1z@}WRh3^vYDV#a2I7V7I~ebxeqBis3k zF{v88Y7o5)6*GnETd&;4xQvIid zuzNCL-d`2d0RLOwE@GvOsj@ie7)u?H#p9wbo*X&stS|Evn2{9`0F2^g;0$T%QVYM} zrTjO8AnCZQBWXq=tGZ4^V%FH4ZgFi~hp5KLDf9gid6(+2uP;+8Jg>1#6=a`Gkm4Dd z-CS&qZrPtoDxJpO<5{_R=f%Q|XI^t9Yqq<#MHJLZWn18BbcL25fKas@lwjCYB()5bVW za%$XTw1M=W?Ail0u87(^L3=M&Fhdgx{&Dv9s@B$@%<8l3oZKH^JF)qNIFxd=XSk8^ zmJa2HBk^?~wq79*$+{<$f7I_1`z_TacJl@?-jGbk>n-7!86#2#!&cH}-08I*w|pVf zO!Oz-PrPlTC_`zR3d$bJ${yO+?Z1@1*#1m+%f58W`B`~%*4l<9BJ=W9Giy2E`7-Op z=j9f_U#-*d$B|_@7lRLkyI&!;>8|G5@wn0oVY8+>cy&EiGG04*e8bWm;^JbTZj~^+ z5-bx%XVxqG`7CxtYTDk&U%-jXV@}SOaUFNSck}gFZ}%)yRIaM3iM*ZWA!|{CE?I3B zB_7>xn7Y6W4o+sZuX~hRng}KI4Ha%izxrlgW!^flLRMTRB*^bA8J?G{oofTX{nNx=B`^z|xGUS053Cb*tqbZ%N&nUHQ~) zzi>OZG2+_8)A86Us(){fj-W(FcoMk!lWeJA`7}B9PlB1H)UN#>+{nMJrcjp33#6je zkQ}_>yp}f@csMI&CS5uOpFViuE?r5QQHUBQmN4)7pD*yfh>y-kT33BQlTLYPa>T*+ zCKtS;wW9PZtM`GYEe1C97vxku?WS`$ zaT!WbfI9(loXcFY@%^TjA^t)(7k2pxF#0HM-vsQa*OA8QgZ8(y>R-@LU*4R?4{VDkgmU4~ET zUF4JxfAJ|hu#9x4f6P_%hJ+Wqk_~@;hs?z{FDva?UYK8X`uWm#cKxG6j_6V}mVOk! z@?gJweZQpD+_=%^5(c@%_~n-smKMes?Ha4h43iE@BR^AwuZrKUWc;=#LUC$;%;}PT zW$R7{s>V(cZwF0w89`CPH-(MN*XtFpX!FJC7#MnSfS<;Iiv$g&7s1Gf{YHr|Va3QA zU6fBTd*nXQZQ+n!PI-WZ$}In+U-Yk-8ytrg9>amKP^Vvihu6=pzzPV$U`DzwB`Rqi z&DhRIhiUG!U#rk4sb)2Oh+8(9Iv}=oW!ZqcQs8V^&k@N;$XbC(A+fVPUBlN4Ce(S; zSl>cd=h}acBgFikrG?^|o zyFY=SvU9)Lb>116=P`GfO{v@m)}dx2^Y&{; zt+UEsPm?R!;-i3*5xYV&ezHALX?dgg6~UESSqkSjsu%O`(Js6?OLuya2rqJo7T7w; zrX)1x4F&l_co<0p(QzXCTc*|^VCBiYp;&FzwKxcwnOjo-g-zC%tw*OzuebYfWGk@x z^WO_3n;{Xz(0wsqpDcpu?Wr1B6fStFJsM7uMI&^LSZ%nT_b)rv?Rf zeCJNurfoI@P&(m{nUQQQgu=22JhIsUL`DT|>_M9KYYov$q9-IltYY$h+Hpg;}t0=Lblq>Rf zE#YMHE%(~e26dg{D$w((->chw8GQ)=eD1Cd+Q;b1K|SUiPd4d|V`QhvXz|XF07rK& z{bg62HwI!EaN2^(M0*~zvzC=ft@sb3D$C|z_b~3A3TyS{o|X^HJ;U!NF|CEexFXxw z5T0DE2xrj$__}tE<&FNBD8mWs6kSJuhF>Qpm<$dfA$D3@sJ>;bMYD<}u|M8|BbT5? zXF7h_!u`x&t25g-bCp8GZxq&e`yjDzb_4>ZXHNl^n3=PeHE#2jTROw}lXHE$_w9_* zq;l9mUrok%b!xQS+jLEwviA6Dv zM|DgW#mGlMH1=(jsqjK`ne+qpX54}+&uPf_QD?d&TYbfUPs~iYLf_rlJYU$;#ZieK zNb_#Cb=^9_f963qsZ9RkEb258?Xh#s?oQHQ{l9ULysIg4=iY7WixuMt0|7hDwDCQ- z8w2;Hq|!&ZCjAH1hSZ+T`?}9dIAo{A@U;Dc)JhAkR^$CH|2*`id-P29taW(!`?xrz zuW>eX#I8&SQ5SgjQNCYC2|#3o4d6x-(zCWS(|j;|-idoo)^ zq2kn8$e}#h8ZZ$zNaw%Hs{)r7Titn4pCwjB9xfrzL7k~jXJ;3I3EHY1;`aKT|E#GC z#ZQo4VFsVh&^Q}#S!EWzkT$f=DE+y2`BjHH_pP8fY3XN7{*zz*G3VeuHI>;u`L?GB zr>u72LX^LrIicPlm7bL;x^p{FrylrsvwhNkCmpbnIudba<(!29yX)=wV7;vNvic$R z6Xsh4T%0x${qQ}$q<{ZSY%3()d9AMxFKr-8=^a$U)K_(4lI&`wINezE{EL`OGFz`D zEp3kSC?A$~qs1o^Mj>&lDi86yAE_}!3n~bJY?Q)enkqFNmr^ zgqPHs?c|(*G4W`VFZh>{DU+>6LYq%V1Xm7ewiDq~YI6<|Ca;qi@yzrX*IM+A^5mWY z|2VPT)Fk&%W_qCj_l%Wu223<#eDXeldVXLmXFWs(E!7Cw<2(7Oq65 z0146iEFJEk`F1b|zN>id55coP_0rX@se`ij)47)!n0Xno{;*Fv`s1}2(eWsL56|-@ zPxE?oFygvI#(1u=bVh4w#^eo(dZ}m;C{-v_mW=>>4WWJBinh{<+_hM$jdy6`lv_b0Mu^}V_+KqRhDQDT@Bsf1f3hYGT9Q1K?oH5eo~FqRkr9$`*w?#RT9M)XJNR5j$3bN)K1c?9^An33Jo z+$BtIb;Ch8UfHoF0~WE@jhrg;(c?tV@B@Z0o;v|g`EC!I;k}j%g>UyKJI(17OeM~R zO~13vY=ro|+O3I(+>Y1X8~>DrbelZY(YMbfyA6AIX^D?kGqcM`>o;NG`i(_cQIvSw zi=Ch?7tUm})xA_0Xbqv)P!gscX#5`Z)yJr2949;V{x1;EKVVX~r5c6zd(GByOaOh1 zkGz=N={WQxCQH|z{75%79M{f9T=QiNE%v8`g6+kBp^TadH6ct6O_2z4d_xeZn z0_t=)vw*6Yuk>-ExG@agdJn6l=sV?lTQga_=Dk1xvynw3#3%nI;@*=PBJYRevlof@ zC_;_m6qt1XCrv!;#C?n)Npg&~RJ<&h2uB)`n}OL$)yi8au=N_c~PD z@H^tM<;#{^ZfhT@_yEtt_$Rx+%O+fQbLnO7BWQu`>7o&BGEt+@dL~s|zw!E}DN#NO za`%|v#3g`te(~ev!N|v+s<2Da&lX)Odqs<3Q9M7t%;H1!kfkkHg>o1hyg=M23mHX4 zS#U!5E?B6}>^`_Mln^%r$;;iXy-}#<7&U^EshRgKt_rqJ-kOTnszt29wXc&q{aVUR zmqK4Q8@xbU*3E?ez0$%@5i_`~#-tZx!Rr`Pc=EXFaA z3FOBGLEt$i!NZ>fpay-;@}H!_(aOI#`)c>*lz?%R>{K&il!rA=lhb|j)8*PQI7OdL z30rL8hPEMv4gZmP*TvrQCwc|CmNZ$W+wyD13XA0t%jeC6<1xMM4mBI|$dfd5RCZwj z`+?-+y*MM&K$Z8CcQoyOa;Hx42&c-+>hNOW@70kxd~D>uO-#u(wtt8f(y&8eWOj_Rur>1E`n$lKJ8HmSu4-$Px_VWc)y4xAzjRR}?Io(^HXQhSM{Lbb4c#twA5J)B zM=FsEt7DlFWyL>&`0~sOfw|=Ag~2yN!JT1kh^M4sHjQ7XT<`^Hh<0GMixQgQ&t@5V zw#aYa-caw>3U+po;G6RlJ4fw!O2m9}>PTsUd)Qlv;2IDSLOy7>7)u>Yw@{d=15t4* ztWdlD{l?=znGv%ezdu%{EPIN1o3I~f3S-WD`d~$PrIx?Go@Kor?XE^zvy@+PtWlkY z8WX)}W@jm_moBG&nxy-O5FtzJ=&i6f|WIybd7iMgj-Abf%2l1S&xmEP*&Z z*e1rZa6$B8eKf<2`E6mU_gq;nvjjVX4=4`ku&kpTqDXtN^Vji->H}&z&c+CkCmsJq zcQNyNBK?vTjuJHpaS7%Pn@Gf}d1g9hzHWAQdIYng<~Irnw&ij)eFD5{Ag~9V%?x6g zHPPT8sWKOGy_2cHs#ujrRkMdb%xcL*O_XCLg|qeX@4II`^5G%u`^!|9>Rak9B%@%Y zMlYFzyl2a+dwP$+x;~cq2d>^YjH^J&iEsw%iMl?`qnIR|fKr)9+M&zU`f}wD_d(L; zDAR>j%POQxTl952`f{BjV?$Y5)YI%zy5H(OVf)$vBw1rV&H;v+^+5JaHSqf3qMFIq z;DIne=*x;l7DwYf^ddR*i^5L6+XLJmuP&>2ZFTTV|30FdiUxza@zLzXUzR1=`BZNv zAz{z6iZic{xF0x+cs7aw8U>eDvHd}0TtQz)5Z%ei)3+&|UTJ)z2v!Ed^;a&tm-VG| zLXGz|%LX|np3BK(Mf7U+NdFc`j5$5@PXnDQrI(0C7Xdme((zi>$uuG&Q?jqIQn#9sNyzUlQ8=PHT@6jwrNn%7Yd0ImVgTj ziZeU$*o}K??p`AVnVKZ~?6(l~(dy=Utg54`{D%CTMXDmMtf2*QQIi;mMficpTxi&5 z*%*c4%QZ)^I%Eb6io4g)R1)`j-h5duB~p16fIH0 zCd`E+%M8Y9lK;&K1%H+C;S2iLMa@5WtV&VClcyIPXkO2w#2;`2uQ+Xy8;EUaNMMPu zx;6~WtfJ+*+Ka9%Uz;kc*z%vt%l{uu-yKi&|Gu9TWsAtBjO;y5R`%Y>CL{aU;+U0{ zb*!?Ek?n*eD?5b5aU70y$nG3O_B_V#<^B77|LpNNkE8Qk_jOEi+N{EtjKv`DX ztE1%x$-ravm9g*iC}#^fq|a;a==bLZYA_VGZS+V^$wDCYtuhHMFKvb9-qwiJnz?=o z(2=31MmZrj|5b<4(l}K!p5^$>!m-!g1Ue6#L+9CzciA0oL|O+lvkw$U$jGL<9%pTl z7`t0j?a5(m9ikj1WBP!uJ?HWVKbV4egPk+q^cnM#bauP}w}b4vPJ&p8HR$Q6kB<`Z z-4{-HG7Lb>?l64dJis3Al62FMbn%+w%rASR!(S-bL@DjfJ+tOhSd}9^etTh#WrNIj zetwqD08q^@Ng5@iIM3ePS{V@U?LAWbFjTj(uN=TfO6m(Ut_@Mi*mNawTOceAXsKqf zLy1jk>~><`nZ&H|0t;>@>+?M#5(0XT)p)009*}>^QokfhSK^*KGR1Bhe@ioFa8T;O z%Y+ErCtOUa1t+QsxFdqP@Qc4i9Qd#M+(N3-UyL8C5*@tw@=|h?Q1)~z!u2{ie)Ln= zwtqoh@~$Lbk}$lg$T`06u3P3BII7&Gz}HMZkFK;R2Sm-+DBGa)ah*<5({^jCbYW5C zS00aSdC&-=+t|d0`@vFjB=PHClD~>ZHIoj0S1+X|QtgW6N{-v!uq=n#= zOhfyI^cjI{edjJ*d)CV*2VlP)l&qoBXin5YR^;)RIQjv~Bd%2kDxdQ4Ldu0H3~IFB z=lda782V-*ph?Mw0(l?FrN;t6bU5JVws}UeWW-f))MAz;zkdNee@svR=;2>jg=>Q~ zbUhaK=NVt%nu)&N^wx9=L+La{$g(&`u6>BRwykDyvz8`2+&=()i4&1AmZq{92`i$C zXB&U%9d9x&u@!@+7T0_>54<-X6LHai^wQufg0JyQ))hp`BeLly#E+vj<4`w1-`i=P z|G~I~-c$i*pP|&(CE@RUXb0a*Wr>l7th3M)`+OzTZ_aAtP!OzfN7$f}q-%|XnFAy0%&7+ASF$^HmpHjzfAfEp+rZU!{Y0usnz5C3q z3lGPNf2NYn6O_+K&KBNJ{2=Q1-KfZzsi(W(Sn_IKlfy!xA>y@^T{xWfAi)U*G|=g7 zWI{_2zA)rX|Ag1|=3C>gCS85LQ%p3GP${JJp^CuruifD)H)P~W%PAvXDGrws)i6k0y^t4yvH&PoK#&L*-MH_2#v_^xU!(&W# zu6&(-T>b|~r@n>7kCQt`%`4|&*K%+0rsE$7(aAnEr1_2LdEJ_}<_Da9`f)eBjyQh) z-ok9m8h8wZ^~o)FOFN2?H+uM_baTvjsHli)+|5O)HbAr3O4GdTXmhRdq9U2R#+W9& z`y9Zpa!pJ{%7{ zb|vmziO~;P%OGzbs-XWwpVNZ%ZTF@ja|3s0$e$*w?obU!oKk6v#{mF}W69*PH zjVUqCft?jd?V#At{WEOeWDx5?ZyeOwYSmRK2-vf4A?`f1VFtm zbbqUJ{N=ytg)EAuF0~XB7k~!n&hCAtq|^|y;r?Sv?0Nl&TPgmbMJyyhvz5MvC!tlh zed?YiP$~noS-vhIZ{xOeJ*+#8>Z0e~uf!x;-mV2|y${SYSvTC~;urg&Jc!*pb@KI= zgWRq0K<=UXEN8&{@lZmK!1s8qba$PU~ zt0!6YY{CMZnVZzbW)``{bwl$o?QIw5zkfo85+9X~^%uD_UigNM(zqC<^lYwHxtdU_ zvQS9saJY;*M`X@A&F?t6hCbn89iVPl&rnq~8fk+_ZmvJ>4g*PQ3={9qi6FsH4Ql^$ zfPm2vx%zjiuoJc~(}M}t0Zf`L$K}%34DY$aV?uHZo$Hr^AM8?vP!}Fb2)CzLr0i+9 zMlyp#2J0@fK@o~*yvW@ie8EbFw5<`nO2oBkDfABS3YAK5l%8b{f|ndlcJEtgZ!s1k=ZGp4p_0KV1UAHP$8V$Rvt3 zw5IDB!ro-1hq)5eVv6nDehRZyICymuUr(+d;14_4S;R#G++=r7$;1--X|rn=Ik?j| z#F8&01<>!x9l%DNy&YH{`nQW@d*h_$6Q%kphvi`_cl#(L?)@8(NO>-<=}@RyS@z1; zXS6`80$gsMbXthwfdMW=i`eiB!3I#O14KKaBn~#7k2Pam>g7VR83MajWwMUkbi?)*ybj6_Zyk=Phd%S$foA;O; z>yZds0r$IIRCfjRM|nB~m2_~#nx+qRlD;zKrQR=wNJMcS0i#*mmp;9B`D)R|*^x#? zURTH3WrF#VTjg=H^iD4+X~9J@m0q0)$rL~EwZFMJ%3i&4DlIN*`^^=zBjbSW9Prb z6G{n~Z^zRz@6FMU9u5C~98$UlCGu}B(IW_Zq(KuNu;KQ-ZL&`8&=Y&-7p^q)4+ERdg(1p)K?b6B?h zThmd)V|lxAq(&zJE5-MqTk}c1nmrN5uEYL=x25N&NA?GusLO6J4}pY2>xr3ZEOfK+ z2#20$-%(Fz@^%YqVa#na#3y+Nw$#Cjp1BS}!UMMs*R4l9V-04@gFQ;v=C=W?+cF{~ z*vQOCA}YNY;DLA;|7!fH_SX4YrH#4|XS8b`K)XJwD>t(!(R!faH5fsxDeC*qMMX$e z-1<;E8DN|-T0jyK#%*HJ8WxU%!RVAFv|r0`Ww`$q-31DwqXNdjshClqEurZcn7Hi{ zJ~~t#!1;s1^~DE_PD@_L*Csk0d0;Z93-)AJGf&L8bj?vi!ilb>ma%k~J$R3-xZ4WK z*1WB!4knVfL8vvj{pxEMCq;<4=pvFpL}tSaVsXP4rL{B&8U%_^)ZoIXAxGAw0LDn* zpFle1v-{gs{QV1C45Zbls{xoX_$MUkpU0M;sR`8#F;MC;y$7VCi)+^> z&T!d=g^g6^)lR{37|(z>k}BqjF83wy@5`deAE5@gIe`HiR{Cv=4bN`|^Ig7O!9tY? zdk#wjWFQ5YQE3mNv_)K~SP;$uq}0%IC^OAzxO@6AsPLQu@pwj@;l}9sbe;=WEirzE zcoh3v*+kP{;>96y#WXkuK+W$o`O}Ep=j~Qwz<6v%erS0EIl)c(cWnVZWX7w5l7>Pa-Epotf#zs{sMQ<)&ezDZ2f>Tq z)==JLocjFLxM0TTZ%+At_*brlLSLU^oVa(tZqZ$OBeFmlk8mAlTV?^0gBT@p>ix+8 z=Z`mk`y(&fT*bGMp8PyJ5Y~2W$t0N;yMvxZU*o;aQ+D5VuG^mke*FB0;5Df|9z(`s zKAkaN4y^~E4gTDZuzCju=vpY%?5AS`8K+2bSR)%eV1LO?G6Y2ZaL?Y4?fVG3bb*Y*bz=MOP*^njVwnK3K>cH9SL zjt8v~;rmT@*8->H-uJa$ubSsYI7e-l+`&vhhF zk@{aG_+e41{~t-+aUX!Ky+}DnO<-qa8AT(8aa)vt(zQNE`Q2|vh^K(q%lE)bihft} zN@Ik*vFJhmntwa5H}GMddjOgvK;?wZcbW>caGofbNqu-FT*?oydbH$ zq9mdtTq^xLv=$gnU2|_#SZ2ZTGpxldHdZcw7^mRvn_XH&Pjvi7R4|Ze+ZPDFql)i3 zHHUGXubb$1#~j~+BCz@J4hOGukPDM;;-0ln7pQr#y#bgrv28f;EHYdh$i2XD<#ymlbwHg8NCC@5(Ib7Cv%Mmk|n0Uy?4?)4SKziz7c&wpGqr;U3T= z+DFIk;phERyK+`z0%^e;lkJt!1yP%X;xsgMjNybTFB>9u*hFvFYo=ZCIJ=y`y;>?+57{->D<6t{KRZY2+A3OA;7IpZC0Vt^*)*vV(&D zZ4b5N1-yJcWn}Q|drXY=h&~cI@I3S`$5H0#`nRm^2;_3T}_*YSC*lBZmjotPI9j%2&!s)X<{xE*TVTZix-?K^>QXO z$Z|bRXver@Wqv(`=y*SgKg9suP3PnhikGYm$fa?+sCn+=k9sW0LA=5r!-+9TX%Ob& zZZT;u==_qNtBHEJYaeD}&=J2+!|g zfck%eRNy1@w_set%4XXr_S>*%>Pkk%R5ACSSA(0LCJRCagNEdm++C4eX<1~x>+uuP zm(m55!t)jS&v9Q6Kv;*@&BD0r=9%QT8F&4cgMyQL@h<;X2M?ogNbi1u18oe!yd$@A z#W4DZ?UrepUc;|XIFk~K(n}}Ud&|1Bw~|@N*|}v00YfU~fLm$7T)kx76fz&Q!Lo_y zG;z(zf;Gbf&=g(%@sst`vqZ45`}2HX@ Vbqz55Cj=;sSNLq5?7oS@*;$dwj1}Mm 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 MQGBjYDQAKDKhG%-p#T5? diff --git a/media/vscodeignore/spec.png b/media/vscodeignore/spec.png new file mode 100644 index 0000000000000000000000000000000000000000..5e105ef30193f19d1180a9e8130c2c8c4a9c0ee1 GIT binary patch literal 57981 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 Vbqz55Cj=;sSNLq5?7oS@*;$dwj1}Mm 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 MQGBjYDQAKDKhG%-p#T5? literal 0 HcmV?d00001 diff --git a/package.json b/package.json index 33fbdc1..d7ced8d 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,9 @@ "explorer": [ { "id": "spectrogram-explorer", - "name": "spectrogram" + "name": "spectrogram", + "icon": "media/icon.svg", + "contextualTitle": "Spectrogram" } ] } @@ -41,23 +43,24 @@ "package": "webpack --mode production --devtool hidden-source-map", "wp": "webpack", "wp-watch": "webpack --watch", - "task-clean-output": "ts-node tasks.js clean" + "task-clean-output": "ts-node tasks.js clean", + "lint": "eslint --fix ." }, "devDependencies": { - "@types/node": "^18.7.23", - "@types/vscode": "^1.71.0", - "@types/vscode-webview": "^1.57.0", - "@vscode/codicons": "^0.0.32", - "copy-webpack-plugin": "^11.0.0", - "css-minimizer-webpack-plugin": "^4.1.0", - "eslint": "^8.24.0", - "eslint-plugin-jsdoc": "^39.3.6", - "pug": "^3.0.2", + "@types/node": "^22.10.10", + "@types/vscode": "^1.96.0", + "@types/vscode-webview": "^1.57.5", + "@vscode/codicons": "^0.0.36", + "copy-webpack-plugin": "^12.0.2", + "css-minimizer-webpack-plugin": "^7.0.0", + "eslint": "^9.19.0", + "eslint-plugin-jsdoc": "^50.6.3", + "pug": "^3.0.3", "shelljs": "^0.8.5", - "ts-node": "^10.9.1", - "vsce": "^2.11.0", - "webpack": "^5.74.0", - "webpack-cli": "^4.10.0" + "ts-node": "^10.9.2", + "vsce": "^2.15.0", + "webpack": "^5.97.1", + "webpack-cli": "^6.0.1" }, "repository": { "type": "git", diff --git a/vsc-extension-quickstart.md b/vsc-extension-quickstart.md index e6da2a5..70c8d64 100644 --- a/vsc-extension-quickstart.md +++ b/vsc-extension-quickstart.md @@ -27,13 +27,16 @@ ## Run tests -* Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`. -* Press `F5` to run the tests in a new window with your extension loaded. -* See the output of the test result in the debug console. -* Make changes to `src/test/suite/extension.test.js` or create new test files inside the `test/suite` folder. - * The provided test runner will only consider files matching the name pattern `**.test.ts`. +* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner) +* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A` +* See the output of the test result in the Test Results view. +* Make changes to `test/extension.test.js` or create new test files inside the `test` folder. + * The provided test runner will only consider files matching the name pattern `**.test.js`. * You can create folders inside the `test` folder to structure your tests any way you want. + ## Go further - * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VSCode extension marketplace. + * [Follow UX guidelines](https://code.visualstudio.com/api/ux-guidelines/overview) to create extensions that seamlessly integrate with VS Code's native interface and patterns. + * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace. * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration). + * Integrate to the [report issue](https://code.visualstudio.com/api/get-started/wrapping-up#issue-reporting) flow to get issue and feature requests reported by users. From 5331f3584b994b63d3986c4033cde490e5160b27 Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Mon, 27 Jan 2025 01:08:08 -0800 Subject: [PATCH 02/17] fix: format --- .vscode/settings.json | 1 + eslint.config.mjs | 70 +++++++++++++++++++++---------------------- package.json | 7 +++-- src/controller.js | 43 +++++++++++++------------- src/extension.js | 12 ++++---- src/treeview.js | 6 ++-- src/webview.js | 7 +++-- 7 files changed, 76 insertions(+), 70 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index ba0f75f..81b622c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ "flac", "lanly", "susres", + "treeview", "Typecheck", "vscodeignore", "webaudio" diff --git a/eslint.config.mjs b/eslint.config.mjs index 4abebfa..f5fa25b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,37 +1,37 @@ -import globals from "globals"; +import globals from 'globals' -export default [{ - files: ["**/*.js"], - languageOptions: { - globals: { - ...globals.commonjs, - ...globals.node, - ...globals.mocha, - ...globals.browser +export default [ + { ignores: ['dist'] }, + { + files: ['**/*.js', '**/*.mjs'], + languageOptions: { + globals: { + ...globals.commonjs, + ...globals.node, + ...globals.mocha, + ...globals.browser + }, + ecmaVersion: 2022, + sourceType: 'module' }, - - ecmaVersion: 2022, - sourceType: "module", - }, - - rules: { - 'comma-dangle': ['error', 'never'], - 'eol-last': ['error', 'always'], - 'max-len': ['error', { code: 120 }], - 'no-throw-literal': 'warn', - 'no-trailing-spaces': 'error', - 'quote-props': ['error', 'as-needed'], - "constructor-super": "warn", - "no-const-assign": "warn", - "no-this-before-super": "warn", - "no-undef": "warn", - "no-unreachable": "warn", - "no-unused-vars": "warn", - "valid-typeof": "warn", - curly: ['error', 'multi-or-nest'], - eqeqeq: 'error', - indent: ['error', 2], - quotes: ['error', 'single', { allowTemplateLiterals: true }], - semi: ['error', 'never'] - }, -}]; \ No newline at end of file + rules: { + 'comma-dangle': ['error', 'never'], + 'eol-last': ['error', 'always'], + 'max-len': ['error', { code: 120 }], + 'no-throw-literal': 'warn', + 'no-trailing-spaces': 'error', + 'quote-props': ['error', 'as-needed'], + 'constructor-super': 'warn', + 'no-const-assign': 'warn', + 'no-this-before-super': 'warn', + 'no-undef': 'warn', + 'no-unreachable': 'warn', + 'no-unused-vars': 'warn', + 'valid-typeof': 'warn', + curly: ['error', 'multi-or-nest'], + eqeqeq: 'error', + indent: ['error', 2], + quotes: ['error', 'single', { allowTemplateLiterals: true }], + semi: ['error', 'never'] + } + }] diff --git a/package.json b/package.json index d7ced8d..9fe0d3b 100644 --- a/package.json +++ b/package.json @@ -44,21 +44,22 @@ "wp": "webpack", "wp-watch": "webpack --watch", "task-clean-output": "ts-node tasks.js clean", - "lint": "eslint --fix ." + "lint": "eslint --format summary-chart .", + "lint-fix": "eslint --fix ." }, "devDependencies": { "@types/node": "^22.10.10", "@types/vscode": "^1.96.0", "@types/vscode-webview": "^1.57.5", "@vscode/codicons": "^0.0.36", + "@vscode/vsce": "^3.2.1", "copy-webpack-plugin": "^12.0.2", "css-minimizer-webpack-plugin": "^7.0.0", "eslint": "^9.19.0", + "eslint-formatter-summary-chart": "^0.3.0", "eslint-plugin-jsdoc": "^50.6.3", "pug": "^3.0.3", "shelljs": "^0.8.5", - "ts-node": "^10.9.2", - "vsce": "^2.15.0", "webpack": "^5.97.1", "webpack-cli": "^6.0.1" }, diff --git a/src/controller.js b/src/controller.js index 8350453..8a88bd5 100644 --- a/src/controller.js +++ b/src/controller.js @@ -48,7 +48,8 @@ const REFRESH_ICON = '' const imageDataFrame = canvasContext.createImageData(2, canvasElement.height) // TODO: note this for (let i = 0; i < imageDataFrame.data.length * 4; i += 8) { - for (let j = 3; j <= 7; j++) imageDataFrame.data[i + j] = 255 // = 0,0,0,255 | 255,255,255,255 + // Format: 0,0,0,255 | 255,255,255,255 + for (let j = 3; j <= 7; j++) imageDataFrame.data[i + j] = 255 } const request = new XMLHttpRequest() @@ -160,26 +161,26 @@ const REFRESH_ICON = '' function togglePlaybackButtons(state) { switch (state) { - case 'loading': - susresBtn.textContent = 'Loading' - susresBtn.classList.add('disabled') - susresBtn.disabled = true - backBtn.style.display = 'none' - forwardBtn.style.display = 'none' - break - case 'playing': - susresBtn.innerHTML = PAUSE_ICON - susresBtn.classList.remove('disabled') - susresBtn.disabled = false - backBtn.style.display = 'inline-block' - forwardBtn.style.display = 'inline-block' - break - case 'ended': - susresBtn.innerHTML = REFRESH_ICON - durationText.innerHTML = null - backBtn.style.display = 'none' - forwardBtn.style.display = 'none' - break + case 'loading': + susresBtn.textContent = 'Loading' + susresBtn.classList.add('disabled') + susresBtn.disabled = true + backBtn.style.display = 'none' + forwardBtn.style.display = 'none' + break + case 'playing': + susresBtn.innerHTML = PAUSE_ICON + susresBtn.classList.remove('disabled') + susresBtn.disabled = false + backBtn.style.display = 'inline-block' + forwardBtn.style.display = 'inline-block' + break + case 'ended': + susresBtn.innerHTML = REFRESH_ICON + durationText.innerHTML = null + backBtn.style.display = 'none' + forwardBtn.style.display = 'none' + break } } diff --git a/src/extension.js b/src/extension.js index 8b13965..7cc6233 100644 --- a/src/extension.js +++ b/src/extension.js @@ -1,18 +1,18 @@ 'use strict' const path = require('path') -// @ts-ignore -const { ExtensionContext, Uri, window } = require('vscode') -const { TreeView } = require('./treeView') +const { Uri, window } = require('vscode') +const { TreeView } = require('./treeview') const { SpecWebviewPanel } = require('./webview') /** - * @param {ExtensionContext} context + * @param {import('vscode').ExtensionContext} context */ function activate(context) { const specExplorer = TreeView.create(context) specExplorer.onDidChangeSelection(file => { try { file.selection[0].fullFilePath + // eslint-disable-next-line no-unused-vars } catch (error) { window.showInformationMessage('Slow down ðŸ˜ĩ') return @@ -28,8 +28,8 @@ function activate(context) { SpecWebviewPanel.currentPanel.panel.webview.postMessage({ path: `${panel}`, name: label }) SpecWebviewPanel.currentPanel.panel.webview.onDidReceiveMessage( ({ type, message }) => { - if (type == 'finished') window.showInformationMessage('Finished Playing 😎') - else if (type == 'error') window.showErrorMessage(`${message} ðŸ˜ĩ`) + if (type === 'finished') window.showInformationMessage('Finished Playing 😎') + else if (type === 'error') window.showErrorMessage(`${message} ðŸ˜ĩ`) }, undefined, context.subscriptions diff --git a/src/treeview.js b/src/treeview.js index d676467..7c40f03 100644 --- a/src/treeview.js +++ b/src/treeview.js @@ -48,7 +48,7 @@ class SpecTreeDataProvider { getFiles(thePath) { // name const toFileItem = (name, targetPath, type) => { - if (type == 'directory') { + if (type === 'directory') { let descriptionText, collapsibleState const filesCount = fs.readdirSync(path.join(targetPath, name)).filter(this.isSupportedMedia).length if (filesCount > 0) { @@ -74,8 +74,8 @@ class SpecTreeDataProvider { } isSupportedMedia(name) { - if (name.indexOf('.mp3') != -1) return true - if (name.indexOf('.flac') != -1) return true + if (name.indexOf('.mp3') !== -1) return true + if (name.indexOf('.flac') !== -1) return true } } diff --git a/src/webview.js b/src/webview.js index efbed3c..8024d9f 100644 --- a/src/webview.js +++ b/src/webview.js @@ -29,8 +29,11 @@ class SpecWebviewPanel { const panelSetting = { enableScripts: true } - if (os.platform() == 'darwin') { - panelSetting.localResourceRoots = [Uri.file(__dirname), Uri.file(workspace.workspaceFolders[0].uri.fsPath)] + if (os.platform() === 'darwin') { + panelSetting.localResourceRoots = [ + Uri.file(__dirname), + Uri.file(workspace.workspaceFolders[0].uri.fsPath) + ] } const viewColumn = column || ViewColumn.One From 1599a112ba9d4eaeb2af092d2c2544b06a5ef60c Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Mon, 27 Jan 2025 11:07:26 -0800 Subject: [PATCH 03/17] WIP: playing logic The AudioContext was not allowed to start. It must be resumed (or created) from a user gesture event handler --- eslint.config.mjs | 8 ++++---- package.json | 3 ++- src/controller.js | 43 ++++++++++++++++++++++++++----------------- webpack.config.js | 5 +++-- 4 files changed, 35 insertions(+), 24 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index f5fa25b..9ddaa93 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -16,17 +16,17 @@ export default [ }, rules: { 'comma-dangle': ['error', 'never'], + 'constructor-super': 'warn', 'eol-last': ['error', 'always'], 'max-len': ['error', { code: 120 }], - 'no-throw-literal': 'warn', - 'no-trailing-spaces': 'error', - 'quote-props': ['error', 'as-needed'], - 'constructor-super': 'warn', 'no-const-assign': 'warn', 'no-this-before-super': 'warn', + 'no-throw-literal': 'warn', + 'no-trailing-spaces': 'error', 'no-undef': 'warn', 'no-unreachable': 'warn', 'no-unused-vars': 'warn', + 'quote-props': ['error', 'as-needed'], 'valid-typeof': 'warn', curly: ['error', 'multi-or-nest'], eqeqeq: 'error', diff --git a/package.json b/package.json index 9fe0d3b..7e1efa3 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,8 @@ "wp": "webpack", "wp-watch": "webpack --watch", "task-clean-output": "ts-node tasks.js clean", - "lint": "eslint --format summary-chart .", + "lint": "eslint .", + "lint-sum": "eslint --format summary-chart .", "lint-fix": "eslint --fix ." }, "devDependencies": { diff --git a/src/controller.js b/src/controller.js index 8a88bd5..ffbac0c 100644 --- a/src/controller.js +++ b/src/controller.js @@ -35,7 +35,7 @@ const REFRESH_ICON = '' function player(file) { canvasElement.width = window.innerWidth - 10 const WIDTH = canvasElement.width - togglePlaybackButtons('loading') + togglePlaybackButtons('LOADING') const audioCtx = new AudioContext() const analyser = audioCtx.createAnalyser() analyser.smoothingTimeConstant = 0.0 @@ -46,21 +46,29 @@ const REFRESH_ICON = '' const dataArray = new Uint8Array(bufferLength) const imageDataFrame = canvasContext.createImageData(2, canvasElement.height) - // TODO: note this - for (let i = 0; i < imageDataFrame.data.length * 4; i += 8) { - // Format: 0,0,0,255 | 255,255,255,255 - for (let j = 3; j <= 7; j++) imageDataFrame.data[i + j] = 255 + // Initialize the imageDataFrame with alternating black and white pixels + for (let i = 0; i < imageDataFrame.data.length; i += 8) { + // Set the first pixel to black (0, 0, 0, 255) + imageDataFrame.data[i] = 0 + imageDataFrame.data[i + 1] = 0 + imageDataFrame.data[i + 2] = 0 + imageDataFrame.data[i + 3] = 255 + + // Set the second pixel to white (255, 255, 255, 255) + imageDataFrame.data[i + 4] = 255 + imageDataFrame.data[i + 5] = 255 + imageDataFrame.data[i + 6] = 255 + imageDataFrame.data[i + 7] = 255 } const request = new XMLHttpRequest() request.open('GET', file.path) request.responseType = 'arraybuffer' - request.onload = () => audioCtx.decodeAudioData(request.response, start, onBufferError) + request.onload = () => audioCtx.decodeAudioData(request.response, audioCtxSetup, onBufferError) request.send() fileLabel.innerHTML = file.name let source = audioCtx.createBufferSource() - // prettier-ignore let buffer, startAt, length, lengthMs, played = 0, isEnded = false susresBtn.onclick = () => { @@ -88,7 +96,7 @@ const REFRESH_ICON = '' startAt = Date.now() played = 0 durationWatch() - togglePlaybackButtons('playing') + togglePlaybackButtons('PLAYING') } else { // Was suspended so resume it audioCtx.resume().then(() => { @@ -103,7 +111,8 @@ const REFRESH_ICON = '' backBtn.onclick = () => seek(-5000) forwardBtn.onclick = () => seek(5000) - function start(theBuffer) { + function audioCtxSetup(theBuffer) { + console.log('AudioContext setup', audioCtx.state) // This prevents clicking too fast - closed before starting if (audioCtx.state === 'closed') return isEnded = false @@ -116,10 +125,9 @@ const REFRESH_ICON = '' source.onended = playEnd source.start() - draw() startAt = Date.now() durationWatch() - togglePlaybackButtons('playing') + togglePlaybackButtons('READY') } function onBufferError(err) { @@ -154,28 +162,29 @@ const REFRESH_ICON = '' function playEnd() { isEnded = true clearTimeout(durationId) - togglePlaybackButtons('ended') + togglePlaybackButtons('ENDED') cancelAnimationFrame(id) vscode.postMessage({ type: 'Finish playing' }) } function togglePlaybackButtons(state) { switch (state) { - case 'loading': - susresBtn.textContent = 'Loading' + case 'LOADING': + susresBtn.textContent = 'Loading...' susresBtn.classList.add('disabled') susresBtn.disabled = true backBtn.style.display = 'none' forwardBtn.style.display = 'none' break - case 'playing': - susresBtn.innerHTML = PAUSE_ICON + case 'READY': + case 'PLAYING': + susresBtn.innerHTML = state === 'PLAYING' ? PAUSE_ICON : PLAY_ICON susresBtn.classList.remove('disabled') susresBtn.disabled = false backBtn.style.display = 'inline-block' forwardBtn.style.display = 'inline-block' break - case 'ended': + case 'ENDED': susresBtn.innerHTML = REFRESH_ICON durationText.innerHTML = null backBtn.style.display = 'none' diff --git a/webpack.config.js b/webpack.config.js index 8532112..a9a1067 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,6 +4,7 @@ const TerserPlugin = require('terser-webpack-plugin') const path = require('path') const CODICON_PATH = '@vscode/codicons/dist/' +const isProduction = process.env.NODE_ENV === 'production' module.exports = { target: 'node', @@ -30,7 +31,7 @@ module.exports = { }) ], optimization: { - minimize: true, - minimizer: [new TerserPlugin({ extractComments: false }), new CssMinimizerPlugin()] + minimize: isProduction, + minimizer: isProduction ? [new TerserPlugin({ extractComments: false }), new CssMinimizerPlugin()] : [] } } From c032d6b05a79a188a24e8abf0c3f83011c02148a Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Mon, 27 Jan 2025 11:36:39 -0800 Subject: [PATCH 04/17] ref: minor --- src/treeview.js | 3 +-- src/webview.js | 29 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/treeview.js b/src/treeview.js index 7c40f03..4cd2c42 100644 --- a/src/treeview.js +++ b/src/treeview.js @@ -80,10 +80,9 @@ class SpecTreeDataProvider { } class fileItem extends vscode.TreeItem { - constructor(label, filePath, collapsibleState, descriptionText, command) { + constructor(label, filePath, collapsibleState, descriptionText) { super(label, collapsibleState) this.collapsibleState = collapsibleState - this.command = command this.contextValue = 'dependency' this.description = descriptionText this.filePath = filePath diff --git a/src/webview.js b/src/webview.js index 8024d9f..e7e5dc2 100644 --- a/src/webview.js +++ b/src/webview.js @@ -4,19 +4,7 @@ const os = require('os') const path = require('path') const pug = require('pug') -class SpecWebviewPanel { - constructor(panel, extensionPath) { - this.disposables = [] - this.panel = panel - this.extensionPath = extensionPath - - this.panel.onDidDispose(() => this.dispose(), null, this.disposables) - // eslint-disable-next-line no-unused-vars - this.panel.onDidChangeViewState(event => {}, null, this.disposables) - // eslint-disable-next-line no-unused-vars - this.panel.webview.onDidReceiveMessage(message => {}, null, this.disposables) - this.panel.webview.html = this.getHtmlForWebview(extensionPath) - } +export class SpecWebviewPanel { static createOrShow(extensionPath) { const column = window.activeTextEditor ? window.activeTextEditor.viewColumn : undefined @@ -45,6 +33,19 @@ class SpecWebviewPanel { SpecWebviewPanel.currentPanel = new SpecWebviewPanel(panel, extensionPath) } + constructor(panel, extensionPath) { + this.disposables = [] + this.panel = panel + this.extensionPath = extensionPath + + this.panel.onDidDispose(() => this.dispose(), null, this.disposables) + // eslint-disable-next-line no-unused-vars + this.panel.onDidChangeViewState(event => {}, null, this.disposables) + // eslint-disable-next-line no-unused-vars + this.panel.webview.onDidReceiveMessage(message => {}, null, this.disposables) + this.panel.webview.html = this.getHtmlForWebview(extensionPath) + } + dispose() { SpecWebviewPanel.currentPanel = undefined @@ -74,5 +75,3 @@ function getNonce() { for (let i = 0; i < 32; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)) return text } - -exports.SpecWebviewPanel = SpecWebviewPanel From d7ebc959aaed319ea198d393e2a1c7547b1c5ff8 Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Mon, 27 Jan 2025 18:54:50 -0800 Subject: [PATCH 05/17] bugfix: flags --- src/controller.js | 8 ++++++-- src/extension.js | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/controller.js b/src/controller.js index ffbac0c..12b0625 100644 --- a/src/controller.js +++ b/src/controller.js @@ -112,9 +112,13 @@ const REFRESH_ICON = '' forwardBtn.onclick = () => seek(5000) function audioCtxSetup(theBuffer) { - console.log('AudioContext setup', audioCtx.state) // This prevents clicking too fast - closed before starting if (audioCtx.state === 'closed') return + if (audioCtx.state === 'suspended') { + // https://goo.gl/7K7WLu + vscode.postMessage({ type: 'INFO', message: 'Please click the play button' }) + } + isEnded = false buffer = theBuffer source.buffer = theBuffer @@ -164,7 +168,7 @@ const REFRESH_ICON = '' clearTimeout(durationId) togglePlaybackButtons('ENDED') cancelAnimationFrame(id) - vscode.postMessage({ type: 'Finish playing' }) + vscode.postMessage({ type: 'DONE', message: 'Playing ended' }) } function togglePlaybackButtons(state) { diff --git a/src/extension.js b/src/extension.js index 7cc6233..8581681 100644 --- a/src/extension.js +++ b/src/extension.js @@ -28,8 +28,9 @@ function activate(context) { SpecWebviewPanel.currentPanel.panel.webview.postMessage({ path: `${panel}`, name: label }) SpecWebviewPanel.currentPanel.panel.webview.onDidReceiveMessage( ({ type, message }) => { - if (type === 'finished') window.showInformationMessage('Finished Playing 😎') - else if (type === 'error') window.showErrorMessage(`${message} ðŸ˜ĩ`) + if (type === 'DONE') window.showInformationMessage(`${message} 😎`) + else if (type === 'ERROR') window.showErrorMessage(`${message} ðŸ˜ĩ`) + else window.showInformationMessage(message) }, undefined, context.subscriptions From c796f8359a261571dd45d40500dff37c187a2a2f Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Mon, 27 Jan 2025 19:03:59 -0800 Subject: [PATCH 06/17] =?UTF-8?q?docs:=20proofread=20by=20copilot=20?= =?UTF-8?q?=F0=9F=98=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscodeignore | 1 - CHANGELOG.md | 26 ++++++++++++-------------- README.md | 18 +++++++++--------- src/extension.js | 2 +- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/.vscodeignore b/.vscodeignore index 6978cd3..63b0346 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -2,7 +2,6 @@ .token .vscode **/*.map -CHANGELOG.md demo jsconfig.json lab diff --git a/CHANGELOG.md b/CHANGELOG.md index bdff262..67cfc70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,6 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## [Future Works] - [Display duration in explorer](https://code.visualstudio.com/api/extension-guides/tree-view#view-actions) -- Generate spectrogram faster than real time, depend on Web Audio API? -- New icon - More colors - Real-time input mode/recorder - Seeker bar slider @@ -14,9 +12,9 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how --- ## [2.0.0] - September 2022 -- Add seeking 5s back and fort buttons +- Add seeking 5s back and forth buttons - Reduce extension size with Webpack -- Support flac +- Support FLAC - Switch to `main` branch - Use [Codicons](https://microsoft.github.io/vscode-codicons/dist/codicon.html) - Use resume/suspend methods @@ -24,19 +22,19 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how - 12 files, 278.85KB, 1.71.0 Known issues: -- Webpack watch doesn't work as expect - it does rebuild but not each change-save -- Vscode's debugging needs to click twice - run and restart in order to launch the app (maybe only for macOS) +- Webpack watch doesn't work as expected - it does rebuild but not on each change-save +- VS Code's debugging needs to click twice - run and restart in order to launch the app (maybe only for macOS) Notes: -- **Finally! This extension works on vscode stable version** 🙌🎊🎉ðŸĨģðŸĨ‚ -- It turns out that vscode doesn't ship mp3 codec with its ffmpeg library (it probably got removed in the past since this extension worked before) and recently add it back in v1.71 - [Issue](https://github.com/microsoft/vscode/ssues/48494) -- Put back resume/suspend since the issue got fixed for electron/chromium - [Issue1052747](https://bugs.chromium.org/p/chromium/issues/detail?id=1052747) | [Issue1018499](https://bugs.chromium.org/p/chromium/issues/detail?id=1018499) -- Reduce extension size - it was funny to see previous version use webpack to minimize the 1 file - `controller.js` -- All previous versions of this extension will not work in any recent vscode versions +- **Finally! This extension works on VS Code stable version** 🙌🎊🎉ðŸĨģðŸĨ‚ +- It turns out that VS Code doesn't ship MP3 codec with its FFmpeg library (it probably got removed in the past since this extension worked before) and recently added it back in v1.71 - [Issue](https://github.com/microsoft/vscode/issues/48494) +- Put back resume/suspend since the issue got fixed for Electron/Chromium - [Issue1052747](https://bugs.chromium.org/p/chromium/issues/detail?id=1052747) | [Issue1018499](https://bugs.chromium.org/p/chromium/issues/detail?id=1018499) +- Reduce extension size - it was funny to see the previous version use Webpack to minimize the 1 file - `controller.js` +- All previous versions of this extension will not work in any recent VS Code versions ## [1.1.0] - December 2019 - Display duration -- Minor bugfixes +- Minor bug fixes - Remove [Semantic](https://semantic-ui.com) - Switch method suspend/resume -> start/stop - Update CSS @@ -58,8 +56,8 @@ References: Notes: - Pumped up the VS Code requirement to 1.40 due to Web Audio API bug, probably from Chrome -- Just right after the 1st release, the Web Audio API stops working on 1.30 to 1.39 of VS code (VS code 1.38 stable build is on Electron 4 | Chrome 69) -- The extension works (except the pause/resume function - API bug again) on VS code exploration build 1.37 with ELectron 6 | Chrome 76 +- Just right after the 1st release, the Web Audio API stops working on 1.30 to 1.39 of VS Code (VS Code 1.38 stable build is on Electron 4 | Chrome 69) +- The extension works (except the pause/resume function - API bug again) on VS Code exploration build 1.37 with Electron 6 | Chrome 76 - This extension will not be working for a while ðŸ˜Ĩ --- diff --git a/README.md b/README.md index fa158ba..408cb19 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,23 @@ # Spectrogram -This is an extension that play and display spectrogram of mp3 and flac audio files. So, are you now curious to see how your favorite songs' spectrogram looks like? ðŸĶðŸĪŠðŸ’­ +This is an extension that plays and displays spectrograms of mp3 and flac audio files. Are you curious to see how your favorite songs' spectrograms look? ðŸĶðŸĪŠðŸ’­ ![Spectrogram2.0.0](media/vscodeignore/spec-sc3.png) ## Release Notes ### 2.0.0 -- Add `flac` support -- Add 5s seeking buttons -- Extension size greatly reduce +- Added `flac` support +- Added 5-second seeking buttons +- Greatly reduced extension size ### 1.1.0 -- Improve UI -- Fix pause/resume -- Fix minor bugs -- Refactor code +- Improved UI +- Fixed pause/resume +- Fixed minor bugs +- Refactored code ### 1.0.1 -- Fix path issue for MacOS +- Fixed path issue for MacOS ### 1.0.0 - Initial release of Spectrogram diff --git a/src/extension.js b/src/extension.js index 8581681..88c407a 100644 --- a/src/extension.js +++ b/src/extension.js @@ -14,7 +14,7 @@ function activate(context) { file.selection[0].fullFilePath // eslint-disable-next-line no-unused-vars } catch (error) { - window.showInformationMessage('Slow down ðŸ˜ĩ') + window.showInformationMessage('Slow down ðŸ˜ĩ‍ðŸ’Ŧ') return } const { fullFilePath } = file.selection[0] From 74c4f465542efed421bcad2e68ebab5248b73227 Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Mon, 27 Jan 2025 19:43:02 -0800 Subject: [PATCH 07/17] add: seekbar --- .vscode/settings.json | 1 + package.json | 2 +- src/controller.js | 34 ++++++++++++++++++++++++++++++++++ src/index.pug | 3 ++- src/style.css | 9 +++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 81b622c..ae3c5fb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "codicons", "flac", "lanly", + "seekbar", "susres", "treeview", "Typecheck", diff --git a/package.json b/package.json index 7e1efa3..b8be042 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "explorer": [ { "id": "spectrogram-explorer", - "name": "spectrogram", + "name": "Spectrogram", "icon": "media/icon.svg", "contextualTitle": "Spectrogram" } diff --git a/src/controller.js b/src/controller.js index 12b0625..c19a33e 100644 --- a/src/controller.js +++ b/src/controller.js @@ -16,6 +16,7 @@ const REFRESH_ICON = '' const forwardBtn = /** @type {HTMLButtonElement} */ (document.getElementById('forwardBtn')) const durationText = document.getElementById('duration') const fileLabel = document.getElementById('label') + const seekbar = /** @type {HTMLInputElement} */ (document.getElementById('seekbar')) let currPlayer, id, durationId // Receive data from vscode @@ -110,6 +111,8 @@ const REFRESH_ICON = '' backBtn.onclick = () => seek(-5000) forwardBtn.onclick = () => seek(5000) + seekbar.oninput = () => seekTo(seekbar.value) + seekbar.onmousemove = (event) => showHoverDuration(event) function audioCtxSetup(theBuffer) { // This prevents clicking too fast - closed before starting @@ -132,6 +135,7 @@ const REFRESH_ICON = '' startAt = Date.now() durationWatch() togglePlaybackButtons('READY') + seekbar.max = lengthMs.toString() } function onBufferError(err) { @@ -163,6 +167,27 @@ const REFRESH_ICON = '' if (audioCtx.state === 'suspended') updateDurationText() } + function seekTo(ms) { + played = parseInt(ms) + if (played < 0) played = 0 + if (played > lengthMs) played = lengthMs + + source.onended = null + source.disconnect(audioCtx.destination) + source.disconnect(analyser) + + source = audioCtx.createBufferSource() + source.buffer = buffer + source.connect(audioCtx.destination) + source.connect(analyser) + source.onended = playEnd + + startAt = Date.now() + source.start(0, played / 1000) + + if (audioCtx.state === 'suspended') updateDurationText() + } + function playEnd() { isEnded = true clearTimeout(durationId) @@ -179,6 +204,7 @@ const REFRESH_ICON = '' susresBtn.disabled = true backBtn.style.display = 'none' forwardBtn.style.display = 'none' + seekbar.style.display = 'none' break case 'READY': case 'PLAYING': @@ -187,12 +213,14 @@ const REFRESH_ICON = '' susresBtn.disabled = false backBtn.style.display = 'inline-block' forwardBtn.style.display = 'inline-block' + seekbar.style.display = 'block' break case 'ENDED': susresBtn.innerHTML = REFRESH_ICON durationText.innerHTML = null backBtn.style.display = 'none' forwardBtn.style.display = 'none' + seekbar.style.display = 'none' break } } @@ -207,12 +235,18 @@ const REFRESH_ICON = '' function updateDurationText() { const durationPlayed = Date.now() - startAt + played durationText.innerHTML = `- ${fmtMSS(Math.trunc(durationPlayed / 1000))} | ${fmtMSS(Math.trunc(length))}` + seekbar.value = durationPlayed.toString() } function fmtMSS(s) { return (s - (s %= 60)) / 60 + (9 < s ? ':' : ':0') + s } + function showHoverDuration(event) { + const hoverTime = (event.offsetX / seekbar.clientWidth) * lengthMs + durationText.innerHTML = `- ${fmtMSS(Math.trunc(hoverTime / 1000))} | ${fmtMSS(Math.trunc(length))}` + } + let x = 0 function draw() { id = requestAnimationFrame(draw) diff --git a/src/index.pug b/src/index.pug index 15d9add..9e6873d 100644 --- a/src/index.pug +++ b/src/index.pug @@ -14,7 +14,7 @@ html body canvas#canvas - div + div.text-center button#backBtn.d-none i.codicon.codicon-play.me-n5 i.codicon.codicon-play @@ -22,6 +22,7 @@ html button#forwardBtn.d-none i.codicon.codicon-play.me-n5 i.codicon.codicon-play + input#seekbar(type='range', min='0', value='0', step='1') span#label span#duration script(nonce=`${nonce}`, src=`${controllerUri}`) diff --git a/src/style.css b/src/style.css index 5b423c1..dd28dcc 100644 --- a/src/style.css +++ b/src/style.css @@ -10,6 +10,11 @@ padding: 0 5px; } +#seekbar { + margin: auto; + width: 90%; +} + body { padding-left: 5px; } @@ -46,3 +51,7 @@ button:hover { .me-n5 { margin-right: -5px; } + +.text-center { + text-align: center; +} From 3494a6f2362097417be54efcfb09af5afc520d0c Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Mon, 27 Jan 2025 22:12:39 -0800 Subject: [PATCH 08/17] add: rbg config --- CHANGELOG.md | 1 - package.json | 27 +++++++++++++++++++++++++++ src/controller.js | 28 +++++++++++++++++++--------- src/extension.js | 5 +++-- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67cfc70..c610143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how - [Display duration in explorer](https://code.visualstudio.com/api/extension-guides/tree-view#view-actions) - More colors - Real-time input mode/recorder -- Seeker bar slider - [Support more audio codecs](https://code.visualstudio.com/updates/v1_71#_ffmpeg-codecs-support) --- diff --git a/package.json b/package.json index b8be042..dfc32f4 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,33 @@ "contextualTitle": "Spectrogram" } ] + }, + "configuration": { + "type": "object", + "properties": { + "spectrogram.rgbColor": { + "type": "object", + "default": { "r": 0, "g": 0, "b": 0 }, + "description": "RGB color for the drawing", + "properties": { + "r": { + "type": "number", + "minimum": 0, + "maximum": 255 + }, + "g": { + "type": "number", + "minimum": 0, + "maximum": 255 + }, + "b": { + "type": "number", + "minimum": 0, + "maximum": 255 + } + } + } + } } }, "scripts": { diff --git a/src/controller.js b/src/controller.js index c19a33e..b9c838d 100644 --- a/src/controller.js +++ b/src/controller.js @@ -7,10 +7,10 @@ const REFRESH_ICON = '' ;(() => { // eslint-disable-next-line no-undef const vscode = acquireVsCodeApi() - const canvasElement = /** @type {HTMLCanvasElement} */ (document.getElementById('canvas')) - const canvasContext = canvasElement.getContext('2d') + const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('canvas')) + const canvasContext = canvas.getContext('2d') - canvasElement.height = 512 + canvas.height = 512 const susresBtn = /** @type {HTMLButtonElement} */ (document.getElementById('susresBtn')) const backBtn = /** @type {HTMLButtonElement} */ (document.getElementById('backBtn')) const forwardBtn = /** @type {HTMLButtonElement} */ (document.getElementById('forwardBtn')) @@ -18,7 +18,7 @@ const REFRESH_ICON = '' const fileLabel = document.getElementById('label') const seekbar = /** @type {HTMLInputElement} */ (document.getElementById('seekbar')) - let currPlayer, id, durationId + let currPlayer, id, durationId, rgbColor // Receive data from vscode window.addEventListener('message', event => { if (currPlayer) { @@ -27,6 +27,7 @@ const REFRESH_ICON = '' cancelAnimationFrame(id) clearTimeout(durationId) } + rgbColor = event.data.rgbColor currPlayer = player(event.data) }) @@ -34,8 +35,8 @@ const REFRESH_ICON = '' * @param {{ path: string; name: string; }} file */ function player(file) { - canvasElement.width = window.innerWidth - 10 - const WIDTH = canvasElement.width + canvas.width = window.innerWidth - 10 + const WIDTH = canvas.width togglePlaybackButtons('LOADING') const audioCtx = new AudioContext() const analyser = audioCtx.createAnalyser() @@ -46,16 +47,18 @@ const REFRESH_ICON = '' const eightBufferLength = 8 * bufferLength const dataArray = new Uint8Array(bufferLength) - const imageDataFrame = canvasContext.createImageData(2, canvasElement.height) + const imageDataFrame = canvasContext.createImageData(2, canvas.height) // Initialize the imageDataFrame with alternating black and white pixels for (let i = 0; i < imageDataFrame.data.length; i += 8) { // Set the first pixel to black (0, 0, 0, 255) + // This is the background color imageDataFrame.data[i] = 0 imageDataFrame.data[i + 1] = 0 imageDataFrame.data[i + 2] = 0 imageDataFrame.data[i + 3] = 255 // Set the second pixel to white (255, 255, 255, 255) + // This is the color of the vertical moving line imageDataFrame.data[i + 4] = 255 imageDataFrame.data[i + 5] = 255 imageDataFrame.data[i + 6] = 255 @@ -119,7 +122,7 @@ const REFRESH_ICON = '' if (audioCtx.state === 'closed') return if (audioCtx.state === 'suspended') { // https://goo.gl/7K7WLu - vscode.postMessage({ type: 'INFO', message: 'Please click the play button' }) + vscode.postMessage({ type: 'INFO', message: 'Please click the play button - autoplay policy' }) } isEnded = false @@ -130,11 +133,13 @@ const REFRESH_ICON = '' source.connect(audioCtx.destination) source.connect(analyser) source.onended = playEnd + if (audioCtx.state === 'running') draw() source.start() startAt = Date.now() durationWatch() togglePlaybackButtons('READY') + seekbar.value = '0' seekbar.max = lengthMs.toString() } @@ -251,7 +256,12 @@ const REFRESH_ICON = '' function draw() { id = requestAnimationFrame(draw) analyser.getByteFrequencyData(dataArray) - for (let i = 0, y = eightBufferLength; i < bufferLength; i++, y -= 8) imageDataFrame.data[y] = dataArray[i] + for (let i = 0, y = eightBufferLength; i < bufferLength; i++, y -= 8) { + imageDataFrame.data[y] = rgbColor.r + imageDataFrame.data[y + 1] = rgbColor.g + imageDataFrame.data[y + 2] = rgbColor.b + imageDataFrame.data[y + 3] = dataArray[i] + } canvasContext.putImageData(imageDataFrame, x, 0) x < WIDTH ? x++ : (x = 0) } diff --git a/src/extension.js b/src/extension.js index 88c407a..3497f40 100644 --- a/src/extension.js +++ b/src/extension.js @@ -1,6 +1,6 @@ 'use strict' const path = require('path') -const { Uri, window } = require('vscode') +const { Uri, window, workspace } = require('vscode') const { TreeView } = require('./treeview') const { SpecWebviewPanel } = require('./webview') @@ -25,7 +25,8 @@ function activate(context) { const songPath = Uri.file(fullFilePath) SpecWebviewPanel.createOrShow(context.extensionPath) const panel = SpecWebviewPanel.currentPanel.panel.webview.asWebviewUri(songPath) - SpecWebviewPanel.currentPanel.panel.webview.postMessage({ path: `${panel}`, name: label }) + const rgbColor = workspace.getConfiguration('spectrogram').get('rgbColor', { r: 0, g: 0, b: 0 }) + SpecWebviewPanel.currentPanel.panel.webview.postMessage({ path: `${panel}`, name: label, rgbColor }) SpecWebviewPanel.currentPanel.panel.webview.onDidReceiveMessage( ({ type, message }) => { if (type === 'DONE') window.showInformationMessage(`${message} 😎`) From 753ecdaef490642cb2e432c8dc5db0939983004f Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Tue, 28 Jan 2025 10:32:39 -0800 Subject: [PATCH 09/17] fix: default RGB value --- CHANGELOG.md | 1 - package.json | 4 ++-- src/extension.js | 4 ++-- src/treeview.js | 10 ++-------- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c610143..704064b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,6 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## [Future Works] - [Display duration in explorer](https://code.visualstudio.com/api/extension-guides/tree-view#view-actions) -- More colors - Real-time input mode/recorder - [Support more audio codecs](https://code.visualstudio.com/updates/v1_71#_ffmpeg-codecs-support) --- diff --git a/package.json b/package.json index dfc32f4..f651c01 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,8 @@ "properties": { "spectrogram.rgbColor": { "type": "object", - "default": { "r": 0, "g": 0, "b": 0 }, - "description": "RGB color for the drawing", + "default": { "r": 100, "g": 0, "b": 0 }, + "description": "RGB color for the spectrogram", "properties": { "r": { "type": "number", diff --git a/src/extension.js b/src/extension.js index 3497f40..16e8b1b 100644 --- a/src/extension.js +++ b/src/extension.js @@ -8,7 +8,7 @@ const { SpecWebviewPanel } = require('./webview') * @param {import('vscode').ExtensionContext} context */ function activate(context) { - const specExplorer = TreeView.create(context) + const specExplorer = TreeView.create() specExplorer.onDidChangeSelection(file => { try { file.selection[0].fullFilePath @@ -25,7 +25,7 @@ function activate(context) { const songPath = Uri.file(fullFilePath) SpecWebviewPanel.createOrShow(context.extensionPath) const panel = SpecWebviewPanel.currentPanel.panel.webview.asWebviewUri(songPath) - const rgbColor = workspace.getConfiguration('spectrogram').get('rgbColor', { r: 0, g: 0, b: 0 }) + const rgbColor = workspace.getConfiguration('spectrogram').get('rgbColor') SpecWebviewPanel.currentPanel.panel.webview.postMessage({ path: `${panel}`, name: label, rgbColor }) SpecWebviewPanel.currentPanel.panel.webview.onDidReceiveMessage( ({ type, message }) => { diff --git a/src/treeview.js b/src/treeview.js index 4cd2c42..547b738 100644 --- a/src/treeview.js +++ b/src/treeview.js @@ -4,10 +4,10 @@ const path = require('path') const fs = require('fs') class TreeView { - static create(context) { + static create() { const path = vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders[0].uri.fsPath : null const specTreeDataProvider = new SpecTreeDataProvider(path) - context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('spec', specTreeDataProvider)) + return vscode.window.createTreeView('spectrogram-explorer', { treeDataProvider: specTreeDataProvider, showCollapseAll: true @@ -35,16 +35,10 @@ class SpecTreeDataProvider { vscode.window.showInformationMessage('Please open a folder') return Promise.resolve([]) } - if (element) return this.getFiles(path.join(element.filePath, element.label)) else return this.getFiles(this.workspaceRoot) } - // ?? - provideTextDocumentContent(uri, token) { - return uri + token - } - getFiles(thePath) { // name const toFileItem = (name, targetPath, type) => { From efaa50389e651fd614620224c3ade475f808b30e Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Tue, 28 Jan 2025 11:44:55 -0800 Subject: [PATCH 10/17] add: duration --- src/treeview.js | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/treeview.js b/src/treeview.js index 547b738..325b5b4 100644 --- a/src/treeview.js +++ b/src/treeview.js @@ -2,6 +2,7 @@ const vscode = require('vscode') const path = require('path') const fs = require('fs') +const { loadMusicMetadata } = require('music-metadata') // Import loadMusicMetadata class TreeView { static create() { @@ -39,9 +40,9 @@ class SpecTreeDataProvider { else return this.getFiles(this.workspaceRoot) } - getFiles(thePath) { + async getFiles(thePath) { // name - const toFileItem = (name, targetPath, type) => { + const toFileItem = async (name, targetPath, type) => { if (type === 'directory') { let descriptionText, collapsibleState const filesCount = fs.readdirSync(path.join(targetPath, name)).filter(this.isSupportedMedia).length @@ -54,19 +55,39 @@ class SpecTreeDataProvider { descriptionText = 'Empty' } return new fileItem(name, targetPath, collapsibleState, descriptionText) - } else return new fileItem(name, targetPath, vscode.TreeItemCollapsibleState.None) + } else { + const duration = await this.getAudioDuration(path.join(targetPath, name)) + return new fileItem(name, targetPath, vscode.TreeItemCollapsibleState.None, duration) + } } const isDirectory = name => fs.lstatSync(path.join(thePath, name)).isDirectory() const subDirs = fs.readdirSync(thePath).filter(isDirectory) const mp3s = fs.readdirSync(thePath).filter(this.isSupportedMedia) - const subDirsItem = subDirs.map(name => toFileItem(name, thePath, 'directory')) - const mp3filesItem = mp3s.map(name => toFileItem(name, thePath, 'audio')) + const subDirsItem = await Promise.all(subDirs.map(name => toFileItem(name, thePath, 'directory'))) + const mp3filesItem = await Promise.all(mp3s.map(name => toFileItem(name, thePath, 'audio'))) return subDirsItem.concat(mp3filesItem) } + async getAudioDuration(filePath) { + const mm = await loadMusicMetadata() // Dynamically load the ESM module + return mm.parseFile(filePath).then(metadata => { + const duration = metadata.format.duration + return this.formatDuration(duration) + }).catch(err => { + console.error(`Error parsing file ${filePath}:`, err) + return 'Unknown duration' + }) + } + + formatDuration(duration) { + const minutes = Math.floor(duration / 60) + const seconds = Math.floor(duration % 60) + return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}` + } + isSupportedMedia(name) { if (name.indexOf('.mp3') !== -1) return true if (name.indexOf('.flac') !== -1) return true From ac07c077a1ca9812044193a68767382ea45046eb Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Tue, 28 Jan 2025 11:57:37 -0800 Subject: [PATCH 11/17] add: revealInFileExplorer --- package.json | 17 ++++++++++++++++- src/treeview.js | 9 +++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index f651c01..19710b5 100644 --- a/package.json +++ b/package.json @@ -29,13 +29,28 @@ "views": { "explorer": [ { - "id": "spectrogram-explorer", + "id": "spectrogram", "name": "Spectrogram", "icon": "media/icon.svg", "contextualTitle": "Spectrogram" } ] }, + "commands": [ + { + "command": "spectrogram.revealInFileExplorer", + "title": "Reveal in File Explorer" + } + ], + "menus": { + "view/item/context": [ + { + "command": "spectrogram.revealInFileExplorer", + "when": "view == spectrogram && viewItem == fileItem", + "group": "navigation" + } + ] + }, "configuration": { "type": "object", "properties": { diff --git a/src/treeview.js b/src/treeview.js index 325b5b4..b8def62 100644 --- a/src/treeview.js +++ b/src/treeview.js @@ -9,7 +9,12 @@ class TreeView { const path = vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders[0].uri.fsPath : null const specTreeDataProvider = new SpecTreeDataProvider(path) - return vscode.window.createTreeView('spectrogram-explorer', { + vscode.commands.registerCommand('spectrogram.revealInFileExplorer', (fileItem) => { + const uri = vscode.Uri.file(fileItem.fullFilePath) + vscode.commands.executeCommand('revealFileInOS', uri) + }) + + return vscode.window.createTreeView('spectrogram', { treeDataProvider: specTreeDataProvider, showCollapseAll: true }) @@ -98,7 +103,7 @@ class fileItem extends vscode.TreeItem { constructor(label, filePath, collapsibleState, descriptionText) { super(label, collapsibleState) this.collapsibleState = collapsibleState - this.contextValue = 'dependency' + this.contextValue = 'fileItem' this.description = descriptionText this.filePath = filePath From 0a153056bc91a2c63779572bc34afef51d2811c7 Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Tue, 28 Jan 2025 14:49:59 -0800 Subject: [PATCH 12/17] add: duration config --- package.json | 11 +++++++++++ src/style.css | 1 + src/treeview.js | 13 +++++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 19710b5..488839f 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,12 @@ } ], "menus": { + "commandPalette": [ + { + "command": "spectrogram.revealInFileExplorer", + "when": "false" + } + ], "view/item/context": [ { "command": "spectrogram.revealInFileExplorer", @@ -75,6 +81,11 @@ "maximum": 255 } } + }, + "spectrogram.showDuration": { + "type": "boolean", + "default": true, + "description": "Show audio's duration in the treeview" } } } diff --git a/src/style.css b/src/style.css index dd28dcc..39c2047 100644 --- a/src/style.css +++ b/src/style.css @@ -11,6 +11,7 @@ } #seekbar { + cursor: pointer; margin: auto; width: 90%; } diff --git a/src/treeview.js b/src/treeview.js index b8def62..cf0e576 100644 --- a/src/treeview.js +++ b/src/treeview.js @@ -26,6 +26,14 @@ class SpecTreeDataProvider { this.workspaceRoot = workspaceRoot this._onDidChangeTreeData = new vscode.EventEmitter() this.onDidChangeTreeData = this._onDidChangeTreeData.event + this.showDuration = vscode.workspace.getConfiguration('spectrogram').get('showDuration', true) + + vscode.workspace.onDidChangeConfiguration(event => { + if (event.affectsConfiguration('spectrogram.showDuration')) { + this.showDuration = vscode.workspace.getConfiguration('spectrogram').get('showDuration', true) + this.refresh() + } + }) } refresh() { @@ -61,8 +69,9 @@ class SpecTreeDataProvider { } return new fileItem(name, targetPath, collapsibleState, descriptionText) } else { - const duration = await this.getAudioDuration(path.join(targetPath, name)) - return new fileItem(name, targetPath, vscode.TreeItemCollapsibleState.None, duration) + let descriptionText = '' + if (this.showDuration) descriptionText = await this.getAudioDuration(path.join(targetPath, name)) + return new fileItem(name, targetPath, vscode.TreeItemCollapsibleState.None, descriptionText) } } const isDirectory = name => fs.lstatSync(path.join(thePath, name)).isDirectory() From 0ad82f8021127d6c8a9f88e77dd202cb9a447de1 Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Wed, 29 Jan 2025 14:44:29 -0800 Subject: [PATCH 13/17] support WAV --- .vscode/settings.json | 2 +- CHANGELOG.md | 16 ++++++++++++++-- src/treeview.js | 10 +++++----- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index ae3c5fb..b6032ab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ + "autoplay", "codicon", "codicons", "flac", @@ -7,7 +8,6 @@ "seekbar", "susres", "treeview", - "Typecheck", "vscodeignore", "webaudio" ], diff --git a/CHANGELOG.md b/CHANGELOG.md index 704064b..323c905 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,22 @@ All notable changes to the "Spectrogram" extension will be documented in this fi Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. ## [Future Works] -- [Display duration in explorer](https://code.visualstudio.com/api/extension-guides/tree-view#view-actions) - Real-time input mode/recorder -- [Support more audio codecs](https://code.visualstudio.com/updates/v1_71#_ffmpeg-codecs-support) + --- +## [3.0.0] - February 2025 +- Support WAV +- Add seekbar +- Add color RBG config +- Add duration in treeview +- New icon and logo + +### Notes +- Clicking treeview item some time able trigger playing the song, but due to the autoplay policy, most of the time you have to click play button inside the webview + +### References +- Treeview API https://code.visualstudio.com/api/extension-guides/tree-view#view-actions +- VS Code support codecs https://code.visualstudio.com/updates/v1_71#_ffmpeg-codecs-support ## [2.0.0] - September 2022 - Add seeking 5s back and forth buttons diff --git a/src/treeview.js b/src/treeview.js index cf0e576..702cacc 100644 --- a/src/treeview.js +++ b/src/treeview.js @@ -77,12 +77,12 @@ class SpecTreeDataProvider { const isDirectory = name => fs.lstatSync(path.join(thePath, name)).isDirectory() const subDirs = fs.readdirSync(thePath).filter(isDirectory) - const mp3s = fs.readdirSync(thePath).filter(this.isSupportedMedia) + const audios = fs.readdirSync(thePath).filter(this.isSupportedMedia) const subDirsItem = await Promise.all(subDirs.map(name => toFileItem(name, thePath, 'directory'))) - const mp3filesItem = await Promise.all(mp3s.map(name => toFileItem(name, thePath, 'audio'))) + const audioFilesItem = await Promise.all(audios.map(name => toFileItem(name, thePath, 'audio'))) - return subDirsItem.concat(mp3filesItem) + return subDirsItem.concat(audioFilesItem) } async getAudioDuration(filePath) { @@ -97,14 +97,14 @@ class SpecTreeDataProvider { } formatDuration(duration) { + if (!duration) return 'Unknown duration' const minutes = Math.floor(duration / 60) const seconds = Math.floor(duration % 60) return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}` } isSupportedMedia(name) { - if (name.indexOf('.mp3') !== -1) return true - if (name.indexOf('.flac') !== -1) return true + return /\.(mp3|flac|wav)$/i.test(name) } } From db95b54977b19ccc52753b921bf2a9038c5cdd90 Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Wed, 29 Jan 2025 21:11:05 -0800 Subject: [PATCH 14/17] try: wavesurfer --- .vscode/settings.json | 1 + CHANGELOG.md | 1 + package.json | 10 +++++++++- src/controller.js | 21 ++++++++++++++++++++- src/index.pug | 7 +++++-- src/webview.js | 10 ++++++---- webpack.config.js | 2 ++ 7 files changed, 44 insertions(+), 8 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b6032ab..5b11833 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,7 @@ "susres", "treeview", "vscodeignore", + "wavesurfer", "webaudio" ], "editor.rulers": [120], diff --git a/CHANGELOG.md b/CHANGELOG.md index 323c905..ec97423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## [Future Works] - Real-time input mode/recorder +- [Wavesurfer](https://wavesurfer.xyz/) --- ## [3.0.0] - February 2025 diff --git a/package.json b/package.json index 488839f..e40edee 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,11 @@ "properties": { "spectrogram.rgbColor": { "type": "object", - "default": { "r": 100, "g": 0, "b": 0 }, + "default": { + "r": 100, + "g": 0, + "b": 0 + }, "description": "RGB color for the spectrogram", "properties": { "r": { @@ -120,5 +124,9 @@ "repository": { "type": "git", "url": "https://github.com/lanly-dev/VSCode-Spectrogram.git" + }, + "dependencies": { + "music-metadata": "^10.8.0", + "wavesurfer.js": "^7.9.0" } } diff --git a/src/controller.js b/src/controller.js index b9c838d..90ef460 100644 --- a/src/controller.js +++ b/src/controller.js @@ -9,6 +9,7 @@ const REFRESH_ICON = '' const vscode = acquireVsCodeApi() const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('canvas')) const canvasContext = canvas.getContext('2d') + const waveform = document.getElementById('waveform') canvas.height = 512 const susresBtn = /** @type {HTMLButtonElement} */ (document.getElementById('susresBtn')) @@ -18,7 +19,7 @@ const REFRESH_ICON = '' const fileLabel = document.getElementById('label') const seekbar = /** @type {HTMLInputElement} */ (document.getElementById('seekbar')) - let currPlayer, id, durationId, rgbColor + let currPlayer, id, durationId, rgbColor, wavesurfer // Receive data from vscode window.addEventListener('message', event => { if (currPlayer) { @@ -81,6 +82,7 @@ const REFRESH_ICON = '' susresBtn.innerHTML = PLAY_ICON cancelAnimationFrame(id) played += Date.now() - startAt + wavesurfer.pause() }) } else if (isEnded) { isEnded = false @@ -101,6 +103,7 @@ const REFRESH_ICON = '' played = 0 durationWatch() togglePlaybackButtons('PLAYING') + wavesurfer.play() } else { // Was suspended so resume it audioCtx.resume().then(() => { @@ -108,6 +111,7 @@ const REFRESH_ICON = '' startAt = Date.now() draw() durationWatch() + wavesurfer.play() }) } } @@ -141,6 +145,18 @@ const REFRESH_ICON = '' togglePlaybackButtons('READY') seekbar.value = '0' seekbar.max = lengthMs.toString() + + // @ts-ignore + // eslint-disable-next-line no-undef + wavesurfer = WaveSurfer.create({ + container: '#waveform', + waveColor: 'violet', + progressColor: 'purple', + backend: 'MediaElement', + mediaControls: false, + height: 128 + }) + wavesurfer.load(file.path) } function onBufferError(err) { @@ -170,6 +186,7 @@ const REFRESH_ICON = '' source.start(0, played / 1000) if (audioCtx.state === 'suspended') updateDurationText() + wavesurfer.seekTo(played / lengthMs) } function seekTo(ms) { @@ -191,6 +208,7 @@ const REFRESH_ICON = '' source.start(0, played / 1000) if (audioCtx.state === 'suspended') updateDurationText() + wavesurfer.seekTo(played / lengthMs) } function playEnd() { @@ -199,6 +217,7 @@ const REFRESH_ICON = '' togglePlaybackButtons('ENDED') cancelAnimationFrame(id) vscode.postMessage({ type: 'DONE', message: 'Playing ended' }) + wavesurfer.stop() } function togglePlaybackButtons(state) { diff --git a/src/index.pug b/src/index.pug index 9e6873d..b948dc1 100644 --- a/src/index.pug +++ b/src/index.pug @@ -5,15 +5,18 @@ html meta(http-equiv='Content-Security-Policy', content=`default-src 'none';\ script-src 'nonce-${nonce}';\ - style-src 'nonce-${nonce}' https:;\ - font-src vscode-resource: data: https:;\ + media-src https: vscode-webview:;\ + style-src https: 'unsafe-inline';\ + font-src data: https: vscode-resource:;\ connect-src vscode-resource: 'nonce-${nonce}';`) meta(name='viewport', content='width=device-width, initial-scale=1.0') link(nonce=`${nonce}`, rel='stylesheet', type='text/css', href=`${styleCssUri}`) link(nonce=`${nonce}`, rel='stylesheet', type='text/css', href=`${codiconsUri}`) + script(nonce=`${nonce}`, src=`${wavesurferUri}`) body canvas#canvas + #waveform div.text-center button#backBtn.d-none i.codicon.codicon-play.me-n5 diff --git a/src/webview.js b/src/webview.js index e7e5dc2..a7860e3 100644 --- a/src/webview.js +++ b/src/webview.js @@ -57,12 +57,14 @@ export class SpecWebviewPanel { } getHtmlForWebview(extensionUri) { - const controllerUri = this.panel.webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'controller.js'))) - const styleCssUri = this.panel.webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'style.css'))) - const codiconsUri = this.panel.webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'codicon.css'))) + const { webview } = this.panel + const wavesurferUri = webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'wavesurfer.min.js'))) + const controllerUri = webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'controller.js'))) + const styleCssUri = webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'style.css'))) + const codiconsUri = webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'codicon.css'))) const compiledFunction = pug.compileFile(path.join(__dirname, 'index.pug')) - return compiledFunction({ controllerUri, codiconsUri, styleCssUri, nonce: getNonce() }) + return compiledFunction({ wavesurferUri, controllerUri, codiconsUri, styleCssUri, nonce: getNonce() }) } } diff --git a/webpack.config.js b/webpack.config.js index a9a1067..8ed37a3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,6 +4,7 @@ const TerserPlugin = require('terser-webpack-plugin') const path = require('path') const CODICON_PATH = '@vscode/codicons/dist/' +const WAVE_SURFER_PATH = 'node_modules/wavesurfer.js/dist/wavesurfer.min.js' const isProduction = process.env.NODE_ENV === 'production' module.exports = { @@ -24,6 +25,7 @@ module.exports = { patterns: [ { from: require.resolve(`${CODICON_PATH}/codicon.css`) }, { from: require.resolve(`${CODICON_PATH}/codicon.ttf`) }, + { from: WAVE_SURFER_PATH }, { from: 'src/controller.js' }, { from: 'src/index.pug' }, { from: 'src/style.css' } From 951bf93ac31673d07a30065d3df9f372f3a4f7de Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Wed, 29 Jan 2025 22:32:12 -0800 Subject: [PATCH 15/17] Revert "try: wavesurfer" This reverts commit db95b54977b19ccc52753b921bf2a9038c5cdd90. --- .vscode/settings.json | 1 - package.json | 10 +--------- src/controller.js | 21 +-------------------- src/index.pug | 7 ++----- src/webview.js | 10 ++++------ webpack.config.js | 2 -- 6 files changed, 8 insertions(+), 43 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5b11833..b6032ab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,6 @@ "susres", "treeview", "vscodeignore", - "wavesurfer", "webaudio" ], "editor.rulers": [120], diff --git a/package.json b/package.json index e40edee..488839f 100644 --- a/package.json +++ b/package.json @@ -62,11 +62,7 @@ "properties": { "spectrogram.rgbColor": { "type": "object", - "default": { - "r": 100, - "g": 0, - "b": 0 - }, + "default": { "r": 100, "g": 0, "b": 0 }, "description": "RGB color for the spectrogram", "properties": { "r": { @@ -124,9 +120,5 @@ "repository": { "type": "git", "url": "https://github.com/lanly-dev/VSCode-Spectrogram.git" - }, - "dependencies": { - "music-metadata": "^10.8.0", - "wavesurfer.js": "^7.9.0" } } diff --git a/src/controller.js b/src/controller.js index 90ef460..b9c838d 100644 --- a/src/controller.js +++ b/src/controller.js @@ -9,7 +9,6 @@ const REFRESH_ICON = '' const vscode = acquireVsCodeApi() const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('canvas')) const canvasContext = canvas.getContext('2d') - const waveform = document.getElementById('waveform') canvas.height = 512 const susresBtn = /** @type {HTMLButtonElement} */ (document.getElementById('susresBtn')) @@ -19,7 +18,7 @@ const REFRESH_ICON = '' const fileLabel = document.getElementById('label') const seekbar = /** @type {HTMLInputElement} */ (document.getElementById('seekbar')) - let currPlayer, id, durationId, rgbColor, wavesurfer + let currPlayer, id, durationId, rgbColor // Receive data from vscode window.addEventListener('message', event => { if (currPlayer) { @@ -82,7 +81,6 @@ const REFRESH_ICON = '' susresBtn.innerHTML = PLAY_ICON cancelAnimationFrame(id) played += Date.now() - startAt - wavesurfer.pause() }) } else if (isEnded) { isEnded = false @@ -103,7 +101,6 @@ const REFRESH_ICON = '' played = 0 durationWatch() togglePlaybackButtons('PLAYING') - wavesurfer.play() } else { // Was suspended so resume it audioCtx.resume().then(() => { @@ -111,7 +108,6 @@ const REFRESH_ICON = '' startAt = Date.now() draw() durationWatch() - wavesurfer.play() }) } } @@ -145,18 +141,6 @@ const REFRESH_ICON = '' togglePlaybackButtons('READY') seekbar.value = '0' seekbar.max = lengthMs.toString() - - // @ts-ignore - // eslint-disable-next-line no-undef - wavesurfer = WaveSurfer.create({ - container: '#waveform', - waveColor: 'violet', - progressColor: 'purple', - backend: 'MediaElement', - mediaControls: false, - height: 128 - }) - wavesurfer.load(file.path) } function onBufferError(err) { @@ -186,7 +170,6 @@ const REFRESH_ICON = '' source.start(0, played / 1000) if (audioCtx.state === 'suspended') updateDurationText() - wavesurfer.seekTo(played / lengthMs) } function seekTo(ms) { @@ -208,7 +191,6 @@ const REFRESH_ICON = '' source.start(0, played / 1000) if (audioCtx.state === 'suspended') updateDurationText() - wavesurfer.seekTo(played / lengthMs) } function playEnd() { @@ -217,7 +199,6 @@ const REFRESH_ICON = '' togglePlaybackButtons('ENDED') cancelAnimationFrame(id) vscode.postMessage({ type: 'DONE', message: 'Playing ended' }) - wavesurfer.stop() } function togglePlaybackButtons(state) { diff --git a/src/index.pug b/src/index.pug index b948dc1..9e6873d 100644 --- a/src/index.pug +++ b/src/index.pug @@ -5,18 +5,15 @@ html meta(http-equiv='Content-Security-Policy', content=`default-src 'none';\ script-src 'nonce-${nonce}';\ - media-src https: vscode-webview:;\ - style-src https: 'unsafe-inline';\ - font-src data: https: vscode-resource:;\ + style-src 'nonce-${nonce}' https:;\ + font-src vscode-resource: data: https:;\ connect-src vscode-resource: 'nonce-${nonce}';`) meta(name='viewport', content='width=device-width, initial-scale=1.0') link(nonce=`${nonce}`, rel='stylesheet', type='text/css', href=`${styleCssUri}`) link(nonce=`${nonce}`, rel='stylesheet', type='text/css', href=`${codiconsUri}`) - script(nonce=`${nonce}`, src=`${wavesurferUri}`) body canvas#canvas - #waveform div.text-center button#backBtn.d-none i.codicon.codicon-play.me-n5 diff --git a/src/webview.js b/src/webview.js index a7860e3..e7e5dc2 100644 --- a/src/webview.js +++ b/src/webview.js @@ -57,14 +57,12 @@ export class SpecWebviewPanel { } getHtmlForWebview(extensionUri) { - const { webview } = this.panel - const wavesurferUri = webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'wavesurfer.min.js'))) - const controllerUri = webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'controller.js'))) - const styleCssUri = webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'style.css'))) - const codiconsUri = webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'codicon.css'))) + const controllerUri = this.panel.webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'controller.js'))) + const styleCssUri = this.panel.webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'style.css'))) + const codiconsUri = this.panel.webview.asWebviewUri(Uri.file(path.join(extensionUri, 'dist', 'codicon.css'))) const compiledFunction = pug.compileFile(path.join(__dirname, 'index.pug')) - return compiledFunction({ wavesurferUri, controllerUri, codiconsUri, styleCssUri, nonce: getNonce() }) + return compiledFunction({ controllerUri, codiconsUri, styleCssUri, nonce: getNonce() }) } } diff --git a/webpack.config.js b/webpack.config.js index 8ed37a3..a9a1067 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,7 +4,6 @@ const TerserPlugin = require('terser-webpack-plugin') const path = require('path') const CODICON_PATH = '@vscode/codicons/dist/' -const WAVE_SURFER_PATH = 'node_modules/wavesurfer.js/dist/wavesurfer.min.js' const isProduction = process.env.NODE_ENV === 'production' module.exports = { @@ -25,7 +24,6 @@ module.exports = { patterns: [ { from: require.resolve(`${CODICON_PATH}/codicon.css`) }, { from: require.resolve(`${CODICON_PATH}/codicon.ttf`) }, - { from: WAVE_SURFER_PATH }, { from: 'src/controller.js' }, { from: 'src/index.pug' }, { from: 'src/style.css' } From 9d4a0c848c9c1a4d3c36aaff655a8f70d17f9a1d Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Wed, 29 Jan 2025 23:34:51 -0800 Subject: [PATCH 16/17] dependencies --- .vscode/settings.json | 1 + CHANGELOG.md | 2 +- package.json | 9 ++++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b6032ab..5b11833 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,7 @@ "susres", "treeview", "vscodeignore", + "wavesurfer", "webaudio" ], "editor.rulers": [120], diff --git a/CHANGELOG.md b/CHANGELOG.md index ec97423..bf8ceb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## [Future Works] - Real-time input mode/recorder -- [Wavesurfer](https://wavesurfer.xyz/) +- Generate spectrogram instead real-time - [Wavesurfer](https://wavesurfer.xyz/) --- ## [3.0.0] - February 2025 diff --git a/package.json b/package.json index 488839f..6d41ca8 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,11 @@ "properties": { "spectrogram.rgbColor": { "type": "object", - "default": { "r": 100, "g": 0, "b": 0 }, + "default": { + "r": 100, + "g": 0, + "b": 0 + }, "description": "RGB color for the spectrogram", "properties": { "r": { @@ -101,6 +105,9 @@ "lint-sum": "eslint --format summary-chart .", "lint-fix": "eslint --fix ." }, + "dependencies": { + "music-metadata": "^10.8.0" + }, "devDependencies": { "@types/node": "^22.10.10", "@types/vscode": "^1.96.0", From 3de38b8f99253f5473af735c78444ff04b72f080 Mon Sep 17 00:00:00 2001 From: Lan Ly Date: Wed, 29 Jan 2025 23:59:17 -0800 Subject: [PATCH 17/17] bugfix: minor ui --- README.md | 12 ++++++++++-- src/controller.js | 16 +++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 408cb19..d7d6a0d 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,16 @@ This is an extension that plays and displays spectrograms of mp3 and flac audio ## Release Notes +### 3.0.0 +- Added support for `WAV` +- Added seekbar +- Added color configuration +- Fixed bugs +- Improved treeview +- New logo and icon + ### 2.0.0 -- Added `flac` support +- Added support for `FLAC` - Added 5-second seeking buttons - Greatly reduced extension size @@ -17,7 +25,7 @@ This is an extension that plays and displays spectrograms of mp3 and flac audio - Refactored code ### 1.0.1 -- Fixed path issue for MacOS +- Fixed path issue for macOS ### 1.0.0 - Initial release of Spectrogram diff --git a/src/controller.js b/src/controller.js index b9c838d..e9b0599 100644 --- a/src/controller.js +++ b/src/controller.js @@ -133,12 +133,15 @@ const REFRESH_ICON = '' source.connect(audioCtx.destination) source.connect(analyser) source.onended = playEnd - if (audioCtx.state === 'running') draw() - source.start() + if (audioCtx.state === 'running') { + draw() + togglePlaybackButtons('PLAYING') + } else togglePlaybackButtons('READY') + + source.start() startAt = Date.now() durationWatch() - togglePlaybackButtons('READY') seekbar.value = '0' seekbar.max = lengthMs.toString() } @@ -231,10 +234,9 @@ const REFRESH_ICON = '' } function durationWatch() { - if (audioCtx.state === 'running') { - updateDurationText() - durationId = setTimeout(durationWatch, 1000) - } + if (audioCtx.state !== 'running') return + updateDurationText() + durationId = setTimeout(durationWatch, 1000) } function updateDurationText() {