From 8a231b19a8179e854996d581410645ec239d2d8d Mon Sep 17 00:00:00 2001 From: Adam Scott Date: Mon, 17 May 2021 22:27:44 +1000 Subject: [PATCH 01/13] #11 - Started work on styling working files --- src/index.html | 23 ++++++++++++++++------- src/views/css/styles.css | 39 +++++++++++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/index.html b/src/index.html index 5bf290e..e736698 100644 --- a/src/index.html +++ b/src/index.html @@ -76,21 +76,25 @@

Text Options

-
+
+ Bold text
-
+
+ Italicise text
-
+
+ Underline text
-
+
+ Colour text
@@ -109,9 +113,10 @@
Working files -

Samplefile.yarn

-

Samplefile2.yarn

-
+

Samplefile.yarn

+

Samplefile.yarn

+

Samplefile.yarn

+
@@ -122,6 +127,10 @@

samplefile.yarn

+
+

samplefile.yarn

+ +
diff --git a/src/views/css/styles.css b/src/views/css/styles.css index f1b3458..6643b54 100644 --- a/src/views/css/styles.css +++ b/src/views/css/styles.css @@ -25,8 +25,9 @@ /* Human readable colour theme - light theme*/ --editor: #CFD8DC; --topSideEdit: #CFD8DC; + --workingFile: #d5dee2; --tabGap: #546E7A; - --dividerColour: #90A4AE; + --dividerColour: #9eb5c0; /* ~~CONSTANT COLOURS~~ */ --primary_text: #000000; @@ -250,8 +251,10 @@ select{ color: var(--primary_text); background-color: var(--topSideEdit); min-width: 150px; - + max-height: 85vh; padding-top: 10px; + + overflow-y: auto; grid-column-start: 1; grid-column-end: 1; @@ -259,13 +262,41 @@ select{ #workingFilesDetail{ background-color: var(--topSideEdit); - padding: 5%; } -#workingFilesDetail > p { +#workingFilesDetail > div { + display: flex; + align-items: center; + justify-content: center; + + padding-left: 5px; + height: 30px; + + border-radius: 2px; + + background-color: var(--workingFile); + border: var(--dividerColour); + border-style: solid hidden hidden solid; + border-width: thin; +} + +#workingFilesDetail > div:last-of-type { + border: var(--dividerColour); + border-style: solid hidden solid solid; + border-width: thin; +} + +#workingFilesDetail > div > p { text-align: justify; word-wrap: break-word; font-size: smaller; + margin: auto 0px auto 0px; +} + +#workingFilesDetail > div > button { + margin: auto 0px auto 0px; + background-color: var(--workingFile); + border-style: hidden; } From 567e9f83d3fef0e73fdd4527911bec6a2400a929 Mon Sep 17 00:00:00 2001 From: Adam Scott Date: Tue, 18 May 2021 09:20:07 +1000 Subject: [PATCH 02/13] #11 - Minor removals of redundant html classes --- package-lock.json | 6 +++--- src/index.html | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 46bedbf..ff963fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5555,9 +5555,9 @@ } }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "html-encoding-sniffer": { "version": "2.0.1", diff --git a/src/index.html b/src/index.html index e736698..a354aa9 100644 --- a/src/index.html +++ b/src/index.html @@ -113,9 +113,9 @@
Working files -

Samplefile.yarn

-

Samplefile.yarn

-

Samplefile.yarn

+

Samplefile.yarn

+

Samplefile.yarn

+

Samplefile.yarn

From dcfc6fde88e1832f5484b61caae83de866fbe51c Mon Sep 17 00:00:00 2001 From: Adam Scott <40220121+AdamJScott@users.noreply.github.com> Date: Wed, 19 May 2021 14:14:51 +1000 Subject: [PATCH 03/13] Update License as per Client Suggestion --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 37cefb1..3af235a 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,7 @@ MIT License Yarn Spinner Editor -Copyright (c) 2021 UTAS Yarn Spinner Editor Team +Copyright (c) 2021 UTAS Yarn Spinner Editor Team: Seth Hilder, Adam Scott, Cullie McElduff, Harry Clark and Nicky Milsom Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 89a16b7d9126671e7c020a373ea3e4057179f32f Mon Sep 17 00:00:00 2001 From: Adam Scott Date: Wed, 19 May 2021 15:15:55 +1000 Subject: [PATCH 04/13] #11 - Logo added to top right Top right felt too empty --- src/index.html | 7 ++++--- src/views/YSLogo.png | Bin 0 -> 47590 bytes src/views/css/styles.css | 5 ++++- 3 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 src/views/YSLogo.png diff --git a/src/index.html b/src/index.html index a354aa9..360a1ec 100644 --- a/src/index.html +++ b/src/index.html @@ -101,12 +101,13 @@
- +

An editor for Yarn Spinner, lorem ipsum dolor sit

