Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5327511
Add auth loading screen and logout button to app bar
FransVanEk Jan 21, 2026
5a5f145
Improve Blazor WASM loading screen and startup logic
FransVanEk Jan 21, 2026
0606d2d
Improve Blazor WASM loading screen and startup logic
FransVanEk Jan 21, 2026
ec62ae8
Change @keyframes to @@keyframes in _Host.cshtml
FransVanEk Jan 21, 2026
8a8ffc8
Initial plan
Copilot Jan 22, 2026
f402b1b
Create Elsa.Studio.Hosting framework with reusable components
Copilot Jan 22, 2026
c961097
Add JavaScript initialization file to Hosting project
Copilot Jan 22, 2026
30d565f
Add reusable hosting components to Elsa.Studio.Shared
Copilot Jan 22, 2026
459f33a
Add documentation and fix CSHTML host files to use inline HTML with s…
Copilot Jan 22, 2026
4127240
Add implementation summary document
Copilot Jan 22, 2026
b41be70
Create complete single-script loaders for minimal host integration
Copilot Jan 22, 2026
44f7261
Update implementation summary with complete single-script loader details
Copilot Jan 22, 2026
73d42e8
Refine loading screen logic for Blazor Server & WASM
FransVanEk Jan 22, 2026
c34852a
Merge branch 'main' into feature/remove-rendering-before--login
sfmskywalker Jan 27, 2026
b10dc3c
Refactor script loading to ensure Monaco loads first
FransVanEk Jan 28, 2026
3a01ef0
Merge branch 'feature/remove-rendering-before--login' of https://gith…
FransVanEk Jan 28, 2026
645b289
Refactor loaders, improve startup and resilience
FransVanEk Jan 29, 2026
198ae84
Smarter unauthorized handling in MainLayout
FransVanEk Jan 29, 2026
ddf4b98
Unified logout service & login page performance optimizations
FransVanEk Feb 2, 2026
bf93b74
Configurable branding for login page via appsettings
FransVanEk Feb 2, 2026
897a8a1
Refactor logout and branding to use config-driven values
FransVanEk Feb 2, 2026
bc5cb5f
Merge branch 'main' into feature/remove-rendering-before--login
sfmskywalker Feb 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<MudIconButton Icon="@Icons.Material.Outlined.Logout" Color="Color.Inherit" Href="/login" Target="_self" Title="Logout"/>
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ protected override void OnInitialized()
if (BrandingProvider.AppBarIcons.ShowGitHubLink) AppBarService.AddComponent<GitHub>(15);
AppBarService.AddComponent<DarkModeToggle>(20);
AppBarService.AddComponent<ProductInfo>(25);
AppBarService.AddComponent<LogoutButton>(99);

ThemeService.CurrentThemeChanged += OnThemeChanged;
ThemeService.IsDarkModeChanged += OnDarkModeChanged;
Expand Down
40 changes: 37 additions & 3 deletions src/framework/Elsa.Studio.Shell/App.razor
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
@inherits StudioComponentBase
@inject IJSRuntime JSRuntime

<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="@AdditionalAssemblies">
<Found Context="routeData">
@if (!AuthorizationIsDisabled)
{
<CascadingAuthenticationState>
<AuthorizeRouteView RouteData="routeData" DefaultLayout="@typeof(MainLayout)">
<Authorizing>
<Well>
<MudText Typo="Typo.body2">Authorizing</MudText>
</Well>
<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #f5f5f5; display: flex; justify-content: center; align-items: center; z-index: 9998;">
<div style="text-align: center;">
<div style="width: 40px; height: 40px; border: 4px solid #e0e0e0; border-top: 4px solid #1976d2; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 20px;"></div>
<MudText Typo="Typo.body1">Checking authentication...</MudText>
</div>
</div>
</Authorizing>
<NotAuthorized>
@UnauthorizedComponentProvider.GetUnauthorizedComponent()
Expand All @@ -30,3 +35,32 @@
</LayoutView>
</NotFound>
</Router>

<style>
@@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>

@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Hide the initial loading screen after the first render
try
{
await Task.Delay(300); // Small delay to ensure smooth transition

// Try both Server and WASM hiding functions (one will work, one will be ignored)
await JSRuntime.InvokeVoidAsync("hideAuthLoading"); // Server
await JSRuntime.InvokeVoidAsync("hideWasmLoading"); // WASM
}
catch (Exception ex)
{
Console.WriteLine($"Failed to call loading screen hide functions: {ex.Message}");
}
}
}
}
Empty file.
109 changes: 93 additions & 16 deletions src/hosts/Elsa.Studio.Host.HostedWasm/Pages/_Host.cshtml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@page "/"
 @page "/"
