Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
21e88aa
feat(nav-v2): add section: item type for independent nav islands
theletterf Apr 16, 2026
071f456
feat(nav-v2): migrate top-level labels to section: items
theletterf Apr 16, 2026
3a46434
feat(nav-v2): wrap all content in Docs section; add Release notes, Tr…
theletterf Apr 16, 2026
ef333f8
fix(nav-v2): compact left-aligned top bar, section root highlight, au…
theletterf Apr 16, 2026
23f27b3
feat(nav-v2): add Extension points and Account isolated sections; rem…
theletterf Apr 16, 2026
7fed2c5
fix: correct import ordering in PlaceholderPageWriter
theletterf Apr 16, 2026
cdabed1
feat(nav-v2): support absolute URLs for section tabs; add API reference
theletterf Apr 16, 2026
0557264
feat(nav-v2): add island: item type for intra-section nav islands
theletterf Apr 16, 2026
1e4da5a
feat(nav-v2): convert Account and preferences to island under Docs
theletterf Apr 22, 2026
ca03a50
Merge branch 'nav-v2' into nav-v2-sections
theletterf Apr 22, 2026
a0b98a3
fix(nav-v2): register island root URL in island lookup
theletterf Apr 22, 2026
b813024
fix(nav-v2): use toc root identity for island lookup instead of URL m…
theletterf Apr 22, 2026
e02fc81
fix(nav-v2): scope top-level padding to label sections only
theletterf Apr 22, 2026
6803630
style: fix Prettier formatting in styles.css
theletterf Apr 22, 2026
fdb278a
fix(nav-v2): back arrow swaps full page including sidebar
theletterf Apr 22, 2026
2d20e27
fix(nav-v2): open external section links in new tab with ↗ indicator
theletterf Apr 23, 2026
c4c5bab
feat(nav-v2): rename Docs to Guides, API reference to APIs
theletterf Apr 23, 2026
13a6695
feat(nav-v2): reorder tabs by task flow
theletterf Apr 23, 2026
4e3bde9
fix(nav-v2): shorten long nav label to 'Query languages and APIs'
theletterf Apr 23, 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
5,616 changes: 2,828 additions & 2,788 deletions config/navigation-v2.yml

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/Elastic.Codex/Page/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
Previous = Model.PreviousDocument,
Next = Model.NextDocument,
NavigationHtml = Model.NavigationHtml,
NavV2Sections = Model.NavV2Sections,
ActiveSectionId = Model.ActiveSectionId,
UrlPathPrefix = Model.UrlPathPrefix,
GithubEditUrl = Model.GithubEditUrl,
MarkdownUrl = Model.MarkdownUrl,
Expand Down
46 changes: 46 additions & 0 deletions src/Elastic.Documentation.Configuration/Toc/NavigationV2File.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,28 @@ IReadOnlyList<INavV2Item> Children
/// </summary>
public record PageNavV2Item(Uri? Page, string? Title) : INavV2Item;

/// <summary>
/// A top-level section that owns an independent sidebar tree and (optionally) a tab in the
/// secondary nav bar. When <see cref="Isolated"/> is <c>true</c> the section does not appear
/// in the top bar and renders with a back-arrow instead.
/// </summary>
public record SectionNavV2Item(
string Label,
string Url,
bool Isolated,
IReadOnlyList<INavV2Item> Children
) : INavV2Item;

/// <summary>
/// A nav island nested inside a section. When a user navigates into pages belonging to
/// this island's toc, the sidebar shows only the island's tree with a back arrow to the
/// parent section. The island does not appear in the secondary nav bar.
/// </summary>
public record IslandNavV2Item(
string Label,
Uri Source
) : INavV2Item;

/// <summary>
/// A folder node — has a title and children, with an optional <c>page:</c> URI.
/// When <see cref="Page"/> is set, the header is a real clickable link; otherwise it renders
Expand Down Expand Up @@ -110,6 +132,30 @@ private static IReadOnlyList<INavV2Item> ReadItemList(IParser parser, ObjectDese
parser.SkipThisAndNestedEvents();
}

if (dict.TryGetValue("section", out var sectionVal) && sectionVal is string sectionStr)
{
var sectionUrl = dict.TryGetValue("url", out var suVal) && suVal is string suStr ? suStr : "/";
var isolated = dict.TryGetValue("isolated", out var isoVal)
&& isoVal is string isoStr
&& bool.TryParse(isoStr, out var isoBool)
&& isoBool;
var sectionChildren = dict.TryGetValue("children", out var sch) && sch is IReadOnlyList<INavV2Item> sChildList
? sChildList
: [];
return new SectionNavV2Item(sectionStr, sectionUrl, isolated, sectionChildren);
}

if (dict.TryGetValue("island", out var islandVal) && islandVal is string islandStr)
{
if (dict.TryGetValue("toc", out var itVal) && itVal is string itStr)
{
var itUriString = itStr.Contains("://") ? itStr : $"docs-content://{itStr}";
if (Uri.TryCreate(itUriString, UriKind.Absolute, out var itSource))
return new IslandNavV2Item(islandStr, itSource);
}
return null;
}

