33// See the LICENSE file in the project root for more information
44
55using System . Text ;
6+ using Documentation . Assembler ;
67using Elastic . Documentation . Site . Navigation ;
8+ using Elastic . Markdown ;
79using Elastic . Markdown . IO ;
810using Elastic . Markdown . IO . Navigation ;
911
@@ -14,7 +16,7 @@ namespace Documentation.Assembler.Navigation;
1416/// </summary>
1517public class LlmsNavigationEnhancer
1618{
17- public string GenerateNavigationSections ( GlobalNavigation navigation )
19+ public async Task < string > GenerateNavigationSectionsAsync ( GlobalNavigation navigation )
1820 {
1921 var content = new StringBuilder ( ) ;
2022
@@ -39,8 +41,8 @@ public string GenerateNavigationSections(GlobalNavigation navigation)
3941 foreach ( var child in firstLevelChildren )
4042 {
4143 var title = child . NavigationTitle ;
42- var url = ConvertToMarkdownUrl ( child . Url ) ;
43- var description = GetDescription ( child ) ;
44+ var url = ConvertToAbsoluteMarkdownUrl ( child . Url ) ;
45+ var description = await GetDescriptionAsync ( child ) ;
4446
4547 _ = ! string . IsNullOrEmpty ( description )
4648 ? content . AppendLine ( $ "* [{ title } ]({ url } ): { description } ")
@@ -87,35 +89,62 @@ private static List<INavigationItem> GetFirstLevelChildren(DocumentationGroup gr
8789 return children ;
8890 }
8991
90- private static string ConvertToMarkdownUrl ( string url )
92+ private static string ConvertToAbsoluteMarkdownUrl ( string url )
9193 {
9294 // Convert HTML URLs to .md URLs for LLM consumption
93- // e.g., "/docs/solutions/search/" -> "/solutions/search.md"
95+ // e.g., "/docs/solutions/search/" -> "https://www.elastic.co/docs /solutions/search.md"
9496 var cleanUrl = url . TrimStart ( '/' ) ;
9597
96- // Remove "docs/" prefix if present
97- if ( cleanUrl . StartsWith ( "docs/" ) )
98- cleanUrl = cleanUrl . Substring ( 5 ) ;
98+ // Remove "docs/" prefix if present for the markdown filename
99+ var markdownPath = cleanUrl ;
100+ if ( markdownPath . StartsWith ( "docs/" ) )
101+ markdownPath = markdownPath . Substring ( 5 ) ;
99102
100103 // Convert directory URLs to .md files
101- if ( cleanUrl . EndsWith ( '/' ) )
102- cleanUrl = cleanUrl . TrimEnd ( '/' ) + ".md" ;
103- else if ( ! cleanUrl . EndsWith ( ".md" ) )
104- cleanUrl += ".md" ;
105-
106- return "/" + cleanUrl ;
104+ if ( markdownPath . EndsWith ( '/' ) )
105+ markdownPath = markdownPath . TrimEnd ( '/' ) + ".md" ;
106+ else if ( ! markdownPath . EndsWith ( ".md" ) )
107+ markdownPath += ".md" ;
108+
109+ // Make absolute URL using the canonical base URL (always https://www.elastic.co for production)
110+ var baseUrl = "https://www.elastic.co" ;
111+ return $ "{ baseUrl } /docs/{ markdownPath } ";
107112 }
108113
109- private static string ? GetDescription ( INavigationItem navigationItem ) => navigationItem switch
114+ private static async Task < string ? > GetDescriptionAsync ( INavigationItem navigationItem )
110115 {
111- // For file navigation items, extract from frontmatter
112- FileNavigationItem fileItem when fileItem . Model is MarkdownFile markdownFile
113- => markdownFile . YamlFrontMatter ? . Description ,
116+ var descriptionGenerator = new DescriptionGenerator ( ) ;
117+
118+ return navigationItem switch
119+ {
120+ // For file navigation items, extract from frontmatter or generate
121+ FileNavigationItem fileItem when fileItem . Model is MarkdownFile markdownFile =>
122+ await GetDescriptionFromMarkdownFileAsync ( markdownFile , descriptionGenerator ) ,
114123
115- // For documentation groups, try to get from index file
116- DocumentationGroup group when group . Index is MarkdownFile indexFile
117- => indexFile . YamlFrontMatter ? . Description ,
124+ // For documentation groups, try to get from index file
125+ DocumentationGroup group when group . Index is MarkdownFile indexFile =>
126+ await GetDescriptionFromMarkdownFileAsync ( indexFile , descriptionGenerator ) ,
118127
119- _ => null
120- } ;
128+ _ => null
129+ } ;
130+ }
131+
132+ private static async Task < string ? > GetDescriptionFromMarkdownFileAsync ( MarkdownFile markdownFile , DescriptionGenerator descriptionGenerator )
133+ {
134+ // First try frontmatter description
135+ if ( ! string . IsNullOrEmpty ( markdownFile . YamlFrontMatter ? . Description ) )
136+ return markdownFile . YamlFrontMatter . Description ;
137+
138+ // Fallback to generating description from content
139+ try
140+ {
141+ var document = await markdownFile . MinimalParseAsync ( default ) ;
142+ return descriptionGenerator . GenerateDescription ( document ) ;
143+ }
144+ catch
145+ {
146+ // If parsing fails, return null (no description)
147+ return null ;
148+ }
149+ }
121150}
0 commit comments