diff --git a/examples/ShellDocs.Preview/Program.cs b/examples/ShellDocs.Preview/Program.cs
index 317e4dc..c75944f 100644
--- a/examples/ShellDocs.Preview/Program.cs
+++ b/examples/ShellDocs.Preview/Program.cs
@@ -33,6 +33,17 @@
o.AddNavLink("Showcase", "/showcase");
o.AddNavLink("Blog", "/blog");
+ o.AddPackage("shelldocs", "ShellDocs", "The docs framework itself.", "/docs/introduction",
+ "M12 2 4 6v6c0 5 3.5 9.5 8 10 4.5-.5 8-5 8-10V6z");
+ o.AddPackage("shelldocs.markdown", "ShellDocs.Markdown", "Markdig extensions, slots, MDX.", "/docs/markdown",
+ "M4 4h16v16H4z M4 9h16 M9 4v16");
+ o.AddPackage("shelldocs.core", "ShellDocs.Core", "Navigation graph + content model.", "/docs/core",
+ "M12 2 2 7l10 5 10-5-10-5z M2 17l10 5 10-5 M2 12l10 5 10-5");
+ o.AddPackage("shelldocs.cli", "ShellDocs.CLI", "Scaffold, build, publish.", "/docs/cli",
+ "m8 6-6 6 6 6 M16 6l6 6-6 6");
+ o.AddPackage("shelldocs.components","ShellDocs.Components","Layouts, header, sidebar, TOC.", "/docs/components",
+ "M3 3h7v7H3z M14 3h7v7h-7z M3 14h7v7H3z M14 14h7v7h-7z");
+
// Callout, Card, Steps, FileTree, CodeGroup, LinkCard etc. are
// auto-registered by AddShellDocs — no extra work needed here.
});
diff --git a/src/ShellDocs.Components/Chrome/PackageSelector.razor b/src/ShellDocs.Components/Chrome/PackageSelector.razor
index 1f2d29f..3937134 100644
--- a/src/ShellDocs.Components/Chrome/PackageSelector.razor
+++ b/src/ShellDocs.Components/Chrome/PackageSelector.razor
@@ -1,63 +1,70 @@
@inject NavigationManager Nav
+@inject ShellDocsOptions Options
-
-
+}
@code {
private bool _open;
- private record Package(string Id, string Title, string Description, string IconPath, string RootUrl);
+ private const string DefaultIcon = "M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z M3.27 6.96 12 12.01l8.73-5.05 M12 22.08V12";
- private static readonly List Packages = new()
+ // Longest RootUrl prefix wins — "/docs/components" beats "/docs" for "/docs/components/callout".
+ private DocsPackage Selected
{
- new("shelldocs", "ShellDocs", "The docs framework itself.", "M12 2 4 6v6c0 5 3.5 9.5 8 10 4.5-.5 8-5 8-10V6z", "/docs/introduction"),
- new("shelldocs.markdown", "ShellDocs.Markdown", "Markdig extensions, slots, MDX.", "M4 4h16v16H4z M4 9h16 M9 4v16", "/docs/markdown"),
- new("shelldocs.core", "ShellDocs.Core", "Navigation graph + content model.","M12 2 2 7l10 5 10-5-10-5z M2 17l10 5 10-5 M2 12l10 5 10-5", "/docs/core"),
- new("shelldocs.cli", "ShellDocs.CLI", "Scaffold, build, publish.", "m8 6-6 6 6 6 M16 6l6 6-6 6", "/docs/cli"),
- new("shelldocs.components","ShellDocs.Components","Layouts, header, sidebar, TOC.","M3 3h7v7H3z M14 3h7v7h-7z M3 14h7v7H3z M14 14h7v7h-7z", "/docs/components")
- };
-
- private Package Selected => Packages.FirstOrDefault(p => Nav.Uri.Contains(p.RootUrl.Split('/', StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? "")) ?? Packages[0];
+ get
+ {
+ var path = new Uri(Nav.Uri).AbsolutePath;
+ return Options.Packages
+ .Where(p => path.StartsWith(p.RootUrl, StringComparison.OrdinalIgnoreCase))
+ .OrderByDescending(p => p.RootUrl.Length)
+ .FirstOrDefault()
+ ?? Options.Packages[0];
+ }
+ }
private void Toggle() => _open = !_open;
- private void Choose(Package pkg)
+ private void Choose(DocsPackage pkg)
{
_open = false;
Nav.NavigateTo(pkg.RootUrl);
diff --git a/src/ShellDocs.Components/ShellDocsOptions.cs b/src/ShellDocs.Components/ShellDocsOptions.cs
index 4e6db87..d048b3f 100644
--- a/src/ShellDocs.Components/ShellDocsOptions.cs
+++ b/src/ShellDocs.Components/ShellDocsOptions.cs
@@ -14,6 +14,8 @@ public class ShellDocsOptions
public DocsLayoutVariant LayoutVariant { get; set; } = DocsLayoutVariant.TopNav;
public List PrimaryNav { get; } = new();
+ // 0 or 1 entries hides the sidebar package selector entirely.
+ public List Packages { get; } = new();
public List RegisteredComponents { get; } = new();
/* Per-type tag alias — when a type appears here, BuildTypeRegistry uses this
string as the markdown-facing tag name instead of the type's short name.
@@ -109,6 +111,12 @@ public ShellDocsOptions AddNavMenu(string label, params NavMenuItem[] items)
return this;
}
+ public ShellDocsOptions AddPackage(string id, string title, string description, string rootUrl, string? iconPath = null)
+ {
+ Packages.Add(new DocsPackage(id, title, description, rootUrl, iconPath));
+ return this;
+ }
+
internal TypeRegistry BuildTypeRegistry()
{
var registry = new TypeRegistry();
@@ -126,6 +134,9 @@ internal TypeRegistry BuildTypeRegistry()
public record NavLink(string Label, string Href, List? Children = null);
public record NavMenuItem(string Label, string Href, string? Description = null, string? IconSvg = null);
+// IconPath is a raw SVG `d` attribute value on a 24×24 viewBox — not a URL.
+public record DocsPackage(string Id, string Title, string Description, string RootUrl, string? IconPath = null);
+
public enum ShellDocsTheme
{
Shadcn,
diff --git a/tests/ShellDocs.Tests/PackageRegistrationTests.cs b/tests/ShellDocs.Tests/PackageRegistrationTests.cs
new file mode 100644
index 0000000..5325ce9
--- /dev/null
+++ b/tests/ShellDocs.Tests/PackageRegistrationTests.cs
@@ -0,0 +1,30 @@
+using ShellDocs.Components;
+using Xunit;
+
+namespace ShellDocs.Tests;
+
+public class PackageRegistrationTests
+{
+ [Fact]
+ public void AddPackage_FluentChain_AppendsInOrder()
+ {
+ var options = new ShellDocsOptions()
+ .AddPackage("core", "Core", "Foundation.", "/docs/core")
+ .AddPackage("components", "Components", "Widgets.", "/docs/components", iconPath: "M0 0h10v10H0z");
+
+ Assert.Equal(2, options.Packages.Count);
+ Assert.Equal("core", options.Packages[0].Id);
+ Assert.Equal("Core", options.Packages[0].Title);
+ Assert.Equal("/docs/core", options.Packages[0].RootUrl);
+ Assert.Null(options.Packages[0].IconPath);
+ Assert.Equal("components", options.Packages[1].Id);
+ Assert.Equal("M0 0h10v10H0z", options.Packages[1].IconPath);
+ }
+
+ [Fact]
+ public void Packages_DefaultToEmpty_SoSelectorHides()
+ {
+ var options = new ShellDocsOptions();
+ Assert.Empty(options.Packages);
+ }
+}