BlueprintShell 0.1.4

There is a newer version of this package available.
See the version list below for details.
dotnet add package BlueprintShell --version 0.1.4
                    
NuGet\Install-Package BlueprintShell -Version 0.1.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BlueprintShell" Version="0.1.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="BlueprintShell" Version="0.1.4" />
                    
Directory.Packages.props
<PackageReference Include="BlueprintShell" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add BlueprintShell --version 0.1.4
                    
#r "nuget: BlueprintShell, 0.1.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package BlueprintShell@0.1.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=BlueprintShell&version=0.1.4
                    
Install as a Cake Addin
#tool nuget:?package=BlueprintShell&version=0.1.4
                    
Install as a Cake Tool

BlueprintShell

logo

BlueprintShell is an embeddable editor shell built on BlazorBlueprint. Add it to any .NET application and get a fully themed, dockable editor UI - panels, toolbars, dark-mode, hot-reload - served on a configurable port from your own process.

Packages are published to GitHub Packages.


Installation

Add the GitHub Packages feed to your NuGet.config (or ~/.nuget/NuGet/NuGet.Config):

<packageSources>
  <add key="EggyStudio" value="https://nuget.pkg.github.com/EggyStudio/index.json" />
</packageSources>

Then add the package:

dotnet add package BlueprintShell

Usage

Standalone mode - dedicated port

Spin up a separate Kestrel server inside your existing process. The shell runs independently and doesn't interfere with your app's own HTTP stack.

using BlueprintShell;

// Start the shell server (non-blocking).
var shell = await EditorServerHost.StartAsync(new BlueprintShellOptions
{
    Url      = "http://localhost:5100",
    AppTitle = "My Engine Editor",
});

// ... run your loop ...

// Shut down cleanly when done.
await shell.StopAsync();

Open http://localhost:5100 in a browser to see the shell.

Embedded mode - same ASP.NET Core app

If your host is already an ASP.NET Core app, register the shell services and map its hub into your existing pipeline:

// Program.cs
builder.Services.AddBlueprintShell(o =>
{
    o.AppTitle = "My Editor";
});

// After builder.Build():
app.MapBlueprintShell();   // maps /shell-hub SignalR endpoint

Then add the shell's App, Routes, and ShellLayout into your Razor routing as needed.


Configuration - BlueprintShellOptions

Property Type Default Description
Url string "http://localhost:5000" Kestrel listen URL. Accepts any format supported by UseUrls, e.g. "http://*:5100" or "https://localhost:5001;http://localhost:5000". Only used in standalone mode.
AppTitle string "Blueprint Shell" Title shown in the shell header and browser tab.
LaunchBrowser bool false Open the system browser pointing at Url once the server is ready.
ChromeMode ShellChromeMode Full Default chrome level: Full (dock + header), Minimal (header only), Hidden (nothing - just shell services around @Body).
ChromeFor Func<HttpContext, ShellChromeMode>? null Per-request override; evaluated before ChromeMode.
ChromeForServices Func<HttpContext, IServiceProvider, ShellChromeMode>? null DI-aware variant of ChromeFor; evaluated first when set.
MobileBreakpointPx int 768 Viewport width below which the dock collapses.
MobileBehavior MobileBehavior Stacked Stacked, Drawer, or Hidden below the mobile breakpoint.
StaticAssetsBasePath string? null Override the /_content/BlueprintShell prefix used by the shell's CSS imports.
ScanAssemblies IList<Assembly> all loaded Explicit assemblies to scan for [EditorShell], [EditorPanel], [ReaderPage]. Empty (default) scans the whole AppDomain.
EnableDiagnostics bool false Expose GET /_shell/diagnostics (recommended only in Development).
SignalRHubPath string "/shell-hub" Customise to avoid collisions with other SignalR endpoints.
ActiveTheme string? null Name of the initial theme preset (see Theme presets).

Per-request chrome (anonymous reader vs. logged-in editor)

