├── SpawnDev.BlazorJS.Photino.App.Demo
├── publish.win-x64.bat
├── icon-192.ico
├── Properties
│ └── launchSettings.json
├── wwwroot
│ └── index.html
├── Program.cs
└── SpawnDev.BlazorJS.Photino.App.Demo.csproj
├── SpawnDev.BlazorJS.Photino
├── Assets
│ └── icon-128.png
├── Native
│ └── NativeMethodsWindows.cs
├── JsonConverters
│ ├── IntPtrJsonConverter.cs
│ └── ClaimsIdentityConverter.cs
├── IWebRootServer.cs
├── SpawnDev.BlazorJS.Photino.csproj
├── PhotinoAppDispatcher.cs
├── PhotinoBlazorWASMWindow.cs
├── JsonElementListExtensions.cs
├── PhotinoBlazorWASMApp.cs
├── AsyncCallDispatcherSlim.cs
└── RemoteDispatcher.cs
├── SpawnDev.BlazorJS.Photino.App
├── Assets
│ └── icon-128.png
├── SpawnDev.BlazorJS.Photino.App.csproj
├── PhotinoBlazorWASMAppBuilder.cs
└── WebRootServer.cs
├── SpawnDev.BlazorJS.Photino.App.Demo.Client
├── wwwroot
│ ├── favicon.png
│ ├── icon-192.png
│ ├── index.html
│ └── css
│ │ └── app.css
├── Pages
│ ├── Counter.razor
│ └── Home.razor
├── Layout
│ ├── MainLayout.razor
│ ├── NavMenu.razor
│ ├── MainLayout.razor.css
│ └── NavMenu.razor.css
├── _Imports.razor
├── App.razor
├── Services
│ └── ConsoleLogger.cs
├── SpawnDev.BlazorJS.Photino.App.Demo.Client.csproj
├── Properties
│ └── launchSettings.json
└── Program.cs
├── LICENSE.txt
├── .gitattributes
├── SpawnDev.BlazorJS.Photino.sln
├── README.md
└── .gitignore
/SpawnDev.BlazorJS.Photino.App.Demo/publish.win-x64.bat:
--------------------------------------------------------------------------------
1 |
2 | dotnet publish -c Release -r win-x64
3 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino/Assets/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LostBeard/SpawnDev.BlazorJS.Photino/master/SpawnDev.BlazorJS.Photino/Assets/icon-128.png
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo/icon-192.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LostBeard/SpawnDev.BlazorJS.Photino/master/SpawnDev.BlazorJS.Photino.App.Demo/icon-192.ico
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App/Assets/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LostBeard/SpawnDev.BlazorJS.Photino/master/SpawnDev.BlazorJS.Photino.App/Assets/icon-128.png
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "HelloPhotinoApp": {
4 | "commandName": "Project"
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/wwwroot/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LostBeard/SpawnDev.BlazorJS.Photino/master/SpawnDev.BlazorJS.Photino.App.Demo.Client/wwwroot/favicon.png
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/wwwroot/icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LostBeard/SpawnDev.BlazorJS.Photino/master/SpawnDev.BlazorJS.Photino.App.Demo.Client/wwwroot/icon-192.png
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/Pages/Counter.razor:
--------------------------------------------------------------------------------
1 | @page "/counter"
2 |
3 | Counter
4 |
5 |
Counter
6 |
7 | Current count: @currentCount
8 |
9 |
10 |
11 | @code {
12 |
13 | private int currentCount = 0;
14 |
15 | private void Increment()
16 | {
17 | currentCount++;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/Layout/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 |
6 |
7 |
8 |
11 |
12 |
13 | @Body
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino/Native/NativeMethodsWindows.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace SpawnDev.BlazorJS.Photino.Native;
4 |
5 | internal static class NativeMethodsWindows
6 | {
7 | [DllImport("user32.dll")]
8 | public static extern bool ShowWindow(nint hWnd, int nCmdShow);
9 |
10 | // ShowWindow commands
11 | public const int SW_HIDE = 0;
12 | public const int SW_SHOW = 5;
13 | public const int SW_MINIMIZE = 6;
14 | public const int SW_RESTORE = 9;
15 | }
16 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using System.Net.Http
2 | @using System.Net.Http.Json
3 | @using Microsoft.AspNetCore.Components.Forms
4 | @using Microsoft.AspNetCore.Components.Routing
5 | @using Microsoft.AspNetCore.Components.Web
6 | @using Microsoft.AspNetCore.Components.Web.Virtualization
7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http
8 | @using Microsoft.JSInterop
9 | @using SpawnDev.BlazorJS.Photino.App.Demo.Client
10 | @using SpawnDev.BlazorJS.Photino.App.Demo.Client.Layout
11 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/App.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Not found
8 |
9 | Sorry, there's nothing at this address.
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/Services/ConsoleLogger.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.Photino.App.Demo.Client.Services
2 | {
3 | public interface IConsoleLogger
4 | {
5 | Task LogAsync(string message);
6 | void Log(string message);
7 | }
8 |
9 | public class ConsoleLogger : IConsoleLogger
10 | {
11 | public ConsoleLogger()
12 | {
13 | var nmt = true;
14 | }
15 | public void Log(string message)
16 | {
17 | Console.WriteLine(message);
18 | }
19 |
20 | public Task LogAsync(string message)
21 | {
22 | Console.WriteLine(message);
23 | return Task.CompletedTask;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/SpawnDev.BlazorJS.Photino.App.Demo.Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino/JsonConverters/IntPtrJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using System.Text.Json;
3 | using System.Text.Json.Serialization;
4 |
5 | namespace SpawnDev.BlazorJS.Photino.JsonConverters
6 | {
7 | public class IntPtrJsonConverter : JsonConverter
8 | {
9 | public override nint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
10 | {
11 | if (reader.TokenType == JsonTokenType.Null)
12 | {
13 | return default(nint);
14 | }
15 | var value = JsonSerializer.Deserialize(ref reader, options);
16 | return new nint(value);
17 | }
18 | public override void Write(Utf8JsonWriter writer, nint value, JsonSerializerOptions options)
19 | {
20 | var sValue = value.ToInt64();
21 | JsonSerializer.Serialize(writer, sValue, options);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino/IWebRootServer.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.Photino
2 | {
3 | ///
4 | /// Simple http server
5 | ///
6 | public interface IWebRootServer
7 | {
8 | ///
9 | /// True if running
10 | ///
11 | bool Running { get; }
12 | ///
13 | /// The currently served url
14 | ///
15 | string? Url { get; }
16 | ///
17 | /// The served folder
18 | ///
19 | string? WwwRootFolder { get; }
20 | ///
21 | /// Starts the server
22 | ///
23 | void CreateStaticFileServer(int startPort, int portRange, string webRootFolder, out string baseUrl);
24 | ///
25 | /// Starts the server
26 | ///
27 | void CreateStaticFileServer(out string baseUrl);
28 | ///
29 | /// Starts the server
30 | ///
31 | void CreateStaticFileServer(string webRootFolder, out string baseUrl);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [year] [fullname]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SpawnDev.BlazorJS.Photino.App.Demo.Client
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 | An unhandled error has occurred.
26 |
Reload
27 |
🗙
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SpawnDev.BlazorJS.Photino.App.Demo.Client
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 | An unhandled error has occurred.
26 |
Reload
27 |
🗙
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/Layout/NavMenu.razor:
--------------------------------------------------------------------------------
1 |
9 |
10 |
24 |
25 | @code {
26 | private bool collapseNavMenu = true;
27 |
28 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
29 |
30 | private void ToggleNavMenu()
31 | {
32 | collapseNavMenu = !collapseNavMenu;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:29852",
8 | "sslPort": 44338
9 | }
10 | },
11 | "profiles": {
12 | "http": {
13 | "commandName": "Project",
14 | "dotnetRunMessages": true,
15 | "launchBrowser": true,
16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
17 | "applicationUrl": "http://localhost:5174",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | },
22 | "https": {
23 | "commandName": "Project",
24 | "dotnetRunMessages": true,
25 | "launchBrowser": true,
26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
27 | "applicationUrl": "https://localhost:7174;http://localhost:5174",
28 | "environmentVariables": {
29 | "ASPNETCORE_ENVIRONMENT": "Development"
30 | }
31 | },
32 | "IIS Express": {
33 | "commandName": "IISExpress",
34 | "launchBrowser": true,
35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
36 | "environmentVariables": {
37 | "ASPNETCORE_ENVIRONMENT": "Development"
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino/JsonConverters/ClaimsIdentityConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using System.Text.Json;
3 | using System.Text.Json.Serialization;
4 |
5 | namespace SpawnDev.BlazorJS.Photino.JsonConverters
6 | {
7 | public class ClaimsIdentityConverter : JsonConverter
8 | {
9 | public override ClaimsIdentity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
10 | {
11 | var value = JsonSerializer.Deserialize(ref reader, options);
12 | return string.IsNullOrWhiteSpace(value) ? default! : Base64ToClaimsIdentity(value);
13 | }
14 | public override void Write(Utf8JsonWriter writer, ClaimsIdentity value, JsonSerializerOptions options)
15 | {
16 | var sValue = ToBase64(value);
17 | JsonSerializer.Serialize(writer, sValue, options);
18 | }
19 | static string ToBase64(ClaimsIdentity claimsIdentity)
20 | {
21 | using var buffer = new MemoryStream();
22 | using var writer = new BinaryWriter(buffer);
23 | claimsIdentity.WriteTo(writer);
24 | var data = buffer.ToArray();
25 | return Convert.ToBase64String(data);
26 | }
27 | static ClaimsIdentity Base64ToClaimsIdentity(string claimsIdentity)
28 | {
29 | var data = Convert.FromBase64String(claimsIdentity);
30 | using var buffer = new MemoryStream(data);
31 | using var reader = new BinaryReader(buffer);
32 | return new ClaimsIdentity(reader);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Components.Web;
2 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
3 | using SpawnDev.BlazorJS;
4 | using SpawnDev.BlazorJS.Photino;
5 | using SpawnDev.BlazorJS.Photino.App.Demo.Client;
6 | using SpawnDev.BlazorJS.Photino.App.Demo.Client.Services;
7 |
8 | var builder = WebAssemblyHostBuilder.CreateDefault(args);
9 | builder.RootComponents.Add("#app");
10 | builder.RootComponents.Add("head::after");
11 |
12 | // BlazorJSRuntime (PhotinoAppDispatcher dependency)
13 | builder.Services.AddBlazorJSRuntime();
14 |
15 | // PhotinoAppDispatcher lets Blazor WASM call into the Photino hosting app (if available) using:
16 | // Expressions:
17 | // var result = await PhotinoAppDispatcher.Run(service => service.SomeMethod(someVariable1, someVariable2));
18 | // - or -
19 | // Interface DispatchProxy:
20 | // var service = PhotinoAppDispatcher.GetService() where TService : interface
21 | // var result = await service.SomeMethod(someVariable1, someVariable2);
22 | // - or -
23 | // Register Photino host app service interface DispatchProxy and use as a normal service
24 | // (See IConsoleLogger below)
25 | builder.Services.AddSingleton();
26 |
27 | // This adds IConsoleLogger provided by PhotinoAppDispatcher which will relay all
28 | // async method calls to the Photino app instance via an interface DispatchProxy
29 | builder.Services.AddSingleton(sp => sp.GetRequiredService().GetService());
30 |
31 | // Start
32 | await builder.Build().BlazorJSRunAsync();
33 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino/SpawnDev.BlazorJS.Photino.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0;net9.0;net10.0
5 | enable
6 | enable
7 | 1.2.0
8 | True
9 | true
10 | true
11 | Embedded
12 | SpawnDev.BlazorJS.Photino
13 | LostBeard
14 | Blazor WebAssembly in Photino. Use this package in the Blazor WebAssembly project.
15 | https://github.com/LostBeard/SpawnDev.BlazorJS.Photino
16 | README.md
17 | LICENSE.txt
18 | icon-128.png
19 | https://github.com/LostBeard/SpawnDev.BlazorJS.Photino.git
20 | git
21 | Blazor;BlazorWebAssembly;WebBrowser;Photino
22 | latest
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/Layout/MainLayout.razor.css:
--------------------------------------------------------------------------------
1 | .page {
2 | position: relative;
3 | display: flex;
4 | flex-direction: column;
5 | }
6 |
7 | main {
8 | flex: 1;
9 | }
10 |
11 | .sidebar {
12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
13 | }
14 |
15 | .top-row {
16 | background-color: #f7f7f7;
17 | border-bottom: 1px solid #d6d5d5;
18 | justify-content: flex-end;
19 | height: 3.5rem;
20 | display: flex;
21 | align-items: center;
22 | }
23 |
24 | .top-row ::deep a, .top-row ::deep .btn-link {
25 | white-space: nowrap;
26 | margin-left: 1.5rem;
27 | text-decoration: none;
28 | }
29 |
30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
31 | text-decoration: underline;
32 | }
33 |
34 | .top-row ::deep a:first-child {
35 | overflow: hidden;
36 | text-overflow: ellipsis;
37 | }
38 |
39 | @media (max-width: 640.98px) {
40 | .top-row {
41 | justify-content: space-between;
42 | }
43 |
44 | .top-row ::deep a, .top-row ::deep .btn-link {
45 | margin-left: 0;
46 | }
47 | }
48 |
49 | @media (min-width: 641px) {
50 | .page {
51 | flex-direction: row;
52 | }
53 |
54 | .sidebar {
55 | width: 250px;
56 | height: 100vh;
57 | position: sticky;
58 | top: 0;
59 | }
60 |
61 | .top-row {
62 | position: sticky;
63 | top: 0;
64 | z-index: 1;
65 | }
66 |
67 | .top-row.auth ::deep a:first-child {
68 | flex: 1;
69 | text-align: right;
70 | width: 0;
71 | }
72 |
73 | .top-row, article {
74 | padding-left: 2rem !important;
75 | padding-right: 1.5rem !important;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App/SpawnDev.BlazorJS.Photino.App.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0;net9.0;net10.0
5 | enable
6 | enable
7 | 1.2.0
8 | True
9 | true
10 | true
11 | Embedded
12 | SpawnDev.BlazorJS.Photino.App
13 | LostBeard
14 | Blazor WebAssembly in Photino. Use this package in the Photino.Net app project.
15 | https://github.com/LostBeard/SpawnDev.BlazorJS.Photino
16 | README.md
17 | LICENSE.txt
18 | icon-128.png
19 | https://github.com/LostBeard/SpawnDev.BlazorJS.Photino.git
20 | git
21 | Blazor;BlazorWebAssembly;WebBrowser;Photino
22 | latest
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App/PhotinoBlazorWASMAppBuilder.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace SpawnDev.BlazorJS.Photino;
4 | ///
5 | /// Builds PhotinoBlazorWASMApp
6 | ///
7 | public class PhotinoBlazorWASMAppBuilder
8 | {
9 | ///
10 | /// PhotinoBlazorWASMApp services collection
11 | ///
12 | public IServiceCollection Services { get; }
13 |
14 | internal PhotinoBlazorWASMAppBuilder()
15 | {
16 | Services = new ServiceCollection();
17 | }
18 | ///
19 | /// Create default
20 | ///
21 | ///
22 | ///
23 | public static PhotinoBlazorWASMAppBuilder CreateDefault(string[]? args = null)
24 | {
25 | PhotinoBlazorWASMAppBuilder photinoBlazorAppBuilder = new PhotinoBlazorWASMAppBuilder();
26 | photinoBlazorAppBuilder.Services.AddSingleton(photinoBlazorAppBuilder.Services);
27 | photinoBlazorAppBuilder.Services.AddSingleton(sp => sp);
28 | photinoBlazorAppBuilder.Services.AddSingleton();
29 | photinoBlazorAppBuilder.Services.AddSingleton();
30 | return photinoBlazorAppBuilder;
31 | }
32 | ///
33 | /// Builds PhotinoBlazorWASMApp and returns it.
34 | ///
35 | ///
36 | ///
37 | public PhotinoBlazorWASMApp Build(Action? serviceProviderOptions = null)
38 | {
39 | var serviceProvider = Services.BuildServiceProvider();
40 | var PhotinoBlazorWASMApp = serviceProvider.GetRequiredService();
41 | serviceProviderOptions?.Invoke(serviceProvider);
42 | return PhotinoBlazorWASMApp;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/Pages/Home.razor:
--------------------------------------------------------------------------------
1 | @page "/"
2 | @using SpawnDev.BlazorJS.Photino.App.Demo.Client.Services
3 |
4 | Home
5 |
6 | Home
7 |
8 | Connected to Photino app services: @PhotinoAppDispatcher.IsReady
9 |
10 |
11 |
12 |
13 |
14 | @code {
15 | [Inject]
16 | PhotinoAppDispatcher PhotinoAppDispatcher { get; set; } = default!;
17 |
18 | [Inject]
19 | IConsoleLogger ConsoleLogger { get; set; } = default!;
20 |
21 | private async Task OpenWindow()
22 | {
23 | // this calls IConsoleLogger.LogAsync() which relays the call to the Photino host app IConsoleLogger service
24 | await ConsoleLogger.LogAsync(">> Window being opened by " + PhotinoAppDispatcher.WindowId);
25 |
26 | // call PhotinoBlazorWASMApp.OpenWindow() in the Photino host app on the PhotinoBlazorWASMApp service
27 | var windowId = await PhotinoAppDispatcher.Run(s => s.OpenWindow());
28 |
29 | // this calls IConsoleLogger.LogAsync() which relays the call to the Photino host app IConsoleLogger service
30 | await ConsoleLogger.LogAsync(">> Window opened: " + windowId);
31 | }
32 |
33 | private async Task CloseThisWindow()
34 | {
35 | // this calls IConsoleLogger.LogAsync() which relays the call to the Photino host app IConsoleLogger service
36 | await ConsoleLogger.LogAsync(">> Window closing: " + PhotinoAppDispatcher.WindowId);
37 |
38 | // call PhotinoBlazorWASMWindow.Close() in the Photino host app on this window's PhotinoBlazorWASMWindow instance
39 | await PhotinoAppDispatcher.Run(s => s.Close());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using SpawnDev;
3 | using SpawnDev.BlazorJS.Photino;
4 | using SpawnDev.BlazorJS.Photino.App.Demo.Client.Services;
5 |
6 | namespace HelloPhotinoApp
7 | {
8 | // NOTE: To hide the console window, go to the project properties and change the Output Type to Windows Application.
9 | // Or edit the .csproj file and change the tag from "WinExe" to "Exe".
10 | internal class Program
11 | {
12 | [STAThread]
13 | static void Main(string[] args)
14 | {
15 | // Create RemoteServiceProviderBuilder
16 | var appBuilder = PhotinoBlazorWASMAppBuilder.CreateDefault(args);
17 |
18 | // Blazor WebAssembly instances can call these services using expressions or
19 | // an interface DispatchProxy provided by the PhotinoAppDispatcher service
20 | // Singleton services are shared with all windows
21 | // Scoped services are per-window
22 | // Transient are per call
23 |
24 | // The demo uses this service via an interface DispatchProxy
25 | appBuilder.Services.AddSingleton();
26 |
27 | // build
28 | var app = appBuilder.Build();
29 |
30 | ///
31 | /// If true, closing the main window will hide it instead of closing it.
32 | /// This allows the app to stay alive until all windows are closed.
33 | /// NOTE: Only supported when PhotinoWindow.IsWindowsPlatform == true
34 | /// Default: false
35 | ///
36 | app.IndependentWindows = false;
37 |
38 | ///
39 | /// If true the app will not exit when there are no windows except invisible MainWindow.
40 | /// Setting this to true is useful for a system tray icon that can be used to create a new window or show the main one.
41 | /// NOTE: Only supported when PhotinoWindow.IsWindowsPlatform == true
42 | /// Default: false
43 | ///
44 | app.InvisibleKeepAlive = false;
45 |
46 | #if DEBUG
47 | // Set the Url where the Blazor WebAssembly dev server is hosting when DEBUG
48 | // if not set, the app's "wwwroot/index.html" path will be used.
49 | // In production a release build of your Blazor WASM app could be served from there.
50 | app.SetAppBaseUri("https://localhost:7174/");
51 | #endif
52 |
53 | // Start app. Show main window
54 | app.Run();
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.14.36623.8
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpawnDev.BlazorJS.Photino", "SpawnDev.BlazorJS.Photino\SpawnDev.BlazorJS.Photino.csproj", "{307238B9-F37F-43CA-950F-48BFEB35D7A5}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpawnDev.BlazorJS.Photino.App.Demo.Client", "SpawnDev.BlazorJS.Photino.App.Demo.Client\SpawnDev.BlazorJS.Photino.App.Demo.Client.csproj", "{72F1EA39-4075-4CCC-A3D1-05D468337FE3}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpawnDev.BlazorJS.Photino.App.Demo", "SpawnDev.BlazorJS.Photino.App.Demo\SpawnDev.BlazorJS.Photino.App.Demo.csproj", "{1D43CB3B-6BE9-41C6-BD08-A208814E47A7}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpawnDev.BlazorJS.Photino.App", "SpawnDev.BlazorJS.Photino.App\SpawnDev.BlazorJS.Photino.App.csproj", "{6DBD81D6-F4D2-4722-9B48-7FED0C18C1E5}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {307238B9-F37F-43CA-950F-48BFEB35D7A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {307238B9-F37F-43CA-950F-48BFEB35D7A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {307238B9-F37F-43CA-950F-48BFEB35D7A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {307238B9-F37F-43CA-950F-48BFEB35D7A5}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {72F1EA39-4075-4CCC-A3D1-05D468337FE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {72F1EA39-4075-4CCC-A3D1-05D468337FE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {72F1EA39-4075-4CCC-A3D1-05D468337FE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {72F1EA39-4075-4CCC-A3D1-05D468337FE3}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {1D43CB3B-6BE9-41C6-BD08-A208814E47A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {1D43CB3B-6BE9-41C6-BD08-A208814E47A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {1D43CB3B-6BE9-41C6-BD08-A208814E47A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {1D43CB3B-6BE9-41C6-BD08-A208814E47A7}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {6DBD81D6-F4D2-4722-9B48-7FED0C18C1E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {6DBD81D6-F4D2-4722-9B48-7FED0C18C1E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {6DBD81D6-F4D2-4722-9B48-7FED0C18C1E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {6DBD81D6-F4D2-4722-9B48-7FED0C18C1E5}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(ExtensibilityGlobals) = postSolution
41 | SolutionGuid = {14EB1C29-66E3-450C-B11F-8F4298333AF8}
42 | EndGlobalSection
43 | EndGlobal
44 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/Layout/NavMenu.razor.css:
--------------------------------------------------------------------------------
1 | .navbar-toggler {
2 | background-color: rgba(255, 255, 255, 0.1);
3 | }
4 |
5 | .top-row {
6 | height: 3.5rem;
7 | background-color: rgba(0,0,0,0.4);
8 | }
9 |
10 | .navbar-brand {
11 | font-size: 1.1rem;
12 | }
13 |
14 | .bi {
15 | display: inline-block;
16 | position: relative;
17 | width: 1.25rem;
18 | height: 1.25rem;
19 | margin-right: 0.75rem;
20 | top: -1px;
21 | background-size: cover;
22 | }
23 |
24 | .bi-house-door-fill-nav-menu {
25 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
26 | }
27 |
28 | .bi-plus-square-fill-nav-menu {
29 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
30 | }
31 |
32 | .bi-list-nested-nav-menu {
33 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
34 | }
35 |
36 | .nav-item {
37 | font-size: 0.9rem;
38 | padding-bottom: 0.5rem;
39 | }
40 |
41 | .nav-item:first-of-type {
42 | padding-top: 1rem;
43 | }
44 |
45 | .nav-item:last-of-type {
46 | padding-bottom: 1rem;
47 | }
48 |
49 | .nav-item ::deep a {
50 | color: #d7d7d7;
51 | border-radius: 4px;
52 | height: 3rem;
53 | display: flex;
54 | align-items: center;
55 | line-height: 3rem;
56 | }
57 |
58 | .nav-item ::deep a.active {
59 | background-color: rgba(255,255,255,0.37);
60 | color: white;
61 | }
62 |
63 | .nav-item ::deep a:hover {
64 | background-color: rgba(255,255,255,0.1);
65 | color: white;
66 | }
67 |
68 | @media (min-width: 641px) {
69 | .navbar-toggler {
70 | display: none;
71 | }
72 |
73 | .collapse {
74 | /* Never collapse the sidebar for wide screens */
75 | display: block;
76 | }
77 |
78 | .nav-scrollable {
79 | /* Allow sidebar to scroll for tall menus */
80 | height: calc(100vh - 3.5rem);
81 | overflow-y: auto;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo/SpawnDev.BlazorJS.Photino.App.Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 | WinExe
8 | net8.0
9 | enable
10 | enable
11 |
12 |
15 |
16 |
17 | ./icon-192.ico
18 |
19 |
20 | true
21 |
22 |
23 | true
24 | true
25 |
26 |
27 | true
28 |
29 |
33 | false
34 | false
35 |
36 |
37 | embedded
38 |
39 | ./bin/Publish
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | $(ProjectDir)..\SpawnDev.BlazorJS.Photino.App.Demo.Client
61 | $(BlazorWasmProjectDir)\bin\Publish\$(Configuration)\net8.0\publish\wwwroot
62 | $(ProjectDir)wwwroot
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino/PhotinoAppDispatcher.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 |
3 | namespace SpawnDev.BlazorJS.Photino;
4 | ///
5 | /// Handles interop between a Blazor WebAssembly app and the hosting Photino app
6 | ///
7 | public class PhotinoAppDispatcher : RemoteDispatcher, IAsyncBackgroundService
8 | {
9 | Task? _Ready = null;
10 | ///
11 | public Task Ready => _Ready ??= InitAsync();
12 | BlazorJSRuntime JS;
13 | ///
14 | /// Serialization options used for interop
15 | ///
16 | public JsonSerializerOptions SerializerOptions { get; private set; } = new JsonSerializerOptions();
17 | ActionCallback? External_OnMessageCallback;
18 | ///
19 | /// Returns true if the app appears to be running in a Photino window
20 | ///
21 | public bool PhotinoFound { get; }
22 | ///
23 | /// This will be the PhotinoBlazorWASMWindow.Id after the Photino app has connected.
24 | ///
25 | public string? WindowId { get; private set; }
26 | ///
27 | /// New instance
28 | ///
29 | ///
30 | ///
31 | public PhotinoAppDispatcher(BlazorJSRuntime js, IServiceProvider serviceProvider) : base(serviceProvider, createNewScope: false)
32 | {
33 | JS = js;
34 | RequireRemoteCallableAttribute = false;
35 | AllowPrivateMethods = true;
36 | AllowSpecialMethods = true;
37 | AllowStaticMethods = true;
38 | AllowNonServiceStaticMethods = true;
39 | PhotinoFound = PhotinoBlazorWASM;
40 | }
41 | static Lazy _PhotinoBlazorWASM = new Lazy(() =>
42 | {
43 | return BlazorJSRuntime.JS.IsBrowser == true
44 | && BlazorJSRuntime.JS?.IsUndefined("external?.sendMessage") == false
45 | && BlazorJSRuntime.JS?.IsUndefined("external?.receiveMessage") == false;
46 | });
47 | ///
48 | /// Returns true if the app appears to be running Blazor WASM in a Photino window
49 | ///
50 | public static bool PhotinoBlazorWASM => _PhotinoBlazorWASM.Value;
51 | async Task InitAsync()
52 | {
53 | if (PhotinoFound)
54 | {
55 | External_OnMessageCallback = new ActionCallback(External_OnMessage);
56 | JS.CallVoid("external.receiveMessage", External_OnMessageCallback);
57 | SendReadyFlag();
58 | await WhenReady;
59 | WindowId = await Run(s => s.Id);
60 | #if DEBUG
61 | JS.Log($"WindowId: {WindowId}");
62 | #endif
63 | }
64 | }
65 | async void External_OnMessage(string message)
66 | {
67 | try
68 | {
69 | var args = JsonSerializer.Deserialize>(message, SerializerOptions);
70 | if (args != null)
71 | {
72 | await HandleCall(args);
73 | }
74 | }
75 | catch { }
76 | }
77 | ///
78 | protected override void SendCall(object?[] args)
79 | {
80 | if (!PhotinoFound) return;
81 | try
82 | {
83 | var response = JsonSerializer.Serialize(args, SerializerOptions);
84 | JS.CallVoid("external.sendMessage", response);
85 | }
86 | catch { }
87 | }
88 | ///
89 | public override void Dispose()
90 | {
91 | base.Dispose();
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino.App.Demo.Client/wwwroot/css/app.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
3 | }
4 |
5 | h1:focus {
6 | outline: none;
7 | }
8 |
9 | a, .btn-link {
10 | color: #0071c1;
11 | }
12 |
13 | .btn-primary {
14 | color: #fff;
15 | background-color: #1b6ec2;
16 | border-color: #1861ac;
17 | }
18 |
19 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
20 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
21 | }
22 |
23 | .content {
24 | padding-top: 1.1rem;
25 | }
26 |
27 | .valid.modified:not([type=checkbox]) {
28 | outline: 1px solid #26b050;
29 | }
30 |
31 | .invalid {
32 | outline: 1px solid red;
33 | }
34 |
35 | .validation-message {
36 | color: red;
37 | }
38 |
39 | #blazor-error-ui {
40 | background: lightyellow;
41 | bottom: 0;
42 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
43 | display: none;
44 | left: 0;
45 | padding: 0.6rem 1.25rem 0.7rem 1.25rem;
46 | position: fixed;
47 | width: 100%;
48 | z-index: 1000;
49 | }
50 |
51 | #blazor-error-ui .dismiss {
52 | cursor: pointer;
53 | position: absolute;
54 | right: 0.75rem;
55 | top: 0.5rem;
56 | }
57 |
58 | .blazor-error-boundary {
59 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
60 | padding: 1rem 1rem 1rem 3.7rem;
61 | color: white;
62 | }
63 |
64 | .blazor-error-boundary::after {
65 | content: "An error has occurred."
66 | }
67 |
68 | .loading-progress {
69 | position: relative;
70 | display: block;
71 | width: 8rem;
72 | height: 8rem;
73 | margin: 20vh auto 1rem auto;
74 | }
75 |
76 | .loading-progress circle {
77 | fill: none;
78 | stroke: #e0e0e0;
79 | stroke-width: 0.6rem;
80 | transform-origin: 50% 50%;
81 | transform: rotate(-90deg);
82 | }
83 |
84 | .loading-progress circle:last-child {
85 | stroke: #1b6ec2;
86 | stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
87 | transition: stroke-dasharray 0.05s ease-in-out;
88 | }
89 |
90 | .loading-progress-text {
91 | position: absolute;
92 | text-align: center;
93 | font-weight: bold;
94 | inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
95 | }
96 |
97 | .loading-progress-text:after {
98 | content: var(--blazor-load-percentage-text, "Loading");
99 | }
100 |
101 | code {
102 | color: #c02d76;
103 | }
104 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.Photino/PhotinoBlazorWASMWindow.cs:
--------------------------------------------------------------------------------
1 | using Photino.NET;
2 | using SpawnDev.BlazorJS.Photino.Native;
3 | using System.Text.Json;
4 |
5 | namespace SpawnDev.BlazorJS.Photino;
6 | ///
7 | /// Handles interop between a Photino app and Photino windows hosting Blazor WASM apps.
8 | ///
9 | public class PhotinoBlazorWASMWindow : RemoteDispatcher
10 | {
11 | ///
12 | /// Window Guid as a string
13 | ///
14 | public string Id => Window.Id.ToString();
15 | ///
16 | /// Photino window
17 | ///
18 | public PhotinoWindow Window { get; }
19 | ///
20 | /// Returns true if the window can be hidden
21 | ///
22 | public bool CanHide => PhotinoWindow.IsWindowsPlatform;
23 | JsonSerializerOptions SerializerOptions;
24 | ///
25 | /// New instance
26 | ///
27 | ///
28 | ///
29 | ///
30 | public PhotinoBlazorWASMWindow(IServiceProvider serviceProvider, PhotinoWindow window, JsonSerializerOptions serializerOptions) : base(serviceProvider, createNewScope: true)
31 | {
32 | Window = window;
33 | SerializerOptions = serializerOptions;
34 | window.WebMessageReceived += HandleMessage;
35 | RequireRemoteCallableAttribute = false;
36 | AllowPrivateMethods = true;
37 | AllowSpecialMethods = true;
38 | AllowStaticMethods = true;
39 | AllowNonServiceStaticMethods = true;
40 | }
41 | ///
42 | /// True if the window is visible
43 | ///
44 | public bool Visible
45 | {
46 | get => _Visible;
47 | set => Show(value);
48 | }
49 | bool _Visible = true;
50 | ///
51 | /// Closes the window
52 | ///
53 | public void Close()
54 | {
55 | Window?.Close();
56 | }
57 | ///
58 | /// Show or hide the window
59 | ///
60 | ///
61 | public bool Show(bool show)
62 | {
63 | if (PhotinoWindow.IsWindowsPlatform)
64 | {
65 | var hWnd = Window.WindowHandle;
66 | if (show)
67 | {
68 | _Visible = true;
69 | // Show the window and restore it if minimized
70 | NativeMethodsWindows.ShowWindow(hWnd, NativeMethodsWindows.SW_RESTORE);
71 | NativeMethodsWindows.ShowWindow(hWnd, NativeMethodsWindows.SW_SHOW);
72 | }
73 | else
74 | {
75 | _Visible = false;
76 | NativeMethodsWindows.ShowWindow(hWnd, NativeMethodsWindows.SW_HIDE);
77 | }
78 | return true;
79 | }
80 | return false;
81 | }
82 | ///
83 | /// Wait for close (only waits if it starts a message pump, such as the MainWindow)
84 | /// If it doesn't start a message pump, it just starts the window and shows it
85 | ///
86 | public void WaitForClose()
87 | {
88 | Window.WaitForClose();
89 | }
90 | ///
91 | /// Add this PhotinoWindow and PhotinoBlazorWASMWindow to services this instance can access
92 | ///
93 | ///
94 | ///
95 | protected override async Task