Browse Source

Implementing WASM runtime into PixiEditor wip

Krzysztof Krysiński 1 year ago
parent
commit
4779994e00

+ 26 - 0
src/PixiEditor.AvaloniaUI/Models/AppExtensions/DllExtensionEntry.cs

@@ -0,0 +1,26 @@
+using System.Reflection;
+using PixiEditor.Extensions;
+
+namespace PixiEditor.AvaloniaUI.Models.AppExtensions;
+
+public class DllExtensionEntry : ExtensionEntry
+{
+    public Assembly Assembly { get; }
+    public Type ExtensionType { get; }
+    public DllExtensionEntry(Assembly assembly, Type extensionType)
+    {
+        Assembly = assembly;
+        ExtensionType = extensionType;
+    }
+
+    public override Extension CreateExtension()
+    {
+        var extension = (Extension)Activator.CreateInstance(ExtensionType);
+        if (extension is null)
+        {
+            throw new NoClassEntryException(Assembly.Location);
+        }
+
+        return extension;
+    }
+}

+ 8 - 0
src/PixiEditor.AvaloniaUI/Models/AppExtensions/ExtensionEntry.cs

@@ -0,0 +1,8 @@
+using PixiEditor.Extensions;
+
+namespace PixiEditor.AvaloniaUI.Models.AppExtensions;
+
+public abstract class ExtensionEntry
+{
+    public abstract Extension CreateExtension();
+}

+ 2 - 2
src/PixiEditor.AvaloniaUI/Models/AppExtensions/ExtensionException.cs

@@ -10,9 +10,9 @@ public class ExtensionException : RecoverableException
     }
     }
 }
 }
 
 
-public class NoEntryAssemblyException : ExtensionException
+public class NoEntryException : ExtensionException
 {
 {
-    public NoEntryAssemblyException(string containingFolder) : base(new LocalizedString("ERROR_NO_ENTRY_ASSEMBLY", containingFolder))
+    public NoEntryException(string containingFolder) : base(new LocalizedString("ERROR_NO_ENTRY_ASSEMBLY", containingFolder))
     {
     {
     }
     }
 }
 }

+ 65 - 18
src/PixiEditor.AvaloniaUI/Models/AppExtensions/ExtensionLoader.cs

@@ -8,6 +8,7 @@ using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Models.IO;
 using PixiEditor.AvaloniaUI.Models.IO;
 using PixiEditor.Extensions;
 using PixiEditor.Extensions;
 using PixiEditor.Extensions.Metadata;
 using PixiEditor.Extensions.Metadata;
+using PixiEditor.Extensions.WasmRuntime;
 using PixiEditor.Platform;
 using PixiEditor.Platform;
 
 
 namespace PixiEditor.AvaloniaUI.Models.AppExtensions;
 namespace PixiEditor.AvaloniaUI.Models.AppExtensions;
@@ -17,6 +18,8 @@ internal class ExtensionLoader
     private readonly Dictionary<string, OfficialExtensionData> _officialExtensionsKeys = new Dictionary<string, OfficialExtensionData>();
     private readonly Dictionary<string, OfficialExtensionData> _officialExtensionsKeys = new Dictionary<string, OfficialExtensionData>();
     public List<Extension> LoadedExtensions { get; } = new();
     public List<Extension> LoadedExtensions { get; } = new();
 
 