builder.Services.AddBlueprintShell(o =>
{
    o.AppTitle = "Encyclopedia";
    o.ChromeFor = ctx => ctx.Request.Path.StartsWithSegments("/edit")
        ? ShellChromeMode.Full
        : ShellChromeMode.Hidden;
});

Writing panels

Decorate any Blazor component with [EditorPanel] to register it as a dockable panel. The attribute is discovered at startup by StaticShellLoader via reflection.

@* MyPanel.razor *@
@attribute [EditorPanel("my-panel", "My Panel", DockZone.Right,
    Icon    = "settings",
    Route   = "/my-panel",
    Visible = true)]

@inject ShellRegistry Registry

<div class="p-4">
    <h3 class="text-sm font-semibold">Hello from My Panel</h3>
</div>
Parameter Description
id Unique panel key (used for tab-grouping and persistence).
title Display name in the panel header / tab.
zone Default dock position: Top, Left, Right, Bottom, Center, Float.
Icon Optional Lucide icon name shown in the header.
Route When set, a nav-link to this panel appears in the shell header and the catch-all router serves the panel at the given URL. Route parameters (e.g. "/edit/article/{Identifier}") are forwarded to matching [Parameter] properties — same convention as @page.
TabGroup Groups this panel as a tab inside another panel.
TabOrder Sort order within a tab group.
InitialSize Fraction (0–1) of the parent dock area. Default 0.25.
Closeable Whether the user can close the panel. Default true.
Visible Whether the panel starts visible. Default true.
RequiresRole Filters the panel out for requests where IShellAuthContext.Roles doesn't contain the value ("*" means any authenticated user).

Writing shell builders

For code-driven layout (no Razor), implement IEditorShellBuilder and decorate with [EditorShell]:

using BlueprintShell.Shell;

[EditorShell]
public class MyShell : IEditorShellBuilder
{
    public int Order => 0;   // lower runs first

    public void Build(IShellBuilder shell)
    {
        shell.Panel("toolbar", "Toolbar", DockZone.Top, p => p
            .Content(c => c
                .Row(null, row => row
                    .Button(null, "Save",  onClick: OnSave,  icon: "save")
                    .Button(null, "Build", onClick: OnBuild, icon: "hammer", variant: "secondary")
                    .Spacer()
                    .DarkModeToggle())));
    }

    private static void OnSave()  { /* ... */ }
    private static void OnBuild() { /* ... */ }
}

Multiple builders coexist: Order controls execution order; higher-precedence sources (dynamic hot-reload) override lower ones on panel-id collisions.


Multiple shell sources

ShellRegistry supports multiple named sources that are merged at runtime. This lets an engine contribute a static source at startup and a hot-reload compiler contribute a dynamic source later:

var registry = new ShellRegistry();

// Static source - built-in panels:
registry.RegisterSource("static", new ShellSource
{
    Builders   = new[] { new MyShell() },
    Precedence = 0,
});

// Dynamic source - runtime-compiled panels override static ones on id collision:
registry.RegisterSource("dynamic", new ShellSource
{
    PanelComponents = discoveredPanels,
    Precedence      = 100,
});

var shell = await EditorServerHost.StartAsync(registry: registry);

Reader pages

Reader pages are routed components that bypass the dock layout - useful for full-bleed content (articles, marketing pages) hosted alongside an editor.

@* Article.razor *@
@attribute [ReaderPage("article", "/wiki/{Identifier}",
    Chrome = ShellChromeMode.Hidden)]

@code {
    [Parameter] public string? Identifier { get; set; }
}

The shell resolves the route via a catch-all router and forwards segments as [Parameter] properties on the component (same convention as @page).

Incremental adoption (UseBlazorRouter = true)

If you already have a catalog of @page-routed components and don't want to migrate them wholesale, set UseBlazorRouter = true. The shell records the entry in ShellRegistry (so it shows up in diagnostics and participates in role tracking) but the catch-all router skips it — your existing Blazor Router continues to handle the URL. RequiresRole becomes advisory in this mode: the shell can't gate a request it doesn't render, so enforce the role check inside the component or via your own routing middleware.

