/** * 🤖 AGENTE DE QUALIDADE DE FONTES * * Implementação programática do agente de validação de tipografia */ export class FontQualityAgent { constructor() { this.name = 'FontQualityAgent'; this.description = 'Valida qualidade e consistência de tipografia'; // Personalidade e Background this.personality = { name: 'Lucas "The Typographer"', traits: [ 'Apaixonado por tipografia', 'Detalhista com hierarquia visual', 'Artístico e criativo', 'Valoriza legibilidade acima de tudo', 'Conhece todas as fontes do projeto de cor' ], quirks: [ 'Consegue identificar fontes só de olhar', 'Fica incomodado quando vê texto sem hierarquia clara', 'Tem uma paleta mental de tamanhos de fonte ideais', 'Prefere tipografia consistente a designs "criativos"' ], catchphrases: [ 'Essa hierarquia está clara?', 'O contraste está adequado?', 'Esse texto é legível?', 'Precisamos de mais peso aqui' ] }; this.background = { origin: 'Ex-designer gráfico especializado em tipografia que descobriu o mundo web', motivation: 'Garantir que todo texto seja legível, acessível e visualmente agradável', experience: '10 anos trabalhando com tipografia em design gráfico e web', turningPoint: 'Viu um projeto ser rejeitado por um cliente porque o texto era ilegível', philosophy: 'Tipografia é a voz visual do conteúdo - deve ser clara e respeitosa', relationships: { withUIAdaptation: 'Trabalham juntos - ela cuida do layout, eu cuido da tipografia', withPerformance: 'Respeita, mas às vezes prioriza qualidade visual sobre performance', withDocumentation: 'Aprecia quando a documentação inclui guias de tipografia' } }; } /** * Valida um componente * @param {string|Object} component - Componente ou caminho * @param {Object} context - Contexto * @returns {Promise} */ async validate(component, context = {}) { const errors = []; const warnings = []; const metadata = {}; const componentPath = typeof component === 'string' ? component : component.path; const componentCode = context.code || await this.readComponent(componentPath); if (!componentCode) { return { passed: false, errors: ['Não foi possível ler o componente'], warnings: [], metadata: {} }; } // 1. Verificar uso de classes Tailwind de texto const hasTextClasses = this.hasTextClasses(componentCode); if (!hasTextClasses && this.hasTextContent(componentCode)) { warnings.push('Texto pode não estar usando classes Tailwind consistentes'); } // 2. Verificar hierarquia visual (h1, h2, h3) const hasHierarchy = this.hasTextHierarchy(componentCode); if (!hasHierarchy && this.hasMultipleTextElements(componentCode)) { warnings.push('Pode não haver hierarquia visual clara entre títulos e corpo de texto'); } // 3. Verificar tamanhos de fonte const fontSizes = this.extractFontSizes(componentCode); const hasSmallFonts = fontSizes.some(size => this.isTooSmall(size)); if (hasSmallFonts) { warnings.push('Alguns textos podem estar muito pequenos para leitura confortável (mínimo recomendado: 12-14px)'); } // 4. Verificar contraste (classes de cor) const hasColorClasses = this.hasColorClasses(componentCode); if (!hasColorClasses && this.hasTextContent(componentCode)) { warnings.push('Textos podem não ter contraste adequado definido'); } // 5. Verificar font-weight const hasFontWeights = this.hasFontWeights(componentCode); if (!hasFontWeights && this.hasMultipleTextElements(componentCode)) { warnings.push('Pode não haver distinção de peso de fonte para destacar informações importantes'); } metadata.fontSizes = fontSizes; metadata.hasHierarchy = hasHierarchy; const passed = errors.length === 0; return { passed, errors, warnings, metadata }; } /** * Verifica se tem classes de texto Tailwind * @param {string} code - Código * @returns {boolean} */ hasTextClasses(code) { const textClasses = [ 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl', 'text-xs', 'text-3xl', 'text-4xl', 'text-5xl', 'text-6xl' ]; return textClasses.some(className => code.includes(className)); } /** * Verifica se tem conteúdo de texto * @param {string} code - Código * @returns {boolean} */ hasTextContent(code) { return code.includes('text') || code.includes('p>') || code.includes('span') || code.includes('h1') || code.includes('h2') || code.includes('h3'); } /** * Verifica se tem hierarquia de texto * @param {string} code - Código * @returns {boolean} */ hasTextHierarchy(code) { return code.includes('h1') || code.includes('h2') || code.includes('h3') || code.includes('h4') || code.includes('text-2xl') || code.includes('text-3xl') || code.includes('text-4xl'); } /** * Verifica se tem múltiplos elementos de texto * @param {string} code - Código * @returns {boolean} */ hasMultipleTextElements(code) { const textElements = ['p', 'span', 'div', 'h1', 'h2', 'h3']; let count = 0; textElements.forEach(el => { if (code.includes(`<${el}`) || code.includes(` 2; } /** * Extrai tamanhos de fonte usados * @param {string} code - Código * @returns {string[]} */ extractFontSizes(code) { const sizes = []; const sizePatterns = [ 'text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl', 'text-3xl', 'text-4xl' ]; sizePatterns.forEach(size => { if (code.includes(size)) { sizes.push(size); } }); return sizes; } /** * Verifica se o tamanho é muito pequeno * @param {string} size - Tamanho (ex: 'text-xs', 'text-sm') * @returns {boolean} */ isTooSmall(size) { const smallSizes = ['text-xs']; return smallSizes.includes(size); } /** * Verifica se tem classes de cor * @param {string} code - Código * @returns {boolean} */ hasColorClasses(code) { const colorPatterns = [ 'text-', 'text-white', 'text-black', 'text-gray', 'text-blue', 'text-green', 'text-red', 'text-yellow' ]; return colorPatterns.some(pattern => code.includes(pattern)); } /** * Verifica se tem font-weights * @param {string} code - Código * @returns {boolean} */ hasFontWeights(code) { return code.includes('font-') || code.includes('font-bold') || code.includes('font-semibold') || code.includes('font-medium') || code.includes('font-light') || code.includes('font-normal'); } /** * Lê o conteúdo do componente * @param {string} path - Caminho do arquivo * @returns {Promise} */ async readComponent(path) { try { const fs = await import('fs/promises'); return await fs.readFile(path, 'utf-8'); } catch (error) { return null; } } }