feat: list pages and islands - #3781
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>
TryPeek returns false as soon as TryRead dequeues an item from the channel, but output.Write(item) inside Drain() hasn't run yet. On a loaded CI runner this window is enough for WaitForDrain to return before the item reaches the IDiagnosticsOutput sinks. Fix: track a volatile _draining flag that is set at the start of Drain() and cleared at the end. WaitForDrain now polls TryPeek()||IsDraining so it keeps waiting while a drain pass is in flight even if the channel appears empty. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…odel Resolve conflicts after PR #3784 (Navigation: drop ViewModel, use Leaf/Node + RootIndex). The new rendering pipeline projects raw nav items into NavigationRenderNode records before templates see them. Island listing nodes need a matching projection: - Add IsIslandListing bool to NavigationRenderNode (hashed in ContentHash) - NavigationRenderModel.CreateNode: detect IsIslandListing; set ShowToggle=false and NavigationItems=[] so the toggle and subtree are suppressed in the main nav - _TocTreeNav.cshtml: for IsIslandListing nodes render a link chevron (<a href>) instead of the expand/collapse label; guard subtree on NavigationItems.Count > 0 - IsolatedBuildNavigationHtmlWriter / GlobalNavigationHtmlWriter: use separate ConcurrentDictionary<string,string> for island HTML (NavigationRenderCache is keyed by IRootNavigationItem object reference, not by string id) - GlobalNavigationHtmlWriter: drop IDisposable + Dispose() (semaphore removed in #3784) 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>
|
The island sidebar will disappear on the first click inside it unless links within the island also swap The nav-v2 branch fixes this by passing |
|
I double checked @theletterf but not sure thats true on |
Summary
Implements a way to create listings/overview pages that automatically lists and groups pages nested pages.
Great if you have data driven docs that you don't want to modify
docset.ymlfor. Also if you have long running branches all trying to append a child in the toc (like our RFC docs) these tend to always merge conflict. This solves that.Adds a
listing:TOC entry type that glob-discovers documentation files, hides them from the main nav tree, and generates a grouped index page with a client-side filter.listing: <path>— declares a listing root; files are discovered viaglob:and grouped bylisting:frontmattervisual: none|groups|all— controls island nav depth: groups only, or groups + pagesisland: true— listing pages get a dedicated sidebar nav instead of the main tree; the listing appears as a single item with a>chevron in the parent navgroups: [...]— explicit group ordering with alphabetical fallbacksort: asc|desc— page order within groupsOverview pages
Visual
Dictates how much you want to see in the nav of the generated pages, the default is
none.visual: all
visual: groups
visual: none
Island nav
cursorful-video-1785946111134.mp4
An island creates a dedicated navigation for the generated tree, this is great if you have 100s of pages. This allows you to use
visual: allin a way that doesn't bloat the overal parent navigation.Depends on / Related
docs-eng-team usage: elastic/docs-eng-team#735
A first usage of this feature to list our RFCs automatically.
Follow ups
We could extend
island: trueto- folderin thetocand allow nesting/stacking islands. This would do wonders for our reference docs where we can make e.g all top level items an island so our reference navigation is not HUGE. but alsoclientsoresqlcould be a sub island because these are HUGE subnavigations of their own.The other thing this unlocks is a much simpler extension mechanism for e.g detection rules.
Test plan
dotnet buildpasses./build.sh unit-testpasses~/Projects/docs-eng-team/docswithlisting: rfcs,visual: all,island: true— RFC index shows grouped cards + filter; island sidebar renders correctly on all pagesvisual: groups— island sidebar shows group headings only (no individual pages)island: true + visual: noneemits a build error>chevron (no children)🤖 Generated with Claude Code