Add project files.

This commit is contained in:
2025-08-20 11:07:36 +03:30
parent 3863c74931
commit 1a308a4069
899 changed files with 85379 additions and 0 deletions
@@ -0,0 +1,34 @@
using Elsa.Studio.Components;
using Elsa.Studio.Host.CustomElements.Services;
using Microsoft.AspNetCore.Components;
namespace Elsa.Studio.Host.CustomElements.Components;
public abstract class BackendComponentBase : StudioComponentBase
{
[Parameter] public string? RemoteEndpoint { get; set; }
[Parameter] public string? ApiKey { get; set; }
[Parameter] public string? AccessToken { get; set; }
[Parameter] public string? TenantId { get; set; }
[Parameter] public string? TenantIdHeaderName { get; set; }
[Inject] private BackendService BackendService { get; set; } = null!;
/// <inheritdoc />
protected override void OnInitialized()
{
if (!string.IsNullOrWhiteSpace(RemoteEndpoint))
BackendService.RemoteEndpoint = RemoteEndpoint;
if (!string.IsNullOrWhiteSpace(ApiKey))
BackendService.ApiKey = ApiKey;
if (!string.IsNullOrWhiteSpace(AccessToken))
BackendService.AccessToken = AccessToken;
if(!string.IsNullOrWhiteSpace(TenantId))
BackendService.TenantId = TenantId;
if(!string.IsNullOrWhiteSpace(TenantIdHeaderName))
BackendService.TenantIdHeaderName = TenantIdHeaderName;
}
}
@@ -0,0 +1 @@
@inherits BackendComponentBase
@@ -0,0 +1,18 @@
@using Elsa.Studio.Contracts
@inherits BackendComponentBase
@inject IThemeService ThemeService
<MudThemeProvider IsDarkMode="false" Theme="@ThemeService.CurrentTheme"/>
<MudPopoverProvider/>
<MudDialogProvider/>
<MudSnackbarProvider/>
@ChildContent
@code
{
/// <summary>
/// The component to render.
/// </summary>
[Parameter]
public RenderFragment ChildContent { get; set; } = default!;
}
@@ -0,0 +1,54 @@
@using System.Text.Json.Nodes
@using Elsa.Api.Client.Resources.WorkflowDefinitions.Models
@inherits BackendComponentBase
<ThemedComponentWrapper>
<WorkflowDefinitionEditor DefinitionId="@DefinitionId"
WorkflowDefinitionExecuted="WorkflowDefinitionExecuted"
WorkflowDefinitionVersionSelected="WorkflowDefinitionVersionSelected"
ActivitySelected="ActivitySelected"/>
</ThemedComponentWrapper>
@code
{
private bool _shouldRender;
private string _definitionId = default!;
/// The ID of the workflow definition to edit.
[Parameter] public string DefinitionId
{
get => _definitionId;
set
{
if (_definitionId == value) return;
_definitionId = value;
_shouldRender = true;
}
}
/// <summary>An event that is invoked when a workflow definition has been executed.</summary>
/// <remarks>The ID of the workflow instance is provided as the value to the event callback.</remarks>
[Parameter] public Func<string, Task>? WorkflowDefinitionExecuted { get; set; }
/// Gets or sets the event that occurs when the workflow definition version is updated.
[Parameter] public Func<WorkflowDefinition, Task>? WorkflowDefinitionVersionSelected { get; set; }
/// Gets or sets the event triggered when an activity is selected.
[Parameter] public Func<JsonObject, Task>? ActivitySelected { get; set; }
/// Gets the currently selected activity ID.
public string? SelectedActivityId => WorkflowDefinitionEditor.SelectedActivityId;
private WorkflowDefinitionEditor WorkflowDefinitionEditor { get; set; } = default!;
/// Gets the currently selected workflow definition version.
public WorkflowDefinition? GetSelectedWorkflowDefinitionVersion() => WorkflowDefinitionEditor.GetSelectedWorkflowDefinitionVersion();
/// <inheritdoc />
protected override bool ShouldRender()
{
if (!_shouldRender) return false;
_shouldRender = false;
return true;
}
}
@@ -0,0 +1,13 @@
@inherits BackendComponentBase
<ThemedComponentWrapper>
<WorkflowDefinitionList EditWorkflowDefinition="@EditWorkflowDefinition"/>
</ThemedComponentWrapper>
@code
{
/// <summary>
/// An event that is invoked when a workflow definition is edited.
/// </summary>
[Parameter] public EventCallback<string> EditWorkflowDefinition { get; set; }
}
@@ -0,0 +1,14 @@
@inherits BackendComponentBase
<ThemedComponentWrapper>
<WorkflowInstanceList ViewWorkflowInstance="@ViewWorkflowInstance"/>
</ThemedComponentWrapper>
@code{
/// <summary>
/// An event that is invoked when a workflow instance is viewed.
/// </summary>
[Parameter] public EventCallback<string> ViewWorkflowInstance { get; set; }
}
@@ -0,0 +1,20 @@
@inherits BackendComponentBase
<ThemedComponentWrapper>
<WorkflowInstanceViewer InstanceId="@InstanceId" EditWorkflowDefinition="EditWorkflowDefinition"/>
</ThemedComponentWrapper>
@code
{
/// <summary>
/// The ID of the workflow definition to edit.
/// </summary>
[Parameter]
public string InstanceId { get; set; } = default!;
/// <summary>
/// An event that is invoked when a workflow definition is edited.
/// </summary>
[Parameter]
public EventCallback<string> EditWorkflowDefinition { get; set; }
}
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer"/>
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<!-- <Content Update="npm\package.json">-->
<!-- <CopyToOutputDirectory>Always</CopyToOutputDirectory>-->
<!-- <CopyToPublishDirectory>Always</CopyToPublishDirectory>-->
<!-- </Content>-->
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\framework\Elsa.Studio.Core.BlazorWasm\Elsa.Studio.Core.BlazorWasm.csproj" />
<ProjectReference Include="..\..\framework\Elsa.Studio.Shell\Elsa.Studio.Shell.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Studio.Localization.BlazorWasm\Elsa.Studio.Localization.BlazorWasm.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Login\Elsa.Studio.Login.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Workflows\Elsa.Studio.Workflows.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,38 @@
using Elsa.Studio.Host.CustomElements.Services;
namespace Elsa.Studio.Host.CustomElements.HttpMessageHandlers;
/// <summary>
/// An <see cref="HttpMessageHandler"/> that configures the outgoing HTTP request to use the configured API key or bearer token.
/// </summary>
public class AuthHttpMessageHandler : DelegatingHandler
{
private readonly BackendService _backendService;
/// <summary>
/// Initializes a new instance of the <see cref="AuthHttpMessageHandler"/> class.
/// </summary>
public AuthHttpMessageHandler(BackendService backendService)
{
_backendService = backendService;
}
/// <inheritdoc />
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var apiKey = _backendService.ApiKey;
var accessToken = _backendService.AccessToken;
var tenantId = _backendService.TenantId;
var tenantIdHeaderName = _backendService.TenantIdHeaderName;
if(!string.IsNullOrEmpty(apiKey))
request.Headers.Authorization = new("ApiKey", apiKey);
else if(!string.IsNullOrEmpty(accessToken))
request.Headers.Authorization = new("Bearer", accessToken);
if(!string.IsNullOrEmpty(tenantId))
request.Headers.Add(tenantIdHeaderName, tenantId);
return await base.SendAsync(request, cancellationToken);
}
}
@@ -0,0 +1,69 @@
using Elsa.Studio.Shell.Extensions;
using Elsa.Studio.Workflows.Extensions;
using Elsa.Studio.Contracts;
using Elsa.Studio.Core.BlazorWasm.Extensions;
using Elsa.Studio.Extensions;
using Elsa.Studio.Host.CustomElements.Components;
using Elsa.Studio.Host.CustomElements.HttpMessageHandlers;
using Elsa.Studio.Host.CustomElements.Services;
using Elsa.Studio.Localization.Time;
using Elsa.Studio.Localization.Time.Providers;
using Elsa.Studio.Localization.Models;
using Elsa.Studio.Localization.BlazorWasm.Extensions;
using Elsa.Studio.Models;
using Elsa.Studio.Workflows.Designer.Extensions;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection.Extensions;
// Build the host.
var builder = WebAssemblyHostBuilder.CreateDefault(args);
var configuration = builder.Configuration;
// Register the custom elements.
builder.RootComponents.RegisterCustomElsaStudioElements();
builder.RootComponents.RegisterCustomElement<BackendProvider>("elsa-backend-provider");
builder.RootComponents.RegisterCustomElement<WorkflowDefinitionEditorWrapper>("elsa-workflow-definition-editor");
builder.RootComponents.RegisterCustomElement<WorkflowInstanceViewerWrapper>("elsa-workflow-instance-viewer");
builder.RootComponents.RegisterCustomElement<WorkflowInstanceListWrapper>("elsa-workflow-instance-list");
builder.RootComponents.RegisterCustomElement<WorkflowDefinitionListWrapper>("elsa-workflow-definition-list");
// Register local services.
builder.Services.AddSingleton<BackendService>();
// Register the modules.
var backendApiConfig = new BackendApiConfig
{
ConfigureBackendOptions = options => configuration.GetSection("Backend").Bind(options),
ConfigureHttpClientBuilder = options =>
{
options.ApiKey = configuration["Backend:ApiKey"];
options.AuthenticationHandler = typeof(AuthHttpMessageHandler);
},
};
var localizationConfig = new LocalizationConfig
{
ConfigureLocalizationOptions = options => configuration.GetSection("Localization").Bind(options),
};
builder.Services.AddCore();
builder.Services.AddShell();
builder.Services.AddRemoteBackend(backendApiConfig);
builder.Services.Replace(ServiceDescriptor.Scoped<IRemoteBackendAccessor, ComponentRemoteBackendAccessor>());
builder.Services.AddWorkflowsModule();
builder.Services.AddLocalizationModule(localizationConfig);
builder.Services.AddScoped<ITimeZoneProvider, LocalTimeZoneProvider>();
// Build the application.
var app = builder.Build();
await app.UseElsaLocalization();
// Run each startup task.
var startupTask = app.Services.GetServices<IStartupTask>();
foreach (var task in startupTask) await task.ExecuteAsync();
// Run the application.
await app.RunAsync();
@@ -0,0 +1,37 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:15721",
"sslPort": 44319
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5122",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7251;http://localhost:5122",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
@@ -0,0 +1,65 @@
namespace Elsa.Studio.Host.CustomElements.Services;
/// <summary>
/// Represents a service for backend configuration and information management.
/// </summary>
/// <remarks>
/// The <see cref="BackendService"/> class provides properties to manage backend connectivity,
/// including endpoint configuration, API key management, access token, and tenant identification.
/// It acts as a centralized configuration point for backend communication settings.
/// </remarks>
public class BackendService
{
/// <summary>
/// Gets or sets the remote endpoint URL for backend communication.
/// </summary>
/// <remarks>
/// The <c>RemoteEndpoint</c> property specifies the base URL of the backend service that the application interacts with.
/// It is used as a key configuration for defining the remote backend's address and is essential for facilitating communication with the service.
/// This property is typically configured during application initialization or dynamically at runtime.
/// </remarks>
public string RemoteEndpoint { get; set; } = null!;
/// <summary>
/// Gets or sets the API key used for authenticating requests to the backend service.
/// </summary>
/// <remarks>
/// The <c>ApiKey</c> property is used to supply authentication credentials in the form of an API key
/// when communicating with the backend service. It is typically included in the request headers to identify
/// and authorize the client. This property should hold a secure, non-empty value if API key-based authentication
/// is enabled for the backend. Configuring this property correctly is critical for ensuring authorized access
/// to backend resources.
/// </remarks>
public string? ApiKey { get; set; }
/// <summary>
/// Gets or sets the access token used for authentication in backend communication.
/// </summary>
/// <remarks>
/// The <c>AccessToken</c> property specifies a bearer token used for authorizing requests to protected backend resources.
/// This property is typically set during initialization or dynamically updated to ensure proper authorization headers are sent with HTTP requests.
/// It is utilized when API key-based authentication is not provided, ensuring secure access through bearer token mechanisms.
/// </remarks>
public string? AccessToken { get; set; }
/// <summary>
/// Gets or sets the tenant identifier for backend communication.
/// </summary>
/// <remarks>
/// The <c>TenantId</c> property specifies the unique identifier associated with the tenant context in the backend service.
/// It is used to distinguish resources and operations specific to a particular tenant, enabling multi-tenancy support.
/// This property can be set during initialization or dynamically altered at runtime to switch between tenants.
/// Setting this property is not necessary if the <see cref="AccessToken"/> carries a claim that can be used on the back-end to determine the current tenant.
/// </remarks>
public string? TenantId { get; set; }
/// <summary>
/// Gets or sets the name of the HTTP header used for tenant identification during backend communication.
/// </summary>
/// <remarks>
/// The <c>TenantIdHeaderName</c> property defines the header key that carries the tenant identifier in HTTP requests.
/// It is essential for multi-tenant applications to distinguish between different tenants while interacting with the backend service.
/// This property can be customized to align with specific header naming conventions required by the backend.
/// </remarks>
public string TenantIdHeaderName { get; set; } = "X-Tenant-Id";
}
@@ -0,0 +1,25 @@
using Elsa.Studio.Contracts;
using Elsa.Studio.Models;
using Elsa.Studio.Options;
using Elsa.Studio.Services;
namespace Elsa.Studio.Host.CustomElements.Services;
/// <summary>
/// A default implementation of <see cref="IRemoteBackendAccessor"/> that uses the <see cref="BackendOptions"/> to determine the URL of the remote backend.
/// </summary>
public class ComponentRemoteBackendAccessor : IRemoteBackendAccessor
{
private readonly BackendService _backendService;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultRemoteBackendAccessor"/> class.
/// </summary>
public ComponentRemoteBackendAccessor(BackendService backendService)
{
_backendService = backendService;
}
/// <inheritdoc />
public RemoteBackend RemoteBackend => new(new(_backendService.RemoteEndpoint));
}
@@ -0,0 +1,9 @@
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@using MudBlazor
@using Elsa.Studio.Components
@using Elsa.Studio.Shell
@using Elsa.Studio.Workflows.Components.WorkflowDefinitionEditor
@using Elsa.Studio.Workflows.Components.WorkflowInstanceViewer
@using Elsa.Studio.Workflows.Components.WorkflowDefinitionList
@using Elsa.Studio.Workflows.Components.WorkflowInstanceList
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Shell": {
"DisableAuthorization": false
}
}
@@ -0,0 +1,22 @@
{
"name": "@elsa-workflows/elsa-studio-wasm",
"version": "3.0.0-preview.1",
"description": "Provides Elsa Studio Blazor components wrapped as custom elements.",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"elsa workflows",
"blazor"
],
"author": "Sipke Schoorstra",
"license": "MIT",
"files": [
"_framework/*",
"_content/*",
"index.html",
"appsettings.json",
"Elsa.Studio.Host.CustomElements.styles.css"
]
}
@@ -0,0 +1,11 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Backend": {
"Url": "https://localhost:5001/elsa/api"
}
}
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>MyApplication</title>
<base href="/"/>
<link rel="apple-touch-icon" sizes="180x180" href="_content/Elsa.Studio.Shell/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="_content/Elsa.Studio.Shell/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="_content/Elsa.Studio.Shell/favicon-16x16.png">
<link rel="manifest" href="_content/Elsa.Studio.Shell/site.webmanifest">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Grandstander:wght@100&display=swap" rel="stylesheet">
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
<link href="_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" rel="stylesheet" />
<link href="_content/Radzen.Blazor/css/material-base.css" rel="stylesheet" >
<link href="_content/Elsa.Studio.Shell/css/shell.css" rel="stylesheet">
<link href="Elsa.Studio.Host.CustomElements.styles.css" rel="stylesheet">
</head>
<body>
<elsa-studio-workflow-definition-editor definition-id="c6deed7e9c4847e8af0647109781e1f3"></elsa-studio-workflow-definition-editor>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_content/BlazorMonaco/jsInterop.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
<script src="_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.js"></script>
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Elsa.Studio.Host.Wasm\Elsa.Studio.Host.Wasm.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,63 @@
namespace Elsa.Studio.Host.HostedWasm.Middleware;
/// <summary>
/// Middleware that intercepts HTTP requests and rewrites specific asset-related paths
/// for Web Assembly applications to ensure proper routing and handling of static files.
/// </summary>
public class WasmAssetsRewritingMiddleware(RequestDelegate next, ILogger<WasmAssetsRewritingMiddleware> logger)
{
private static readonly string[] TargetSegments = { "_content", "_framework" };
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.HasValue && NeedsRewriting(context.Request.Path.Value!))
{
var path = context.Request.Path.Value!;
// Call the private method to get the index of the target segment using spans
var segmentIndex = GetTargetSegmentIndex(path.AsSpan());
if (segmentIndex > 0)
{
// Use slices instead of Substring to avoid allocations
var newPath = path.AsSpan(segmentIndex).ToString(); // Convert back to a string only when required
context.Request.Path = newPath.StartsWith('/') ? newPath : $"/{newPath}";
if (logger.IsEnabled(LogLevel.Debug)) logger.LogDebug("Rewritten: {Path} -> {NewPath}", path, context.Request.Path.Value);
}
}
// Call the next middleware in the pipeline
await next(context);
}
private static bool NeedsRewriting(string path)
{
// Fast path: Return if the path does not include potential target segments
foreach (var segment in TargetSegments)
{
if (path.Contains(segment, StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
private static int GetTargetSegmentIndex(ReadOnlySpan<char> path)
{
foreach (var segment in TargetSegments)
{
// Use Span instead of string concatenation and index search
var segmentAsSpan = $"{segment}/".AsSpan();
var index = path.IndexOf(segmentAsSpan, StringComparison.OrdinalIgnoreCase);
if (index >= 0)
{
return index;
}
}
return -1; // No valid index found
}
}
@@ -0,0 +1,79 @@
@page "/"
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@inject IConfiguration Configuration;
@{
var apiUrl = Configuration["Hosting:ApiUrl"];
// Get the first segment of the request path, which represents the tenant ID.
var segments = Request.Path.Value!.Split('/', StringSplitOptions.RemoveEmptyEntries);
var tenantId = segments.Length is 1 or > 2 ? segments[0] : null;
var baseRef = tenantId != null ? $"/{tenantId}/" : "/";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>Elsa Studio 3</title>
<base href="@baseRef"/>
<link rel="apple-touch-icon" sizes="180x180" href="_content/Elsa.Studio.Shell/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="_content/Elsa.Studio.Shell/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="_content/Elsa.Studio.Shell/favicon-16x16.png">
<link rel="manifest" href="_content/Elsa.Studio.Shell/site.webmanifest">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Grandstander:wght@100&display=swap" rel="stylesheet">
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet"/>
<link href="_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" rel="stylesheet"/>
<link href="_content/Radzen.Blazor/css/material-base.css" rel="stylesheet">
<link href="_content/Elsa.Studio.Shell/css/shell.css" rel="stylesheet">
</head>
<body>
<div id="app">
<div class="loading-splash mud-container mud-container-maxwidth-false">
<h5 class="mud-typography mud-typography-h5 mud-primary-text my-6">Loading...</h5>
</div>
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_content/BlazorMonaco/jsInterop.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
<script src="_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.js"></script>
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
<script>
window.getClientConfig = function() { return {
"apiUrl": "@apiUrl"
} };
</script>
<script src="_framework/blazor.webassembly.js" autostart="false"></script>
<script type="application/javascript">
document.addEventListener('DOMContentLoaded', function () {
Blazor.start({
loadBootResource: function (type, name, defaultUri, integrity) {
if (defaultUri.startsWith('http'))
return defaultUri;
if (!defaultUri.startsWith('/'))
return `/${defaultUri}`;
return defaultUri;
}
});
});
</script>
</body>
</html>
@@ -0,0 +1,43 @@
using Elsa.Studio.Host.HostedWasm.Middleware;
using Microsoft.AspNetCore.StaticFiles;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseStaticWebAssets();
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()));
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseRouting();
app.UseCors();
// Insert this BEFORE UseStaticFiles middleware.
app.UseMiddleware<WasmAssetsRewritingMiddleware>();
// Use Static Files instead of MapStaticAssets.
app.UseStaticFiles(new StaticFileOptions
{
ContentTypeProvider = new FileExtensionContentTypeProvider
{
Mappings =
{
// Add custom MIME type mappings for WASM
[".dat"] = "application/octet-stream"
}
}
});
app.MapFallbackToPage("/_Host");
app.Run();
@@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7267",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Hosting": {
"ApiUrl": "https://localhost:5001/elsa/api"
}
}
@@ -0,0 +1,20 @@
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["Elsa.Studio.ServerApp/Elsa.Studio.ServerApp.csproj", "Elsa.Studio.ServerApp/"]
RUN dotnet restore "Elsa.Studio.ServerApp/Elsa.Studio.ServerApp.csproj"
COPY . .
WORKDIR "/src/Elsa.Studio.ServerApp"
RUN dotnet build "Elsa.Studio.ServerApp.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "Elsa.Studio.ServerApp.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Elsa.Studio.ServerApp.dll"]
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<Content Include="..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\bundles\Elsa.Studio\Elsa.Studio.csproj" />
<ProjectReference Include="..\..\framework\Elsa.Studio.Core.BlazorServer\Elsa.Studio.Core.BlazorServer.csproj" />
<ProjectReference Include="..\..\framework\Elsa.Studio.Translations\Elsa.Studio.Translations.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Dashboard\Elsa.Studio.Dashboard.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Localization.BlazorServer\Elsa.Studio.Localization.BlazorServer.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Login.BlazorServer\Elsa.Studio.Login.BlazorServer.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Login\Elsa.Studio.Login.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Secrets\Elsa.Studio.Secrets.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Webhooks\Elsa.Studio.Webhooks.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.WorkflowContexts\Elsa.Studio.WorkflowContexts.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Workflows\Elsa.Studio.Workflows.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,56 @@
@page "/"
@using Elsa.Studio.Shell
@using Microsoft.AspNetCore.Components.Web
@namespace Elsa.Studio.Host.Server.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<base href="~/"/>
<link rel="apple-touch-icon" sizes="180x180" href="_content/Elsa.Studio.Shell/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="_content/Elsa.Studio.Shell/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="_content/Elsa.Studio.Shell/favicon-16x16.png">
<link rel="manifest" href="_content/Elsa.Studio.Shell/site.webmanifest">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Grandstander:wght@100&display=swap" rel="stylesheet">
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet"/>
<link href="_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" rel="stylesheet"/>
<link href="_content/Radzen.Blazor/css/material-base.css" rel="stylesheet">
<link href="_content/Elsa.Studio.Shell/css/shell.css" rel="stylesheet">
<link href="_content/Elsa.Studio.Workflows.Designer/designer.css" rel="stylesheet">
@* Use designer.v1.css for the old designer (and comment out the previous designer.css style reference).*@
@* <link href="_content/Elsa.Studio.Workflows.Designer/designer.css" rel="stylesheet"> *@
<link href="Elsa.Studio.Host.Server.styles.css" rel="stylesheet">
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered"/>
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" />
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_content/BlazorMonaco/jsInterop.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
<script src="_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.js"></script>
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
<script src="_framework/blazor.server.js"></script>
</body>
</html>
@@ -0,0 +1,122 @@
using Elsa.Studio.Core.BlazorServer.Extensions;
using Elsa.Studio.Dashboard.Extensions;
using Elsa.Studio.Extensions;
using Elsa.Studio.Localization.Time;
using Elsa.Studio.Localization.Time.Providers;
using Elsa.Studio.Login.BlazorServer.Extensions;
using Elsa.Studio.Login.HttpMessageHandlers;
using Elsa.Studio.Models;
using Elsa.Studio.Secrets;
using Elsa.Studio.Shell.Extensions;
using Elsa.Studio.Webhooks.Extensions;
using Elsa.Studio.Workflows.Extensions;
using Elsa.Studio.Workflows.Designer.Extensions;
using Elsa.Studio.Localization.BlazorServer.Extensions;
using Elsa.Studio.Localization.Models;
using Elsa.Studio.Localization.Options;
using Elsa.Studio.Translations;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Elsa.Studio.Branding;
using Elsa.Studio.Host.Server;
using Elsa.Studio.Login.Extensions;
// Build the host.
var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
// Register Razor services.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor(options =>
{
// Register the root components.
// V2 activity wrapper by default.
options.RootComponents.RegisterCustomElsaStudioElements();
// To use V1 activity wrapper layout, specify the V1 component instead:
//options.RootComponents.RegisterCustomElsaStudioElements(typeof(Elsa.Studio.Workflows.Designer.Components.ActivityWrappers.V1.EmbeddedActivityWrapper));
options.RootComponents.MaxJSRootComponents = 1000;
});
// Register shell services and modules.
var backendApiConfig = new BackendApiConfig
{
ConfigureBackendOptions = options => configuration.GetSection("Backend").Bind(options),
ConfigureHttpClientBuilder = options =>
{
options.AuthenticationHandler = typeof(AuthenticatingApiHttpMessageHandler);
options.ConfigureHttpClient = (_, client) =>
{
// Set a long time out to simplify debugging both Elsa Studio and the Elsa Server backend.
client.Timeout = TimeSpan.FromHours(1);
};
},
};
var localizationConfig = new LocalizationConfig
{
ConfigureLocalizationOptions = options =>
{
configuration.GetSection(LocalizationOptions.LocalizationSection).Bind(options);
options.SupportedCultures = new[] { options?.DefaultCulture ?? new LocalizationOptions().DefaultCulture }
.Concat(options?.SupportedCultures.Where(culture => culture != options?.DefaultCulture) ?? []) .ToArray();
}
};
builder.Services.AddScoped<IBrandingProvider, StudioBrandingProvider>();
builder.Services.AddCore().Replace(new(typeof(IBrandingProvider), typeof(StudioBrandingProvider), ServiceLifetime.Scoped));
builder.Services.AddShell(options => configuration.GetSection("Shell").Bind(options));
builder.Services.AddRemoteBackend(backendApiConfig);
builder.Services.AddLoginModule();
builder.Services.UseElsaIdentity();
// builder.Services.UseOAuth2(options =>
// {
// options.ClientId = "ElsaStudio";
// options.TokenEndpoint = "https://localhost:44366/connect/token";
// options.Scope = "YourSite offline_access";
// });
builder.Services.AddDashboardModule();
builder.Services.AddWorkflowsModule();
builder.Services.AddWebhooksModule();
builder.Services.AddSecretsModule(backendApiConfig);
builder.Services.AddLocalizationModule(localizationConfig);
builder.Services.AddTranslations();
// Replace some services with other implementations.
builder.Services.AddScoped<ITimeZoneProvider, LocalTimeZoneProvider>();
// Uncomment for V1 designer theme (default is V2).
// builder.Services.Configure<DesignerOptions>(options =>
// {
// options.DesignerCssClass = "elsa-flowchart-diagram-designer-v1";
// options.GraphSettings.Grid.Type = "mesh";
// });
// Configure SignalR.
builder.Services.AddSignalR(options =>
{
// Set MaximumReceiveMessageSize:
options.MaximumReceiveMessageSize = 5 * 1024 * 1000; // 5MB
});
// Build the application.
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseResponseCompression();
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseElsaLocalization();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
@@ -0,0 +1,35 @@
{
"iisSettings": {
"iisExpress": {
"applicationUrl": "http://localhost:50413",
"sslPort": 44382
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5010",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7113;http://localhost:5010",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
@@ -0,0 +1,10 @@
using Elsa.Studio.Branding;
namespace Elsa.Studio.Host.Server
{
/// <inheritdoc />
public class StudioBrandingProvider : DefaultBrandingProvider
{
public override string AppName => "Elsa Studio 3.5";
}
}
@@ -0,0 +1,5 @@
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using Microsoft.JSInterop
@@ -0,0 +1,9 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
@@ -0,0 +1,22 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Shell": {
"DisableAuthorization": false
},
"Backend": {
"Url": "https://localhost:5001/elsa/api"
},
"Localization": {
"DefaultCulture": "fa-IR",
"SupportedCultures": [
"fa-IR",
"en-US"
]
}
}
@@ -0,0 +1,20 @@
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["src/Elsa.Studio.WebAssemblyApp/Elsa.Studio.WebAssemblyApp.csproj", "src/Elsa.Studio.WebAssemblyApp/"]
RUN dotnet restore "src/Elsa.Studio.WebAssemblyApp/Elsa.Studio.WebAssemblyApp.csproj"
COPY . .
WORKDIR "/src/src/Elsa.Studio.WebAssemblyApp"
RUN dotnet build "Elsa.Studio.WebAssemblyApp.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "Elsa.Studio.WebAssemblyApp.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Elsa.Studio.WebAssemblyApp.dll"]
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" />
</ItemGroup>
<ItemGroup>
<Content Update="wwwroot\appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\bundles\Elsa.Studio\Elsa.Studio.csproj" />
<ProjectReference Include="..\..\framework\Elsa.Studio.Core.BlazorWasm\Elsa.Studio.Core.BlazorWasm.csproj" />
<ProjectReference Include="..\..\framework\Elsa.Studio.DomInterop\Elsa.Studio.DomInterop.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Dashboard\Elsa.Studio.Dashboard.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Localization.BlazorWasm\Elsa.Studio.Localization.BlazorWasm.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.Login.BlazorWasm\Elsa.Studio.Login.BlazorWasm.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Studio.WorkflowContexts\Elsa.Studio.WorkflowContexts.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,63 @@
using Elsa.Studio.Dashboard.Extensions;
using Elsa.Studio.Shell;
using Elsa.Studio.Shell.Extensions;
using Elsa.Studio.Workflows.Extensions;
using Elsa.Studio.Contracts;
using Elsa.Studio.Core.BlazorWasm.Extensions;
using Elsa.Studio.Extensions;
using Elsa.Studio.Localization.Time;
using Elsa.Studio.Localization.Time.Providers;
using Elsa.Studio.Login.BlazorWasm.Extensions;
using Elsa.Studio.Login.HttpMessageHandlers;
using Elsa.Studio.Models;
using Elsa.Studio.WorkflowContexts.Extensions;
using Elsa.Studio.Workflows.Designer.Extensions;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Elsa.Studio.Localization.Models;
using Elsa.Studio.Localization.BlazorWasm.Extensions;
// Build the host.
var builder = WebAssemblyHostBuilder.CreateDefault(args);
var configuration = builder.Configuration;
// Register root components.
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.RootComponents.RegisterCustomElsaStudioElements();
// Register shell services and modules.
var backendApiConfig = new BackendApiConfig
{
ConfigureBackendOptions = options => configuration.GetSection("Backend").Bind(options),
ConfigureHttpClientBuilder = options => options.AuthenticationHandler = typeof(AuthenticatingApiHttpMessageHandler),
};
var localizationConfig = new LocalizationConfig
{
ConfigureLocalizationOptions = options => configuration.GetSection("Localization").Bind(options),
};
builder.Services.AddCore();
builder.Services.AddShell();
builder.Services.AddRemoteBackend(backendApiConfig);
builder.Services.AddLoginModule();
builder.Services.AddDashboardModule();
builder.Services.AddWorkflowsModule();
builder.Services.AddWorkflowContextsModule();
builder.Services.AddLocalizationModule(localizationConfig);
// Replace some services with other implementations.
builder.Services.AddScoped<ITimeZoneProvider, LocalTimeZoneProvider>();
// Build the application.
var app = builder.Build();
await app.UseElsaLocalization();
// Run each startup task.
var startupTaskRunner = app.Services.GetRequiredService<IStartupTaskRunner>();
await startupTaskRunner.RunStartupTasksAsync();
// Run the application.
await app.RunAsync();
@@ -0,0 +1,38 @@
{
"iisSettings": {
"iisExpress": {
"applicationUrl": "http://localhost:10111",
"sslPort": 44341
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5275",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:7052;http://localhost:5275",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
@@ -0,0 +1,6 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@@ -0,0 +1,11 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Backend": {
"Url": "https://localhost:5001/elsa/api"
}
}
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>Elsa Studio 3.5</title>
<base href="/"/>
<link rel="apple-touch-icon" sizes="180x180" href="_content/Elsa.Studio.Shell/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="_content/Elsa.Studio.Shell/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="_content/Elsa.Studio.Shell/favicon-16x16.png">
<link rel="manifest" href="_content/Elsa.Studio.Shell/site.webmanifest">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Grandstander:wght@100&display=swap" rel="stylesheet">
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
<link href="_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" rel="stylesheet" />
<link href="_content/Radzen.Blazor/css/material-base.css" rel="stylesheet" >
<link href="_content/Elsa.Studio.Shell/css/shell.css" rel="stylesheet">
<link href="Elsa.Studio.Host.Wasm.styles.css" rel="stylesheet">
</head>
<body>
<div id="app">
<div class="loading-splash mud-container mud-container-maxwidth-false">
<h5 class="mud-typography mud-typography-h5 mud-primary-text my-6">Loading...</h5>
</div>
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_content/BlazorMonaco/jsInterop.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
<script src="_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.js"></script>
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>