+    private WasmRuntime _wasmRuntime = new WasmRuntime();
+
     public ExtensionLoader()
     public ExtensionLoader()
     {
     {
         ValidateExtensionFolder();
         ValidateExtensionFolder();
@@ -81,10 +84,10 @@ internal class ExtensionLoader
         {
         {
             var metadata = JsonConvert.DeserializeObject<ExtensionMetadata>(json);
             var metadata = JsonConvert.DeserializeObject<ExtensionMetadata>(json);
             string directory = Path.GetDirectoryName(packageJsonPath);
             string directory = Path.GetDirectoryName(packageJsonPath);
-            Assembly entry = GetEntryAssembly(directory, out Type extensionType);
+            ExtensionEntry? entry = GetEntry(directory);
             if (entry is null)
             if (entry is null)
             {
             {
-                throw new NoEntryAssemblyException(directory);
+                throw new NoEntryException(directory);
             }
             }
 
 
             if (!ValidateMetadata(metadata, entry))
             if (!ValidateMetadata(metadata, entry))
@@ -92,7 +95,7 @@ internal class ExtensionLoader
                 return;
                 return;
             }
             }
 
 
-            LoadExtensionFrom(entry, extensionType, metadata);
+            LoadExtensionFrom(entry, metadata);
         }
         }
         catch (JsonException)
         catch (JsonException)
         {
         {
@@ -109,22 +112,35 @@ internal class ExtensionLoader
         }
         }
     }
     }
 
 
-    private void LoadExtensionFrom(Assembly entry, Type extensionType, ExtensionMetadata metadata)
+    private void LoadExtensionFrom(ExtensionEntry entry, ExtensionMetadata metadata)
     {
     {
-        var extension = LoadExtensionEntry(entry, extensionType, metadata);
+        var extension = LoadExtensionEntry(entry, metadata);
         extension.Load();
         extension.Load();
         LoadedExtensions.Add(extension);
         LoadedExtensions.Add(extension);
     }
     }
 
 
-    private Assembly? GetEntryAssembly(string assemblyFolder, out Type extensionType)
+    private ExtensionEntry? GetEntry(string assemblyFolder)
     {
     {
         string[] dlls = Directory.GetFiles(assemblyFolder, "*.dll");
         string[] dlls = Directory.GetFiles(assemblyFolder, "*.dll");
-        Assembly? entryAssembly = GetEntryAssembly(dlls, out extensionType);
+        Assembly? entryAssembly = GetEntryAssembly(dlls, out Type extensionType);
+
+        if (entryAssembly != null)
+        {
+            return new DllExtensionEntry(entryAssembly, extensionType);
+        }
+
+        string[] wasm = Directory.GetFiles(assemblyFolder, "*.wasm");
+        WasmExtensionInstance? entryWasm = GetEntryWasm(wasm);
 
 
-        return entryAssembly;
+        if (entryWasm != null)
+        {
+            return new WasmExtensionEntry(entryWasm);
+        }
+
+        return null;
     }
     }
 
 
