Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 21 additions & 0 deletions src/MauiSherpa.Core/Interfaces.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1218,3 +1218,24 @@ public interface ICopilotToolsService
/// </summary>
IReadOnlyList<string> ReadOnlyToolNames { get; }
}

/// <summary>
/// Service for coordinating splash screen visibility between MAUI and Blazor
/// </summary>
public interface ISplashService
{
/// <summary>
/// Event fired when Blazor is ready and splash should hide
/// </summary>
event Action? OnBlazorReady;

/// <summary>
/// Called by Blazor when it's fully loaded and ready
/// </summary>
void NotifyBlazorReady();

/// <summary>
/// Whether Blazor has signaled it's ready
/// </summary>
bool IsBlazorReady { get; }
}
11 changes: 9 additions & 2 deletions src/MauiSherpa/App.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
using MauiSherpa.Core.Interfaces;

namespace MauiSherpa;

public class App : Application
{
public App()
private readonly IServiceProvider _serviceProvider;

public App(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

protected override Window CreateWindow(IActivationState? activationState)
{
var splashService = _serviceProvider.GetRequiredService<ISplashService>();

var window = new Window
{
Page = new MainPage()
Page = new MainPage(splashService)
};

window.Created += (s, e) =>
Expand Down
10 changes: 10 additions & 0 deletions src/MauiSherpa/Components/MainLayout.razor
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@inherits LayoutComponentBase
@using MauiSherpa.Core.Interfaces
@inject IThemeService ThemeService
@inject ISplashService SplashService
@implements IDisposable

<div class="main-layout @ThemeClass">
Expand Down Expand Up @@ -80,6 +81,15 @@
{
ThemeService.ThemeChanged += OnThemeChanged;
}

protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
// Notify MAUI that Blazor is ready - splash can be hidden
SplashService.NotifyBlazorReady();
}
}

private void OnThemeChanged()
{
Expand Down
123 changes: 121 additions & 2 deletions src/MauiSherpa/MainPage.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
using Microsoft.AspNetCore.Components.WebView.Maui;
using MauiSherpa.Core.Interfaces;

namespace MauiSherpa;

public class MainPage : ContentPage
{
public MainPage()
private readonly Grid _splashOverlay;
private readonly ISplashService _splashService;

public MainPage(ISplashService splashService)
{
_splashService = splashService;

var blazorWebView = new BlazorWebView
{
HostPage = "wwwroot/index.html"
Expand All @@ -16,6 +22,119 @@ public MainPage()
ComponentType = typeof(Components.App)
});

Content = blazorWebView;
// Create splash overlay
_splashOverlay = CreateSplashOverlay();

// Use a Grid to layer the BlazorWebView and splash
var container = new Grid();
container.Children.Add(blazorWebView);
container.Children.Add(_splashOverlay);

Content = container;

// Subscribe to Blazor ready event
_splashService.OnBlazorReady += OnBlazorReady;

// Safety timeout - hide splash after 15 seconds
Dispatcher.StartTimer(TimeSpan.FromSeconds(15), () =>
{
if (_splashOverlay.Opacity > 0)
{
HideSplash();
}
return false; // Don't repeat
});
}

private Grid CreateSplashOverlay()
{
var overlay = new Grid
{
BackgroundColor = Color.FromArgb("#1a1625"),
ZIndex = 1000
};

// Add gradient background using BoxView layers
var gradientTop = new BoxView
{
Color = Color.FromArgb("#2d1f4e"),
Opacity = 0.5
};
overlay.Children.Add(gradientTop);

// Content stack
var contentStack = new VerticalStackLayout
{
Spacing = 0,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center
};

// App logo image
var logoImage = new Image
{
Source = "sherpalogo.png",
WidthRequest = 200,
HeightRequest = 200,
HorizontalOptions = LayoutOptions.Center
};
contentStack.Children.Add(logoImage);

// Title
var title = new Label
{
Text = "MAUI Sherpa",
TextColor = Colors.White,
FontSize = 28,
FontAttributes = FontAttributes.Bold,
HorizontalOptions = LayoutOptions.Center,
Margin = new Thickness(0, 24, 0, 0)
};
contentStack.Children.Add(title);

// Subtitle
var subtitle = new Label
{
Text = "Your guide to .NET MAUI development",
TextColor = Color.FromArgb("#9999aa"),
FontSize = 14,
HorizontalOptions = LayoutOptions.Center,
Margin = new Thickness(0, 8, 0, 0)
};
contentStack.Children.Add(subtitle);

// Loading indicator
var loadingIndicator = new ActivityIndicator
{
IsRunning = true,
Color = Color.FromArgb("#8b5cf6"),
WidthRequest = 32,
HeightRequest = 32,
Margin = new Thickness(0, 40, 0, 0),
HorizontalOptions = LayoutOptions.Center
};
contentStack.Children.Add(loadingIndicator);

overlay.Children.Add(contentStack);

return overlay;
}

private void OnBlazorReady()
{
Dispatcher.Dispatch(() => HideSplash());
}

private async void HideSplash()
{
// Fade out animation
await _splashOverlay.FadeToAsync(0, 400, Easing.CubicOut);
_splashOverlay.IsVisible = false;
}

protected override void OnDisappearing()
{
base.OnDisappearing();
_splashService.OnBlazorReady -= OnBlazorReady;
}
}
3 changes: 3 additions & 0 deletions src/MauiSherpa/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public static MauiApp CreateMauiApp()
builder.Logging.AddProvider(new DebugOverlayLoggerProvider(debugLogService));
builder.Logging.SetMinimumLevel(LogLevel.Debug);

// Splash service (must be registered early as singleton for sharing)
builder.Services.AddSingleton<ISplashService, SplashService>();

// Platform services
builder.Services.AddSingleton<IAlertService, AlertService>();
builder.Services.AddSingleton<ILoggingService, LoggingService>();
Expand Down
Binary file added src/MauiSherpa/Resources/Images/sherpalogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions src/MauiSherpa/Services/SplashService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using MauiSherpa.Core.Interfaces;

namespace MauiSherpa.Services;

public class SplashService : ISplashService
{
public event Action? OnBlazorReady;

public bool IsBlazorReady { get; private set; }

public void NotifyBlazorReady()
{
if (IsBlazorReady) return;

IsBlazorReady = true;
OnBlazorReady?.Invoke();
}
}
126 changes: 0 additions & 126 deletions src/MauiSherpa/wwwroot/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,104 +70,9 @@
cursor: pointer;
margin-left: 1rem;
}

