feat: feature-flag navigation_preview.yml, enable for dev + assembler-preview - #3862
Merged
Conversation
Adds a new `listing:` entry type for docset.yml / toc.yml that:
- Glob-discovers pages under a folder and registers them in the nav
tree with Hidden=true (links, prev/next, search all work; nav tree
does not render them)
- Generates a grouped index page with stacked page-cards, a text
filter input, and multi-select group chips
- Pages opt into a group via frontmatter `listing: group-name`
(shorthand) or `listing: {group: group-name}` (mapping form)
- Supports a real index.md at the listing root and per group;
creates a synthetic page if absent (appends `:::{listing}\n:::`
to the body)
- Groups ordered by the `groups:` list in the TOC entry, then
remaining groups alphabetically; unnamed pages rendered last
New files:
- Toc/Listing/ListingFrontMatterReader.cs — frontmatter-only YAML read
- Extensions/Listing/ListingDocsBuilderExtension.cs — auto-enabled
extension; intercepts real index files; creates synthetic ones
- Extensions/Listing/ListingIndexFile.cs — MarkdownFile subtype that
appends the listing directive to its content
- Myst/Directives/Listing/ListingBlock.cs — render-time card collection
- Myst/Directives/Listing/ListingView.cshtml — grouped page-cards UI
- Myst/Directives/Listing/ListingViewModel.cs
- Assets/listing.ts — client-side multi-select filter + group chips
- Assets/listing.test.ts — 17 Jest tests (pure predicate + DOM)
Key decisions:
- ExcludeFromIndexing added to INavigationItem (default = Hidden) so
listing pages are hidden from nav but still indexed for search
- ListingFrontMatterConverter handles both scalar and mapping YAML
- NavigationDocumentationFileLookup uses reference identity; synthetic
index files must be created directly (not via defaultFileHandling
which returns ExcludedFile, filtered from Files)
- MarkdownParser constructed before InstantiateExtensions so it can
be passed to ListingDocsBuilderExtension
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When visual: island is set, listing pages are hidden from the main nav tree but get a dedicated island sidebar nav instead. The island nav renders a back button linking to the parent root, a flat listing root entry (using the listing's navigation_title), and all groups with their pages expanded. - Add ListingVisual.Island enum value - Add IslandListingRoot and IsIslandListing to INavigationItem interface - Set IslandListingRoot on all island listing pages/groups so they render the island nav instead of the main tree - Set IsIslandListing on the listing root FolderNavigation so the index page also enters the island nav - Add IslandNavViewModel, _IslandNav.cshtml for island sidebar rendering - Wire island nav rendering in both IsolatedBuildNavigationHtmlWriter and GlobalNavigationHtmlWriter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove ListingVisual.Island; island nav is now configured with island: true independently of visual: (none|groups|all) - Add Island bool to ListingOptions; parse island: true/false from YAML - Validate: island: true + visual: none is an error (listing unreachable) - groupHidden is now derived from visual: none only, not island status - Back button in _IslandNav uses full-width outlined button style (border-2 border-blue-elastic, w-full) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Island listings always show as a single item in the parent nav tree. The visual: option now controls what the island sidebar renders: - visual: groups -> island shows group headings only (no pages listed) - visual: all -> island shows groups with their pages Parent nav changes: - Island listing groups are always hidden from the parent tree - Island folders render a link chevron (right-pointing) instead of a toggle, so clicking it navigates into the island Island nav changes: - Pages beneath groups only rendered when visual: all - Visual passed through IslandVisual on FolderNavigation / INavigationItem Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three fixes: 1. QueryIndex path-first matching: island groups hide their index page so the main nav tree suppresses the group node, but QueryIndex was skipping hidden items and picking the first visible content page as the folder index. Now QueryIndex checks by SourcePath first (visibility-independent) before falling back to the visibility scan. 2. Island nav group headings now use nav-link (flex w-full) instead of nav-folder-link, making them full-width clickable like all other nav items. Page items under groups use ml-4 indentation. 3. Island chevron in parent nav: remove inline style that was conflicting with nav-chevron CSS; the class already applies -rotate-90 (right- pointing) by default. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- NavigationRenderNodeKind gets an Island value alongside Leaf/Node - CreateNode returns Kind=Island for listing nodes with island:true; no NavigationItems/ShowToggle needed (island folders have no subtree in the main nav) - IsIslandListing bool removed from NavigationRenderNode (Kind captures it) - _TocTreeNav.cshtml: explicit else-if branch for Island renders a link with a double-chevron (>>) signalling the user enters a sub-navigation - Add icon-chevron-double-down symbol to the SVG sprite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d chevron
BuildContext: when --path targets the docs folder directly, rootFolder equals
DocumentationSourceDirectory and the FindGitRoot ceiling blocks walking up to
find .git one level above, so DocumentationSet.Name falls back to
"unknown-{docsFolder}". Use null ceiling (depth-1 limit) when rootFolder is
already the docs folder — the .git directory is always within one hop.
Double-chevron: island entries in the parent nav show >> (icon-chevron-double-down
in the SVG sprite, rotated right by nav-chevron CSS) to signal they open a
sub-navigation rather than expand an inline subtree.
NavigationRenderNodeKind: add Island as a dedicated kind so the template has a
clean three-way branch (Leaf / Island / Node) instead of a Node kind with a
separate IsIslandListing boolean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NavigationRenderCache only supports IRootNavigationItem keys via GetOrRenderAsync; island nav uses string keys. Use a separate ConcurrentDictionary<string, string> for island HTML caching and make RenderNavigation async to support awaiting island render. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Extends the island navigation concept from listing-only to any TOC element. Islands render as a ≫ stub in the parent sidebar; clicking swaps the sidebar for a dedicated island nav with a root-first back-link stack. ## New placement options for island: true - Root of docset.yml or any nested toc.yml - Inline beside - toc: <path> in parent docset.yml/toc.yml (OR-ed with the toc.yml's own flag) - Any - toc: entry in config/navigation.yml (assembler-side override; can only enable, never disable) In isolated builds the docset root with island: true is silently inert (no parent → RendersAsIsland() returns false). After SiteNavigation re-parents it the same flag becomes active. ## Design Three old members (IslandListingRoot, IsIslandListing, IslandVisual) on INavigationItem are collapsed into a single bool IsIsland, set via a new IAssignableIslandNavigation interface. The island root of a page is now found by walking the Parent chain (FindIslandRoot()), which makes nesting work for free. RendersAsIsland() = item.IsIsland && item.Parent is not null FindIslandRoot() = nearest ancestor where RendersAsIsland() is true ## Island sidebar The island view model now reuses the same NavigationRenderNode tree the main sidebar uses, plus a root-first back-link stack built by NavigationRenderModel.CreateIsland(). The old hand-rolled IslandNavGroup/IslandNavPage types and duplicated writer code are gone. The NavigationRenderCache key is widened from IRootNavigationItem to INavigationItem so islands share the same cache without the old per- writer ConcurrentDictionary that collided across repos. ## Tests added - IslandNavigationTests (7 tests): isolation-level coverage of all goal cases - IslandSiteNavigationTests (3 tests): assembler-level OR semantics, docset root island after re-parenting, navigation.yml override - NavigationRenderModelTests: IslandNode_ProjectsAsIslandKind, CreateIsland_BuildsBackLinkStack, CreateIsland_ContentHash tests - Configuration Tests: island: true parsing at all four config levels, private-repo stripping survival Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ion.yml
- SiteNavigation implicitly marks every top-level toc entry as an island
so the two parallel 'owns the sidebar' mechanisms (top-level sections +
explicit islands) are now one: FindIslandRoot resolves both.
- Delete _IslandNav.cshtml and IslandNavViewModel; merge their behavior
into NavigationRenderModel + _TocTree.cshtml:
- NavigationRenderModel.Create gains BackLinks computed from the
render root's ancestor walk (nav root suppressed when dropdown is on)
- CreateRootIndex now shows the island's own title row for nested islands
in assembler builds
- Current top-level item resolved by ancestor walk so nested islands
correctly highlight the right dropdown entry
- Both writers simplified to a single GetOrRenderAsync(renderRoot) path;
RenderIslandAsync removed.
- config/navigation.yml: add island: true to 69 reference section entries
(elasticsearch, kibana, cloud, security, observability, ingestion tools,
APM agents x8, elasticsearch-clients x11, ecs-logging x9, EDOT SDKs x8,
beats, logstash, logstash plugins, fleet, machine-learning, search-ui, ecs)
Top-level entries (reference, troubleshoot, …) stay without the marker —
SiteNavigation makes them islands structurally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace heavy border-2 box back-links with compact text links in a subtle grouped section separated from the dropdown by a border-b line - Add <hr class="border-grey-20"> after the island root-index row so the island home heading is visually distinct from the tree content below it Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…chronous Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tsFileSystem FileSystemFactory.ScopeSourceDirectory was removed; callers now construct CheckoutsFileSystem directly, matching the pattern in SiteNavigationTests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-preview Add a NAVIGATION_PREVIEW feature flag that switches the assembled site's navigation from config/navigation.yml to config/navigation_preview.yml. This lets island: true markers across /reference be staged and reviewed on real assembled builds before they hit production. How it works: - FeatureFlags gets NavigationPreviewEnabled (key: navigation-preview) - PublishEnvironment.ToFeatureFlags() normalizes UPPER_SNAKE yaml keys through FeatureFlags.Set() so env-var overrides (FEATURE_NAVIGATION_PREVIEW) work out of the box - ConfigurationFileProvider.UseNavigationPreview() repoints NavigationFile at a new navigation_preview.yml temp copy (falls back to navigation.yml when the file is absent, safe for old remote config refs) - GetEmbeddedStream gains fallback support to match Local/Remote branches - AssembleContext constructor calls UseNavigationPreview() immediately after resolving the environment — before any reader touches NavigationFile — so private-repo stripping applies to the preview content automatically - config/navigation_preview.yml: copy of navigation.yml + 69 island: true lines across the /reference subtrees - config/assembler.yml: NAVIGATION_PREVIEW: true under dev and preview (assembler-preview.yml uses --environment preview, so every PR preview deployment picks it up with no workflow change) config/navigation.yml is deliberately unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
theletterf
approved these changes
Aug 13, 2026
Mpdreamz
enabled auto-merge (squash)
August 13, 2026 14:49
Mpdreamz
disabled auto-merge
August 13, 2026 14:59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
NAVIGATION_PREVIEWfeature flag that switches assembled-site navigation fromconfig/navigation.ymltoconfig/navigation_preview.yml, enabling staged review of the ~69island: truemarkers across/referencebefore they go to production.Why a separate file instead of editing
navigation.ymldirectly: The island treatment is a large, visible restructure of the sidebar across the entire/referencesection. It needs to be exercised on a real assembled build first.Stacked on:
feature/toc-islands(#3851) →feature/toc-listings(#3781)What changes
FeatureFlags— addsNavigationPreviewEnabled(navigation-previewkey); env-varFEATURE_NAVIGATION_PREVIEWalso works for local testing without editing configPublishEnvironment.ToFeatureFlags()— normalizesUPPER_SNAKEyaml keys throughSet()so the round-trip is correct (guard:NavigationPreviewEnabled_ReadsUnderscoredEnvironmentKeytest)ConfigurationFileProvider.UseNavigationPreview()— repointsNavigationFileatnavigation_preview.yml; falls back tonavigation.ymlwhen the file is absent (safe for old remote config refs);GetEmbeddedStreamgains fallback support to match Local/Remote branchesAssembleContextctor — callsUseNavigationPreview()immediately after the environment is resolved, before anyNavigationFilereader, so private-repo stripping applies to preview content automaticallyconfig/navigation_preview.yml— copy ofnavigation.yml+ 69island: truelines across the/referencesubtreesconfig/assembler.yml—NAVIGATION_PREVIEW: trueunderdevandpreview(theassembler-previewworkflow uses--environment preview, so every PR preview deployment picks it up with no workflow change)config/navigation.ymlis not changed.Test plan
dotnet test tests/Elastic.Documentation.Configuration.Tests/— 668 pass including newUseNavigationPreviewTests./build.sh unit-test— all pass--environment prod— navigation byte-identical tomain; no islands--environment dev— log lineFeature flag navigation-previewappears;/referenceshows≫island stubsFEATURE_NAVIGATION_PREVIEW=true --environment prod— env-var override produces same result as dev🤖 Generated with Claude Code