+
-
+
diff --git a/src/views/YSLogo.png b/src/views/YSLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..513e08094c60281ad2842c9533427ad871468f7b GIT binary patch literal 47590 zcmY(r1yoeu_dYx{(u&flQj#LwA)p{3(v5(0cbBxlh;$<<-Cas|mz3ntHPZE-@$>us z*LoL=n0xQ3z4!C%IOh_kq#%QfMTP}|Kycr_d94hApxA@&8ccL>r5)Gm0Q^G#C@1q8 za*z1RY{~xyt~{`Nqv;5N;B_Frk$P+kjKM_=r?>J_7#nDWXjl*9gLF|K5E{ta*WxN} zzxNhhQ%y{~MUM^_)8@8cX&d+llM`+?l zIT{V0m(oZ-16rOkhmJ>72jx~-rRg0WM$hTGhRmh89JIjrVYfaln^M{bsn@BnRJhmh z#0w-b*+TBCh4Alaeq3&5D9@=Drc>>AQbnxxw_zSU5I+J8G2xDRoxph^lr&RR2&O$` zkhj_7$-*YdmJkC(EEL6n4XJc`MLrP)LW}2zgRIlikwSw3k)VLUF>NF9?0!)o+B_%u zgUSyfScS~W`3-2hrO*z#`#{73S(H&BkqPGz-l3OjY21kW{W;0Xekt365YU4d6S9s) z-h@Y~F+mFd9@CV9qChy!-$0bqr#ORC znX;xFF(I386e+~kVk>2AY{iKniAE^Me%%;JoPT>Yw@@LQiin$Wr82`ee30IP6^NLm z>Hv|;U^K!cGVR1mdku%n|Hv3jL*qsf zNPg8Sd6#`JxSj}a2z}ZbzWyf;KhW^)tJFt+IvCHe(;#9`@vR|b-;-yKjGx&-5_1^v z{N_IAEdSb57$pRrT-vg&LIhXC2FJB z!!=J0NnA!DgM7kzLzhCG(Brq!fa@261j)s9;Kn!iWG?#R$tyg91X{o0>{YGAvv9vC zb;R*X-$&TM9#yDA>q9pO+LGYqc}FUQaX|<4FOrxBV+}0>Nc-F2h-;a9w52R71qS}Z zj@17iAzXii?U;Ux=pi7LhG7s17za%+Z7*S$^DB*S$bRmQi|1 z5yInNLLgqEJkt1~NZ?s7(s~NNJ=B08QNF`EIGvx2WE!>hxvImFV38-5_70(6$Rp^Q zA4f)aa@|fOt`*tbCC|`6lx(;&$F4xFTtmGn(3F@kiq5Y{6+>Kcn$ae(%r4ju8oLTbaWT6-$N9LyLy zm}u{DqQ4jTvnE}&ZhraT$~y7%pbQhCnL)^ z&DrsM3f7p9%E%{d55?Q_o{P52tA1Vd7_U$j%)Ub^K$EY*xv;b6e~* zu8X@&20XANtltGv288PX6gwSuvTqdGLXy5}8cb-UXAQ&n%r-Zhr)b)dpj8;ppm`Q0 zWHy*q?qzTyfjLjoDaOtpMPL;)y>Ty!8(^PwmT`-UEki%J0)UAnB23akl?M=HMG_%K z*HJmYq#&98FP$Njlx}O3T@MA==ie?6zhqGy|Bp#7NSKB;;^ITXuX;`wZt52YI=%P+ z1{gIGnLlJ||LONQhz`(^6)%mKWYWb)2rhvm!J}Zn;L`ED9eHX$W+eI*HDWFOWLqbb zG{bh$FU|!3<%uh>Q;79x%%b+^a?UI9&O>VIq>U(fv8H=jT+?btA3$<#Mv+t;-QFFj z4mK{u+xh5l?;ct+(uuXQDxAFy3mcSTRLD8;(nd>%XpJr_e9wTVa#j4 z(g~y&H@JFL(?}7FbP*{_6}a$tO3Oq&B5DfQN%MjKxRZcvt z^p6@)8*q>lyWSx@9-}FAAh6jAwUijKe!m5b22@zjb`T2qkh$>xF5k%6XA!p8A-ckJ z;Kz61&j0Um<3Js%J;VRq;^6LmV*Kn#a{?Pd5@IJOLdDAL6p*HqGhDyQqD@wgeKN)> z5h~CRm^`Kf_vrNGqd=n$Zrsq{NDy<*)JG5pl25372w5bWKnJ!Z7&OuUeIoEs#wmu> zhoOk=IPi+Ht%>c-;X{F`Ldu)^^^gewNW{?~dn%z+{GjiyYz90?9>Wpj4-y*W!SA_e z30k(PdVGE#5upPdTPo@af*l0QD0v|wTvyF)aUVeF$ISc$~``+`1!8q9f z?+`!p0G+3XZ`l#l23hCsC6#^v76!pr{|x*rxGx60Ea>ae0|zh|D#U!Y;Z%&OmXd=V zFY^NQBxa4QQi@Cu=~}_1gcwuAlPx~ul~4TJi~*WL#PR&3Ilz)DzZN}VhI5GGBYKL# za){)i>rJ9Y2oU0l?LZ{(FCf`8PBx4S_-`8g&;RdsSP%p7kU&^}Rtnv5>|RkaBjgH| z1X07|z(`Rf8uGrszkp|@vG!Q?izVx=x6)ssONMnl>c1U1PLcU`^ z;?RctCa}&5)7Cjtz0@Abh8-q;`R(m3>u)(^VF$=%J_8<7Dc+eK&XNOcPw1z_0k@{P zx!$T4szF(^ZgzQOVKdL`<_({V%R*SgIJr#v?qTWaE7NC~I?NFM?})a;gMv)^uF`%f za%Rc<>9M7nKq07n;-#}N%l^X&4!RjK()DILWP(*OzX`Eqc~UXX^Hj34U#bpXkys8u z^09&hu^gT2AN5&V?4M^=hWNom(EJEO#Gq9)>Z~1?0h3cr#B8SG!!PwRH!taSrz?D$cuXYC3^k}>^gzF z{^*S~uq9leg$Kd>aF}dI_xaw-|1c+B8h~|)=XAOk#C#kcHk8%&3HmoVu}sS^q#i4Sk(LI}G;MVbKo~6+8hBH_;wXu{_-=5hklIAI`AE0O zz$l9lp+fu2BG1FrCroHxg$h^Xa*{Fl|H0P`757pAqp^jH#}(JfBX~zflb9EEhHf_T z9|_PsaD0W@sKv|0e0sCUgj%19@%4M*l6&kNke z;e&P|E+%9ecG{1njw`Z{%y6;?zk>KC<seXy6Y3AtL;b1<`OzQpR|_d?s<}7VdE)dZs37?Ny3Z%ap$*#J zCmR@hK;Youc_o*QT8moV4joxKP}eTORq$JB4oCCSfx#{B7pE&C6@cVu1A^^UQd z%#U-qFV&N*g?kI}29I4CbZ1TPruM0_X48i!g#nZRFe{Lw34qK>}i- z*-BiQ;dAXjjyZP&a1SKcMHcoqT2(d^v;iv7A2V^8eR{YhJ5x`n=4e>=5a~d2^6GcS zMvGDb3N7Bax!C(1u>|LqeiUbIXyD}n_UX8IAC(031+!|U0l$vsOSp&d5S5pS#$pw1 z)_hAS`cg+VJoB~HnLH`gN2m@B0eZ}Pd-m^NKh2UX(XZ2Vino$0ws*)+V29Ai{6tx# z!gK`J?#L9$6rL-vr_x{~1vij-z<`yUh<7|Va}!q4aXiI$=nrW|6)?mL@7jrdoVyY` z`NH1QVs_i#yuf1Jpgv1LsrY)Vbf(Esj_QDQLddtUX;PQwp~otta))_0G!_P_TWfRo zTt1?i39Xx$BAcm_Z7CB!&kjil*-dW#%rESyR36O!IOpMAIfhykijGNbq?_$AIbrdx zQ(19OEi(SKx8_ahBP({Lp-dYU-HGQe_Y)$hfD;?|MV_NII-oTsvzS#T*u`xhGMj~_ z*xJ4}Y0cP9mQF+gxP6~E@aJd2vHG6a3{;Pg*2ZvV4w+ZOMINL95)^|q6)Mrv%8^vT zWWHV4PE*Arx-G%VM2wxRTOt+hmW$kx92l7Pqgxzx3Ee)qRLgJI%2xbs^jeCYcY+94 zKh;MODYMlaIJ0$Kw?!i1ZjJUB)Sla6Gm+ET#$IMfftYU-}kBjT7oc%`dYy6aAsucS25{ULW( zd}}fvJbBm@K!I>>nQ|A&kG4+8x5&n@89JwE+c$E&cF4}ioY~Fo>KRpSf6!n^P&}ra zR1N>zg4!62*64v}UCn8dhOC1WU~|yVImVgpI644t(SQOIBiKT1ESO)ilh6s2kavky!v^JTE(Y(ckAhos5a zj&3%xw0OWmx`ZwpitYnFB|su(d&=ZhR-LNkM}m$|63I042V8_V7<->EJs%<2I&o?N zO8bL^)5knt-k*xv@I4-0?O*!Pu+&dD6R;?Bm*%T`hp?%`7`8emm?Rz6lLkyjgviAF z$*!7)=iZoI=a5 zjt@932d+P7WLpMA7Sq`3YqV`soQoJNPUA;#rMi{4VATKujOUZReVOlR2EUSf1V((w zlj!P@JdJ|C&v$&<>ra63I5niD`@DHaE?$@|IAw~5uYP15=2ZX#qWq<-Oa6eX`XILS zVX-OOK8>l*LdUSoP)Oqr5*iSeCd!QVLe4Lp8viy?_|?aNN%fb|Zt0R-q zxZf4KB*_aW8R9oeV>C*HL_dwsy|lk9ny=ww=qLZoMhbNCTl45Tiw0m0qRB&m)|i8m#(O0p8UIrsTC~DHMh*-}X+HR!{4eF~D9dE&{x9TT zu$|tbH)>6;^)HbDjZ29#03(9db%%*1xMUX1Z7#)%^zt)s3s8Hk0e4N57Ykqc#P`Xc zZBZ=Y0kiVvvtI5(A1vakMmB&)nuJJve>PEno6<;IEP{0wC*?f@R*pad4C8B#5?2k` zEJL!%-Kml~c|4M{CRh_JM2I;qeSA4D5bkw!gn z-bNu3(~+EM^E)~NzQOInGaTyQ{&0-=(HcVopZa9Y#K$|61prl z2PGIT8+a`EctPythyIz4OCjb}2D-Wb$bQFW#n304RiR%CR)>;A0>@s+KyCQAe>}Jn zSzaJx+lrZz8elzM|J&Wr11yazxNhLo64UhIn-UkzW%R=9jvw3&GC36M7T#%|Q~`2|Hx)%d|ntro!D28hk*Q)9Wrgw1}OCHu$P)H0%lo*sT}8rPCH=s*$8B zzj6!#XPJO?7UAhbuqZ2JY0W6PwaAv9&oM4L z4hWt)n4Df&5@ zI0?{M2yWeXn{=JSLuKpEL*gw9qD|RlJKaCt{SpYUD19%Foe@h8fj7+zb)hw0a-W@# z|0$Eol&c}O5jQc&`t~;+C4gfv%6ru>nD^DBV|ORQYM~%-jOM$0QiN@Z{(qT`CoCCp-YSEHkrN&{d~?;Ruw#E6rx z49rqZpT%9;A+ty|xK2GaXrlJ`gm7)Cxl8Rw@##V-S;TC8o`}#L_}7#LbR2E$^&$#K zf?#f|J~=+?PUoW58Q2m!sAC4Z%ac>YdBkT^iJg%AwbWs^M#TdefZzLIX*%<1-FYJ8 zQbu;;69fpkk95SsixnQ^P`!xD<`63D&T355gceQmg-%xLZJJ9BS-8=n4aNW~UBYc0 zBwv%dP>8$|G4WX|31O=u#+Z83#PGuqJ1voVW)y<|7VLOPd<*so>#*x zxiqlS&w1{=e@pTTe$JF1@itASSUm=Rj4G z(lysfg50s0h)etScUCVU$lVes(%EEPL|IC%RVoRYEHrFe&6C&3%hu11JZyzI92S z10DRu^pOs()0(9g$2jT5k^cIJ_1A=hQR#dH9LipaWNJAFd1p=Mjl^kGjV%~SEuR-G zCi6ppAze1RWL1Ms-*SnaOxKs3r%FmPeJf#)BfSGpy6ZT4^0hhdW)+>^)H4Y?m?Tld zOeit!Y51lt4R?l$?Y<>6rkpRcj^+|>u{gy{SQ7X5+R4&zn|KH^S`N_;UTDLW}WP;^2XYB4~|X=mO#jP!KmW>a>?9f9FWmk>r3m;+f(&O$v{y zF5p+@vjG%Am7f(p#o=A|%+u)h#C4kF9^EkM>i^z~3W|ON9GrM0ksbw>^@UCKR3{`` zWVbOng*J#*DZ!t!Vt?61wwl&JOsqtd*HCnmU_C#40q|l6a3X~b>)l|ntLTkFX#6wy zfa)b2YAimAdqy`qvl%oHWj(Yav0y{i)gu!4QRu6L$8(MDFi*nXmog{Y_sfnnz$nH< z=+t9^&)1EA@T1Xjns4bJa({JQQP4ZLnUz~2{fF*fcEKCJ3l6n9iSe3z82oLf{SSC~ zM=azLmjvq2f2oS`*Zp)6*IoSv_~O3+c7S^MD%mAl=*22-0O zm1V+#$>)kY*C>})z9M#_n%T|97ZPaH7F^AliVkHyZ2a+6ob8erQO$;8dzu?R=-_(3 zcbJ>5!nFW9*zvT9DmrXvFm(Qy08jc|E}P9}5D$R4JuuWCi*_J9?#HUpu(RFi9Dn_< zhnz_<#xcp3rv?Q@4<`GxY`-Y%qo{G0%@BYJNl2}=U{uD9U|+b}%q!gPMNHZOlkFm=PrJ_l{~uv*Jar-`DhRY`h5`l01ERxnTR(7--U%k=W4)>rRRHzSmhf z9^EW=824N_J`EHdXw(xp@CTz-V%xpQb3LLt+16rORulv%Ed4%d@t!v}T$GxWFgP2E z+oYdP@8kg0)OhfHvm(XraK0KwYfvaWy%W}qpJy4KC`L*1Tn-4-(cA1Hm3;t+7izbb zReG26DCS#f5kLM-T+(p1UgNvcXpknV3eSO_B0Y~1e!OJD$N8l$Md%X)8;d_A|*IV{)6Ins{StC!YYL)Jz2 z2VoV2i|F%;miM8=$i~<;+i0#v4~rQxNQE9I2}m*#emFumqYGceW*7xQl2S7P#{fjB zlV94M*@!b|Ouhs;?Mk(t10S%Q!Ne%^{TaIQoF315tpfd$U55R#P>T~V!gV@S6|y72 z0g~#^E1%bMyo$nnTpp@?dcp3`)?^BbeDjLQ7XLd7fW&XJPO6v9HkC;=m&%v%vpUof z6Rj~w|Nh__h%l#1ssDM0LT=_MJ$!`i{b9Kn!zf4;FsWj*D^g`iko?)^EgkUBS^V+9 ziq{AX_`Q~iv6BbuTGc0s{S`7n9#xJheqvHA^Qw;n897Z1hz+_M9Q@}igf=i0Lw}}D zOg%y@Uk`LiruT~RR7uvc2z5P`i|n)&+X;E`uL<2KElA_6INs&bep}_3lF(|~zPmW8 z8;L}KuHYwcmsnlT?pm48?)h2|@BNF?R@mnF(kt9Lz-oy`f%8iJH!ou=ZA?;9Jd!t@ zFO-(!-=R==;t(a(+wMwM=%1gsb=Sh%&U@{8{UvuJult#V9<-VVWVzOv zES85+Uymy;x=%%mrpg$N=kTH@r(}%B-P*J|Gbu6#&mFu6Wk|Vs7J{4lh@s6hk8`{ z&8tmVPvsj(3q!?Vq@lo97SbwjqRCfY9TX+(Q!lq|pJ-8j`aw=rC+uWvwKX3*2h-Gj znPx}-g%%;%$@+(Cn=8K8E^HChi#Kv6nkcR|jWegS2G&C*zetdQw}Et3`b^x8iEwjGDuP>Trv(=i0=?Wq4T^;!uxH@+_V4P z=D_|y_4F)Q4yN*#~&VU6jf zC%Pq+V~f8ZE?TEmRF}MJoEOM$s(Hq9W*hlI^m?fgEkStjTPfCm!?$&}geYjiu+7Rr z+pRgOYLDLn+4(ikK%V0M!=XQ+H|L3!#yqJwS*Kd3Jshn?Ys#@#x%tJO*QpihX&po`UX_=J(@Q?`?>b+1Nc6D;E~KZ^ zgAKMLNtn`@hvhP>EZeC_y4(mbR{&eW7N>pP1TpZ<^D$F zZ61$qlCPr*_$!2>5R7InxD*%8whTO_6t<>pVp%^f99uIWvdS51Qic***4rM@6QJnPfVv1KyZP4ZOKj4w@ZV#gk#*(9DGf zLCy3lCA`~3ch$#Xqq8(*|5fcXsB?8S6Cc^mU%2Sf(LUN4JwuCR?oQUkGjMJ|g5vp) z{1VIS7vZ`98h7Pc;ezTPxNN_43hx$+M*plZ%zBzFrXFkRh!R{0Oql@VorR^vTPIh7!JaqGU>FJw?~5tf>bh#_bG8&wMK;$q z?`K<-Ma%~`=WUuht$paD~Fz##Lj* z*>#~xrU(x!{n>2mw?zSg6eJ|1;g$yt{6CBotWG<}ZP}v0A)-iD%iX1t^CE!jcxNY> znKP4vro6xLC0kqX&7kG_C2dR-Bc3M@8M;Sy%KJ^ew@4zG>5ueCdJPwQSbF^Mmiggt zYue~Os*0!(*7dQ)X;NL*eJniL%@Z70M~U$5f`)bv?1KWV=YbQZeLEGYtmac)*7=a< z2b@9`rk(`z&fHxG`|$p=eVx2fKg}N-UiBcL(>{d6?Bq>0;5lt}Jg|r4*5>@+Z`7tI zKGFDnmg3zLe=tryEo?$vnO>`ahB4V0|8uF{aiWDAA5w!u6*d$tCxSo?sc^H|myLa4 zLC-E1Hv2a3H>*)cQ#FnxMyADkTBW0YIt`l#+7_g*cX&SL_=&Ue`VUjtZ>v+#h!#rd zxWw1E9`2p){9bxhYjj_cuIth5vM2n>L@JZVGQt!M`w2F*K%T~iub^*p8e_S0%ciRW_0#Sq6}*rHlg$E1H*eV24S&;1jT zu-6{ad90W_{}hrBk61atVrZF>ZIr-BNbOLzx55QDcI6{*dBlAh*c=K!U=U zQ*_wcu+-*Y3bky%;rsM*PuUHO^cs+U_jVh+2MamViuNEq87Dv0d2LyO0eIiXqu@B! z<+lyV!yhu~1!73Do9V3h-P1ppfk|Hi7Oc?0>lkMLbpU?F81``ostC3#N^1!(97 zE^x7npaDmnX~XHeRn4OJ$P&*tHU^C`Nt+YL9pKlZ zc5?=QDK5mI7Ctg#q_Gi;s9w_I6=bk1)04?QP*SK9suywXG4=t~JSDCqQL|)X-mZx# zk1vRBTUUP0&bHC9hZrGlw_6F{jjrM5qB3Uf9+d3TFC)tIN;aB!xi_rfgo*6-GzDJn zpg5v5->brXjqUkfXl<*9Nz?Dq)hP=11N*pCuWqlv)FnQ!yk#oaUi-1u993~h>?rHy zUYMr_v}zQ@^w4IO5FepSOwSIa*fHQ6rfpkAlh(LJLG)@M6DoM}ItZd9vCC^h{KtZ% z9Be_;;>E;YlK-31 z_ebmCMo^zaK~M5CsuD4zezMg^$@sk7h<#j;G$_l#W8C(BMk^6O=xTf>P~K9vAvR~$ zbvN;&7$_XPSPJWlkz~nN$9Kab$`Nx``lQGc`zW&-T_*S=yuJ3FND-1}J=oFx%=bnn z-WE0EImoEhB$z)8zv!Jxq5BLkYcQ>ZQ_T2 z!{=A~fgC#<*ph;Mp7w8*So=9sYC!Z=xLpsx*-k*pmvK|%?ZBO#cdM>N1r&uqgn%Imfp_YMf}z5@Ni}J*wUUhEk+? z>_LIvshEH5rru&`Pojc5dy5*AWz)S+n8ia;Dt(n$3RPC0!&e}()<(Ya!OChU7rLy7 z89zVViVI1`$53)W)1EZ$76X+Gpx|wcpKmvT4wv%otW7SG!HzI3UieB*;68ZJWgh*y z|3sC%;XmPNijXJ>Y>}X0DLJ%s+?rf9Mrv)nwxwto%fY14#b6ht5zzYm7`1PnXOdbQ z-NR_Wz}AX%E3zceyXh7l|Fe~Zlsd9QtvI}g+Qmy~Bb4w7*`5b3?peZFQyE}mkF#Xe zXZhHzAes;3^O;(~@?RiLA&DMBng0kEv$a}$*ol7qaiG0+mUEU^8^gm0ZlG^PxfL-K zIRA0p!1s%KkLl@Ln~txM*GB(g&oKrCDYfUNcJ_oEP9l!IWG=RS+-$CCalE(SQV0_&(HFZxc@mX~hJ|D1N?Dkyq*1vWd zhxuegnH*R42n|R<9=?2^Rv<<5X#ErQ`e3wVD9TxBQSP%#y-ZqiX%s5pZ^rM%OXyUsBmP0mDIr(2ofxf``o|H1Wr~}Lytr18 zWxDV+KIb8(ZLYuc-D~@>v?usyxh*ukP3h;2e8bI3v-`h4UD24{^M&{RqK9U7oN1~S z9Kyv8^W1J>qEHpT`4#nDTZJ7rQK#cM`?cHMm{N9Mw-d)T{%Efla-_YF%`ZClBZCwG z2~vEnU;W#O8->@Sf4`hmK|7Y7Rjl^8DCEB3X7%C>*YaB_yCUD;&u(iI`YZI14__i5 z*iRI2aUgag_YOv%bZvzdsTCa$m)*%MG;A*!w{?8I&U;wk+j{W(qEoRo)yZ44#p?c| zhq$HhE|eoOEKv#b66+r?)0`H^oMI4}C@kdrT*cMzMOl8k!y6Gf3u_SVK214@cGbNI zODxEn&)c%t4z1F=n>N!P--$wIVeHMlxxt{tdo(pY8M9y!I&nFSYq+|ybot1HoJ%@ zGO*zY|I#QRbR5S-y)*yFR`9TxgpykBOc3FjOLHZfDyLQuCCrUoE^_bxg>LVD7E3IAq-C>3oD>5NHlcf8 zEj_+l1Q>QG74;&U1)2u1F3S8)@&(ZhYlD8jhnjYx#CJY$K!Md5DDu zc{)iI8Zv3rnFA2cg>=Xd85<*LwLXlIxozlEjEdrWL&gg45h zKU1_~+}QMAa+_Bl4qrL`YCO>FZvP?ma(v=r#&B+f$Z+)iAKz7V*TqDqTc2Tj%hkB( zYf55)K}ofaXt`O-|9Yv#??Q>sBV3j4?pZ~klKi^vx%a`+bpUUtJqATQ5cAn-LU)4i zX$uhZXH}Ul09X%R?8N-iX_p}Idcr^F8#{ToIPi^s`rP++;vYEK4n;kF!Pk;{Qd4)0 znm5z6S0#z_TkNm;hc}K#*u5j@#to4KSW#omp+!%`fwjmo#>Fv--otG>@oie4ZddyJ z%3|ou5Ir>kmV)8I+a)-B?e;~r=Nl`mc5|9<_la#pY3HVg#FEAk_dJd1Kj-}Fnr@Wv z9PT{Ro$uqIH@@Mub)tG4&F=9*3D{ix&< zxf-wQ^wGUMIKEj|Y^6w8w6`92zD$x4Wz&OqE<3-Mlx}pqi&2Dn@*#kj$%Vh|HB3$h ze0yES`LSEM?T7J=>rz+?8TY3;+SP^|rb<9lRpnocPNCPyB;; zWqltSE@}U@Ui=TlnLNMpg9wO?vxsGm)b!sTptM&C-@3M`O z;@1mv01kKQEUxO~u7T~i$?XzxGi{WfdwB$@g@L=Lt4d+ga?j@n|3u=%*@DDwJuvf~ zewTNV+Sl{0t?M=SBJ7=vqc$Sq*!1zub=CjdS8vjBmBSu9Q|fMbz^f3F^G!PU(e+j& zB9`&<>YMPYzgzjqw?9A~aX0q!v#n3WY6rW!WAV-$?@Z67eu54`!h(zHxbr!aOe%@* z$vSaUXjXS6S)o-$qz)QCP|U;1M%p^oz8X#Q~{Ke zvE*TrT6Ojv;AV8AXcZ;D`Q;3GzU2GjJUKF~r+N*z&`;i_h}w;bc7*|zIm7O{rMHyDKN>@-?=`$WP! z5z)sWXD4?#!@*Js`6?yMTZsPS)43B%OPffpV8;aEZy0HmlC*pr@{~cKa;th$~^}Bbq(2J^AVx_%;xns zwkBSY+uY;M4u#&+mo}t(k&3zwdMBsVzQ!C&f4lH|YO{QZS-IMGa_4YAN$@hv9+yv5OYlSy=PWu{(OSU-(nBQ$kk|V&G}Lo|F2DWU7D-fKOQYY z1k?bfL<7H@V)!?|;R)nHPhISs7?-X#qPtveGk2CA(QiA5xtK4z)Bf@$x}hLTI%@9FAZS$MA0^F8>O_*aEVoqf zImUy4zMH)5Lb2lP%rZvs%#_N}!Rsc@Hu`LH`q%UZ0?RiW#G;S9I|A9P3XtHd{r{+h ztZ%1VcXuULYJS4f*ote({;sWartJ2uLyyn+j!dVi-tBosb4z~tOqcWdBAM^W<#`Xt ze;1z981+!uB}y>9g}y3Q4n=V&oo*qL(or$`t`+HfGws`x?{gBj9(8-3)})}9V{VG?@ol|2Tr1#D^cQKE1<-4s51EP*$V z60G{PQ@qcLywEz-^Xh#xt5ews!YAMD$Wcf7f6dB?YVv+-d9%yB{BT6*%05V#7bRQr zIG+91@#fD=n_ZUVC)$G}Ew5sCds2dbiKE3LVI8RaiQKI6$^eo!95^a$@};!B_!Y96 z)yn#o(skl{*YQu{TdC$N5E|??kQPPtG_l_pd#zO3*3Z(SFi^O~O7s#Vcut%YWLQ%- zuElz_`d(L4hvD9W3BHx5+ODd~47Zar1^j6|y8{9WI*esnVcQC@7h zikhFwxWT2~6c+>_p<{eP7Z&y7gwrSCN!xa(1+iY0h1PU1U4ycr4+@ znZ?|KZSS(-Pa*YhIXt3*>|9nneS2s?uREd~Bx|5e>9O|oshZQ@$3Ehbu`t6D{+sv5 zeWDjlAiihZyR1l{U|D#JL9H7RJtd!R2-SY0_&fGqo$v9gUvnW$r<9YWpu&i18s>9% z3RFFNiywhd^=z4?l;KEp%esLf@FVtEv`E3PfkJ*xHN9*y5|0%1kgzA7EKn;v{*jSR zeD8KV_wwtkP%%nEDZ)^e+&X=_7H4Pl95{TV`7a(O4P|k&vpK@ksq4=EI-dTb1%+?k zDqfNOjpJchYi&8U#p~Bzw~cZj^1GG$@+Feqc>(aj>W8wcCbw0?=ofnpU=f3gAY7%W zIUzv%ssv488r-gt3=Kr(b}+wL7G5zA(pOr8?Usg#-e>rR523WYr96+x+q{4!+&SMI z94j*Y*TqO*2!(b;G&g&xw7AU-N8Ph>`CAy%eHl0kjn~_RQOoUOqXZOCzwPgQ+z4>I zP_^;1+GaCOwi)hT7A@-8FNfu6<5QKez`KZJ5KrZJRDnesNyBSRGA}ohazqa{Wa6ie zLO`U*d(#waC>{y^>fRMOzpFpKbMB+OrY>_gY(7M@4bAIcl}JzNYguo#V&_k^4%NO5 z)kqz?ZEvn`3~J~Xx)%f_Cj%%vF=CCeBY+H)ZjQUz?>^kD$LrI%?zK5CBwt)GFH`Lg zl>`Zkp2Y;tX^u})FqHVbW)=CZ9eaf)@DrC7kIas6crdVq+;PH;6?F2EX<9UWOrG?cP|>B_X7UtUC0dowi{ ziF#$AtfkI0GvI-+=|B7U)aE?c=XqlrSJ3%gmX1i3$ol@rYZLZEYTO@)edrL7at_SM z=b$2#eAA}y#e8@7>prw1>^LXN(`4r4qD<675!BgUcD2y&(P4PjD8k11yfOl*Tn_b` z&E9jd>QTrgCuirnpP0V=;E4DbLCl&wRLdExA`!@Q>kQ= zm!&tXC^`(3EhFS?T3*m&UFqo89WT|NlbS#cG5IlRp37Spe{i zMnZP_0t+H%?*eWzTKHmCW3FaF7bDNDz*bJj`Fv;|W#PM--U0=y{Lt9%g-0brtYhG1 zIaYbO@9y>!#-T+l;i0lBMNVeG%rD%m@#j}3^bv>c)M#H za_D^&%iC3?;A3MrGW2IlDb;7G$H?ejjTb9%l}RSO`c*rtxkdQ&Cf!&?4y%Wjpple} zV)2%az~#gkp>RnW;;L3kLT5}~o`%X?O9P+Do7^QnGl;OglILMWqL*Wml&uc;Q$2_A zSB}>v_*-)f=??2}kF!&JNdvPxf`qcPL#|c@d3A^sg>F8BY&vbSLKKvsjX<1R+_L{$ zqSTp(i|0`UbHwVpB=z<%O4DqerKZ zo@(=6EfrS?4HN*cs#~y6+(m_1a38!e(E9*zPeMW&KGAwpaRCY+-vrO{sZSFx%GBEg z8?^+mT=fo?PW+ni-mPi&r2aF>s$AiC*T0=0y-x4pjxpTTt_ zcNs;e+Ui4{y_`x|kOFvF833ZM1ne1f+Pl)K9zI;e{+-mDZe|J3| z0niDeTA^Y0AYd3oWJ>*Q#73X(U_{@1bTmMDyO|SUn8V;x<`n?rb`OW zI}jtj|NZT87Uta)FJn5e-drD_M^{l_>Bdu}3hK$_40To-8G{u(tXdS*USs;5)I@IL zk)fgYH*;vm(Zm^)gi~QTQP=Ic%m|S4g52kg;cyoqO&C3r=h2`M75ToLmXW!qx>JqI zYZ*Duztye>I9;cPe-NF^AIy|BJr{90=I^6CuRJTgSTDL`qw?)N4&uWv^Z5l|d~Se0O>wrB z=DS>NW?#+4Eu-xyS8aQ5ZfO%1pSNkI#RW4V{3p_IbMR8drxSkO1y}8u<@+uM;a7LZ zUA~%Ij$X&7qJRXNUNo%PfM&k#UGu9!3_`4Pm4`O_Uli|tF4NVG;!IBLY+QHs5BIe^ z?xF8CamkBX4xE}elWrqWfuFQ7U|3U^1V!VAzY2j>i@hz3-SK`Upl>lbn#J!69 zMMr&d->Y?En5Yv^4almQR}*jXBJ(NTm2c)g=skk9tbB3%D=yO{-i{Nc@9c&su!Dqr zi<&C51jW0e!dA{|Z~l=s#?^OUHyV$SE29B1TbwUAhz$0N{JVEe$92MMnJ>XV37C|k z+YOmNJzpS_*>hBi390~oxb0^7)vEteWRT!oo6n!&oap-+MFBcIokrW{d-D4ygzvBU zeIh=-|6A?cJ$_cYWxo5xWM3C?Si|FYu6uLQl2lML)%1?V`A zu-f-$XQb~PA8IJARkLT1@`}2dW(^M=&AvHVUU7}m2GCUR3}F*tI7nKHxwV#O9h+Es z0(NTXiDk@EbZ88>&y=QHUZw71TBfm?6~J!mOqb2Hs`(C8nH#S~?*EUctAMI9*t#f4cXx+$H;8nD(nvQ-Ug?(Z77-~4MM}E6yG6RYyX&9p zd+%S1CD*ksGxN=y*k_+T_XZ2{1755Jb7`$JFeZlmX(x0Iq2*Cp-?zsFa5(x7t&&PU_J8{l4{e5nLVlWWoe28lu)!epw=5hyA|Y{ zK9lPQgUTKDhe1GAmogtcx^UV{{THq9i)?iD`6X+LGOyvqKG-GU^j3jL>Hf!Gw(DY1)o$5oD>@GJp2;&Pe4UO zvnFQr@Eh>O1=X;1SM##37;i%nava<#tI@7{s#hMNX8|^Xkd!QD>|{*$g~0d6!SKK! z1ISi%@SVzt@MB+Dw$uCjp_?b-j8f89fa9cF+W#v%Skd%q*ML{8;{?+j&2LoGc89*z|uAc&-%jHq5FD6{FdKnJd z6)KNno?61)<6oixX*{vXO!!Nc!(N1sP;!AL%$f7;4Mi+8VpWWQ&!MRTx2Lh)Zpfwx z9)*8zZ--k^+`%!Be1|tY-?lO*Ui~y%AwG!B5BTL6?*qB$4p)iM3yhr`bL0v@199E z{1qTh9i<-9-Ro>P4{hjWdR#PoW{L{}4MpG;b>Ziool;S%N+G2H{t*(9M4mu@QA7_t zfR5lBBWToX?R|6Z@U#bh&7UqCl)03$hxEuoS4mI5S(7=lDjjL+aW@X?3xpn%J5s_} zf#p&G*A~f~iYie6O3dE=AXWZ>=99W6o#}1!GqY|W4Cg?(_iKj0%TIepO{~?Zm)+yl zY{Ad|=r0|=20-oW^rL#QmYFvgKbsf_2M08)pawiG^oIiQ>71oaS4F_OZ$b0f>8W~~ zf+$LuZ;=APaZV8-?2a`W&R1`rqJQ)-(3?sw{e+fnzV$waI_ee>5zh-w$SGA%cLn*$ zH84;z&@9eYTV^SD*eZAb%NZMc^mKdr&f8lvF3`5_`*%*RCHBLk45tr`z<#vLTcbdP zah)6WFg2wv;Jg``4F8~!LmW8g)qGonPzu(!O?DWo{4fVPbs%HFuO70JWV=~Y!dD|y z#-EJ!acGxr>|z)+Gx^?4_w*cP99KfIo5>z1{ul8buKg$m_s@#d06&Zf;dY10ab%57 z@##?LIQclqCl5^W>vM9Cg0EH9aMmFG3}B%M z)fSg2&!%(o!uaZRwMDHwE{^2Th#-0XqlLcj78>L((5-8`(;YOz@suRq z;j@Qpi#`gTgvR>$0zXrJyz_QFm|C4kPGg7a#QxB9N=td)4Ly+q)ujT#$Nfh;lC=i@ zdl6svGxoLN=&SDK=~4;#c~FDJOk24$-qaWp#R{+2=C%y{ZEJR4Y-tgk7{Ohq)T1AH zy=MN3osD`mw56F^#iLS_iOK=k+iJPXRF{YLBc;8EvCla=#x{gG4q#*ZnbKE_khY$+ z*v#Vq_Z_ngGN8$l_f5{I9v0XD!7lRmO*jsUpuN=d=tK{P`EUd>oXqRM7R~ZF91>6d zo((sBf-Qra4xp*>0Ug8KHww#0p&6KdH+m&TT90#F{7%=c=iIwcsj*>w`u;WJ#MMVj|nBf-eJzPQTgpTbQydALE_p0_9t;n>obF%t zC~5gC8 zgWlHO5AUru7h^CY_Ku=m6-Q3_Jw8B={-PlVdg#@hWC*MYXGVhN26oS#a8L2SJFgtk z-&h<^Cky?4nN!hR+0I}d2^<-jdYej^lo}^5_}bks;VsE;x~m$THKh1vKxg^>rkqsG z0~}5G36hs)n}pr|60yTwziyd_V`@7k4ls~c4-+V>8LaQmccD!Wpv6OG0Hy2qztw0L zT~`kE`r@t6&(Sctz1VIwpf=M^sScw5NxXv}U$~h;qcx<_HQEPyrS@k*UtIrL$!&Z> zu*bJwDp363ckoPhNWbjy*1VcbU}H=*HYt4|od@)PcFYBHPrrftF>~bgdRgrv`py@z z0A9|@e44!loHP|kR`igf62DW%|31biQGqAzC2d3wS?vWMr?i^~_LqnsK$`=jd#dE- zJ$BFS{aPkA^AOZ*rj_k3qZ(Gm0Wwvq=TN+I@Oe3We@IXyCBKl?blG0(H4q5;20-H6 zlQ|15(!XB0wD;O(3l=vxnjkQESfjm+@j9f*#6|e`c4K~C z-Q{(~mivr^VBOls=D9WS`gg$h!)C|A8B(CPlsSrlDcbKZVJ|g5 zss$r^0YN>X)zf}EXz8mH{#gL5IiR}>69xV*3EC?cd#%BN-qt5jmELsK;62~(Vn1r+ z^sRe@e<9xC{qRi@h>ITilZ-U-J4rH5y;6KPKQ9V^V7vau+&@u|uPD~2xMm4YX$!Er zK%>JJ6<&Z2L%g+#a2VCVCl=RP_IO#Q8YP)rQeYA1#0iA1fOmoC0e^@z$*ix^BG z{O8lOboIj#7mud5MiiUgBZAypuT=LC-B$7~Q0jibFt+cC&W#f9YIIqp|FIG9S_>mw z4cm58&N7;JDFwBl2Df6(U7j zQ^U{JW$4F@A`VBHqOx*PMd2i^p!RvhHQ(y-G`HSlTU~>-Qu(qQi?b6eKZOM)Eti`} zr2HCBD;cr1zgu~Fd@#C6UbniEhdM*)*XG$G#|>!)4|RwDb;6vD4Sp8ybe}}rp-<_1 z!RKX|DB=<2g+3Inl}DOGTVxuGC}sr+$aaZM5a>!i&?`FVGbOg0W!|Sw(iue3t?7)F z#s^bw1lJDJ?`!pG!6F!l-RKk2hBL&5QN@I_IUfcsuC zvOeTSYXjd*=0PXRuADyxyjO0>d?z}R0q1%vucV?rS1*&-_Vr6F6DqlQ*Zd*|beP|1 z92EG8y2D}t;xUA)I?t&+U2o68DVfd$2v~GsejysGKl-GPeez_CV$a|T60K<%O*X8#?j>_V*lLOe zimt(UQt?m&rizpKS7+%s-Ltc$0 z_}ado304Xj@z1pl*HBB!oHOTG-96(mJ zI*RmT$`%-Ne$((nbOii`XS_xMp9}>4P6D{YqguX+$IT8AK?X?lc>P{ zjMqppg}1PKh6hs)A@`WEJkJjp%8kaBu_?!UpTZGT)E(Iyt*4om%QgPNYwX zlEL@K9r;k%YUlQ0)18foo4~$sdd=kDvtapj8HsuNOH(R;d}j;YK?_5qkEG-W^KM$g zY-GZr3}2vzN@?aW-+rN3nn*&O2tPB3FPG3M5tm(8jIMEw%h>KTE zS$l37rd8-zs|O_4Hu#WKnSAO`bIxdCslxvcTH=2Eu%!bxt`k<#X1qK?$*AW8iMEVA z2>fq2 zfWN4mA3Pt|PfM7BGX)p7`Lz!SCXRK}ck%ym{|~ft5olTNaH`&?4q9I|9IP#jRw9`& z)59i0(@;aZeh!#DEuKeGT9_NyhC#zon4E#QinGJK`oN-;O=%(?z2=^gS|nHWQJ3^C zXKf>Y$mhKpw_2eR+&qj|^46*IFlpd#!qBlmNScZ@G^dhV5DS8b4b@06O%wI5xA$5> zzx{iCQ%cMV+yaF4{~dfu(IbqW9x-^_s<*tbD^+zmi2Vgr8$KM(uBC^2achoir;xmp z07?*;s0Brj7rBA=%(j*bX>27|V1Cw2jul3efu$INSx&>s``(+*@GoaT2ag9b%j9=? z8w=PAJ}T-W?z-X!5-kyn#RgwT$Tx*R2A)BqPC!%>WNw!DWcC{d7`s?MkD%fq@)c zCC8*kO1QB_ic6re9YvFdTH{Z9ffR$axLu3h_-pRu@92=GlwYL=|HmvAo`%KZhjT%SD$e(jF&q9A&H|TS zIyMP=lB28bC{~NZQy}YyTD;Bt{}Vxt1K^Y6)2*Wlus)fzZD+VATq*)(AA?l2i=E1U_~V@Qvl zQ*9!3jf{SQwZEH)kLZxt!30e)+nB1XdSM}#^!Nc9#ns?FziNoz;Y;`1SHV74z3;XA73^~+!I{!-$YA$g&3nVoSP81mikNC_=-yGEz-pto5 z)BhouDkl^&=OJ|59A&&7cu})f}M#)U%_|UgiiV2N@w0SMQ=hQ+p&+r3y zvK%11z9LslgTE*`Ot*s;Y&&jfESJY3kGjzkMwZO5+8wv2IhQ-i2 z1yNMsR2%o=&Wl_;*ZRsslcBV{pq#mSbtDoUBS(OeE$;Fn>Fd0gfXXf}YEoSnhnzRY zZ(0zrAS*OtxU-wuGC9X7DbQ_L0=HZd92aj?*wk0+#D*iguuHW*rPJ- zh3{OLaeS|2K%N(5i3oUDjSfBO-^CzYhk6#_`a1GQ953`P_AP@RwS_I_E;er=Q0Lw={}qenD9F#y75|LxR_i9^7j(0UhgXb*USBl z$sikdH{nrZW~{5KmBRC0Z_mE5ao4A@dD9HelnP&Jg@vBGS$M-+sfYx=WT7kI*Pl!= zZ}M0+56xl99xE#~IP`7$>=K&2*hwD;(P897%gOcR6_Bx4waiFS7XMI{U zj<$JFn>eQpCa0}e;#b;&ZI^}FR{4KV6G6-!z0Wx!n9}ZPS^@hu`R{6I#qU;%k-KMX zB-{j(9u}oUtf7fHvUvy90py(yAX= z@$R@ggO7S6);yH)3XdaxRy>4p@<&fsYBJ}nvE%>K0!Ya8w<2~tlE0Fd5K0f}vkn^a z=J=#*!kebe{2ZZWuj}h7Sp3dhYYLjw{(@X@O$y^w#wDWq2NNya(OYkxfRJ6}thCcRLUJD0x({X-H31PKIh%N)kN zO!hBPYi(DVZ57OmT6WP6Y2IwZ#C+)lsUhB5`Cg@_Q=agGh=9e<=!=8v8(c zj_EYz>aef&-C<9i$qs`-*gwK*d|W?soCKf!S`6X9UkU!n30+m>YbflDgADj5MUw=p zN-<;~nt|AK@>THqWWUhl%?b^?WZLC9TS5DuS2Q5*C;caUGavfh$=eIQ&i$x<7t`K^ zVlR=kY_Yh39}(E-c)pLON9yGn1vrH$i|v!UC}62bd6e_)7nw}N9Sz2x#Tv&!iwCu$K`Ot8 zs{yIwKwYlH_NS&R)I#N4EVe?jDHS0UZB+ruvPdNqvnpaEQ# zq%-ECq=V_>7Rpl`$`pIs&rSFYR_bUD;;35VKP>kx>g<7&)^F#x6m!+(tfo`4E%GiY zLTB%h1p`UhDR=SdzkHIL6lbw%zP*GvXh*X64IeFeAY-ICF^}-YhYW7><4b3Vcpu%| z;qeA>CaF-RD72e$ozcFZ*2LLd`%B>0BN101&+-f_@ayQ*1g=$*h*pr`Z zX8Cl&0v8YeBHQGCv$&_!@gbqe7d>_;SS-^|xZq=?hnn+ph}B^iD3bnB7k><W_+g&iawl?$<}U4%;LBwgNWWmVR% zQv#sOczg+xz^`0YaC0Yv<-r0D>-pf1p=F}C&AkYSsE$t!s?3kN4I4!y7~rC75?~*a}K5i_#hk*>(g-Z__dY3#!gN741%L~42DrN zb#K^{%PKaVU1oozemIp4_^QMIIhb->AhRQylNYu$rx+Sbl+o#cQDE`wjGp8(g}(rpl?-GMV3}o0YYiq zAK>4^3?DiG#9-tT{G!8PwDbP_80gOErUnNx#u>s-%GiiS)(6~&CGvGHj(sXljZy>R z7P{CAz!Loxi_}BiR&O2%tq4`dnWf+Aj!zR{h$+WgY~C4&*|uSwX+|C(`D>wdIDjDCM~T0bPFUqe7!t2a&83nVsDBv+?o{14Kr0z3(?lHspyy~ zhb7MhY07adBy4=t^ax=~LtuV%1DRM6vO3o3r!xxzPSYCyEmh@ER#-cZWvK0JY&!k^ zew5~%L)p)mhe9zfmm+ISf+=9Lqmc$_ronbVF_Si6;J?B$>PCj0Apw zTqvkkL>z~U2`44zzrc7iN^M1deU{wS2AyJus}ZtI>(ge+)n-?f-MaJn$AfHgBeEZD zuLRCcT+e3bh-wRd9>zCXbCdvQ`e>a56us?~1=Eh;9Ls-BMN6!mM=SVze#!?D-{0|d z669F(fFbLE$;zh^_Qyr*ph@!(@cOCe!~D|``h`gcdXde0{M)+r@R#I|*B0{v8zLFB z)S|DPQ;Tcg_uqg@uRHKaEYh6bMWV%YOp4UCA}M&XtRh6ULwx{0n`XSrG>#espFX^0 z!o|;C6RFYk(}Q=Ce;=2t#DZqPtCR)nfDPuSc~c&wn|4HogzK$uNpe3YBw`0(U^ALm zlV6_U9n<3MXb7cu&yggrZasMrmUW(7{1hDI6LH;9eX}}bCGK*VZ}HT2wB!R=nMenp zppg|?IN&qx?q;7RWC?{0(JPPwBNR)$N9@Zm9OTvs| zCIm6CJrEU~D!7|q?dY5JyqZ6scPMLcTH<_gQ>h?p5#4LpHhMQejcs_pPj-J>-1G0) zvHe9Mv@EThjbs#|T>%rG7r}D{nKuNMfzN0L`j6jssAL`d1XS?xBlq>iJ;}eJJyftB zkJ9uYbCDRv9nA+l$D#=#U7bBNvksT7;qK>&Q=_ETW}I?wYlY}ITP<{7lJ_W*Oyw}+ zPfS9tm|q6;(ECbGO6zHx4rLTKmWCk)AL`#979E*>ELq_o-RMt}3B-~<6v2;$*{5K1 z>Z+Xp%S>XDhoez^jp{g)ESFbWm4n9Htgqv84nO^#&rDCY;n>x!@}MitAIIG!p6SOp z_U2Hb^88ADqK}&Q#Fu_m@>%n}@b&sGDD#{I6D)}Qpy=gA24I|F1xWMi1$0(~n(?yb=f0BbM7^P{NglQ$oU%8@!o;af5B z!(g<%QEcp%o;in+dp4Pi?GfQG<-d8S!`+$urMSSq;efjYuXU(nbhu)w<0#s!3H@~l z{jCuV#xMc%ba|~;Itf<4e92IP*c@c$RdKrI_byL z1^^>_V3+!RUGJc1iOT!JDze&6cQZ_Sf6*@dW_{SAZCi0szXeXr^I}9|ZhamK{TASB zAEh;au@^~|+*7X$iS*nr)dWAEp8p$UXXjN`m4ifvJk3$xyVJgJ=wr;aZB}`YnD8z7_)fm&vT^@2EbJHXNmm=;Wz5^nuL?D~&B4FUa4WW4pTX z&?L-@MJeq|UHZ z9=v7=D7I79sVI5ITrN;jz{gLZUU+dvdD8twd3uMSCFo3b%aiSN&Cxn2S#!=p7~mJVF{3Y&8X?6wHzs8IVe+Yk1yIml(G^d!(UdG|m#t%Sdb>n!Eqpc6sSd@7q| z#|0&3-wU;R6rn5q+6^t!Y%;EK9$|{{w=M!=t1Kk1|8axbOFnyTcr@vrL0S?T<0z)|88fm8#vfXL}3jb7lEe z((VmzX`X`6@-HeUu*M( zyj4q1SwIQWx0{I7EYRYT49M0KFdxv2f}b z@qTCXYhj6#mqP8nx#~L|A!SO$1z#P0;=M{GA&5>1z6oULFy*SClnOl=JPodWO7{cl z6iOn?RXrOvsa^?a!x_sf)yI>b9b1g@D{?1DFN-Uw57?pz%)q4LF#g0EAtj(gz?6of0kz>lftwXya^HN)^(H%*K z{w+MXM?ea=AFQZg;DzXeHi|T8=x!Q;zpW8VN^S`IoRKf+ChM2JeA<2knixCI0FQM> z_vU})yB^9;3#+jSVe{_yh2~rZf z?`@&Q_Aw%c1gg~l5eL@bz!u^>m0h!Hmj{J)!2 z;2U`Uk;pM>KZRlEb9WzWt+#1)czI;==}QpVBGLBrh%N58sr%qQeEi7k$GIs;I=$i` zOH1Wi@*7QAm;;!mvVq&ed)Ya1k@|H!k>f+|Yxg@QDICtr#vQN=U3aNkt#(y8V0PN@ zbT}zd6z1dk)0%i}o{=kNNv0!HXq_sw3*&aID5XMxAxOk9Y3(#~uVD1-e}<|rChzu zwhcj%T9bLDb%FVMCVNelXO}PTtrJY{{H?k2BNqkoW}=35;K<^`MqQ`&7b5BcRRShV zw`y8VZ0Bom{M@e?MV@Aa!|2vn5Fgl)bQ;`9l;COLwa-^b1;?vS)2JIuPss!&NC{WB zVmQn(P0f+!l#VHTGf1G>IDG{=5v46xvGD3Qo@=Do2Xd^e`8<7MPgHawXFxY(H80SUs) z$?(1Ip`ZcF2|K%Dd=xfP5qWpSJ{X;eMpXjdbDg4J?6@ssHv@&Qq|~)ZT42!Sthn64 zWG4epA^|^tYvBjQ+}7VbDz@}^`lASA=8x%i{47TVTUDOuL-=FDwx`O>@?$OWr`dbc zsQg`;#sxGx)_UDVA1PfTr2y@AMGLj-Ia&78Ihfb*MDtIVIM)N&1o3iox|l-rWm2SK zo*N_6GmD+3arV(if^flXkh8~s)Qxi|3NhzOpr-_xs-mNKBSy|dc|6FVIJnt4RoH(JC@lp9>-ED4fI|uPB?Ew% zvw6B<_O5iHb5Ij986f_~gi}4c{`3I!D|8)kUT=n|mgd)qCqFyvwmRytiY9S)O2FJv zf{$hKz}vgD2jD)L6raQrjgbA0n-pLdq{)_F$m)CWWtc{0o(sk+cJ}sDBder7XDBO) zCRu4qEoYw(e|OCpdcrUhEz5EE(S9;w^~ME=Hd4Iad9(CA%X5&;-;`grdlajr30pre zyhroI`<&gbPFXord~o~D1Y)ime9Wi4|1MV5rBdmZV$la#vASdxO}-q8AfF5$s8Gb~ zHR9ESAiS%F)=vI70L$tc2$*Bg^cA%tjUw*K1so;HKF|s(UB?5GWpu_*;t>|qI zllFW>zy5L%NYk|n4*tg(bOu3j5r(FcX+}m4jf(ng8aJIKpKgzP*lnsULFd@-cG;sc zcv@KEbU==%vB`w!^0p2lvXdB<>UYKp;LpxR&H3^Z<^z4t2d=dQ69&lyTCE$x===hl z*~MmK%rXrHBXOOOItVZc6R@stMN<<4_@-UIFB)%t%{fcmyW4l^L8UK)k88_3bN{+o z8z~D`wBD`+C=k*#d|`N8O(Z*~P{4R@5g-D>&uI-x73U>4^^wHZYl5`tm8#_k z>o`tV*cZU)Ba3JgD+#z*6(jS#;KE_X?%IOIqGKdNwRbd?P2I1P4?hTGSE9-UPM4H{ z##i_4&x3FIZ)IyyVtr19o^!I@s4SESj>h(I_vIe$zLX8Sv))tSspQXZ=DVw(Jw9bRoRXfY zYO>>Ztv;}zl1tu1$E^uvyNZG)G#AQ^D;R=&)5b{WN~<#_Rl5xqL9%>vUpD z(gtUfy+2^d7q^lCQ zX;(Z&I`@uA$=W`jhQmB1Pk_d$v2qu7(fsoR1ubJBMveEdL>$|O!2H)yr?CH}9nQjI z{O@khdR$S8^Vu=@5_NUFiiOZ1_Jw$&XQ#n6clQxBntF*>{Oq!l!l9+{T>JaeQ0WYG zIlRUJ`pQmnbfD;n->%_xx<$2m`y!DR$p@wL!0j*b*b#D&WTawH&CR^k(XVoRP=7ud z4t3GW399YjzM8522F4&Z5+P2PT&=vhg?QdH3j5F<{4LGS~ z+xu!(kwed*ZJqik2UGIPQEqh?v|t)4QuqlNUv~Z|D~Hg4!{W7R6)>@b$N?G2*xOV# zM-hG>-4t2dV{4SXTK?Y}_l`>pVhF{47DUaIEs3_j_?lnSJ!`jIF@NnJRi8CI?JX@mAzluony-&=9m zCruDlb}x0_o)+7!cbX*y^0QE$b+jxtm~!D8%~A$b`t^?Tw42S*vyR$=|D{`GDgi;* zj&LWQputN>LvZy?U(95|Owsj3`W~h#y}Nq+sX48!%}R4{t`Wl|TH?W>sSDAnT$fnv zM;@X2Y<}iBG=-+s&8{1>G!))na`(qedCBY=_mI%Fx3=?Btt1hjZ)lSGjJ0KP##4Ae zG+nQp4L&4-_7!A#3bJFQx8dmyNlol_nt=YTpz)P^F<+KlGqf|A-EYWa3BtlQs>?|T zwX_0FbjYPoZ|TSu)8pAlL<)*Fj`IR}lPexVCl8 zI(UHtS(vKSHhje*lQ_zksdvIRj(jMivs4+#kHh%Rt2b6ds7oTweukAVs*va(ciz`w z=i(@yXirX1|fzAAm3N(DpAx!pS>^n6;7 zNOxq&HMJ1r6LkI@)??AZl3GHXYTNbGF=0lH9Kly=T4^^H_nM(~Q*TGWdyLDEaiw9( z1_3#OG5?%-U6+ml*nqkfJW<^s`^~Zl5;dX-?#^eQhRh{=#Ir{Ev=0`84(z;e-HOl- zl;u8tC1%tYN`$}mjdLD0BQec?l6h!+;xl%VsT!%pi|V>)M57;(P&HEA+4uj$yR&K=C$<0HXHsGdhO4Di?V7sT|k6ZcfcVKkyl zkd1a`M;!{X4uX2|`H$B~mz_THb#t&an)?}{?2NV}wXb;wag-yrfPe@t8}=u%{*MH3 z)CNH@&xV0@?nwuE-TVk5 zg-R9pASR;{8i>A*`6-y2Ko#@n_4-`$Y#KE_kD?NNMXiIwVb^)f1ux-f-uKtmsU^iX zI-EULSndy)eb}qCNTMiAwFr!3RP=w*Ot8S7sVEuvB1O?RQw`zAH%aB=O3tsKVv6K) zVWO-wDG%VDINEY@fi+9&yZ92J0_QS`J=o!Xpu3@lm^xgp8+-Ij@F2*FuNAo024m|Oj$+}<`3H99k_4$=6w?bo8i;7Ry_6wDAvD|WzP>kMO{RCPQb4?@sfNc&?UHr8v@| zk2>fe#~9@E$4d8>OEEEWbV4p_O4c}1$0V(nG<3WiiGJ+*a51saOY7I~vKlr$g(%q}_{u>vjkQb!e1#);90MGoTY zUU6sI2ZayI6?MUtUH>eJX2brn(?U;#j;%B0y7WAnsmZXGWp&fvRjM9D{`w+vb!A^h zRTjn=c`j3h%y<^Kv8gy(B%Iw^1(1h!0)D}6p)vnY3&79n;mXfqX^)#`NA0z$l!oJ` zaf=^z5bb4W=Sm>3WAqLsu@k5=Sp-!upp-5+`RiaaE8Byiz8&hK@Br>GDLm1ryNsCma1n(#>Frt zu3CUShAPIHGL}jOi@GnU7PALTUJOVB_{G^$9-AIhF2c7)YD0+%5pT71!e4cX>L6z4 z@TA=_VoQ>e6+aJ6m45Tdn1#BCwXm-%{&y2{VbFiie+Q)%rXjd+hLO4xiB#qN z))%av+`)qo_2mpmrma!pBvn%0pV*MkE66`Si{_yTAxSddf*x6qi4NTnnQ7Pr%UcKS zkNYb4>d&ad1=QqFY+^+rJu(TjS#xN)@!i5C6*aAW+q?e12ANmFe-wfCui4^qqAp_g z8c)Do7buB*aAKpWRFDG3!bUG`EknSktE(SpKpcLL7={hFcyd&YAZBRvM<~jS$fT6S z1yc&C1orP5e-^+6;->9AiL}XK>X)yzm2AVDzOu0Fa`smUFIUV2RbA~2S8YeZmAi^0 zdZNi@F()&8sh|A3NW)om^b$BL+%&kHjaO-a`X9S%d088gMGd!#)wY!cR-Z#JYrE~? zwpI@I&`w;Cz5?`f-`15N5=05%p+`n{Q3{E7FnLx$3umLVQU@T#jzENPLL!Z)2?t}v zuGhoJ)NU4kZs>|;0Qp_cXnGHh;_1_uuSM5?YbVAUej!AP)4pyQ&-lnQ@XrD9@LwG> zOIXpqEMi5_{wZQ#=h|>gl{BOOqZ~--Mt>27x`hho?{6FwmNYe)-*MFU`+9(|G4$I#7idAt_(Xa+cH)AN z5wAvSrvFz`zn8sIG&5Qh!cfq<^2uPCDU-s^;78cnx4&*Y!z7%63}0jPO4^z3cN!_a zSAS3IqD)AcG#6KH{^}JSnaa@_|CcMxpNegditXiZhT-?sub(f?hEslAI~p1i9>YR^ zxmwFc;U)wPcQ{HtwlWWdBF-xqZ+itDr51it)DQz6L#T*HG0sk+hQOFTjYAX;Bmds< z3|X2qxzD&$^vvOP8AB#RO3b0Sc*1ehdxF(rUF~*jj&e$=g@VwqEm@HZBhgZ+>aaGge(5Lm#-XIS69gH_l&Uaot>ID=QUt;+e-}^F_wZc2t8O-Gy@n?pbxB2LoOP08QUbjQ z=EM9gM0HWKS$wtilq|=FKC+=nlI9yw4`C)NN2db?%78@}PQfe#Tc>mar+2kYPIim~ zh+gx2YuL5LDjBx75>E*kwQi5mwchh0wZ*76Qo2v9{c8%PP>%wtEuA3SklNp_WmLn% zm<(#*(r{&yh42*9AtA#RxqoKHrQPS7+NI>hBu^B_v(IOc5h8wL+XG5AJC$5aQIuK< zXnph)BmlD7guP#3X|)nO?EL(;-ph)yFzgyV9WO<P7ChKqvMfNT*7zS zVwAcbX$6^L2^9D5aSplN99K+~bTy$o`MdG4Niua9da`m8q5?v@qq3QXvv~_sV-vT- ze7!$WO5sn%cOYsA5x6b4W*Vk8m#0r`v-j^{bh6{a*ZhLH)i%DcI;*!>N)8myOs6^v zS_p^VeDFNilUBiPC#QMZB7ZAwwXmDKdZoJtu~&~MNgZ0WhP zc4=`{Jc0kW5+iN&)AU#8z0BRE{mt7(e{nSH=vYwWBiwCO)`c3aG46Cxfh#{`1I%lo z7}$E_2oq;x7M7?OJQX-(XByJX5KrPiG)s?Vfi+!e6>YL;=@gJ^*dF zP7uosjk`rZsc@)z|BvxC9Q;*cq04NV`C!t}Q~ufCgRI>cRtEk=!c(h#KOBr_hA+~X zr-t?NOxlfWlg%$V`zMaHi_j`Pm z@w887Yh$zCU(Mh-LOjTks>C4&ucKrM^!$WqG6Kb}>w-r)k)Q|t0i}PX zS_mj1sUSOk)C>7TTgXuE+6Wo4A3pF!1_YMayN~t4 zM&-YXI#Mq#9wTMEyQBwt$z|l4P!wx=?x+MTnLe06sFD??VFtNmDEN5|1|T66d~SMO z+{Y?@VSA{s&+{>`fG;-08N-s$H+caS622?F{wo_~J3HMsxmBJmiv3#0_|veHfUZVL zQvH0p#Qauts)imyAg#%}rLnIdHojV@!yjSwLs4`s5~oi3b8!J4gcJ0l@dSfGn$P;9 z9R?D~M^gLRBJ{7Q`pubPF-FoVSkRfyEl#AK<5#2vZ_A*in-cIc8iyqsq|60Q4k=5_ z&aOF#>-ENp6m^a?{T@G8C*b$+@uTKjwsMuTPV0ox8ypvoF(fB~uNk%J=eVS$% zRY(?8Rs|a`M~|gSH;TR88dEJgy;zMfy8;3onuOC>*x z2{YGTVeb4Us;5#*X3yP7PS6}3MznvtuQjDgk=@A9Z0!=#vPKb0^E~;Sz8;t_D z?2o6Iv()1FS;DaTRwUR&kjU4#wfZa{_!%MLdu5CBjW{OhO|LmgluJ{Kf!YxK#8KwE zmixvtPyp>e*`WU0gs^&f0MP!0w31;CNqx83LxP51@>m4CJKdOD*njs@hRsXgwfL&-6<;FgMf5*{Ws6=eZO$c zncdjo>7G>UJ%{bW6^E)5cH-4bVZJMRV5Q# zhsQNU?@#W}{`9(CM+f~>=B-nk-!4K7GGYim8Yvl}!9AT}5-+!zivo7|8tz+(`LIqF zKu!SlHi!%f8^-Yb!@p0-nb)zAfE}49Slbu6jczHsTiQzl{%;w=Ka}Ab()BX2*rYJiL#^4l`}R6A5WscPKVD z`U)5w%YcXCK}~1>X!~T5w zDDyd`s3v_#lmi6dTL8e1|M}Z{a7Dz!Wqb^Rh+zELk*DH}E1Qq7m#Q3#Q*&b0kFya@ zH1~UstPEbAduy3At1W^W8m9zI?!uH{LR^JYaDkkLs>#0d+&Z)S?GjS?oh{5@86mCz zbT`!nO*5N*Sk~g#Kg>OR)3jyCb<8vVPQBoV2nc^?`yG0XB|&KXH}%8}pzFMc4^_6wOz5X+DHBuVfNa;KL` z6s6o3%FzGfadZa_@#K7)d`l%6^5H~!cnSoN3cI}-<4gfjFaMq_hB7r4!D3{^K~GRA)9L z$w6L66E}mFO?wW+2%uiVf|l?xmZH%KLv{^V5b$y$;Bx?OXU6$4?l9$GXShLo3*2>% z^GK)ZEB;N3sUk?JeC$N((6j?^?3-m*WFb92-P^xYPjMu|n20gEjWHRFr`XDCyDd4M zEiN1SS28c|5|AKkYF-@ z?%M@u7u9(y021lNJZs1{a+X5Dlo0zDfgV+~0}$4rDUJZruHv0mQ?>o;kB6`A4jq6< zop(3wwb|7iNBWnw`gJ~xTkYl7LG4cdg+n}f-K#OyY~WHQF=&cWDxOB07#FLB$VB~a|1A(4LK>!UZo{dcHNrL?ML$^M+8Y*$8FzM6Et2ja@&cVa2>N1 zEOoyWm4^DQYcz!p4>tnHyo9Eyuc9_Hq4M8#`P|sLojj4H#6nWT+pjE}Ud&(V0eG+L zKF8FU&RhF>NhkW|VXMGaBl9TBubUe)K5asoo^#1gkY3{GsxI8+?j;Uo7t>^hS&|9- z)FBbsA0^^^xT5?ej#GQ_pQ}ztWhK+AD_VuQHvk_*8ym9WTK9F(D){Yyen=b_5j@SE z5iB!g;LEndpp&}Qz$?P{UFv7kd)`|xz$jV)+=HTuQxKnYT=LaeA2!U)j(@fnx>em} zz-Tk7=KgPDiICUR*sx@|wYKsuvW^3Gd`%oix~pI=YD_bX;1@wFob)`rHh&9HA8P&> zx{WTF#%Y}X@{W!rw^{zm)%dHz+4IVjQVug1VT^~kLeIwl#h|yb?nW8=*rBdocY%w= zTy0dxQ%q%93_tw^Ec6N+MnNK9!mG-RL3rvgnX#-4Zb`7{vq^e>(nP@l@Q?Eg7Ipx2 zou%Nj3}V5V&<}ID+$3W3guyotnU0x$vq&I09}@(*4LNvyH zk}63IGtI?OTvO&C$LT{%YhyBe`527dcx~+SuD1I1COFUlSL$2j^u-%;O>WFdIbBG2 zj7Tt@VEr*Jz>JU6^iph*4uG_LLW;CWB~`?MSuuIR z;?^g;Z~{!B9HVRlj-2thTK`5ljh~x+7y~tJrayv9S34B-3|F~mUQrXTKyCq za)%9(-JZRX1~m2h?oAKqSNeOF5cAm+q`zCn*!H`mVBuTRQo3O3Ve3@_F%W}YG_a? zL9-)V%T!O^oQz?|_Ui(rWoJV4omxV%aJqmox{u=w4~NXmeBJFh56yDo`rc$$wwd%` z_tg39oLq?ADRwxCYX!)^+;Gof+m#SXkhNEN64wsN z814~c{xW9hr@B-F!KJ!#rbPawQDAxSjmJ=MgaXFOuAiLBhkQItts|J+=9Y2yQvdea zd))h!_KEj?685eryt8=vsM2WBRrfTSCPyZ4mVxR8H>ok;67MX1GxzRW_kwm4J}q*q z=G&-=0ujWVhDX`nviglf=ij4>7DQcQ!~5Ag7ug$# z6W(-x7vl509?{wn0$j2 z-Acx{InA96Y%OZVyS6DFxlshqb+(=mph@w126n#eh{ozHy%{}h6gH24t5pK zH_^u3lbObxquW6}0*4juGuonx$YDJV0cz;k)qM)td{mx|uvdO`Avkc0#QO03p4tMF zV2#@|4N)bJd7-wuX*pp_l}6IYW}if5PT}J)z)-+mxYj*04Du!2sOw%d3gNDrzXa-% zbfux0MWfyh?DZZ2iWA%QZ|aifo`)Mu%pvYcnzRZ!PT6LxahjVyRQK!asZp>J$n3U*W#-tVl=q!F!jn>B08A2Mud*p*@>SON3fp73IH0;+jPsj3ae@ zTxAU#+28ms)Z@$;kkNX;aeF!z4ptaw)XPaNatTTcF?thGYNt_#j@xULkx*5(TJ^aF z6DwbFFS;hCDPj<7?*IM`e>?gaOFtgV4G?^@J2by|E_zWuWy$FWCu1O!#-^qtfTeg4 zkelwdhH)0#crkrdZoWq%pPmxn*+1`ec|tapld&n>st};TMF>;Hr|W zq?0xoQeuBvkK^4hSScNo8U38#su$1+>u7~UM?rE@>4U-d;AeUj8F}gzAg4d6v6j*= zCX20dF2K`o_E#|umJ>%`d)VRaGPtdm*c*MiA#(|=dwjpvKJw;3pnHGFp#(J4Kog7|E7yB=0erN45+&ZdY|=j{6MFaE!-!t zSWSfz0?54~C)L%LoBrmYm2A$+dZIj8s=4>Om-)VtiJ81*cc248Ab0><)kSMgG;{Ca ze{}pX7Ckj8#SQL`myayBr-EnAbbV4pVj&NNFH@ErY$-N~rg0gW#ka85}xf}m`?uL`mZJ6W&( zTXb6)wS0Mb2NAeGL0Z9$T~!|XAp1ZfdeZtB!vcd&^+3#i2_lD@or|Q zv~F&fCC5_g^6z_Gx4!d-+hzXp%$}3Dk)ZgoUB$6QlIAZNt+zj6w#6~>l7RAZ@>$?# zYi_>z=rEIj#*5QRhX1rs$X{AlW#U&;nPCJLB&0}wmpNh!;00Xea>M3xE0vKfe$#*- zW#bS2_O5jdtB&;6GNpB6D=>{b{B(q@(lrbHvQ)I|LZRSL0E;EF9{~8j}`~85vL{`0xE4Q3ow>+%Po=OYm ztVT8B(RN2{4Exqd*>UTWg*0yl+6{t4HZppnXg`UE5*lxnbk{aRY^`$1x;$w8TL`OBiQ~^0C4-KhC*9U|#R4WnScy{6o1VwG*fG z%O(SM{mf;T%MF?nLG?xc^=rJ;l0PfT;_6QV%SEf(SL0jg(nBdqo2ujnbrSytacfv! z$XF08g-va6uWcFF-3EOxrqI0-Xc?dXF?#b?U=!<9+htz-y+Nz9f1`OH=_s@xF{!a_ za15$1LiLFsjF3wusrI-BphvkP&p&=T|9xh8TK5rtLnVMJba~zCE!}L?>N0uT&&@t= z;JdA%$>uF&zq|3_wRgHn*{D5pSLE`={ZWZg01*C(_-eUC*>0aKM3hbJt$!^i0PE$q{Js)Y=GyZzBnC7-BrwoxXP7a)My2A%!iE3Qq(@8YnLf+9@`tHo)fg6UarOrBaF}$8{dos;hLwnvi2aLp-L8How zH3gMtWWBCJd%*!-kMsSp#hKh@F+tD(ZrGS<>SvU_Y_gHq**V1gcWe3FxCW;aRx~yP ztlQGTmP`*~3;s*;8^l*fhBwqGpdku9js+cE#hQBBbQC%Kcn<6cf<)7U%mk$|}qDuRPg5EJJ2c1=Llv9H!e z7MGV#CO7W!V<=w!=(n<9yB3{!Fui{y{xuImv9Ns=x*d)hHTok; zWBzJRvE-KO#~;O?-jj21u4t#TGY0OD-e$wBCz971lb5^iMeXVpwgq#mA9V89ks!S+ea@`n%l-E>#}T9sJ+ap$6eCL zUTZHlA8O=`Zi*dB2888lS>c1jqO~HogDa2ytsEF%rToZjqk-)?MOz)+Y83khKgmzc zqZrXz{%5`YZ|~Q<;?28h-H{poN%aZKaTm8f9V7eS z?)++p58)?eSE$1Ls-W;7nZ=>IKe||If}CCDyUJ7|cO9x5J?Ct;ubvs6YP0-q+Bcu- zKOF=+cXwdx9?UH6ZLK{3Rn3=7BNMhmE0uYz?aXXh!I_#-QcMFK+RYbeTS>(}hCTfa zE65bRqf4gyqOnk|!rq+CLq{y5EV+@nA;0`Dz1$o#N=&k|NvHEQ*XX@&*>iu!iduaS z7E9qr@t2Pp&WdJrPuS)cN7F&wOh~U)i7887HSNffoUX^mr)X$Wu=h*@ia=&^%EoXp z@+adxt2Y}2)K;?9UtV1{K7P*>5z+k=_5}*@X!C)3=HZTXfzwS1t$BZkRuIfp=$`Ds zTZRt?)uShMf=>^hLBQH^4l!XI`M`MArJjh$CHab;cv|`?Mj_tA*Jsrc4u#&-tnyj* z55?)Z(NwusubXI%ggozhAUSyb6sDd^Xz?PB8)h~HX5yd3o2shOjWlN8bA!7PM|F(Z zh4CKL2SA+rH(9@hRwnc(*I|iKhEcx1!dkEAGZ?>{J#HE=Bc(_zZdpG|)KzF~5O8M( z$IO2-Ka8lG3B$s&Dpcb&u_-xJNmR{tug@c1HSp@Ycz;zxJ(t(&acWe&C5Wh*27OtK zu2~3>HI0?($u4|EiziAQ|5p|hMur2+dkor%o5ozG<hF%XBWb39Aoj#rI`}-7zN*k37eays1$<=IV6~6lc7M@ea znHXWcYI61>K154rJ$A0GeWS8h-sME-K*Q&>^S~&80HZB_21~e3hXPq)?m1V^gxB5o zGFWcWZFnb2*pxqMaAOOsi=CS^ph6T!TYC;L@>71!R7VG+0>xMrV z5S5t=2xDrnzIIOJ&TNTKvo3TG$J=V!4BpCaT=>P+l^6*Mo>EdHfmYt3u;n*%7BojH z$^DbihPKWeH272;JO%oNNin_D^VOZP@*D-l>;lJWH@dt$CJcg3Ch{B(y5}jbJD?j< zTvn8q$xLpyA>nZqZ0GF7G_VsqbG&15+r7yjUHtKn_hbp%clHL}D%(|BaGd>;GtW?$ z_TXQ~0V#VbG|4dsAd_2(UQ=U}QxvCLBRs|N1G%N8PG%nicUxWaz88hBKfRb);5+1G z`|sUy>4cinA(a|Kh9TDQ!u(8TtY{#f&xkg1cXgX({k8fwKlrYj_!a?EM>2eaDBJsr z7kuTRbU#5<+_2Lya$SY#rP8cShh}-I(ocR0@n{FNt=F16{BuSwr6Rtk3H-q9OS(ii znLDsBTrGp%9_Id8iAjq3GQl-Hm0S2d8Cpk?#*Dq5Ft1YucL^9TOIm&T{`kU4Sl{`q zzo&-JL+k!sX|<^ki%Rz}Ay^v%v->%ii_Gd-eZ=L-FD|1WEiM;wD5?;Lyh1%xnEOS^4p;S@6DXFfx$pMb<1wrkBi!qcw;)cj5;w=U}Gc!>?xVg9-hPJ-dfUj#ae3IxP zyW^$ERtAick2bMYO4y;kM~h1i0x^Xz9fJB*TAeMwN<-mI&UzOjmB{XIkU~7oH(14r zfHJjhdsY3!xW`zP$E66Lw`#uJ8wB%Sf+>GqlIwM1F#5t1gBw)5<(Dt=FcS}ZSEPEa zOBlP_^>|&XHvJ85hBgN*T^DkqQYMj%hOK6dpo2)AeUPD`B9~PjdT8&;@xO> z2?O85jQHWzk{hZZe&q?ZPo<5gz_6WEKgk$d6g~?XgKe=|qjzehkr!K!|0`8>l5Zw2 zvO*V|HiTM}kq*9~$Y3EVIomaEQ#X(JEG= zGd|hdpO3z)f0D+$*#88ZT`8?rJMC*Q+t?UwYTmsscwwK&_K?72Xtku%ZPPH!ZZxZ*H}& zWTzarYv+HsTQu?*BzkGDooy(X4OA3ryRAddkdA&o_Bic??dWW>4_h8MJ-shj_Owg; zg(XLqymhMR+hx|6+~)OmJ;aiip0zU5fQhn!dXD7p^@9%Q0)wUtR>?NbECGgn;nb7n zQ8J3bLAlgw6d5N^r1@+4>`aFQ;9k5ayUNK=&nff%TX~Hk(Oz}pVx>7MY_fu&8JYhU zib|Os+ry|a@YfNG!p4TH6hK9Ze-PD)Iu9$0(G~#~I|24q|6;4DO;e8fvb!=d67pAECThL7+P*1hG$dt2j#Bpr zyhv)X_0qM}YuiUsWzXJMH^8mH(>RU1iN~r4PP35;=Ki-KUw6+YzkHGPkMr`LxYjH7 zFkq4mDHK^~M>K9=PVy9jhnB37@=Id*@@YBJ!bi43H6PP_ez)jdT?#DvB-)&3V9d@( z^J6x&>%6C3mQA*iinjekYtVW00I2R~2C~{MCZ(N(PU{Bpc4X{|k8XY}Df}j9vRswq zQ^s6k9<8;DXD4~hON}cZhMGYF!7#)RYHoOLWMYDdCxV2H-rWqd|2JHb1A=p5ykS37 z!tzvldQzr`4p1OUa=dO$c|y%sM?8Xy1otCwB4x@rBFmVhBKvFZ5jQg{7RMx9s73dxRil4yQ(<>p7> zn~~?t4}n-Q6tB3Fi0^B~0MrxP5_I8OF7^tA#qxOt~}3r+3GGfH>3x161{CFkGtF zlq8A(!w|)o=O60Xx4|Ne$wF z2sZGR#kijT|My4aRcePBhN|%vk7U*-*Tw3Fj-$wij00cJf&6|DZG9RJo;KI5@I6KW{!?hwCGWO(D21RK&IO{fZ1SJ` zdj)86Hj3Y1Q-x=^&+*}=nAfi1f1^}Fh)jK%GWgNWH)D)5Y52zr21s51L_p(7WWKK& zsQ3jY^sMc?*5Hv?YEJcqcTe80x^Q5(StU}vYq)NfyolqztN^7p!|88Oh^Ai zNr!@o1?rrYVVm#S9&rkpO0}0iKs022sD9dC9lwm0-@-aPn!*5M{ndaW z){zn8xZC8rzNVrX)w6{Df29kZQoYB0IpK=>vaknjMD{M#TPY&GN*FiuXcr#a0ibkz z`BxE=98A1}wPicA3hf+|bRLVW%_1x2R}iQ!l<@bR{@oX#dBxg&7_XMVI%XRuex&*k zw8i9BXEM4R=po8Ig#6a=ih)R}5}lYIcw!sPBt;}^+Hld3_-GcQYR940I4ET(qU_@@ z$)nNSI>?ezF<)zDhMkv&(DLo)TU`_&oNZ(tJM1)L+KX2a$V*9w9!Zr z5QvQST)|+^0ylDa$M{Xyd20hD`nE;OK~7kN;J!_|nF5rIU6ENY_0q!fhetZbHPp5S zBnMeQ7eoaU{uF!qmQ~MASc}h2a5m!o_qHj{g{(tSj!eloM%8If0vU$t>e*`q3XMUN z?)Ve+$7GP6zJ-N=nO_Hq6MGlUyfyX|M_aUQU>(*CWjTb_ z5l(O1`PSOfoDE9W7j)Yyy4{yt=^$97D-(qzydQeXes`xhQ~px#2d(COyvp-}@6EYB zEF#$1O%^M*9kFoQl#L6!_PM&VlFR(3%l8eRg-5WNLzR)${ovW^S`i8SZL7^1} zB-rqc;0j$dJccUciW0!QB_|%P{IlLHC>WE z->kYJ$H=~N7kvc((dX9<2BRR8hl7H5Zq3JXamwKtHo_j~A@ngNJ6l`oa+!+!PIz2y z-tQmQ2d3sOavy3fl4h=3nj*Z9l9M(ku;KZAe&WYLH&|WL{47Dn%gQhJo=XSuoJRWZ zqrc}M#(>VP!NvK_I_TBo6IF0fd8sysbo`17_cv9Ya~~ddkm5%2Uwb$#P8ptc#k58b zMf~^fx~J@1Z6!@ZJukltCN#s)w~ww)289HUcyCMB4T@8e^rf==-3VnRFvKR8Y^Dg` z+Whe_5%(PYj0oxIXAVSN#w7~`S_);h0e|B!Y-Lq51kRnuGvS|I&*KJ_1nXY@Z|c~Rl3oGIfWp>U?N&wNh1$8$ zzv%AcA$Vsgz@~{brp9l)dkZAW=EkGBgWo$o3kvuJ*n*E{Kg*0 zS$53`>M@iIZxhHe?x_(YUU{EyZS6;6`O-}&bezkn;1Z z9;xAo=D`+(C|k)pE~#a|YjnO23d#Udy00`IDUm5!s>Q`?8DfEVA?fe5Mz?3OOc`6R z>~|aflIYc2zCr7q4P#izG4Dp_XF|VQ+F$Q05DD+2!B>X1X}3p?4mnVoBO%zG{S88m z&i$z*VF!4p7^wI1@XKQkl}W~8SpS{h3%cj)k+|FV*tjE~1BsL`ahhJY=%3gvGwj_eg#9-6dOAwk}e7c4cca-1C%KGj?!m^dsEvr=rN2rq%I; zvU9kpQ+*60RZas79^XEhehu`OcFa+aU-lzgFWw*?;LO|LzPRx&VtzS(!EK=&@wRnq z^G{Cz2G$|T_0EfycYYKmS?;D#FVGgWAd5~ZcJ+v;O?Y1mr(e00Wq`nb2ioC_GSHeo zzXbJ3M}!dt;gm8ACn}yPcn`5Fy>=4KlD5Lv^*W1Zzm8q?px;uH3%c3RN1!1I1Ch!X zCbWT5eCPxn=iA|Qy>gU>^?EnWSd1$T&Le{Nf$huY-pR5gftdt3(zT(Yyh;*+r3Fd* zDc?W!#?LDapIbx$S$`I>N@au#@4=?*Yi%0skx|hz%X?*?Ty{Aykj-pG7dJ6@HB&t~ zX;7Z=Cg)=X_3a;_qQc{bhMH+zSBKH5zPF?8Q_5h#fe!i3V6sRiO18qy+J=i#$Jw`w=R@Q74C63_Ky zgkdU>H~)yy?|#2Q_{=;+p#>)pVNUC}^zIej`M34^kl4I{Kc7w+h77Y}AKIt`?`Q`Gi@#<6WHhD`8va-Q=`NICnqQ%m| z0o`W*=eqKH(qNQnCFyQawroSX5((D+GzdZ8IVFZ)3Qaj@-xIDb+&5VWnU}*WuPd{Z zSE-|)W5S>_e@jt z_ZEe-n0StY4SD|NA!437WytgoA!fnzf2t{?`Odt9#Si@=jUq-hAnrdQcK9$8s=q;h z;x(ImC+E+AM}>Lexf6Ekrv~Bj9fB@eBp#FJwPM)yO5+3 zEq(GNp{SVhytpM>>?A?Gh?QMaTP9RiD36u>q@biLhikOWKpLakkPdDr2p8p|^%aCG z(ZNOOlz-+tfIRkowo-`)0!BaNBS(lR87lY)`3$b`OuTL(5bA8mTRg}&s{i*jq=O6s z9C^QI7z91;_85@;Obkp%2&M_-e|L#KTWKbN0E_D|_cn?H{0{`Og!|}O--B-Q9``#` z2ylD})*$A+;Ok+0@LAomZ`B45LFj>0k#^nCz<(9l6B4Kkff3QS!8_v>T%OedA{ r=Mmty{VcRLPaGG)SqKD@x_XDIQ~mMcJ2@v4xP-_^DoDV^3_t%Lf9DB% literal 0 HcmV?d00001 diff --git a/src/views/css/styles.css b/src/views/css/styles.css index 6643b54..f58ebd1 100644 --- a/src/views/css/styles.css +++ b/src/views/css/styles.css @@ -12,7 +12,10 @@ width: 0px; } - +img { + width: 65px; + height: 65px; +} /* ROOT */ From 10e572ec063274cf53adef858714f46ce007fe4f Mon Sep 17 00:00:00 2001 From: Adam Scott Date: Sat, 22 May 2021 15:35:43 +1000 Subject: [PATCH 05/13] #11 Basics of theme-ing started Is it theming or themeing? --- src/controllers/themeReader.ts | 86 ++++++++++++++++++++++++++++++++++ src/index.html | 32 ++++++++++--- src/views/css/styles.css | 11 +++-- src/views/ts/renderer.ts | 56 +++++++++++++++++++++- 4 files changed, 172 insertions(+), 13 deletions(-) create mode 100644 src/controllers/themeReader.ts diff --git a/src/controllers/themeReader.ts b/src/controllers/themeReader.ts new file mode 100644 index 0000000..6093e47 --- /dev/null +++ b/src/controllers/themeReader.ts @@ -0,0 +1,86 @@ +/* + WORK IN PROGRESS FILE + INTENDING TO EXPORT VARIABLES INTO YARNSPINNER MONARCH + AND THE CSS FILE + +*/ + +//File to handle the list of variables + +//Pink theme +/* +module.exports = { + primary_text: "#000000", + secondary_text: "#FFFFFF", + editor: "#f7c2c2", + + commands: "#FF00AA", + fileTag: "#718C70", + interpolation: "#CC8400", + option: "#AD00C4", + variables: "#347F36", + float: "#063B0E", + number: "#063B0E", + yarnCommands: "#A30A70", + hashtag: "#AAAAAA", + operator: "#AAAFFF", + + workingFile: "#fce5e5", + tabGap: "#ff7474", + divideColour: "#c64242", + lineSelection: "#eba0a0" + +} +/**/ + +/* +//OG Blue theme +module.exports = { + primary_text: "#000000", + secondary_text: "#FFFFFF", + editor: "#CFD8DC", + + commands: "#FF00AA", + fileTag: "#718C70", + interpolation: "#CC8400", + option: "#AD00C4", + variables: "#347F36", + float: "#063B0E", + number: "#063B0E", + yarnCommands: "#A30A70", + hashtag: "#AAAAAA", + operator: "#AAAFFF", + + workingFile: "#d5dee2", + tabGap: "#546E7A", + divideColour: "#9eb5c0", + lineSelection: "#d5dee2" + +} +/**/ + +//Night theme +module.exports = { + primary_text: "#FFFFFF", + secondary_text: "#000000", + editor: "#797979", + + commands: "#FF00AA", + fileTag: "#718C70", + interpolation: "#CC8400", + option: "#AD00C4", + variables: "#347F36", + float: "#063B0E", + number: "#063B0E", + yarnCommands: "#A30A70", + hashtag: "#AAAAAA", + operator: "#AAAFFF", + + workingFile: "#d7d7d7", + tabGap: "#5c5c5c", + divideColour: "#2c2c2c", + lineSelection: "#a5a5a5" + +} +/**/ + diff --git a/src/index.html b/src/index.html index 360a1ec..38688b8 100644 --- a/src/index.html +++ b/src/index.html @@ -77,23 +77,41 @@

Text Options

- + + + + + Bold text
- + + + + + + Italicise text
- + + + + + Underline text
- - + + + + + + + + + Colour text
diff --git a/src/views/css/styles.css b/src/views/css/styles.css index f58ebd1..0e4674c 100644 --- a/src/views/css/styles.css +++ b/src/views/css/styles.css @@ -26,15 +26,17 @@ img { https://davidwalsh.name/css-variables-javascript?__cf_chl_jschl_tk__=1173ddcdfb18d816eca6b7bb352bd7e26ea09bb6-1619762597-0-AV0wN_9mCGCFJbZCQw2qZQRw_E6hWY3uv-XKcHdIcuTriEY5dh7sI65ygYpW4rqmGbK-Eiq5IfQ-1z9FLf7zTXFZ7ftctl9rA9YHVUiBE8OMUJXBhO3gKey25UmyZKse30DzFYpMgVAK0V3jlq49VYyHsPlNpbHPD-yQqjZSnYkh49z3r0XbPLHi7E07N5kmOanvDYv6LlSi4vDyllmOxzRPAwN9Xj3PUA9IVWeiLDU0547GFYiSA7dDRh-VI-qUaEisTCgN8XnmQQ8T0mmdDfXVZM68wlq2-W3DpCCxF4TjOHPmeZP5D03XvsZ2X3VeHMDC4RGORytHxs07YHYJc0vaPLN8iUyBL0OOAD6ZCBpA2OzJarmnxjRJu1DhYzYqePM18rg6lOJAcjTsc7WoK_g */ /* Human readable colour theme - light theme*/ - --editor: #CFD8DC; + + /*No longer needed but here for reference*/ + /* --editor: #CFD8DC; --topSideEdit: #CFD8DC; --workingFile: #d5dee2; --tabGap: #546E7A; --dividerColour: #9eb5c0; /* ~~CONSTANT COLOURS~~ */ - --primary_text: #000000; - --secondary_text: #FFFFFF; + /* --primary_text: #000000; + --secondary_text: #FFFFFF; */ } @@ -357,7 +359,7 @@ select{ margin-left: 5px; margin-right: 5px; border-style: hidden; - + color: var(--primary_text); background-color: var(--editor); } @@ -365,6 +367,7 @@ select{ margin-left: 5px; font-size: 12px; margin-top: 1px; + color: var(--primary_text); } .editorView { diff --git a/src/views/ts/renderer.ts b/src/views/ts/renderer.ts index c6ae2cf..8dad259 100644 --- a/src/views/ts/renderer.ts +++ b/src/views/ts/renderer.ts @@ -43,6 +43,7 @@ LISTENERS import * as monaco from "monaco-editor"; import * as yarnSpinner from "../../YarnSpinner/yarnSpinnerMonarch"; import { ipcRenderer } from "electron"; +import exports from "../../controllers/themeReader.ts"; const openFiles: FileClass[] = []; let editor: monaco.editor.IStandaloneCodeEditor; @@ -56,14 +57,65 @@ monaco.languages.setLanguageConfiguration("yarnSpinner", yarnSpinner.config); //set the completions NOT WORKING CURRENTLY monaco.languages.registerCompletionItemProvider("yarnSpinner", yarnSpinner.completions); -monaco.editor.defineTheme("yarnSpinnerTheme", yarnSpinner.theme); +//monaco.editor.defineTheme("yarnSpinnerTheme", yarnSpinner.theme); + +//Utilising exports we can get the variable information from themeReader + +monaco.editor.defineTheme('customTheme', { + base: 'vs', + inherit: true, + rules: [ + //{ background: 'CFD8DC'}, + + { token: 'body.bold', fontStyle: 'bold' }, + { token: 'body.underline', fontStyle: 'underline' }, + { token: 'body.italic', fontStyle: 'italic' }, + { token: 'body.commands', foreground : exports.commands }, + { token: 'commands', foreground : exports.commands }, + { token: 'file.tag', foreground : exports.fileTag }, + { token: 'interpolation', foreground : exports.interpolation }, + { token: 'options', foreground : exports.option }, + { token: 'variables', foreground : exports.variables }, + { token: 'float', foreground : exports.float }, + { token: 'number', foreground : exports.number }, + { token: 'yarn.commands', foreground : exports.yarnCommands }, + { token: 'commands.float', foreground : exports.commands }, + { token: 'commands.number', foreground : exports.commands }, + { token: 'commands.operator', foreground: exports.operator }, + { token: 'hashtag', foreground: exports.hashtag }, + { token: 'dialogue', foreground : exports.primary_text } + ], + + colors: { + 'editor.foreground': exports.primary_text, + 'editor.background': exports.editor, + 'editorCursor.foreground': exports.workingFile, + 'editor.lineHighlightBackground': exports.lineSelection, + 'editorLineNumber.foreground': exports.primary_text, + 'editor.selectionBackground': exports.lineSelection, + 'editor.inactiveSelectionBackground': exports.editor, + 'minimap.background': exports.lineSelection + } +}); + +//set css variables +document.documentElement.style.setProperty(`--editor`, exports.editor); +document.documentElement.style.setProperty(`--topSideEdit`, exports.editor); +document.documentElement.style.setProperty(`--workingFile`, exports.workingFile); +document.documentElement.style.setProperty(`--tabGap`, exports.tabGap); +document.documentElement.style.setProperty(`--dividerColour`, exports.divideColour); +document.documentElement.style.setProperty(`--primary_text`, exports.primary_text); +document.documentElement.style.setProperty(`--secondary_text`, exports.secondary_text); + const containerElement = document.getElementById("container"); + if (containerElement) { editor = monaco.editor.create(containerElement, { - theme: "yarnSpinnerTheme", + //theme: "yarnSpinnerTheme", + theme: "customTheme", value: "".toString(), language: "yarnSpinner", automaticLayout: true, From d0ff956d6a23a7e5286f25a0cbe952929095dbe9 Mon Sep 17 00:00:00 2001 From: Adam Scott Date: Sun, 23 May 2021 10:51:13 +1000 Subject: [PATCH 06/13] #11 Icon functionality + beginnings of WF removal --- src/index.html | 17 ++++---- src/views/ts/renderer.ts | 86 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 9 deletions(-) diff --git a/src/index.html b/src/index.html index 38688b8..06708c5 100644 --- a/src/index.html +++ b/src/index.html @@ -55,7 +55,7 @@ Open File
-
+
@@ -76,15 +76,15 @@

Text Options

-
+
- Bold text + Bold text (ctrl + b)
-
+
@@ -93,7 +93,7 @@ Italicise text
-
+
@@ -102,6 +102,8 @@ Underline text
+ +
@@ -110,7 +112,6 @@ - Colour text
@@ -132,9 +133,7 @@
Working files -

Samplefile.yarn

-

Samplefile.yarn

-

Samplefile.yarn

+
diff --git a/src/views/ts/renderer.ts b/src/views/ts/renderer.ts index 8dad259..a089eaa 100644 --- a/src/views/ts/renderer.ts +++ b/src/views/ts/renderer.ts @@ -126,6 +126,92 @@ if (containerElement) }); } + + +const workingFiles = document.getElementById("workingFilesDetail"); + +if (workingFiles){ + + var childrenOfWF = workingFiles.children; + + +} + + + +/* + Generic function for inserting at the front and the end of a selection +*/ +function wrapTextWithTag(textFront: String, textBack: String){ + + var selection = editor.getSelection() as monaco.IRange; + var selectFront = new monaco.Selection(selection.startLineNumber, selection.startColumn, selection.startLineNumber, selection.startColumn); + var selectBack = new monaco.Selection(selection.endLineNumber, selection.endColumn, selection.endLineNumber, selection.endColumn); + + + var frontString = textFront.concat("");//Needs to concat an empty character in order to set the cursor properly + + //In this order, so when nothing is selected, textFront is prepended after textBack is inserted + editor.focus();//Set focus back to editor + + editor.executeEdits("", [{range: selectBack, text: textBack as string}]);//Set textBack + editor.setPosition(new monaco.Position(selectFront.startLineNumber, selectFront.startColumn));//reset cursor to behind textFront input + editor.executeEdits("", [{range: selectFront, text: frontString as string}]);//Set textFront + editor.setSelection(new monaco.Selection(selection.startLineNumber, selection.startColumn + frontString.length, selection.startLineNumber, selection.startColumn + frontString.length)); + //Reset collection to an empty range +} + + +//Set selection to BOLD +const boldText = document.getElementById("boldTextIcon"); +if (boldText){ + boldText.onclick = () => { wrapTextWithTag("[b]", "[\\b]"); }; +} + +//Set selection to Italics +const italicText = document.getElementById("italicTextIcon"); +if (italicText){ + italicText.onclick = () => { wrapTextWithTag("[i]", "[\\i]"); }; +} + +//Set selection to Underline +const underlineText = document.getElementById("underlineTextIcon"); +if (underlineText){ + underlineText.onclick = () => { wrapTextWithTag("[u]", "[\\u]"); }; +} + +//TODO Set selection to selected colour +const colourPick = document.getElementById("colourPicker"); +if (colourPick){ + colourPick.onchange = () => { + var value = (colourPick as HTMLInputElement).value; + + var startText = "[col=\'".concat(value.substr(1)).concat("\']"); + var endText = "[\\col]"; + + wrapTextWithTag(startText, endText); + editor.focus(); + } +} + +//Listen for editor commands +window.addEventListener("keydown", (e) =>{ + if (e.ctrlKey && e.key === "b"){ + boldText?.click();//send bold click event + } + + //TODO remove the monaco commands that use these command combinations + // if (e.ctrlKey && e.key === "i"){ + // italicText?.click(); + // } + + // if (e.ctrlKey && e.key === "u"){ + // underlineText?.click(); + // } +}); + + + const saveFileIcon = document.getElementById("saveFileIcon"); if (saveFileIcon) From 6907ff8efe9cb5d4d4270141dc323fc1cbb3dd90 Mon Sep 17 00:00:00 2001 From: Adam Scott Date: Sun, 23 May 2021 12:48:29 +1000 Subject: [PATCH 07/13] #11 bold, italics, underline commands implemented --- src/main.ts | 2 +- src/views/ts/renderer.ts | 33 +++++++++++++++------------------ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/main.ts b/src/main.ts index 1668e05..0f3c739 100644 --- a/src/main.ts +++ b/src/main.ts @@ -23,7 +23,7 @@ function createWindow() height: 540, width: 960, minHeight: 480, - minWidth: 480, + minWidth: 540, webPreferences: { nodeIntegration: true, contextIsolation: false, diff --git a/src/views/ts/renderer.ts b/src/views/ts/renderer.ts index a089eaa..5450e67 100644 --- a/src/views/ts/renderer.ts +++ b/src/views/ts/renderer.ts @@ -124,6 +124,21 @@ if (containerElement) mouseWheelZoom: true, wordWrap: "on" }); + + + //Override monaco's default commands to add our own + editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_I, () => { + italicText?.click(); + } ); + + editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_U, () => { + underlineText?.click(); + } ); + + editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_B, () => { + boldText?.click(); + } ); + } @@ -194,24 +209,6 @@ if (colourPick){ } } -//Listen for editor commands -window.addEventListener("keydown", (e) =>{ - if (e.ctrlKey && e.key === "b"){ - boldText?.click();//send bold click event - } - - //TODO remove the monaco commands that use these command combinations - // if (e.ctrlKey && e.key === "i"){ - // italicText?.click(); - // } - - // if (e.ctrlKey && e.key === "u"){ - // underlineText?.click(); - // } -}); - - - const saveFileIcon = document.getElementById("saveFileIcon"); if (saveFileIcon) From 91ba2468637e341991367eaf54099972d827e800 Mon Sep 17 00:00:00 2001 From: Seth Hilder Date: Sun, 23 May 2021 12:55:30 +1000 Subject: [PATCH 08/13] #21 - Start on swapping files --- src/views/ts/renderer.ts | 156 +++++++++++++++++++++++++++++++++------ 1 file changed, 134 insertions(+), 22 deletions(-) diff --git a/src/views/ts/renderer.ts b/src/views/ts/renderer.ts index a089eaa..21d175e 100644 --- a/src/views/ts/renderer.ts +++ b/src/views/ts/renderer.ts @@ -45,7 +45,7 @@ import * as yarnSpinner from "../../YarnSpinner/yarnSpinnerMonarch"; import { ipcRenderer } from "electron"; import exports from "../../controllers/themeReader.ts"; -const openFiles: FileClass[] = []; +const openFiles = new Map(); let editor: monaco.editor.IStandaloneCodeEditor; //Register our new custom language @@ -126,17 +126,110 @@ if (containerElement) }); } - - const workingFiles = document.getElementById("workingFilesDetail"); if (workingFiles){ - - var childrenOfWF = workingFiles.children; + console.log("workingFilesExists"); + workingFiles.addEventListener('click', (event) => { + if(event && event.target && (event.target as HTMLButtonElement).tagName === "BUTTON"){ + + //Get file ID information and HTML elements + var button = (event.target as HTMLButtonElement); + var parentDiv = button.parentElement; + var fileIdentifier = Number(parentDiv?.id); + + if(Number.isNaN(fileIdentifier) || !parentDiv) { + console.error("Attempted to remove broken file instance, please file a bug at https://github.com/setho246/YarnSpinnerEditor/issues"); + return; + } + + //alert("Before delete".concat(openFiles.toString())); //Debug information + + //Remove file from array + openFiles.delete(fileIdentifier); + + + //Remove the HTML elements from working files + parentDiv.parentElement?.removeChild(parentDiv); + + //alert("post delete".concat(openFiles.toString())); //Debug information + } + + else if (event && event.target && (event.target as HTMLElement).tagName !== "DETAILS" && (event.target as HTMLElement).tagName !== "SUMMARY"){ + var fileIdentifier: number; + + if ((event.target as HTMLElement).tagName === "P") { + fileIdentifier = Number((event.target as HTMLParagraphElement).parentElement?.id) + } + else{ + var divElement = (event.target as HTMLDivElement); + fileIdentifier = Number(divElement.id); + } + + // alert(fileIdentifier); + + //Bug checking + var currentValue = editor.getValue() + + var openedFile = openFiles.get(fileIdentifier); + + if(openedFile){ + editor.setValue(openedFile.contents); // Swap to push edit operations? https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.itextmodel.html#pusheditoperations + editor.updateOptions({readOnly: false}); + } + + + // if (currentValue == editor.getValue()){ + // alert("Didn't work or was same file or is empty file anyway"); + // //console.log("File not found in working files"); + // } + } + + + }); } +function updateFileObjectContent(file: YarnFileClass) { + // file = editor.getValue() + + /* + SETH -----> + Possibly follow the modelModel class structure of having the array of files be stored within a class + + e.g. yarnFolderModel + arrayOfFiles = something + currentFile = file that's in the editor + + ****Maybe further variable to get the editor for when we split views*** + + getters and setters for stuff + + method to find matching ID and return matched one? + + */ + + + + /* + Store current file (current editor value) into current open file (maybe also make into variable itself?) + Note: This is different to save, it's storing the value as rn we lose all data + + pushEditOperations of new content into the editor (rather than using direct setValue, microsoft says no ewwie yucky) + range would be entire document + text would be contents of new file + + Set current file, to the file that was changed + + + + + */ + + + +} /* @@ -195,19 +288,18 @@ if (colourPick){ } //Listen for editor commands -window.addEventListener("keydown", (e) =>{ - if (e.ctrlKey && e.key === "b"){ - boldText?.click();//send bold click event - } - +window.addEventListener("IModelContentChangedEvent", (e) =>{ + console.log("TEAEWQWRASD"); //TODO remove the monaco commands that use these command combinations // if (e.ctrlKey && e.key === "i"){ // italicText?.click(); // } - + // if (e.ctrlKey && e.key === "u"){ // underlineText?.click(); // } + + // if filestate is unsaved { then check } }); @@ -245,8 +337,8 @@ if (openFolderIcon) function createNewFile() { console.log("IN CREATE NEW FILE"); - const newFile:FileClass = new FileClass(null, null, null, false); - openFiles.push(newFile); + const newFile:YarnFileClass = new YarnFileClass(null, null, null, false, Date.now()); + openFiles.set(newFile.getUniqueIdentifier(), newFile); addFileToDisplay(newFile); editor.setValue(newFile.contents); } @@ -254,20 +346,31 @@ function createNewFile() /** * Creates and appends the HTML required for showing a new file. * - * @param {FIleClass} file The file to add to the display. + * @param {YarnFIleClass} file The file to add to the display. * * @returns {void} */ -function addFileToDisplay(file: FileClass) : void +function addFileToDisplay(file: YarnFileClass) : void { - console.log("ADDING FILE TO DISPLAY"); + const div = document.createElement("div"); + div.setAttribute("id", file.uniqueIdentifier.toString()); + + const closeButton = document.createElement("button"); + closeButton.setAttribute("id", file.uniqueIdentifier.toString()); + closeButton.textContent = "x"; + const para = document.createElement("p"); para.textContent = file.getName(); - const fileListElement = document.getElementById("workingFilesDetail"); + + div.appendChild(para); + div.appendChild(closeButton); + + + const fileListElement = document.getElementById("workingFilesDetail"); if(fileListElement) { - fileListElement.appendChild(para); + fileListElement.appendChild(div); console.log("JUST APPENED"); } else @@ -296,8 +399,10 @@ ipcRenderer.on("openFile", (event, path, contents, name) => name = "New File"; } - const openedFile = new FileClass(path, contents, name, true); - openFiles.push(openedFile); + + + const openedFile = new YarnFileClass(path, contents, name, true, Date.now()); + openFiles.set(openedFile.getUniqueIdentifier(), openedFile); addFileToDisplay(openedFile); editor.setValue(openedFile.getContents()); }); @@ -394,23 +499,26 @@ export interface YarnFile fileName: string; contents: string; isSaved: boolean; + uniqueIdentifier: number; getName(): string; getSaved(): boolean; } -export class FileClass implements YarnFile +export class YarnFileClass implements YarnFile { filePath: string | null; fileName: string; contents: string; isSaved: boolean; + uniqueIdentifier: number; - constructor(filePath: string | null, contents:string|null, name: string|null, isSaved: boolean|null) + constructor(filePath: string | null, contents:string|null, name: string|null, isSaved: boolean|null, uniqueIdentifier: number) { this.filePath = filePath ? filePath : null; this.fileName = name ? name : "New File"; this.contents = contents ? contents : ""; this.isSaved = isSaved ? true : false; + this.uniqueIdentifier = uniqueIdentifier; } getName():string @@ -435,4 +543,8 @@ export class FileClass implements YarnFile return false; } } + + getUniqueIdentifier():number { + return this.uniqueIdentifier; + } } \ No newline at end of file From b9b60119fefa971d0f715e440b366d3789e9979d Mon Sep 17 00:00:00 2001 From: Adam Scott Date: Sun, 23 May 2021 13:23:53 +1000 Subject: [PATCH 09/13] #21 #11 - ondidChange + tiny gui changes --- src/main.ts | 2 +- src/views/css/styles.css | 2 ++ src/views/ts/renderer.ts | 43 ++++++++++++++++++++++++++-------------- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/main.ts b/src/main.ts index 0f3c739..443b64c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,7 +20,7 @@ function createWindow() { // Create the browser window. const mainWindow = new BrowserWindow({ - height: 540, + height: 545, width: 960, minHeight: 480, minWidth: 540, diff --git a/src/views/css/styles.css b/src/views/css/styles.css index 0e4674c..71b25fb 100644 --- a/src/views/css/styles.css +++ b/src/views/css/styles.css @@ -223,6 +223,8 @@ select{ grid-column-start: 3; grid-column-end: 3; border-style: hidden; + margin-left: 10px; + margin-right: 10px; } #iconFour{ display: flex; diff --git a/src/views/ts/renderer.ts b/src/views/ts/renderer.ts index 948ff44..b2720b6 100644 --- a/src/views/ts/renderer.ts +++ b/src/views/ts/renderer.ts @@ -46,6 +46,8 @@ import { ipcRenderer } from "electron"; import exports from "../../controllers/themeReader.ts"; const openFiles = new Map(); +var currentOpenYarnFile: YarnFileClass; + let editor: monaco.editor.IStandaloneCodeEditor; //Register our new custom language @@ -139,12 +141,31 @@ if (containerElement) boldText?.click(); } ); + + //Editor specific events + editor.onDidChangeModelContent(e => { + + //Check the currentOpenYarnFile against the editor's value + if (currentOpenYarnFile.contents == editor.getValue()){ + console.log("they are the same"); + currentOpenYarnFile.isSaved = true; + } + + else{ + console.log("they are not the same"); + currentOpenYarnFile.isSaved = false; + } + + }); } +//Working file details specific events const workingFiles = document.getElementById("workingFilesDetail"); if (workingFiles){ - console.log("workingFilesExists"); + + //console.log("workingFilesExists"); + workingFiles.addEventListener('click', (event) => { if(event && event.target && (event.target as HTMLButtonElement).tagName === "BUTTON"){ @@ -303,20 +324,9 @@ if (colourPick){ } //Listen for editor commands -window.addEventListener("keydown", (e) =>{ - if (e.ctrlKey && e.key === "b"){ - boldText?.click();//send bold click event - } - - //TODO remove the monaco commands that use these command combinations - // if (e.ctrlKey && e.key === "i"){ - // italicText?.click(); - // } - - // if (e.ctrlKey && e.key === "u"){ - // underlineText?.click(); - // } -}); +// window.addEventListener("keydown", (e) =>{ + //No more need for this at this stage +// }); @@ -355,6 +365,9 @@ function createNewFile() console.log("IN CREATE NEW FILE"); const newFile:YarnFileClass = new YarnFileClass(null, null, null, false, Date.now()); openFiles.set(newFile.getUniqueIdentifier(), newFile); + + currentOpenYarnFile = newFile; + addFileToDisplay(newFile); editor.setValue(newFile.contents); } From 322b5fd3f1f24e0670d58500c717fa7134c48355 Mon Sep 17 00:00:00 2001 From: Seth Hilder Date: Sun, 23 May 2021 17:36:17 +1000 Subject: [PATCH 10/13] #21 - More work on file swapping --- src/views/ts/renderer.ts | 706 +++++++++++++++++++-------------------- 1 file changed, 347 insertions(+), 359 deletions(-) diff --git a/src/views/ts/renderer.ts b/src/views/ts/renderer.ts index b2720b6..35b787b 100644 --- a/src/views/ts/renderer.ts +++ b/src/views/ts/renderer.ts @@ -10,29 +10,123 @@ IPC Renderer console.log output will be in the developer tools window in the actual Electron client */ - +//TODO Temporary classes location +//!---------------------------------------------------------------------------------------------------------------------------------- /* -TODO + -------------------------------------------------- + THIS NEEDS TO BE MOVED TO ANOTHER FILE + STAYING HERE FOR NOW BC WEBPACK ISSUES + -------------------------------------------------- +*/ -SENDERS -- File read -- File write +export class YarnFile { + private filePath: string | null; + private fileName: string; + private contents: string; + private contentsOnDisk: string; + private uniqueIdentifier: number; + + constructor(filePath: string | null, contents: string | null, name: string | null, uniqueIdentifier: number) { + this.filePath = filePath ? filePath : null; + this.fileName = name ? name : "New File"; + this.contents = contents ? contents : ""; + this.contentsOnDisk = contents ? contents : ""; + this.uniqueIdentifier = uniqueIdentifier; + } + //Getters + getPath(): string | null { + return this.filePath; + } -LISTENERS -- File read -- File write (save / save as) -- Menu events + getName(): string { + return this.fileName; + } + getContents(): string { + return this.contents; + } + getSaved(): boolean { + return this.getContents() === this.contentsOnDisk; + } + getUniqueIdentifier(): number { + return this.uniqueIdentifier; + } + //Setters + setFilePath(filePath: string) { + this.filePath = filePath; + } + setName(name: string) { + this.fileName = name; + } + + setContents(contents: string) { + this.contents = contents; + } + + //Functions + fileSaved(){ + this.contentsOnDisk = this.contents; + } + +} + +export class YarnFileManager { + private openFiles = new Map(); + private currentOpenYarnFile: YarnFile; + + constructor() { + this.currentOpenYarnFile = this.createEmptyFile(); + } + + //Getters + getFiles(): Map { + return this.openFiles; + } + + getCurrentOpenFile(): YarnFile { + return this.currentOpenYarnFile; + } + + getYarnFile(yarnIDNumber: number) { + return this.openFiles.get(yarnIDNumber); + } + + //Setters + setCurrentOpenYarnFile(yarnIDNumber: number) { + var newCurrent = this.openFiles.get(yarnIDNumber); + if (newCurrent) { + this.currentOpenYarnFile = newCurrent; + } + } + + //Functions + addToFiles(newFile: YarnFile): void { + this.openFiles.set(newFile.getUniqueIdentifier(), newFile); + } + + removeFromFiles(yarnIDNumber: number) { + this.openFiles.delete(yarnIDNumber); + } + + createEmptyFile(): YarnFile { + const newFile: YarnFile = new YarnFile(null, null, null, false, Date.now()); + this.addToFiles(newFile); + this.setCurrentOpenYarnFile(newFile.getUniqueIdentifier()); + + return newFile; + } +} -*/ +//!---------------------------------------------------------------------------------------------------------------------------------- + // This file is required by the index.html file and will // be executed in the renderer process for that window. @@ -45,8 +139,7 @@ import * as yarnSpinner from "../../YarnSpinner/yarnSpinnerMonarch"; import { ipcRenderer } from "electron"; import exports from "../../controllers/themeReader.ts"; -const openFiles = new Map(); -var currentOpenYarnFile: YarnFileClass; +let yarnFileManager = new YarnFileManager(); let editor: monaco.editor.IStandaloneCodeEditor; @@ -65,39 +158,39 @@ monaco.languages.registerCompletionItemProvider("yarnSpinner", yarnSpinner.compl monaco.editor.defineTheme('customTheme', { base: 'vs', - inherit: true, - rules: [ - //{ background: 'CFD8DC'}, - - { token: 'body.bold', fontStyle: 'bold' }, - { token: 'body.underline', fontStyle: 'underline' }, - { token: 'body.italic', fontStyle: 'italic' }, - { token: 'body.commands', foreground : exports.commands }, - { token: 'commands', foreground : exports.commands }, - { token: 'file.tag', foreground : exports.fileTag }, - { token: 'interpolation', foreground : exports.interpolation }, - { token: 'options', foreground : exports.option }, - { token: 'variables', foreground : exports.variables }, - { token: 'float', foreground : exports.float }, - { token: 'number', foreground : exports.number }, - { token: 'yarn.commands', foreground : exports.yarnCommands }, - { token: 'commands.float', foreground : exports.commands }, - { token: 'commands.number', foreground : exports.commands }, - { token: 'commands.operator', foreground: exports.operator }, - { token: 'hashtag', foreground: exports.hashtag }, - { token: 'dialogue', foreground : exports.primary_text } - ], - - colors: { - 'editor.foreground': exports.primary_text, - 'editor.background': exports.editor, - 'editorCursor.foreground': exports.workingFile, - 'editor.lineHighlightBackground': exports.lineSelection, - 'editorLineNumber.foreground': exports.primary_text, - 'editor.selectionBackground': exports.lineSelection, - 'editor.inactiveSelectionBackground': exports.editor, - 'minimap.background': exports.lineSelection - } + inherit: true, + rules: [ + //{ background: 'CFD8DC'}, + + { token: 'body.bold', fontStyle: 'bold' }, + { token: 'body.underline', fontStyle: 'underline' }, + { token: 'body.italic', fontStyle: 'italic' }, + { token: 'body.commands', foreground: exports.commands }, + { token: 'commands', foreground: exports.commands }, + { token: 'file.tag', foreground: exports.fileTag }, + { token: 'interpolation', foreground: exports.interpolation }, + { token: 'options', foreground: exports.option }, + { token: 'variables', foreground: exports.variables }, + { token: 'float', foreground: exports.float }, + { token: 'number', foreground: exports.number }, + { token: 'yarn.commands', foreground: exports.yarnCommands }, + { token: 'commands.float', foreground: exports.commands }, + { token: 'commands.number', foreground: exports.commands }, + { token: 'commands.operator', foreground: exports.operator }, + { token: 'hashtag', foreground: exports.hashtag }, + { token: 'dialogue', foreground: exports.primary_text } + ], + + colors: { + 'editor.foreground': exports.primary_text, + 'editor.background': exports.editor, + 'editorCursor.foreground': exports.workingFile, + 'editor.lineHighlightBackground': exports.lineSelection, + 'editorLineNumber.foreground': exports.primary_text, + 'editor.selectionBackground': exports.lineSelection, + 'editor.inactiveSelectionBackground': exports.editor, + 'minimap.background': exports.lineSelection + } }); //set css variables @@ -109,250 +202,245 @@ document.documentElement.style.setProperty(`--dividerColour`, exports.divideColo document.documentElement.style.setProperty(`--primary_text`, exports.primary_text); document.documentElement.style.setProperty(`--secondary_text`, exports.secondary_text); - const containerElement = document.getElementById("container"); -if (containerElement) -{ - editor = monaco.editor.create(containerElement, { - //theme: "yarnSpinnerTheme", - theme: "customTheme", - value: "".toString(), - language: "yarnSpinner", - automaticLayout: true, - fontFamily: "Courier New", - fontSize: 14, - mouseWheelZoom: true, - wordWrap: "on" - }); +if (containerElement) { + editor = monaco.editor.create(containerElement, { + //theme: "yarnSpinnerTheme", + theme: "customTheme", + value: "".toString(), + language: "yarnSpinner", + automaticLayout: true, + fontFamily: "Courier New", + fontSize: 14, + mouseWheelZoom: true, + wordWrap: "on" + }); + + //Instantiate with new empty file + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); + //Override monaco's default commands to add our own + editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_I, () => { + italicText?.click(); + }); - //Override monaco's default commands to add our own - editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_I, () => { - italicText?.click(); - } ); + editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_U, () => { + underlineText?.click(); + }); - editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_U, () => { - underlineText?.click(); - } ); + editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_B, () => { + boldText?.click(); + }); - editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_B, () => { - boldText?.click(); - } ); + //Editor specific events + editor.onDidChangeModelContent(e => { - //Editor specific events - editor.onDidChangeModelContent(e => { + var workingDetailDiv = document.getElementById(yarnFileManager.getCurrentOpenFile().getUniqueIdentifier().toString()); //TODO change to ID - //Check the currentOpenYarnFile against the editor's value - if (currentOpenYarnFile.contents == editor.getValue()){ - console.log("they are the same"); - currentOpenYarnFile.isSaved = true; - } + var unsavedIdentifier = "*"; - else{ - console.log("they are not the same"); - currentOpenYarnFile.isSaved = false; - } + if (workingDetailDiv) { + var paraElementContent = workingDetailDiv.children[0].innerHTML + + //Check the currentOpenYarnFile against the editor's value + + //Then appends the unsaved identifier if the file is not saved, and set's it to false + if (yarnFileManager.getCurrentOpenFile().getContents() === editor.getValue()) { + yarnFileManager.getCurrentOpenFile().setSaved(true); + + if (paraElementContent.substr(paraElementContent.length - unsavedIdentifier.length) === unsavedIdentifier) { + workingDetailDiv.children[0].innerHTML = paraElementContent.slice(0, - unsavedIdentifier.length); + } + } + + else { + if (yarnFileManager.getCurrentOpenFile().getSaved()) { + yarnFileManager.getCurrentOpenFile().setSaved(false); + workingDetailDiv.children[0].innerHTML = paraElementContent.concat(unsavedIdentifier); + } + } + } + }); - }); } //Working file details specific events const workingFiles = document.getElementById("workingFilesDetail"); -if (workingFiles){ +if (workingFiles) { - //console.log("workingFilesExists"); + //Set the intiated new empty file into working space + addFileToDisplay(yarnFileManager.getCurrentOpenFile()); - workingFiles.addEventListener('click', (event) => { - if(event && event.target && (event.target as HTMLButtonElement).tagName === "BUTTON"){ + //Add all listeners + workingFiles.addEventListener('click', (event) => { + if (event && event.target && (event.target as HTMLElement).tagName === "BUTTON") { - //Get file ID information and HTML elements - var button = (event.target as HTMLButtonElement); - var parentDiv = button.parentElement; + //Get file ID information and HTML elements + var button = (event.target as HTMLButtonElement); + var parentDiv = button.parentElement; var fileIdentifier = Number(parentDiv?.id); - - if(Number.isNaN(fileIdentifier) || !parentDiv) { + + if (Number.isNaN(fileIdentifier) || !parentDiv) { console.error("Attempted to remove broken file instance, please file a bug at https://github.com/setho246/YarnSpinnerEditor/issues"); return; } - //alert("Before delete".concat(openFiles.toString())); //Debug information - - //Remove file from array - openFiles.delete(fileIdentifier); - - - //Remove the HTML elements from working files - parentDiv.parentElement?.removeChild(parentDiv); - - //alert("post delete".concat(openFiles.toString())); //Debug information - } + //Remove file from array + if (yarnFileManager.getFiles().size === 1) { + alert("Cannot delete the only file in work space."); + } + else { + //TODO update currentOpenFile if deleted file is/was currentOpenFile + //If that was the case, change the editor value + + + var openfile = yarnFileManager.getCurrentOpenFile().getUniqueIdentifier(); + + console.log("Open file is: " + openfile + typeof openfile); + console.log("closing file is: " + fileIdentifier + typeof fileIdentifier); + + if (fileIdentifier === yarnFileManager.getCurrentOpenFile().getUniqueIdentifier()) { + console.log("removing current file") + //Remove file + yarnFileManager.removeFromFiles(fileIdentifier); + var arrayOfFiles = Array.from(yarnFileManager.getFiles().keys()); + console.log(arrayOfFiles); + yarnFileManager.setCurrentOpenYarnFile(arrayOfFiles[0]); + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); + } + else { + console.log("removing another file that shouldn't be the editor's content"); + yarnFileManager.removeFromFiles(fileIdentifier); + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); + } + //Remove file + //yarnFileManager.removeFromFiles(fileIdentifier); + + //Remove the HTML elements from working files + parentDiv.parentElement?.removeChild(parentDiv); + } + } - else if (event && event.target && (event.target as HTMLElement).tagName !== "DETAILS" && (event.target as HTMLElement).tagName !== "SUMMARY"){ - var fileIdentifier: number; + else if (event && event.target && (event.target as HTMLElement).tagName !== "DETAILS" && (event.target as HTMLElement).tagName !== "SUMMARY") { + var fileIdentifier: number; - if ((event.target as HTMLElement).tagName === "P") { + if ((event.target as HTMLElement).tagName === "P") { fileIdentifier = Number((event.target as HTMLParagraphElement).parentElement?.id) } - else{ - var divElement = (event.target as HTMLDivElement); - fileIdentifier = Number(divElement.id); - } - - // alert(fileIdentifier); - - //Bug checking - var currentValue = editor.getValue() - - var openedFile = openFiles.get(fileIdentifier); - - if(openedFile){ - editor.setValue(openedFile.contents); // Swap to push edit operations? https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.itextmodel.html#pusheditoperations - editor.updateOptions({readOnly: false}); + else { + var divElement = (event.target as HTMLDivElement); + fileIdentifier = Number(divElement.id); } - - - // if (currentValue == editor.getValue()){ - // alert("Didn't work or was same file or is empty file anyway"); - // //console.log("File not found in working files"); - // } - } - - }); + var openedFile = yarnFileManager.getYarnFile(fileIdentifier);//Gets the new thing + if (openedFile) { + //update currentOpen content + syncCurrentFile(); + //Change currentOpen + yarnFileManager.setCurrentOpenYarnFile(fileIdentifier) + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); //TODO Swap to push edit operations? https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.itextmodel.html#pusheditoperations + editor.updateOptions({ readOnly: false }); + } + } + }); } -function updateFileObjectContent(file: YarnFileClass) { - // file = editor.getValue() - - /* - SETH -----> - Possibly follow the modelModel class structure of having the array of files be stored within a class - - e.g. yarnFolderModel - arrayOfFiles = something - currentFile = file that's in the editor - - ****Maybe further variable to get the editor for when we split views*** - - getters and setters for stuff - - method to find matching ID and return matched one? - - */ - - - - /* - Store current file (current editor value) into current open file (maybe also make into variable itself?) - Note: This is different to save, it's storing the value as rn we lose all data - - pushEditOperations of new content into the editor (rather than using direct setValue, microsoft says no ewwie yucky) - range would be entire document - text would be contents of new file - - Set current file, to the file that was changed - - - - - */ - - - +/** + * Update the file manager file to match the code editor. + * + * @returns void + */ +function syncCurrentFile() { + yarnFileManager.getCurrentOpenFile().setContents(editor.getValue()); } -/* - Generic function for inserting at the front and the end of a selection -*/ -function wrapTextWithTag(textFront: String, textBack: String){ +/** + * Generic function for inserting at the front and the end of a selection + */ +function wrapTextWithTag(textFront: String, textBack: String) { - var selection = editor.getSelection() as monaco.IRange; - var selectFront = new monaco.Selection(selection.startLineNumber, selection.startColumn, selection.startLineNumber, selection.startColumn); - var selectBack = new monaco.Selection(selection.endLineNumber, selection.endColumn, selection.endLineNumber, selection.endColumn); + var selection = editor.getSelection() as monaco.IRange; + var selectFront = new monaco.Selection(selection.startLineNumber, selection.startColumn, selection.startLineNumber, selection.startColumn); + var selectBack = new monaco.Selection(selection.endLineNumber, selection.endColumn, selection.endLineNumber, selection.endColumn); - var frontString = textFront.concat("");//Needs to concat an empty character in order to set the cursor properly + var frontString = textFront.concat("");//Needs to concat an empty character in order to set the cursor properly - //In this order, so when nothing is selected, textFront is prepended after textBack is inserted - editor.focus();//Set focus back to editor + //In this order, so when nothing is selected, textFront is prepended after textBack is inserted + editor.focus();//Set focus back to editor - editor.executeEdits("", [{range: selectBack, text: textBack as string}]);//Set textBack - editor.setPosition(new monaco.Position(selectFront.startLineNumber, selectFront.startColumn));//reset cursor to behind textFront input - editor.executeEdits("", [{range: selectFront, text: frontString as string}]);//Set textFront - editor.setSelection(new monaco.Selection(selection.startLineNumber, selection.startColumn + frontString.length, selection.startLineNumber, selection.startColumn + frontString.length)); - //Reset collection to an empty range + editor.executeEdits("", [{ range: selectBack, text: textBack as string }]);//Set textBack + editor.setPosition(new monaco.Position(selectFront.startLineNumber, selectFront.startColumn));//reset cursor to behind textFront input + editor.executeEdits("", [{ range: selectFront, text: frontString as string }]);//Set textFront + editor.setSelection(new monaco.Selection(selection.startLineNumber, selection.startColumn + frontString.length, selection.startLineNumber, selection.startColumn + frontString.length)); + //Reset collection to an empty range } //Set selection to BOLD const boldText = document.getElementById("boldTextIcon"); -if (boldText){ - boldText.onclick = () => { wrapTextWithTag("[b]", "[\\b]"); }; +if (boldText) { + boldText.onclick = () => { wrapTextWithTag("[b]", "[\\b]"); }; } //Set selection to Italics const italicText = document.getElementById("italicTextIcon"); -if (italicText){ - italicText.onclick = () => { wrapTextWithTag("[i]", "[\\i]"); }; +if (italicText) { + italicText.onclick = () => { wrapTextWithTag("[i]", "[\\i]"); }; } //Set selection to Underline const underlineText = document.getElementById("underlineTextIcon"); -if (underlineText){ - underlineText.onclick = () => { wrapTextWithTag("[u]", "[\\u]"); }; +if (underlineText) { + underlineText.onclick = () => { wrapTextWithTag("[u]", "[\\u]"); }; } //TODO Set selection to selected colour const colourPick = document.getElementById("colourPicker"); -if (colourPick){ - colourPick.onchange = () => { - var value = (colourPick as HTMLInputElement).value; +if (colourPick) { + colourPick.onchange = () => { + var value = (colourPick as HTMLInputElement).value; - var startText = "[col=\'".concat(value.substr(1)).concat("\']"); - var endText = "[\\col]"; + var startText = "[col=\'".concat(value.substr(1)).concat("\']"); + var endText = "[\\col]"; - wrapTextWithTag(startText, endText); - editor.focus(); - } + wrapTextWithTag(startText, endText); + editor.focus(); + } } //Listen for editor commands // window.addEventListener("keydown", (e) =>{ - //No more need for this at this stage +//No more need for this at this stage // }); const saveFileIcon = document.getElementById("saveFileIcon"); -if (saveFileIcon) -{ - saveFileIcon.onclick = () => {saveAsEmitter();}; +if (saveFileIcon) { + saveFileIcon.onclick = () => { saveAsEmitter(); }; } const newFileIcon = document.getElementById("newFileIcon"); -if (newFileIcon) -{ - newFileIcon.onclick = function () - { - createNewFile(); - }; +if (newFileIcon) { + newFileIcon.onclick = function () { + createNewFile(); + }; } const openFolderIcon = document.getElementById("openFolderIcon"); -if (openFolderIcon) -{ - openFolderIcon.onclick = function () - { - openFileEmitter(); - }; +if (openFolderIcon) { + openFolderIcon.onclick = function () { + openFileEmitter(); + }; } /** @@ -360,16 +448,10 @@ if (openFolderIcon) * * @returns {void} */ -function createNewFile() -{ - console.log("IN CREATE NEW FILE"); - const newFile:YarnFileClass = new YarnFileClass(null, null, null, false, Date.now()); - openFiles.set(newFile.getUniqueIdentifier(), newFile); - - currentOpenYarnFile = newFile; - - addFileToDisplay(newFile); - editor.setValue(newFile.contents); +function createNewFile() { + yarnFileManager.createEmptyFile() + addFileToDisplay(yarnFileManager.createEmptyFile()); + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); } /** @@ -379,33 +461,28 @@ function createNewFile() * * @returns {void} */ -function addFileToDisplay(file: YarnFileClass) : void -{ +function addFileToDisplay(file: YarnFile): void { const div = document.createElement("div"); - div.setAttribute("id", file.uniqueIdentifier.toString()); - + div.setAttribute("id", file.getUniqueIdentifier().toString()); + const closeButton = document.createElement("button"); - closeButton.setAttribute("id", file.uniqueIdentifier.toString()); closeButton.textContent = "x"; - - const para = document.createElement("p"); - para.textContent = file.getName(); - + + const para = document.createElement("p"); + para.textContent = file.getName(); + div.appendChild(para); div.appendChild(closeButton); - - + + const fileListElement = document.getElementById("workingFilesDetail"); - - if(fileListElement) - { - fileListElement.appendChild(div); - console.log("JUST APPENED"); - } - else - { - console.error("OpenFileError: Cannot append file to display list"); - } + + if (fileListElement) { + fileListElement.appendChild(div); + } + else { + console.error("OpenFileError: Cannot append file to display list"); + } } @@ -421,49 +498,39 @@ function addFileToDisplay(file: YarnFileClass) : void ------------------------------------ */ -ipcRenderer.on("openFile", (event, path, contents, name) => -{ - if(!name) - { - name = "New File"; - } - - - - const openedFile = new YarnFileClass(path, contents, name, true, Date.now()); - openFiles.set(openedFile.getUniqueIdentifier(), openedFile); - addFileToDisplay(openedFile); - editor.setValue(openedFile.getContents()); +ipcRenderer.on("openFile", (event, path, contents, name) => { + if (!name) { + name = "New File"; + } + + const openedFile = new YarnFile(path, contents, name, true, Date.now()); + yarnFileManager.addToFiles(openedFile); + addFileToDisplay(openedFile); + yarnFileManager.setCurrentOpenYarnFile(openedFile.getUniqueIdentifier()); + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); }); -ipcRenderer.on("fileSaveResponse", (event, arg) => -{ - if (arg) - { - alert("File successfully"); - } - else - { - alert("File save error occured"); - } +ipcRenderer.on("fileSaveResponse", (event, arg) => { + if (arg) { + alert("File successfully"); + } + else { + alert("File save error occured"); + } }); -ipcRenderer.on("mainRequestSaveAs", () => -{ - saveAsEmitter(); +ipcRenderer.on("mainRequestSaveAs", () => { + saveAsEmitter(); }); -ipcRenderer.on("mainRequestNewFile", () => -{ - console.log("REQUEST RECIEVED FROM MAIN"); - createNewFile(); +ipcRenderer.on("mainRequestNewFile", () => { + createNewFile(); }); -ipcRenderer.on("gotPing", (event, arg) => -{ - console.log("RESPONSE RECIEVED"); - console.log(arg);//Should be pong +ipcRenderer.on("gotPing", (event, arg) => { + console.log("RESPONSE RECIEVED"); + console.log(arg);//Should be pong }); @@ -486,9 +553,8 @@ ipcRenderer.on("gotPing", (event, arg) => * * @returns {void} */ -function saveAsEmitter() -{ - ipcRenderer.send("fileSaveAsToMain", null, editor.getValue().toString()); +function saveAsEmitter() { + ipcRenderer.send("fileSaveToMain", null, yarnFileManager.getCurrentOpenFile().getContents()); } /** @@ -496,84 +562,6 @@ function saveAsEmitter() * * @returns {void} */ -function openFileEmitter() -{ - ipcRenderer.send("fileOpenToMain"); -} - -// ipcRenderer.send('fileOpenToMain', 'ping'); -// ipcRenderer.send('fileSaveAsToMain', 'ping'); -// ipcRenderer.send('fileSaveToMain', 'ping'); - -// ipcRenderer.send('getPing','ping'); - - - - - - - - - -/* - -------------------------------------------------- - THIS NEEDS TO BE MOVED TO ANOTHER FILE - STAYING HERE FOR NOW BC WEBPACK ISSUES - -------------------------------------------------- -*/ - -export interface YarnFile -{ - filePath: string | null; - fileName: string; - contents: string; - isSaved: boolean; - uniqueIdentifier: number; - getName(): string; - getSaved(): boolean; -} - -export class YarnFileClass implements YarnFile -{ - filePath: string | null; - fileName: string; - contents: string; - isSaved: boolean; - uniqueIdentifier: number; - - constructor(filePath: string | null, contents:string|null, name: string|null, isSaved: boolean|null, uniqueIdentifier: number) - { - this.filePath = filePath ? filePath : null; - this.fileName = name ? name : "New File"; - this.contents = contents ? contents : ""; - this.isSaved = isSaved ? true : false; - this.uniqueIdentifier = uniqueIdentifier; - } - - getName():string - { - return this.fileName; - } - - getContents():string - { - return this.contents; - } - - getSaved():boolean - { - if(this.isSaved != undefined) - { - return this.isSaved; - } - else - { - alert("Failed to get saved. Does file not exist?"); - return false; - } - } - - getUniqueIdentifier():number { - return this.uniqueIdentifier; - } +function openFileEmitter() { + ipcRenderer.send("fileOpenToMain"); } \ No newline at end of file From 942ec77e0dd662399ab9102998fc2c84a06e129b Mon Sep 17 00:00:00 2001 From: Rooster_ Date: Sun, 23 May 2021 17:47:41 +1000 Subject: [PATCH 11/13] #21 Menu functions stubs and shortcuts --- src/main.ts | 106 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 91 insertions(+), 15 deletions(-) diff --git a/src/main.ts b/src/main.ts index 443b64c..dfa380d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -61,6 +61,7 @@ const template = [ submenu: [ { label: "New", + accelerator: 'CmdOrCtrl+N', click: async () => { handleNewFile(); @@ -68,15 +69,28 @@ const template = [ }, { label: "Open", + accelerator: 'CmdOrCtrl+O', click: async () => { handleFileOpen(); } }, - { label: "save" }, - { label: "save as" }, - { label: "import" }, - { label: "export" }, + { + label: "save", + accelerator: 'CmdOrCtrl+S', + click: async () => + { + handleFileSave(); + } + }, + { + label: "save as", + accelerator: 'CmdOrCtrl+Shift+S', + click: async () => + { + handleFileSaveAs(); + } + }, isMac ? { role: "close" } : { role: "quit" }, ] }, @@ -91,8 +105,22 @@ const template = [ { role: "cut" }, { role: "paste" }, { type: "separator" }, - { label: "find" }, - { label: "replace" }, + { + label: "find", + accelerator: 'CmdOrCtrl+F', + click: async () => + { + handleFind(); + } + }, + { + label: "replace", + accelerator: 'CmdOrCtrl+R', + click: async () => + { + handleReplace(); + } + }, ...(isMac ? [ { role: "pasteAndMatchStyle" }, @@ -145,6 +173,7 @@ const template = [ ]) ] }, + // optionsMenu { label: "Options", submenu: [ @@ -239,6 +268,9 @@ ipcMain.on("fileSaveAsToMain", (event, filePath, contents) => //BrowserWindow.getFocusedWindow()?.webContents.send("ChannelMessage", args); //This should ONLY be used for menu interaction +//File Options +//---------------------------- + /** * Emits message to renderer to create new file. * @@ -254,12 +286,56 @@ function handleNewFile() * * @returns {void} */ -function handleFileOpen() -{ - //Sends message from main to renderer - const fileContent = YarnOpenFile(); - if(fileContent) - { - BrowserWindow.getFocusedWindow()?.webContents.send("openFile", fileContent.path, fileContent.contents, fileContent.name); //Pass the result to renderer - } -} + function handleFileOpen() + { + //Sends message from main to renderer + const fileContent = YarnOpenFile(); + if(fileContent) + { + BrowserWindow.getFocusedWindow()?.webContents.send("openFile", fileContent.path, fileContent.contents, fileContent.name); //Pass the result to renderer + } + } + +/** + * Emits message to renderer to save the file. + * + * @returns {void} + */ + function handleFileSave() + { + BrowserWindow.getFocusedWindow()?.webContents.send("saveFile"); + } + + /** + * Emits message to renderer to saveAs the file. + * + * @returns {void} + */ + function handleFileSaveAs() + { + BrowserWindow.getFocusedWindow()?.webContents.send("mainRequestSaveAs"); + } + +//Edit Options +//---------------------------- + + /** + * Emits message to renderer to find the selected code within the file. + * + * @returns {void} + */ + function handleFind() + { + BrowserWindow.getFocusedWindow()?.webContents.send("findInFile"); + } + + /** + * Emits message to renderer to replace the selected code within the file. + * + * @returns {void} + */ + function handleReplace() + { + BrowserWindow.getFocusedWindow()?.webContents.send("ReplceInFile"); + } + From f2b24fda66e34df623b2d9fd2721acdbeecfdfa0 Mon Sep 17 00:00:00 2001 From: Adam Scott Date: Sun, 23 May 2021 18:30:41 +1000 Subject: [PATCH 12/13] #21 - Tidy up and small bug fixes on swapping between files --- src/views/ts/renderer.ts | 60 +++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/views/ts/renderer.ts b/src/views/ts/renderer.ts index 35b787b..c82742b 100644 --- a/src/views/ts/renderer.ts +++ b/src/views/ts/renderer.ts @@ -115,7 +115,7 @@ export class YarnFileManager { } createEmptyFile(): YarnFile { - const newFile: YarnFile = new YarnFile(null, null, null, false, Date.now()); + const newFile: YarnFile = new YarnFile(null, null, null, Date.now()); this.addToFiles(newFile); this.setCurrentOpenYarnFile(newFile.getUniqueIdentifier()); @@ -234,34 +234,31 @@ if (containerElement) { boldText?.click(); }); - //Editor specific events editor.onDidChangeModelContent(e => { + - var workingDetailDiv = document.getElementById(yarnFileManager.getCurrentOpenFile().getUniqueIdentifier().toString()); //TODO change to ID + var workingDetailDiv = document.getElementById(yarnFileManager.getCurrentOpenFile().getUniqueIdentifier().toString()); - var unsavedIdentifier = "*"; + syncCurrentFile();//Update the contents at each point + var unsavedIdentifier = "*";//Can change to anything if (workingDetailDiv) { - var paraElementContent = workingDetailDiv.children[0].innerHTML - - //Check the currentOpenYarnFile against the editor's value - - //Then appends the unsaved identifier if the file is not saved, and set's it to false - if (yarnFileManager.getCurrentOpenFile().getContents() === editor.getValue()) { - yarnFileManager.getCurrentOpenFile().setSaved(true); - - if (paraElementContent.substr(paraElementContent.length - unsavedIdentifier.length) === unsavedIdentifier) { - workingDetailDiv.children[0].innerHTML = paraElementContent.slice(0, - unsavedIdentifier.length); + var paraElementContent = workingDetailDiv.children[0].innerHTML//Access the paragraph text + + //Checks if it is not saved + if (yarnFileManager.getCurrentOpenFile().getSaved() === false){ + if (paraElementContent.substr(paraElementContent.length - unsavedIdentifier.length) !== unsavedIdentifier) { + workingDetailDiv.children[0].innerHTML = paraElementContent.concat(unsavedIdentifier); } - } - - else { - if (yarnFileManager.getCurrentOpenFile().getSaved()) { - yarnFileManager.getCurrentOpenFile().setSaved(false); - workingDetailDiv.children[0].innerHTML = paraElementContent.concat(unsavedIdentifier); + } + //Checks if it is saved + else if (yarnFileManager.getCurrentOpenFile().getSaved()) { + //check if it is saved, but still has the * + if (paraElementContent.substr(paraElementContent.length - unsavedIdentifier.length) === unsavedIdentifier) { + workingDetailDiv.children[0].innerHTML = paraElementContent.slice(0, - unsavedIdentifier.length); } - } + } } }); @@ -291,7 +288,13 @@ if (workingFiles) { //Remove file from array if (yarnFileManager.getFiles().size === 1) { - alert("Cannot delete the only file in work space."); + //TODO + + /* + Delete the current file, create a new one, because alerts break + the window focus and half an hour of documentation searching + resulting in nothing + */ } else { //TODO update currentOpenFile if deleted file is/was currentOpenFile @@ -300,20 +303,15 @@ if (workingFiles) { var openfile = yarnFileManager.getCurrentOpenFile().getUniqueIdentifier(); - console.log("Open file is: " + openfile + typeof openfile); - console.log("closing file is: " + fileIdentifier + typeof fileIdentifier); - if (fileIdentifier === yarnFileManager.getCurrentOpenFile().getUniqueIdentifier()) { - console.log("removing current file") - //Remove file + yarnFileManager.removeFromFiles(fileIdentifier); - var arrayOfFiles = Array.from(yarnFileManager.getFiles().keys()); - console.log(arrayOfFiles); + var arrayOfFiles = Array.from(yarnFileManager.getFiles().keys());//Get new list of files yarnFileManager.setCurrentOpenYarnFile(arrayOfFiles[0]); + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); } else { - console.log("removing another file that shouldn't be the editor's content"); yarnFileManager.removeFromFiles(fileIdentifier); editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); } @@ -503,7 +501,7 @@ ipcRenderer.on("openFile", (event, path, contents, name) => { name = "New File"; } - const openedFile = new YarnFile(path, contents, name, true, Date.now()); + const openedFile = new YarnFile(path, contents, name, Date.now()); yarnFileManager.addToFiles(openedFile); addFileToDisplay(openedFile); yarnFileManager.setCurrentOpenYarnFile(openedFile.getUniqueIdentifier()); From cc3f0293c1a6a533fd88b0012244dfb6b3f6011c Mon Sep 17 00:00:00 2001 From: Seth Hilder Date: Sun, 23 May 2021 20:27:53 +1000 Subject: [PATCH 13/13] #21 - Set readOnly when no files + fix tests --- src/controllers/themeReader.ts | 2 +- src/main.ts | 68 ++-- src/views/ts/renderer.ts | 683 ++++++++++++++++++--------------- 3 files changed, 407 insertions(+), 346 deletions(-) diff --git a/src/controllers/themeReader.ts b/src/controllers/themeReader.ts index 6093e47..8b11259 100644 --- a/src/controllers/themeReader.ts +++ b/src/controllers/themeReader.ts @@ -81,6 +81,6 @@ module.exports = { divideColour: "#2c2c2c", lineSelection: "#a5a5a5" -} +}; /**/ diff --git a/src/main.ts b/src/main.ts index dfa380d..d0b8e32 100644 --- a/src/main.ts +++ b/src/main.ts @@ -61,7 +61,7 @@ const template = [ submenu: [ { label: "New", - accelerator: 'CmdOrCtrl+N', + accelerator: "CmdOrCtrl+N", click: async () => { handleNewFile(); @@ -69,7 +69,7 @@ const template = [ }, { label: "Open", - accelerator: 'CmdOrCtrl+O', + accelerator: "CmdOrCtrl+O", click: async () => { handleFileOpen(); @@ -77,7 +77,7 @@ const template = [ }, { label: "save", - accelerator: 'CmdOrCtrl+S', + accelerator: "CmdOrCtrl+S", click: async () => { handleFileSave(); @@ -85,7 +85,7 @@ const template = [ }, { label: "save as", - accelerator: 'CmdOrCtrl+Shift+S', + accelerator: "CmdOrCtrl+Shift+S", click: async () => { handleFileSaveAs(); @@ -107,7 +107,7 @@ const template = [ { type: "separator" }, { label: "find", - accelerator: 'CmdOrCtrl+F', + accelerator: "CmdOrCtrl+F", click: async () => { handleFind(); @@ -115,7 +115,7 @@ const template = [ }, { label: "replace", - accelerator: 'CmdOrCtrl+R', + accelerator: "CmdOrCtrl+R", click: async () => { handleReplace(); @@ -286,56 +286,56 @@ function handleNewFile() * * @returns {void} */ - function handleFileOpen() - { - //Sends message from main to renderer - const fileContent = YarnOpenFile(); - if(fileContent) - { - BrowserWindow.getFocusedWindow()?.webContents.send("openFile", fileContent.path, fileContent.contents, fileContent.name); //Pass the result to renderer - } - } +function handleFileOpen() +{ + //Sends message from main to renderer + const fileContent = YarnOpenFile(); + if(fileContent) + { + BrowserWindow.getFocusedWindow()?.webContents.send("openFile", fileContent.path, fileContent.contents, fileContent.name); //Pass the result to renderer + } +} /** * Emits message to renderer to save the file. * * @returns {void} */ - function handleFileSave() - { - BrowserWindow.getFocusedWindow()?.webContents.send("saveFile"); - } +function handleFileSave() +{ + BrowserWindow.getFocusedWindow()?.webContents.send("saveFile"); +} - /** +/** * Emits message to renderer to saveAs the file. * * @returns {void} */ - function handleFileSaveAs() - { - BrowserWindow.getFocusedWindow()?.webContents.send("mainRequestSaveAs"); - } +function handleFileSaveAs() +{ + BrowserWindow.getFocusedWindow()?.webContents.send("mainRequestSaveAs"); +} //Edit Options //---------------------------- - /** +/** * Emits message to renderer to find the selected code within the file. * * @returns {void} */ - function handleFind() - { - BrowserWindow.getFocusedWindow()?.webContents.send("findInFile"); - } +function handleFind() +{ + BrowserWindow.getFocusedWindow()?.webContents.send("findInFile"); +} - /** +/** * Emits message to renderer to replace the selected code within the file. * * @returns {void} */ - function handleReplace() - { - BrowserWindow.getFocusedWindow()?.webContents.send("ReplceInFile"); - } +function handleReplace() +{ + BrowserWindow.getFocusedWindow()?.webContents.send("ReplceInFile"); +} diff --git a/src/views/ts/renderer.ts b/src/views/ts/renderer.ts index c82742b..40cd9e1 100644 --- a/src/views/ts/renderer.ts +++ b/src/views/ts/renderer.ts @@ -20,106 +20,127 @@ -------------------------------------------------- */ -export class YarnFile { +export class YarnFile +{ private filePath: string | null; private fileName: string; private contents: string; private contentsOnDisk: string; private uniqueIdentifier: number; - constructor(filePath: string | null, contents: string | null, name: string | null, uniqueIdentifier: number) { - this.filePath = filePath ? filePath : null; - this.fileName = name ? name : "New File"; - this.contents = contents ? contents : ""; - this.contentsOnDisk = contents ? contents : ""; - this.uniqueIdentifier = uniqueIdentifier; + constructor(filePath: string | null, contents: string | null, name: string | null, uniqueIdentifier: number) + { + this.filePath = filePath ? filePath : null; + this.fileName = name ? name : "New File"; + this.contents = contents ? contents : ""; + this.contentsOnDisk = contents ? contents : ""; + this.uniqueIdentifier = uniqueIdentifier; } //Getters - getPath(): string | null { - return this.filePath; + getPath(): string | null + { + return this.filePath; } - getName(): string { - return this.fileName; + getName(): string + { + return this.fileName; } - getContents(): string { - return this.contents; + getContents(): string + { + return this.contents; } - getSaved(): boolean { - return this.getContents() === this.contentsOnDisk; + getSaved(): boolean + { + return this.getContents() === this.contentsOnDisk; } - getUniqueIdentifier(): number { - return this.uniqueIdentifier; + getUniqueIdentifier(): number + { + return this.uniqueIdentifier; } //Setters - setFilePath(filePath: string) { - this.filePath = filePath; + setFilePath(filePath: string) : void + { + this.filePath = filePath; } - setName(name: string) { - this.fileName = name; + setName(name: string) : void + { + this.fileName = name; } - setContents(contents: string) { - this.contents = contents; + setContents(contents: string) : void + { + this.contents = contents; } //Functions - fileSaved(){ - this.contentsOnDisk = this.contents; + fileSaved() : void + { + this.contentsOnDisk = this.contents; } } -export class YarnFileManager { +export class YarnFileManager +{ private openFiles = new Map(); private currentOpenYarnFile: YarnFile; - constructor() { - this.currentOpenYarnFile = this.createEmptyFile(); + constructor() + { + this.currentOpenYarnFile = this.createEmptyFile(); } //Getters - getFiles(): Map { - return this.openFiles; + getFiles(): Map + { + return this.openFiles; } - getCurrentOpenFile(): YarnFile { - return this.currentOpenYarnFile; + getCurrentOpenFile(): YarnFile + { + return this.currentOpenYarnFile; } - getYarnFile(yarnIDNumber: number) { - return this.openFiles.get(yarnIDNumber); + getYarnFile(yarnIDNumber: number) : YarnFile | undefined + { + return this.openFiles.get(yarnIDNumber); } //Setters - setCurrentOpenYarnFile(yarnIDNumber: number) { - var newCurrent = this.openFiles.get(yarnIDNumber); - if (newCurrent) { - this.currentOpenYarnFile = newCurrent; - } + setCurrentOpenYarnFile(yarnIDNumber: number) : void + { + const newCurrent = this.openFiles.get(yarnIDNumber); + if (newCurrent) + { + this.currentOpenYarnFile = newCurrent; + } } //Functions - addToFiles(newFile: YarnFile): void { - this.openFiles.set(newFile.getUniqueIdentifier(), newFile); + addToFiles(newFile: YarnFile): void + { + this.openFiles.set(newFile.getUniqueIdentifier(), newFile); } - removeFromFiles(yarnIDNumber: number) { - this.openFiles.delete(yarnIDNumber); + removeFromFiles(yarnIDNumber: number) : void + { + this.openFiles.delete(yarnIDNumber); } - createEmptyFile(): YarnFile { - const newFile: YarnFile = new YarnFile(null, null, null, Date.now()); - this.addToFiles(newFile); - this.setCurrentOpenYarnFile(newFile.getUniqueIdentifier()); + createEmptyFile(): YarnFile + { + const newFile: YarnFile = new YarnFile(null, null, null, Date.now()); + this.addToFiles(newFile); + this.setCurrentOpenYarnFile(newFile.getUniqueIdentifier()); - return newFile; + return newFile; } } @@ -139,9 +160,7 @@ import * as yarnSpinner from "../../YarnSpinner/yarnSpinnerMonarch"; import { ipcRenderer } from "electron"; import exports from "../../controllers/themeReader.ts"; -let yarnFileManager = new YarnFileManager(); - -let editor: monaco.editor.IStandaloneCodeEditor; +const yarnFileManager = new YarnFileManager(); //Register our new custom language monaco.languages.register({ id: "yarnSpinner" }); @@ -156,262 +175,283 @@ monaco.languages.registerCompletionItemProvider("yarnSpinner", yarnSpinner.compl //Utilising exports we can get the variable information from themeReader -monaco.editor.defineTheme('customTheme', { - base: 'vs', - inherit: true, - rules: [ - //{ background: 'CFD8DC'}, - - { token: 'body.bold', fontStyle: 'bold' }, - { token: 'body.underline', fontStyle: 'underline' }, - { token: 'body.italic', fontStyle: 'italic' }, - { token: 'body.commands', foreground: exports.commands }, - { token: 'commands', foreground: exports.commands }, - { token: 'file.tag', foreground: exports.fileTag }, - { token: 'interpolation', foreground: exports.interpolation }, - { token: 'options', foreground: exports.option }, - { token: 'variables', foreground: exports.variables }, - { token: 'float', foreground: exports.float }, - { token: 'number', foreground: exports.number }, - { token: 'yarn.commands', foreground: exports.yarnCommands }, - { token: 'commands.float', foreground: exports.commands }, - { token: 'commands.number', foreground: exports.commands }, - { token: 'commands.operator', foreground: exports.operator }, - { token: 'hashtag', foreground: exports.hashtag }, - { token: 'dialogue', foreground: exports.primary_text } - ], - - colors: { - 'editor.foreground': exports.primary_text, - 'editor.background': exports.editor, - 'editorCursor.foreground': exports.workingFile, - 'editor.lineHighlightBackground': exports.lineSelection, - 'editorLineNumber.foreground': exports.primary_text, - 'editor.selectionBackground': exports.lineSelection, - 'editor.inactiveSelectionBackground': exports.editor, - 'minimap.background': exports.lineSelection - } +monaco.editor.defineTheme("customTheme", { + base: "vs", + inherit: true, + rules: [ + //{ background: 'CFD8DC'}, + + { token: "body.bold", fontStyle: "bold" }, + { token: "body.underline", fontStyle: "underline" }, + { token: "body.italic", fontStyle: "italic" }, + { token: "body.commands", foreground: exports.commands }, + { token: "commands", foreground: exports.commands }, + { token: "file.tag", foreground: exports.fileTag }, + { token: "interpolation", foreground: exports.interpolation }, + { token: "options", foreground: exports.option }, + { token: "variables", foreground: exports.variables }, + { token: "float", foreground: exports.float }, + { token: "number", foreground: exports.number }, + { token: "yarn.commands", foreground: exports.yarnCommands }, + { token: "commands.float", foreground: exports.commands }, + { token: "commands.number", foreground: exports.commands }, + { token: "commands.operator", foreground: exports.operator }, + { token: "hashtag", foreground: exports.hashtag }, + { token: "dialogue", foreground: exports.primary_text } + ], + + colors: { + "editor.foreground": exports.primary_text, + "editor.background": exports.editor, + "editorCursor.foreground": exports.workingFile, + "editor.lineHighlightBackground": exports.lineSelection, + "editorLineNumber.foreground": exports.primary_text, + "editor.selectionBackground": exports.lineSelection, + "editor.inactiveSelectionBackground": exports.editor, + "minimap.background": exports.lineSelection + } }); //set css variables -document.documentElement.style.setProperty(`--editor`, exports.editor); -document.documentElement.style.setProperty(`--topSideEdit`, exports.editor); -document.documentElement.style.setProperty(`--workingFile`, exports.workingFile); -document.documentElement.style.setProperty(`--tabGap`, exports.tabGap); -document.documentElement.style.setProperty(`--dividerColour`, exports.divideColour); -document.documentElement.style.setProperty(`--primary_text`, exports.primary_text); -document.documentElement.style.setProperty(`--secondary_text`, exports.secondary_text); +document.documentElement.style.setProperty("--editor", exports.editor); +document.documentElement.style.setProperty("--topSideEdit", exports.editor); +document.documentElement.style.setProperty("--workingFile", exports.workingFile); +document.documentElement.style.setProperty("--tabGap", exports.tabGap); +document.documentElement.style.setProperty("--dividerColour", exports.divideColour); +document.documentElement.style.setProperty("--primary_text", exports.primary_text); +document.documentElement.style.setProperty("--secondary_text", exports.secondary_text); const containerElement = document.getElementById("container"); +if (!containerElement) +{ + throw new Error("Container element not found"); +} + +const editor = monaco.editor.create(containerElement, { + //theme: "yarnSpinnerTheme", + theme: "customTheme", + value: "".toString(), + language: "yarnSpinner", + automaticLayout: true, + fontFamily: "Courier New", + fontSize: 14, + mouseWheelZoom: true, + wordWrap: "on" +}); + +//Instantiate with new empty file +editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); + +//Override monaco's default commands to add our own +editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_I, () => +{ + italicText?.click(); +}); + +editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_U, () => +{ + underlineText?.click(); +}); -if (containerElement) { - editor = monaco.editor.create(containerElement, { - //theme: "yarnSpinnerTheme", - theme: "customTheme", - value: "".toString(), - language: "yarnSpinner", - automaticLayout: true, - fontFamily: "Courier New", - fontSize: 14, - mouseWheelZoom: true, - wordWrap: "on" - }); - - //Instantiate with new empty file - editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); - - //Override monaco's default commands to add our own - editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_I, () => { - italicText?.click(); - }); - - editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_U, () => { - underlineText?.click(); - }); - - editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_B, () => { - boldText?.click(); - }); - - //Editor specific events - editor.onDidChangeModelContent(e => { - - - var workingDetailDiv = document.getElementById(yarnFileManager.getCurrentOpenFile().getUniqueIdentifier().toString()); - - syncCurrentFile();//Update the contents at each point - var unsavedIdentifier = "*";//Can change to anything - - if (workingDetailDiv) { - var paraElementContent = workingDetailDiv.children[0].innerHTML//Access the paragraph text - - //Checks if it is not saved - if (yarnFileManager.getCurrentOpenFile().getSaved() === false){ - if (paraElementContent.substr(paraElementContent.length - unsavedIdentifier.length) !== unsavedIdentifier) { - workingDetailDiv.children[0].innerHTML = paraElementContent.concat(unsavedIdentifier); - } +editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_B, () => +{ + boldText?.click(); +}); + +//Editor specific events +editor.onDidChangeModelContent(() => +{ + const workingDetailDiv = document.getElementById(yarnFileManager.getCurrentOpenFile().getUniqueIdentifier().toString()); + + syncCurrentFile();//Update the contents at each point + const unsavedIdentifier = "*";//Can change to anything + + if (workingDetailDiv) + { + const paraElementContent = workingDetailDiv.children[0].innerHTML;//Access the paragraph text + + //Checks if it is not saved + if (yarnFileManager.getCurrentOpenFile().getSaved() === false) + { + if (paraElementContent.substr(paraElementContent.length - unsavedIdentifier.length) !== unsavedIdentifier) + { + workingDetailDiv.children[0].innerHTML = paraElementContent.concat(unsavedIdentifier); } - //Checks if it is saved - else if (yarnFileManager.getCurrentOpenFile().getSaved()) { - //check if it is saved, but still has the * - if (paraElementContent.substr(paraElementContent.length - unsavedIdentifier.length) === unsavedIdentifier) { - workingDetailDiv.children[0].innerHTML = paraElementContent.slice(0, - unsavedIdentifier.length); - } + } + //Checks if it is saved + else if (yarnFileManager.getCurrentOpenFile().getSaved()) + { + //check if it is saved, but still has the * + if (paraElementContent.substr(paraElementContent.length - unsavedIdentifier.length) === unsavedIdentifier) + { + workingDetailDiv.children[0].innerHTML = paraElementContent.slice(0, - unsavedIdentifier.length); } - } - }); + } + } +}); -} //Working file details specific events const workingFiles = document.getElementById("workingFilesDetail"); -if (workingFiles) { +if (workingFiles) +{ + + //Set the intiated new empty file into working space + addFileToDisplay(yarnFileManager.getCurrentOpenFile()); + editor.updateOptions({ readOnly: false }); + + //Add all listeners + workingFiles.addEventListener("click", (event) => + { + if (event && event.target && (event.target as HTMLElement).tagName === "BUTTON") + { + //Get file ID information and HTML elements + const button = (event.target as HTMLButtonElement); + const parentDiv = button.parentElement; + const fileIdentifier = Number(parentDiv?.id); + + if (Number.isNaN(fileIdentifier) || !parentDiv) + { + console.error("Attempted to remove broken file instance, please file a bug at https://github.com/setho246/YarnSpinnerEditor/issues"); + return; + } - //Set the intiated new empty file into working space - addFileToDisplay(yarnFileManager.getCurrentOpenFile()); + //Remove file from array + if (yarnFileManager.getFiles().size === 1) + { + editor.updateOptions({ readOnly: true }); + } - //Add all listeners - workingFiles.addEventListener('click', (event) => { - if (event && event.target && (event.target as HTMLElement).tagName === "BUTTON") { + if (fileIdentifier === yarnFileManager.getCurrentOpenFile().getUniqueIdentifier()) + { + editor.setValue(""); + yarnFileManager.removeFromFiles(fileIdentifier); + const arrayOfFiles = Array.from(yarnFileManager.getFiles().keys());//Get new list of files + + if(arrayOfFiles.length) + { + yarnFileManager.setCurrentOpenYarnFile(arrayOfFiles[0]); + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); + editor.updateOptions({readOnly: false}); + } + } + else + { + yarnFileManager.removeFromFiles(fileIdentifier); + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); + editor.updateOptions({readOnly: false}); + } - //Get file ID information and HTML elements - var button = (event.target as HTMLButtonElement); - var parentDiv = button.parentElement; - var fileIdentifier = Number(parentDiv?.id); + //Remove the HTML elements from working files + parentDiv.parentElement?.removeChild(parentDiv); + } + else if (event && event.target && (event.target as HTMLElement).tagName !== "DETAILS" && (event.target as HTMLElement).tagName !== "SUMMARY") + { + let fileIdentifier: number; - if (Number.isNaN(fileIdentifier) || !parentDiv) { - console.error("Attempted to remove broken file instance, please file a bug at https://github.com/setho246/YarnSpinnerEditor/issues"); - return; - } + if ((event.target as HTMLElement).tagName === "P") + { + fileIdentifier = Number((event.target as HTMLParagraphElement).parentElement?.id); + } + else + { + const divElement = (event.target as HTMLDivElement); + fileIdentifier = Number(divElement.id); + } - //Remove file from array - if (yarnFileManager.getFiles().size === 1) { - //TODO + const openedFile = yarnFileManager.getYarnFile(fileIdentifier);//Gets the new thing - /* - Delete the current file, create a new one, because alerts break - the window focus and half an hour of documentation searching - resulting in nothing - */ - } - else { - //TODO update currentOpenFile if deleted file is/was currentOpenFile - //If that was the case, change the editor value + if (openedFile) + { + //update currentOpen content + syncCurrentFile(); - - var openfile = yarnFileManager.getCurrentOpenFile().getUniqueIdentifier(); - - if (fileIdentifier === yarnFileManager.getCurrentOpenFile().getUniqueIdentifier()) { - - yarnFileManager.removeFromFiles(fileIdentifier); - var arrayOfFiles = Array.from(yarnFileManager.getFiles().keys());//Get new list of files - yarnFileManager.setCurrentOpenYarnFile(arrayOfFiles[0]); - - editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); - } - else { - yarnFileManager.removeFromFiles(fileIdentifier); - editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); - } - //Remove file - //yarnFileManager.removeFromFiles(fileIdentifier); - - //Remove the HTML elements from working files - parentDiv.parentElement?.removeChild(parentDiv); - } - } - - else if (event && event.target && (event.target as HTMLElement).tagName !== "DETAILS" && (event.target as HTMLElement).tagName !== "SUMMARY") { - var fileIdentifier: number; - - if ((event.target as HTMLElement).tagName === "P") { - fileIdentifier = Number((event.target as HTMLParagraphElement).parentElement?.id) - } - else { - var divElement = (event.target as HTMLDivElement); - fileIdentifier = Number(divElement.id); - } - - var openedFile = yarnFileManager.getYarnFile(fileIdentifier);//Gets the new thing - - if (openedFile) { - //update currentOpen content - syncCurrentFile(); - - //Change currentOpen - yarnFileManager.setCurrentOpenYarnFile(fileIdentifier) - editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); //TODO Swap to push edit operations? https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.itextmodel.html#pusheditoperations - editor.updateOptions({ readOnly: false }); - } - } - }); + //Change currentOpen + yarnFileManager.setCurrentOpenYarnFile(fileIdentifier); + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); //TODO Swap to push edit operations? https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.itextmodel.html#pusheditoperations + editor.updateOptions({ readOnly: false }); + } + } + }); } /** * Update the file manager file to match the code editor. * - * @returns void + * @returns {void} */ -function syncCurrentFile() { - yarnFileManager.getCurrentOpenFile().setContents(editor.getValue()); +function syncCurrentFile() +{ + yarnFileManager.getCurrentOpenFile().setContents(editor.getValue()); } /** - * Generic function for inserting at the front and the end of a selection + * Generic function for inserting at the front and the end of a selection. + * + * @param {string} textFront The tag to insert at the front of the selection + * @param {string} textBack The tag to insert at the back of the selection + * + * @returns {void} */ -function wrapTextWithTag(textFront: String, textBack: String) { +function wrapTextWithTag(textFront: string, textBack: string) +{ - var selection = editor.getSelection() as monaco.IRange; - var selectFront = new monaco.Selection(selection.startLineNumber, selection.startColumn, selection.startLineNumber, selection.startColumn); - var selectBack = new monaco.Selection(selection.endLineNumber, selection.endColumn, selection.endLineNumber, selection.endColumn); + const selection = editor.getSelection() as monaco.IRange; + const selectFront = new monaco.Selection(selection.startLineNumber, selection.startColumn, selection.startLineNumber, selection.startColumn); + const selectBack = new monaco.Selection(selection.endLineNumber, selection.endColumn, selection.endLineNumber, selection.endColumn); - var frontString = textFront.concat("");//Needs to concat an empty character in order to set the cursor properly + const frontString = textFront.concat("");//Needs to concat an empty character in order to set the cursor properly - //In this order, so when nothing is selected, textFront is prepended after textBack is inserted - editor.focus();//Set focus back to editor + //In this order, so when nothing is selected, textFront is prepended after textBack is inserted + editor.focus();//Set focus back to editor - editor.executeEdits("", [{ range: selectBack, text: textBack as string }]);//Set textBack - editor.setPosition(new monaco.Position(selectFront.startLineNumber, selectFront.startColumn));//reset cursor to behind textFront input - editor.executeEdits("", [{ range: selectFront, text: frontString as string }]);//Set textFront - editor.setSelection(new monaco.Selection(selection.startLineNumber, selection.startColumn + frontString.length, selection.startLineNumber, selection.startColumn + frontString.length)); - //Reset collection to an empty range + editor.executeEdits("", [{ range: selectBack, text: textBack as string }]);//Set textBack + editor.setPosition(new monaco.Position(selectFront.startLineNumber, selectFront.startColumn));//reset cursor to behind textFront input + editor.executeEdits("", [{ range: selectFront, text: frontString as string }]);//Set textFront + editor.setSelection(new monaco.Selection(selection.startLineNumber, selection.startColumn + frontString.length, selection.startLineNumber, selection.startColumn + frontString.length)); + //Reset collection to an empty range } //Set selection to BOLD const boldText = document.getElementById("boldTextIcon"); -if (boldText) { - boldText.onclick = () => { wrapTextWithTag("[b]", "[\\b]"); }; +if (boldText) +{ + boldText.onclick = () => { wrapTextWithTag("[b]", "[\\b]"); }; } //Set selection to Italics const italicText = document.getElementById("italicTextIcon"); -if (italicText) { - italicText.onclick = () => { wrapTextWithTag("[i]", "[\\i]"); }; +if (italicText) +{ + italicText.onclick = () => { wrapTextWithTag("[i]", "[\\i]"); }; } //Set selection to Underline const underlineText = document.getElementById("underlineTextIcon"); -if (underlineText) { - underlineText.onclick = () => { wrapTextWithTag("[u]", "[\\u]"); }; +if (underlineText) +{ + underlineText.onclick = () => { wrapTextWithTag("[u]", "[\\u]"); }; } //TODO Set selection to selected colour const colourPick = document.getElementById("colourPicker"); -if (colourPick) { - colourPick.onchange = () => { - var value = (colourPick as HTMLInputElement).value; - - var startText = "[col=\'".concat(value.substr(1)).concat("\']"); - var endText = "[\\col]"; - - wrapTextWithTag(startText, endText); - editor.focus(); - } +if (colourPick) +{ + colourPick.onchange = () => + { + const value = (colourPick as HTMLInputElement).value; + + const startText = "[col='".concat(value.substr(1)).concat("']"); + const endText = "[\\col]"; + + wrapTextWithTag(startText, endText); + editor.focus(); + }; } //Listen for editor commands @@ -423,22 +463,27 @@ if (colourPick) { const saveFileIcon = document.getElementById("saveFileIcon"); -if (saveFileIcon) { - saveFileIcon.onclick = () => { saveAsEmitter(); }; +if (saveFileIcon) +{ + saveFileIcon.onclick = () => { saveAsEmitter(); }; } const newFileIcon = document.getElementById("newFileIcon"); -if (newFileIcon) { - newFileIcon.onclick = function () { - createNewFile(); - }; +if (newFileIcon) +{ + newFileIcon.onclick = function () + { + createNewFile(); + }; } const openFolderIcon = document.getElementById("openFolderIcon"); -if (openFolderIcon) { - openFolderIcon.onclick = function () { - openFileEmitter(); - }; +if (openFolderIcon) +{ + openFolderIcon.onclick = function () + { + openFileEmitter(); + }; } /** @@ -446,10 +491,12 @@ if (openFolderIcon) { * * @returns {void} */ -function createNewFile() { - yarnFileManager.createEmptyFile() - addFileToDisplay(yarnFileManager.createEmptyFile()); - editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); +function createNewFile() +{ + yarnFileManager.createEmptyFile(); + addFileToDisplay(yarnFileManager.createEmptyFile()); + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); + editor.updateOptions({ readOnly: false }); } /** @@ -459,28 +506,31 @@ function createNewFile() { * * @returns {void} */ -function addFileToDisplay(file: YarnFile): void { - const div = document.createElement("div"); - div.setAttribute("id", file.getUniqueIdentifier().toString()); +function addFileToDisplay(file: YarnFile): void +{ + const div = document.createElement("div"); + div.setAttribute("id", file.getUniqueIdentifier().toString()); - const closeButton = document.createElement("button"); - closeButton.textContent = "x"; + const closeButton = document.createElement("button"); + closeButton.textContent = "x"; - const para = document.createElement("p"); - para.textContent = file.getName(); + const para = document.createElement("p"); + para.textContent = file.getName(); - div.appendChild(para); - div.appendChild(closeButton); + div.appendChild(para); + div.appendChild(closeButton); - const fileListElement = document.getElementById("workingFilesDetail"); + const fileListElement = document.getElementById("workingFilesDetail"); - if (fileListElement) { - fileListElement.appendChild(div); - } - else { - console.error("OpenFileError: Cannot append file to display list"); - } + if (fileListElement) + { + fileListElement.appendChild(div); + } + else + { + console.error("OpenFileError: Cannot append file to display list"); + } } @@ -496,39 +546,48 @@ function addFileToDisplay(file: YarnFile): void { ------------------------------------ */ -ipcRenderer.on("openFile", (event, path, contents, name) => { - if (!name) { - name = "New File"; - } - - const openedFile = new YarnFile(path, contents, name, Date.now()); - yarnFileManager.addToFiles(openedFile); - addFileToDisplay(openedFile); - yarnFileManager.setCurrentOpenYarnFile(openedFile.getUniqueIdentifier()); - editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); +ipcRenderer.on("openFile", (event, path, contents, name) => +{ + if (!name) + { + name = "New File"; + } + + const openedFile = new YarnFile(path, contents, name, Date.now()); + yarnFileManager.addToFiles(openedFile); + addFileToDisplay(openedFile); + yarnFileManager.setCurrentOpenYarnFile(openedFile.getUniqueIdentifier()); + editor.setValue(yarnFileManager.getCurrentOpenFile().getContents()); + editor.updateOptions({ readOnly: false }); }); -ipcRenderer.on("fileSaveResponse", (event, arg) => { - if (arg) { - alert("File successfully"); - } - else { - alert("File save error occured"); - } +ipcRenderer.on("fileSaveResponse", (event, arg) => +{ + if (arg) + { + alert("File successfully"); + } + else + { + alert("File save error occured"); + } }); -ipcRenderer.on("mainRequestSaveAs", () => { - saveAsEmitter(); +ipcRenderer.on("mainRequestSaveAs", () => +{ + saveAsEmitter(); }); -ipcRenderer.on("mainRequestNewFile", () => { - createNewFile(); +ipcRenderer.on("mainRequestNewFile", () => +{ + createNewFile(); }); -ipcRenderer.on("gotPing", (event, arg) => { - console.log("RESPONSE RECIEVED"); - console.log(arg);//Should be pong +ipcRenderer.on("gotPing", (event, arg) => +{ + console.log("RESPONSE RECIEVED"); + console.log(arg);//Should be pong }); @@ -551,8 +610,9 @@ ipcRenderer.on("gotPing", (event, arg) => { * * @returns {void} */ -function saveAsEmitter() { - ipcRenderer.send("fileSaveToMain", null, yarnFileManager.getCurrentOpenFile().getContents()); +function saveAsEmitter() +{ + ipcRenderer.send("fileSaveToMain", null, yarnFileManager.getCurrentOpenFile().getContents()); } /** @@ -560,6 +620,7 @@ function saveAsEmitter() { * * @returns {void} */ -function openFileEmitter() { - ipcRenderer.send("fileOpenToMain"); +function openFileEmitter() +{ + ipcRenderer.send("fileOpenToMain"); } \ No newline at end of file