@using Elsa.Studio.Branding
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@inject IConfiguration Configuration;
Expand All @@ -18,7 +18,7 @@
<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>
<title>Elsa Studio</title>
<base href="@baseRef"/>
<link rel="apple-touch-icon" sizes="180x180" href="@BrandingProvider.AppleTouchIconUrl">
<link rel="icon" type="image/png" sizes="32x32" href="@BrandingProvider.Favicon32Url">
Expand All @@ -31,45 +31,122 @@
</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>

<!-- Initial loading screen for HostedWasm -->
<div id="hosted-wasm-loading" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #f5f5f5; display: flex; justify-content: center; align-items: center; z-index: 9999;">
<div style="text-align: center;">
<div style="width: 40px; height: 40px; border: 4px solid #e0e0e0; border-top: 4px solid #1976d2; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 20px;"></div>
<div id="hosted-loading-text" style="color: #666; font-family: 'Roboto', sans-serif;">Initializing...</div>
</div>
</div>

<div id="app"></div>

<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>

<style>
@@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

.blazor-ready #hosted-wasm-loading {
display: none !important;
}
</style>

<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;
<script>
let hostedWasmLoadingHidden = false;
let blazorStartAttempted = false;

function hideHostedWasmLoadingScreen() {
if (!hostedWasmLoadingHidden) {
hostedWasmLoadingHidden = true;
document.body.classList.add('blazor-ready');
}
}

if (!defaultUri.startsWith('/'))
return `/${defaultUri}`;
function updateHostedLoadingText(text) {
const loadingTextEl = document.getElementById('hosted-loading-text');
if (loadingTextEl) {
loadingTextEl.textContent = text;
}
}

return defaultUri;
}
});
// Expose functions for Blazor components
window.hideAuthLoading = function () {
hideHostedWasmLoadingScreen();
};

window.hideWasmLoading = function () {
hideHostedWasmLoadingScreen();
};

window.updateLoadingStatus = function (status) {
updateHostedLoadingText(status);
};

// Initialize Blazor with custom configuration
function initializeBlazor() {
if (!blazorStartAttempted) {
blazorStartAttempted = true;
updateHostedLoadingText('Starting Blazor WASM...');

Blazor.start({
loadBootResource: function (type, name, defaultUri, integrity) {
if (defaultUri.startsWith('http'))
return defaultUri;

if (!defaultUri.startsWith('/'))
return `/${defaultUri}`;

return defaultUri;
}
}).then(() => {
updateHostedLoadingText('Loading application...');
}).catch((error) => {
if (error.message && error.message.includes('already started')) {
setTimeout(hideHostedWasmLoadingScreen, 100);
} else {
updateHostedLoadingText('Startup failed');
setTimeout(hideHostedWasmLoadingScreen, 2000);
}
});
}
}

// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', function () {
initializeBlazor();
});

// Safety timeout
setTimeout(() => {
if (!hostedWasmLoadingHidden) {
hideHostedWasmLoadingScreen();
}
}, 5000);
</script>

</body>

</html>
89 changes: 61 additions & 28 deletions src/hosts/Elsa.Studio.Host.Server/Pages/_Host.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,83 @@
@using Elsa.Studio.Branding
@using Elsa.Studio.Shell
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Authorization
@namespace Elsa.Studio.Host.Server.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@inject IBrandingProvider BrandingProvider
@attribute [AllowAnonymous]

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<base href="~/"/>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="~/" />
<title>Elsa Studio</title>
<link rel="apple-touch-icon" sizes="180x180" href="@BrandingProvider.AppleTouchIconUrl">
<link rel="icon" type="image/png" sizes="32x32" href="@BrandingProvider.Favicon32Url">
<link rel="icon" type="image/png" sizes="16x16" href="@BrandingProvider.Favicon16Url">
<link rel="manifest" href="_content/Elsa.Studio.Shell/site.webmanifest">
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet"/>
<link href="_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" 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"> *@
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered"/>

<component type="typeof(HeadOutlet)" render-mode="Server" />
</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>

@* Show loading screen initially *@
<div id="auth-loading" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #f5f5f5; display: flex; justify-content: center; align-items: center; z-index: 9999;">
<div style="text-align: center;">
<div style="width: 40px; height: 40px; border: 4px solid #e0e0e0; border-top: 4px solid #1976d2; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 20px;"></div>
<div style="color: #666; font-family: 'Roboto', sans-serif;">Initializing...</div>
</div>
</div>

<component type="typeof(App)" render-mode="Server" />

<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>

<style>
@@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

.blazor-ready #auth-loading {
display: none !important;
}
</style>

<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>

<script>
window.hideAuthLoading = function() {
document.body.classList.add('blazor-ready');
};

// Fallback: Hide after maximum wait time
setTimeout(function() {
window.hideAuthLoading();
}, 10000); // 10 second maximum wait
</script>

</body>
</html>
Loading