@page "/wiki/{Identifier}"
@attribute [ReaderPage("article", "/wiki/{Identifier}", UseBlazorRouter = true)]

Auth-aware chrome

Implement IShellAuthContext and the shell will filter [EditorPanel(..., RequiresRole = "...")] panels and [ReaderPage(..., RequiresRole = "...")] pages automatically. Use "*" to require any authenticated user.

public sealed class MyAuthContext : IShellAuthContext
{
    public bool IsAuthenticated => /* ... */;
    public string? UserId => /* ... */;
    public IReadOnlySet<string> Roles => /* ... */;
}

builder.Services.AddScoped<IShellAuthContext, MyAuthContext>();

Header slots

Drop your own content into the shell header (e.g. a global search box):

registry.RegisterHeaderSlot(builder =>
{
    builder.OpenComponent<GlobalSearchBox>(0);
    builder.CloseComponent();
});

Theme presets

registry.RegisterTheme("reader", new ThemePreset
{
    Variables =
    {
        ["--background"] = "oklch(1 0 0)",
        ["--foreground"] = "oklch(0.15 0 0)",
        ["--font-sans"]  = "\"Source Serif Pro\", Georgia, serif",
    },
});

registry.SetActiveTheme("reader");

The active preset is rendered as <style id="bp-active-theme"> and replaced when SetActiveTheme is called.

PWA

app.MapBlueprintShellPwa(new BlueprintShellPwaOptions
{
    ShortName        = "Encyclopedia",
    ThemeColor       = "#0a0a0a",
    BackgroundColor  = "#ffffff",
    CacheStaticAssets = true,
});

Serves a manifest at /manifest.webmanifest and a service worker at /sw.js, precaching the shell's own CSS and any URLs you add via ExtraCacheUrls.

The generated service worker skips /_blazor, /_framework, and the configured SignalR hub path by default (caching them breaks Blazor Server reconnects after deploy). Add more prefixes via BlueprintShellPwaOptions.ExcludePathPrefixes.

To register the service worker on the client, either drop the helper component into your layout / App.razor:

<BlueprintShellPwaBootstrap />

…or set RenderRegistrationScript = false and emit the navigator.serviceWorker.register('/sw.js') call yourself.

DI-aware chrome resolution

ChromeFor runs without access to scoped services. When you need to resolve IShellAuthContext (or anything else from DI) to pick chrome, use ChromeForServices instead — it receives the scoped IServiceProvider and is evaluated before ChromeFor.

o.ChromeForServices = (ctx, sp) =>
{
    var auth = sp.GetRequiredService<IShellAuthContext>();
    return auth.IsAuthenticated ? ShellChromeMode.Full : ShellChromeMode.Hidden;
};

Dialogs / popovers (BbPortalHost)

BlazorBlueprint renders dialogs, popovers, tooltips, and toasts into a portal host — your layout must include <BbPortalHost /> (from BlazorBlueprint.Primitives.Services, not BlazorBlueprint.Primitives) for those primitives to appear. All three of the shell's built-in layouts (ShellLayout, MinimalLayout, ReaderLayout) already include it; consumers writing their own layout need to add it themselves.

Diagnostics

Set o.EnableDiagnostics = true and GET /_shell/diagnostics returns a JSON dump of panels, reader pages, themes, and loaded assemblies - useful when something doesn't render and you want to know whether it was discovered.

CSS / theming

The shell uses BlazorBlueprint's OKLCH-based CSS variable system. Override variables in your own stylesheet or supply raw CSS snippets via the builder API:

// Custom CSS injected into every page:
registry.RegisterSource("my-theme", new ShellSource
{
    CustomCss = new[]
    {
        ":root { --primary: oklch(0.55 0.2 260); }",
    },
});

License

MPL 2.0

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.8 0 5/18/2026
0.1.7 0 5/17/2026
0.1.6 0 5/17/2026
0.1.4 0 5/17/2026
0.1.3 27 5/17/2026
0.1.2 46 5/15/2026