| @@ -27,9 +27,8 @@ | ||
| 27 | 27 | historyLoading: false, |
| 28 | 28 | phraseTimer: null, |
| 29 | 29 | phraseIndex: 0, |
| 30 | 30 | pollStartTime: null, |
| 31 | - customSystemPrompt: '', | |
| 32 | 31 | lastProgressTime: null, |
| 33 | 32 | postStatus: null |
| 34 | 33 | }; |
| 35 | 34 | |
| @@ -180,91 +179,8 @@ | ||
| 180 | 179 | startGeneration(); |
| 181 | 180 | }); |
| 182 | 181 | } |
| 183 | 182 | |
| 184 | - // ── Edit Default Prompt Modal ────────────────────────────── | |
| 185 | - function initPromptModal() { | |
| 186 | - var $modal = $('#mxch-cg-prompt-modal'); | |
| 187 | - var $editor = $('#mxch-cg-system-prompt-editor'); | |
| 188 | - var $btn = $('#mxch-cg-edit-prompt-btn'); | |
| 189 | - var currentDefault = ''; | |
| 190 | - | |
| 191 | - function fetchPromptData(callback) { | |
| 192 | - var contentType = $('#mxch-cg-type').val() || 'post'; | |
| 193 | - $.post(mxchatContent.ajaxUrl, { | |
| 194 | - action: 'mxchat_get_default_prompt', | |
| 195 | - nonce: mxchatContent.nonce, | |
| 196 | - content_type: contentType | |
| 197 | - }, function(response) { | |
| 198 | - if (response.success) { | |
| 199 | - currentDefault = response.data.default_prompt; | |
| 200 | - var saved = response.data.saved_prompt || ''; | |
| 201 | - state.customSystemPrompt = saved; | |
| 202 | - updateButtonState(); | |
| 203 | - if (callback) callback(currentDefault, saved); | |
| 204 | - } | |
| 205 | - }); | |
| 206 | - } | |
| 207 | - | |
| 208 | - function updateButtonState() { | |
| 209 | - if (state.customSystemPrompt) { | |
| 210 | - $btn.addClass('mxch-cg-prompt-modified'); | |
| 211 | - } else { | |
| 212 | - $btn.removeClass('mxch-cg-prompt-modified'); | |
| 213 | - } | |
| 214 | - } | |
| 215 | - | |
| 216 | - function openModal() { | |
| 217 | - fetchPromptData(function(def, saved) { | |
| 218 | - $editor.val(saved || def); | |
| 219 | - $modal.fadeIn(200); | |
| 220 | - }); | |
| 221 | - } | |
| 222 | - | |
| 223 | - function closeModal() { | |
| 224 | - $modal.fadeOut(200); | |
| 225 | - } | |
| 226 | - | |
| 227 | - function saveToServer(promptText, callback) { | |
| 228 | - var contentType = $('#mxch-cg-type').val() || 'post'; | |
| 229 | - $.post(mxchatContent.ajaxUrl, { | |
| 230 | - action: 'mxchat_save_custom_prompt', | |
| 231 | - nonce: mxchatContent.nonce, | |
| 232 | - content_type: contentType, | |
| 233 | - custom_prompt: promptText | |
| 234 | - }, function(response) { | |
| 235 | - if (callback) callback(response.success); | |
| 236 | - }); | |
| 237 | - } | |
| 238 | - | |
| 239 | - $btn.on('click', openModal); | |
| 240 | - $('#mxch-cg-prompt-modal-close, #mxch-cg-prompt-cancel, .mxch-cg-prompt-modal-overlay').on('click', closeModal); | |
| 241 | - | |
| 242 | - $('#mxch-cg-prompt-save').on('click', function() { | |
| 243 | - var edited = $editor.val().trim(); | |
| 244 | - var customValue = (edited && edited !== currentDefault) ? edited : ''; | |
| 245 | - state.customSystemPrompt = customValue; | |
| 246 | - saveToServer(customValue); | |
| 247 | - updateButtonState(); | |
| 248 | - closeModal(); | |
| 249 | - }); | |
| 250 | - | |
| 251 | - $('#mxch-cg-prompt-reset').on('click', function() { | |
| 252 | - $editor.val(currentDefault); | |
| 253 | - state.customSystemPrompt = ''; | |
| 254 | - saveToServer(''); | |
| 255 | - updateButtonState(); | |
| 256 | - }); | |
| 257 | - | |
| 258 | - // Load saved state for initial content type on page load | |
| 259 | - fetchPromptData(); | |
| 260 | - | |
| 261 | - // When content type changes, load the saved prompt for that type | |
| 262 | - $('#mxch-cg-type').on('change', function() { | |
| 263 | - fetchPromptData(); | |
| 264 | - }); | |
| 265 | - } | |
| 266 | - | |
| 267 | 183 | function startGeneration() { |
| 268 | 184 | var prompt = $('#mxch-cg-prompt').val().trim(); |
| 269 | 185 | if (!prompt) { |
| 270 | 186 | showNotice('Please enter a prompt describing the content you want to generate.', 'error'); |
| @@ -292,11 +208,9 @@ | ||
| 292 | 208 | content_type: $('#mxch-cg-type').val(), |
| 293 | 209 | post_status: $('#mxch-cg-status').val(), |
| 294 | 210 | schedule_date: $('#mxch-cg-schedule').val() || '', |
| 295 | 211 | layout: $('#mxch-cg-layout').val() || 'fullwidth', |
| 296 | - title_display: $('#mxch-cg-title-display').val() || 'hide', | |
| 297 | - template_mode: $('#mxch-cg-template-mode').val() || 'off', | |
| 298 | - custom_system_prompt: state.customSystemPrompt || '' | |
| 212 | + title_display: $('#mxch-cg-title-display').val() || 'hide' | |
| 299 | 213 | }; |
| 300 | 214 | |
| 301 | 215 | $.ajax({ |
| 302 | 216 | url: mxchatContent.ajaxUrl, |
| @@ -948,15 +862,8 @@ | ||
| 948 | 862 | }); |
| 949 | 863 | $('#mxch-seo-ai-optimize').on('click', function() { |
| 950 | 864 | if (state.postId && !seoState.fixing) runAiOptimize(); |
| 951 | 865 | }); |
| 952 | - // Per-check AI fix buttons (content editor panel only, not dashboard modal) | |
| 953 | - $(document).on('click', '.mxch-seo-check-fix:not(.mxch-seod-check-fix-btn)', function(e) { | |
| 954 | - e.stopPropagation(); | |
| 955 | - var $btn = $(this); | |
| 956 | - if ($btn.hasClass('mxch-seo-check-fixing') || !state.postId) return; | |
| 957 | - runSeoFixSingle($btn.data('field'), $btn); | |
| 958 | - }); | |
| 959 | 866 | // Auto-analyze when switching to SEO tab if content exists |
| 960 | 867 | $(document).on('click', '.mxch-cg-left-tab[data-tab="seo"]', function() { |
| 961 | 868 | if (state.postId && !seoState.analyzed && !seoState.analyzing) runSeoAnalysis(); |
| 962 | 869 | }); |
| @@ -1011,11 +918,8 @@ | ||
| 1011 | 918 | fail: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>', |
| 1012 | 919 | }; |
| 1013 | 920 | // Checks that require the Advanced Content Editor add-on to fix |
| 1014 | 921 | var addonChecks = { readability: true, internal_links: true, img_alt: true, featured_img: true }; |
| 1015 | - // Map check key → optimize field name for per-check fix buttons | |
| 1016 | - var fixableMap = { meta_desc: 'meta_description', title_length: 'seo_title', slug: 'slug', readability: 'readability', internal_links: 'internal_links', img_alt: 'img_alt', featured_img: 'featured_img' }; | |
| 1017 | - var sparkleIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 3-1.9 5.8a2 2 0 0 1-1.287 1.288L3 12l5.8 1.9a2 2 0 0 1 1.288 1.287L12 21l1.9-5.8a2 2 0 0 1 1.287-1.288L21 12l-5.8-1.9a2 2 0 0 1-1.288-1.287Z"/></svg>'; | |
| 1018 | 922 | var sorted = Object.keys(checks).sort(function(a, b) { |
| 1019 | 923 | var o = { fail: 0, warn: 1, pass: 2 }; |
| 1020 | 924 | return (o[checks[a].status] || 2) - (o[checks[b].status] || 2); |
| 1021 | 925 | }); |
| @@ -1036,17 +940,8 @@ | ||
| 1036 | 940 | badge = ' <a href="https://mxchat.ai/" target="_blank" class="mxch-seod-addon-badge mxch-seod-pro-badge">PRO</a>'; |
| 1037 | 941 | } |
| 1038 | 942 | } |
| 1039 | 943 | |
| 1040 | - // Per-check AI fix button for non-passing, fixable checks | |
| 1041 | - var fixBtn = ''; | |
| 1042 | - if (c.status !== 'pass' && fixableMap[key]) { | |
| 1043 | - var canFix = !addonChecks[key] || mxchatContent.hasAdvancedContent; | |
| 1044 | - if (canFix) { | |
| 1045 | - fixBtn = '<button type="button" class="mxch-seo-check-fix" data-field="' + fixableMap[key] + '" title="AI Fix">' + sparkleIcon + '</button>'; | |
| 1046 | - } | |
| 1047 | - } | |
| 1048 | - | |
| 1049 | 944 | $list.append( |
| 1050 | 945 | '<div class="mxch-seo-check" data-check="' + key + '">' + |
| 1051 | 946 | '<div class="mxch-seo-check-icon mxch-seo-' + c.status + '">' + icons[c.status] + '</div>' + |
| 1052 | 947 | '<div class="mxch-seo-check-content">' + |
| @@ -1052,9 +947,8 @@ | ||
| 1052 | 947 | '<div class="mxch-seo-check-content">' + |
| 1053 | 948 | '<span class="mxch-seo-check-label">' + escapeHtml(c.label) + badge + '</span>' + |
| 1054 | 949 | '<span class="mxch-seo-check-detail">' + escapeHtml(c.detail) + '</span>' + |
| 1055 | 950 | '</div>' + |
| 1056 | - fixBtn + | |
| 1057 | 951 | '</div>' |
| 1058 | 952 | ); |
| 1059 | 953 | }); |
| 1060 | 954 | $('#mxch-seo-actions').toggle(summary.fail > 0 || summary.warn > 0); |
| @@ -1063,28 +957,8 @@ | ||
| 1063 | 957 | function renderSeoError(msg) { |
| 1064 | 958 | $('#mxch-seo-checklist').html('<div class="mxch-seo-empty"><span style="color:#ef4444;">' + escapeHtml(msg) + '</span></div>'); |
| 1065 | 959 | } |
| 1066 | 960 | |
| 1067 | - function runSeoFixSingle(field, $btn) { | |
| 1068 | - $btn.addClass('mxch-seo-check-fixing').prop('disabled', true); | |
| 1069 | - $.post(ajaxurl, { | |
| 1070 | - action: 'mxchat_seo_suggest', | |
| 1071 | - nonce: mxchatContent.nonce, | |
| 1072 | - post_id: state.postId, | |
| 1073 | - field: field, | |
| 1074 | - }).done(function(res) { | |
| 1075 | - if (res.success) { | |
| 1076 | - if (field === 'meta_description') $('#mxch-cg-meta-description').val(res.data.suggestion).trigger('input'); | |
| 1077 | - else if (field === 'seo_title') $('#mxch-cg-meta-title').val(res.data.suggestion); | |
| 1078 | - $('.mxch-cg-left-tab[data-tab="meta"]').addClass('mxch-cg-tab-flash'); | |
| 1079 | - setTimeout(function() { $('.mxch-cg-left-tab[data-tab="meta"]').removeClass('mxch-cg-tab-flash'); }, 2000); | |
| 1080 | - } | |
| 1081 | - }).always(function() { | |
| 1082 | - $btn.removeClass('mxch-seo-check-fixing').prop('disabled', false); | |
| 1083 | - runSeoAnalysis(); | |
| 1084 | - }); | |
| 1085 | - } | |
| 1086 | - | |
| 1087 | 961 | function runAiOptimize() { |
| 1088 | 962 | if (!state.postId || seoState.fixing) return; |
| 1089 | 963 | seoState.fixing = true; |
| 1090 | 964 | var $btn = $('#mxch-seo-ai-optimize'), origHtml = $btn.html(); |
| @@ -1095,28 +969,12 @@ | ||
| 1095 | 969 | if (seoState.checks) { |
| 1096 | 970 | if (prefs.meta_description !== false && seoState.checks.meta_desc && seoState.checks.meta_desc.status !== 'pass') fields.push('meta_description'); |
| 1097 | 971 | if (prefs.seo_title !== false && seoState.checks.title_length && seoState.checks.title_length.status !== 'pass') fields.push('seo_title'); |
| 1098 | 972 | if (prefs.slug !== false && seoState.checks.slug && seoState.checks.slug.status !== 'pass') fields.push('slug'); |
| 1099 | - // Readability, internal links, images require Advanced Content Editor add-on | |
| 1100 | - if (mxchatContent.hasAdvancedContent) { | |
| 1101 | - if (prefs.readability !== false && seoState.checks.readability && seoState.checks.readability.status !== 'pass') fields.push('readability'); | |
| 1102 | - if (prefs.internal_links !== false && seoState.checks.internal_links && seoState.checks.internal_links.status !== 'pass') fields.push('internal_links'); | |
| 1103 | - if (prefs.img_alt !== false && seoState.checks.img_alt && seoState.checks.img_alt.status !== 'pass') fields.push('img_alt'); | |
| 1104 | - if (prefs.featured_img !== false && seoState.checks.featured_img && seoState.checks.featured_img.status !== 'pass') fields.push('featured_img'); | |
| 1105 | - } | |
| 1106 | 973 | } |
| 1107 | 974 | if (!fields.length) fields.push('meta_description'); |
| 1108 | - // Run fields sequentially to avoid race conditions | |
| 1109 | - // (multiple optimizers read/write post_content) | |
| 1110 | - var idx = 0; | |
| 1111 | - function runNext() { | |
| 1112 | - if (idx >= fields.length) { | |
| 1113 | - seoState.fixing = false; | |
| 1114 | - $btn.removeClass('mxch-seo-fixing').prop('disabled', false).html(origHtml); | |
| 1115 | - runSeoAnalysis(); | |
| 1116 | - return; | |
| 1117 | - } | |
| 1118 | - var field = fields[idx]; | |
| 975 | + var done = 0; | |
| 976 | + fields.forEach(function(field) { | |
| 1119 | 977 | $.post(ajaxurl, { |
| 1120 | 978 | action: 'mxchat_seo_suggest', |
| 1121 | 979 | nonce: mxchatContent.nonce, |
| 1122 | 980 | post_id: state.postId, |
| @@ -1129,13 +987,15 @@ | ||
| 1129 | 987 | $('.mxch-cg-left-tab[data-tab="meta"]').addClass('mxch-cg-tab-flash'); |
| 1130 | 988 | setTimeout(function() { $('.mxch-cg-left-tab[data-tab="meta"]').removeClass('mxch-cg-tab-flash'); }, 2000); |
| 1131 | 989 | } |
| 1132 | 990 | }).always(function() { |
| 1133 | - idx++; | |
| 1134 | - runNext(); | |
| 991 | + if (++done >= fields.length) { | |
| 992 | + seoState.fixing = false; | |
| 993 | + $btn.removeClass('mxch-seo-fixing').prop('disabled', false).html(origHtml); | |
| 994 | + runSeoAnalysis(); | |
| 995 | + } | |
| 1135 | 996 | }); |
| 1136 | - } | |
| 1137 | - runNext(); | |
| 997 | + }); | |
| 1138 | 998 | } |
| 1139 | 999 | |
| 1140 | 1000 | function resetSeoPanel() { |
| 1141 | 1001 | seoState = { analyzed: false, analyzing: false, fixing: false, score: null, checks: null }; |
| @@ -1656,15 +1516,8 @@ | ||
| 1656 | 1516 | seodState.scanAborted = true; |
| 1657 | 1517 | $(this).prop('disabled', true).find('span').text('Stopping...'); |
| 1658 | 1518 | }); |
| 1659 | 1519 | |
| 1660 | - // "Connect Search Console" hint row → jump to the Settings section, | |
| 1661 | - // where the add-on's GSC card lives (eadc60). | |
| 1662 | - $(document).on('click', '.mxch-seod-gsc-hint-link', function(e) { | |
| 1663 | - e.preventDefault(); | |
| 1664 | - $('.mxch-nav-link[data-target="content-settings"]').trigger('click'); | |
| 1665 | - }); | |
| 1666 | - | |
| 1667 | 1520 | // Sortable column headers (all columns sort server-side) |
| 1668 | 1521 | $(document).on('click', '.mxch-seod-header-cell[data-sort]', function() { |
| 1669 | 1522 | var col = $(this).data('sort'); |
| 1670 | 1523 | if (seodState.sortBy === col) { |
| @@ -1682,65 +1535,10 @@ | ||
| 1682 | 1535 | e.stopPropagation(); |
| 1683 | 1536 | var postId = $(this).closest('.mxch-seod-detail').data('post-id'); |
| 1684 | 1537 | runSeodOptimize(postId, $(this)); |
| 1685 | 1538 | }); |
| 1686 | - | |
| 1687 | - // Per-check AI fix buttons in detail modal | |
| 1688 | - $(document).on('click', '.mxch-seod-check-fix-btn', function(e) { | |
| 1689 | - e.stopPropagation(); | |
| 1690 | - var $btn = $(this); | |
| 1691 | - if ($btn.hasClass('mxch-seo-check-fixing')) return; | |
| 1692 | - var field = $btn.data('field'); | |
| 1693 | - var postId = $btn.data('post-id'); | |
| 1694 | - $btn.addClass('mxch-seo-check-fixing').prop('disabled', true); | |
| 1695 | - $.post(ajaxurl, { | |
| 1696 | - action: 'mxchat_seo_suggest', | |
| 1697 | - nonce: mxchatContent.nonce, | |
| 1698 | - post_id: postId, | |
| 1699 | - field: field, | |
| 1700 | - }).always(function() { | |
| 1701 | - $btn.removeClass('mxch-seo-check-fixing').prop('disabled', false); | |
| 1702 | - if (seodState.expandedId === postId) { | |
| 1703 | - openSeoModal(postId); | |
| 1704 | - } | |
| 1705 | - }); | |
| 1706 | - }); | |
| 1707 | - | |
| 1708 | - // Checkbox: prevent row click when clicking checkbox | |
| 1709 | - $(document).on('click', '.mxch-seod-cell-check, .mxch-seod-header-check', function(e) { | |
| 1710 | - e.stopPropagation(); | |
| 1711 | - }); | |
| 1712 | - | |
| 1713 | - // Select all checkbox | |
| 1714 | - $(document).on('change', '.mxch-seod-check-all', function() { | |
| 1715 | - var checked = $(this).prop('checked'); | |
| 1716 | - $('.mxch-seod-row-check').prop('checked', checked); | |
| 1717 | - updateOptimizeSelectedBtn(); | |
| 1718 | - }); | |
| 1719 | - | |
| 1720 | - // Individual row checkbox | |
| 1721 | - $(document).on('change', '.mxch-seod-row-check', function() { | |
| 1722 | - var allChecked = $('.mxch-seod-row-check').length === $('.mxch-seod-row-check:checked').length; | |
| 1723 | - $('.mxch-seod-check-all').prop('checked', allChecked); | |
| 1724 | - updateOptimizeSelectedBtn(); | |
| 1725 | - }); | |
| 1726 | 1539 | } |
| 1727 | 1540 | |
| 1728 | - function updateOptimizeSelectedBtn() { | |
| 1729 | - var count = $('.mxch-seod-row-check:checked').length; | |
| 1730 | - var $btn = $('#mxch-seod-optimize-selected'); | |
| 1731 | - var $note = $('#mxch-seod-bulk-note'); | |
| 1732 | - var isLocked = $btn.hasClass('mxch-seod-bulk-locked'); | |
| 1733 | - if (count > 0) { | |
| 1734 | - $btn.find('span').first().text(isLocked ? 'Bulk Optimize' : 'Optimize Selected (' + count + ')'); | |
| 1735 | - $btn.show(); | |
| 1736 | - if (isLocked) $note.show(); | |
| 1737 | - } else { | |
| 1738 | - $btn.hide(); | |
| 1739 | - $note.hide(); | |
| 1740 | - } | |
| 1741 | - } | |
| 1742 | - | |
| 1743 | 1541 | function loadSeoPosts() { |
| 1744 | 1542 | seodState.loading = true; |
| 1745 | 1543 | $('#mxch-seod-loading').show(); |
| 1746 | 1544 | $('#mxch-seod-empty').hide(); |
| @@ -1792,39 +1590,20 @@ | ||
| 1792 | 1590 | var $table = $('#mxch-seod-table'); |
| 1793 | 1591 | $table.empty(); |
| 1794 | 1592 | seodState.expandedId = null; |
| 1795 | 1593 | |
| 1796 | - // Column headers. | |
| 1797 | - // gscUnlinked: the add-on unlocked the GSC columns, but Search Console | |
| 1798 | - // itself isn't linked yet — every cell is a dash, so sorting Clicks / | |
| 1799 | - // Impr. is noise. Keep the unlocked look, drop data-sort, and show one | |
| 1800 | - // slim hint row pointing at the Settings tab (eadc60). When linked, | |
| 1801 | - // rendering below is byte-identical to before. | |
| 1802 | - var gscUnlinked = mxchatContent.hasGSC && !mxchatContent.gscLinked; | |
| 1803 | - var gscSortable = mxchatContent.hasGSC && !gscUnlinked; | |
| 1594 | + // Column headers | |
| 1804 | 1595 | var activeClass = function(col) { return seodState.sortBy === col ? ' mxch-seod-header-active' : ''; }; |
| 1805 | 1596 | $table.append( |
| 1806 | 1597 | '<div class="mxch-seod-header">' + |
| 1807 | - '<div class="mxch-seod-header-cell mxch-seod-header-check"><input type="checkbox" class="mxch-seod-check-all" title="Select all"></div>' + | |
| 1808 | 1598 | '<div class="mxch-seod-header-cell mxch-seod-header-title' + activeClass('title') + '" data-sort="title">Title' + seodSortArrow('title') + '</div>' + |
| 1809 | 1599 | '<div class="mxch-seod-header-cell mxch-seod-header-date' + activeClass('date') + '" data-sort="date">Date' + seodSortArrow('date') + '</div>' + |
| 1810 | 1600 | '<div class="mxch-seod-header-cell mxch-seod-header-score' + activeClass('score') + '" data-sort="score">Score' + seodSortArrow('score') + '</div>' + |
| 1811 | - '<div class="mxch-seod-header-cell mxch-seod-header-clicks' + (!mxchatContent.hasGSC ? ' mxch-seod-header-locked' : '') + '"' + (gscSortable ? ' data-sort="clicks"' : '') + '>Clicks' + (gscSortable ? seodSortArrow('clicks') : '') + (!mxchatContent.hasGSC ? ' <svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>' : '') + '</div>' + | |
| 1812 | - '<div class="mxch-seod-header-cell mxch-seod-header-impressions' + (!mxchatContent.hasGSC ? ' mxch-seod-header-locked' : '') + '"' + (gscSortable ? ' data-sort="impressions"' : '') + '>Impr.' + (gscSortable ? seodSortArrow('impressions') : '') + (!mxchatContent.hasGSC ? ' <svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>' : '') + '</div>' + | |
| 1813 | - '<div class="mxch-seod-header-cell mxch-seod-header-position' + (!mxchatContent.hasGSC ? ' mxch-seod-header-locked' : '') + '" title="Average search position, last 28 days">Pos.' + (!mxchatContent.hasGSC ? ' <svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>' : '') + '</div>' + | |
| 1601 | + '<div class="mxch-seod-header-cell mxch-seod-header-clicks' + (!mxchatContent.hasGSC ? ' mxch-seod-header-locked' : '') + '"' + (mxchatContent.hasGSC ? ' data-sort="clicks"' : '') + '>Clicks' + (mxchatContent.hasGSC ? seodSortArrow('clicks') : ' <svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>') + '</div>' + | |
| 1602 | + '<div class="mxch-seod-header-cell mxch-seod-header-impressions' + (!mxchatContent.hasGSC ? ' mxch-seod-header-locked' : '') + '"' + (mxchatContent.hasGSC ? ' data-sort="impressions"' : '') + '>Impr.' + (mxchatContent.hasGSC ? seodSortArrow('impressions') : ' <svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>') + '</div>' + | |
| 1814 | 1603 | '</div>' |
| 1815 | 1604 | ); |
| 1816 | 1605 | |
| 1817 | - if (gscUnlinked) { | |
| 1818 | - $table.append( | |
| 1819 | - '<div class="mxch-seod-gsc-hint">' + | |
| 1820 | - '<span class="mxch-seod-gsc-hint-dot"></span>' + | |
| 1821 | - '<span>Connect Google Search Console to fill the Clicks, Impressions and Position columns — ' + | |
| 1822 | - '<a href="#" class="mxch-seod-gsc-hint-link">open Settings</a></span>' + | |
| 1823 | - '</div>' | |
| 1824 | - ); | |
| 1825 | - } | |
| 1826 | - | |
| 1827 | 1606 | posts.forEach(function(p) { |
| 1828 | 1607 | var scoreHtml; |
| 1829 | 1608 | if (p.score !== null) { |
| 1830 | 1609 | var cls = p.score >= 80 ? 'mxch-seod-good' : p.score >= 50 ? 'mxch-seod-ok' : 'mxch-seod-bad'; |
| @@ -1832,14 +1611,13 @@ | ||
| 1832 | 1611 | } else { |
| 1833 | 1612 | scoreHtml = '<div class="mxch-seod-score-badge mxch-seod-unscored">—</div>'; |
| 1834 | 1613 | } |
| 1835 | 1614 | |
| 1836 | - var typeLabel = p.type.charAt(0).toUpperCase() + p.type.slice(1); | |
| 1615 | + var typeLabel = p.type === 'page' ? 'Page' : 'Post'; | |
| 1837 | 1616 | |
| 1838 | 1617 | $table.append( |
| 1839 | - '<div class="mxch-seod-row" data-post-id="' + p.id + '" data-score="' + (p.score !== null ? p.score : -1) + '" data-permalink="' + escapeAttr(p.permalink) + '">' + | |
| 1618 | + '<div class="mxch-seod-row" data-post-id="' + p.id + '" data-score="' + (p.score !== null ? p.score : -1) + '">' + | |
| 1840 | 1619 | '<div class="mxch-seod-row-main">' + |
| 1841 | - '<div class="mxch-seod-cell mxch-seod-cell-check"><input type="checkbox" class="mxch-seod-row-check" data-post-id="' + p.id + '"></div>' + | |
| 1842 | 1620 | '<div class="mxch-seod-cell mxch-seod-cell-title">' + |
| 1843 | 1621 | '<span class="mxch-seod-title">' + escapeHtml(p.title) + '</span>' + |
| 1844 | 1622 | '<span class="mxch-seod-meta">' + typeLabel + '</span>' + |
| 1845 | 1623 | '</div>' + |
| @@ -1848,9 +1626,8 @@ | ||
| 1848 | 1626 | '</div>' + |
| 1849 | 1627 | '<div class="mxch-seod-cell mxch-seod-cell-score">' + scoreHtml + '</div>' + |
| 1850 | 1628 | '<div class="mxch-seod-cell mxch-seod-cell-clicks' + (!mxchatContent.hasGSC ? ' mxch-seod-cell-locked' : '') + '" data-clicks="' + (p.clicks !== null ? p.clicks : 0) + '">' + (!mxchatContent.hasGSC ? '—' : (p.clicks !== null ? p.clicks : '—')) + '</div>' + |
| 1851 | 1629 | '<div class="mxch-seod-cell mxch-seod-cell-impressions' + (!mxchatContent.hasGSC ? ' mxch-seod-cell-locked' : '') + '" data-impressions="' + (p.impressions !== null ? p.impressions : 0) + '">' + (!mxchatContent.hasGSC ? '—' : (p.impressions !== null ? seodFormatNum(p.impressions) : '—')) + '</div>' + |
| 1852 | - '<div class="mxch-seod-cell mxch-seod-cell-position' + (!mxchatContent.hasGSC ? ' mxch-seod-cell-locked' : '') + '">—</div>' + | |
| 1853 | 1630 | '</div>' + |
| 1854 | 1631 | '</div>' |
| 1855 | 1632 | ); |
| 1856 | 1633 | }); |
| @@ -1888,9 +1665,8 @@ | ||
| 1888 | 1665 | seodState.expandedId = postId; |
| 1889 | 1666 | |
| 1890 | 1667 | var $row = $('.mxch-seod-row[data-post-id="' + postId + '"]'); |
| 1891 | 1668 | var title = $row.find('.mxch-seod-title').text() || 'Post #' + postId; |
| 1892 | - var permalink = $row.attr('data-permalink') || ''; | |
| 1893 | 1669 | |
| 1894 | 1670 | var $overlay = $( |
| 1895 | 1671 | '<div class="mxch-seod-modal-overlay">' + |
| 1896 | 1672 | '<div class="mxch-seod-modal">' + |
| @@ -1895,11 +1671,8 @@ | ||
| 1895 | 1671 | '<div class="mxch-seod-modal-overlay">' + |
| 1896 | 1672 | '<div class="mxch-seod-modal">' + |
| 1897 | 1673 | '<div class="mxch-seod-modal-header">' + |
| 1898 | 1674 | '<h3 class="mxch-seod-modal-title">' + escapeHtml(title) + '</h3>' + |
| 1899 | - (permalink ? '<a href="' + escapeAttr(permalink) + '" target="_blank" class="mxch-seod-modal-view-page" title="View Page">' + | |
| 1900 | - '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>' + | |
| 1901 | - ' View Page</a>' : '') + | |
| 1902 | 1675 | '<button type="button" class="mxch-seod-modal-close" title="Close">' + |
| 1903 | 1676 | '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>' + |
| 1904 | 1677 | '</button>' + |
| 1905 | 1678 | '</div>' + |
| @@ -1956,10 +1729,8 @@ | ||
| 1956 | 1729 | }; |
| 1957 | 1730 | |
| 1958 | 1731 | // Checks that require the Advanced Content Editor add-on to fix |
| 1959 | 1732 | var addonChecks = { readability: true, internal_links: true, img_alt: true, featured_img: true }; |
| 1960 | - var fixableMap = { meta_desc: 'meta_description', title_length: 'seo_title', slug: 'slug', readability: 'readability', internal_links: 'internal_links', img_alt: 'img_alt', featured_img: 'featured_img' }; | |
| 1961 | - var sparkleIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 3-1.9 5.8a2 2 0 0 1-1.287 1.288L3 12l5.8 1.9a2 2 0 0 1 1.288 1.287L12 21l1.9-5.8a2 2 0 0 1 1.287-1.288L21 12l-5.8-1.9a2 2 0 0 1-1.288-1.287Z"/></svg>'; | |
| 1962 | 1733 | |
| 1963 | 1734 | var sorted = Object.keys(checks).sort(function(a, b) { |
| 1964 | 1735 | var o = { fail: 0, warn: 1, pass: 2 }; |
| 1965 | 1736 | return (o[checks[a].status] || 2) - (o[checks[b].status] || 2); |
| @@ -1983,17 +1754,8 @@ | ||
| 1983 | 1754 | badge = ' <a href="https://mxchat.ai/" target="_blank" class="mxch-seod-addon-badge mxch-seod-pro-badge">PRO</a>'; |
| 1984 | 1755 | } |
| 1985 | 1756 | } |
| 1986 | 1757 | |
| 1987 | - // Per-check AI fix button | |
| 1988 | - var fixBtn = ''; | |
| 1989 | - if (c.status !== 'pass' && fixableMap[key]) { | |
| 1990 | - var canFix = !addonChecks[key] || mxchatContent.hasAdvancedContent; | |
| 1991 | - if (canFix) { | |
| 1992 | - fixBtn = '<button type="button" class="mxch-seo-check-fix mxch-seod-check-fix-btn" data-field="' + fixableMap[key] + '" data-post-id="' + postId + '" title="AI Fix">' + sparkleIcon + '</button>'; | |
| 1993 | - } | |
| 1994 | - } | |
| 1995 | - | |
| 1996 | 1758 | html += '<div class="mxch-seod-check mxch-seod-check-' + c.status + '">' + |
| 1997 | 1759 | '<div class="mxch-seod-check-icon">' + icons[c.status] + '</div>' + |
| 1998 | 1760 | '<div class="mxch-seod-check-text">' + |
| 1999 | 1761 | '<span class="mxch-seod-check-label">' + escapeHtml(c.label) + badge + '</span>' + |
| @@ -1998,19 +1760,18 @@ | ||
| 1998 | 1760 | '<div class="mxch-seod-check-text">' + |
| 1999 | 1761 | '<span class="mxch-seod-check-label">' + escapeHtml(c.label) + badge + '</span>' + |
| 2000 | 1762 | '<span class="mxch-seod-check-detail">' + escapeHtml(c.detail) + '</span>' + |
| 2001 | 1763 | '</div>' + |
| 2002 | - fixBtn + | |
| 2003 | 1764 | '</div>'; |
| 2004 | 1765 | }); |
| 2005 | 1766 | html += '</div>'; |
| 2006 | 1767 | |
| 2007 | - // Optimize All button (only if there are issues) | |
| 1768 | + // AI Optimize button (only if there are issues) | |
| 2008 | 1769 | if (summary.fail > 0 || summary.warn > 0) { |
| 2009 | 1770 | html += '<div class="mxch-seod-detail-actions">' + |
| 2010 | 1771 | '<button type="button" class="mxch-seod-optimize-btn">' + |
| 2011 | 1772 | '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 3-1.9 5.8a2 2 0 0 1-1.287 1.288L3 12l5.8 1.9a2 2 0 0 1 1.288 1.287L12 21l1.9-5.8a2 2 0 0 1 1.287-1.288L21 12l-5.8-1.9a2 2 0 0 1-1.288-1.287Z"/></svg>' + |
| 2012 | - ' Optimize All' + | |
| 1773 | + ' AI Optimize' + | |
| 2013 | 1774 | '</button>' + |
| 2014 | 1775 | '</div>'; |
| 2015 | 1776 | } |
| 2016 | 1777 | |
| @@ -2197,30 +1958,25 @@ | ||
| 2197 | 1958 | if (prefs.featured_img !== false && checks.featured_img && checks.featured_img.status !== 'pass') fields.push('featured_img'); |
| 2198 | 1959 | } |
| 2199 | 1960 | if (!fields.length) fields.push('meta_description'); |
| 2200 | 1961 | |
| 2201 | - // Run fields sequentially to avoid race conditions | |
| 2202 | - // (multiple optimizers read/write post_content) | |
| 2203 | - var idx = 0; | |
| 2204 | - function runNext() { | |
| 2205 | - if (idx >= fields.length) { | |
| 2206 | - if (seodState.expandedId === postId) { | |
| 2207 | - openSeoModal(postId); | |
| 2208 | - } | |
| 2209 | - $btn.prop('disabled', false).html(origHtml); | |
| 2210 | - return; | |
| 2211 | - } | |
| 1962 | + var done = 0; | |
| 1963 | + fields.forEach(function(field) { | |
| 2212 | 1964 | $.post(ajaxurl, { |
| 2213 | 1965 | action: 'mxchat_seo_suggest', |
| 2214 | 1966 | nonce: mxchatContent.nonce, |
| 2215 | 1967 | post_id: postId, |
| 2216 | - field: fields[idx], | |
| 1968 | + field: field, | |
| 2217 | 1969 | }).always(function() { |
| 2218 | - idx++; | |
| 2219 | - runNext(); | |
| 1970 | + if (++done >= fields.length) { | |
| 1971 | + // Re-analyze and re-render in modal | |
| 1972 | + if (seodState.expandedId === postId) { | |
| 1973 | + openSeoModal(postId); | |
| 1974 | + } | |
| 1975 | + $btn.prop('disabled', false).html(origHtml); | |
| 1976 | + } | |
| 2220 | 1977 | }); |
| 2221 | - } | |
| 2222 | - runNext(); | |
| 1978 | + }); | |
| 2223 | 1979 | }).fail(function() { |
| 2224 | 1980 | $btn.prop('disabled', false).html(origHtml); |
| 2225 | 1981 | }); |
| 2226 | 1982 | } |
| @@ -2238,9 +1994,8 @@ | ||
| 2238 | 1994 | $(document).ready(function() { |
| 2239 | 1995 | initNavigation(); |
| 2240 | 1996 | initInlineForm(); |
| 2241 | 1997 | initGeneration(); |
| 2242 | - initPromptModal(); | |
| 2243 | 1998 | initPreview(); |
| 2244 | 1999 | initChat(); |
| 2245 | 2000 | initSettingsAutoSave(); |
| 2246 | 2001 | initLeftTabs(); |