@redpanda-data/docs-extensions-and-macros
Version:
Antora extensions and macros developed for Redpanda documentation.
96 lines (72 loc) • 3.82 kB
JavaScript
/**
* Adds llms.txt directive to HTML pages for agent-friendly documentation.
*
* This extension injects a blockquote directive pointing to /llms.txt immediately
* after the <body> tag of each documentation page. This helps AI agents discover
* the documentation index according to the Agent-Friendly Docs spec.
*
* The directive is styled to be visually hidden but remains in the HTML for agents
* to discover when they fetch pages.
*
* @see https://agentdocsspec.com/spec/#llms-txt-directive
*/
const { formatLlmsDirective } = require('../extension-utils/llms-utils')
module.exports.register = function () {
const logger = this.getLogger('add-llms-directive-extension')
this.on('pagesComposed', ({ contentCatalog }) => {
const pages = contentCatalog.getPages()
let processedCount = 0
pages.forEach(page => {
if (!page.contents) return
// Skip field-only pages (marked by generate-fields-only-pages extension)
if (page.isFieldOnlyPage === true) return
try {
const html = page.contents.toString('utf8')
// Find the <body> tag and inject directive immediately after it
// This ensures the directive appears early in the HTML for better agent discovery
const bodyMatch = html.match(/(<body[^>]*>)/i)
if (!bodyMatch) {
logger.debug(`No <body> tag found in ${page.src?.path}`)
return
}
const bodyTag = bodyMatch[1]
// Get component name for component-specific link
const componentName = page.src?.component || ''
// Generate the directive in markdown blockquote format
const directiveMarkdown = formatLlmsDirective(componentName)
// Convert markdown blockquote to HTML blockquote
// Remove leading '> ' from each line and convert markdown links to HTML
let directiveText = directiveMarkdown
.split('\n')
.map(line => line.replace(/^>\s*/, ''))
.join('\n')
// Convert markdown links [text](url) to HTML <a> tags
// Add space after <a to match afdocs test pattern expectations
// Add tabindex="-1" and aria-hidden="true" to remove from tab order and hide from assistive tech
directiveText = directiveText.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" tabindex="-1" aria-hidden="true">$1</a>')
// Convert markdown bold **text** to HTML <strong>
directiveText = directiveText.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
// Convert markdown code `text` to HTML <code>
directiveText = directiveText.replace(/`([^`]+)`/g, '<code>$1</code>')
// Add tabindex="-1" and aria-hidden="true" to blockquote to fully hide from assistive tech
const directiveHtml = `\n<blockquote class="llms-directive" tabindex="-1" aria-hidden="true">\n<p>${directiveText}</p>\n</blockquote>\n`
// Inject the directive immediately after the <body> tag
let newHtml = html.replace(bodyTag, bodyTag + directiveHtml)
// Add CSS to hide the directive visually (screen-reader-only pattern)
// This keeps it in HTML for agents but hidden from visual users
const cssTag = `<style>.llms-directive{position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden}</style>`
// Inject CSS before </head> if not already present
if (!html.includes('.llms-directive{')) {
newHtml = newHtml.replace('</head>', `${cssTag}\n</head>`)
}
// Update page contents
page.contents = Buffer.from(newHtml, 'utf8')
processedCount++
} catch (err) {
logger.error(`Error adding llms directive to ${page.src?.path}: ${err.message}`)
}
})
logger.info(`Added llms.txt directive to ${processedCount} HTML pages`)
})
}