import { __, _n, sprintf } from '@/wp-externals/i18n'; import { useSelect } from '@/wp-externals/data'; import { useEntityProp } from '@/wp-externals/core-data'; import { Button, Notice, PanelBody, Spinner, ToggleControl } from '@/wp-externals/components'; import editorPkg from '@/wp-externals/editor'; import editPostPkg from '@/wp-externals/edit-post'; import { ScoreBadge } from '@/components/score-badge'; import { DiagnosticsChecklist } from '@/components/editor/diagnostics-checklist'; import { useVisibilityData, type VisibilityData } from '@/components/editor/use-visibility-data'; import { normalizeRecommendations } from '@/components/readiness-recommendations-list'; import '@/components/editor/editor-data'; /** * Gutenberg surfaces for LLMagnet (adoption plan Feature 2.2 + 5.2, * Phase E Lane E1): * - PluginSidebar: score badge, diagnostics checklist, recommendations, * llms.txt include toggle, Fix deep-link; * - PluginDocumentSettingPanel: compact score row + toggle in the Document tab; * - PluginPrePublishPanel: "AI Visibility: N — quick fixes" publish-flow summary. * * Built ONLY via vite.editor.config.ts (WordPress-provided React); registered * by src/editor-main.tsx through @wordpress/plugins. */ const TD = 'llmagnet-llm-txt-generator'; // Since WP 6.6 the Plugin* slot-fills live on wp.editor; the wp.editPost // copies still work on older versions (and log deprecations on newer ones), // so prefer the editor package and fall back. const PluginSidebar = editorPkg?.PluginSidebar ?? editPostPkg?.PluginSidebar; const PluginSidebarMoreMenuItem = editorPkg?.PluginSidebarMoreMenuItem ?? editPostPkg?.PluginSidebarMoreMenuItem; const PluginDocumentSettingPanel = editorPkg?.PluginDocumentSettingPanel ?? editPostPkg?.PluginDocumentSettingPanel; const PluginPrePublishPanel = editorPkg?.PluginPrePublishPanel ?? editPostPkg?.PluginPrePublishPanel; export function LlmagnetIcon() { // assets/llmagnet-icon.svg, fill switched to currentColor for editor chrome. return ( ); } /** "Include in llms.txt" toggle bound to `_llmagnet_exclude_from_llms` (E1-3). */ function IncludeToggle({ postType }: { postType: string }) { // Meta registered show_in_rest with per-post edit_post auth // (includes/class-post-meta.php) — zero custom endpoints needed. const [meta, setMeta] = useEntityProp('postType', postType, 'meta'); const excluded = Boolean(meta?._llmagnet_exclude_from_llms); return ( setMeta({ ...(meta ?? {}), _llmagnet_exclude_from_llms: !on })} /> ); } /** Score badge row + saving/dirty/error states + Fix deep-link (E1-2/E1-4). */ function ScoreRow({ data, isDirty, compact = false, }: { data: VisibilityData; isDirty: boolean; compact?: boolean; }) { const editorData = window.llmagnetEditorData; const fixUrl = editorData?.canManage ? editorData.drawerUrl : ''; if (data.loading) { return (

{__('Loading score…', TD)}

); } if (data.error) { return ( {sprintf( /* translators: %s: error message */ __('Could not load the AI visibility score: %s', TD), data.error )} ); } return (
{data.score === null && ( {__('Not calculated yet', TD)} )} {fixUrl && data.score !== null && data.score < 100 && ( )}
{isDirty && (

{__('You have unsaved changes — save to refresh the score.', TD)}

)}
); } /** "AI Visibility: N — quick fixes" pre-publish summary (E1-6 / plan 5.2). */ function PrePublishSummary({ data }: { data: VisibilityData }) { const recommendations = normalizeRecommendations(data.details?.recommendations); const editorData = window.llmagnetEditorData; const title = data.score !== null ? sprintf( /* translators: %d: AI visibility score 0-100 */ __('AI Visibility: %d', TD), data.score ) : __('AI Visibility', TD); return ( }>
{recommendations.length > 0 && ( <>

{sprintf( /* translators: %d: number of quick fixes */ _n('%d quick fix available', '%d quick fixes available', recommendations.length, TD), recommendations.length )}

    {recommendations.slice(0, 3).map((rec) => (
  • {rec.message}
  • ))}
)} {editorData?.canManage && editorData.drawerUrl && data.score !== null && data.score < 100 && ( )}
); } export default function EditorPanels() { const { postId, postType } = useSelect((select: any) => { const editor = select('core/editor'); return { postId: editor?.getCurrentPostId?.() ?? undefined, postType: editor?.getCurrentPostType?.() ?? undefined, }; }, []); const isDirty: boolean = useSelect( (select: any) => select('core/editor')?.isEditedPostDirty?.() ?? false, [] ); const data = useVisibilityData(postId, postType); if (!postId || !postType || !PluginSidebar || !PluginDocumentSettingPanel) { return null; } return ( <> {PluginSidebarMoreMenuItem && ( }> {__('LLMagnet', TD)} )} } >
{data.details && ( )} {data.restricted && (

{__('Detailed diagnostics require an administrator account.', TD)}

)}
{PluginPrePublishPanel && } ); }