@@ -133,6 +133,11 @@ function getGeneratedIndexSlug(item: SidebarItem): string | undefined {
133133 return slug . startsWith ( '/' ) ? slug : `/${ slug } ` ;
134134}
135135
136+ /** Check if a doc is visible (not unlisted and not a draft) */
137+ function isVisibleDoc ( doc ?: DocInfo ) : doc is DocInfo {
138+ return Boolean ( doc && ! doc . unlisted && ! doc . draft ) ;
139+ }
140+
136141/** Remove trailing colon from descriptions */
137142function sanitizeDescription ( desc : string ) : string {
138143 return desc . replace ( / : \s * $ / , '' ) . trim ( ) ;
@@ -278,7 +283,7 @@ function buildCategoryEntry(
278283 lines . push ( '' , description ) ;
279284 }
280285
281- if ( linkedDoc && ! linkedDoc . unlisted && ! linkedDoc . draft && ctx . siteDir ) {
286+ if ( isVisibleDoc ( linkedDoc ) && ctx . siteDir ) {
282287 const tree = getCachedTree ( linkedDoc , ctx . siteDir ) ;
283288 if ( tree ) {
284289 const stripTitle =
@@ -317,25 +322,23 @@ function buildCategoryEntry(
317322 ensureBlankLine ( lines ) ;
318323
319324 // Add overview link if available
320- const isVisible = linkedDoc && ! linkedDoc . unlisted && ! linkedDoc . draft ;
321- let overviewUrl = '' ;
322- if ( isVisible ) {
323- overviewUrl = urlForPermalink ( linkedDoc . permalink , ctx . siteBase ) ;
324- } else if ( generatedSlug ) {
325- overviewUrl = urlForPermalink ( generatedSlug , ctx . siteBase ) ;
326- }
325+ const hasVisibleLinkedDoc = isVisibleDoc ( linkedDoc ) ;
326+ const overviewUrl = hasVisibleLinkedDoc
327+ ? urlForPermalink ( linkedDoc . permalink , ctx . siteBase )
328+ : generatedSlug
329+ ? urlForPermalink ( generatedSlug , ctx . siteBase )
330+ : '' ;
327331
328332 if ( overviewUrl ) {
329- let overviewDesc = '' ;
330- if (
331- isVisible &&
332- linkedDoc . description &&
333- normalizeTitle ( linkedDoc . description ) !== normalizeTitle ( description )
334- ) {
335- overviewDesc = `: ${ sanitizeDescription (
336- truncate ( linkedDoc . description , ctx . maxDescriptionLength ) ,
337- ) } `;
338- }
333+ const linkedDocDesc = hasVisibleLinkedDoc ? linkedDoc . description : '' ;
334+ const showDesc =
335+ linkedDocDesc &&
336+ normalizeTitle ( linkedDocDesc ) !== normalizeTitle ( description ) ;
337+ const overviewDesc = showDesc
338+ ? `: ${ sanitizeDescription (
339+ truncate ( linkedDocDesc , ctx . maxDescriptionLength ) ,
340+ ) } `
341+ : '' ;
339342 lines . push (
340343 `- [Overview](${ toMarkdownUrl (
341344 overviewUrl ,
@@ -390,7 +393,7 @@ function buildSidebarItems(
390393 switch ( normalized . type ) {
391394 case 'doc' : {
392395 const doc = ctx . docsById . get ( normalized . id ?? normalized . docId ?? '' ) ;
393- if ( ! doc || doc . unlisted || doc . draft ) continue ;
396+ if ( ! isVisibleDoc ( doc ) ) continue ;
394397 lines . push (
395398 ...buildDocEntry ( doc , ctx , normalized . label , normalized . href ) ,
396399 ) ;
@@ -430,7 +433,7 @@ function collectFlatDocEntries(
430433
431434 if ( normalized . type === 'doc' ) {
432435 const doc = docsById . get ( normalized . id ?? normalized . docId ?? '' ) ;
433- if ( doc && ! doc . unlisted && ! doc . draft ) {
436+ if ( isVisibleDoc ( doc ) ) {
434437 entries . push ( {
435438 breadcrumb,
436439 doc,
@@ -442,7 +445,7 @@ function collectFlatDocEntries(
442445 const linkedDoc = getLinkedDoc ( item , docsById ) ;
443446
444447 // Add linked doc for category (if exists)
445- if ( linkedDoc && ! linkedDoc . unlisted && ! linkedDoc . draft ) {
448+ if ( isVisibleDoc ( linkedDoc ) ) {
446449 entries . push ( {
447450 breadcrumb,
448451 doc : linkedDoc ,
@@ -456,9 +459,8 @@ function collectFlatDocEntries(
456459 entries . push (
457460 ...collectFlatDocEntries ( childItems , docsById , childBreadcrumb ) ,
458461 ) ;
459- } else if ( normalized . type === 'link' ) {
460- // External links are skipped in flat mode (no doc to reference)
461462 }
463+ // Note: 'link' type items are skipped in flat mode (external links have no doc)
462464 }
463465
464466 return entries ;
@@ -523,7 +525,7 @@ async function writeLlmsTxt(params: GeneratorParams): Promise<void> {
523525 }
524526
525527 const otherDocs = Array . from ( params . docsById . values ( ) )
526- . filter ( ( d ) => ! d . unlisted && ! d . draft && ! sidebarDocIds . has ( d . id ) )
528+ . filter ( ( d ) => isVisibleDoc ( d ) && ! sidebarDocIds . has ( d . id ) )
527529 . sort ( ( a , b ) => a . permalink . localeCompare ( b . permalink ) ) ;
528530
529531 if ( otherDocs . length ) {
@@ -597,9 +599,7 @@ async function writeLlmsFullTxt(params: GeneratorParams): Promise<void> {
597599 firstItem . id ?? firstItem . docId ?? '' ,
598600 ) ;
599601 if (
600- firstDoc &&
601- ! firstDoc . unlisted &&
602- ! firstDoc . draft &&
602+ isVisibleDoc ( firstDoc ) &&
603603 normalizeTitle ( firstDoc . title ) === normalizeTitle ( sectionTitle )
604604 ) {
605605 lines . push ( ...buildDocEntry ( firstDoc , { ...ctx , headingLevel : 1 } ) ) ;
@@ -611,7 +611,7 @@ async function writeLlmsFullTxt(params: GeneratorParams): Promise<void> {
611611 const linkedDoc = getLinkedDoc ( items [ 0 ] , params . docsById ) ;
612612 const desc = getCategoryDescription ( items [ 0 ] , linkedDoc ) . trim ( ) ;
613613 if ( ! linkedDoc && desc ) lines . push ( '' , desc ) ;
614- if ( linkedDoc && ! linkedDoc . unlisted && ! linkedDoc . draft ) {
614+ if ( isVisibleDoc ( linkedDoc ) ) {
615615 lines . push ( ...buildDocEntry ( linkedDoc , { ...ctx , headingLevel : 1 } ) ) ;
616616 }
617617 remainingItems = filterOutLinkedDoc ( firstItem . items ?? [ ] , linkedDoc ) ;
@@ -632,7 +632,7 @@ async function writeLlmsFullTxt(params: GeneratorParams): Promise<void> {
632632 }
633633
634634 const otherDocs = Array . from ( params . docsById . values ( ) )
635- . filter ( ( d ) => ! d . unlisted && ! d . draft && ! sidebarDocIds . has ( d . id ) )
635+ . filter ( ( d ) => isVisibleDoc ( d ) && ! sidebarDocIds . has ( d . id ) )
636636 . sort ( ( a , b ) => a . permalink . localeCompare ( b . permalink ) ) ;
637637
638638 if ( otherDocs . length ) {
@@ -670,7 +670,7 @@ async function writeLlmsFullTxt(params: GeneratorParams): Promise<void> {
670670
671671async function writePerPageMarkdown ( params : GeneratorParams ) : Promise < number > {
672672 const docs = Array . from ( params . docsById . values ( ) )
673- . filter ( ( d ) => ! d . unlisted && ! d . draft )
673+ . filter ( isVisibleDoc )
674674 . sort ( ( a , b ) => a . permalink . localeCompare ( b . permalink ) ) ;
675675
676676 let count = 0 ;
@@ -696,12 +696,20 @@ async function writePerPageMarkdown(params: GeneratorParams): Promise<number> {
696696 const source = params . siteBase
697697 ? `Source: ${ joinUrl ( params . siteBase , doc . permalink ) } `
698698 : '' ;
699+ const llmsTxtUrl = params . siteBase
700+ ? joinUrl ( params . siteBase , params . llmsTxtFilename )
701+ : '' ;
702+ const footer = llmsTxtUrl
703+ ? `---\n\n*To find navigation and other pages in this documentation, fetch the llms.txt file at: ${ llmsTxtUrl } *`
704+ : '' ;
699705 const content = joinLines ( [
700706 `# ${ doc . title } ` ,
701707 '' ,
702708 source ,
703709 source ? '' : '' ,
704710 body . trim ( ) ,
711+ '' ,
712+ footer ,
705713 ] ) ;
706714
707715 await fsp . writeFile ( filePath , content , 'utf8' ) ;
@@ -721,9 +729,7 @@ interface GeneratedIndexPage {
721729async function writeGeneratedIndexPages (
722730 params : GeneratorParams ,
723731) : Promise < number > {
724- const docs = Array . from ( params . docsById . values ( ) ) . filter (
725- ( d ) => ! d . unlisted && ! d . draft ,
726- ) ;
732+ const docs = Array . from ( params . docsById . values ( ) ) . filter ( isVisibleDoc ) ;
727733 const existingPermalinks = new Set (
728734 docs . map ( ( d ) => d . permalink . replace ( / \/ + $ / , '' ) || '/' ) ,
729735 ) ;
@@ -742,20 +748,15 @@ async function writeGeneratedIndexPages(
742748 const doc = params . docsById . get (
743749 normalized . id ?? normalized . docId ?? '' ,
744750 ) ;
745- if ( doc && ! doc . unlisted && ! doc . draft && activePage ) {
751+ if ( isVisibleDoc ( doc ) && activePage ) {
746752 const permalink = doc . permalink . replace ( / \/ + $ / , '' ) || '/' ;
747753 if ( ! activePage . childPermalinks . includes ( permalink ) ) {
748754 activePage . childPermalinks . push ( permalink ) ;
749755 }
750756 }
751757 } else if ( normalized . type === 'category' ) {
752758 const linkedDoc = getLinkedDoc ( item , params . docsById ) ;
753- if (
754- linkedDoc &&
755- ! linkedDoc . unlisted &&
756- ! linkedDoc . draft &&
757- activePage
758- ) {
759+ if ( isVisibleDoc ( linkedDoc ) && activePage ) {
759760 const permalink = linkedDoc . permalink . replace ( / \/ + $ / , '' ) || '/' ;
760761 if ( ! activePage . childPermalinks . includes ( permalink ) ) {
761762 activePage . childPermalinks . push ( permalink ) ;
@@ -838,7 +839,7 @@ export async function generateLlmsExports(
838839) : Promise < void > {
839840 // Get all visible docs
840841 const allDocs = Array . from ( params . docsById . values ( ) )
841- . filter ( ( d ) => ! d . unlisted && ! d . draft )
842+ . filter ( isVisibleDoc )
842843 . sort ( ( a , b ) => a . permalink . localeCompare ( b . permalink ) ) ;
843844
844845 // Check for missing trees and log warnings (graceful handling)
0 commit comments