diff --git a/src/MauiSherpa.Core/Interfaces.cs b/src/MauiSherpa.Core/Interfaces.cs index 14707012..ae9a2ae6 100644 --- a/src/MauiSherpa.Core/Interfaces.cs +++ b/src/MauiSherpa.Core/Interfaces.cs @@ -1218,3 +1218,24 @@ public interface ICopilotToolsService /// IReadOnlyList ReadOnlyToolNames { get; } } + +/// +/// Service for coordinating splash screen visibility between MAUI and Blazor +/// +public interface ISplashService +{ + /// + /// Event fired when Blazor is ready and splash should hide + /// + event Action? OnBlazorReady; + + /// + /// Called by Blazor when it's fully loaded and ready + /// + void NotifyBlazorReady(); + + /// + /// Whether Blazor has signaled it's ready + /// + bool IsBlazorReady { get; } +} diff --git a/src/MauiSherpa/App.cs b/src/MauiSherpa/App.cs index f9b4242a..ba4d1ec5 100644 --- a/src/MauiSherpa/App.cs +++ b/src/MauiSherpa/App.cs @@ -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(); + var window = new Window { - Page = new MainPage() + Page = new MainPage(splashService) }; window.Created += (s, e) => diff --git a/src/MauiSherpa/Components/MainLayout.razor b/src/MauiSherpa/Components/MainLayout.razor index ebc70f2c..0ae2fa85 100644 --- a/src/MauiSherpa/Components/MainLayout.razor +++ b/src/MauiSherpa/Components/MainLayout.razor @@ -1,6 +1,7 @@ @inherits LayoutComponentBase @using MauiSherpa.Core.Interfaces @inject IThemeService ThemeService +@inject ISplashService SplashService @implements IDisposable
@@ -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() { diff --git a/src/MauiSherpa/MainPage.cs b/src/MauiSherpa/MainPage.cs index 5b57d581..f79515f6 100644 --- a/src/MauiSherpa/MainPage.cs +++ b/src/MauiSherpa/MainPage.cs @@ -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" @@ -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; } } diff --git a/src/MauiSherpa/MauiProgram.cs b/src/MauiSherpa/MauiProgram.cs index 14ffd76f..d3d94090 100644 --- a/src/MauiSherpa/MauiProgram.cs +++ b/src/MauiSherpa/MauiProgram.cs @@ -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(); + // Platform services builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/src/MauiSherpa/Resources/Images/sherpalogo.png b/src/MauiSherpa/Resources/Images/sherpalogo.png new file mode 100644 index 00000000..316d1ad0 Binary files /dev/null and b/src/MauiSherpa/Resources/Images/sherpalogo.png differ diff --git a/src/MauiSherpa/Services/SplashService.cs b/src/MauiSherpa/Services/SplashService.cs new file mode 100644 index 00000000..e04449d1 --- /dev/null +++ b/src/MauiSherpa/Services/SplashService.cs @@ -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(); + } +} diff --git a/src/MauiSherpa/wwwroot/index.html b/src/MauiSherpa/wwwroot/index.html index d5b16259..a85b05db 100644 --- a/src/MauiSherpa/wwwroot/index.html +++ b/src/MauiSherpa/wwwroot/index.html @@ -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%); } - } - -
- -
MAUI Sherpa
-
Your guide to .NET MAUI development
-
-
-
@@ -178,36 +83,5 @@ -