PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.0.7
MxChat – AI Chatbot & Content Generation for WordPress v3.0.7
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
← All changes | js/mxchat-admin.js +64 -434 3.2.83.0.7 View file →
@@ -897,27 +897,14 @@
897 897 // Toggle visibility handlers
898 898 function toggleVisibility(selector) {
899 899 $(selector).on('click', function() {
900 900 var inputField = $(this).prev('input');
901 - // Check if this is a CSS-masked field (type="text" with mxchat-api-key-field class)
902 - if (inputField.hasClass('mxchat-api-key-field')) {
903 - // Use CSS class toggle for CSS-masked fields
904 - if (inputField.hasClass('mxchat-show-key')) {
905 - inputField.removeClass('mxchat-show-key');
906 - $(this).text('Show');
907 - } else {
908 - inputField.addClass('mxchat-show-key');
909 - $(this).text('Hide');
910 - }
901 + if (inputField.attr('type') === 'password') {
902 + inputField.attr('type', 'text');
903 + $(this).text('Hide');
911 904 } else {
912 - // Legacy type toggle for password fields
913 - if (inputField.attr('type') === 'password') {
914 - inputField.attr('type', 'text');
915 - $(this).text('Hide');
916 - } else {
917 - inputField.attr('type', 'password');
918 - $(this).text('Show');
919 - }
905 + inputField.attr('type', 'password');
906 + $(this).text('Show');
920 907 }
921 908 });
922 909 }
923 910
@@ -994,9 +981,8 @@
994 981 <button class="mxchat-model-category-btn" data-category="openai">OpenAI</button>
995 982 <button class="mxchat-model-category-btn" data-category="claude">Claude</button>
996 983 <button class="mxchat-model-category-btn" data-category="xai">X.AI</button>
997 984 <button class="mxchat-model-category-btn" data-category="deepseek">DeepSeek</button>
998 - <button class="mxchat-model-category-btn" data-category="custom">Custom / Local</button>
999 985 </div>
1000 986 <div class="mxchat-model-selector-grid" id="mxchat_models_grid"></div>
1001 987 </div>
1002 988 <div class="mxchat-model-selector-modal-footer">
@@ -1011,37 +997,54 @@
1011 997 // MOVE THIS OUTSIDE - Make it a property of the window object so it's accessible globally
1012 998 window.populateModelsGrid = function(filter = '', category = 'all') {
1013 999 const $grid = $('#mxchat_models_grid');
1014 1000 $grid.empty();
1015 -
1016 - // Catalog refactor (plan-d14e89): when class-mxchat-model-catalog.php
1017 - // is loaded (via wp_localize_script as mxchatChatModelCatalog), use
1018 - // its data so a single edit there flows to this picker grid. The
1019 - // inline fallback below keeps the picker working if for some reason
1020 - // the localize hasn't run (e.g. legacy admin page bootstrap order).
1021 - const models = (typeof mxchatChatModelCatalog === 'object' && mxchatChatModelCatalog) ? mxchatChatModelCatalog : {
1001 +
1002 + const models = {
1022 1003 openrouter: [
1023 1004 { value: 'openrouter', label: 'OpenRouter', description: 'Access 100+ models from multiple providers (add API key to browse)' }
1024 1005 ],
1025 1006 gemini: [
1026 - { value: 'gemini-3.5-flash', label: 'Gemini 3.5 Flash', description: 'Stable — newest Flash generation, recommended default' },
1007 + { value: 'gemini-3-pro-preview', label: 'Gemini 3 Pro', description: 'Most intelligent model - multimodal understanding & agentic' },
1008 + { value: 'gemini-3-flash-preview', label: 'Gemini 3 Flash', description: 'Most balanced - speed, scale & frontier intelligence' },
1009 + { value: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro', description: 'Advanced thinking - code, math, STEM & long context' },
1010 + { value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash', description: 'Best price-performance with thinking capabilities' },
1011 + { value: 'gemini-2.5-flash-lite', label: 'Gemini 2.5 Flash-Lite', description: 'Ultra fast - optimized for cost & high throughput' },
1012 + { value: 'gemini-2.0-flash', label: 'Gemini 2.0 Flash', description: 'Deprecated March 2026' },
1013 + { value: 'gemini-2.0-flash-lite', label: 'Gemini 2.0 Flash-Lite', description: 'Deprecated March 2026' },
1014 + { value: 'gemini-1.5-pro', label: 'Gemini 1.5 Pro', description: 'Deprecated September 2025' },
1015 + { value: 'gemini-1.5-flash', label: 'Gemini 1.5 Flash', description: 'Deprecated September 2025' },
1027 1016 ],
1028 1017 openai: [
1018 + { value: 'gpt-5.2', label: 'GPT-5.2', description: 'Best general-purpose & agentic model with fast responses' },
1029 1019 { value: 'gpt-5.1-chat-latest', label: 'GPT-5.1 Chat Latest', description: 'Recommended for most use cases' },
1020 + { value: 'gpt-5.1-2025-11-13', label: 'GPT-5.1', description: 'Flagship for coding & agentic tasks with low reasoning (400K context)' },
1021 + { value: 'gpt-5', label: 'GPT-5', description: 'Flagship for coding, reasoning, and agentic tasks across domains' },
1022 + { value: 'gpt-5-mini', label: 'GPT-5 Mini', description: 'Fast and lightweight' },
1023 + { value: 'gpt-5-nano', label: 'GPT-5 Nano', description: 'Fastest and cheapest; ideal for summarization and classification' },
1030 1024 ],
1031 1025 claude: [
1032 - { value: 'claude-opus-4-8', label: 'Claude Opus 4.8', description: 'Latest Flagship — newest Anthropic flagship model' },
1033 - { value: 'claude-opus-4-7', label: 'Claude Opus 4.7', description: 'Previous Anthropic flagship model' },
1026 + { value: 'claude-opus-4-6', label: 'Claude Opus 4.6', description: 'Most capable Claude model - recommended' },
1027 + { value: 'claude-opus-4-5', label: 'Claude Opus 4.5', description: 'Highly capable for complex tasks' },
1028 + { value: 'claude-sonnet-4-5-20250929', label: 'Claude Sonnet 4.5', description: 'Best for complex agents and coding' },
1029 + { value: 'claude-opus-4-1-20250805', label: 'Claude Opus 4.1', description: 'Exceptional for specialized complex tasks' },
1030 + { value: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5', description: 'Fastest and most intelligent Haiku' },
1031 + { value: 'claude-opus-4-20250514', label: 'Claude 4 Opus', description: 'Complex tasks' },
1032 + { value: 'claude-sonnet-4-20250514', label: 'Claude 4 Sonnet', description: 'High performance' },
1034 1033 ],
1035 1034 xai: [
1036 - { value: 'grok-4-0709', label: 'Grok 4', description: 'Latest flagship model' },
1035 + { value: 'grok-4-1-fast-reasoning', label: 'Grok 4.1 Fast (Reasoning)', description: '2M context window and reasoning' },
1036 + { value: 'grok-4-1-fast-non-reasoning', label: 'Grok 4.1 Fast (Non-Reasoning)', description: '2M context window and faster responses' },
1037 + { value: 'grok-4-0709', label: 'Grok 4', description: 'Latest flagship model - unparalleled performance in natural language, math and reasoning' },
1038 + { value: 'grok-3-beta', label: 'Grok-3', description: 'Powerful model with 131K context' },
1039 + { value: 'grok-3-fast-beta', label: 'Grok-3 Fast', description: 'High performance with faster responses' },
1040 + { value: 'grok-3-mini-beta', label: 'Grok-3 Mini', description: 'Affordable model with good performance' },
1041 + { value: 'grok-3-mini-fast-beta', label: 'Grok-3 Mini Fast', description: 'Quick and cost-effective' },
1042 + { value: 'grok-2', label: 'Grok 2', description: 'Latest X.AI model' },
1037 1043 ],
1038 1044 deepseek: [
1039 1045 { value: 'deepseek-chat', label: 'DeepSeek-V3', description: 'Advanced AI assistant' },
1040 1046 ],
1041 - custom: [
1042 - { value: 'custom-provider', label: 'Custom Provider', description: 'OpenAI-compatible local LLM — configure in API Keys tab' },
1043 - ],
1044 1047 };
1045 1048
1046 1049 let allModels = [];
1047 1050 Object.keys(models).forEach(key => {
@@ -1082,9 +1085,8 @@
1082 1085 if (modelValue.startsWith('gemini-')) return '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48" class="mxchat-model-icon-gemini"><defs><path id="a" d="M44.5 20H24v8.5h11.8C34.7 33.9 30.1 37 24 37c-7.2 0-13-5.8-13-13s5.8-13 13-13c3.1 0 5.9 1.1 8.1 2.9l6.4-6.4C34.6 4.1 29.6 2 24 2 11.8 2 2 11.8 2 24s9.8 22 22 22c11 0 21-8 21-22 0-1.3-.2-2.7-.5-4z"></path></defs><clipPath id="b"><use xlink:href="#a" overflow="visible"></use></clipPath><path clip-path="url(#b)" fill="#FBBC05" d="M0 37V11l17 13z"></path><path clip-path="url(#b)" fill="#EA4335" d="M0 11l17 13 7-6.1L48 14V0H0z"></path><path clip-path="url(#b)" fill="#34A853" d="M0 37l30-23 7.9 1L48 0v48H0z"></path><path clip-path="url(#b)" fill="#4285F4" d="M48 48L17 24l-4-3 35-10z"></path></svg>';
1083 1086 if (modelValue.startsWith('gpt-')) return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 320" class="mxchat-model-icon-openai"><path fill="currentColor" d="M297 131a80.6 80.6 0 0 0-93.7-104.2 80.6 80.6 0 0 0-137 29A80.6 80.6 0 0 0 23 189a80.6 80.6 0 0 0 93.7 104.2 80.6 80.6 0 0 0 137-29A80.7 80.7 0 0 0 297.1 131zM176.9 299c-14 .1-27.6-4.8-38.4-13.8l1.9-1 63.7-36.9c3.3-1.8 5.3-5.3 5.2-9v-89.9l27 15.6c.3.1.4.4.5.7v74.4a60 60 0 0 1-60 60zM47.9 244a59.7 59.7 0 0 1-7.1-40.1l1.9 1.1 63.7 36.8c3.2 1.9 7.2 1.9 10.5 0l77.8-45V228c0 .3-.2.6-.4.8L129.9 266a60 60 0 0 1-82-22zM31.2 105c7-12.2 18-21.5 31.2-26.3v75.8c0 3.7 2 7.2 5.2 9l77.8 45-27 15.5a1 1 0 0 1-.9 0L53.1 187a60 60 0 0 1-22-82zm221.2 51.5-77.8-45 27-15.5a1 1 0 0 1 .9 0l64.4 37.1a60 60 0 0 1-9.3 108.2v-75.8c0-3.7-2-7.2-5.2-9zm26.8-40.4-1.9-1.1-63.7-36.8a10.4 10.4 0 0 0-10.5 0L125.4 123V92c0-.3 0-.6.3-.8L190.1 54a60 60 0 0 1 89.1 62.1zm-168.5 55.4-27-15.5a1 1 0 0 1-.4-.7V80.9a60 60 0 0 1 98.3-46.1l-1.9 1L116 72.8a10.3 10.3 0 0 0-5.2 9v89.8zm14.6-31.5 34.7-20 34.6 20v40L160 200l-34.7-20z"></path></svg>';
1084 1087 if (modelValue.startsWith('claude-')) return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 176" fill="none" class="mxchat-model-icon-claude"><path fill="currentColor" d="m147.487 0l70.081 175.78H256L185.919 0zM66.183 106.221l23.98-61.774l23.98 61.774zM70.07 0L0 175.78h39.18l14.33-36.914h73.308l14.328 36.914h39.179L110.255 0z"></path></svg>';
1085 1088 if (modelValue.startsWith('grok-')) return '<svg fill="currentColor" fill-rule="evenodd" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="mxchat-model-icon-xai"><path d="M6.469 8.776L16.512 23h-4.464L2.005 8.776H6.47zm-.004 7.9l2.233 3.164L6.467 23H2l4.465-6.324zM22 2.582V23h-3.659V7.764L22 2.582zM22 1l-9.952 14.095-2.233-3.163L17.533 1H22z"></path></svg>';
1086 - if (modelValue === 'custom-provider' || modelValue.startsWith('custom-')) return '<span class="dashicons dashicons-admin-site-alt3" style="font-size: 24px; color: #2c8a3d;"></span>';
1087 1089 if (modelValue.startsWith('deepseek-')) return '<svg height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg" class="mxchat-model-icon-deepseek"><path d="M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z" fill="currentColor"></path></svg>';
1088 1090 return '<span class="dashicons dashicons-admin-generic mxchat-model-icon-generic"></span>';
1089 1091 }
1090 1092
@@ -1597,71 +1599,8 @@
1597 1599 });
1598 1600
1599 1601 }
1600 1602
1601 -// 3.2.3: Confirmation dialog shown when the user attempts to switch embedding
1602 -// models after they've already embedded content with a different model. Mixing
1603 -// embeddings from two models silently breaks similarity matching.
1604 -function showEmbeddingSwitchWarning(data, newValue, onChoice) {
1605 - $('#mxchat_embedding_switch_warning').remove();
1606 -
1607 - const dimsBlock = data.dims_differ ? `
1608 - <div class="mxchat-embed-warn-dims">
1609 - <strong>Dimension mismatch:</strong>
1610 - Existing vectors are ${data.active_dims}-dimensional, but ${data.new_label}
1611 - produces ${data.new_dims}-dimensional vectors.
1612 - If you use Pinecone, your index will reject queries entirely until you re-embed.
1613 - </div>` : '';
1614 -
1615 - const $modal = $(`
1616 - <div id="mxchat_embedding_switch_warning" class="mxchat-embed-warn-overlay">
1617 - <div class="mxchat-embed-warn-dialog">
1618 - <div class="mxchat-embed-warn-header">
1619 - <span class="mxchat-embed-warn-icon">⚠️</span>
1620 - <h3>Switching embedding models will break similarity matching</h3>
1621 - </div>
1622 - <div class="mxchat-embed-warn-body">
1623 - <p>
1624 - Your knowledge base and actions are currently embedded with
1625 - <code>${data.active_label}</code>. Switching to
1626 - <code>${data.new_label}</code> means new queries are embedded with
1627 - a different model than the stored vectors — the chatbot will return
1628 - inaccurate results or fail to match anything.
1629 - </p>
1630 - ${dimsBlock}
1631 - <p><strong>To switch safely:</strong></p>
1632 - <ol>
1633 - <li>Go to <em>Knowledge Base</em> and delete all existing entries.</li>
1634 - <li>Go to <em>Actions</em> and delete all existing actions.</li>
1635 - <li>Come back here, switch the model, then re-import your content and re-add your actions.</li>
1636 - </ol>
1637 - </div>
1638 - <div class="mxchat-embed-warn-footer">
1639 - <button type="button" class="button button-secondary" id="mxchat_embed_warn_cancel">Cancel — keep ${data.active_label}</button>
1640 - <button type="button" class="button button-primary mxchat-embed-warn-danger" id="mxchat_embed_warn_continue">Switch anyway (I'll handle it)</button>
1641 - </div>
1642 - </div>
1643 - </div>
1644 - `);
1645 -
1646 - $('body').append($modal);
1647 -
1648 - let resolved = false;
1649 - function resolve(confirmed) {
1650 - if (resolved) return;
1651 - resolved = true;
1652 - $modal.remove();
1653 - if (typeof onChoice === 'function') onChoice(confirmed);
1654 - }
1655 -
1656 - // Click on the overlay background dismisses (cancel)
1657 - $modal.on('click', function(e) {
1658 - if (e.target === $modal[0]) resolve(false);
1659 - });
1660 - $modal.find('#mxchat_embed_warn_cancel').on('click', function() { resolve(false); });
1661 - $modal.find('#mxchat_embed_warn_continue').on('click', function() { resolve(true); });
1662 -}
1663 -
1664 1603 // Embedding model selector - completely separate from chat model selector
1665 1604 function setupMxChatEmbeddingModelSelector() {
1666 1605 const $embeddingModelSelect = $('#embedding_model');
1667 1606
@@ -1842,53 +1781,25 @@
1842 1781 const activeCategory = $('.mxchat-embedding-model-selector-categories .mxchat-embedding-model-category-btn.active').data('category');
1843 1782 populateEmbeddingModelsGrid(searchTerm, activeCategory);
1844 1783 });
1845 1784
1846 - // 3.2.3: Commit a model change — same as the original click handler, just
1847 - // factored out so it can be invoked from both the safe path and the
1848 - // post-confirmation path.
1849 - function commitEmbeddingModelChange(modelValue) {
1785 + // Use a direct selector to avoid conflicts with other card elements
1786 + $(document).on('click.embeddingModelSelector', '.mxchat-embedding-model-selector-grid .mxchat-embedding-model-selector-card', function(e) {
1787 + e.stopPropagation(); // Prevent event bubbling
1788 + const modelValue = $(this).data('value');
1789 +
1790 + // Important: Only update this specific select element
1850 1791 $embeddingModelSelect.val(modelValue);
1792 +
1793 + // Manually trigger change only on this element
1851 1794 const changeEvent = new Event('change', { bubbles: true });
1852 1795 $embeddingModelSelect[0].dispatchEvent(changeEvent);
1796 +
1797 + // Update button text
1853 1798 updateButtonText();
1799 +
1800 + // Hide modal
1854 1801 $('#' + embeddingModalId).hide();
1855 - }
1856 -
1857 - // Use a direct selector to avoid conflicts with other card elements
1858 - $(document).on('click.embeddingModelSelector', '.mxchat-embedding-model-selector-grid .mxchat-embedding-model-selector-card', function(e) {
1859 - e.stopPropagation(); // Prevent event bubbling
1860 - const modelValue = $(this).data('value');
1861 - const previousValue = $embeddingModelSelect.val();
1862 -
1863 - // No-op if the user clicked the already-selected card
1864 - if (modelValue === previousValue) {
1865 - $('#' + embeddingModalId).hide();
1866 - return;
1867 - }
1868 -
1869 - // 3.2.3: Preflight — if the user has content embedded with a different
1870 - // model, surface a confirmation dialog before committing the switch.
1871 - $.post(ajaxurl, {
1872 - action: 'mxchat_check_embedding_switch',
1873 - security: (typeof mxchatAdmin !== 'undefined' ? mxchatAdmin.nonce : ''),
1874 - new_model: modelValue
1875 - }).done(function(resp) {
1876 - if (resp && resp.success && resp.data && resp.data.is_mismatch) {
1877 - showEmbeddingSwitchWarning(resp.data, modelValue, function(confirmed) {
1878 - if (confirmed) {
1879 - commitEmbeddingModelChange(modelValue);
1880 - }
1881 - });
1882 - } else {
1883 - commitEmbeddingModelChange(modelValue);
1884 - }
1885 - }).fail(function() {
1886 - // If preflight fails, fall back to the original behavior so a network
1887 - // hiccup doesn't block legitimate model switches.
1888 - commitEmbeddingModelChange(modelValue);
1889 - });
1890 - return;
1891 1802 });
1892 1803
1893 1804 // Close modal when clicking outside - use namespaced events
1894 1805 $(window).on('click.embeddingModelSelector', function(event) {
@@ -2186,14 +2097,24 @@
2186 2097 const actionLabel = this.dataset.label;
2187 2098 const actionIcon = this.querySelector('.dashicons').getAttribute('class').replace('dashicons dashicons-', '');
2188 2099 const actionDescription = this.querySelector('p').textContent;
2189 2100
2190 - // Check if this is a promotional card (add-on not installed)
2191 - const isPromo = this.dataset.promo === 'true';
2192 -
2193 - if (isPromo || (addonName && !isInstalled)) {
2194 - // Add-on required but not installed — show informational notice
2195 - const addonDisplayName = this.querySelector('.mxchat-addon-info')?.textContent?.replace('Requires ', '').replace('— Get Add-on', '').trim() || addonName + ' Add-on';
2101 + // Pro check using the proper detection method
2102 + const proIsActivated = typeof mxchatAdmin !== 'undefined' &&
2103 + (mxchatAdmin.is_activated === '1' ||
2104 + mxchatAdmin.is_activated === 'true' ||
2105 + mxchatAdmin.is_activated === true);
2106 +
2107 + // Handle different states
2108 + if (isPro && !proIsActivated) {
2109 + // Pro feature but no Pro license
2110 + showProFeatureNotice();
2111 + return;
2112 + }
2113 +
2114 + if (addonName && !isInstalled) {
2115 + // Add-on required but not installed
2116 + const addonDisplayName = this.querySelector('.mxchat-addon-info')?.textContent?.replace('Requires ', '') || addonName + ' Add-on';
2196 2117 showAddonRequiredNotice(addonDisplayName);
2197 2118 return;
2198 2119 }
2199 2120
@@ -3355,296 +3276,5 @@
3355 3276 var div = document.createElement('div');
3356 3277 div.appendChild(document.createTextNode(text));
3357 3278 return div.innerHTML;
3358 3279 }
3359 -
3360 - // ========================================
3361 - // DEBUG & OPTIMIZATION TOOLS
3362 - // ========================================
3363 -
3364 - // Debug Mode Toggle
3365 - $('#mxchat_debug_mode').on('change', function() {
3366 - var $toggle = $(this);
3367 - var enabled = $toggle.is(':checked') ? 'on' : 'off';
3368 - var $label = $toggle.closest('label');
3369 -
3370 - // Add loading indicator next to the toggle label
3371 - var $indicator = $label.find('.mxchat-save-indicator');
3372 - if ($indicator.length === 0) {
3373 - $indicator = $('<span class="mxchat-save-indicator" style="margin-left: 10px;"></span>');
3374 - $label.append($indicator);
3375 - }
3376 - $indicator.html('<span class="mxchat-saving-spinner"></span>').show();
3377 -
3378 - $.ajax({
3379 - url: ajaxurl,
3380 - type: 'POST',
3381 - data: {
3382 - action: 'mxchat_toggle_debug_mode',
3383 - enabled: enabled,
3384 - _ajax_nonce: mxchatAdmin.setting_nonce
3385 - },
3386 - success: function(response) {
3387 - if (response.success) {
3388 - // Show success indicator
3389 - $indicator.html('<span class="mxchat-save-success">✓</span>');
3390 - setTimeout(function() {
3391 - $indicator.fadeOut(300);
3392 - }, 2000);
3393 -
3394 - // Always refresh the log after toggling
3395 - refreshDebugLog();
3396 - } else {
3397 - $indicator.html('<span class="mxchat-save-error">✗</span>');
3398 - alert(response.data.message || 'Error toggling debug mode');
3399 - $toggle.prop('checked', !$toggle.is(':checked'));
3400 - }
3401 - },
3402 - error: function() {
3403 - $indicator.html('<span class="mxchat-save-error">✗</span>');
3404 - alert('Error toggling debug mode');
3405 - $toggle.prop('checked', !$toggle.is(':checked'));
3406 - }
3407 - });
3408 - });
3409 -
3410 - // Refresh Debug Log
3411 - function refreshDebugLog() {
3412 - var $container = $('#mxchat-debug-log');
3413 - var $countBadge = $('#mxchat-log-count');
3414 -
3415 - $.ajax({
3416 - url: ajaxurl,
3417 - type: 'POST',
3418 - data: {
3419 - action: 'mxchat_get_debug_log',
3420 - _ajax_nonce: mxchatAdmin.setting_nonce
3421 - },
3422 - success: function(response) {
3423 - if (response.success) {
3424 - var log = response.data.log;
3425 - var count = response.data.count;
3426 -
3427 - if (count > 0) {
3428 - $countBadge.text(count + ' entries').show();
3429 - var html = '<div class="mxchat-debug-log-entries">';
3430 - log.forEach(function(entry) {
3431 - var typeClass = 'mxchat-log-type-' + entry.type;
3432 - var typeLabel = entry.type.replace(/_/g, ' ').toUpperCase();
3433 - html += '<div class="mxchat-debug-log-entry ' + typeClass + '">';
3434 - html += '<div class="mxchat-log-header">';
3435 - html += '<span class="mxchat-log-type">' + escapeHtml(typeLabel) + '</span>';
3436 - html += '<span class="mxchat-log-time">' + escapeHtml(entry.time) + '</span>';
3437 - html += '</div>';
3438 - html += '<div class="mxchat-log-message">' + escapeHtml(entry.message) + '</div>';
3439 - if (entry.data) {
3440 - html += '<div class="mxchat-log-data">' + escapeHtml(JSON.stringify(entry.data, null, 2)) + '</div>';
3441 - }
3442 - html += '</div>';
3443 - });
3444 - html += '</div>';
3445 - $container.html(html);
3446 - } else {
3447 - $countBadge.hide();
3448 - $container.html(
3449 - '<div class="mxchat-debug-log-empty">' +
3450 - '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" style="opacity: 0.3;"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>' +
3451 - '<p>No log entries yet. Enable debug mode to start logging.</p>' +
3452 - '</div>'
3453 - );
3454 - }
3455 - }
3456 - }
3457 - });
3458 - }
3459 -
3460 - // Load debug log on page load
3461 - if ($('#mxchat-debug-log').length) {
3462 - refreshDebugLog();
3463 - }
3464 -
3465 - // Refresh Log Button
3466 - $('#mxchat-refresh-log').on('click', function() {
3467 - var $btn = $(this);
3468 - $btn.prop('disabled', true);
3469 - refreshDebugLog();
3470 - setTimeout(function() {
3471 - $btn.prop('disabled', false);
3472 - }, 500);
3473 - });
3474 -
3475 - // Clear Log Button
3476 - $('#mxchat-clear-log').on('click', function() {
3477 - if (!confirm('Are you sure you want to clear the debug log?')) {
3478 - return;
3479 - }
3480 -
3481 - var $btn = $(this);
3482 - $btn.prop('disabled', true);
3483 -
3484 - $.ajax({
3485 - url: ajaxurl,
3486 - type: 'POST',
3487 - data: {
3488 - action: 'mxchat_clear_debug_log',
3489 - _ajax_nonce: mxchatAdmin.setting_nonce
3490 - },
3491 - success: function(response) {
3492 - if (response.success) {
3493 - refreshDebugLog();
3494 - } else {
3495 - alert(response.data.message || 'Error clearing log');
3496 - }
3497 - },
3498 - error: function() {
3499 - alert('Error clearing log');
3500 - },
3501 - complete: function() {
3502 - $btn.prop('disabled', false);
3503 - }
3504 - });
3505 - });
3506 -
3507 - // Export Settings Button
3508 - $('#mxchat-export-settings').on('click', function() {
3509 - var $btn = $(this);
3510 - var originalHtml = $btn.html();
3511 - $btn.prop('disabled', true).html(
3512 - '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="animation: spin 1s linear infinite;"><circle cx="12" cy="12" r="10" stroke-dasharray="32" stroke-dashoffset="12"/></svg> Exporting...'
3513 - );
3514 -
3515 - $.ajax({
3516 - url: ajaxurl,
3517 - type: 'POST',
3518 - data: {
3519 - action: 'mxchat_export_settings',
3520 - _ajax_nonce: mxchatAdmin.setting_nonce
3521 - },
3522 - success: function(response) {
3523 - if (response.success) {
3524 - // Create and download the JSON file
3525 - var dataStr = JSON.stringify(response.data.settings, null, 2);
3526 - var blob = new Blob([dataStr], { type: 'application/json' });
3527 - var url = URL.createObjectURL(blob);
3528 - var a = document.createElement('a');
3529 - a.href = url;
3530 - a.download = response.data.filename;
3531 - document.body.appendChild(a);
3532 - a.click();
3533 - document.body.removeChild(a);
3534 - URL.revokeObjectURL(url);
3535 - } else {
3536 - alert(response.data.message || 'Error exporting settings');
3537 - }
3538 - },
3539 - error: function() {
3540 - alert('Error exporting settings');
3541 - },
3542 - complete: function() {
3543 - $btn.prop('disabled', false).html(originalHtml);
3544 - }
3545 - });
3546 - });
3547 -
3548 - // Reset Settings Modal
3549 - var $resetModal = $('#mxchat-reset-modal');
3550 - var $resetConfirmInput = $('#mxchat-reset-confirmation');
3551 - var $resetConfirmBtn = $('#mxchat-reset-confirm');
3552 -
3553 - $('#mxchat-reset-settings').on('click', function() {
3554 - $resetModal.fadeIn(200);
3555 - $resetConfirmInput.val('').focus();
3556 - $resetConfirmBtn.prop('disabled', true);
3557 - });
3558 -
3559 - $('#mxchat-reset-modal-close, #mxchat-reset-cancel, .mxch-modal-backdrop').on('click', function() {
3560 - $resetModal.fadeOut(200);
3561 - });
3562 -
3563 - $resetConfirmInput.on('input', function() {
3564 - var value = $(this).val().toUpperCase();
3565 - $resetConfirmBtn.prop('disabled', value !== 'RESET');
3566 - });
3567 -
3568 - $resetConfirmBtn.on('click', function() {
3569 - var $btn = $(this);
3570 - var confirmation = $resetConfirmInput.val();
3571 -
3572 - $btn.prop('disabled', true).text('Resetting...');
3573 -
3574 - $.ajax({
3575 - url: ajaxurl,
3576 - type: 'POST',
3577 - data: {
3578 - action: 'mxchat_reset_all_settings',
3579 - confirmation: confirmation,
3580 - _ajax_nonce: mxchatAdmin.setting_nonce
3581 - },
3582 - success: function(response) {
3583 - if (response.success) {
3584 - alert(response.data.message);
3585 - window.location.reload();
3586 - } else {
3587 - alert(response.data.message || 'Error resetting settings');
3588 - $btn.prop('disabled', false).text('Reset All Settings');
3589 - }
3590 - },
3591 - error: function() {
3592 - alert('Error resetting settings');
3593 - $btn.prop('disabled', false).text('Reset All Settings');
3594 - }
3595 - });
3596 - });
3597 -
3598 - // Load debug log on page load if we're on the optimization section
3599 - if ($('#optimization').hasClass('active') || window.location.hash === '#optimization') {
3600 - refreshDebugLog();
3601 - }
3602 -
3603 - // Also refresh when switching to optimization tab
3604 - $(document).on('click', '[data-section="optimization"]', function() {
3605 - setTimeout(refreshDebugLog, 100);
3606 - });
3607 -});
3608 -
3609 -// Global rate-limit usage: "Reset counter" button (plan-mxchat-20260603-e9b3f9)
3610 -jQuery(document).ready(function($) {
3611 - var $usage = $('#mxch-global-usage');
3612 - if (!$usage.length) {
3613 - return;
3614 - }
3615 -
3616 - $('#mxch-global-usage-reset').on('click', function() {
3617 - var $btn = $(this);
3618 - if (!window.confirm('Reset the global usage counter to zero now? This clears how many messages have been used in the current window.')) {
3619 - return;
3620 - }
3621 -
3622 - var original = $btn.text();
3623 - $btn.prop('disabled', true).text('Resetting…');
3624 -
3625 - $.ajax({
3626 - url: mxchatAdmin.ajax_url,
3627 - type: 'POST',
3628 - data: {
3629 - action: 'mxchat_reset_global_rate_limit',
3630 - bot_id: $usage.data('bot-id') || 'default',
3631 - _ajax_nonce: $usage.data('reset-nonce')
3632 - },
3633 - success: function(response) {
3634 - $btn.prop('disabled', false).text(original);
3635 - if (response && response.success) {
3636 - $('#mxch-global-usage-fill').css('width', (response.data.pct || 0) + '%');
3637 - if (response.data.text) {
3638 - $('#mxch-global-usage-text').text(response.data.text);
3639 - }
3640 - } else {
3641 - window.alert((response && response.data && response.data.message) || 'Reset failed. Please try again.');
3642 - }
3643 - },
3644 - error: function() {
3645 - $btn.prop('disabled', false).text(original);
3646 - window.alert('Reset failed. Please try again.');
3647 - }
3648 - });
3649 - });
3650 3280 });