if (dict.TryGetValue("label", out var labelVal) && labelVal is string labelStr)
{
var expanded = dict.TryGetValue("expanded", out var expVal)
Expand Down
49 changes: 49 additions & 0 deletions src/Elastic.Documentation.Navigation/V2/IslandNavigationNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using Elastic.Documentation.Extensions;

namespace Elastic.Documentation.Navigation.V2;

/// <summary>
/// A nav island that wraps an existing toc node. When a page belongs to this island,
/// the sidebar shows only the island's tree with a back arrow to the parent section.
/// In the parent section's sidebar, the island renders as a normal folder link.
/// </summary>
public class IslandNavigationNode(
string label,
IRootNavigationItem<IDocumentationFile, INavigationItem> source,
INodeNavigationItem<INavigationModel, INavigationItem>? parent
) : INodeNavigationItem<INavigationModel, INavigationItem>
{
/// <summary>The Id of the wrapped toc root, used for island lookup by nav ownership.</summary>
public string SourceTocRootId { get; } = source.Id;

/// <inheritdoc />
public string Id { get; } = ShortId.Create("island", label);

/// <inheritdoc />
public string Url => source.Url;

/// <inheritdoc />
public string NavigationTitle { get; } = label;

/// <inheritdoc />
public IRootNavigationItem<INavigationModel, INavigationItem> NavigationRoot { get; } = parent?.NavigationRoot!;

/// <inheritdoc />
public INodeNavigationItem<INavigationModel, INavigationItem>? Parent { get; set; } = parent;

/// <inheritdoc />
public bool Hidden => source.Hidden;

/// <inheritdoc />
public int NavigationIndex { get; set; }

/// <inheritdoc />
public ILeafNavigationItem<INavigationModel> Index => source.Index;

/// <inheritdoc />
public IReadOnlyCollection<INavigationItem> NavigationItems => source.NavigationItems;
}
30 changes: 30 additions & 0 deletions src/Elastic.Documentation.Navigation/V2/NavigationSection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

namespace Elastic.Documentation.Navigation.V2;

/// <summary>
/// Lightweight data carrier for a navigation section, used by the rendering layer
/// to drive the secondary nav bar tabs and resolve which sidebar to show.
/// </summary>
public record NavigationSection(
string Id,
string Label,
string Url,
bool Isolated,
IReadOnlyList<INavigationItem> NavigationItems
);

/// <summary>
/// A nav island nested within a parent section. When a page belongs to an island,
/// the sidebar shows only the island's tree with a back arrow to the parent section.
/// </summary>
public record NavigationIsland(
string Id,
string Label,
string Url,
string SourceTocRootId,
NavigationSection ParentSection,
IReadOnlyList<INavigationItem> NavigationItems
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using Elastic.Documentation.Extensions;

namespace Elastic.Documentation.Navigation.V2;

/// <summary>
/// A top-level section that owns an independent sidebar nav tree.
/// Unlike <see cref="LabelNavigationNode"/>, a section has a real URL (the tab link)
/// and an <see cref="Isolated"/> flag that controls whether it appears in the top bar.
/// </summary>
public class SectionNavigationNode : INodeNavigationItem<INavigationModel, INavigationItem>
{
private readonly SectionIndexLeaf _index;

public SectionNavigationNode(
string label,
string url,
bool isolated,
IReadOnlyCollection<INavigationItem> children,
INodeNavigationItem<INavigationModel, INavigationItem>? parent
)
{
Id = ShortId.Create("section", label);
NavigationTitle = label;
Url = url;
Isolated = isolated;
NavigationItems = children;
Parent = parent;
NavigationRoot = parent?.NavigationRoot!;
_index = new SectionIndexLeaf(this);
}

/// <summary>Whether this section is excluded from the top bar and renders with a back arrow.</summary>
public bool Isolated { get; }

/// <inheritdoc />
public string Id { get; }

/// <inheritdoc />
public string Url { get; }

/// <inheritdoc />
public string NavigationTitle { get; }

/// <inheritdoc />
public IRootNavigationItem<INavigationModel, INavigationItem> NavigationRoot { get; }

/// <inheritdoc />
public INodeNavigationItem<INavigationModel, INavigationItem>? Parent { get; set; }

/// <inheritdoc />
public bool Hidden => false;

/// <inheritdoc />
public int NavigationIndex { get; set; }

/// <inheritdoc />
public ILeafNavigationItem<INavigationModel> Index => _index;

/// <inheritdoc />
public IReadOnlyCollection<INavigationItem> NavigationItems { get; }

private sealed class SectionIndexLeaf(SectionNavigationNode owner)
: ILeafNavigationItem<INavigationModel>, INavigationModel
{
public INavigationModel Model => this;
public string Url => owner.Url;
public string NavigationTitle => owner.NavigationTitle;
public IRootNavigationItem<INavigationModel, INavigationItem> NavigationRoot => owner.NavigationRoot;
public INodeNavigationItem<INavigationModel, INavigationItem>? Parent { get; set; } = owner;
public bool Hidden => true;
public int NavigationIndex { get; set; }
}
}
Loading
Loading