From 7c4f85ec21e04c746cae821c31d36748aebdf7c0 Mon Sep 17 00:00:00 2001 From: Mike Nolan Date: Sat, 6 Sep 2025 11:14:02 -0500 Subject: [PATCH] New dev build --- .gitignore | 3 +- Assets/crosslang.ico | Bin 46215 -> 67758 bytes Assets/crosslang.png | Bin 0 -> 4832 bytes CrossLangDevStudio.csproj | 2 + CrossLangDevStudio.sln | 22 ++ CrossLangFile.cs | 4 +- CrossLangShell.cs | 250 +++++++++++++++++++- Messages/InstallPackageMessage.cs | 12 + Messages/InstallPackageResponseMessage.cs | 9 + Models/IPackageManager.cs | 42 ++++ Models/ProjectPackageManager.cs | 70 ++++++ Packaging/Linux/build.sh | 15 ++ Packaging/Linux/crossdev | 2 + Packaging/Linux/crosslang-devstudio.desktop | 8 + Packaging/Linux/debian/control-amd64 | 8 + Packaging/Linux/push.sh | 6 + Packaging/Windows/build.sh | 2 +- ViewModels/InstallPackageDialogViewModel.cs | 88 +++++++ ViewModels/MainWindowViewModel.cs | 35 ++- ViewModels/NewProjectDialogViewModel.cs | 2 +- ViewModels/PackageManagerViewModel.cs | 203 ++++++++++++++++ ViewModels/ProjectConfigurationViewModel.cs | 5 +- Views/InstallPackageDialog.axaml | 46 ++++ Views/InstallPackageDialog.axaml.cs | 21 ++ Views/MainWindow.axaml | 18 ++ Views/MainWindow.axaml.cs | 9 + Views/PackageManagerView.axaml | 76 ++++++ Views/PackageManagerView.axaml.cs | 11 + 28 files changed, 952 insertions(+), 17 deletions(-) create mode 100644 Assets/crosslang.png create mode 100644 CrossLangDevStudio.sln create mode 100644 Messages/InstallPackageMessage.cs create mode 100644 Messages/InstallPackageResponseMessage.cs create mode 100644 Models/IPackageManager.cs create mode 100644 Models/ProjectPackageManager.cs create mode 100644 Packaging/Linux/crossdev create mode 100644 Packaging/Linux/crosslang-devstudio.desktop create mode 100644 Packaging/Linux/debian/control-amd64 create mode 100644 Packaging/Linux/push.sh create mode 100644 ViewModels/InstallPackageDialogViewModel.cs create mode 100644 ViewModels/PackageManagerViewModel.cs create mode 100644 Views/InstallPackageDialog.axaml create mode 100644 Views/InstallPackageDialog.axaml.cs create mode 100644 Views/PackageManagerView.axaml create mode 100644 Views/PackageManagerView.axaml.cs diff --git a/.gitignore b/.gitignore index 1d0ef60..de79242 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ bin obj out -build \ No newline at end of file +build +Installing \ No newline at end of file diff --git a/Assets/crosslang.ico b/Assets/crosslang.ico index 3fd66aed3ac39aa76f8981b19628b487e76ca6f8..6cdeb85d73c91603744f0c63de8e50ace8494b7b 100644 GIT binary patch literal 67758 zcmeI53y_t?mB)LS0Y*`g$0Ew2JOl*=<)J|$Auds|6h)RbhNu`LD&iVMQxpP0zz1Q; zA`c@dC?dXCRMgs(DNPYeqd_;DiYscl#EKzqcqf1&D024yyFDNG&CI=X=gys(ZtBl> z=X-RYKL384KHcBf&XwukU3WSC-^DGdEOQ;4bEgVA#mU%te#ZXrKf?b(13?2p13?2p z15H%}&6M%>Rp56BO-lp4l-(&+;CBd3Lj#?Z&1u3PgzZ&$Pscd|W^)LQQv=YoF;@&S{jgdGbzx=PZi!0(%bsuPce7Jz2@$E z)7+A6=AJfl&j=6y&D`vF%zgi7=DMAgS`PSor|^z&k3f5n7*b_OycT{JM*}^S%niay z;V(imhbNqB?)(YnZh6w&Lp#hpE6&#lPgdbSbmZLfJ?198VD6%c>KFPX$Km7LrnuJ# zHw&rdK&L_|q6X;Wj}y)p77DwCIA`?nZB8)PceuH6%f;<(bI*%o>iZK}z_sUu6CX+JFV7mz80y4L`0J$CtrLUNsllatOg zH+GS^1%LAV!R=E*D#z9R%`=pPM?N>V=+EYET4C;#b2F8J&m_l2VVMy61Sz480=m6Z zctuFJ?M_sEzx*fWrmi!$bi420srQ9I%r;P4Fyl9}AMY{Ob8x0|KwGd*m@NzvLMV&| zdMcVR!sEgYA+GzfGIQ8=L$5P8ex3QyHn{D1bHi>lN13Z=5tqly_PJzyP8ciTf51)&AvXD8A%vP~;0)bHf4WmRAjG-HPsbR2!V~5m-0FEq z?aSflvHj+L@N;vF`FS22J{Rx|&k_f5MP(~7Ey}=BjVC8QE8Ad*x$?Ae!q-ZBm+-Ve zSq|aw8tA9XjOBhO(4WS6=Qq4xv(UHajMb@=nRtouzt7u&|Ijr#j?_30_czaCOFpK! zvZaFz*ogB!GXhO#8L$&}r(MzHJU4L~>r}$6gku2~E8m(8n{DyG+2gqmIP&MIVp;&$-gv zt*gbsm*!rm(sjghwqpkVk2>lo8$EioEnmLeUVQOITeD_O5;(^-?&02;EH&XjQ3ldQ zM$S!sS$4-*bDesb!yl2DH{YkN)w$6^Z=osq4zP!h5ayU=AW+`~Zz{@b=~YtyGsx2;>Z zW}`oLjeEE^CRe`vBSR$jO0`?eG2X6naOTD86UvhF>)bx2wN-dZ_>Rz6!#T>>{lXhU zx^0KugsnB}x8@$*>&M%N^VLUnEz|KZ{BPN^B`g12D;WQgT*!j&fU9xs@7cN48z0zkH6R#D63| zGBLM=UkV%Xk{^3NB7I$I8|ZzU*uw*ahUhnFF1cAdb%}s4>?n0e-=zvlDGd|JM?T_Vrfem4~UBl(e+c}m*DS?lHF zT;RtFl~sO-cpFMTx?Y$o&>qxhpHQjvdkfbK*mlY7d`qpDU=7tjjgxKns^=T~hrCAl zuB&51<3G{^a#J^$Uz+)rUx&tehRUOpb?bHgE9rcKz`Xd;La{=H5^5t{Ea1ybmW}?s zbw}ThVdv7n=eqtT(nr3>hRT1GUu?tJ9JY>(hX;=_*S1rpHR|a8b%FWu_5$40Wg!0H z!hZ@|1?D)D`R_Nv96m4RXBf|sr(FFvksk6rHgx_Ib;jKDBcJ)clX8&$o&tRW>mq0m zMhbP+C(z$KBruPKJrd^~pY1@6YTl62vUmwTD#DAne z@)YUz-v2iD-TSn*`9i-ACb2I1-`I#h5ooKA5poSj>Va;;4}@2RcwOf=cIk2QS+MSk z^>*~{^;zE|9Yx2+$bXc#s6D_=oUM0O@SUw2V@#A9RTKQW(_1^h>I-qkqk2LDcDCeECDQ(Rsn(4HSI zVAnIIcuz>~<5~aCnmGLQ%*`>cMfxC7rc(IbRQSiPM*sK?9^7K?PK~qSDE(apY=aM~ zxP{|vf%j>CD&TYUb)WZS2YuJv^$&U54t{CxOR?TZWv{95ALW-a04My-I%l|oGq{U* z^nN+Gh2w0YgD%V#Sa+#DMQuIv6LmTbQ-%*+w@*nsQ{Ij+*)c+jp|4{#{fBz?Hb4ux$q5TW(zhD0!>VK&J*OS9L;n}r|Iq%2_CK`$Vf+`ye_{M5 zZ&2otJpUi;zhM6b`_KCluqmsvJ4@wy2K!glJieIG_DHdRXRv?jW&F?kNPPQ} z_mPSTzB9zTk$h{jV~_mTfAcJH5EpS4GY_PN?$BYb<)LrxpwB z@waG`xhv`Op&7};~H~*B`=v!u2&o?(h-|ind zy?7Wk!`!7)^qtA!hrW-I7ys-rLmb3KoW;xoIzxAS4-Q@C(hk%&|FQJw`>tKuwH9-3Er-J!#5`_E)+6H0=Ay?@4c4BK^% z-ao9@V`XKfO`bg2HgDdX&HRsD;~wsny;T1|cjz!z|68f>kMEc{1nIK8yxiKfX=7cx zcC{Wodf3S)pIkI_?%df14jgFn=g+t8+qY-spKIL1y*yJ)8PN&4p`AikvGSMi{7VV) z->OwB8$Nuv&6zXDmM&dtix)308Wt>AV9S;*v-RuO+uprRiN|IUsbJF@D(sb4@h1=)Y71l#|b*xyZ^|AM`L7Wi+FMJe##AZaU@(1HJgsj@*v z)R_6l{+l&xmhIWIr$KU44585tI;!>llO@>yw{G3q#*ZIwZ@lq_ef;sq_Q3}q6bt|P8b(P(8(@i#R z+_<9Q+H0@1i4!N<+O=zK_wL=<=#O3F9`5CtV#W)7A(h>JLjnFn-+?$BZMt^I8Cy{YHxyZ*CYihT*@Z!|agWpn@fw9f~5 zDF$wP-rOxunLF=#bH{d!wK;k5&$GlqT*O%n*`qYj8M;G<=n|c#@|^AOg7ROB?>{3m zvJ@MvokM5WJy`qof5bst#98cokX|mlN9BR%STj=F?>`sf`|rqKZ1DYa+RTLf=T-k> z^Y1GE#6_IN&PP7nQw9q4{r9l{L&JLi6T7Q+`#*&JUmD{2e|Rs<{x4zwr-sBo{R7+= zZvQ6@Jzd!Ut+wlLB0H?Ek8qD|TDbk+;GYoof2@uFtuA?obH*ZdxBsIr0sepX3mhth z{a+bV)g`ah#ZCIf+y6DO3`D=f{?FBMR2SEy`d&Z#KS$h0$FTomkI{UvT zmI1~I7Yi>7$#Xhon!jo-zv)?{%uV~X>=V7a$k-&xPo}&U`drNawa<^;tKL7!-~Xd` zSaO|zscnAH6@5JM%=)dl!DGy|)m#woqowNGxz}}WjL=@7eikcKDxu!O^}<>qSvK~D z@7zcG5|7iK!LNFs6z@Eo$`s?d!rj7a!d@XRZ+V5e-shPcGtb=g*SuevJmRCQZ9U2Md2|#^SHK=?A(T=WCSe{G zHVU5!$?~5*%-qdSXin>o{=IhcjeV0%FZrI&z(4OEPMS2yHf-1sb2#_;04I^LsUy zH&5lAzuAlZ6@mKRTxbXZIXerN2+s+$Tk*VAXnkAzlgtem>F3pH_X@eUcn1E<%F1HA zQnzW}u?G{uIj+Gy_r~O{i~3KUXB>}>7xCVqySWxsWiC-q&iz7ij1syCrPkIsMYkwY3c*{%gDbEZ_B4nd(0>VgFL+85dph zV{<(Ro1-oy>cs!1zTY7X5E>%y5<4xNqcislZwP-ElJnhbsJXF={CJ0N-tg_Ce9J+M zfAy(1uQGRHZA5UNTw(x*(fzV87 zjNxo$YMJm?A$hDondtdVbGNQGxA;@9Gup{qeFxl6pYqfH@%+&au#Pqt@C@7&2XW;i z1JAH_gl{?B{*t%pqrN3x{;}=03Qq~&5gL1-Tr;omE#YxtzYuQ=npOBVvG=#lP0&~Y z`-nP}tNy8j@J7AEf0GyVEO8{(=bHRR)=1_%#C!h{zJE`*@4i;vUlOqGS_zG{&MRwa zp+aYR2$uvOL3?ZIQJM=waPx%3GU4{(t$aw&JDfsB+9)@}|NYpzqT zLu-%|@`oLSJv3VAEwmJxa==E!-k~n+65{26-`&ray);=m{iV5w{}#6qbK#~Y^){b- zkbyFZO~2v`Z`)lyO+GKRU+Lrc&y_cP=F@~hLI}wk=%+sy3cnN5ZNzS8>3diU&CT6l zZuuVXJEG3z!&@PqA#KKUk9=-!-iPLHeAFCvY-;)SZQHv#zf=gkO9|Bk+5>Dv_N<|; zjO&y!Bjo_w;K8l=*#(8*o;axcOZ8pQiO-rlWr#U^-HEa~NB>TH_OyV#6GF{2fNgNB zfX)~X(Qd|di*3NSIhj|u=`n93;y*7&TR=&970-^51TA;k5}Sn7mR%?-WI-1wE|mhJNWFyrk))$u5v#6F&Pfri~^ z?!>;Cc&Du5^BgPm5t@=suk6-YV4m|%0pHjbAzlvn4HuXH#N5<%zD=e*fV0}zh>>l_ zeC6`p=4SlH+!gnDzg=qj-dKYkCzhaC@^N6CwwUE7Lv;W?@i)IT<|B)8TB`x zIYyp=e{8!&e^x)ULVf(X$!Ynt@!M?_uvaLfA=JGF7%!hDEE2W}$-FeL)Y=oZ2fY8a zWLxg*kKmm?o;p9_N!gnN<-gH-meh27oqt&8`wLBd3@;TFHPEl}zS8*uW5(pRfU$R* z6U_A;uJ4d9_gpeYKb-%FYs}Fwj>q>q;8K0R`b4d}O0(W{u9@EQGdR zN~q_++Ox#aO@B-k-V)Mn#N$tq{dTXpyWaHu0%JMqJU+$Q@0k1k&%AFTo%fxJ?;YVD zA(i)38FWhs^{j#JO8-9L6M^}qgF?JMckF5IdvpC9BXe^aNeuoe=5Ht`p5Ht`p5Ht`p5Ht`p5Ht`p O5Ht`p5HwIA4g5bRh!2ba literal 46215 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%0D^%`YSjm=J zz7JxG_`PRt+RBMV7eyItykjFJ3?~gYDHlj_(aL44i3`MG*1#-T^f782KUF~&Id!BQ zH{RCJnB%L}f(`_FTWl2E>k@NK-J{FuTa$>N3?N;zm1@p)oqItKGd!PDZ2G3WF9(#s zl3+vdXpIk?{K3mD2>BrxI3-NN(;?!7M-L+D5#QSy+ndmiHrPpkWnRk|)r(yxNPkhKetV9afU=5CAb;`Q%TkKY z%sCkm9_p;S@>R|`^2p#dociE#a8SmDDZz6>-L?tKO+0vWRjXGiUa#iQTT-}nVMAGL zDZ@u8Y>4+O&C~v<-$D^%LpPkZm=Y+CnBP9S>rZuW)rbc-{MHqcpP>oSc{xMm?YK9k z4Opk3ny@ITkG7bKcF#QJ+Y$7>bz!(<4%DG`n@ISx{9t$GwV$re8|^H@UJ={&ZGNi69Hi6#qFC`5XyyjP)~+PXBB>PbBJBN z$v%XKmg%H`bp6t;^GQ%ur#liYbJ67pjvnHyi%M%TEOJN7a-gAw8z!-mW3(RNpY@Vi8286I?)uyh?|c^{`UzSWaiHB}A0)!wi&ldDYJ zA!dw7I&^(SQ+G_0+a{+cWEZnvj;f--aHriKK{&^upoCh36tWm&FmlP1q`RSGoO7aI zU*{!3jgqvbCo(iBl>hXIP;(f7fQxUq^Y_Cp_`IH)e>8~n9z-pH{s~g3GfE};wud2o zj!u5Q>N{6(OhhbHU*ART`uOFIxV4`k5G3KB7f_i`WKzN$A6{|>J~$sW#7Y9ch1}6T ztSPbakIg&MQ_RLbi?rA&Z>G>R$U7u118FVT4 z;c)E^W2~nT#Ae5`B9op?g{bR~q-?CJ8q1w*9O>3%{aDO<_sVE^RzPvQn%I#VjTGiI zX$oTo^GoJEIZmHcr^?~pa1|eH-W^gbu=3lS(Dz6sK3qoktrFwBPXNEC5bco$_US7n z$o@%+z@q}onCrClKW9$+od@5 zlTGg9wWh~8s#6WZrV*w*bmxg<=UBnm-SGI*o>G~$s$2vw>)U<<;(g-gh^8Y#p7g@S zVoUqrPf3H+=%&Cvtrue app.manifest true + Assets\crosslang.ico @@ -26,6 +27,7 @@ + diff --git a/CrossLangDevStudio.sln b/CrossLangDevStudio.sln new file mode 100644 index 0000000..67a9c16 --- /dev/null +++ b/CrossLangDevStudio.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossLangDevStudio", "CrossLangDevStudio.csproj", "{07F003F4-ED31-462B-8711-5BE508A4CD35}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {07F003F4-ED31-462B-8711-5BE508A4CD35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07F003F4-ED31-462B-8711-5BE508A4CD35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07F003F4-ED31-462B-8711-5BE508A4CD35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07F003F4-ED31-462B-8711-5BE508A4CD35}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/CrossLangFile.cs b/CrossLangFile.cs index ddcaf9d..18463ee 100644 --- a/CrossLangFile.cs +++ b/CrossLangFile.cs @@ -127,7 +127,7 @@ public class CrossLangFile public Bitmap GetIcon(int height=128) { - using (Stream strm = (Icon > -1 && Icon < Resources.Count) ? new MemoryStream(Resources[Icon], false) : AssetLoader.Open(new Uri("avares://CrossLangDevStudio/Assets/crosslang.ico"))) + using (Stream strm = (Icon > -1 && Icon < Resources.Count) ? new MemoryStream(Resources[Icon], false) : AssetLoader.Open(new Uri("avares://CrossLangDevStudio/Assets/crosslang.png"))) { return Bitmap.DecodeToHeight(strm, height); } @@ -210,7 +210,7 @@ public class CrossLangDependency { [JsonProperty("name")] public string Name { get; set; } = ""; - [JsonProperty("verson")] + [JsonProperty("version")] public CrossLangVersion Version { get; set; } } diff --git a/CrossLangShell.cs b/CrossLangShell.cs index 9ed29c6..1ff5aaa 100644 --- a/CrossLangShell.cs +++ b/CrossLangShell.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Text.Json.Serialization; using System.Threading.Tasks; using Newtonsoft.Json; @@ -14,6 +16,69 @@ namespace CrossLangDevStudio; class CrossLangShell { + public static string PackageServerFile = Path.Combine(CrossLangConfigDirectory, "package_servers.json"); + public static List GetServers() + { + if (File.Exists(PackageServerFile)) + { + var res = JsonConvert.DeserializeObject>(File.ReadAllText(PackageServerFile)); + if (res is not null) return res; + } + return ["https://cpkg.tesseslanguage.com/"]; + } + public static string[] ProjectTypes { get; } = ["console", "lib", "webapp", "template", "compile_tool", "tool", "archive"]; + public static string[] ProjectProperTypes { get; } = ["Console Application","Library","Web Application","Template","Compile Tool","Tool","Archive"]; + private static void EscapeBashElement(StringBuilder builder, string val) + { + builder.Append("\""); + foreach (var item in val) + { + switch (item) + { + case '$': + case '`': + case '\\': + case '\"': + case '!': + builder.Append('\\'); + builder.Append(item); + break; + default: + builder.Append(item); + break; + } + } + builder.Append("\""); + } + private static string EscapeBashCommand(string name, string[] args) + { + StringBuilder builder = new StringBuilder(); + EscapeBashElement(builder,name); + foreach (var item in args) + { + builder.Append(' '); + EscapeBashElement(builder,item); + } + builder.Append(" ; read -p \"Press Enter to close.\""); + return builder.ToString(); + } + + private static string EscapeMacBashCommand(string workDir, bool keepOpen,string name, string[] args) + { + StringBuilder builder = new StringBuilder(); + builder.Append("cd "); + EscapeBashElement(builder, workDir); + builder.Append(" ; "); + EscapeBashElement(builder,name); + foreach (var item in args) + { + builder.Append(' '); + EscapeBashElement(builder,item); + } + if (keepOpen) + builder.Append(" ; read -p \"Press Enter to close.\""); + return builder.ToString(); + } private static CrossLangSettings? settings = null; public static CrossLangSettings Settings { @@ -88,7 +153,15 @@ class CrossLangShell } public static string GetRealPath(string exec) { - foreach (var item in (Environment.GetEnvironmentVariable("PATH") ?? "").Split(Path.PathSeparator)) + var paths = (Environment.GetEnvironmentVariable("PATH") ?? "").Split(Path.PathSeparator).ToList(); + var execDir = Environment.ProcessPath; + if (execDir is not null) + { + var dir = Path.GetDirectoryName(execDir); + if(dir is not null) + paths.Add(dir); + } + foreach (var item in paths) { var path = Path.Combine(item, exec); if (File.Exists(path)) return path; @@ -154,6 +227,10 @@ class CrossLangShell { string konsole = GetRealPath("konsole"); string gnome_terminal = GetRealPath("gnome-terminal"); + string mate_terminal = GetRealPath("mate-terminal"); + string lxterminal = GetRealPath("lxterminal"); + string deepin_terminal = GetRealPath("deepin-terminal"); + string xfce_terminal = GetRealPath("xfce4-terminal"); string xterm = GetRealPath("xterm"); if (File.Exists(konsole)) @@ -175,28 +252,185 @@ class CrossLangShell } process.Start(); } + if (File.Exists(xfce_terminal)) + { + using Process process = new Process(); + process.StartInfo.WorkingDirectory = workingDirectory; + process.StartInfo.FileName = xfce_terminal; + process.StartInfo.UseShellExecute = false; + if (commandName.Length != 0) + { + if (keepOpen) + process.StartInfo.ArgumentList.Add("--hold"); + process.StartInfo.ArgumentList.Add("-e"); + process.StartInfo.ArgumentList.Add(commandName); + foreach (var arg in args) + { + process.StartInfo.ArgumentList.Add(arg); + } + } + process.Start(); + } else if (File.Exists(gnome_terminal)) { + using Process process = new Process(); + process.StartInfo.WorkingDirectory = workingDirectory; + process.StartInfo.FileName = gnome_terminal; + process.StartInfo.UseShellExecute = false; + if (commandName.Length != 0) + { + process.StartInfo.ArgumentList.Add("--"); + if (keepOpen) + { + process.StartInfo.ArgumentList.Add("bash"); + process.StartInfo.ArgumentList.Add("-c"); + process.StartInfo.ArgumentList.Add(EscapeBashCommand(commandName, args)); + } + else + { + process.StartInfo.ArgumentList.Add(commandName); + foreach (var arg in args) + { + process.StartInfo.ArgumentList.Add(arg); + } + } + } + process.Start(); + } + else if (File.Exists(mate_terminal)) + { + using Process process = new Process(); + process.StartInfo.WorkingDirectory = workingDirectory; + process.StartInfo.FileName = mate_terminal; + process.StartInfo.UseShellExecute = false; + if (commandName.Length != 0) + { + process.StartInfo.ArgumentList.Add("--"); + if (keepOpen) + { + process.StartInfo.ArgumentList.Add("bash"); + process.StartInfo.ArgumentList.Add("-c"); + process.StartInfo.ArgumentList.Add(EscapeBashCommand(commandName, args)); + } + else + { + process.StartInfo.ArgumentList.Add(commandName); + foreach (var arg in args) + { + process.StartInfo.ArgumentList.Add(arg); + } + } + } + process.Start(); + } + else if (File.Exists(lxterminal)) + { + using Process process = new Process(); + process.StartInfo.WorkingDirectory = workingDirectory; + process.StartInfo.FileName = lxterminal; + process.StartInfo.UseShellExecute = false; + if (commandName.Length != 0) + { + process.StartInfo.ArgumentList.Add("-e"); + if (keepOpen) + { + process.StartInfo.ArgumentList.Add("bash"); + process.StartInfo.ArgumentList.Add("-c"); + process.StartInfo.ArgumentList.Add(EscapeBashCommand(commandName, args)); + } + else + { + process.StartInfo.ArgumentList.Add(commandName); + foreach (var arg in args) + { + process.StartInfo.ArgumentList.Add(arg); + } + } + } + process.Start(); + } + + else if (File.Exists(deepin_terminal)) + { + using Process process = new Process(); + process.StartInfo.WorkingDirectory = workingDirectory; + process.StartInfo.FileName = deepin_terminal; + process.StartInfo.UseShellExecute = false; + if (commandName.Length != 0) + { + if (keepOpen) + process.StartInfo.ArgumentList.Add("--keep-open"); + process.StartInfo.ArgumentList.Add("-e"); + process.StartInfo.ArgumentList.Add(commandName); + foreach (var arg in args) + { + process.StartInfo.ArgumentList.Add(arg); + } + } + process.Start(); } else if (File.Exists(xterm)) { - + using Process process = new Process(); + process.StartInfo.WorkingDirectory = workingDirectory; + process.StartInfo.FileName = xterm; + process.StartInfo.UseShellExecute = false; + if (commandName.Length != 0) + { + if (keepOpen) + process.StartInfo.ArgumentList.Add("-hold"); + process.StartInfo.ArgumentList.Add("-e"); + process.StartInfo.ArgumentList.Add(commandName); + foreach (var arg in args) + { + process.StartInfo.ArgumentList.Add(arg); + } + } + process.Start(); } } else if (OperatingSystem.IsWindows()) { using Process process = new Process(); process.StartInfo.FileName = GetRealPath("cmd.exe"); - if(commandName.Length > 0) - process.StartInfo.ArgumentList.Add(keepOpen ? "/K" : "/C"); + if (commandName.Length > 0) + process.StartInfo.ArgumentList.Add(keepOpen ? "/K" : "/C"); process.StartInfo.CreateNoWindow = false; process.StartInfo.UseShellExecute = false; process.StartInfo.WorkingDirectory = workingDirectory; - if(commandName.Length > 0) - process.StartInfo.ArgumentList.Add(commandName); - foreach (var item in args) - process.StartInfo.ArgumentList.Add(item); + if (commandName.Length > 0) + process.StartInfo.ArgumentList.Add(commandName); + if (commandName.Length > 0) + foreach (var item in args) + process.StartInfo.ArgumentList.Add(item); + process.Start(); + } + else if (OperatingSystem.IsMacOS()) + { + using Process process = new Process(); + process.StartInfo.WorkingDirectory = workingDirectory; + process.StartInfo.FileName = GetRealPath("open"); + process.StartInfo.UseShellExecute = false; + if (commandName.Length != 0) + { + process.StartInfo.ArgumentList.Add("-b"); + process.StartInfo.ArgumentList.Add("com.apple.terminal"); + if (keepOpen) + { + process.StartInfo.ArgumentList.Add("bash"); + process.StartInfo.ArgumentList.Add("-c"); + process.StartInfo.ArgumentList.Add(EscapeMacBashCommand(workingDirectory, keepOpen, commandName, args)); + } + else + { + process.StartInfo.ArgumentList.Add(commandName); + foreach (var arg in args) + { + process.StartInfo.ArgumentList.Add(arg); + } + } + } process.Start(); } } diff --git a/Messages/InstallPackageMessage.cs b/Messages/InstallPackageMessage.cs new file mode 100644 index 0000000..6931bf4 --- /dev/null +++ b/Messages/InstallPackageMessage.cs @@ -0,0 +1,12 @@ +using CrossLangDevStudio.ViewModels; +using CommunityToolkit.Mvvm.Messaging.Messages; +using System.Net.Http; + +namespace CrossLangDevStudio.Messages; + +public class InstallPackageMessage(HttpClient client,string name, string server) : AsyncRequestMessage +{ + public HttpClient Client => client; + public string Name => name; + public string Server => server; +} \ No newline at end of file diff --git a/Messages/InstallPackageResponseMessage.cs b/Messages/InstallPackageResponseMessage.cs new file mode 100644 index 0000000..66ed5d0 --- /dev/null +++ b/Messages/InstallPackageResponseMessage.cs @@ -0,0 +1,9 @@ +using CrossLangDevStudio.ViewModels; +using CommunityToolkit.Mvvm.Messaging.Messages; + +namespace CrossLangDevStudio.Messages; + +public class InstallPackageResponseMessage(CrossLangVersion? version) : AsyncRequestMessage +{ + public CrossLangVersion? Version => version; +} \ No newline at end of file diff --git a/Models/IPackageManager.cs b/Models/IPackageManager.cs new file mode 100644 index 0000000..829fa5c --- /dev/null +++ b/Models/IPackageManager.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using Avalonia.Controls; + +namespace CrossLangDevStudio.Models; + +public interface IPackageManager +{ + Task InstallPackageAsync(string name, CrossLangVersion version); + string Filter { get; } + void UninstallPackage(string name); + + IEnumerable GetInstalledPackages(); +} + +public static class PackageManagerExtensions +{ + public static string Summarize(this string txt) + { + var lines = txt.Replace("\r", "").Split('\n'); + bool needsthreedots = false; + if (lines.Length > 1) needsthreedots = true; + if (lines.Length == 0) return ""; + if (lines[0].Length > 120) { needsthreedots = true; lines[0] = lines[0].Substring(0, 120); } + if (needsthreedots) return $"{lines[0]}..."; + return lines[0]; + } + public static bool IsPackageInstalled(this IPackageManager pm, string name) + { + foreach (var item in pm.GetInstalledPackages()) + { + if (item.Name == name) return true; + } + return false; + } + public static async Task InstallPackageAsync(this IPackageManager pm, CrossLangDependency dep) + { + await pm.InstallPackageAsync(dep.Name, dep.Version); + } +} \ No newline at end of file diff --git a/Models/ProjectPackageManager.cs b/Models/ProjectPackageManager.cs new file mode 100644 index 0000000..d55a095 --- /dev/null +++ b/Models/ProjectPackageManager.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Threading.Tasks; +using Avalonia.Controls; +using Newtonsoft.Json; + +namespace CrossLangDevStudio.Models; + +public class ProjectPackageManager : IPackageManager +{ + public ProjectPackageManager(string path) + { + Path = path; + } + public string Path { get; init; } + public string Filter => "lib,compile_tool"; + + public IEnumerable GetInstalledPackages() + { + var cfg = JsonConvert.DeserializeObject(File.ReadAllText(Path)); + if (cfg is not null) + { + foreach (var item in cfg.Dependencies) + { + yield return item; + } + } + } + + public async Task InstallPackageAsync(string name, CrossLangVersion version) + { + var cfg = JsonConvert.DeserializeObject(await File.ReadAllTextAsync(Path)) ?? new CrossLangConfig(); + cfg.Dependencies ??= new List(); + bool has = false; + foreach (var item in cfg.Dependencies) + { + if (item.Name == name) + { + has = true; + if(version.CompareTo(item.Version) > 0) + item.Version = version; + break; + } + } + if (!has) cfg.Dependencies.Add(new CrossLangDependency { Name = name, Version = version }); + await File.WriteAllTextAsync(Path, JsonConvert.SerializeObject(cfg, Formatting.Indented, new JsonSerializerSettings(){NullValueHandling= NullValueHandling.Ignore})); + + } + + + + public void UninstallPackage(string name) + { + var cfg = JsonConvert.DeserializeObject(File.ReadAllText(Path)) ?? new CrossLangConfig(); + cfg.Dependencies ??= new List(); + + foreach (var item in cfg.Dependencies) + { + if (item.Name == name) + { + cfg.Dependencies.Remove(item); + + break; + } + } + File.WriteAllText(Path, JsonConvert.SerializeObject(cfg, Formatting.Indented,new JsonSerializerSettings(){NullValueHandling= NullValueHandling.Ignore})); + } +} \ No newline at end of file diff --git a/Packaging/Linux/build.sh b/Packaging/Linux/build.sh index e69de29..bd63f93 100644 --- a/Packaging/Linux/build.sh +++ b/Packaging/Linux/build.sh @@ -0,0 +1,15 @@ +#!/bin/bash +mkdir build +cd build +mkdir crosslang-devstudio_1.0.0_amd64 +cd crosslang-devstudio_1.0.0_amd64 +dotnet publish -c Release -r linux-x64 -o opt/CrossLangDevStudio -p:PublishReadyToRun=true -p:PublishSingleFile=true --self-contained ../../../../CrossLangDevStudio.csproj +mkdir DEBIAN +mkdir -p usr/share/applications +mkdir -p usr/bin +cp ../../debian/control-amd64 DEBIAN/control +cp ../../crosslang-devstudio.desktop usr/share/applications/ +cp ../../crossdev usr/bin/crossdev +chmod 755 usr/bin/crossdev +cd .. +dpkg-deb --build crosslang-devstudio_1.0.0_amd64/ diff --git a/Packaging/Linux/crossdev b/Packaging/Linux/crossdev new file mode 100644 index 0000000..60c7e1d --- /dev/null +++ b/Packaging/Linux/crossdev @@ -0,0 +1,2 @@ +#!/bin/bash +/opt/CrossLangDevStudio/CrossLangDevStudio "$@" \ No newline at end of file diff --git a/Packaging/Linux/crosslang-devstudio.desktop b/Packaging/Linux/crosslang-devstudio.desktop new file mode 100644 index 0000000..951c3e0 --- /dev/null +++ b/Packaging/Linux/crosslang-devstudio.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Version=1.0 +Name=CrossLang DevStudio +Exec=crossdev +Icon=crosslang +Categories=Education;Languages;Programming +Comment=IDE For CrossLang \ No newline at end of file diff --git a/Packaging/Linux/debian/control-amd64 b/Packaging/Linux/debian/control-amd64 new file mode 100644 index 0000000..542c9eb --- /dev/null +++ b/Packaging/Linux/debian/control-amd64 @@ -0,0 +1,8 @@ +Package: crosslang-devstudio +Version: 1.0.0 +Architecture: amd64 +Essential: no +Priority: optional +Depends: crosslang +Maintainer: Mike Nolan +Description: IDE for CrossLang diff --git a/Packaging/Linux/push.sh b/Packaging/Linux/push.sh new file mode 100644 index 0000000..2cbe2f8 --- /dev/null +++ b/Packaging/Linux/push.sh @@ -0,0 +1,6 @@ +#!/bin/bash +curl --user tesses50:$GITEA_AUTH -X DELETE \ + https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/jammy/main/crosslang-devstudio/1.0.0/amd64 +curl --user tesses50:$GITEA_AUTH \ + --upload-file build/crosslang-devstudio_1.0.0_amd64.deb \ + https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/jammy/main/upload diff --git a/Packaging/Windows/build.sh b/Packaging/Windows/build.sh index f664233..a972c2a 100644 --- a/Packaging/Windows/build.sh +++ b/Packaging/Windows/build.sh @@ -5,7 +5,7 @@ git clone https://onedev.site.tesses.net/crosslang cd crosslang mkdir build cd build -cmake -S .. -B . -DCMAKE_TOOLCHAIN_FILE=../../../Toolchain.cmake -DTESSESFRAMEWORK_ENABLE_SHARED=OFF -DTESSESFRAMEWORK_FETCHCONTENT=ON +cmake -S .. -B . -DCMAKE_TOOLCHAIN_FILE=../../../Toolchain.cmake -DTESSESFRAMEWORK_ENABLE_SHARED=OFF -DTESSESFRAMEWORK_ENABLE_STATIC=ON -DTESSESFRAMEWORK_ENABLE_APPS=OFF -DTESSESFRAMEWORK_ENABLE_EXAMPLES=OFF -DTESSESFRAMEWORK_FETCHCONTENT=ON make -j`nproc` mkdir -p ../../package/bin mkdir -p ../../package/share/Tesses/CrossLang diff --git a/ViewModels/InstallPackageDialogViewModel.cs b/ViewModels/InstallPackageDialogViewModel.cs new file mode 100644 index 0000000..6f73790 --- /dev/null +++ b/ViewModels/InstallPackageDialogViewModel.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Threading.Tasks; +using System.Web; +using Avalonia.Markup.Xaml.Templates; +using Avalonia.Media; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using CrossLangDevStudio.Messages; +using CrossLangDevStudio.Views; +using Newtonsoft.Json; + +namespace CrossLangDevStudio.ViewModels; + +public partial class InstallPackageDialogViewModel : ViewModelBase +{ + public InstallPackageMessage Message { get; } + [ObservableProperty] + private int _versionIndex = 0; + public ObservableCollection Versions { get; } = new ObservableCollection(); + + public InstallPackageDialogViewModel(InstallPackageMessage msg) + { + Message = msg; + + Task.Run(async () => + { + var res = await Message.Client.GetStringAsync($"{msg.Server.TrimEnd('/')}/api/v1/versions?name={HttpUtility.UrlEncode(msg.Name)}"); + if (!string.IsNullOrWhiteSpace(res)) + { + var res2 = JsonConvert.DeserializeObject(res); + if (res2 is not null && res2.Success) + { + foreach (var res3 in res2.Versions) + { + Versions.Add(res3.ToObject()); + } + VersionIndex = 0; + } + } + }); + } + [RelayCommand] + private void Install() + { + if(VersionIndex < Versions.Count) + WeakReferenceMessenger.Default.Send(new InstallPackageResponseMessage(Versions[VersionIndex].Version)); + } + [RelayCommand] + private void Cancel() + { + WeakReferenceMessenger.Default.Send(new InstallPackageResponseMessage(null)); + } + public partial class VersionObject(CrossLangVersion version, DateTime date) : ObservableObject + { + public CrossLangVersion Version => version; + [ObservableProperty] + private string _text = $"{version} at {date.ToShortDateString()}"; + } + + public class VersionsMessage + { + [JsonProperty("success")] + public bool Success { get; set; } + [JsonProperty("versions")] + public List Versions { get; set; } = new(); + + public class VersionMessage + { + [JsonProperty("version")] + public CrossLangVersion Version { get; set; } + + [JsonProperty("uploadTime")] + public long UploadTime { get; set; } = 0; + + public VersionObject ToObject() + { + + return new VersionObject(Version, DateTimeOffset.FromUnixTimeSeconds(UploadTime).LocalDateTime); + } + + + } + } +} \ No newline at end of file diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs index 714480b..e1b5e55 100644 --- a/ViewModels/MainWindowViewModel.cs +++ b/ViewModels/MainWindowViewModel.cs @@ -122,7 +122,12 @@ public partial class MainWindowViewModel : ViewModelBase OpenProjectConfig(); }), - new ProjectFileNode("Packages") + new ProjectFileNode("Packages", ()=>{ + OpenProjectPackages(); + }), + new ProjectFileNode("Project Dependencies",()=>{ + + }) ]) }; @@ -151,6 +156,33 @@ public partial class MainWindowViewModel : ViewModelBase ProjectFiles.Add(new ProjectFileNode(Path.GetFileName(obj), entries)); } + private void OpenProjectPackages() + { + if (Directory.Exists(CurrentProject)) + { + var config = Path.Combine(CurrentProject, "cross.json"); + if (File.Exists(config)) + { + foreach (var item in TabItems) + { + if (item.Body is PackageManagerViewModel vm) + { + if (vm.Packages is ProjectPackageManager) + { + SelectedTab = item; + return; + } + } + } + AddTab(new TabItemViewModel() + { + Header = "Project Packages", + Body = new PackageManagerViewModel(this, new ProjectPackageManager(config)) + }); + } + } + } + private void OpenProjectConfig() { @@ -161,6 +193,7 @@ public partial class MainWindowViewModel : ViewModelBase { if (item.Body is ProjectConfigurationViewModel model && model.FilePath == config) { + SelectedTab = item; return; } } diff --git a/ViewModels/NewProjectDialogViewModel.cs b/ViewModels/NewProjectDialogViewModel.cs index 1d1a076..6abf493 100644 --- a/ViewModels/NewProjectDialogViewModel.cs +++ b/ViewModels/NewProjectDialogViewModel.cs @@ -39,7 +39,7 @@ public partial class NewProjectDialogViewModel : ViewModelBase int i; for (i = 1; i < int.MaxValue; i++) { - string name2 = $"{name}{i}"; + string name2 = $"{newName}{i}"; if (!Directory.Exists(Path.Combine(ParentDirectory, name2))) { Name = name2; diff --git a/ViewModels/PackageManagerViewModel.cs b/ViewModels/PackageManagerViewModel.cs new file mode 100644 index 0000000..f58f10d --- /dev/null +++ b/ViewModels/PackageManagerViewModel.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Threading.Tasks; +using System.Web; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform.Storage; + +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using CrossLangDevStudio.Messages; +using CrossLangDevStudio.Models; +using CrossLangDevStudio.Views; +using Newtonsoft.Json; + +namespace CrossLangDevStudio.ViewModels; + + +public partial class PackageManagerViewModel : ViewModelBase +{ + public IPackageManager Packages { get; } + + public ObservableCollection PackageList { get; } = new ObservableCollection(); + + public ObservableCollection Servers { get; } = new ObservableCollection(); + + [ObservableProperty] + private int _serverIndex = 0; + + [ObservableProperty] + private string _search = ""; + + [ObservableProperty] + private int _page = 1; + + public MainWindowViewModel Main { get; } + + + public PackageManagerViewModel(MainWindowViewModel mvm, IPackageManager pm) + { + Main = mvm; + Packages = pm; + foreach(var svr in CrossLangShell.GetServers()) + this.Servers.Add(svr); + } + + [RelayCommand] + private async Task SearchPackagesAsync() + { + var svr = Servers[ServerIndex].TrimEnd('/'); + var body = await Main.Client.GetStringAsync($"{svr}/api/v1/search?q={HttpUtility.UrlEncode(Search)}&filter={HttpUtility.UrlEncode(Packages.Filter)}&offset={Page-1}&limit=20"); + var res = JsonConvert.DeserializeObject(body); + PackageList.Clear(); + if (res is not null) + { + + foreach (var item in res.Packages) + { + Package pkg = new Package(); + var date = DateTimeOffset.FromUnixTimeSeconds(item.UploadTime); + var date2 = date.ToLocalTime().DateTime; + + pkg.Info = $"Version: {item.Version}\tAccount: {item.AccountName}\tUpdated: {date2.ToShortDateString()}\tLicense: {item.License}\tType: {item.Type}"; + + pkg.Version = item.Version; + pkg.Name = item.Name; + pkg.Description = item.Description.Summarize(); + pkg.IsInstalled = Packages.IsPackageInstalled(item.Name); + pkg.Install = async() => + { + var resp = await WeakReferenceMessenger.Default.Send(new InstallPackageMessage(Main.Client, item.Name, svr)); + if (resp.HasValue) + { + await Packages.InstallPackageAsync(item.Name, resp.Value); + pkg.IsInstalled = true; + } + }; + pkg.Uninstall = () => + { + Packages.UninstallPackage(item.Name); + pkg.IsInstalled = false; + }; + + + byte[] data = await Main.Client.GetByteArrayAsync($"{svr}/api/v1/package_icon.png?name={HttpUtility.UrlEncode(item.Name)}&version={item.Version.ToString()}"); + using var ms = new MemoryStream(data, false); + pkg.Icon = Bitmap.DecodeToHeight(ms, 64); + PackageList.Add(pkg); + } + } + } + + public class SearchPackage + { + [JsonProperty("packageName")] + public string Name { get; set; } = ""; + + [JsonProperty("version")] + public CrossLangVersion Version { get; set; } + + [JsonProperty("uploadTime")] + public long UploadTime { get; set; } + + [JsonProperty("license")] + public string License { get; set; } = ""; + + [JsonProperty("type")] + public string Type { get; set; } = ""; + [JsonProperty("accountName")] + public string AccountName { get; set; } = ""; + [JsonProperty("description")] + public string Description { get; set; } = ""; + } + + public partial class Package : ObservableObject + { + [ObservableProperty] + private string _name = ""; + + public CrossLangVersion Version { get; set; } + public Func? Install { get; set; } + public Func? GetInfo { get; set; } + + [ObservableProperty] + private string _info = ""; + [ObservableProperty] + private string _description = ""; + + [ObservableProperty] + private IImage? _icon = null; + + [ObservableProperty] + private string _mainButtonText = ""; + + [ObservableProperty] + private string _subButton1Text = ""; + private bool _isInstalled = false; + public bool IsInstalled + { + get => _isInstalled; + set + { + SetProperty(ref _isInstalled, value, nameof(IsInstalled)); + + MainButtonText = value ? "Uninstall" : "Install"; + SubButton1Text = value ? "Change Version" : "Info"; + } + } + + public Action? Uninstall { get; set; } + + [RelayCommand] + private async Task MainButtonAsync() + { + /*var i = Install; + if (i is not null) await i();*/ + + if (_isInstalled) + { + Uninstall?.Invoke(); + } + else + { + var i = Install; + if (i is not null) await i(); + } + } + + [RelayCommand] + private async Task SubButton1Async() + { + if (_isInstalled) + { + var i = Install; + if (i is not null) await i(); + } + else + { + var i = GetInfo; + if (i is not null) await i(); + } + } + + [RelayCommand] + private async Task SubButton2Async() + { + var i = GetInfo; + if (i is not null) await i(); + } + } + + public class SearchResult + { + [JsonProperty("packages")] + public List Packages { get; set; } = new List(); + } +} diff --git a/ViewModels/ProjectConfigurationViewModel.cs b/ViewModels/ProjectConfigurationViewModel.cs index 8f75f5b..b295207 100644 --- a/ViewModels/ProjectConfigurationViewModel.cs +++ b/ViewModels/ProjectConfigurationViewModel.cs @@ -26,7 +26,6 @@ public partial class ProjectConfigurationViewModel : ViewModelBase, ISavable Tool Archive */ - static readonly string[] types = ["console","lib","app","template","compile_tool","tool","archive"]; TabItemViewModel tab; public string FilePath { get; } bool _modified = false; @@ -186,7 +185,7 @@ public partial class ProjectConfigurationViewModel : ViewModelBase, ISavable _license = config?.Info?.License ?? ""; _templatename = config?.Info?.TemplateName ?? ""; _templatenamepretty = config?.Info?.TemplateNamePretty ?? ""; - _type = Array.IndexOf(types, config?.Info?.Type ?? "console"); + _type = Array.IndexOf(CrossLangShell.ProjectTypes, config?.Info?.Type ?? "console"); _type = _type == -1 ? 0 : _type; _description = config?.Info?.Description ?? ""; @@ -210,7 +209,7 @@ public partial class ProjectConfigurationViewModel : ViewModelBase, ISavable config.Info.Repoository = string.IsNullOrWhiteSpace(Repository) ? null : Repository; config.Info.TemplateName = string.IsNullOrWhiteSpace(TemplateName) ? null : TemplateName; config.Info.TemplateNamePretty = string.IsNullOrWhiteSpace(TemplateNamePretty) ? null : TemplateNamePretty; - config.Info.Type = types[Type]; + config.Info.Type = CrossLangShell.ProjectTypes[Type]; File.WriteAllText(FilePath, JsonConvert.SerializeObject(config, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore diff --git a/Views/InstallPackageDialog.axaml b/Views/InstallPackageDialog.axaml new file mode 100644 index 0000000..138e948 --- /dev/null +++ b/Views/InstallPackageDialog.axaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +