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 3fd66ae..6cdeb85 100644
Binary files a/Assets/crosslang.ico and b/Assets/crosslang.ico differ
diff --git a/Assets/crosslang.png b/Assets/crosslang.png
new file mode 100644
index 0000000..0291e4e
Binary files /dev/null and b/Assets/crosslang.png differ
diff --git a/CrossLangDevStudio.csproj b/CrossLangDevStudio.csproj
index da5e124..dcf008b 100644
--- a/CrossLangDevStudio.csproj
+++ b/CrossLangDevStudio.csproj
@@ -6,6 +6,7 @@
true
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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Views/InstallPackageDialog.axaml.cs b/Views/InstallPackageDialog.axaml.cs
new file mode 100644
index 0000000..454ef03
--- /dev/null
+++ b/Views/InstallPackageDialog.axaml.cs
@@ -0,0 +1,21 @@
+using System.Diagnostics;
+using Avalonia.Controls;
+using CommunityToolkit.Mvvm.Messaging;
+using CrossLangDevStudio.Messages;
+
+namespace CrossLangDevStudio.Views;
+
+public partial class InstallPackageDialog : Window
+{
+ public InstallPackageDialog()
+ {
+ InitializeComponent();
+ if (Design.IsDesignMode) return;
+
+ WeakReferenceMessenger.Default.Register(this, static (w, m) =>
+ {
+ w.Close(m.Version);
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/Views/MainWindow.axaml b/Views/MainWindow.axaml
index 1f16f10..30ef252 100644
--- a/Views/MainWindow.axaml
+++ b/Views/MainWindow.axaml
@@ -28,6 +28,7 @@
+
@@ -38,6 +39,23 @@
+
+
+
+
+
+
+
diff --git a/Views/MainWindow.axaml.cs b/Views/MainWindow.axaml.cs
index 5905c2c..2440990 100644
--- a/Views/MainWindow.axaml.cs
+++ b/Views/MainWindow.axaml.cs
@@ -28,6 +28,15 @@ public partial class MainWindow : Window
// Show dialog window and reply with returned AlbumViewModel or null when the dialog is closed.
m.Reply(dialog.ShowDialog(w));
});
+ WeakReferenceMessenger.Default.Register(this, static (w, m) =>
+ {
+ var dialog = new InstallPackageDialog
+ {
+ DataContext = new InstallPackageDialogViewModel(m)
+ };
+
+ m.Reply(dialog.ShowDialog(w));
+ });
WeakReferenceMessenger.Default.Register(this, static (w, m) =>
{
m.Reply(w);
diff --git a/Views/PackageManagerView.axaml b/Views/PackageManagerView.axaml
new file mode 100644
index 0000000..286d5e5
--- /dev/null
+++ b/Views/PackageManagerView.axaml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Views/PackageManagerView.axaml.cs b/Views/PackageManagerView.axaml.cs
new file mode 100644
index 0000000..b5e5eb7
--- /dev/null
+++ b/Views/PackageManagerView.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace CrossLangDevStudio.Views;
+
+public partial class PackageManagerView : UserControl
+{
+ public PackageManagerView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file