UNPKG

apple-hig-mcp

Version:

High-performance MCP server providing instant access to Apple's Human Interface Guidelines via hybrid static/dynamic content delivery

856 lines (809 loc) • 71.2 kB
#!/usr/bin/env node /** * Content Generator * Follows SOLID principles with proper separation of concerns and semantic search capabilities */ import path from 'path'; import { HIGCache } from '../cache.js'; import { CrawleeHIGService } from '../services/crawlee-hig.service.js'; import { HIGContentExtractor } from '../services/hig-content-extractor.service.js'; import { ComprehensiveHIGDiscoveryService } from '../services/comprehensive-hig-discovery.service.js'; // Services (Dependency Injection) import { FileSystemService } from '../services/file-system.service.js'; import { ContentProcessorService } from '../services/content-processor.service.js'; import { ContentQualityValidatorService } from '../services/content-quality-validator.service.js'; import { SearchIndexerService } from '../services/search-indexer.service.js'; import { CrossReferenceGeneratorService } from '../services/cross-reference-generator.service.js'; /** * Main Content Generator Class * Single Responsibility: Orchestrate the content generation process */ export class ContentGenerator { config; fileSystem; contentProcessor; searchIndexer; crossReferenceGenerator; crawleeService; contentExtractor; cache; sections = []; startTime = Date.now(); extractionStats = { totalSections: 0, successfulExtractions: 0, fallbackUsage: 0, averageQuality: 0, averageConfidence: 0, extractionSuccessRate: 0 }; qualityValidator; comprehensiveDiscovery; constructor(config, fileSystem, contentProcessor, searchIndexer, crossReferenceGenerator, crawleeService, contentExtractor, cache) { this.config = config; this.fileSystem = fileSystem; this.contentProcessor = contentProcessor; this.searchIndexer = searchIndexer; this.crossReferenceGenerator = crossReferenceGenerator; this.crawleeService = crawleeService; this.contentExtractor = contentExtractor; this.cache = cache; // Initialize quality validator with enhanced thresholds this.qualityValidator = new ContentQualityValidatorService({ minQualityScore: 0.6, // Higher than default minConfidence: 0.7, // Higher than default minContentLength: 300, // Higher than default maxFallbackRate: 3, // Lower than default (97%+ real content) minStructureScore: 0.5, // Higher than default minAppleTermsScore: 0.2 // Higher than default }); // Initialize comprehensive discovery service this.comprehensiveDiscovery = new ComprehensiveHIGDiscoveryService(this.cache); } /** * Main generation process - orchestrates all steps */ async generate() { console.log('šŸŽ Starting Apple HIG content generation...'); console.log(`šŸ“ Output directory: ${this.config.outputDirectory}`); try { // Step 1: Discovery await this.discoverSections(); // Step 2: Setup await this.createDirectoryStructure(); // Step 3: Content generation await this.generateContent(); // Step 4: Metadata generation await this.generateMetadata(); const duration = Date.now() - this.startTime; console.log(`āœ… Content generation completed in ${duration}ms`); console.log(`šŸ“Š Generated ${this.sections.length} sections`); // Log extraction statistics and SLA compliance this.logExtractionResults(); // Generate and log comprehensive quality report this.logQualityReport(); } catch (error) { console.error('āŒ Content generation failed:', error); throw error; } } /** * Discover all HIG sections from Apple's website using dynamic discovery */ async discoverSections() { // Use fast discovery when semantic search is disabled (offline mode) if (process.env.DISABLE_SEMANTIC_SEARCH === 'true') { console.log('šŸ” Using comprehensive static section discovery (offline mode)...'); this.sections = this.getStaticSections(); } else { console.log('šŸ” Discovering HIG sections dynamically from Apple website...'); try { // Use comprehensive discovery combining known patterns + HTTP discovery this.sections = await this.comprehensiveDiscovery.discoverSections(); if (this.sections.length < 50) { throw new Error(`Insufficient sections discovered: ${this.sections.length}`); } console.log(`āœ… Comprehensive discovery found ${this.sections.length} sections`); } catch (error) { console.warn('āš ļø Comprehensive discovery failed, falling back to static sections:', error); console.log('šŸ” Using static section discovery as fallback...'); this.sections = this.getStaticSections(); } } this.extractionStats.totalSections = this.sections.length; // Log discovery results const platformCounts = this.sections.reduce((acc, section) => { acc[section.platform] = (acc[section.platform] || 0) + 1; return acc; }, {}); console.log(`šŸ“‹ Discovered ${this.sections.length} sections:`); Object.entries(platformCounts).forEach(([platform, count]) => { console.log(` ${platform}: ${count} sections`); }); // Log extraction method distribution console.log('šŸŽÆ Target: High-quality Apple HIG content for AI assistant use'); } /** * Get comprehensive static list of all major HIG sections * This serves as a reliable fallback when dynamic discovery fails * and provides much better coverage than the previous 14 sections */ getStaticSections() { const comprehensiveHIGSections = [ // Universal/Foundations - Core design principles { id: 'accessibility', title: 'Accessibility', platform: 'universal', category: 'foundations', url: 'https://developer.apple.com/design/human-interface-guidelines/accessibility' }, { id: 'inclusion', title: 'Inclusion', platform: 'universal', category: 'foundations', url: 'https://developer.apple.com/design/human-interface-guidelines/inclusion' }, { id: 'privacy', title: 'Privacy', platform: 'universal', category: 'foundations', url: 'https://developer.apple.com/design/human-interface-guidelines/privacy' }, { id: 'branding', title: 'Branding', platform: 'universal', category: 'foundations', url: 'https://developer.apple.com/design/human-interface-guidelines/branding' }, // Layout and Organization { id: 'layout', title: 'Layout', platform: 'universal', category: 'layout', url: 'https://developer.apple.com/design/human-interface-guidelines/layout' }, { id: 'collections', title: 'Collections', platform: 'universal', category: 'layout', url: 'https://developer.apple.com/design/human-interface-guidelines/collections' }, { id: 'lists-and-tables', title: 'Lists and Tables', platform: 'universal', category: 'layout', url: 'https://developer.apple.com/design/human-interface-guidelines/lists-and-tables' }, { id: 'split-views', title: 'Split Views', platform: 'universal', category: 'layout', url: 'https://developer.apple.com/design/human-interface-guidelines/split-views' }, { id: 'scroll-views', title: 'Scroll Views', platform: 'universal', category: 'layout', url: 'https://developer.apple.com/design/human-interface-guidelines/scroll-views' }, // Navigation and Search { id: 'navigation-bars', title: 'Navigation Bars', platform: 'universal', category: 'navigation', url: 'https://developer.apple.com/design/human-interface-guidelines/navigation-bars' }, { id: 'tab-bars', title: 'Tab Bars', platform: 'universal', category: 'navigation', url: 'https://developer.apple.com/design/human-interface-guidelines/tab-bars' }, { id: 'sidebars', title: 'Sidebars', platform: 'universal', category: 'navigation', url: 'https://developer.apple.com/design/human-interface-guidelines/sidebars' }, { id: 'search-fields', title: 'Search Fields', platform: 'universal', category: 'navigation', url: 'https://developer.apple.com/design/human-interface-guidelines/search-fields' }, { id: 'searching', title: 'Searching', platform: 'universal', category: 'navigation', url: 'https://developer.apple.com/design/human-interface-guidelines/searching' }, // Presentation { id: 'alerts', title: 'Alerts', platform: 'universal', category: 'presentation', url: 'https://developer.apple.com/design/human-interface-guidelines/alerts' }, { id: 'action-sheets', title: 'Action Sheets', platform: 'universal', category: 'presentation', url: 'https://developer.apple.com/design/human-interface-guidelines/action-sheets' }, { id: 'sheets', title: 'Sheets', platform: 'universal', category: 'presentation', url: 'https://developer.apple.com/design/human-interface-guidelines/sheets' }, { id: 'popovers', title: 'Popovers', platform: 'universal', category: 'presentation', url: 'https://developer.apple.com/design/human-interface-guidelines/popovers' }, { id: 'modality', title: 'Modality', platform: 'universal', category: 'presentation', url: 'https://developer.apple.com/design/human-interface-guidelines/modality' }, { id: 'notifications', title: 'Notifications', platform: 'universal', category: 'presentation', url: 'https://developer.apple.com/design/human-interface-guidelines/notifications' }, // Selection and Input { id: 'buttons', title: 'Buttons', platform: 'universal', category: 'selection-and-input', url: 'https://developer.apple.com/design/human-interface-guidelines/buttons' }, { id: 'text-fields', title: 'Text Fields', platform: 'universal', category: 'selection-and-input', url: 'https://developer.apple.com/design/human-interface-guidelines/text-fields' }, { id: 'text-views', title: 'Text Views', platform: 'universal', category: 'selection-and-input', url: 'https://developer.apple.com/design/human-interface-guidelines/text-views' }, { id: 'pickers', title: 'Pickers', platform: 'universal', category: 'selection-and-input', url: 'https://developer.apple.com/design/human-interface-guidelines/pickers' }, { id: 'segmented-controls', title: 'Segmented Controls', platform: 'universal', category: 'selection-and-input', url: 'https://developer.apple.com/design/human-interface-guidelines/segmented-controls' }, { id: 'sliders', title: 'Sliders', platform: 'universal', category: 'selection-and-input', url: 'https://developer.apple.com/design/human-interface-guidelines/sliders' }, { id: 'toggles', title: 'Toggles', platform: 'universal', category: 'selection-and-input', url: 'https://developer.apple.com/design/human-interface-guidelines/toggles' }, { id: 'steppers', title: 'Steppers', platform: 'universal', category: 'selection-and-input', url: 'https://developer.apple.com/design/human-interface-guidelines/steppers' }, // Visual Design { id: 'menus', title: 'Menus', platform: 'universal', category: 'visual-design', url: 'https://developer.apple.com/design/human-interface-guidelines/menus' }, { id: 'toolbars', title: 'Toolbars', platform: 'universal', category: 'visual-design', url: 'https://developer.apple.com/design/human-interface-guidelines/toolbars' }, { id: 'context-menus', title: 'Context Menus', platform: 'universal', category: 'visual-design', url: 'https://developer.apple.com/design/human-interface-guidelines/context-menus' }, { id: 'pull-down-buttons', title: 'Pull-down Buttons', platform: 'universal', category: 'visual-design', url: 'https://developer.apple.com/design/human-interface-guidelines/pull-down-buttons' }, { id: 'pop-up-buttons', title: 'Pop-up Buttons', platform: 'universal', category: 'visual-design', url: 'https://developer.apple.com/design/human-interface-guidelines/pop-up-buttons' }, // Icons and Images { id: 'app-icons', title: 'App Icons', platform: 'universal', category: 'icons-and-images', url: 'https://developer.apple.com/design/human-interface-guidelines/app-icons' }, { id: 'sf-symbols', title: 'SF Symbols', platform: 'universal', category: 'icons-and-images', url: 'https://developer.apple.com/design/human-interface-guidelines/sf-symbols' }, { id: 'icons', title: 'Icons', platform: 'universal', category: 'icons-and-images', url: 'https://developer.apple.com/design/human-interface-guidelines/icons' }, { id: 'images', title: 'Images', platform: 'universal', category: 'icons-and-images', url: 'https://developer.apple.com/design/human-interface-guidelines/images' }, // Color and Materials { id: 'color', title: 'Color', platform: 'universal', category: 'color-and-materials', url: 'https://developer.apple.com/design/human-interface-guidelines/color' }, { id: 'materials', title: 'Materials', platform: 'universal', category: 'color-and-materials', url: 'https://developer.apple.com/design/human-interface-guidelines/materials' }, { id: 'dark-mode', title: 'Dark Mode', platform: 'universal', category: 'color-and-materials', url: 'https://developer.apple.com/design/human-interface-guidelines/dark-mode' }, // Typography { id: 'typography', title: 'Typography', platform: 'universal', category: 'typography', url: 'https://developer.apple.com/design/human-interface-guidelines/typography' }, { id: 'writing', title: 'Writing', platform: 'universal', category: 'typography', url: 'https://developer.apple.com/design/human-interface-guidelines/writing' }, // Motion { id: 'motion', title: 'Motion', platform: 'universal', category: 'motion', url: 'https://developer.apple.com/design/human-interface-guidelines/motion' }, // iOS Specific { id: 'designing-for-ios', title: 'Designing for iOS', platform: 'iOS', category: 'foundations', url: 'https://developer.apple.com/design/human-interface-guidelines/designing-for-ios' }, { id: 'home-screen-quick-actions', title: 'Home Screen Quick Actions', platform: 'iOS', category: 'system-capabilities', url: 'https://developer.apple.com/design/human-interface-guidelines/home-screen-quick-actions' }, { id: 'widgets', title: 'Widgets', platform: 'iOS', category: 'system-capabilities', url: 'https://developer.apple.com/design/human-interface-guidelines/widgets' }, { id: 'live-activities', title: 'Live Activities', platform: 'iOS', category: 'system-capabilities', url: 'https://developer.apple.com/design/human-interface-guidelines/live-activities' }, // macOS Specific { id: 'designing-for-macos', title: 'Designing for macOS', platform: 'macOS', category: 'foundations', url: 'https://developer.apple.com/design/human-interface-guidelines/designing-for-macos' }, { id: 'windows', title: 'Windows', platform: 'macOS', category: 'layout', url: 'https://developer.apple.com/design/human-interface-guidelines/windows' }, { id: 'panels', title: 'Panels', platform: 'macOS', category: 'presentation', url: 'https://developer.apple.com/design/human-interface-guidelines/panels' }, { id: 'the-menu-bar', title: 'The Menu Bar', platform: 'macOS', category: 'system-capabilities', url: 'https://developer.apple.com/design/human-interface-guidelines/the-menu-bar' }, // watchOS Specific { id: 'designing-for-watchos', title: 'Designing for watchOS', platform: 'watchOS', category: 'foundations', url: 'https://developer.apple.com/design/human-interface-guidelines/designing-for-watchos' }, { id: 'complications', title: 'Complications', platform: 'watchOS', category: 'system-capabilities', url: 'https://developer.apple.com/design/human-interface-guidelines/complications' }, { id: 'digital-crown', title: 'Digital Crown', platform: 'watchOS', category: 'selection-and-input', url: 'https://developer.apple.com/design/human-interface-guidelines/digital-crown' }, { id: 'always-on', title: 'Always On', platform: 'watchOS', category: 'system-capabilities', url: 'https://developer.apple.com/design/human-interface-guidelines/always-on' }, // tvOS Specific { id: 'designing-for-tvos', title: 'Designing for tvOS', platform: 'tvOS', category: 'foundations', url: 'https://developer.apple.com/design/human-interface-guidelines/designing-for-tvos' }, { id: 'focus-and-selection', title: 'Focus and Selection', platform: 'tvOS', category: 'foundations', url: 'https://developer.apple.com/design/human-interface-guidelines/focus-and-selection' }, { id: 'top-shelf', title: 'Top Shelf', platform: 'tvOS', category: 'system-capabilities', url: 'https://developer.apple.com/design/human-interface-guidelines/top-shelf' }, // visionOS Specific { id: 'designing-for-visionos', title: 'Designing for visionOS', platform: 'visionOS', category: 'foundations', url: 'https://developer.apple.com/design/human-interface-guidelines/designing-for-visionos' }, { id: 'spatial-layout', title: 'Spatial Layout', platform: 'visionOS', category: 'layout', url: 'https://developer.apple.com/design/human-interface-guidelines/spatial-layout' }, { id: 'eyes', title: 'Eyes', platform: 'visionOS', category: 'selection-and-input', url: 'https://developer.apple.com/design/human-interface-guidelines/eyes' }, { id: 'gestures', title: 'Gestures', platform: 'visionOS', category: 'selection-and-input', url: 'https://developer.apple.com/design/human-interface-guidelines/gestures' }, { id: 'immersive-experiences', title: 'Immersive Experiences', platform: 'visionOS', category: 'system-capabilities', url: 'https://developer.apple.com/design/human-interface-guidelines/immersive-experiences' }, // Key Technologies { id: 'apple-pay', title: 'Apple Pay', platform: 'universal', category: 'technologies', url: 'https://developer.apple.com/design/human-interface-guidelines/apple-pay' }, { id: 'sign-in-with-apple', title: 'Sign in with Apple', platform: 'universal', category: 'technologies', url: 'https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple' }, { id: 'siri', title: 'Siri', platform: 'universal', category: 'technologies', url: 'https://developer.apple.com/design/human-interface-guidelines/siri' }, { id: 'carplay', title: 'CarPlay', platform: 'universal', category: 'technologies', url: 'https://developer.apple.com/design/human-interface-guidelines/carplay' }, { id: 'augmented-reality', title: 'Augmented Reality', platform: 'universal', category: 'technologies', url: 'https://developer.apple.com/design/human-interface-guidelines/augmented-reality' } ]; return comprehensiveHIGSections.map(section => ({ ...section, platform: section.platform, category: section.category, content: this.generateBasicContentForSection(section.title, section.platform, section.category), lastUpdated: new Date() })); } /** * Generate basic content for a section (used for static sections) */ generateBasicContentForSection(title, platform, _category) { return `# ${title} This section is part of Apple's Human Interface Guidelines for ${platform}. **Note**: This is a placeholder. For complete and up-to-date information, please visit Apple's official documentation. ## Overview ${title} guidelines provide essential design principles for creating great user experiences on ${platform}. --- **Attribution Notice** This content is sourced from Apple's Human Interface Guidelines. Ā© Apple Inc. All rights reserved. For the most up-to-date and official information, please refer to Apple's official documentation.`; } /** * Get the file path for a topic based on topic-first organization */ getTopicFilePath(section, filename) { // Universal topics go at root level if (section.platform === 'universal' || this.isUniversalTopic(section)) { return path.join(this.config.outputDirectory, filename); } // Platform-specific topics go in platforms/{platform}/ return path.join(this.config.outputDirectory, 'platforms', section.platform.toLowerCase(), filename); } /** * Determine if a topic should be treated as universal */ isUniversalTopic(section) { // Topics that should be universal based on their nature const universalTopics = new Set([ 'accessibility', 'privacy', 'inclusion', 'branding', 'layout', 'spatial-layout', 'typography', 'color', 'icons', 'images', 'motion', 'inputs', 'gestures', 'feedback', 'loading', 'onboarding', 'launching', 'navigation-and-search', 'searching', 'modality', 'alerts', 'action-sheets', 'activity-views', 'sheets', 'popovers', 'buttons', 'menus', 'toolbars', 'tab-bars', 'navigation-bars', 'sliders', 'steppers', 'toggles', 'pickers', 'progress-indicators', 'labels', 'text-fields', 'text-views', 'lists-and-tables', 'collections', 'scroll-views', 'split-views', 'boxes', 'gauges', 'charts', 'rating-indicators', 'materials', 'app-clips', 'app-shortcuts', 'apple-pay', 'carplay', 'healthkit', 'homekit', 'icloud', 'in-app-purchase', 'machine-learning', 'maps', 'nfc', 'siri', 'wallet', 'augmented-reality', 'game-center', 'live-activities', 'live-photos', 'notifications', 'shareplay', 'sign-in-with-apple', 'tap-to-pay-on-iphone', 'widgets' ]); // Extract topic name from section ID or title const topicName = section.id.replace(/-\w+$/, ''); // Remove platform suffix return universalTopics.has(topicName) || section.platform === 'universal'; } /** * Create necessary directory structure (topic-first organization) */ async createDirectoryStructure() { console.log('šŸ“ Creating topic-first directory structure...'); const directories = [ 'platforms/ios', 'platforms/macos', 'platforms/watchos', 'platforms/tvos', 'platforms/visionos', // Note: No platforms/universal - universal topics go at root 'metadata' ]; // Create root output directory await this.fileSystem.mkdir(this.config.outputDirectory, { recursive: true }); // Create platform-specific subdirectories for (const dir of directories) { await this.fileSystem.mkdir(path.join(this.config.outputDirectory, dir), { recursive: true }); } } /** * Generate content for all sections */ async generateContent() { console.log('šŸ“ Generating section content with high-quality processing...'); console.log(' ⚔ Processing Apple HIG content for AI assistant use'); // Process in batches to avoid overwhelming the system const batches = this.chunkArray(this.sections, this.config.batchSize); for (let i = 0; i < batches.length; i++) { const batch = batches[i]; console.log(`šŸ“„ Processing batch ${i + 1}/${batches.length} (${batch.length} sections)`); // Process batch in parallel await Promise.all(batch.map(section => this.processSection(section))); // Rate limiting between batches if (i < batches.length - 1) { await this.delay(this.config.rateLimitDelay); } } } /** * Process a single section with enhanced quality monitoring */ async processSection(section) { try { // Fetch content using Crawlee service const sectionWithContent = await this.crawleeService.fetchSectionContent(section); if (!sectionWithContent.content) { console.warn(`āš ļø No content for section: ${section.title}`); this.extractionStats.fallbackUsage++; return; } // Use our enhanced content processor to process HTML content const processingResult = await this.contentProcessor.processContent(sectionWithContent.content, sectionWithContent.url); // Validate content quality const validationResult = await this.qualityValidator.validateContent(processingResult.cleanedMarkdown, { ...sectionWithContent, quality: processingResult.quality }); // Record extraction for monitoring this.qualityValidator.recordExtraction(sectionWithContent, processingResult.quality); // Update extraction statistics this.updateExtractionStats(processingResult.quality); // Log quality validation results if (!validationResult.isValid && validationResult.issues.length > 0) { console.warn(`āš ļø Quality issues for ${section.title}:`, validationResult.issues); if (validationResult.recommendations.length > 0) { console.log(`šŸ’” Recommendations:`, validationResult.recommendations); } } // Generate final markdown with structured content and enhanced front matter const finalContent = this.generateStructuredMarkdown(sectionWithContent, processingResult); // Write to file using topic-first structure const filename = this.generateFilename(section); const filePath = this.getTopicFilePath(section, filename); await this.fileSystem.writeFile(filePath, finalContent); // Update section with quality info and structured content const enrichedSection = { ...sectionWithContent, quality: processingResult.quality, extractionMethod: processingResult.quality.extractionMethod, structuredContent: processingResult.structuredContent, processingMetrics: processingResult.processingMetrics }; // Add to indices with enhanced keyword extraction const keywords = this.contentProcessor.extractKeywords(processingResult.cleanedMarkdown, sectionWithContent); this.searchIndexer.addSection({ ...enrichedSection, keywords }); this.crossReferenceGenerator.addSection(enrichedSection); // Log quality information with enhanced metrics const qualityEmoji = processingResult.quality.score >= 0.8 ? '🟢' : processingResult.quality.score >= 0.5 ? '🟔' : 'šŸ”“'; const structureEmoji = processingResult.processingMetrics.structureScore >= 0.8 ? 'šŸ—ļø' : 'šŸ“'; // Determine display path for logging const displayPath = this.isUniversalTopic(section) || section.platform === 'universal' ? filename : `${section.platform}/${filename}`; console.log(`${qualityEmoji}${structureEmoji} Generated: ${displayPath} (quality: ${processingResult.quality.score.toFixed(3)}, structure: ${processingResult.processingMetrics.structureScore.toFixed(3)}, method: ${processingResult.quality.extractionMethod})`); } catch (error) { console.error(`āŒ Failed to process section ${section.title}:`, error); this.extractionStats.fallbackUsage++; } } /** * Update extraction statistics */ updateExtractionStats(quality) { this.extractionStats.successfulExtractions++; if (quality.isFallbackContent || quality.extractionMethod === 'fallback') { this.extractionStats.fallbackUsage++; } // Update running averages const currentCount = this.extractionStats.successfulExtractions; this.extractionStats.averageQuality = ((this.extractionStats.averageQuality * (currentCount - 1)) + quality.score) / currentCount; this.extractionStats.averageConfidence = ((this.extractionStats.averageConfidence * (currentCount - 1)) + quality.confidence) / currentCount; // Calculate success rate this.extractionStats.extractionSuccessRate = ((this.extractionStats.successfulExtractions - this.extractionStats.fallbackUsage) / this.extractionStats.successfulExtractions) * 100; } /** * Generate structured markdown with enhanced front matter and organized content */ generateStructuredMarkdown(section, processingResult) { // Enhanced front matter with quality metrics and structured data const frontMatter = [ '---', `title: "${section.title}"`, `platform: ${section.platform}`, `category: ${section.category}`, `url: ${section.url}`, `id: ${section.id}`, `lastUpdated: ${section.lastUpdated?.toISOString() || new Date().toISOString()}`, `extractionMethod: ${processingResult.quality.extractionMethod}`, `qualityScore: ${processingResult.quality.score.toFixed(3)}`, `confidence: ${processingResult.quality.confidence.toFixed(3)}`, `contentLength: ${processingResult.quality.length}`, `structureScore: ${processingResult.processingMetrics.structureScore.toFixed(3)}`, `cleaningScore: ${processingResult.processingMetrics.cleaningScore.toFixed(3)}`, `hasCodeExamples: ${processingResult.quality.codeExamplesCount > 0}`, `hasImages: false`, // We remove images per requirements `keywords: [${this.contentProcessor.extractKeywords(processingResult.cleanedMarkdown, section).slice(0, 10).map((k) => `"${k}"`).join(', ')}]`, '---', '' ].join('\n'); // Structured content organization let structuredContent = ''; // Add overview section if (processingResult.structuredContent.overview && processingResult.structuredContent.overview !== 'No overview available') { structuredContent += `## Overview\n\n${processingResult.structuredContent.overview}\n\n`; } // Add guidelines section if (processingResult.structuredContent.guidelines.length > 0 && processingResult.structuredContent.guidelines[0] !== 'No specific guidelines identified') { structuredContent += `## Guidelines\n\n`; processingResult.structuredContent.guidelines.forEach((guideline) => { structuredContent += `- ${guideline}\n`; }); structuredContent += '\n'; } // Add examples section if (processingResult.structuredContent.examples.length > 0 && processingResult.structuredContent.examples[0] !== 'No examples provided') { structuredContent += `## Examples\n\n`; processingResult.structuredContent.examples.forEach((example) => { structuredContent += `- ${example}\n`; }); structuredContent += '\n'; } // Add specifications section if available if (processingResult.structuredContent.specifications) { structuredContent += `## Specifications\n\n`; const specs = processingResult.structuredContent.specifications; if (specs.dimensions) { structuredContent += `### Dimensions\n`; Object.entries(specs.dimensions).forEach(([key, value]) => { structuredContent += `- **${key}**: ${value}\n`; }); structuredContent += '\n'; } if (specs.spacing) { structuredContent += `### Spacing\n`; Object.entries(specs.spacing).forEach(([key, value]) => { structuredContent += `- **${key}**: ${value}\n`; }); structuredContent += '\n'; } } // Add related concepts section if (processingResult.structuredContent.relatedConcepts.length > 0) { structuredContent += `## Related Concepts\n\n`; structuredContent += processingResult.structuredContent.relatedConcepts.map((concept) => `- ${concept}`).join('\n') + '\n\n'; } // Fallback to cleaned markdown if structured content is minimal let finalContent = structuredContent; if (structuredContent.trim().length < 200) { finalContent = `## Content\n\n${processingResult.cleanedMarkdown}\n\n`; } // Enhanced attribution with quality notice const qualityNotice = processingResult.quality.score >= 0.8 ? 'This content was successfully extracted and structured from Apple\'s official documentation.' : processingResult.quality.score >= 0.5 ? 'This content was extracted with good confidence. Structure and guidelines have been enhanced for better usability.' : 'This content was extracted with moderate confidence. Please verify important details with the official Apple documentation.'; const attribution = [ '---', '', '**Attribution Notice**', '', `This content is sourced from Apple's Human Interface Guidelines: ${section.url}`, '', qualityNotice, '', 'Ā© Apple Inc. All rights reserved. This content is provided for educational and development purposes under fair use. This MCP server is not affiliated with Apple Inc. and does not claim ownership of Apple\'s content.', '', 'For the most up-to-date and official information, please refer to Apple\'s official documentation.', '' ].join('\n'); return frontMatter + finalContent + attribution; } /** * Generate enhanced markdown with quality metadata (legacy method) */ generateEnhancedMarkdown(section, content, processedContent) { // Enhanced front matter with quality metrics const frontMatter = [ '---', `title: "${section.title}"`, `platform: ${section.platform}`, `category: ${section.category}`, `url: ${section.url}`, `id: ${section.id}`, `lastUpdated: ${section.lastUpdated?.toISOString() || new Date().toISOString()}`, `extractionMethod: ${processedContent.quality.extractionMethod}`, `qualityScore: ${processedContent.quality.score.toFixed(3)}`, `confidence: ${processedContent.quality.confidence.toFixed(3)}`, `contentLength: ${processedContent.quality.length}`, `hasCodeExamples: ${processedContent.quality.codeExamplesCount > 0}`, `hasImages: ${processedContent.quality.imageReferencesCount > 0}`, `keywords: [${processedContent.keywords.slice(0, 10).map((k) => `"${k}"`).join(', ')}]`, '---', '' ].join('\n'); // Enhanced table of contents (if available) let tableOfContents = ''; if (processedContent.tableOfContents) { tableOfContents = processedContent.tableOfContents + '\n'; } // Content summary (if available and high quality) let summary = ''; if (processedContent.summary && processedContent.quality.score > 0.5) { summary = `## Summary\n\n${processedContent.summary}\n\n`; } // Enhanced attribution with quality notice const qualityNotice = processedContent.quality.score >= 0.8 ? 'This content was successfully extracted from Apple\'s official documentation.' : processedContent.quality.isFallbackContent ? 'āš ļø This content uses fallback information. For the most accurate and complete information, please visit the official Apple documentation.' : 'This content was extracted with moderate confidence. Please verify important details with the official Apple documentation.'; const attribution = [ '', '---', '', '**Attribution Notice**', '', `This content is sourced from Apple's Human Interface Guidelines: ${section.url}`, '', qualityNotice, '', 'Ā© Apple Inc. All rights reserved. This content is provided for educational and development purposes under fair use. This MCP server is not affiliated with Apple Inc. and does not claim ownership of Apple\'s content.', '', 'For the most up-to-date and official information, please refer to Apple\'s official documentation.', '' ].join('\n'); return frontMatter + tableOfContents + summary + content + attribution; } /** * Generate final markdown with front matter and attribution (legacy method) */ generateFinalMarkdown(section, content) { // Front matter const frontMatter = [ '---', `title: "${section.title}"`, `platform: ${section.platform}`, `category: ${section.category}`, `url: ${section.url}`, `id: ${section.id}`, `lastUpdated: ${section.lastUpdated?.toISOString() || new Date().toISOString()}`, '---', '' ].join('\n'); // Table of contents const toc = this.generateTableOfContents(content); // Attribution const attribution = [ '', '---', '', '**Attribution Notice**', '', `This content is sourced from Apple's Human Interface Guidelines: ${section.url}`, '', 'Ā© Apple Inc. All rights reserved. This content is provided for educational and development purposes under fair use. This MCP server is not affiliated with Apple Inc. and does not claim ownership of Apple\'s content.', '', 'For the most up-to-date and official information, please refer to Apple\'s official documentation.', '' ].join('\n'); return frontMatter + toc + content + attribution; } /** * Generate table of contents for content */ generateTableOfContents(content) { const headings = content.match(/^#+\s+.+$/gm); if (!headings || headings.length < 3) { return ''; // Don't add TOC for short content } const toc = ['## Table of Contents', '']; headings.forEach(heading => { const level = heading.match(/^#+/)?.[0].length || 1; const text = heading.replace(/^#+\s+/, ''); const anchor = text.toLowerCase().replace(/[^a-z0-9\s]/g, '').replace(/\s+/g, '-'); const indent = ' '.repeat(Math.max(0, level - 2)); toc.push(`${indent}- [${text}](#${anchor})`); }); toc.push('', ''); return toc.join('\n'); } /** * Generate metadata files with extraction statistics */ async generateMetadata() { console.log('šŸ“Š Generating metadata...'); // Generate search index const searchIndex = this.searchIndexer.generateIndex(); await this.fileSystem.writeFile(path.join(this.config.outputDirectory, 'metadata', 'search-index.json'), JSON.stringify(searchIndex, null, 2)); console.log(`āœ“ Search index generated with ${Object.keys(searchIndex).length} entries`); // Generate cross-references const crossReferences = this.crossReferenceGenerator.generateReferences(); await this.fileSystem.writeFile(path.join(this.config.outputDirectory, 'metadata', 'cross-references.json'), JSON.stringify(crossReferences, null, 2)); console.log('āœ“ Cross-references generated'); // Generate enhanced generation info with extraction statistics const totalSize = await this.fileSystem.calculateDirectorySize(this.config.outputDirectory); const enhancedMetadata = { lastUpdated: new Date().toISOString(), totalSections: this.sections.length, sectionsByPlatform: this.groupByPlatform(), sectionsByCategory: this.groupByCategory(), totalSize, generationDuration: Date.now() - this.startTime, extractionStatistics: this.extractionStats, qualityMetrics: { fallbackUsageRate: (this.extractionStats.fallbackUsage / this.extractionStats.totalSections) * 100, slaCompliance: this.extractionStats.extractionSuccessRate >= 95, averageQuality: this.extractionStats.averageQuality, averageConfidence: this.extractionStats.averageConfidence }, generatedWith: 'Content-Generator-v2.0' }; await this.fileSystem.writeFile(path.join(this.config.outputDirectory, 'metadata', 'generation-info.json'), JSON.stringify(enhancedMetadata, null, 2)); // Generate extraction statistics file await this.fileSystem.writeFile(path.join(this.config.outputDirectory, 'metadata', 'extraction-statistics.json'), JSON.stringify(this.extractionStats, null, 2)); console.log('āœ“ Enhanced metadata generated'); console.log(` Total size: ${(totalSize / 1024 / 1024).toFixed(2)} MB`); console.log(` Extraction success rate: ${this.extractionStats.extractionSuccessRate.toFixed(1)}%`); } /** * Log extraction results and SLA compliance */ logExtractionResults() { const fallbackRate = (this.extractionStats.fallbackUsage / this.extractionStats.totalSections) * 100; const slaCompliant = this.extractionStats.extractionSuccessRate >= 95; console.log('\nšŸŽÆ Extraction Results Summary:'); console.log('================================'); console.log(`šŸ“Š Total sections: ${this.extractionStats.totalSections}`); console.log(`āœ… Successful extractions: ${this.extractionStats.successfulExtractions}`); console.log(`šŸ”„ Fallback content used: ${this.extractionStats.fallbackUsage} (${fallbackRate.toFixed(1)}%)`); console.log(`šŸŽØ Real content rate: ${this.extractionStats.extractionSuccessRate.toFixed(1)}%`); console.log(`⭐ Average quality score: ${this.extractionStats.averageQuality.toFixed(3)}`); console.log(`šŸ”’ Average confidence: ${this.extractionStats.averageConfidence.toFixed(3)}`); console.log(`\nšŸŽÆ SLA Target: ≄95% real content extraction`); if (slaCompliant) { console.log('āœ… SLA ACHIEVED! Extraction rate exceeds 95% target.'); } else { console.log(`āŒ SLA NOT MET. Current rate: ${this.extractionStats.extractionSuccessRate.toFixed(1)}% (target: ≄95%)`); console.log('šŸ”§ Consider reviewing extraction patterns or Apple website changes.'); } console.log('\nšŸ“ˆ Content Generation Summary:'); console.log(` • ${this.extractionStats.totalSections} sections processed successfully`); console.log(` • ${((this.extractionStats.successfulExtractions - this.extractionStats.fallbackUsage) / this.extractionStats.totalSections * 100).toFixed(1)}% high-quality Apple content extracted`); console.log(` • Quality monitoring and validation enabled`); console.log(' • Content optimized for MCP and AI assistant use'); } /** * Log clean quality validation summary */ logQualityReport() { const report = this.qualityValidator.generateReport(); console.log(report); } // Utility methods generateFilename(section) { const cleanTitle = section.title .replace(/^(iOS|macOS|watchOS|tvOS|visionOS)\s+/i, '') .toLowerCase() .replace(/[^a-z0-9\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-') .replace(/^-|-$/g, ''); return `${cleanTitle}.md`; } chunkArray(array, chunkSize) { const chunks = []; for (let i = 0; i < array.length; i += chunkSize) { chunks.push(array.slice(i, i + chunkSize)); } return chunks; } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } groupByPlatform() { return this.sections.reduce((acc, section) => { acc[section.platform] = (acc[section.platform] || 0) + 1; return acc; }, {}); } groupByCategory() { return this.sections.reduce((acc, section) => { acc[section.category] = (acc[section.category] || 0) + 1; return acc; }, {}); } // High-quality fallback content methods getHighQualityButtonContent(platform = 'iOS') { return `# Buttons Buttons initiate app-specific actions, have customizable backgrounds, and can include a title or an icon. The system provides several button styles, each with specific best practices. ## Overview Buttons are interactive elements that trigger actions when tapped. In ${platform}, buttons should be immediately recognizable as interactive elements and provide clear feedback when pressed. ## Guidelines - Use clear, concise button text that describes the action - Make buttons large enough for easy tapping (minimum 44pt touch target) - Choose appropriate button styles for the context - Provide visual feedback when buttons are pressed - Consider button placement and grouping for optimal user flow ## Button Types ### Primary Buttons Use for the most important action in a view. Limit to one primary button per screen. ### Secondary Buttons Use for secondary actions. These can appear alongside primary buttons. ### Destructive Buttons Use for actions that delete or remove content. Use red styling to indicate destructive nature. ## Specifications - Minimum touch target: 44pt Ɨ 44pt - Recommended padding: 8pt-16pt horizontal, 6pt-12pt vertical - Corner radius: Varies by platform design language - Text should be legible with sufficient contrast ## Best Practices - Test buttons with various text lengths and localizations - Ensure buttons work well in both light and dark modes - Consider accessibility requirements for color contrast and VoiceOver support - Group related buttons logically - Use consistent button styling throughout your app`; } getHighQualityNavigationContent() { return `# Navigation Bars A navigation bar appears at the top of an app screen, enabling navigation through a hierarchy of content. Navigation bars are translucent by default and can be configured to hide or show when people scroll. ## Overview Navigation bars provide hierarchical navigation and display the current location within your app's information architecture. They help users understand where they are and how to get back to previous screens. ## Guidelines - Show the current location clearly with a descriptive title - Provide clear navigation paths with back buttons - Use navigation bars consistently throughout your app - Consider the content behind translucent navigation bars - Keep navigation bar content relevant to the current screen ## Elements ### Title Display a descriptive title that represents the current screen or section. ### Back Button Automatically provided by the system, shows the title of the previous screen. ### Navigation Controls Additional buttons for actions relevant to the current screen. ## Specifications - Standard height: 44pt (compact) or 96pt (large title) - Title should be centered and legible - Back button appears on the leading edge - Additional controls on the trailing edge ## Best Practices - Use large titles when appropriate to create visual hierarchy - Ensure navigation bar content is accessible - Test with different device orientations - Consider how the navigation bar behaves during scrolling - Maintain consistency with platform conventions`; } getHighQualityTabBarContent() { return `# Tab Bars A tab bar appears at the bottom of an app screen and lets people quickly switch among different sections of an app. Tab bars are translucent by default. ## Overview Tab bars provide persistent access to top-level navigation in your app. They help users understand your app's structure and make it easy to move between primary content areas. ## Guidelines - Use tab bars for top-level navigation only - Limit to 5 tabs maximum for optimal usability - Use clear, recognizable icons and labels - Maintain tab order consistency - Ensure the selected tab is clearly indicated ## Tab Configuration ### Icon Requirements - Use simple, recognizable symbols - Ensure icons work at small sizes - Provide both selected and unselected states - Consider using SF Symbols for consistency ### Labels - Keep labels short and descriptive - Use title case for tab labels - Ensure labels are localized appropriately ## Specifications - Standard height: 49pt (83pt with safe area) - Maximum 5 tabs (more items move to "More" tab) - Icons should be approximately 30Ɨ30pt - Text should be legible and accessible ## Best Practices - Start with the most important tab selected - Use badges sparingly for notifications - Ensure tab content loads quickly - Test with longer localized text - Consider accessibility for users with visual impairments`; } getHighQualityAlertContent() { return `# Alerts An al