/* Splash screen */
#splash-screen {
position: fixed;
inset: 0;
background: linear-gradient(135deg, #1a1625 0%, #2d1f4e 50%, #1a1625 100%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 99999;
transition: opacity 0.5s ease-out;
}

#splash-screen.fade-out {
opacity: 0;
pointer-events: none;
}

.splash-logo {
width: 120px;
height: 120px;
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
border-radius: 28px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 20px 60px rgba(99, 102, 241, 0.4);
animation: pulse-glow 2s ease-in-out infinite;
}

.splash-logo svg {
width: 70px;
height: 70px;
}

.splash-title {
margin-top: 24px;
font-size: 28px;
font-weight: 600;
color: white;
letter-spacing: 0.5px;
}

.splash-subtitle {
margin-top: 8px;
font-size: 14px;
color: rgba(255, 255, 255, 0.6);
}

.splash-loader {
margin-top: 40px;
width: 48px;
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
overflow: hidden;
}

.splash-loader::after {
content: '';
display: block;
width: 50%;
height: 100%;
background: linear-gradient(90deg, #6366f1, #8b5cf6);
border-radius: 2px;
animation: loader-slide 1s ease-in-out infinite;
}

@keyframes pulse-glow {
0%, 100% { box-shadow: 0 20px 60px rgba(99, 102, 241, 0.4); }
50% { box-shadow: 0 20px 80px rgba(139, 92, 246, 0.6); }
}

@keyframes loader-slide {
0% { transform: translateX(-100%); }
100% { transform: translateX(300%); }
}
</style>
</head>
<body>
<!-- Splash screen overlay -->
<div id="splash-screen">
<div class="splash-logo">
<!-- Mountain/Sherpa icon -->
<svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M8 21l4-10 4 10"/>
<path d="M12 11l5-8"/>
<path d="M12 11L7 3"/>
<path d="M3 21h18"/>
<circle cx="12" cy="6" r="2"/>
</svg>
</div>
<div class="splash-title">MAUI Sherpa</div>
<div class="splash-subtitle">Your guide to .NET MAUI development</div>
<div class="splash-loader"></div>
</div>

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

<div id="blazor-error-ui" data-nosnippet>
Expand All @@ -178,36 +83,5 @@

<script src="_framework/blazor.webview.js" autostart="false"></script>
<script src="js/terminal.js"></script>
<script>
// Hide splash screen once Blazor is ready
document.addEventListener('DOMContentLoaded', function() {
// Check for Blazor load completion
const checkBlazorReady = setInterval(function() {
const app = document.getElementById('app');
if (app && app.children.length > 0) {
const splash = document.getElementById('splash-screen');
if (splash) {
splash.classList.add('fade-out');
setTimeout(function() {
splash.remove();
}, 500);
}
clearInterval(checkBlazorReady);
}
}, 100);

// Safety timeout - hide splash after 10 seconds max
setTimeout(function() {
const splash = document.getElementById('splash-screen');
if (splash) {
splash.classList.add('fade-out');
setTimeout(function() {
splash.remove();
}, 500);
}
clearInterval(checkBlazorReady);
}, 10000);
});
</script>
</body>
</html>
Loading