-    private bool ValidateMetadata(ExtensionMetadata metadata, Assembly assembly)
+    private bool ValidateMetadata(ExtensionMetadata metadata, ExtensionEntry assembly)
     {
     {
         if (string.IsNullOrEmpty(metadata.UniqueName))
         if (string.IsNullOrEmpty(metadata.UniqueName))
         {
         {
@@ -170,10 +186,28 @@ internal class ExtensionLoader
         return IPlatform.Current.AdditionalContentProvider?.IsContentInstalled(product.Value) ?? false;
         return IPlatform.Current.AdditionalContentProvider?.IsContentInstalled(product.Value) ?? false;
     }
     }
 
 
-    private bool IsOfficialAssemblyLegit(string metadataUniqueName, Assembly assembly)
+    private bool IsOfficialAssemblyLegit(string metadataUniqueName, ExtensionEntry entry)
     {
     {
-        if (assembly == null) return false; // All official extensions must have a valid assembly
+        if (entry == null) return false; // All official extensions must have a valid assembly
         if (!_officialExtensionsKeys.ContainsKey(metadataUniqueName)) return false;
         if (!_officialExtensionsKeys.ContainsKey(metadataUniqueName)) return false;
+
+        if (entry is DllExtensionEntry dllExtensionEntry)
+        {
+            return VerifyAssemblySignature(metadataUniqueName, dllExtensionEntry.Assembly);
+        }
+
+        if (entry is WasmExtensionEntry wasmExtensionEntry)
+        {
+            return true;
+            //TODO: Verify wasm signature
+            //return VerifyAssemblySignature(metadataUniqueName, wasmExtensionEntry.Instance);
+        }
+
+        return false;
+    }
+
+    private bool VerifyAssemblySignature(string metadataUniqueName, Assembly assembly)
+    {
         bool wasVerified = false;
         bool wasVerified = false;
         bool verified = StrongNameSignatureVerificationEx(assembly.Location, true, ref wasVerified);
         bool verified = StrongNameSignatureVerificationEx(assembly.Location, true, ref wasVerified);
         if (!verified || !wasVerified) return false;
         if (!verified || !wasVerified) return false;
@@ -200,14 +234,9 @@ internal class ExtensionLoader
     [DllImport("mscoree.dll", CharSet=CharSet.Unicode)]
     [DllImport("mscoree.dll", CharSet=CharSet.Unicode)]
     static extern bool StrongNameSignatureVerificationEx(string wszFilePath, bool fForceVerification, ref bool pfWasVerified);
     static extern bool StrongNameSignatureVerificationEx(string wszFilePath, bool fForceVerification, ref bool pfWasVerified);
 
 
-    private Extension LoadExtensionEntry(Assembly entryAssembly, Type extensionType, ExtensionMetadata metadata)
+    private Extension LoadExtensionEntry(ExtensionEntry entry, ExtensionMetadata metadata)
     {
     {
-        var extension = (Extension)Activator.CreateInstance(extensionType);
-        if (extension is null)
-        {
-            throw new NoClassEntryException(entryAssembly.Location);
-        }
-
+        Extension extension = entry.CreateExtension();
         extension.ProvideMetadata(metadata);
         extension.ProvideMetadata(metadata);
         return extension;
         return extension;
     }
     }
@@ -235,6 +264,24 @@ internal class ExtensionLoader
         return null;
         return null;
     }
     }
 
 
+    private WasmExtensionInstance? GetEntryWasm(string[] wasmFiles)
+    {
+        foreach (var wasm in wasmFiles)
+        {
+            try
+            {
+                WasmExtensionInstance instance = _wasmRuntime.LoadModule(wasm);
+                return instance;
+            }
+            catch
+            {
+                // ignored
+            }
+        }
+
+        return null;
+    }
+
     private void ValidateExtensionFolder()
     private void ValidateExtensionFolder()
     {
     {
         if (!Directory.Exists(Paths.ExtensionsFullPath))
         if (!Directory.Exists(Paths.ExtensionsFullPath))

+ 20 - 0
src/PixiEditor.AvaloniaUI/Models/AppExtensions/WasmExtensionEntry.cs

@@ -0,0 +1,20 @@
+using PixiEditor.Extensions;
+using PixiEditor.Extensions.WasmRuntime;
+
+namespace PixiEditor.AvaloniaUI.Models.AppExtensions;
+
+public class WasmExtensionEntry : ExtensionEntry
+{
+    public WasmExtensionInstance Instance { get; }
+
+    public WasmExtensionEntry(WasmExtensionInstance instance)
+    {
+        Instance = instance;
+    }
+
+    public override Extension CreateExtension()
+    {
+        Instance.Instantiate();
+        return Instance;
+    }
+}

+ 1 - 0
src/PixiEditor.AvaloniaUI/PixiEditor.AvaloniaUI.csproj

@@ -51,6 +51,7 @@
       <ProjectReference Include="..\PixiEditor.ChangeableDocument\PixiEditor.ChangeableDocument.csproj" />
       <ProjectReference Include="..\PixiEditor.ChangeableDocument\PixiEditor.ChangeableDocument.csproj" />
       <ProjectReference Include="..\PixiEditor.DrawingApi.Core\PixiEditor.DrawingApi.Core.csproj" />
       <ProjectReference Include="..\PixiEditor.DrawingApi.Core\PixiEditor.DrawingApi.Core.csproj" />
       <ProjectReference Include="..\PixiEditor.DrawingApi.Skia\PixiEditor.DrawingApi.Skia.csproj" />
       <ProjectReference Include="..\PixiEditor.DrawingApi.Skia\PixiEditor.DrawingApi.Skia.csproj" />
+      <ProjectReference Include="..\PixiEditor.Extensions.WasmRuntime\PixiEditor.Extensions.WasmRuntime.csproj" />
       <ProjectReference Include="..\PixiEditor.Extensions\PixiEditor.Extensions.csproj" />
       <ProjectReference Include="..\PixiEditor.Extensions\PixiEditor.Extensions.csproj" />
       <ProjectReference Include="..\PixiEditor.OperatingSystem\PixiEditor.OperatingSystem.csproj" />
       <ProjectReference Include="..\PixiEditor.OperatingSystem\PixiEditor.OperatingSystem.csproj" />
       <ProjectReference Include="..\PixiEditor.Platform.MSStore\PixiEditor.Platform.MSStore.csproj" />
       <ProjectReference Include="..\PixiEditor.Platform.MSStore\PixiEditor.Platform.MSStore.csproj" />

+ 6 - 0
src/PixiEditor.Extensions.Wasm/Api/IWindowProvider.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.Extensions.Wasm.Api;
+
+public interface IWindowProvider
+{
+    public void CreatePopupWindow(string title, string body);
+}

+ 0 - 4
src/PixiEditor.Extensions.Wasm/Api/Logger.cs

@@ -4,10 +4,6 @@ namespace PixiEditor.Extensions.Wasm.Api;
 
 
 public class Logger : ILogger
 public class Logger : ILogger
 {
 {
-    public Logger() : base()
-    {
-    }
-
     public void Log(string message)
     public void Log(string message)
     {
     {
         InvokeApiLog(message);
         InvokeApiLog(message);

+ 9 - 0
src/PixiEditor.Extensions.Wasm/Api/WindowProvider.cs

@@ -0,0 +1,9 @@
+namespace PixiEditor.Extensions.Wasm.Api;
+
+public class WindowProvider : IWindowProvider
+{
+    public void CreatePopupWindow(string title, string body)
+    {
+        Interop.CreatePopupWindow(title, body);
+    }
+}

+ 3 - 0
src/PixiEditor.Extensions.Wasm/Interop.cs

@@ -7,4 +7,7 @@ internal class Interop
 {
 {
     [MethodImpl(MethodImplOptions.InternalCall)]
     [MethodImpl(MethodImplOptions.InternalCall)]
     internal static extern void LogMessage(string message);
     internal static extern void LogMessage(string message);
+
+    [MethodImpl(MethodImplOptions.InternalCall)]
+    internal static extern void CreatePopupWindow(string title, string body);
 }
 }

+ 2 - 0
src/PixiEditor.Extensions.Wasm/PixiEditorApi.cs

@@ -5,9 +5,11 @@ namespace PixiEditor.Extensions.Wasm;
 public class PixiEditorApi
 public class PixiEditorApi
 {
 {
     public ILogger Logger { get; }
     public ILogger Logger { get; }
+    public IWindowProvider WindowProvider { get; }
 
 
     public PixiEditorApi()
     public PixiEditorApi()
     {
     {
         Logger = new Logger();
         Logger = new Logger();
+        WindowProvider = new WindowProvider();
     }
     }
 }
 }

+ 13 - 2
src/PixiEditor.Extensions.Wasm/native/api_interop.c

@@ -7,7 +7,10 @@ MonoMethod* method_entry;
 __attribute__((import_name("log_message")))
 __attribute__((import_name("log_message")))
 void log_message(const char* message);
 void log_message(const char* message);
 
 
-__attribute__((export_name("entry")))
+__attribute__((import_name("create_popup_window")))
+void create_popup_window(const char* title, const char* content);
+
+/*__attribute__((export_name("entry")))
 void entry()
 void entry()
 {
 {
     if (!method_entry) {
     if (!method_entry) {
@@ -19,7 +22,7 @@ void entry()
     MonoObject *exception;
     MonoObject *exception;
     mono_wasm_invoke_method (method_entry, NULL, method_params, &exception);
     mono_wasm_invoke_method (method_entry, NULL, method_params, &exception);
     assert (!exception);
     assert (!exception);
-}
+}*/
 
 
 void logger_log_message(MonoString* message)
 void logger_log_message(MonoString* message)
 {
 {
@@ -27,7 +30,15 @@ void logger_log_message(MonoString* message)
     log_message(message_utf8);
     log_message(message_utf8);
 }
 }
 
 
+void logger_create_popup_window(MonoString* title, MonoString* content)
+{
+    char* title_utf8 = mono_wasm_string_get_utf8(title);
+    char* content_utf8 = mono_wasm_string_get_utf8(content);
+    create_popup_window(title_utf8, content_utf8);
+}
+
 void attach_internal_calls()
 void attach_internal_calls()
 {
 {
     mono_add_internal_call("PixiEditor.Extensions.Wasm.Interop::LogMessage", logger_log_message);
     mono_add_internal_call("PixiEditor.Extensions.Wasm.Interop::LogMessage", logger_log_message);
+    mono_add_internal_call("PixiEditor.Extensions.Wasm.Interop::CreatePopupWindow", logger_create_popup_window);
 }
 }

+ 21 - 0
src/PixiEditor.Extensions.WasmRuntime/MemoryUtility.cs

@@ -0,0 +1,21 @@
+using System.Text;
+using Wasmtime;
+
+namespace PixiEditor.Extensions.WasmRuntime;
+
+public static class MemoryUtility
+{
+    public static string GetStringFromWasmMemory(int offset, Memory memory)
+    {
+        var span = memory.GetSpan<byte>(offset);
+        int length = 0;
+        while (span[length] != 0)
+        {
+            length++;
+        }
+
+        var buffer = new byte[length];
+        span[..length].CopyTo(buffer);
+        return Encoding.UTF8.GetString(buffer);
+    }
+}

+ 18 - 0
src/PixiEditor.Extensions.WasmRuntime/PixiEditor.Extensions.WasmRuntime.csproj

@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net8.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <PackageReference Include="Wasmtime" Version="16.0.0" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <ProjectReference Include="..\PixiEditor.Extensions\PixiEditor.Extensions.csproj" />
+    </ItemGroup>
+
+</Project>

+ 21 - 0
src/PixiEditor.Extensions.WasmRuntime/PixiEditorApiLinkerExtensions.cs

@@ -0,0 +1,21 @@
+using Wasmtime;
+
+namespace PixiEditor.Extensions.WasmRuntime;
+
+public static class PixiEditorApiLinkerExtensions
+{
+    public static void DefinePixiEditorApi(this Linker linker, WasmExtensionInstance instance)
+    {
+        linker.DefineFunction("env", "log_message",(int messageOffset) =>
+        {
+            string messageString = MemoryUtility.GetStringFromWasmMemory(messageOffset, instance.Instance.GetMemory("memory"));
+            Console.WriteLine(messageString);
+        });
+
+        linker.DefineFunction("env", "create_popup_window",(int titleOffset, int bodyOffset) =>
+        {
+            string title = MemoryUtility.GetStringFromWasmMemory(titleOffset, instance.Instance.GetMemory("memory"));
+            string body = MemoryUtility.GetStringFromWasmMemory(bodyOffset, instance.Instance.GetMemory("memory"));
+        });
+    }
+}

+ 32 - 0
src/PixiEditor.Extensions.WasmRuntime/WasmExtensionInstance.cs

@@ -0,0 +1,32 @@
+using Wasmtime;
+
+namespace PixiEditor.Extensions.WasmRuntime;
+
+public class WasmExtensionInstance : Extension
+{
+    public Instance? Instance { get; private set; }
+
+    private Linker Linker { get; }
+    private Store Store { get; }
+    private Module Module { get; }
+
+    public WasmExtensionInstance(Linker linker, Store store, Module module)
+    {
+        Linker = linker;
+        Store = store;
+        Module = module;
+    }
+
+    public void Instantiate()
+    {
+        Linker.DefinePixiEditorApi(this);
+        Linker.DefineModule(Store, Module);
+
+        Instance = Linker.Instantiate(Store, Module);
+    }
+
+    protected override void OnLoaded()
+    {
+        Instance.GetFunction("_start").Invoke();
+    }
+}

+ 35 - 0
src/PixiEditor.Extensions.WasmRuntime/WasmRuntime.cs

@@ -0,0 +1,35 @@
+using System.Text;
+using Wasmtime;
+
+namespace PixiEditor.Extensions.WasmRuntime;
+
+public class WasmRuntime
+{
+    private Engine engine = new Engine();
+
+    public WasmExtensionInstance LoadModule(string path)
+    {
+        if (!System.IO.File.Exists(path))
+        {
+            throw new System.IO.FileNotFoundException("File not found", path);
+        }
+
+        Module module = Module.FromFile(engine, path);
+        WasiConfiguration wasiConfig = new WasiConfiguration();
+        wasiConfig.WithInheritedStandardError().WithInheritedStandardInput().WithInheritedStandardOutput()
+            .WithInheritedArgs().WithInheritedEnvironment();
+
+        using var config = new Config().WithDebugInfo(true)
+            .WithCraneliftDebugVerifier(true)
+            .WithOptimizationLevel(OptimizationLevel.SpeedAndSize)
+            .WithWasmThreads(true)
+            .WithBulkMemory(true)
+            .WithMultiMemory(true);
+
+        var linker = new Linker(engine);
+        var store = new Store(engine);
+        store.SetWasiConfiguration(wasiConfig);
+        linker.DefineWasi();
+        return new WasmExtensionInstance(linker, store, module);
+    }
+}

+ 0 - 4
src/PixiEditor.WasmRuntime/WasmRuntime.cs

@@ -41,10 +41,6 @@ public class WasmRuntime
         instance = linker.Instantiate(store, module);
         instance = linker.Instantiate(store, module);
 
 
         instance.GetFunction("_start").Invoke();
         instance.GetFunction("_start").Invoke();
-        //linker.GetDefaultFunction(store, "WasmSampleExtension").Invoke();
-        /*var main = instance.GetAction("entry");
-
-        main.Invoke();*/
     }
     }
 
 
     private string GetFromWasmMemory(int offset, Memory memory)
     private string GetFromWasmMemory(int offset, Memory memory)

+ 45 - 90
src/PixiEditor.sln

@@ -80,12 +80,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Tests", "PixiEdi
 EndProject
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WasmSampleExtension", "WasmSampleExtension\WasmSampleExtension.csproj", "{F4436A16-9488-4BAB-B2F6-C1806278CE16}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WasmSampleExtension", "WasmSampleExtension\WasmSampleExtension.csproj", "{F4436A16-9488-4BAB-B2F6-C1806278CE16}"
 EndProject
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.WasmRuntime", "PixiEditor.WasmRuntime\PixiEditor.WasmRuntime.csproj", "{1AD04210-1055-4014-8FB5-15DC1621EA35}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WasmRuntimeTest", "WasmRuntimeTest\WasmRuntimeTest.csproj", "{9E837500-FF9B-4EB8-A411-11C528CDDC7A}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Extensions.Wasm", "PixiEditor.Extensions.Wasm\PixiEditor.Extensions.Wasm.csproj", "{71907779-F1D1-4AA6-BA11-E990DB089841}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Extensions.Wasm", "PixiEditor.Extensions.Wasm\PixiEditor.Extensions.Wasm.csproj", "{71907779-F1D1-4AA6-BA11-E990DB089841}"
 EndProject
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Extensions.WasmRuntime", "PixiEditor.Extensions.WasmRuntime\PixiEditor.Extensions.WasmRuntime.csproj", "{B30622ED-9177-4930-8E64-2B2352D4D8DC}"
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Debug|Any CPU = Debug|Any CPU
@@ -1347,90 +1345,6 @@ Global
 		{F4436A16-9488-4BAB-B2F6-C1806278CE16}.Steam|x64.Build.0 = Debug|Any CPU
 		{F4436A16-9488-4BAB-B2F6-C1806278CE16}.Steam|x64.Build.0 = Debug|Any CPU
 		{F4436A16-9488-4BAB-B2F6-C1806278CE16}.Steam|x86.ActiveCfg = Debug|Any CPU
 		{F4436A16-9488-4BAB-B2F6-C1806278CE16}.Steam|x86.ActiveCfg = Debug|Any CPU
 		{F4436A16-9488-4BAB-B2F6-C1806278CE16}.Steam|x86.Build.0 = Debug|Any CPU
 		{F4436A16-9488-4BAB-B2F6-C1806278CE16}.Steam|x86.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Debug|x64.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Debug|x86.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevRelease|Any CPU.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevRelease|Any CPU.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevRelease|x64.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevRelease|x64.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevRelease|x86.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevRelease|x86.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevSteam|Any CPU.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevSteam|Any CPU.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevSteam|x64.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevSteam|x64.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevSteam|x86.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.DevSteam|x86.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX Debug|x64.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX Debug|x64.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX Debug|x86.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX Debug|x86.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX|Any CPU.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX|Any CPU.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX|x64.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX|x64.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX|x86.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.MSIX|x86.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Release|Any CPU.Build.0 = Release|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Release|x64.ActiveCfg = Release|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Release|x64.Build.0 = Release|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Release|x86.ActiveCfg = Release|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Release|x86.Build.0 = Release|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Steam|Any CPU.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Steam|Any CPU.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Steam|x64.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Steam|x64.Build.0 = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Steam|x86.ActiveCfg = Debug|Any CPU
-		{1AD04210-1055-4014-8FB5-15DC1621EA35}.Steam|x86.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Debug|x64.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Debug|x86.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevRelease|Any CPU.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevRelease|Any CPU.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevRelease|x64.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevRelease|x64.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevRelease|x86.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevRelease|x86.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevSteam|Any CPU.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevSteam|Any CPU.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevSteam|x64.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevSteam|x64.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevSteam|x86.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.DevSteam|x86.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX Debug|x64.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX Debug|x64.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX Debug|x86.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX Debug|x86.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX|Any CPU.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX|Any CPU.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX|x64.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX|x64.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX|x86.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.MSIX|x86.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Release|Any CPU.Build.0 = Release|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Release|x64.ActiveCfg = Release|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Release|x64.Build.0 = Release|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Release|x86.ActiveCfg = Release|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Release|x86.Build.0 = Release|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Steam|Any CPU.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Steam|Any CPU.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Steam|x64.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Steam|x64.Build.0 = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Steam|x86.ActiveCfg = Debug|Any CPU
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A}.Steam|x86.Build.0 = Debug|Any CPU
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Debug|x64.ActiveCfg = Debug|Any CPU
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -1473,6 +1387,48 @@ Global
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Steam|x64.Build.0 = Debug|Any CPU
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Steam|x64.Build.0 = Debug|Any CPU
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Steam|x86.ActiveCfg = Debug|Any CPU
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Steam|x86.ActiveCfg = Debug|Any CPU
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Steam|x86.Build.0 = Debug|Any CPU
 		{71907779-F1D1-4AA6-BA11-E990DB089841}.Steam|x86.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Debug|x64.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Debug|x86.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevRelease|Any CPU.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevRelease|Any CPU.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevRelease|x64.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevRelease|x64.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevRelease|x86.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevRelease|x86.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevSteam|Any CPU.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevSteam|Any CPU.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevSteam|x64.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevSteam|x64.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevSteam|x86.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.DevSteam|x86.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX Debug|x64.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX Debug|x64.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX Debug|x86.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX Debug|x86.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX|Any CPU.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX|Any CPU.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX|x64.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX|x64.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX|x86.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.MSIX|x86.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Release|x64.ActiveCfg = Release|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Release|x64.Build.0 = Release|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Release|x86.ActiveCfg = Release|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Release|x86.Build.0 = Release|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Steam|Any CPU.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Steam|Any CPU.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Steam|x64.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Steam|x64.Build.0 = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Steam|x86.ActiveCfg = Debug|Any CPU
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC}.Steam|x86.Build.0 = Debug|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE
@@ -1507,9 +1463,8 @@ Global
 		{8F4FFC91-BE9F-4476-A372-FBD952865F15} = {1E816135-76C1-4255-BE3C-BF17895A65AA}
 		{8F4FFC91-BE9F-4476-A372-FBD952865F15} = {1E816135-76C1-4255-BE3C-BF17895A65AA}
 		{427CE098-4B13-4E46-8C66-D924140B6CAE} = {1E816135-76C1-4255-BE3C-BF17895A65AA}
 		{427CE098-4B13-4E46-8C66-D924140B6CAE} = {1E816135-76C1-4255-BE3C-BF17895A65AA}
 		{F4436A16-9488-4BAB-B2F6-C1806278CE16} = {E4FF4CE6-5831-450D-8006-0539353C030B}
 		{F4436A16-9488-4BAB-B2F6-C1806278CE16} = {E4FF4CE6-5831-450D-8006-0539353C030B}
-		{1AD04210-1055-4014-8FB5-15DC1621EA35} = {1E816135-76C1-4255-BE3C-BF17895A65AA}
-		{9E837500-FF9B-4EB8-A411-11C528CDDC7A} = {5AFBF881-C054-4CE4-8159-8D4017FFD27A}
 		{71907779-F1D1-4AA6-BA11-E990DB089841} = {1E816135-76C1-4255-BE3C-BF17895A65AA}
 		{71907779-F1D1-4AA6-BA11-E990DB089841} = {1E816135-76C1-4255-BE3C-BF17895A65AA}
+		{B30622ED-9177-4930-8E64-2B2352D4D8DC} = {1E816135-76C1-4255-BE3C-BF17895A65AA}
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {D04B4AB0-CA33-42FD-A909-79966F9255C5}
 		SolutionGuid = {D04B4AB0-CA33-42FD-A909-79966F9255C5}

+ 1 - 1
src/WasmSampleExtension/SampleExtension.cs

@@ -9,9 +9,9 @@ public class SampleExtension : WasmExtension
         Api.Logger.Log("WASM SampleExtension loaded!");
         Api.Logger.Log("WASM SampleExtension loaded!");
     }
     }
 
 
-
     public override void OnInitialized()
     public override void OnInitialized()
     {
     {
         Api.Logger.Log("WASM SampleExtension initialized!");
         Api.Logger.Log("WASM SampleExtension initialized!");
+        Api.WindowProvider.CreatePopupWindow("WASM SampleExtension", "Hello from WASM PixiEditor Extension");
     }
     }
 }
 }

+ 7 - 0
src/WasmSampleExtension/WasmSampleExtension.csproj

@@ -20,4 +20,11 @@
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\PixiEditor.Extensions.Wasm\PixiEditor.Extensions.Wasm.csproj" />
     <ProjectReference Include="..\PixiEditor.Extensions.Wasm\PixiEditor.Extensions.Wasm.csproj" />
   </ItemGroup>
   </ItemGroup>
+
+  <ItemGroup>
+    <None Remove="extension.json" />
+    <Content Include="extension.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
 </Project>
 </Project>