PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.2
MxChat – AI Chatbot & Content Generation for WordPress v3.2.2
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 +54 -171 3.2.113.2.2 View file →
@@ -994,9 +994,8 @@
994 994 <button class="mxchat-model-category-btn" data-category="openai">OpenAI</button>
995 995 <button class="mxchat-model-category-btn" data-category="claude">Claude</button>
996 996 <button class="mxchat-model-category-btn" data-category="xai">X.AI</button>
997 997 <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 998 </div>
1000 999 <div class="mxchat-model-selector-grid" id="mxchat_models_grid"></div>
1001 1000 </div>
1002 1001 <div class="mxchat-model-selector-modal-footer">
@@ -1011,38 +1010,59 @@
1011 1010 // MOVE THIS OUTSIDE - Make it a property of the window object so it's accessible globally
1012 1011 window.populateModelsGrid = function(filter = '', category = 'all') {
1013 1012 const $grid = $('#mxchat_models_grid');
1014 1013 $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 : {
1014 +
1015 + const models = {
1022 1016 openrouter: [
1023 1017 { value: 'openrouter', label: 'OpenRouter', description: 'Access 100+ models from multiple providers (add API key to browse)' }
1024 1018 ],
1025 1019 gemini: [
1026 - { value: 'gemini-3.5-flash', label: 'Gemini 3.5 Flash', description: 'Stable — newest Flash generation, recommended default' },
1020 + { value: 'gemini-3-pro-preview', label: 'Gemini 3 Pro', description: 'Most intelligent model - multimodal understanding & agentic' },
1021 + { value: 'gemini-3-flash-preview', label: 'Gemini 3 Flash', description: 'Most balanced - speed, scale & frontier intelligence' },
1022 + { value: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro', description: 'Advanced thinking - code, math, STEM & long context' },
1023 + { value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash', description: 'Best price-performance with thinking capabilities' },
1024 + { value: 'gemini-2.5-flash-lite', label: 'Gemini 2.5 Flash-Lite', description: 'Ultra fast - optimized for cost & high throughput' },
1025 + { value: 'gemini-2.0-flash', label: 'Gemini 2.0 Flash', description: 'Deprecated March 2026' },
1026 + { value: 'gemini-2.0-flash-lite', label: 'Gemini 2.0 Flash-Lite', description: 'Deprecated March 2026' },
1027 + { value: 'gemini-1.5-pro', label: 'Gemini 1.5 Pro', description: 'Deprecated September 2025' },
1028 + { value: 'gemini-1.5-flash', label: 'Gemini 1.5 Flash', description: 'Deprecated September 2025' },
1027 1029 ],
1028 1030 openai: [
1031 + { value: 'gpt-5.4', label: 'GPT-5.4', description: 'Flagship reasoning and coding model' },
1032 + { value: 'gpt-5.4-mini', label: 'GPT-5.4 Mini', description: 'Fast and affordable with 400K context' },
1033 + { value: 'gpt-5.4-nano', label: 'GPT-5.4 Nano', description: 'Fastest and cheapest for lightweight tasks' },
1034 + { value: 'gpt-5.3-chat-latest', label: 'GPT-5.3 Chat', description: 'Optimized for natural conversations with reduced hallucinations' },
1035 + { value: 'gpt-5.2', label: 'GPT-5.2', description: 'Best general-purpose & agentic model with fast responses' },
1029 1036 { value: 'gpt-5.1-chat-latest', label: 'GPT-5.1 Chat Latest', description: 'Recommended for most use cases' },
1037 + { value: 'gpt-5.1-2025-11-13', label: 'GPT-5.1', description: 'Flagship for coding & agentic tasks with low reasoning (400K context)' },
1038 + { value: 'gpt-5', label: 'GPT-5', description: 'Flagship for coding, reasoning, and agentic tasks across domains' },
1039 + { value: 'gpt-5-mini', label: 'GPT-5 Mini', description: 'Fast and lightweight' },
1040 + { value: 'gpt-5-nano', label: 'GPT-5 Nano', description: 'Fastest and cheapest; ideal for summarization and classification' },
1030 1041 ],
1031 1042 claude: [
1032 - { value: 'claude-fable-5', label: 'Claude Fable 5', description: 'Latest Flagship — newest and most capable Anthropic model' },
1033 - { value: 'claude-opus-4-8', label: 'Claude Opus 4.8', description: 'Previous flagship — most capable Opus-tier model' },
1034 - { value: 'claude-opus-4-7', label: 'Claude Opus 4.7', description: 'Previous Anthropic flagship model' },
1043 + { value: 'claude-opus-4-6', label: 'Claude Opus 4.6', description: 'Most capable Claude model - recommended' },
1044 + { value: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6', description: 'Latest Sonnet - excellent balance of speed and capability' },
1045 + { value: 'claude-opus-4-5', label: 'Claude Opus 4.5', description: 'Highly capable for complex tasks' },
1046 + { value: 'claude-sonnet-4-5-20250929', label: 'Claude Sonnet 4.5', description: 'Best for complex agents and coding' },
1047 + { value: 'claude-opus-4-1-20250805', label: 'Claude Opus 4.1', description: 'Exceptional for specialized complex tasks' },
1048 + { value: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5', description: 'Fastest and most intelligent Haiku' },
1049 + { value: 'claude-opus-4-20250514', label: 'Claude 4 Opus', description: 'Complex tasks' },
1050 + { value: 'claude-sonnet-4-20250514', label: 'Claude 4 Sonnet', description: 'High performance' },
1035 1051 ],
1036 1052 xai: [
1037 - { value: 'grok-4-0709', label: 'Grok 4', description: 'Latest flagship model' },
1053 + { value: 'grok-4-1-fast-reasoning', label: 'Grok 4.1 Fast (Reasoning)', description: '2M context window and reasoning' },
1054 + { value: 'grok-4-1-fast-non-reasoning', label: 'Grok 4.1 Fast (Non-Reasoning)', description: '2M context window and faster responses' },
1055 + { value: 'grok-4-0709', label: 'Grok 4', description: 'Latest flagship model - unparalleled performance in natural language, math and reasoning' },
1056 + { value: 'grok-3-beta', label: 'Grok-3', description: 'Powerful model with 131K context' },
1057 + { value: 'grok-3-fast-beta', label: 'Grok-3 Fast', description: 'High performance with faster responses' },
1058 + { value: 'grok-3-mini-beta', label: 'Grok-3 Mini', description: 'Affordable model with good performance' },
1059 + { value: 'grok-3-mini-fast-beta', label: 'Grok-3 Mini Fast', description: 'Quick and cost-effective' },
1060 + { value: 'grok-2', label: 'Grok 2', description: 'Latest X.AI model' },
1038 1061 ],
1039 1062 deepseek: [
1040 1063 { value: 'deepseek-chat', label: 'DeepSeek-V3', description: 'Advanced AI assistant' },
1041 1064 ],
1042 - custom: [
1043 - { value: 'custom-provider', label: 'Custom Provider', description: 'OpenAI-compatible local LLM — configure in API Keys tab' },
1044 - ],
1045 1065 };
1046 1066
1047 1067 let allModels = [];
1048 1068 Object.keys(models).forEach(key => {
@@ -1083,9 +1103,8 @@
1083 1103 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>';
1084 1104 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>';
1085 1105 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>';
1086 1106 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>';
1087 - if (modelValue === 'custom-provider' || modelValue.startsWith('custom-')) return '<span class="dashicons dashicons-admin-site-alt3" style="font-size: 24px; color: #2c8a3d;"></span>';
1088 1107 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>';
1089 1108 return '<span class="dashicons dashicons-admin-generic mxchat-model-icon-generic"></span>';
1090 1109 }
1091 1110
@@ -1598,71 +1617,8 @@
1598 1617 });
1599 1618
1600 1619 }
1601 1620
1602 -// 3.2.3: Confirmation dialog shown when the user attempts to switch embedding
1603 -// models after they've already embedded content with a different model. Mixing
1604 -// embeddings from two models silently breaks similarity matching.
1605 -function showEmbeddingSwitchWarning(data, newValue, onChoice) {
1606 - $('#mxchat_embedding_switch_warning').remove();
1607 -
1608 - const dimsBlock = data.dims_differ ? `
1609 - <div class="mxchat-embed-warn-dims">
1610 - <strong>Dimension mismatch:</strong>
1611 - Existing vectors are ${data.active_dims}-dimensional, but ${data.new_label}
1612 - produces ${data.new_dims}-dimensional vectors.
1613 - If you use Pinecone, your index will reject queries entirely until you re-embed.
1614 - </div>` : '';
1615 -
1616 - const $modal = $(`
1617 - <div id="mxchat_embedding_switch_warning" class="mxchat-embed-warn-overlay">
1618 - <div class="mxchat-embed-warn-dialog">
1619 - <div class="mxchat-embed-warn-header">
1620 - <span class="mxchat-embed-warn-icon">⚠️</span>
1621 - <h3>Switching embedding models will break similarity matching</h3>
1622 - </div>
1623 - <div class="mxchat-embed-warn-body">
1624 - <p>
1625 - Your knowledge base and actions are currently embedded with
1626 - <code>${data.active_label}</code>. Switching to
1627 - <code>${data.new_label}</code> means new queries are embedded with
1628 - a different model than the stored vectors — the chatbot will return
1629 - inaccurate results or fail to match anything.
1630 - </p>
1631 - ${dimsBlock}
1632 - <p><strong>To switch safely:</strong></p>
1633 - <ol>
1634 - <li>Go to <em>Knowledge Base</em> and delete all existing entries.</li>
1635 - <li>Go to <em>Actions</em> and delete all existing actions.</li>
1636 - <li>Come back here, switch the model, then re-import your content and re-add your actions.</li>
1637 - </ol>
1638 - </div>
1639 - <div class="mxchat-embed-warn-footer">
1640 - <button type="button" class="button button-secondary" id="mxchat_embed_warn_cancel">Cancel — keep ${data.active_label}</button>
1641 - <button type="button" class="button button-primary mxchat-embed-warn-danger" id="mxchat_embed_warn_continue">Switch anyway (I'll handle it)</button>
1642 - </div>
1643 - </div>
1644 - </div>
1645 - `);
1646 -
1647 - $('body').append($modal);
1648 -
1649 - let resolved = false;
1650 - function resolve(confirmed) {
1651 - if (resolved) return;
1652 - resolved = true;
1653 - $modal.remove();
1654 - if (typeof onChoice === 'function') onChoice(confirmed);
1655 - }
1656 -
1657 - // Click on the overlay background dismisses (cancel)
1658 - $modal.on('click', function(e) {
1659 - if (e.target === $modal[0]) resolve(false);
1660 - });
1661 - $modal.find('#mxchat_embed_warn_cancel').on('click', function() { resolve(false); });
1662 - $modal.find('#mxchat_embed_warn_continue').on('click', function() { resolve(true); });
1663 -}
1664 -
1665 1621 // Embedding model selector - completely separate from chat model selector
1666 1622 function setupMxChatEmbeddingModelSelector() {
1667 1623 const $embeddingModelSelect = $('#embedding_model');
1668 1624
@@ -1843,53 +1799,25 @@
1843 1799 const activeCategory = $('.mxchat-embedding-model-selector-categories .mxchat-embedding-model-category-btn.active').data('category');
1844 1800 populateEmbeddingModelsGrid(searchTerm, activeCategory);
1845 1801 });
1846 1802
1847 - // 3.2.3: Commit a model change — same as the original click handler, just
1848 - // factored out so it can be invoked from both the safe path and the
1849 - // post-confirmation path.
1850 - function commitEmbeddingModelChange(modelValue) {
1803 + // Use a direct selector to avoid conflicts with other card elements
1804 + $(document).on('click.embeddingModelSelector', '.mxchat-embedding-model-selector-grid .mxchat-embedding-model-selector-card', function(e) {
1805 + e.stopPropagation(); // Prevent event bubbling
1806 + const modelValue = $(this).data('value');
1807 +
1808 + // Important: Only update this specific select element
1851 1809 $embeddingModelSelect.val(modelValue);
1810 +
1811 + // Manually trigger change only on this element
1852 1812 const changeEvent = new Event('change', { bubbles: true });
1853 1813 $embeddingModelSelect[0].dispatchEvent(changeEvent);
1814 +
1815 + // Update button text
1854 1816 updateButtonText();
1817 +
1818 + // Hide modal
1855 1819 $('#' + embeddingModalId).hide();
1856 - }
1857 -
1858 - // Use a direct selector to avoid conflicts with other card elements
1859 - $(document).on('click.embeddingModelSelector', '.mxchat-embedding-model-selector-grid .mxchat-embedding-model-selector-card', function(e) {
1860 - e.stopPropagation(); // Prevent event bubbling
1861 - const modelValue = $(this).data('value');
1862 - const previousValue = $embeddingModelSelect.val();
1863 -
1864 - // No-op if the user clicked the already-selected card
1865 - if (modelValue === previousValue) {
1866 - $('#' + embeddingModalId).hide();
1867 - return;
1868 - }
1869 -
1870 - // 3.2.3: Preflight — if the user has content embedded with a different
1871 - // model, surface a confirmation dialog before committing the switch.
1872 - $.post(ajaxurl, {
1873 - action: 'mxchat_check_embedding_switch',
1874 - security: (typeof mxchatAdmin !== 'undefined' ? mxchatAdmin.nonce : ''),
1875 - new_model: modelValue
1876 - }).done(function(resp) {
1877 - if (resp && resp.success && resp.data && resp.data.is_mismatch) {
1878 - showEmbeddingSwitchWarning(resp.data, modelValue, function(confirmed) {
1879 - if (confirmed) {
1880 - commitEmbeddingModelChange(modelValue);
1881 - }
1882 - });
1883 - } else {
1884 - commitEmbeddingModelChange(modelValue);
1885 - }
1886 - }).fail(function() {
1887 - // If preflight fails, fall back to the original behavior so a network
1888 - // hiccup doesn't block legitimate model switches.
1889 - commitEmbeddingModelChange(modelValue);
1890 - });
1891 - return;
1892 1820 });
1893 1821
1894 1822 // Close modal when clicking outside - use namespaced events
1895 1823 $(window).on('click.embeddingModelSelector', function(event) {
@@ -3051,20 +2979,18 @@
3051 2979 var $unavailableMessage = $('#web-search-unavailable-message');
3052 2980
3053 2981 if (!$wrapper.length) return; // Element doesn't exist
3054 2982
3055 - // Get the list of web-search-capable models from data attributes (OpenAI + Gemini)
2983 + // Get the list of supported OpenAI models from data attribute
3056 2984 var openaiModelsAttr = $wrapper.data('openai-models');
3057 - var geminiModelsAttr = $wrapper.data('gemini-models');
3058 2985 var unsupportedModelsAttr = $wrapper.data('unsupported-models');
3059 2986
3060 - var openaiModels = openaiModelsAttr ? String(openaiModelsAttr).split(',') : [];
3061 - var geminiModels = geminiModelsAttr ? String(geminiModelsAttr).split(',') : [];
3062 - var unsupportedModels = unsupportedModelsAttr ? String(unsupportedModelsAttr).split(',') : [];
2987 + var openaiModels = openaiModelsAttr ? openaiModelsAttr.split(',') : [];
2988 + var unsupportedModels = unsupportedModelsAttr ? unsupportedModelsAttr.split(',') : [];
3063 2989
3064 - // Check if selected model is an OpenAI or Gemini model that supports web search
3065 - var isCapable = openaiModels.includes(selectedModel) || geminiModels.includes(selectedModel);
3066 - var isSupported = isCapable && !unsupportedModels.includes(selectedModel);
2990 + // Check if selected model is an OpenAI model that supports web search
2991 + var isOpenAI = openaiModels.includes(selectedModel);
2992 + var isSupported = isOpenAI && !unsupportedModels.includes(selectedModel);
3067 2993
3068 2994 if (isSupported) {
3069 2995 $wrapper.show();
3070 2996 $unavailableMessage.hide();
@@ -3605,49 +3531,6 @@
3605 3531
3606 3532 // Also refresh when switching to optimization tab
3607 3533 $(document).on('click', '[data-section="optimization"]', function() {
3608 3534 setTimeout(refreshDebugLog, 100);
3609 - });
3610 -});
3611 -
3612 -// Global rate-limit usage: "Reset counter" button (plan-mxchat-20260603-e9b3f9)
3613 -jQuery(document).ready(function($) {
3614 - var $usage = $('#mxch-global-usage');
3615 - if (!$usage.length) {
3616 - return;
3617 - }
3618 -
3619 - $('#mxch-global-usage-reset').on('click', function() {
3620 - var $btn = $(this);
3621 - if (!window.confirm('Reset the global usage counter to zero now? This clears how many messages have been used in the current window.')) {
3622 - return;
3623 - }
3624 -
3625 - var original = $btn.text();
3626 - $btn.prop('disabled', true).text('Resetting…');
3627 -
3628 - $.ajax({
3629 - url: mxchatAdmin.ajax_url,
3630 - type: 'POST',
3631 - data: {
3632 - action: 'mxchat_reset_global_rate_limit',
3633 - bot_id: $usage.data('bot-id') || 'default',
3634 - _ajax_nonce: $usage.data('reset-nonce')
3635 - },
3636 - success: function(response) {
3637 - $btn.prop('disabled', false).text(original);
3638 - if (response && response.success) {
3639 - $('#mxch-global-usage-fill').css('width', (response.data.pct || 0) + '%');
3640 - if (response.data.text) {
3641 - $('#mxch-global-usage-text').text(response.data.text);
3642 - }
3643 - } else {
3644 - window.alert((response && response.data && response.data.message) || 'Reset failed. Please try again.');
3645 - }
3646 - },
3647 - error: function() {
3648 - $btn.prop('disabled', false).text(original);
3649 - window.alert('Reset failed. Please try again.');
3650 - }
3651 - });
3652 3535 });
3653 3536 });