import { useEffect, useState } from 'react'; import { isPremium, aiProxy } from './config'; // upgradeUrl PARKED below β€” see note import { runChecks, type ContentIssue, type ContentIssueType } from './checks'; const CATEGORIES: { type: ContentIssueType; label: string }[] = [ { type: 'alt_text', label: 'Images have alt text' }, { type: 'heading', label: 'Heading hierarchy valid' }, { type: 'link_text', label: 'Links descriptive' }, { type: 'video_caption', label: 'Videos have captions' }, ]; function summarizeFailure(type: ContentIssueType, catIssues: ContentIssue[]): string { const n = catIssues.length; switch (type) { case 'alt_text': return n === 1 ? '1 image is missing alt text' : `${n} images are missing alt text`; case 'link_text': return n === 1 ? `Link text "${catIssues[0].current}" doesn't describe where it goes` : `${n} links use vague text (e.g. "${catIssues[0].current}")`; case 'heading': return n === 1 ? catIssues[0].message : `${n} heading hierarchy jumps found`; case 'video_caption': return n === 1 ? '1 video is missing captions' : `${n} videos are missing captions`; } } // eslint-disable-next-line @typescript-eslint/no-explicit-any const wp = (window as any).wp; const { PluginPrePublishPanel } = wp.editPost; const { useSelect, dispatch: wpDispatch, select: wpSelect } = wp.data; const { __ } = wp.i18n; function applyFix(issue: ContentIssue, text: string): void { if (!issue.clientId) return; const { updateBlockAttributes } = wpDispatch('core/block-editor'); if (issue.type === 'alt_text') { updateBlockAttributes(issue.clientId, { alt: text }); return; } if (issue.type === 'link_text') { // eslint-disable-next-line @typescript-eslint/no-explicit-any const block = wpSelect('core/block-editor').getBlock(issue.clientId) as any; if (!block?.attributes?.content) return; const doc = new DOMParser().parseFromString(block.attributes.content, 'text/html'); const anchor = Array.from(doc.querySelectorAll('a')).find( (a) => (a.textContent ?? '').trim().toLowerCase() === (issue.current ?? '').trim().toLowerCase() ); if (anchor) { anchor.textContent = text; updateBlockAttributes(issue.clientId, { content: doc.body.innerHTML }); } } } export default function ContentReviewPanel() { const [issues, setIssues] = useState([]); const [fixingId, setFixingId] = useState(null); const [fixError, setFixError] = useState(null); // Re-run checks whenever the saved-to-editor-state content changes. const editedContent: string = useSelect( // eslint-disable-next-line @typescript-eslint/no-explicit-any (select: any) => select('core/editor').getEditedPostContent(), [] ); useEffect(() => { if (!isPremium) return; const timer = setTimeout(() => setIssues(runChecks()), 400); return () => clearTimeout(timer); }, [editedContent]); if (!isPremium) { /* PARKED: Pro upsell teaser for AI Content Review β€” hold until Pro edition public release. * Re-enable by restoring this panel body (re-add `upgradeUrl` to the ./config * import above) in place of `return null` β€” see git history for the removed markup: return (

{__('Catch missing alt text, heading jumps, vague link text and uncaptioned video before you publish.', 'accessmate')}

{__('Upgrade to Pro', 'accessmate')} πŸ”’
); */ return null; } async function fix(issue: ContentIssue) { if (!issue.fixable) return; setFixingId(issue.id); setFixError(null); try { const task: string = issue.type === 'alt_text' ? 'alt_text' : 'link_text'; const params: Record = { context: issue.context ?? '' }; if (issue.type === 'link_text') { params.current = issue.current ?? ''; params.url = issue.url ?? ''; } if (issue.type === 'alt_text' && issue.attachmentId) { // Lets the backend send the actual image to a vision-capable model // instead of guessing from the block's caption/URL text alone. params.attachment_id = String(issue.attachmentId); } const result = await aiProxy(task, params); if (result.text) { applyFix(issue, result.text.trim()); } else { setFixError(result.error || __('AI fix failed β€” no suggestion returned.', 'accessmate')); } } catch { setFixError(__('AI fix failed β€” check your connection and try again.', 'accessmate')); } finally { setFixingId(null); setIssues(runChecks()); } } const title = issues.length ? `${__('AI Content Review', 'accessmate')} (${issues.length})` : __('AI Content Review', 'accessmate'); const fixableIssues = issues.filter((i) => i.fixable); return ( 0}>

{__('Before publishing:', 'accessmate')}

    {CATEGORIES.map((cat) => { const catIssues = issues.filter((i) => i.type === cat.type); const passed = catIssues.length === 0; return (
  • {passed ? __(cat.label, 'accessmate') : summarizeFailure(cat.type, catIssues)}

  • ); })}
{fixableIssues.length > 0 && (
    {fixableIssues.map((issue) => (
  • {issue.message}

  • ))}
)} {fixError && (

{fixError}

)}
); }