/**
* Get Component Accessibility Tool
*
* This tool extracts accessibility sections from HeroUI component documentation,
* including ARIA attributes, keyboard navigation, screen reader support, and
* other accessibility-related information.
*/
import fs from "node:fs/promises";
import path from "node:path";
import { defaultCacheConfig, getHeroUiDocsPath } from "@config/cache.config.js";
import { BaseTool } from "@tools/base-tool.js";
import { replaceCodeDemoWithCode } from "@utils/codedemo-processor.js";
import { logger } from "@utils/logger.js";
import { filterMdxContent } from "@utils/mdx-processor.js";
import {
extractSectionWithSubsections,
getCommonSectionVariations,
type SectionMatchOptions,
} from "@utils/mdx-section-parser.js";
import { z } from "zod";
export class GetComponentAccessibilityTool extends BaseTool {
readonly name = "get_component_accessibility";
readonly title = "Get Component Accessibility Tool";
readonly description =
"Extract accessibility sections from HeroUI component documentation showing ARIA support and accessibility features";
readonly inputSchema = {
componentName: z
.string()
.min(1, "Component name is required")
.describe("Name of the HeroUI component (e.g., 'button', 'input')"),
};
async execute(
params: z.infer<z.ZodObject<typeof this.inputSchema>>,
): Promise<{
content: Array<{ type: "text"; text: string }>;
}> {
this.validateParams(params);
const { componentName } = params;
// Define the accessibility sections to extract
const sectionsToExtract = ["Accessibility"];
const includeSubsections = true;
const exactMatch = false;
try {
// Get the path to the cached docs/components directory
const docsPath = getHeroUiDocsPath(defaultCacheConfig);
const componentsPath = path.join(docsPath, "components");
const componentFilePath = path.join(
componentsPath,
`${componentName}.mdx`,
);
logger.debug("Looking for component accessibility documentation:", {
componentFilePath,
sectionsToExtract,
});
// Check if the component file exists
try {
await fs.access(componentFilePath);
} catch (error) {
logger.warn("Component documentation not found", {
componentName,
componentFilePath,
error: error instanceof Error ? error.message : String(error),
});
return {
content: [
{
type: "text",
text: `Component documentation for '${componentName}' not found in cache. Please ensure the component name is correct and the HeroUI repository has been cloned successfully.`,
},
],
};
}
// Read the MDX file content
let mdxContent = await fs.readFile(componentFilePath, "utf-8");
// Filter out unwanted MDX elements
mdxContent = filterMdxContent(mdxContent);
// Replace CodeDemo components with actual code
mdxContent = await replaceCodeDemoWithCode(mdxContent);
// Extract the accessibility sections
const extractedSections: string[] = [];
const matchOptions: SectionMatchOptions = {
caseInsensitive: true,
partialMatch: !exactMatch,
exact: exactMatch,
};
for (const sectionName of sectionsToExtract) {
// Try direct extraction first
let sectionContent = extractSectionWithSubsections(
mdxContent,
sectionName,
includeSubsections,
matchOptions,
);
// If not found, try common variations
if (!sectionContent) {
const variations = getCommonSectionVariations(sectionName);
for (const variation of variations) {
sectionContent = extractSectionWithSubsections(
mdxContent,
variation,
includeSubsections,
matchOptions,
);
if (sectionContent) {
logger.debug(`Found section using variation: ${variation}`, {
originalName: sectionName,
foundVariation: variation,
});
break;
}
}
}
// Also try some specific accessibility variations
if (!sectionContent) {
const accessibilityVariations = [
"Accessibility",
"accessibility",
"A11y",
"a11y",
"Accessibility Features",
"accessibility features",
"ARIA",
"aria",
"ARIA Support",
"aria support",
"Screen Reader",
"screen reader",
"Keyboard Navigation",
"keyboard navigation",
];
for (const variation of accessibilityVariations) {
sectionContent = extractSectionWithSubsections(
mdxContent,
variation,
includeSubsections,
matchOptions,
);
if (sectionContent) {
logger.debug(
`Found section using accessibility variation: ${variation}`,
{
originalName: sectionName,
foundVariation: variation,
},
);
break;
}
}
}
if (sectionContent) {
extractedSections.push(sectionContent);
logger.debug(`Successfully extracted section: ${sectionName}`, {
componentName,
sectionLength: sectionContent.length,
});
} else {
logger.debug(`Section not found: ${sectionName}`, {
componentName,
availableSections: this.getAvailableSections(mdxContent),
});
}
}
// If no accessibility sections found, return helpful message
if (extractedSections.length === 0) {
return {
content: [
{
type: "text",
text: `No accessibility section found for component '${componentName}'. Available sections: ${this.getAvailableSections(mdxContent).join(", ")}`,
},
],
};
}
// Combine all extracted sections
const combinedContent = extractedSections.join("\n\n---\n\n");
logger.debug("Successfully extracted component accessibility sections", {
componentName,
sectionsFound: extractedSections.length,
totalLength: combinedContent.length,
});
return {
content: [
{
type: "text",
text: combinedContent,
},
],
};
} catch (error) {
logger.error("Error extracting component accessibility:", error);
return {
content: [
{
type: "text",
text: `Error extracting accessibility from component documentation for '${componentName}': ${error instanceof Error ? error.message : "Unknown error"}`,
},
],
};
}
}
/**
* Get list of available sections in the MDX content for error messages
*/
private getAvailableSections(content: string): string[] {
const lines = content.split("\n");
const sections: string[] = [];
for (const line of lines) {
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
if (headerMatch) {
const level = headerMatch[1].length;
const title = headerMatch[2].trim();
// Only show level 1 and 2 headers as main sections
if (level <= 2) {
sections.push(title);
}
}
}
return sections;
}
}