22// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33// See the LICENSE file in the project root for more information
44
5+ using System . Diagnostics ;
56using Elastic . Documentation . Configuration . Assembler ;
67using Elastic . Documentation . Configuration . DocSet ;
78using Elastic . Documentation . Extensions ;
@@ -12,6 +13,7 @@ namespace Elastic.Documentation.Navigation.Assembler;
1213public record SiteNavigationNoIndexFile ( string NavigationTitle ) : IDocumentationFile ;
1314
1415
16+ [ DebuggerDisplay ( "{Url}" ) ]
1517public class SiteNavigation : IRootNavigationItem < IDocumentationFile , INavigationItem >
1618{
1719 private readonly string ? _sitePrefix ;
@@ -45,6 +47,7 @@ public SiteNavigation(SiteNavigationFile siteNavigationFile,
4547 }
4648 }
4749 }
50+ UnseenNodes = [ .. _nodes . Keys ] ;
4851
4952 // Build NavigationItems from SiteTableOfContentsRef items
5053 var items = new List < INavigationItem > ( ) ;
@@ -54,7 +57,9 @@ public SiteNavigation(SiteNavigationFile siteNavigationFile,
5457 var navItem = CreateSiteTableOfContentsNavigation (
5558 tocRef ,
5659 index ++ ,
57- context
60+ context ,
61+ this ,
62+ null
5863 ) ;
5964
6065 if ( navItem != null )
@@ -63,12 +68,16 @@ public SiteNavigation(SiteNavigationFile siteNavigationFile,
6368
6469 NavigationItems = items ;
6570 _ = this . UpdateNavigationIndex ( context ) ;
71+ var count = UnseenNodes . Count ;
72+ var unseen = string . Join ( ", " , UnseenNodes ) ;
6673 Index = this . FindIndex < IDocumentationFile > ( new NotFoundModel ( "/index.md" ) ) ;
6774 }
6875
6976 private readonly Dictionary < Uri , INodeNavigationItem < IDocumentationFile , INavigationItem > > _nodes ;
7077 public IReadOnlyDictionary < Uri , INodeNavigationItem < IDocumentationFile , INavigationItem > > Nodes => _nodes ;
7178
79+ private HashSet < Uri > UnseenNodes { get ; }
80+
7281 public IReadOnlyCollection < PhantomRegistration > Phantoms { get ; }
7382
7483 //TODO Obsolete?
@@ -135,7 +144,9 @@ public SiteNavigation(SiteNavigationFile siteNavigationFile,
135144 private INavigationItem ? CreateSiteTableOfContentsNavigation (
136145 SiteTableOfContentsRef tocRef ,
137146 int index ,
138- IDocumentationContext context
147+ IDocumentationContext context ,
148+ INodeNavigationItem < INavigationModel , INavigationItem > parent ,
149+ IRootNavigationItem < INavigationModel , INavigationItem > ? root
139150 )
140151 {
141152 var pathPrefix = tocRef . PathPrefix ;
@@ -146,14 +157,13 @@ IDocumentationContext context
146157 if ( tocRef . Source . Scheme != NarrativeRepository . RepositoryName )
147158 context . EmitError ( context . ConfigurationPath , $ "path_prefix is required for TOC reference: { tocRef . Source } ") ;
148159
149- pathPrefix = tocRef . Source . Scheme ;
150160 if ( ! string . IsNullOrEmpty ( tocRef . Source . Host ) )
151161 pathPrefix += $ "/{ tocRef . Source . Host } ";
152162 if ( ! string . IsNullOrEmpty ( tocRef . Source . AbsolutePath ) && tocRef . Source . AbsolutePath != "/" )
153163 pathPrefix += $ "/{ tocRef . Source . AbsolutePath } ";
154164 }
155165
156- // Normalize pathPrefix to remove leading/trailing slashes for consistent combination
166+ // Normalize pathPrefix to remove leading/trailing slashes for a consistent combination
157167 pathPrefix = pathPrefix . Trim ( '/' ) ;
158168
159169 // Combine with site prefix if present, otherwise ensure leading slash
@@ -173,12 +183,25 @@ IDocumentationContext context
173183 return null ;
174184 }
175185
186+ _ = UnseenNodes . Remove ( tocRef . Source ) ;
176187 // Set the navigation index
177188 node . NavigationIndex = index ;
178189 prefixProvider . PathPrefixProvider = new PathPrefixProvider ( pathPrefix ) ;
179190
180- // Recursively create child navigation items if children are specified
191+ var wrapped = new SiteTableOfContentsNavigation < IDocumentationFile > ( node , prefixProvider . PathPrefixProvider , parent , root ) ;
192+ parent = wrapped ;
193+ root ??= wrapped . NavigationRoot ;
194+
181195 var children = new List < INavigationItem > ( ) ;
196+ var nodeChildren = node . NavigationItems ;
197+ foreach ( var nodeChild in nodeChildren )
198+ {
199+ nodeChild . Parent = parent ;
200+ if ( nodeChild is IRootNavigationItem < IDocumentationFile , INavigationItem > )
201+ continue ;
202+ children . Add ( nodeChild ) ;
203+ }
204+ // Recursively create child navigation items if children are specified
182205 if ( tocRef . Children . Count > 0 )
183206 {
184207 var childIndex = 0 ;
@@ -187,20 +210,18 @@ IDocumentationContext context
187210 var childItem = CreateSiteTableOfContentsNavigation (
188211 child ,
189212 childIndex ++ ,
190- context
213+ context ,
214+ parent ,
215+ root
191216 ) ;
192217 if ( childItem != null )
193218 children . Add ( childItem ) ;
194219 }
195220 }
196- else
197- {
198- // If no children specified, use the node's original children
199- children = node . NavigationItems . ToList ( ) ;
200- }
201221
222+ wrapped . NavigationItems = children ;
202223 // Always return a wrapper to ensure path_prefix is the URL (not path_prefix + node's URL)
203- return new SiteTableOfContentsNavigation < IDocumentationFile > ( node , prefixProvider . PathPrefixProvider , children ) ;
224+ return wrapped ;
204225 }
205226}
206227
@@ -212,10 +233,12 @@ IDocumentationContext context
212233/// Wrapper for a navigation node that applies a path prefix to URLs and optionally
213234/// overrides the children to show only the children specified in the site navigation configuration.
214235/// </remarks>
236+ [ DebuggerDisplay ( "{Url}" ) ]
215237public sealed class SiteTableOfContentsNavigation < TModel > (
216238 INodeNavigationItem < TModel , INavigationItem > wrappedNode ,
217239 IPathPrefixProvider pathPrefixProvider ,
218- IReadOnlyCollection < INavigationItem > children
240+ INodeNavigationItem < INavigationModel , INavigationItem > parent ,
241+ IRootNavigationItem < INavigationModel , INavigationItem > ? root
219242 ) : INodeNavigationItem < TModel , INavigationItem > , INavigationPathPrefixProvider
220243 where TModel : IDocumentationFile
221244{
@@ -231,12 +254,13 @@ public string Url
231254 }
232255
233256 public string NavigationTitle => wrappedNode . NavigationTitle ;
234- public IRootNavigationItem < INavigationModel , INavigationItem > NavigationRoot => wrappedNode . NavigationRoot ;
257+ public IRootNavigationItem < INavigationModel , INavigationItem > NavigationRoot =>
258+ root ?? wrappedNode as IRootNavigationItem < INavigationModel , INavigationItem > ?? parent . NavigationRoot ;
235259
236260 public INodeNavigationItem < INavigationModel , INavigationItem > ? Parent
237261 {
238- get => wrappedNode . Parent ;
239- set => wrappedNode . Parent = value ;
262+ get => parent ;
263+ set { }
240264 }
241265
242266 public bool Hidden => wrappedNode . Hidden ;
@@ -255,7 +279,7 @@ public int NavigationIndex
255279 // Override to return the specified children from site navigation
256280 // Wrap children to apply path prefix recursively - but don't wrap children that are
257281 // already SiteTableOfContentsNavigation (they have their own path prefix)
258- public IReadOnlyCollection < INavigationItem > NavigationItems { get ; } = children ;
282+ public IReadOnlyCollection < INavigationItem > NavigationItems { get ; set ; } = [ ] ;
259283
260284 /// <inheritdoc />
261285 public IPathPrefixProvider PathPrefixProvider { get ; set ; } = pathPrefixProvider ;
0 commit comments