/** * WP Agentic Writer - Utility Functions * * Pure utility functions with no React dependencies * These are shared utilities that can be used by any script * * @package WP_Agentic_Writer */ // Escape HTML to prevent XSS const escapeHtml = (value) => { if (value === null || value === undefined) return ''; return String(value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }; // Normalize message content (convert objects/arrays to string) const normalizeMessageContent = (content) => { if (typeof content === 'string' || typeof content === 'number') { return String(content); } return JSON.stringify(content); }; // Truncate text with ellipsis const truncateText = (text, maxLength = 40) => { if (!text || text.length <= maxLength) { return text; } return text.substring(0, maxLength) + '...'; }; // Convert markdown to HTML (full renderer) const markdownToHtml = (markdown, markdownit, DOMPurify) => { const raw = normalizeMessageContent(markdown); if (!raw) { return ''; } let rendered = ''; if (markdownit && DOMPurify) { const renderer = markdownit({ html: false, linkify: true, typographer: true, }); if (window.markdownitTaskLists) { renderer.use(window.markdownitTaskLists, { enabled: true, label: true, labelAfter: true }); } rendered = renderer.render(raw); if (DOMPurify.sanitize) { rendered = DOMPurify.sanitize(rendered, { ADD_TAGS: ['input'], ADD_ATTR: ['type', 'checked', 'disabled'], }); } } return rendered; }; // Extract code blocks from HTML const extractCodeBlocks = (html) => { const codeBlocks = []; const preRegex = /]*>([\s\S]*?)<\/code><\/pre>/g; let match; while ((match = preRegex.exec(html)) !== null) { const lang = match[1] || ''; const code = match[2] .replace(/</g, '<') .replace(/>/g, '>') .replace(/&/g, '&') .replace(/"/g, '"') .replace(/'/g, "'"); codeBlocks.push({ lang, code }); } return codeBlocks; }; // Debounce function const debounce = (func, wait) => { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }; // Parse outline plan from AI response const parseOutlinePlan = (content) => { const sections = []; const lines = content.split('\n'); let currentSection = null; let currentSubsection = null; lines.forEach((line) => { const trimmed = line.trim(); if (!trimmed) return; // H2 section (## Title) const h2Match = trimmed.match(/^##\s+(.+)$/); if (h2Match) { if (currentSection) { sections.push(currentSection); } currentSection = { id: 'section-' + (sections.length + 1), title: h2Match[1].trim(), subsections: [], }; currentSubsection = null; return; } // H3 subsection (### Title) const h3Match = trimmed.match(/^###\s+(.+)$/); if (h3Match && currentSection) { currentSubsection = { id: 'subsection-' + (currentSection.subsections.length + 1), title: h3Match[1].trim(), content: '', }; currentSection.subsections.push(currentSubsection); return; } // Content line if (currentSection) { if (currentSubsection) { currentSubsection.content += (currentSubsection.content ? '\n' : '') + trimmed; } else { if (!currentSection.content) { currentSection.content = trimmed; } else { currentSection.content += '\n' + trimmed; } } } }); if (currentSection) { sections.push(currentSection); } return sections; }; // Parse FAQ schema from AI response const parseFaqSchema = (content) => { const faqs = []; const faqBlocks = content.split(/\n\s*#{1,2}\s*Q[^\n]*\n/); faqBlocks.slice(1).forEach((block) => { const lines = block.trim().split('\n'); if (lines.length >= 2) { const question = lines[0].replace(/^[#*]+\s*/, '').trim(); const answer = lines.slice(1).join('\n').trim(); if (question && answer) { faqs.push({ question, answer }); } } }); return faqs; }; // Extract block preview from content const extractBlockPreview = (block) => { if (!block) return ''; const content = block.innerHTML || block.content || ''; const text = content.replace(/<[^>]+>/g, '').trim(); return truncateText(text, 100); }; // To text value helper const toTextValue = (value) => { if (typeof value === 'string') return value; if (typeof value === 'number') return String(value); if (Array.isArray(value)) return value.map(toTextValue).join(', '); if (typeof value === 'object' && value !== null) { return JSON.stringify(value); } return ''; }; // Export for use in other modules if (typeof window !== 'undefined') { window.WPAWUtils = { escapeHtml, normalizeMessageContent, truncateText, markdownToHtml, extractCodeBlocks, debounce, parseOutlinePlan, parseFaqSchema, extractBlockPreview, toTextValue, }; }