PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.25
MxChat – AI Chatbot & Content Generation for WordPress v3.2.25
3.2.25 3.2.24 3.2.23 3.2.22 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 All 156 releases
mxchat-basic / includes / admin-settings-page.php

admin-settings-page.php in MxChat – AI Chatbot & Content Generation for WordPress 3.2.25, at includes/admin-settings-page.php

1,780 lines 132.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MxChat Admin Settings Page - Redesigned with Sidebar Navigation
4 *
5 * @package MxChat
6 * @since 2.7.0
7 */
8
9 if (!defined('ABSPATH')) {
10 exit;
11 }
12
13 /**
14 * Render the new sidebar-based admin settings page
15 */
16 function mxchat_render_settings_page($admin_instance) {
17 $is_activated = $admin_instance->is_activated();
18 $options = get_option('mxchat_options', array());
19 $plugin_url = plugin_dir_url(dirname(__FILE__));
20 ?>
21 <div class="mxch-admin-wrapper">
22 <!-- Mobile Header -->
23 <header class="mxch-mobile-header">
24 <a href="#" class="mxch-mobile-logo">
25 <div class="mxch-mobile-logo-icon">
26 <img src="<?php echo esc_url($plugin_url . 'images/icon-128x128.png'); ?>" alt="MxChat">
27 </div>
28 <span class="mxch-mobile-logo-text">MxChat</span>
29 </a>
30 <button type="button" class="mxch-mobile-menu-btn" aria-label="<?php esc_attr_e('Open menu', 'mxchat'); ?>">
31 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
32 </button>
33 </header>
34
35 <!-- Mobile Menu Overlay -->
36 <div class="mxch-mobile-overlay"></div>
37
38 <!-- Mobile Menu Modal -->
39 <div class="mxch-mobile-menu">
40 <div class="mxch-mobile-menu-header">
41 <span class="mxch-mobile-menu-title"><?php esc_html_e('Settings', 'mxchat'); ?></span>
42 <button type="button" class="mxch-mobile-menu-close" aria-label="<?php esc_attr_e('Close menu', 'mxchat'); ?>">
43 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" 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>
44 </button>
45 </div>
46 <nav class="mxch-mobile-menu-nav">
47 <!-- Configuration Section -->
48 <div class="mxch-mobile-nav-section">
49 <div class="mxch-mobile-nav-section-title"><?php esc_html_e('Configuration', 'mxchat'); ?></div>
50 <button class="mxch-mobile-nav-link" data-parent="chatbot">
51 <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"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg>
52 <span><?php esc_html_e('Chatbot', 'mxchat'); ?></span>
53 <svg class="mxch-mobile-nav-arrow" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
54 </button>
55 <div class="mxch-mobile-nav-sub" data-parent="chatbot">
56 <button class="mxch-mobile-nav-sub-link active" data-target="chatbot-ai-models"><?php esc_html_e('AI Models', 'mxchat'); ?></button>
57 <button class="mxch-mobile-nav-sub-link" data-target="chatbot-behavior"><?php esc_html_e('Behavior', 'mxchat'); ?></button>
58 <button class="mxch-mobile-nav-sub-link" data-target="chatbot-display"><?php esc_html_e('Display', 'mxchat'); ?></button>
59 <button class="mxch-mobile-nav-sub-link" data-target="chatbot-lead-capture"><?php esc_html_e('Lead Capture', 'mxchat'); ?></button>
60 <button class="mxch-mobile-nav-sub-link" data-target="chatbot-quick-questions"><?php esc_html_e('Quick Questions', 'mxchat'); ?></button>
61 <button class="mxch-mobile-nav-sub-link" data-target="chatbot-rate-limits"><?php esc_html_e('Rate Limits', 'mxchat'); ?></button>
62 </div>
63 <button class="mxch-mobile-nav-link" data-target="api-keys">
64 <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"><path d="m21 2-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0 3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
65 <span><?php esc_html_e('API Keys', 'mxchat'); ?></span>
66 </button>
67 <button class="mxch-mobile-nav-link" data-target="optimization">
68 <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"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
69 <span><?php esc_html_e('Optimization', 'mxchat'); ?></span>
70 </button>
71 <button class="mxch-mobile-nav-link" data-target="testing">
72 <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"><path d="M9 2v6"/><path d="M15 2v6"/><path d="M12 17v5"/><path d="M5 8h14"/><path d="M6 11v-1a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1"/><path d="m9 14 3 3 3-3"/></svg>
73 <span><?php esc_html_e('Testing', 'mxchat'); ?></span>
74 </button>
75 </div>
76 <!-- Integrations Section -->
77 <div class="mxch-mobile-nav-section">
78 <div class="mxch-mobile-nav-section-title"><?php esc_html_e('Integrations', 'mxchat'); ?></div>
79 <button class="mxch-mobile-nav-link" data-parent="integrations">
80 <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"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
81 <span><?php esc_html_e('Integrations', 'mxchat'); ?></span>
82 <svg class="mxch-mobile-nav-arrow" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
83 </button>
84 <div class="mxch-mobile-nav-sub" data-parent="integrations">
85 <button class="mxch-mobile-nav-sub-link" data-target="integrations-toolbar"><?php esc_html_e('Toolbar', 'mxchat'); ?></button>
86 <button class="mxch-mobile-nav-sub-link" data-target="integrations-loops"><?php esc_html_e('Loops', 'mxchat'); ?></button>
87 <button class="mxch-mobile-nav-sub-link" data-target="integrations-brave"><?php esc_html_e('Brave Search', 'mxchat'); ?></button>
88 <button class="mxch-mobile-nav-sub-link" data-target="integrations-slack"><?php esc_html_e('Slack', 'mxchat'); ?></button>
89 <button class="mxch-mobile-nav-sub-link" data-target="integrations-telegram"><?php esc_html_e('Telegram', 'mxchat'); ?></button>
90 <button class="mxch-mobile-nav-sub-link" data-target="integrations-webhook"><?php esc_html_e('Webhook', 'mxchat'); ?></button>
91 </div>
92 </div>
93 <!-- Resources Section -->
94 <div class="mxch-mobile-nav-section">
95 <div class="mxch-mobile-nav-section-title"><?php esc_html_e('Resources', 'mxchat'); ?></div>
96 <button class="mxch-mobile-nav-link" data-target="tutorials">
97 <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"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
98 <span><?php esc_html_e('Tutorials', 'mxchat'); ?></span>
99 </button>
100 </div>
101 <?php if (!$is_activated): ?>
102 <div class="mxch-mobile-menu-footer">
103 <a href="https://mxchat.ai/" target="_blank" class="mxch-mobile-upgrade-btn">
104 <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"><path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"/><path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"/><path d="M4 22h16"/><path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"/><path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"/><path d="M18 2H6v7a6 6 0 0 0 12 0V2Z"/></svg>
105 <?php esc_html_e('Upgrade to Pro', 'mxchat'); ?>
106 </a>
107 </div>
108 <?php endif; ?>
109 </nav>
110 </div>
111
112 <!-- Sidebar Navigation -->
113 <aside class="mxch-sidebar">
114 <div class="mxch-sidebar-header">
115 <a href="#" class="mxch-sidebar-logo">
116 <div class="mxch-sidebar-logo-icon">
117 <img src="<?php echo esc_url($plugin_url . 'images/icon-128x128.png'); ?>" alt="MxChat">
118 </div>
119 <span class="mxch-sidebar-logo-text">MxChat</span>
120 <span class="mxch-sidebar-version">v<?php echo esc_html(MXCHAT_VERSION ?? '2.7.0'); ?></span>
121 </a>
122 </div>
123
124 <nav class="mxch-sidebar-nav">
125 <!-- Chatbot Section -->
126 <div class="mxch-nav-section">
127 <div class="mxch-nav-section-title"><?php esc_html_e('Configuration', 'mxchat'); ?></div>
128
129 <div class="mxch-nav-item expanded" data-section="chatbot">
130 <button class="mxch-nav-link active">
131 <span class="mxch-nav-link-icon">
132 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg>
133 </span>
134 <span class="mxch-nav-link-text"><?php esc_html_e('Chatbot', 'mxchat'); ?></span>
135 <svg class="mxch-nav-link-arrow" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
136 </button>
137 <div class="mxch-nav-sub">
138 <button class="mxch-nav-sub-link active" data-target="chatbot-ai-models"><?php esc_html_e('AI Models', 'mxchat'); ?></button>
139 <button class="mxch-nav-sub-link" data-target="chatbot-behavior"><?php esc_html_e('Behavior', 'mxchat'); ?></button>
140 <button class="mxch-nav-sub-link" data-target="chatbot-display"><?php esc_html_e('Display', 'mxchat'); ?></button>
141 <button class="mxch-nav-sub-link" data-target="chatbot-lead-capture"><?php esc_html_e('Lead Capture', 'mxchat'); ?></button>
142 <button class="mxch-nav-sub-link" data-target="chatbot-quick-questions"><?php esc_html_e('Quick Questions', 'mxchat'); ?></button>
143 <button class="mxch-nav-sub-link" data-target="chatbot-rate-limits"><?php esc_html_e('Rate Limits', 'mxchat'); ?></button>
144 </div>
145 </div>
146
147 <div class="mxch-nav-item" data-section="api-keys">
148 <button class="mxch-nav-link" data-target="api-keys">
149 <span class="mxch-nav-link-icon">
150 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 2-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0 3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
151 </span>
152 <span class="mxch-nav-link-text"><?php esc_html_e('API Keys', 'mxchat'); ?></span>
153 </button>
154 </div>
155
156 <div class="mxch-nav-item" data-section="optimization">
157 <button class="mxch-nav-link" data-target="optimization">
158 <span class="mxch-nav-link-icon">
159 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
160 </span>
161 <span class="mxch-nav-link-text"><?php esc_html_e('Optimization', 'mxchat'); ?></span>
162 </button>
163 </div>
164
165 <div class="mxch-nav-item" data-section="testing">
166 <button class="mxch-nav-link" data-target="testing">
167 <span class="mxch-nav-link-icon">
168 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 2v6"/><path d="M15 2v6"/><path d="M12 17v5"/><path d="M5 8h14"/><path d="M6 11v-1a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1"/><path d="m9 14 3 3 3-3"/></svg>
169 </span>
170 <span class="mxch-nav-link-text"><?php esc_html_e('Testing', 'mxchat'); ?></span>
171 </button>
172 </div>
173 </div>
174
175 <!-- Integrations Section -->
176 <div class="mxch-nav-section">
177 <div class="mxch-nav-section-title"><?php esc_html_e('Integrations', 'mxchat'); ?></div>
178
179 <div class="mxch-nav-item" data-section="integrations">
180 <button class="mxch-nav-link">
181 <span class="mxch-nav-link-icon">
182 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
183 </span>
184 <span class="mxch-nav-link-text"><?php esc_html_e('Integrations', 'mxchat'); ?></span>
185 <svg class="mxch-nav-link-arrow" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
186 </button>
187 <div class="mxch-nav-sub">
188 <button class="mxch-nav-sub-link" data-target="integrations-toolbar"><?php esc_html_e('Toolbar', 'mxchat'); ?></button>
189 <button class="mxch-nav-sub-link" data-target="integrations-loops"><?php esc_html_e('Loops', 'mxchat'); ?></button>
190 <button class="mxch-nav-sub-link" data-target="integrations-brave"><?php esc_html_e('Brave Search', 'mxchat'); ?></button>
191 <button class="mxch-nav-sub-link" data-target="integrations-slack"><?php esc_html_e('Slack', 'mxchat'); ?></button>
192 <button class="mxch-nav-sub-link" data-target="integrations-telegram"><?php esc_html_e('Telegram', 'mxchat'); ?></button>
193 <button class="mxch-nav-sub-link" data-target="integrations-webhook"><?php esc_html_e('Webhook', 'mxchat'); ?></button>
194 </div>
195 </div>
196 </div>
197
198 <!-- Resources Section -->
199 <div class="mxch-nav-section">
200 <div class="mxch-nav-section-title"><?php esc_html_e('Resources', 'mxchat'); ?></div>
201
202 <div class="mxch-nav-item" data-section="tutorials">
203 <button class="mxch-nav-link" data-target="tutorials">
204 <span class="mxch-nav-link-icon">
205 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
206 </span>
207 <span class="mxch-nav-link-text"><?php esc_html_e('Tutorials', 'mxchat'); ?></span>
208 </button>
209 </div>
210 </div>
211
212 </nav>
213
214 <?php if (!$is_activated): ?>
215 <div class="mxch-sidebar-footer">
216 <a href="https://mxchat.ai/" target="_blank" class="mxch-sidebar-upgrade-v2">
217 <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"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
218 <?php esc_html_e('Pro Upgrade', 'mxchat'); ?>
219 </a>
220 </div>
221 <?php endif; ?>
222 </aside>
223
224 <!-- Main Content Area -->
225 <main class="mxch-content">
226 <?php if (!$is_activated): ?>
227 <div class="mxch-pro-banner">
228 <div class="mxch-pro-banner-content">
229 <h3 class="mxch-pro-banner-title"><?php esc_html_e('Stop paying $50–200/month for AI chatbot tools.', 'mxchat'); ?></h3>
230 <p class="mxch-pro-banner-text"><?php esc_html_e('One payment unlocks every MxChat Pro add-on for life — MCP server access for Claude / ChatGPT / Claude Code, WooCommerce sales, Google Search Console insights, AI-generated chatbot themes, lead-capture forms, unlimited bots, image analysis, and more. No renewals. No surprise bills.', 'mxchat'); ?></p>
231 </div>
232 <a href="https://mxchat.ai/" target="_blank" class="mxch-pro-banner-btn"><span><?php esc_html_e('Get Lifetime Access →', 'mxchat'); ?></span></a>
233 </div>
234 <?php endif; ?>
235
236 <?php
237 // Show theme migration notice for Pro users
238 $admin_instance->show_theme_migration_banner();
239 ?>
240
241 <!-- ========================================
242 CHATBOT - AI MODELS
243 ======================================== -->
244 <div id="chatbot-ai-models" class="mxch-section active">
245 <div class="mxch-content-header">
246 <h1 class="mxch-content-title"><?php esc_html_e('AI Models', 'mxchat'); ?></h1>
247 <p class="mxch-content-subtitle"><?php esc_html_e('Configure the AI models your chatbot will use for conversations and embeddings.', 'mxchat'); ?></p>
248 </div>
249
250 <?php
251 // Model-retirement warning (b65e8d). Rendered HERE rather than via
252 // admin_notices because the branded shell paints over WP's notice
253 // region — a core notice on this screen is invisible. No-ops unless
254 // the daily liveness check has flagged the configured model.
255 if (class_exists('MxChat_Model_Liveness')) {
256 MxChat_Model_Liveness::render_inline_notice();
257 }
258 ?>
259
260 <div class="mxch-card">
261 <div class="mxch-card-body mxchat-autosave-section">
262 <?php
263 // Chat Model
264 mxchat_render_field_wrapper('model', __('Chat Model', 'mxchat'), function() use ($admin_instance) {
265 $admin_instance->mxchat_model_callback();
266 }, __('Select the AI model your chatbot will use for conversations.', 'mxchat'));
267
268 // Embedding Model
269 mxchat_render_field_wrapper('embedding_model', __('Embedding Model', 'mxchat'), function() use ($admin_instance) {
270 $admin_instance->embedding_model_callback();
271 }, __('Vector embedding model for knowledge base matching. Changing models requires reconfiguring knowledge data.', 'mxchat'));
272
273 // Enable Streaming
274 mxchat_render_field_wrapper('enable_streaming', __('Enable Streaming', 'mxchat'), function() use ($admin_instance) {
275 $admin_instance->enable_streaming_toggle_callback();
276 }, __('Enable real-time streaming responses for supported models (OpenAI, Claude, DeepSeek, Grok).', 'mxchat'));
277
278 // Enable Web Search (OpenAI & Gemini)
279 mxchat_render_field_wrapper('enable_web_search', __('Enable Web Search', 'mxchat'), function() use ($admin_instance) {
280 $admin_instance->enable_web_search_toggle_callback();
281 }, __('Allow the chatbot to search the web for current information (OpenAI and Gemini models).', 'mxchat'));
282 ?>
283 </div>
284 </div>
285 </div>
286
287 <!-- ========================================
288 CHATBOT - BEHAVIOR
289 ======================================== -->
290 <div id="chatbot-behavior" class="mxch-section">
291 <div class="mxch-content-header">
292 <h1 class="mxch-content-title"><?php esc_html_e('Behavior', 'mxchat'); ?></h1>
293 <p class="mxch-content-subtitle"><?php esc_html_e('Control how your AI chatbot responds and processes queries.', 'mxchat'); ?></p>
294 </div>
295
296 <div class="mxch-card">
297 <div class="mxch-card-body mxchat-autosave-section">
298 <?php
299 // AI Instructions
300 mxchat_render_field_wrapper('system_prompt_instructions', __('AI Instructions (Behavior)', 'mxchat'), function() use ($admin_instance) {
301 $admin_instance->system_prompt_instructions_callback();
302 }, __('Provide system-level instructions to guide the AI\'s behavior and responses.', 'mxchat'));
303
304 // Similarity Threshold
305 mxchat_render_field_wrapper('similarity_threshold', __('Similarity Threshold', 'mxchat'), function() use ($admin_instance) {
306 $admin_instance->mxchat_similarity_threshold_callback();
307 }, __('Adjust threshold for content matching. Lower values = more matches, higher values = stricter matching.', 'mxchat'));
308
309 // RAG Sources Limit
310 mxchat_render_field_wrapper('rag_sources_limit', __('RAG Sources Limit', 'mxchat'), function() use ($admin_instance) {
311 $admin_instance->mxchat_rag_sources_limit_callback();
312 }, __('Number of knowledge base sources (pages/documents) to include in AI context. Higher values provide more context but use more tokens.', 'mxchat'));
313
314 // RAG Chunks Limit
315 mxchat_render_field_wrapper('rag_chunks_limit', __('RAG Chunks Limit', 'mxchat'), function() use ($admin_instance) {
316 $admin_instance->mxchat_rag_chunks_limit_callback();
317 }, __('Maximum total content chunks sent to the AI across all sources. Higher values provide more context but increase token usage and cost.', 'mxchat'));
318
319 // Hybrid keyword boost MOVED to Knowledge → Chunking &
320 // Retrieval (plan 11720c) — it is KB retrieval tuning, not
321 // conversation behavior. Same option key over there. The
322 // breadcrumb below is a one-release courtesy: drop it in
323 // the release after 3.2.17.
324 echo '<p class="mxch-field-description">';
325 printf(
326 /* translators: %s: link to the Chunking & Retrieval settings tab. */
327 esc_html__('Hybrid keyword boost has moved to %s.', 'mxchat'),
328 '<a href="' . esc_url(admin_url('admin.php?page=mxchat-prompts#chunking')) . '">' . esc_html__('Knowledge, Chunking & Retrieval', 'mxchat') . '</a>'
329 );
330 echo '</p>';
331
332 // Contextual Awareness
333 mxchat_render_field_wrapper('contextual_awareness', __('Contextual Awareness', 'mxchat'), function() use ($admin_instance) {
334 $admin_instance->mxchat_contextual_awareness_callback();
335 }, __('Allow the chatbot to understand and reference the current page content for more relevant responses.', 'mxchat'));
336
337 // Citation Links
338 mxchat_render_field_wrapper('citation_links', __('Citation Links', 'mxchat'), function() use ($admin_instance) {
339 $admin_instance->mxchat_citation_links_toggle_callback();
340 }, __('Allow the AI to include citation links from your knowledge database in responses. If disabled, ensure your AI Behavior settings do not mention links or the AI may fabricate URLs.', 'mxchat'));
341
342 // Strip Unapproved Links (plan 58f8b4) — enforcement is now
343 // its own control instead of riding Citation Links.
344 mxchat_render_field_wrapper('strip_unapproved_links', __('Strip Unapproved Links', 'mxchat'), function() use ($admin_instance) {
345 $admin_instance->mxchat_strip_unapproved_links_toggle_callback();
346 }, __('Remove links the AI invents from its answers, even when Citation Links is off. Approved citations, real pages on this site, and links returned by your integrations (e.g. WooCommerce products) are always kept. On by default for new installs; existing sites keep their current behavior until enabled.', 'mxchat'));
347
348 // Satisfaction Rating Prompt
349 // The toggle callback inline-renders the 5 customization
350 // fields inside a single sub-options wrapper (plan-29caac).
351 // No separate customization group needed — the wrapper handles
352 // its own server-side display state based on the toggle value.
353 mxchat_render_field_wrapper('satisfaction_rating', __('Satisfaction Rating Prompt', 'mxchat'), function() use ($admin_instance) {
354 $admin_instance->mxchat_satisfaction_rating_toggle_callback();
355 }, __('Show a 👍 / 👎 prompt after a conversation has had a couple of bot replies and the visitor has been idle for a moment. Disable to hide the prompt site-wide.', 'mxchat'));
356
357 // Editor Assistant toggle moved to Content → Settings (plan-f7df40):
358 // it's a content feature, so it lives on the screen that owns it.
359 // Rendering is in admin-content-page.php; option + save path unchanged.
360 ?>
361 </div>
362 </div>
363 </div>
364
365 <!-- ========================================
366 CHATBOT - DISPLAY
367 ======================================== -->
368 <div id="chatbot-display" class="mxch-section">
369 <div class="mxch-content-header">
370 <h1 class="mxch-content-title"><?php esc_html_e('Display Settings', 'mxchat'); ?></h1>
371 <p class="mxch-content-subtitle"><?php esc_html_e('Customize how and where your chatbot appears on your website.', 'mxchat'); ?></p>
372 </div>
373
374 <div class="mxch-card">
375 <div class="mxch-card-header">
376 <h3 class="mxch-card-title">
377 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="21" x2="9" y2="9"/></svg>
378 <?php esc_html_e('Visibility', 'mxchat'); ?>
379 </h3>
380 </div>
381 <div class="mxch-card-body mxchat-autosave-section">
382 <?php
383 // Auto-Display
384 mxchat_render_field_wrapper('auto_display', __('Auto-Display Chatbot', 'mxchat'), function() use ($admin_instance) {
385 $admin_instance->mxchat_append_to_body_callback();
386 }, __('Show chatbot automatically on all pages. Disable to use shortcode [mxchat_chatbot floating="yes"] for floating widget or [mxchat_chatbot floating="no"] for embedded chat.', 'mxchat'));
387
388 // Open Links in New Tab
389 mxchat_render_field_wrapper('link_target', __('Open Links in New Tab', 'mxchat'), function() use ($admin_instance) {
390 $admin_instance->mxchat_link_target_toggle_callback();
391 }, __('Open links clicked in chat responses in a new browser tab.', 'mxchat'));
392
393 // Chat Persistence
394 mxchat_render_field_wrapper('chat_persistence', __('Chat Persistence', 'mxchat'), function() use ($admin_instance) {
395 $admin_instance->mxchat_chat_persistence_toggle_callback();
396 }, __('Keep chat history when users navigate or return within 24 hours.', 'mxchat'));
397
398 // Download Transcript button
399 mxchat_render_field_wrapper('print_button', __('Show Download Transcript Button', 'mxchat'), function() use ($admin_instance) {
400 $admin_instance->mxchat_print_button_toggle_callback();
401 }, __('Show the "Download Transcript" item in the chat window menu.', 'mxchat'));
402
403 // Start-New-Chat button (plan ac2e81) — lets a visitor reset the
404 // conversation without the owner disabling chat persistence globally.
405 mxchat_render_field_wrapper('reset_chat_enabled', __('Show Start-New-Chat Button', 'mxchat'), function() use ($admin_instance) {
406 $admin_instance->mxchat_reset_chat_toggle_callback();
407 }, __('Show a "Start new chat" item in the chat window menu that clears the current conversation and begins a fresh session.', 'mxchat'));
408
409 mxchat_render_field_wrapper('reset_chat_label', __('Start-New-Chat Button Label', 'mxchat'), function() use ($admin_instance) {
410 $admin_instance->mxchat_reset_chat_label_callback();
411 }, __('Label for the Start-New-Chat menu item. Leave blank to use "Start new chat".', 'mxchat'));
412 ?>
413 </div>
414 </div>
415
416 <div class="mxch-card">
417 <div class="mxch-card-header">
418 <h3 class="mxch-card-title">
419 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
420 <?php esc_html_e('Text & Labels', 'mxchat'); ?>
421 </h3>
422 </div>
423 <div class="mxch-card-body mxchat-autosave-section">
424 <?php
425 // Top Bar Title
426 mxchat_render_field_wrapper('top_bar_title', __('Top Bar Title', 'mxchat'), function() use ($admin_instance) {
427 $admin_instance->mxchat_top_bar_title_callback();
428 }, __('Title text shown in the chatbot header.', 'mxchat'));
429
430 // AI Agent Text
431 mxchat_render_field_wrapper('ai_agent_text', __('AI Agent Text', 'mxchat'), function() use ($admin_instance) {
432 $admin_instance->mxchat_ai_agent_text_callback();
433 }, __('Text shown for AI agents in the status indicator.', 'mxchat'));
434
435 // Intro Message
436 mxchat_render_field_wrapper('intro_message', __('Introductory Message', 'mxchat'), function() use ($admin_instance) {
437 $admin_instance->mxchat_intro_message_callback();
438 }, __('First message shown when chat opens. HTML allowed.', 'mxchat'));
439
440 // Input Placeholder
441 mxchat_render_field_wrapper('input_copy', __('Input Placeholder', 'mxchat'), function() use ($admin_instance) {
442 $admin_instance->mxchat_input_copy_callback();
443 }, __('Placeholder text in the chat input field.', 'mxchat'));
444
445 // Max Input Length (characters) — plan a3fae2 part C
446 mxchat_render_field_wrapper('max_input_length', __('Max Input Length (characters)', 'mxchat'), function() use ($admin_instance) {
447 $admin_instance->mxchat_max_input_length_callback();
448 }, __('Cap how many characters a visitor can type or paste into a single message. Set to 0 for unlimited (default).', 'mxchat'));
449
450 // Chat Teaser
451 mxchat_render_field_wrapper('pre_chat_message', __('Chat Teaser Pop-up', 'mxchat'), function() use ($admin_instance) {
452 $admin_instance->mxchat_pre_chat_message_callback();
453 }, __('Message shown before users start chatting.', 'mxchat'));
454 ?>
455 </div>
456 </div>
457 </div>
458
459 <!-- ========================================
460 CHATBOT - LEAD CAPTURE
461 ======================================== -->
462 <div id="chatbot-lead-capture" class="mxch-section">
463 <div class="mxch-content-header">
464 <h1 class="mxch-content-title"><?php esc_html_e('Lead Capture', 'mxchat'); ?></h1>
465 <p class="mxch-content-subtitle"><?php esc_html_e('Collect user information and manage privacy settings.', 'mxchat'); ?></p>
466 </div>
467
468 <div class="mxch-card">
469 <div class="mxch-card-header">
470 <h3 class="mxch-card-title">
471 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>
472 <?php esc_html_e('Email Capture', 'mxchat'); ?>
473 </h3>
474 </div>
475 <div class="mxch-card-body mxchat-autosave-section">
476 <?php
477 // Require Email
478 mxchat_render_field_wrapper('enable_email_block', __('Require Email to Chat', 'mxchat'), function() use ($admin_instance) {
479 $admin_instance->enable_email_block_callback();
480 }, __('Require users to enter their email before chatting. Shows for non-logged-in users.', 'mxchat'));
481
482 // Email Form Content
483 mxchat_render_field_wrapper('email_blocker_header', __('Email Form Content', 'mxchat'), function() use ($admin_instance) {
484 $admin_instance->email_blocker_header_content_callback();
485 }, __('HTML content shown above the email form.', 'mxchat'));
486
487 // Email Button Text
488 mxchat_render_field_wrapper('email_blocker_button', __('Submit Button Text', 'mxchat'), function() use ($admin_instance) {
489 $admin_instance->email_blocker_button_text_callback();
490 }, __('Text for the email form submit button.', 'mxchat'));
491
492 // Require Name
493 mxchat_render_field_wrapper('enable_name_field', __('Also Require Name', 'mxchat'), function() use ($admin_instance) {
494 $admin_instance->enable_name_field_callback();
495 }, __('Also require users to enter their name with email.', 'mxchat'));
496
497 // Name Placeholder
498 mxchat_render_field_wrapper('name_placeholder', __('Name Field Placeholder', 'mxchat'), function() use ($admin_instance) {
499 $admin_instance->name_field_placeholder_callback();
500 }, __('Placeholder text for the name input field.', 'mxchat'));
501
502 // Consent Checkbox (b062c4)
503 mxchat_render_field_wrapper('enable_consent_checkbox', __('Consent Checkbox', 'mxchat'), function() use ($admin_instance) {
504 $admin_instance->enable_consent_checkbox_callback();
505 }, __('Show a consent checkbox on the email form. The decision, time, and exact label shown are stored with the lead.', 'mxchat'));
506
507 // Consent Label
508 mxchat_render_field_wrapper('consent_checkbox_label', __('Consent Label', 'mxchat'), function() use ($admin_instance) {
509 $admin_instance->consent_checkbox_label_callback();
510 }, __('Text shown beside the checkbox. Anchor tags are allowed so you can link your Privacy Policy; other HTML is stripped.', 'mxchat'));
511
512 // Consent Required
513 mxchat_render_field_wrapper('consent_checkbox_required', __('Require Consent to Submit', 'mxchat'), function() use ($admin_instance) {
514 $admin_instance->consent_checkbox_required_callback();
515 }, __('Block form submission until the box is ticked. Off means the checkbox is optional (a soft opt-in).', 'mxchat'));
516
517 // Also show for logged-in users (c0cfaf)
518 mxchat_render_field_wrapper('lead_capture_logged_in_toggle', __('Also Show for Logged-In Users', 'mxchat'), function() use ($admin_instance) {
519 $admin_instance->lead_capture_logged_in_toggle_callback();
520 }, __('Show the form to signed-in visitors too, pre-filled from their profile. Off means signed-in visitors skip it, as they do today.', 'mxchat'));
521
522 // Require email (c0cfaf)
523 mxchat_render_field_wrapper('lead_capture_require_email_toggle', __('Require Email Address', 'mxchat'), function() use ($admin_instance) {
524 $admin_instance->lead_capture_require_email_toggle_callback();
525 }, __('Off means the email field is optional, so you can collect a name (and consent) without an address.', 'mxchat'));
526 ?>
527 </div>
528 </div>
529
530 <div class="mxch-card">
531 <div class="mxch-card-header">
532 <h3 class="mxch-card-title">
533 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
534 <?php esc_html_e('Privacy & Compliance', 'mxchat'); ?>
535 </h3>
536 </div>
537 <div class="mxch-card-body mxchat-autosave-section">
538 <?php
539 // Privacy Notice
540 mxchat_render_field_wrapper('privacy_toggle', __('Privacy Notice', 'mxchat'), function() use ($admin_instance) {
541 $admin_instance->mxchat_privacy_toggle_callback();
542 }, __('Show privacy notice below the chat widget.', 'mxchat'));
543
544 // Complianz
545 mxchat_render_field_wrapper('complianz_toggle', __('Complianz Integration', 'mxchat'), function() use ($admin_instance) {
546 $admin_instance->mxchat_complianz_toggle_callback();
547 }, __('Apply Complianz consent logic to the chatbot (requires Complianz plugin).', 'mxchat'));
548 ?>
549 </div>
550 </div>
551 </div>
552
553 <!-- ========================================
554 CHATBOT - QUICK QUESTIONS
555 ======================================== -->
556 <div id="chatbot-quick-questions" class="mxch-section">
557 <div class="mxch-content-header">
558 <h1 class="mxch-content-title"><?php esc_html_e('Quick Questions', 'mxchat'); ?></h1>
559 <p class="mxch-content-subtitle"><?php esc_html_e('Pre-defined questions displayed above the chat input to help users get started.', 'mxchat'); ?></p>
560 </div>
561
562 <div class="mxch-card">
563 <div class="mxch-card-body mxchat-autosave-section">
564 <?php
565 // Quick Question 1
566 mxchat_render_field_wrapper('popular_question_1', __('Quick Question 1', 'mxchat'), function() use ($admin_instance) {
567 $admin_instance->mxchat_popular_question_1_callback();
568 });
569
570 // Quick Question 2
571 mxchat_render_field_wrapper('popular_question_2', __('Quick Question 2', 'mxchat'), function() use ($admin_instance) {
572 $admin_instance->mxchat_popular_question_2_callback();
573 });
574
575 // Quick Question 3
576 mxchat_render_field_wrapper('popular_question_3', __('Quick Question 3', 'mxchat'), function() use ($admin_instance) {
577 $admin_instance->mxchat_popular_question_3_callback();
578 });
579
580 // Additional Questions
581 mxchat_render_field_wrapper('additional_questions', __('Additional Questions', 'mxchat'), function() use ($admin_instance) {
582 $admin_instance->mxchat_additional_popular_questions_callback();
583 }, __('Add as many quick questions as you need.', 'mxchat'));
584 ?>
585 </div>
586 </div>
587 </div>
588
589 <!-- ========================================
590 CHATBOT - RATE LIMITS
591 ======================================== -->
592 <div id="chatbot-rate-limits" class="mxch-section">
593 <div class="mxch-content-header">
594 <h1 class="mxch-content-title"><?php esc_html_e('Rate Limits', 'mxchat'); ?></h1>
595 <p class="mxch-content-subtitle"><?php esc_html_e('Set message limits for each user role to manage API usage.', 'mxchat'); ?></p>
596 </div>
597
598 <div class="mxch-notice mxch-notice-info" style="margin-bottom: 24px;">
599 <svg class="mxch-notice-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
600 <div>
601 <strong><?php esc_html_e('Placeholders available:', 'mxchat'); ?></strong>
602 <code>{limit}</code>, <code>{timeframe}</code>, <code>{count}</code>, <code>{remaining}</code>
603 <br>
604 <strong><?php esc_html_e('Markdown links supported:', 'mxchat'); ?></strong>
605 <code>[Link text](https://example.com)</code>
606 </div>
607 </div>
608
609 <div class="mxch-card">
610 <div class="mxch-card-body mxchat-autosave-section">
611 <?php
612 // Render rate limits with accordion
613 mxchat_render_rate_limits_accordion($admin_instance);
614 ?>
615 </div>
616 </div>
617 </div>
618
619 <!-- ========================================
620 API KEYS
621 ======================================== -->
622 <div id="api-keys" class="mxch-section">
623 <div class="mxch-content-header">
624 <h1 class="mxch-content-title"><?php esc_html_e('API Keys', 'mxchat'); ?></h1>
625 <p class="mxch-content-subtitle"><?php esc_html_e('Manage API keys for AI providers and integrations.', 'mxchat'); ?></p>
626 </div>
627
628 <div class="mxch-card">
629 <div class="mxch-card-header">
630 <h3 class="mxch-card-title">
631 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg>
632 <?php esc_html_e('AI Providers', 'mxchat'); ?>
633 </h3>
634 </div>
635 <div class="mxch-card-body mxchat-autosave-section">
636 <table class="form-table">
637 <?php do_settings_fields('mxchat-api-keys', 'mxchat_api_keys_section'); ?>
638 </table>
639 </div>
640 </div>
641 </div>
642
643 <!-- ========================================
644 OPTIMIZATION
645 ======================================== -->
646 <div id="optimization" class="mxch-section">
647 <div class="mxch-content-header">
648 <h1 class="mxch-content-title"><?php esc_html_e('Optimization & Diagnostics', 'mxchat'); ?></h1>
649 <p class="mxch-content-subtitle"><?php esc_html_e('Optimize performance, debug issues, and manage plugin settings.', 'mxchat'); ?></p>
650 </div>
651
652 <!-- Script Loading Card -->
653 <div class="mxch-card">
654 <div class="mxch-card-header">
655 <h3 class="mxch-card-title">
656 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
657 <?php esc_html_e('Script Loading', 'mxchat'); ?>
658 </h3>
659 </div>
660 <div class="mxch-card-body mxchat-autosave-section">
661 <?php
662 $script_loading_strategy = isset($options['script_loading_strategy']) ? $options['script_loading_strategy'] : 'default';
663 ?>
664 <div class="mxch-field">
665 <label class="mxch-field-label"><?php esc_html_e('Script Loading Strategy', 'mxchat'); ?></label>
666 <div class="mxch-field-control">
667 <select name="mxchat_options[script_loading_strategy]" id="script_loading_strategy" class="mxch-select mxchat-autosave-field">
668 <option value="default" <?php selected($script_loading_strategy, 'default'); ?>><?php esc_html_e('Default (Immediate)', 'mxchat'); ?></option>
669 <option value="defer" <?php selected($script_loading_strategy, 'defer'); ?>><?php esc_html_e('Deferred', 'mxchat'); ?></option>
670 <option value="delay_1s" <?php selected($script_loading_strategy, 'delay_1s'); ?>><?php esc_html_e('Delay 1 Second', 'mxchat'); ?></option>
671 <option value="delay_3s" <?php selected($script_loading_strategy, 'delay_3s'); ?>><?php esc_html_e('Delay 3 Seconds', 'mxchat'); ?></option>
672 <option value="delay_5s" <?php selected($script_loading_strategy, 'delay_5s'); ?>><?php esc_html_e('Delay 5 Seconds', 'mxchat'); ?></option>
673 <option value="on_interaction" <?php selected($script_loading_strategy, 'on_interaction'); ?>><?php esc_html_e('On User Interaction', 'mxchat'); ?></option>
674 </select>
675 </div>
676 <p class="mxch-field-description"><?php esc_html_e('Control when the chatbot script loads to improve page performance.', 'mxchat'); ?></p>
677 </div>
678
679 <?php
680 // Smart asset loading (plan-915355; rebuilt on the house toggle
681 // pattern in plan-64d34f). Standalone option — never part of
682 // mxchat_options, so mxchat_sanitize() can't strip it.
683 mxchat_render_field_wrapper('mxchat_smart_asset_loading', __('Only load chatbot files on pages where the chatbot appears', 'mxchat'), function() use ($admin_instance) {
684 $admin_instance->mxchat_smart_asset_loading_toggle_callback();
685 }, __('Skips the chatbot\'s CSS and JavaScript on pages where the widget is hidden (per-page visibility, post type include/exclude, or auto-display disabled). Pages using the chatbot shortcode still load everything automatically. Helps with "reduce unused CSS/JS" audit findings.', 'mxchat'),
686 __('If your theme or page builder inserts the chatbot in an unusual way and it stops appearing, turn this off — or force-load assets with the mxchat_should_load_assets filter.', 'mxchat'));
687 ?>
688
689 <div class="mxch-notice mxch-notice-info" style="margin-top: 16px;">
690 <svg class="mxch-notice-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
691 <div>
692 <strong><?php esc_html_e('Loading Strategies:', 'mxchat'); ?></strong>
693 <ul style="margin: 8px 0 0 0; padding-left: 20px;">
694 <li><strong><?php esc_html_e('Default:', 'mxchat'); ?></strong> <?php esc_html_e('Script loads immediately in the footer. Best for sites where chat is critical.', 'mxchat'); ?></li>
695 <li><strong><?php esc_html_e('Deferred:', 'mxchat'); ?></strong> <?php esc_html_e('Script loads after HTML parsing completes. Good balance of performance and availability.', 'mxchat'); ?></li>
696 <li><strong><?php esc_html_e('Delay:', 'mxchat'); ?></strong> <?php esc_html_e('Script loads after a set delay. Helps improve LCP scores.', 'mxchat'); ?></li>
697 <li><strong><?php esc_html_e('On User Interaction:', 'mxchat'); ?></strong> <?php esc_html_e('Script loads when user scrolls, moves mouse, or touches screen. Best for Core Web Vitals but chat appears with slight delay.', 'mxchat'); ?></li>
698 </ul>
699 </div>
700 </div>
701
702 <?php
703 // Plan b76ed6 (Support Ticket 468): the floating launcher is rendered hidden
704 // and revealed by chat-script.js, so a cache plugin's "Delay JavaScript"
705 // that holds the script until the visitor interacts shows no chatbot at all.
706 ?>
707 <div class="mxch-notice mxch-notice-warning" style="margin-top: 16px;">
708 <svg class="mxch-notice-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
709 <div>
710 <strong><?php esc_html_e('Using Delay JavaScript in a cache or performance plugin?', 'mxchat'); ?></strong>
711 <?php esc_html_e('Leave chat-script.js out of the delay (FlyingPress, WP Rocket, LiteSpeed Cache and Perfmatters all have an exclusion list). The chat launcher stays hidden until that script runs, so a page that holds it back until the visitor interacts shows no chatbot. If you want the script delayed, pick a strategy above instead: MxChat then loads it on interaction or after a short wait.', 'mxchat'); ?>
712 </div>
713 </div>
714 </div>
715 </div>
716
717 <!-- Page Cache Compatibility Card -->
718 <div class="mxch-card">
719 <div class="mxch-card-header">
720 <h3 class="mxch-card-title">
721 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>
722 <?php esc_html_e('Page Cache Compatibility', 'mxchat'); ?>
723 </h3>
724 </div>
725 <div class="mxch-card-body mxchat-autosave-section">
726 <?php
727 // Page-cache purge on settings save (plan b76ed6, Support Ticket 468).
728 // Standalone option, default ON — never part of mxchat_options, so
729 // mxchat_sanitize() cannot strip it. Read by MxChat_Cache_Purge.
730 mxchat_render_field_wrapper('mxchat_cache_purge_enabled', __('Clear page cache when MxChat settings change', 'mxchat'), function() use ($admin_instance) {
731 $admin_instance->mxchat_cache_purge_toggle_callback();
732 }, __('On, MxChat clears the page caches it can reach (WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Super Cache, FlyingPress) after its settings are saved, at most once every 30 seconds, so visitors do not keep seeing the old chatbot. Off, nothing is cleared: clear your page cache yourself after changing the chatbot. Turn it off on a large site where every save would throw away the whole cache.', 'mxchat'),
733 __('Developers can narrow which settings trigger the clear with the mxchat_cache_purge_watched_options filter.', 'mxchat'));
734 ?>
735
736 <p class="mxch-field-description" style="margin-top: 0;"><?php esc_html_e('MxChat\'s chat send (and stream send, plus PDF / Word upload) all POST to /wp-admin/admin-ajax.php. Page-cache plugins occasionally cache those responses, which breaks the per-session nonce flow on the first message. MxChat auto-tells the five most common cache plugins to never cache chat AJAX via their own filter APIs — no setup needed for those. For other cache plugins (or Cloudflare APO), copy the manual exclusion string into your cache plugin\'s exclusion list.', 'mxchat'); ?></p>
737
738 <?php
739 $mxch_cache_rows = array(
740 array('name' => 'WP Rocket', 'state' => 'auto', 'exclusion' => '/wp-admin/admin-ajax\\.php\\?action=mxchat_.*'),
741 array('name' => 'LiteSpeed Cache', 'state' => 'auto', 'exclusion' => '/wp-admin/admin-ajax.php?action=mxchat_*'),
742 array('name' => 'W3 Total Cache', 'state' => 'auto', 'exclusion' => '/wp-admin/admin-ajax.php?action=mxchat_*'),
743 array('name' => 'FlyingPress', 'state' => 'auto', 'exclusion' => '/wp-admin/admin-ajax.php?action=mxchat_*'),
744 array('name' => 'WP Super Cache', 'state' => 'auto', 'exclusion' => 'wp-admin/admin-ajax.php'),
745 array('name' => 'Cloudflare APO', 'state' => 'manual', 'exclusion' => 'Page Rule / Cache Rule: URI Path contains "/wp-admin/admin-ajax.php" → Bypass Cache'),
746 );
747 ?>
748
749 <div class="mxch-cache-compat-grid" style="display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 16px;">
750 <?php foreach ($mxch_cache_rows as $row) :
751 $is_auto = ($row['state'] === 'auto');
752 $pill_class = $is_auto ? 'mxch-status-pill-active' : 'mxch-status-pill-warning';
753 $pill_label = $is_auto ? __('Auto-handled', 'mxchat') : __('Manual', 'mxchat');
754 ?>
755 <div class="mxch-cache-compat-row" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 14px; background: #f8fafc; border: 1px solid var(--mxch-card-border); border-radius: var(--mxch-radius-md);">
756 <div style="display: flex; align-items: center; gap: 10px; min-width: 0; flex: 0 0 auto;">
757 <span style="font-weight: 500;"><?php echo esc_html($row['name']); ?></span>
758 <span class="mxch-status-pill <?php echo esc_attr($pill_class); ?>">
759 <span class="mxch-status-dot"></span>
760 <?php echo esc_html($pill_label); ?>
761 </span>
762 </div>
763 <div style="display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 0;">
764 <code style="background: #fff; border: 1px solid var(--mxch-card-border); padding: 4px 8px; border-radius: var(--mxch-radius-sm); font-size: 12px; color: var(--mxch-text-secondary); flex: 1 1 auto; overflow-x: auto; white-space: nowrap; min-width: 0;"><?php echo esc_html($row['exclusion']); ?></code>
765 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" data-mxch-copy="<?php echo esc_attr($row['exclusion']); ?>" style="flex: 0 0 auto;">
766 <?php esc_html_e('Copy', 'mxchat'); ?>
767 </button>
768 </div>
769 </div>
770 <?php endforeach; ?>
771 </div>
772
773 <div class="mxch-notice mxch-notice-info" style="margin-top: 16px;">
774 <svg class="mxch-notice-icon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
775 <span><?php esc_html_e('Auto-handled plugins receive a filter from MxChat that excludes chat AJAX from their cache. Even so, having the exclusion in your own exclusion list is harmless and acts as a belt-and-suspenders guarantee.', 'mxchat'); ?></span>
776 </div>
777 </div>
778 </div>
779
780 <!-- Provider Reliability Card -->
781 <?php
782 $auto_retry_enabled = !isset($options['auto_retry_on_transient_error']) ||
783 (string) $options['auto_retry_on_transient_error'] !== '0';
784 ?>
785 <div class="mxch-card">
786 <div class="mxch-card-header">
787 <h3 class="mxch-card-title">
788 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-6.219-8.56"/><polyline points="21 4 21 10 15 10"/></svg>
789 <?php esc_html_e('Provider Reliability', 'mxchat'); ?>
790 </h3>
791 <span class="mxch-status-pill <?php echo $auto_retry_enabled ? 'mxch-status-pill-active' : 'mxch-status-pill-disabled'; ?>">
792 <span class="mxch-status-dot"></span>
793 <?php echo $auto_retry_enabled ? esc_html__('Active', 'mxchat') : esc_html__('Disabled', 'mxchat'); ?>
794 </span>
795 </div>
796 <div class="mxch-card-body mxchat-autosave-section">
797 <div class="mxch-field">
798 <label class="mxch-field-label" for="auto_retry_on_transient_error">
799 <?php esc_html_e('Auto-retry on provider overload', 'mxchat'); ?>
800 </label>
801 <div class="mxch-field-control">
802 <label class="mxch-toggle">
803 <input type="checkbox"
804 id="auto_retry_on_transient_error"
805 name="mxchat_options[auto_retry_on_transient_error]"
806 class="mxchat-autosave-field"
807 value="1"
808 <?php checked($auto_retry_enabled); ?> />
809 <span class="mxch-toggle-slider"></span>
810 </label>
811 </div>
812 <p class="mxch-field-description"><?php esc_html_e('When the AI provider returns a transient error (rate-limit / overload / 429 / 503 / Gemini "model overloaded"), MxChat retries the chat-send up to twice before surfacing the error. Visitors see a brief pause instead of a raw provider error in the bot bubble.', 'mxchat'); ?></p>
813 <p class="mxch-field-hint"><?php esc_html_e('Configuration errors (invalid API key, 401/403/404/422) still surface immediately — never retried.', 'mxchat'); ?></p>
814 </div>
815 </div>
816 </div>
817
818 <!-- Debug Mode Card -->
819 <?php
820 $debug_mode = isset($options['debug_mode']) ? $options['debug_mode'] : 'off';
821 $debug_active = ($debug_mode === 'on');
822 ?>
823 <div class="mxch-card">
824 <div class="mxch-card-header">
825 <h3 class="mxch-card-title">
826 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"/><path d="m9 12 2 2 4-4"/></svg>
827 <?php esc_html_e('Debug Mode', 'mxchat'); ?>
828 </h3>
829 </div>
830 <div class="mxch-card-body">
831 <?php // Deliberate exception to the house .toggle-switch pattern
832 // (audited in plan-64d34f): this card-body is NOT an
833 // autosave section — the toggle saves through its own
834 // dedicated handler (mxchat_toggle_debug_mode, wired at
835 // mxchat-admin.js #mxchat_debug_mode) which appends its own
836 // save indicator inside this label. Converting the markup
837 // would orphan that indicator without touching the JS. ?>
838 <div class="mxch-field">
839 <label class="mxch-toggle">
840 <input type="checkbox" class="mxch-toggle-input" id="mxchat_debug_mode" name="mxchat_options[debug_mode]" value="on" <?php checked($debug_active); ?>>
841 <span class="mxch-toggle-switch"></span>
842 <span class="mxch-toggle-label"><?php esc_html_e('Enable Debug Logging', 'mxchat'); ?></span>
843 </label>
844 <p class="mxch-field-description"><?php esc_html_e('Tracks settings changes, knowledge base errors, API issues, and embedding failures. Enable only when troubleshooting. Max 100 entries stored.', 'mxchat'); ?></p>
845 </div>
846 </div>
847 </div>
848
849 <!-- Debug Log Viewer Card -->
850 <div class="mxch-card">
851 <div class="mxch-card-header" style="display: flex; justify-content: space-between; align-items: center;">
852 <h3 class="mxch-card-title">
853 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>
854 <?php esc_html_e('Debug Log', 'mxchat'); ?>
855 </h3>
856 <div style="display: flex; gap: 8px; align-items: center;">
857 <span id="mxchat-log-count" class="mxch-badge" style="display: none;"></span>
858 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" id="mxchat-refresh-log">
859 <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="M21.5 2v6h-6M2.5 22v-6h6M2 11.5a10 10 0 0 1 18.8-4.3M22 12.5a10 10 0 0 1-18.8 4.2"/></svg>
860 <?php esc_html_e('Refresh', 'mxchat'); ?>
861 </button>
862 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" id="mxchat-clear-log">
863 <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"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
864 <?php esc_html_e('Clear', 'mxchat'); ?>
865 </button>
866 </div>
867 </div>
868 <div class="mxch-card-body">
869 <div id="mxchat-debug-log" class="mxchat-debug-log-viewer">
870 <div class="mxchat-debug-log-empty">
871 <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>
872 <p><?php esc_html_e('No log entries yet. Enable debug mode to start logging.', 'mxchat'); ?></p>
873 </div>
874 </div>
875 </div>
876 </div>
877
878 <!-- Settings Tools Card -->
879 <div class="mxch-card">
880 <div class="mxch-card-header">
881 <h3 class="mxch-card-title">
882 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
883 <?php esc_html_e('Settings Tools', 'mxchat'); ?>
884 </h3>
885 </div>
886 <div class="mxch-card-body">
887 <div class="mxch-tools-grid">
888 <!-- Export Settings -->
889 <div class="mxch-tool-item">
890 <div class="mxch-tool-icon">
891 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
892 </div>
893 <div class="mxch-tool-content">
894 <h4><?php esc_html_e('Export Settings', 'mxchat'); ?></h4>
895 <p><?php esc_html_e('Download a JSON file of your current settings for backup or support. API keys are masked for security.', 'mxchat'); ?></p>
896 <button type="button" class="mxch-btn mxch-btn-secondary" id="mxchat-export-settings">
897 <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="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
898 <?php esc_html_e('Export Settings', 'mxchat'); ?>
899 </button>
900 </div>
901 </div>
902
903 <!-- Reset All Settings -->
904 <div class="mxch-tool-item mxch-tool-item-danger">
905 <div class="mxch-tool-icon">
906 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>
907 </div>
908 <div class="mxch-tool-content">
909 <h4><?php esc_html_e('Reset All Settings', 'mxchat'); ?></h4>
910 <p><?php esc_html_e('Reset all MxChat settings to their default values. This action cannot be undone.', 'mxchat'); ?></p>
911 <button type="button" class="mxch-btn mxch-btn-danger" id="mxchat-reset-settings">
912 <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="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/></svg>
913 <?php esc_html_e('Reset All Settings', 'mxchat'); ?>
914 </button>
915 </div>
916 </div>
917 </div>
918 </div>
919 </div>
920
921 <!-- Reset Confirmation Modal -->
922 <div id="mxchat-reset-modal" class="mxch-modal" style="display: none;">
923 <div class="mxch-modal-backdrop"></div>
924 <div class="mxch-modal-content">
925 <div class="mxch-modal-header">
926 <h3><?php esc_html_e('Reset All Settings', 'mxchat'); ?></h3>
927 <button type="button" class="mxch-modal-close" id="mxchat-reset-modal-close">&times;</button>
928 </div>
929 <div class="mxch-modal-body">
930 <div class="mxch-notice mxch-notice-warning">
931 <svg class="mxch-notice-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
932 <div>
933 <strong><?php esc_html_e('Warning: This action cannot be undone!', 'mxchat'); ?></strong>
934 <p><?php esc_html_e('All your MxChat settings including API keys, customizations, and configurations will be permanently deleted.', 'mxchat'); ?></p>
935 </div>
936 </div>
937 <div class="mxch-field" style="margin-top: 20px;">
938 <label class="mxch-field-label"><?php esc_html_e('Type RESET to confirm:', 'mxchat'); ?></label>
939 <input type="text" id="mxchat-reset-confirmation" class="mxch-input" placeholder="RESET" autocomplete="off">
940 </div>
941 </div>
942 <div class="mxch-modal-footer">
943 <button type="button" class="mxch-btn mxch-btn-secondary" id="mxchat-reset-cancel"><?php esc_html_e('Cancel', 'mxchat'); ?></button>
944 <button type="button" class="mxch-btn mxch-btn-danger" id="mxchat-reset-confirm" disabled><?php esc_html_e('Reset All Settings', 'mxchat'); ?></button>
945 </div>
946 </div>
947 </div>
948 </div>
949
950 <!-- ========================================
951 INTEGRATIONS - TOOLBAR
952 ======================================== -->
953 <div id="integrations-toolbar" class="mxch-section">
954 <div class="mxch-content-header">
955 <h1 class="mxch-content-title"><?php esc_html_e('Toolbar Settings', 'mxchat'); ?></h1>
956 <p class="mxch-content-subtitle"><?php esc_html_e('Configure the chat toolbar and document upload features.', 'mxchat'); ?></p>
957 </div>
958
959 <div class="mxch-card">
960 <div class="mxch-card-body mxchat-autosave-section">
961 <table class="form-table">
962 <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
963 </table>
964 </div>
965 </div>
966 </div>
967
968 <!-- ========================================
969 INTEGRATIONS - LOOPS
970 ======================================== -->
971 <div id="integrations-loops" class="mxch-section">
972 <div class="mxch-content-header">
973 <h1 class="mxch-content-title"><?php esc_html_e('Loops Integration', 'mxchat'); ?></h1>
974 <p class="mxch-content-subtitle"><?php esc_html_e('Configure email capture with Loops for mailing list growth.', 'mxchat'); ?></p>
975 </div>
976
977 <div class="mxch-card">
978 <div class="mxch-card-body mxchat-autosave-section">
979 <table class="form-table">
980 <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
981 </table>
982 </div>
983 </div>
984 </div>
985
986 <!-- ========================================
987 INTEGRATIONS - BRAVE SEARCH
988 ======================================== -->
989 <div id="integrations-brave" class="mxch-section">
990 <div class="mxch-content-header">
991 <h1 class="mxch-content-title"><?php esc_html_e('Brave Search', 'mxchat'); ?></h1>
992 <p class="mxch-content-subtitle"><?php esc_html_e('Configure Brave Search API for web search capabilities.', 'mxchat'); ?></p>
993 </div>
994
995 <div class="mxch-card">
996 <div class="mxch-card-body mxchat-autosave-section">
997 <table class="form-table">
998 <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
999 </table>
1000 </div>
1001 </div>
1002 </div>
1003
1004 <!-- ========================================
1005 INTEGRATIONS - SLACK
1006 ======================================== -->
1007 <div id="integrations-slack" class="mxch-section">
1008 <div class="mxch-content-header">
1009 <h1 class="mxch-content-title"><?php esc_html_e('Slack', 'mxchat'); ?></h1>
1010 <p class="mxch-content-subtitle"><?php esc_html_e('Connect your chatbot to Slack for live human support.', 'mxchat'); ?></p>
1011 </div>
1012
1013 <div class="mxch-notice mxch-notice-info" style="margin-bottom: 24px;">
1014 <svg class="mxch-notice-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
1015 <div>
1016 <?php printf(
1017 esc_html__('Visit our %s for setup instructions.', 'mxchat'),
1018 '<a href="https://mxchat.ai/documentation/mxchat-core#slack-live-agent-handoff" target="_blank">' . esc_html__('documentation page', 'mxchat') . '</a>'
1019 ); ?>
1020 </div>
1021 </div>
1022
1023 <div class="mxch-card">
1024 <div class="mxch-card-body mxchat-autosave-section">
1025 <table class="form-table">
1026 <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
1027 </table>
1028 </div>
1029 </div>
1030
1031 <div class="mxch-card" style="margin-top: 24px;">
1032 <div class="mxch-card-header">
1033 <h3 class="mxch-card-title">
1034 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
1035 <?php esc_html_e('Test Connection', 'mxchat'); ?>
1036 </h3>
1037 </div>
1038 <div class="mxch-card-body">
1039 <p class="mxch-field-description" style="margin-bottom: 16px;">
1040 <?php esc_html_e('Test your Slack bot token to verify it has the required permissions. This will check if the bot can authenticate and create channels.', 'mxchat'); ?>
1041 </p>
1042 <button type="button" id="mxchat-test-slack-connection" class="mxch-btn mxch-btn-secondary">
1043 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 8px;"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
1044 <?php esc_html_e('Test Slack Connection', 'mxchat'); ?>
1045 </button>
1046 <div id="mxchat-slack-test-result" style="margin-top: 16px; display: none;"></div>
1047 </div>
1048 </div>
1049
1050 <div class="mxch-card" style="margin-top: 24px;">
1051 <div class="mxch-card-header">
1052 <h3 class="mxch-card-title">
1053 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
1054 <?php esc_html_e('Required OAuth Scopes', 'mxchat'); ?>
1055 </h3>
1056 </div>
1057 <div class="mxch-card-body">
1058 <p class="mxch-field-description" style="margin-bottom: 12px;">
1059 <?php esc_html_e('Your Slack app needs these Bot Token Scopes (OAuth & Permissions > Scopes):', 'mxchat'); ?>
1060 </p>
1061 <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; background: #f8f9fa; padding: 16px; border-radius: 8px; font-family: monospace; font-size: 13px;">
1062 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">channels:manage</code>
1063 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">channels:read</code>
1064 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">chat:write</code>
1065 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">chat:write.public</code>
1066 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">groups:write</code>
1067 <code style="background: #e9ecef; padding: 4px 8px; border-radius: 4px;">users:read</code>
1068 </div>
1069 <p class="mxch-field-description" style="margin-top: 12px;">
1070 <?php esc_html_e('After adding scopes, reinstall the app to your workspace.', 'mxchat'); ?>
1071 </p>
1072 </div>
1073 </div>
1074 </div>
1075
1076 <!-- ========================================
1077 INTEGRATIONS - TELEGRAM
1078 ======================================== -->
1079 <div id="integrations-telegram" class="mxch-section">
1080 <div class="mxch-content-header">
1081 <h1 class="mxch-content-title"><?php esc_html_e('Telegram', 'mxchat'); ?></h1>
1082 <p class="mxch-content-subtitle"><?php esc_html_e('Connect your chatbot to Telegram for live human support.', 'mxchat'); ?></p>
1083 </div>
1084
1085 <div class="mxch-notice mxch-notice-info" style="margin-bottom: 24px;">
1086 <svg class="mxch-notice-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
1087 <div>
1088 <?php esc_html_e('Telegram Live Agent requires a Telegram bot and a supergroup with forum topics enabled.', 'mxchat'); ?>
1089 <a href="https://mxchat.ai/documentation-bot/" target="_blank" style="margin-left: 5px;"><?php esc_html_e('Chat with our advanced doc bot for assistance setting up.', 'mxchat'); ?></a>
1090 </div>
1091 </div>
1092
1093 <div class="mxch-card">
1094 <div class="mxch-card-body mxchat-autosave-section">
1095 <table class="form-table">
1096 <?php do_settings_fields('mxchat-embed', 'mxchat_telegram_section'); ?>
1097 </table>
1098 </div>
1099 </div>
1100
1101 <div class="mxch-card" style="margin-top: 24px;">
1102 <div class="mxch-card-header">
1103 <h3 class="mxch-card-title">
1104 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
1105 <?php esc_html_e('Webhook Setup', 'mxchat'); ?>
1106 </h3>
1107 </div>
1108 <div class="mxch-card-body">
1109 <?php
1110 $telegram_bot_token = $options['telegram_bot_token'] ?? '';
1111 $telegram_webhook_secret = $options['telegram_webhook_secret'] ?? '';
1112 $webhook_url = rest_url('mxchat/v1/telegram-webhook');
1113 ?>
1114
1115 <p class="mxch-field-description" style="margin-bottom: 8px;">
1116 <strong><?php esc_html_e('Your Webhook URL:', 'mxchat'); ?></strong>
1117 </p>
1118 <div class="mxch-webhook-url-display" style="margin-bottom: 20px;">
1119 <code id="mxch-telegram-webhook-url"><?php echo esc_url($webhook_url); ?></code>
1120 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" onclick="navigator.clipboard.writeText(document.getElementById('mxch-telegram-webhook-url').textContent); this.textContent='Copied!'; setTimeout(() => this.textContent='Copy', 2000);">
1121 <?php esc_html_e('Copy', 'mxchat'); ?>
1122 </button>
1123 </div>
1124
1125 <?php if (!empty($telegram_bot_token)) : ?>
1126 <p class="mxch-field-description" style="margin-bottom: 8px;">
1127 <strong><?php esc_html_e('Register Webhook URL:', 'mxchat'); ?></strong><br>
1128 <?php esc_html_e('Copy and paste this URL into your browser to register the webhook with Telegram:', 'mxchat'); ?>
1129 </p>
1130 <?php
1131 $registration_url = 'https://api.telegram.org/bot' . $telegram_bot_token . '/setWebhook?url=' . urlencode($webhook_url);
1132 if (!empty($telegram_webhook_secret)) {
1133 $registration_url .= '&secret_token=' . urlencode($telegram_webhook_secret);
1134 }
1135 ?>
1136 <div class="mxch-webhook-url-display" style="margin-bottom: 12px;">
1137 <code id="mxch-telegram-registration-url" style="word-break: break-all; font-size: 12px;"><?php echo esc_url($registration_url); ?></code>
1138 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" onclick="navigator.clipboard.writeText(document.getElementById('mxch-telegram-registration-url').textContent); this.textContent='Copied!'; setTimeout(() => this.textContent='Copy', 2000);">
1139 <?php esc_html_e('Copy', 'mxchat'); ?>
1140 </button>
1141 </div>
1142 <p class="mxch-field-description" style="color: #22c55e;">
1143 <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" style="vertical-align: middle; margin-right: 4px;"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
1144 <?php esc_html_e('You should see {"ok":true,"result":true,"description":"Webhook was set"} after visiting the URL.', 'mxchat'); ?>
1145 </p>
1146 <?php else : ?>
1147 <p class="mxch-field-description" style="color: #f59e0b;">
1148 <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" style="vertical-align: middle; margin-right: 4px;"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
1149 <?php esc_html_e('Enter your Bot Token above and save to generate the registration URL.', 'mxchat'); ?>
1150 </p>
1151 <?php endif; ?>
1152 </div>
1153 </div>
1154 </div>
1155
1156 <!-- ========================================
1157 INTEGRATIONS - WEBHOOK HANDOFF (plan d88e22)
1158 ======================================== -->
1159 <div id="integrations-webhook" class="mxch-section">
1160 <div class="mxch-content-header">
1161 <h1 class="mxch-content-title"><?php esc_html_e('Webhook', 'mxchat'); ?></h1>
1162 <p class="mxch-content-subtitle"><?php esc_html_e('Route live-agent handoffs to any URL you choose — your helpdesk, an automation flow, a CRM.', 'mxchat'); ?></p>
1163 </div>
1164
1165 <div class="mxch-notice mxch-notice-info" style="margin-bottom: 24px;">
1166 <svg class="mxch-notice-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
1167 <div>
1168 <?php esc_html_e('This destination is outbound-only: MxChat delivers the handoff to your URL and the chatbot keeps assisting the visitor. Your team follows up separately — by email, phone, or your own support tool.', 'mxchat'); ?>
1169 </div>
1170 </div>
1171
1172 <div class="mxch-card">
1173 <div class="mxch-card-body mxchat-autosave-section">
1174 <table class="form-table">
1175 <?php do_settings_fields('mxchat-embed', 'mxchat_webhook_handoff_section'); ?>
1176 </table>
1177 </div>
1178 </div>
1179
1180 <div class="mxch-card" style="margin-top: 24px;">
1181 <div class="mxch-card-header">
1182 <h3 class="mxch-card-title">
1183 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
1184 <?php esc_html_e('Payload Reference', 'mxchat'); ?>
1185 </h3>
1186 </div>
1187 <div class="mxch-card-body">
1188 <p class="mxch-field-description" style="margin-bottom: 12px;">
1189 <?php esc_html_e('Each handoff arrives at your URL as an HTTPS POST with Content-Type application/json:', 'mxchat'); ?>
1190 </p>
1191 <pre style="background: #f8f9fa; padding: 16px; border-radius: 8px; font-size: 12px; overflow-x: auto; margin: 0 0 16px;"><?php echo esc_html('{
1192 "event": "live_agent_handoff",
1193 "site": { "name": "Your Site", "url": "https://your-site.com" },
1194 "session_id": "mxchat_chat_…",
1195 "user_id": 0,
1196 "visitor": { "name": "Ada", "email": "[email protected]" },
1197 "current_message": "I need to talk to a human",
1198 "recent_messages": [
1199 { "role": "user", "content": "…", "timestamp": 1755900000 },
1200 { "role": "assistant", "content": "…", "timestamp": 1755900012 }
1201 ],
1202 "requested_at": "2026-08-22T14:30:00+00:00"
1203 }'); ?></pre>
1204 <p class="mxch-field-description" style="margin-bottom: 12px;">
1205 <?php esc_html_e('When a Signing Secret is set, each request also carries this header so your endpoint can verify the sender — compute the HMAC of the raw request body with your secret and compare:', 'mxchat'); ?>
1206 </p>
1207 <div class="mxch-webhook-url-display" style="margin-bottom: 16px;">
1208 <code>X-MxChat-Signature: sha256=&lt;hex hmac-sha256 of the raw body&gt;</code>
1209 </div>
1210 <p class="mxch-field-description">
1211 <?php esc_html_e('Answer with any 2xx status. Delivery uses a 5-second timeout with one retry on transport errors and 5xx responses; redirects are not followed. The last failed delivery is shown under the Webhook URL field above.', 'mxchat'); ?>
1212 </p>
1213 </div>
1214 </div>
1215 </div>
1216
1217 <!-- ========================================
1218 TUTORIALS
1219 ======================================== -->
1220 <div id="tutorials" class="mxch-section">
1221 <div class="mxch-content-header">
1222 <h1 class="mxch-content-title"><?php esc_html_e('Video Tutorials', 'mxchat'); ?></h1>
1223 <p class="mxch-content-subtitle"><?php esc_html_e('Learn how to get the most out of MxChat with our video guides.', 'mxchat'); ?></p>
1224 </div>
1225
1226 <?php
1227 // Setup Guide — reopen the onboarding wizard (plan-347916, moved here from Display tab).
1228 // Only shows once the user has dismissed/graduated from the Onboarding page.
1229 if (function_exists('mxchat_onboarding_is_dismissed') && mxchat_onboarding_is_dismissed()):
1230 $auto_grad = function_exists('mxchat_onboarding_is_auto_graduated') && mxchat_onboarding_is_auto_graduated();
1231 ?>
1232 <div class="mxch-card mxch-onboarding-unhide-card" style="margin-bottom: 24px;">
1233 <div class="mxch-card-header">
1234 <h3 class="mxch-card-title">
1235 <svg class="mxch-card-title-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 10v6M2 10l10-5 10 5-10 5z"/><path d="M6 12v5c0 2 3 3 6 3s6-1 6-3v-5"/></svg>
1236 <?php esc_html_e('Setup Guide', 'mxchat'); ?>
1237 </h3>
1238 </div>
1239 <div class="mxch-card-body">
1240 <p style="margin:0 0 12px;color:var(--mxch-text-secondary);font-size:13px;line-height:1.5;">
1241 <?php if ($auto_grad): ?>
1242 <?php esc_html_e('You finished the setup guide. Reopen it any time to revisit the steps or change a setup choice.', 'mxchat'); ?>
1243 <?php else: ?>
1244 <?php esc_html_e('You hid the setup guide from the MxChat menu. Reopen it any time to revisit the steps.', 'mxchat'); ?>
1245 <?php endif; ?>
1246 </p>
1247 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="margin:0;">
1248 <?php wp_nonce_field('mxchat_unhide_onboarding'); ?>
1249 <input type="hidden" name="action" value="mxchat_unhide_onboarding" />
1250 <button type="submit" class="mxch-btn mxch-btn-secondary mxch-btn-sm">
1251 <?php esc_html_e('Reopen setup guide', 'mxchat'); ?>
1252 </button>
1253 </form>
1254 </div>
1255 </div>
1256 <?php endif; ?>
1257
1258 <div class="mxch-card" style="margin-bottom: 24px;">
1259 <div class="mxch-card-body">
1260 <div style="display: flex; align-items: flex-start; gap: 16px;">
1261 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color: var(--mxch-warning); flex-shrink: 0;"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
1262 <div>
1263 <h3 style="margin: 0 0 8px 0; font-size: 16px; font-weight: 600;"><?php esc_html_e('Need Help?', 'mxchat'); ?></h3>
1264 <p style="margin: 0 0 12px 0; color: var(--mxch-text-secondary);"><?php esc_html_e('If you\'re having trouble or believe something is not working as expected, please submit a support ticket.', 'mxchat'); ?></p>
1265 <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" class="mxch-btn mxch-btn-secondary mxch-btn-sm">
1266 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v5Z"/><path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1"/></svg>
1267 <?php esc_html_e('Submit Support Ticket', 'mxchat'); ?>
1268 </a>
1269 </div>
1270 </div>
1271 </div>
1272 </div>
1273
1274 <div class="mxch-tutorials-grid">
1275 <?php
1276 $tutorials = array(
1277 array(
1278 'title' => __('Quick Setup with MxChat', 'mxchat'),
1279 'description' => __('Learn how to quickly setup and understand how your chatbot works.', 'mxchat'),
1280 'url' => 'https://www.youtube.com/watch?v=3BqoiyWaQiM&t'
1281 ),
1282 array(
1283 'title' => __('AI Content Generation', 'mxchat'),
1284 'description' => __('Generate full blog posts and landing pages with AI—from prompt to publish in minutes.', 'mxchat'),
1285 'url' => 'https://www.youtube.com/watch?v=n8TeEeHpxs4'
1286 ),
1287 array(
1288 'title' => __('Chat With Your GSC Data', 'mxchat'),
1289 'description' => __('Find content gaps, quick win keywords, and SEO opportunities by chatting with your Google Search Console data using the Admin Assistant add-on.', 'mxchat'),
1290 'url' => 'https://www.youtube.com/watch?v=ytgMou_7SZA&t=2s'
1291 ),
1292 array(
1293 'title' => __('AI Theme Generator', 'mxchat'),
1294 'description' => __('Learn how to instantly restyle your chatbot using plain English prompts with real-time previews.', 'mxchat'),
1295 'url' => 'https://www.youtube.com/watch?v=rSQDW2qbtRU&t'
1296 ),
1297 array(
1298 'title' => __('MxChat Forms', 'mxchat'),
1299 'description' => __('Create and manage smart forms that automatically trigger during chat conversations.', 'mxchat'),
1300 'url' => 'https://www.youtube.com/watch?v=3MrWy5dRalA'
1301 ),
1302 array(
1303 'title' => __('Admin Assistant Add-on', 'mxchat'),
1304 'description' => __('Bring a ChatGPT-like experience directly inside your WordPress dashboard.', 'mxchat'),
1305 'url' => 'https://youtu.be/AdEA1k-UCFM'
1306 ),
1307 array(
1308 'title' => __('Chat Themes', 'mxchat'),
1309 'description' => __('Customize appearance with real-time previews to match your brand.', 'mxchat'),
1310 'url' => 'https://youtu.be/MfbB9mZi6ag'
1311 ),
1312 array(
1313 'title' => __('WooCommerce Integration', 'mxchat'),
1314 'description' => __('Provide product recommendations and shopping assistance to customers.', 'mxchat'),
1315 'url' => 'https://www.youtube.com/watch?v=WsqAppHRGdA'
1316 ),
1317 array(
1318 'title' => __('Knowledge Base Setup', 'mxchat'),
1319 'description' => __('Set up your knowledge base using PDFs, sitemaps, and manual entries.', 'mxchat'),
1320 'url' => 'https://www.youtube.com/watch?v=8Ztjs66-VTo'
1321 ),
1322 array(
1323 'title' => __('Document Chat', 'mxchat'),
1324 'description' => __('Chat with PDF and Word documents for enhanced document analysis.', 'mxchat'),
1325 'url' => 'https://www.youtube.com/watch?v=j_c45WWCTG0'
1326 ),
1327 array(
1328 'title' => __('Perplexity Integration', 'mxchat'),
1329 'description' => __('Enable real-time web search capabilities for your chatbot.', 'mxchat'),
1330 'url' => 'https://youtu.be/wpKkbt24-bo'
1331 ),
1332 array(
1333 'title' => __('Brave Search Intent', 'mxchat'),
1334 'description' => __('Leverage Brave Search for improved query understanding.', 'mxchat'),
1335 'url' => 'https://www.youtube.com/watch?v=7vDL5H7vToc'
1336 ),
1337 array(
1338 'title' => __('Loops Email Capture', 'mxchat'),
1339 'description' => __('Set up email capture with Loops to grow your mailing list.', 'mxchat'),
1340 'url' => 'https://www.youtube.com/watch?v=CNgm5TYDyTc'
1341 ),
1342 array(
1343 'title' => __('AI Agent Testing', 'mxchat'),
1344 'description' => __('Evaluate and improve your chatbot\'s performance and accuracy.', 'mxchat'),
1345 'url' => 'https://www.youtube.com/watch?v=A0jowbpyX54'
1346 ),
1347 );
1348
1349 foreach ($tutorials as $tutorial): ?>
1350 <div class="mxch-tutorial-card">
1351 <div class="mxch-tutorial-content">
1352 <h3 class="mxch-tutorial-title"><?php echo esc_html($tutorial['title']); ?></h3>
1353 <p class="mxch-tutorial-description"><?php echo esc_html($tutorial['description']); ?></p>
1354 <a href="<?php echo esc_url($tutorial['url']); ?>" target="_blank" rel="noopener" class="mxch-tutorial-link">
1355 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>
1356 <?php esc_html_e('Watch Tutorial', 'mxchat'); ?>
1357 </a>
1358 </div>
1359 </div>
1360 <?php endforeach; ?>
1361 </div>
1362 </div>
1363
1364 <!-- =====================================================
1365 TESTING SECTION
1366 ===================================================== -->
1367 <div id="testing" class="mxch-section">
1368 <div class="mxch-content-header">
1369 <h1 class="mxch-content-title"><?php esc_html_e('Testing', 'mxchat'); ?></h1>
1370 <p class="mxch-content-subtitle"><?php esc_html_e('Test your chatbot and inspect debug data in real time.', 'mxchat'); ?></p>
1371 </div>
1372
1373 <div class="mxch-testing-layout">
1374 <!-- Left Column: Debug Panel -->
1375 <div class="mxch-testing-debug-panel">
1376 <!-- Quick Actions -->
1377 <div class="mxch-card">
1378 <div class="mxch-card-body">
1379 <h3 class="mxch-testing-section-title"><?php esc_html_e('Quick Actions', 'mxchat'); ?></h3>
1380 <div class="mxch-testing-actions-row">
1381 <button type="button" class="mxch-testing-btn mxch-testing-btn-danger" id="mxch-testing-clear-session">
1382 <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="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>
1383 <?php esc_html_e('Clear Chat Session', 'mxchat'); ?>
1384 </button>
1385 <button type="button" class="mxch-testing-btn mxch-testing-btn-secondary" id="mxch-testing-clear-debug">
1386 <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 2v20M2 12h20"/></svg>
1387 <?php esc_html_e('Clear Debug Log', 'mxchat'); ?>
1388 </button>
1389 </div>
1390 </div>
1391 </div>
1392
1393 <!-- Last Query Analysis -->
1394 <div class="mxch-card">
1395 <div class="mxch-card-body">
1396 <h3 class="mxch-testing-section-title"><?php esc_html_e('Last Query Analysis', 'mxchat'); ?></h3>
1397
1398 <div class="mxch-testing-field">
1399 <label><?php esc_html_e('Similarity Threshold', 'mxchat'); ?></label>
1400 <span id="mxch-testing-threshold"><?php esc_html_e('Loading...', 'mxchat'); ?></span>
1401 </div>
1402
1403 <div class="mxch-testing-field">
1404 <label><?php esc_html_e('User Query', 'mxchat'); ?></label>
1405 <div id="mxch-testing-last-query" class="mxch-testing-query-display"><?php esc_html_e('Waiting for next query...', 'mxchat'); ?></div>
1406 </div>
1407
1408 <div class="mxch-testing-field">
1409 <label><?php esc_html_e('Approved URLs for Citations', 'mxchat'); ?></label>
1410 <div id="mxch-testing-approved-urls" class="mxch-testing-results">
1411 <div class="mxch-testing-no-data"><?php esc_html_e('No URL data yet', 'mxchat'); ?></div>
1412 </div>
1413 </div>
1414
1415 <div class="mxch-testing-field">
1416 <label><?php esc_html_e('Document Matches', 'mxchat'); ?></label>
1417 <div id="mxch-testing-similarity-scores" class="mxch-testing-results">
1418 <div class="mxch-testing-no-data"><?php esc_html_e('No query data yet', 'mxchat'); ?></div>
1419 </div>
1420 </div>
1421
1422 <div class="mxch-testing-field">
1423 <label><?php esc_html_e('Actions Triggered', 'mxchat'); ?></label>
1424 <div id="mxch-testing-action-scores" class="mxch-testing-results">
1425 <div class="mxch-testing-no-data"><?php esc_html_e('No action data yet', 'mxchat'); ?></div>
1426 </div>
1427 </div>
1428 </div>
1429 </div>
1430
1431 <!-- System Information -->
1432 <div class="mxch-card">
1433 <div class="mxch-card-body">
1434 <h3 class="mxch-testing-section-title"><?php esc_html_e('System Information', 'mxchat'); ?></h3>
1435
1436 <div class="mxch-testing-field">
1437 <label><?php esc_html_e('System Prompt', 'mxchat'); ?></label>
1438 <div id="mxch-testing-system-prompt" class="mxch-testing-system-prompt"><?php esc_html_e('Loading...', 'mxchat'); ?></div>
1439 </div>
1440
1441 <div class="mxch-testing-field">
1442 <label><?php esc_html_e('Knowledge Base', 'mxchat'); ?></label>
1443 <span id="mxch-testing-kb-status"><?php esc_html_e('Checking...', 'mxchat'); ?></span>
1444 </div>
1445 </div>
1446 </div>
1447
1448 <!-- Debug Log -->
1449 <div class="mxch-card">
1450 <div class="mxch-card-body">
1451 <h3 class="mxch-testing-section-title"><?php esc_html_e('Debug Log', 'mxchat'); ?></h3>
1452 <div id="mxch-testing-debug-console" class="mxch-testing-debug-console">
1453 <div class="mxch-testing-debug-entry"><?php esc_html_e('Debug panel ready - send a message to begin...', 'mxchat'); ?></div>
1454 </div>
1455 </div>
1456 </div>
1457 </div>
1458
1459 <!-- Right Column: Chatbot Replica -->
1460 <div class="mxch-testing-chatbot-column">
1461 <div class="mxch-testing-chatbot-scope">
1462 <?php
1463 // Render the chatbot inline for testing
1464 echo do_shortcode('[mxchat_chatbot floating="no" bot_id="testing"]');
1465 ?>
1466 </div>
1467 </div>
1468 </div>
1469 </div>
1470
1471 </main>
1472 </div>
1473
1474 <script>
1475 document.addEventListener('DOMContentLoaded', function() {
1476 // Sidebar/tab navigation, accordion groups, ?tab=/#hash deep-linking
1477 // and the mobile menu all live in the shared shell script
1478 // (js/admin-sidebar.js) — consolidated by plan c192a4. Only the
1479 // page-local widgets below remain inline.
1480
1481 // Rate limit accordion
1482 const rateLimitHeaders = document.querySelectorAll('.mxch-rate-limit-header');
1483 rateLimitHeaders.forEach(header => {
1484 header.addEventListener('click', function(e) {
1485 e.preventDefault();
1486 e.stopPropagation();
1487 const item = this.closest('.mxch-rate-limit-item');
1488 item.classList.toggle('expanded');
1489 });
1490 });
1491
1492 // Rate-limit "Custom…" toggle: show/hide the integer input below each
1493 // limit <select> based on whether __custom__ is the active option.
1494 document.querySelectorAll('.mxch-rate-limit-limit-select').forEach(sel => {
1495 const targetId = sel.getAttribute('data-mxch-custom-target');
1496 if (!targetId) return;
1497 const field = document.querySelector('[data-mxch-custom-for="' + targetId + '"]');
1498 if (!field) return;
1499 const sync = () => {
1500 if (sel.value === '__custom__') {
1501 field.hidden = false;
1502 } else {
1503 field.hidden = true;
1504 }
1505 };
1506 sel.addEventListener('change', sync);
1507 sync();
1508 });
1509 });
1510 </script>
1511 <?php
1512 }
1513
1514 /**
1515 * Helper function to render a field with consistent wrapper
1516 *
1517 * $hint renders as a .mxch-field-hint line under the description — the
1518 * softer "caveats / fine print" slot (added for plan-64d34f so hint-bearing
1519 * fields don't have to hand-roll the wrapper markup).
1520 */
1521 function mxchat_render_field_wrapper($id, $label, $callback, $description = '', $hint = '') {
1522 ?>
1523 <div class="mxch-field">
1524 <label class="mxch-field-label"><?php echo esc_html($label); ?></label>
1525 <div class="mxch-field-control">
1526 <?php $callback(); ?>
1527 </div>
1528 <?php if ($description): ?>
1529 <p class="mxch-field-description"><?php echo esc_html($description); ?></p>
1530 <?php endif; ?>
1531 <?php if ($hint): ?>
1532 <p class="mxch-field-hint"><?php echo esc_html($hint); ?></p>
1533 <?php endif; ?>
1534 </div>
1535 <?php
1536 }
1537
1538 /**
1539 * Render rate limits as collapsible accordion
1540 */
1541 function mxchat_render_rate_limits_accordion($admin_instance) {
1542 $all_options = get_option('mxchat_options', []);
1543 $rate_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
1544 $timeframes = array(
1545 'hourly' => __('Per Hour', 'mxchat'),
1546 'daily' => __('Per Day', 'mxchat'),
1547 'weekly' => __('Per Week', 'mxchat'),
1548 'monthly' => __('Per Month', 'mxchat')
1549 );
1550
1551 $roles = wp_roles()->get_names();
1552 $roles['logged_out'] = __('Logged Out Users', 'mxchat');
1553
1554 // Whole-chatbot global cap (sits above per-role; defaults to unlimited so
1555 // existing installs are unchanged). Stored under mxchat_options['rate_limits_global'].
1556 $global_cfg = isset($all_options['rate_limits_global']) && is_array($all_options['rate_limits_global'])
1557 ? $all_options['rate_limits_global']
1558 : array();
1559 $global_limit_raw = isset($global_cfg['limit']) ? (string) $global_cfg['limit'] : 'unlimited';
1560 $global_timeframe = isset($global_cfg['timeframe']) ? (string) $global_cfg['timeframe'] : 'daily';
1561 $global_is_unlimited = ($global_limit_raw === '' || $global_limit_raw === 'unlimited');
1562 $global_is_preset = !$global_is_unlimited && in_array($global_limit_raw, $rate_limits, true);
1563 $global_select_value = $global_is_unlimited ? 'unlimited' : ($global_is_preset ? $global_limit_raw : '__custom__');
1564 $global_custom_value = (!$global_is_unlimited && !$global_is_preset && ctype_digit($global_limit_raw))
1565 ? $global_limit_raw
1566 : '';
1567
1568 echo '<div class="mxch-rate-limits">';
1569
1570 // --- Global cap card -------------------------------------------------
1571 ?>
1572 <div class="mxch-rate-limit-item mxch-rate-limit-global mxchat-autosave-section">
1573 <div class="mxch-rate-limit-header">
1574 <span class="mxch-rate-limit-role"><?php esc_html_e('Total chatbot message limit', 'mxchat'); ?></span>
1575 <div class="mxch-rate-limit-summary">
1576 <span class="mxch-rate-limit-badge mxch-rate-limit-global-badge">
1577 <?php
1578 if ($global_is_unlimited) {
1579 esc_html_e('Unlimited', 'mxchat');
1580 } else {
1581 $tf_label = isset($timeframes[$global_timeframe]) ? $timeframes[$global_timeframe] : $global_timeframe;
1582 echo esc_html(($global_custom_value !== '' ? $global_custom_value : $global_limit_raw) . ' / ' . $tf_label);
1583 }
1584 ?>
1585 </span>
1586 <svg class="mxch-rate-limit-arrow" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>
1587 </div>
1588 </div>
1589 <div class="mxch-rate-limit-body">
1590 <p class="mxch-field-description" style="margin: 0 0 12px;">
1591 <?php esc_html_e('A single ceiling across all users and all roles, per timeframe. Independent of the per-role limits below. When both a per-role limit and this global cap are configured, whichever is hit first stops the conversation. Default is Unlimited — existing installs are unchanged.', 'mxchat'); ?>
1592 </p>
1593 <div class="mxch-rate-limit-controls">
1594 <div class="mxch-field">
1595 <label class="mxch-field-label" for="rate_limits_global_limit"><?php esc_html_e('Limit', 'mxchat'); ?></label>
1596 <select id="rate_limits_global_limit"
1597 name="mxchat_options[rate_limits_global][limit]"
1598 class="mxch-select mxchat-autosave-field mxch-rate-limit-limit-select"
1599 data-mxch-custom-target="rate_limits_global_limit_custom">
1600 <?php foreach ($rate_limits as $limit): ?>
1601 <option value="<?php echo esc_attr($limit); ?>" <?php selected($global_select_value, $limit); ?>>
1602 <?php echo esc_html($limit === 'unlimited' ? __('Unlimited', 'mxchat') : $limit); ?>
1603 </option>
1604 <?php endforeach; ?>
1605 <option value="__custom__" <?php selected($global_select_value, '__custom__'); ?>><?php esc_html_e('Custom…', 'mxchat'); ?></option>
1606 </select>
1607 </div>
1608 <div class="mxch-field mxch-rate-limit-custom-field" data-mxch-custom-for="rate_limits_global_limit_custom" <?php echo $global_select_value === '__custom__' ? '' : 'hidden'; ?>>
1609 <label class="mxch-field-label" for="rate_limits_global_limit_custom"><?php esc_html_e('Custom limit', 'mxchat'); ?></label>
1610 <input type="number"
1611 min="1"
1612 step="1"
1613 id="rate_limits_global_limit_custom"
1614 name="mxchat_options[rate_limits_global][limit_custom]"
1615 class="mxch-input mxchat-autosave-field mxch-rate-limit-custom-input"
1616 placeholder="e.g. 250"
1617 value="<?php echo esc_attr($global_custom_value); ?>" />
1618 </div>
1619 <div class="mxch-field">
1620 <label class="mxch-field-label" for="rate_limits_global_timeframe"><?php esc_html_e('Timeframe', 'mxchat'); ?></label>
1621 <select id="rate_limits_global_timeframe"
1622 name="mxchat_options[rate_limits_global][timeframe]"
1623 class="mxch-select mxchat-autosave-field">
1624 <?php foreach ($timeframes as $value => $label): ?>
1625 <option value="<?php echo esc_attr($value); ?>" <?php selected($global_timeframe, $value); ?>><?php echo esc_html($label); ?></option>
1626 <?php endforeach; ?>
1627 </select>
1628 </div>
1629 </div>
1630 <?php
1631 // --- Current usage readout (read-only; mirrors the integrator's
1632 // window-reset math at READ time — never writes the counter here).
1633 // v1 surfaces the DEFAULT bot's pool. TODO: per-bot selector if asked.
1634 $global_usage_limit_int = (!$global_is_unlimited && ctype_digit($global_limit_raw))
1635 ? (int) $global_limit_raw
1636 : 0;
1637 $usage_raw = get_option('mxchat_chat_limit_default_global', array('count' => 0, 'timestamp' => time()));
1638 $usage_count = isset($usage_raw['count']) ? (int) $usage_raw['count'] : 0;
1639 $usage_ts = isset($usage_raw['timestamp']) ? (int) $usage_raw['timestamp'] : time();
1640 $usage_windows = array('hourly' => 3600, 'daily' => 86400, 'weekly' => 604800, 'monthly' => 2592000);
1641 $usage_window = isset($usage_windows[$global_timeframe]) ? $usage_windows[$global_timeframe] : 86400;
1642 $usage_now = time();
1643 if (($usage_now - $usage_ts) >= $usage_window) {
1644 // Window elapsed since the stored timestamp → effective count is 0.
1645 $usage_count = 0;
1646 $usage_reset_at = $usage_now + $usage_window;
1647 } else {
1648 $usage_reset_at = $usage_ts + $usage_window;
1649 }
1650 $usage_left = max(0, $global_usage_limit_int - $usage_count);
1651 $usage_pct = ($global_usage_limit_int > 0)
1652 ? min(100, (int) round(($usage_count / $global_usage_limit_int) * 100))
1653 : 0;
1654 $usage_reset_nonce = wp_create_nonce('mxchat_reset_global_usage');
1655 ?>
1656 <div class="mxch-rate-limit-usage"
1657 id="mxch-global-usage"
1658 data-bot-id="default"
1659 data-reset-nonce="<?php echo esc_attr($usage_reset_nonce); ?>"
1660 <?php echo $global_is_unlimited ? 'hidden' : ''; ?>>
1661 <div class="mxch-rate-limit-usage-head">
1662 <span class="mxch-field-label" style="margin:0;"><?php esc_html_e('Current usage', 'mxchat'); ?></span>
1663 <button type="button" class="mxch-btn mxch-btn-secondary mxch-btn-sm" id="mxch-global-usage-reset"><?php esc_html_e('Reset counter', 'mxchat'); ?></button>
1664 </div>
1665 <div class="mxch-progress-bar">
1666 <div class="mxch-progress-bar-fill" id="mxch-global-usage-fill" style="width: <?php echo esc_attr($usage_pct); ?>%;"></div>
1667 </div>
1668 <p class="mxch-progress-label" id="mxch-global-usage-text" style="margin:0;">
1669 <?php
1670 printf(
1671 /* translators: 1: used count, 2: limit, 3: remaining, 4: human-readable time until reset */
1672 esc_html__('%1$s of %2$s used · %3$s left · resets in %4$s', 'mxchat'),
1673 esc_html(number_format_i18n($usage_count)),
1674 esc_html(number_format_i18n($global_usage_limit_int)),
1675 esc_html(number_format_i18n($usage_left)),
1676 esc_html(human_time_diff($usage_now, $usage_reset_at))
1677 );
1678 ?>
1679 </p>
1680 </div>
1681 <p class="mxch-field-description mxch-rate-limit-usage-unlimited"
1682 id="mxch-global-usage-unlimited"
1683 style="margin: 12px 0 0;"
1684 <?php echo $global_is_unlimited ? '' : 'hidden'; ?>>
1685 <?php esc_html_e('Unlimited — no cap. Usage tracking applies only when a numeric limit is set.', 'mxchat'); ?>
1686 </p>
1687 </div>
1688 </div>
1689 <?php
1690 // ---------------------------------------------------------------------
1691
1692 foreach ($roles as $role_id => $role_name) {
1693 $default_limit = ($role_id === 'logged_out') ? '10' : '100';
1694 $default_timeframe = 'daily';
1695 $default_message = __('Rate limit exceeded. Please try again later.', 'mxchat');
1696
1697 $selected_limit = isset($all_options['rate_limits'][$role_id]['limit'])
1698 ? (string) $all_options['rate_limits'][$role_id]['limit']
1699 : $default_limit;
1700
1701 $selected_timeframe = isset($all_options['rate_limits'][$role_id]['timeframe'])
1702 ? $all_options['rate_limits'][$role_id]['timeframe']
1703 : $default_timeframe;
1704
1705 $custom_message = isset($all_options['rate_limits'][$role_id]['message'])
1706 ? $all_options['rate_limits'][$role_id]['message']
1707 : $default_message;
1708
1709 $timeframe_label = isset($timeframes[$selected_timeframe]) ? $timeframes[$selected_timeframe] : $selected_timeframe;
1710
1711 // Custom-value handling: if the stored limit is not in the preset list
1712 // and not 'unlimited', it's a custom integer. The select shows __custom__
1713 // and the number input below carries the actual value.
1714 $is_preset_limit = in_array($selected_limit, $rate_limits, true);
1715 $role_select_value = $is_preset_limit ? $selected_limit : '__custom__';
1716 $role_custom_value = (!$is_preset_limit && ctype_digit($selected_limit)) ? $selected_limit : '';
1717 $custom_field_id = 'rate_limits_' . $role_id . '_limit_custom';
1718 ?>
1719 <div class="mxch-rate-limit-item">
1720 <div class="mxch-rate-limit-header">
1721 <span class="mxch-rate-limit-role"><?php echo esc_html($role_name); ?></span>
1722 <div class="mxch-rate-limit-summary">
1723 <span class="mxch-rate-limit-badge"><?php echo esc_html($selected_limit . ' / ' . $timeframe_label); ?></span>
1724 <svg class="mxch-rate-limit-arrow" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>
1725 </div>
1726 </div>
1727 <div class="mxch-rate-limit-body mxchat-autosave-section">
1728 <div class="mxch-rate-limit-controls">
1729 <div class="mxch-field">
1730 <label class="mxch-field-label" for="rate_limits_<?php echo esc_attr($role_id); ?>_limit"><?php esc_html_e('Limit', 'mxchat'); ?></label>
1731 <select id="rate_limits_<?php echo esc_attr($role_id); ?>_limit"
1732 name="mxchat_options[rate_limits][<?php echo esc_attr($role_id); ?>][limit]"
1733 class="mxch-select mxchat-autosave-field mxch-rate-limit-limit-select"
1734 data-mxch-custom-target="<?php echo esc_attr($custom_field_id); ?>">
1735 <?php foreach ($rate_limits as $limit): ?>
1736 <option value="<?php echo esc_attr($limit); ?>" <?php selected($role_select_value, $limit); ?>>
1737 <?php echo esc_html($limit === 'unlimited' ? __('Unlimited', 'mxchat') : $limit); ?>
1738 </option>
1739 <?php endforeach; ?>
1740 <option value="__custom__" <?php selected($role_select_value, '__custom__'); ?>><?php esc_html_e('Custom…', 'mxchat'); ?></option>
1741 </select>
1742 </div>
1743 <div class="mxch-field mxch-rate-limit-custom-field" data-mxch-custom-for="<?php echo esc_attr($custom_field_id); ?>" <?php echo $role_select_value === '__custom__' ? '' : 'hidden'; ?>>
1744 <label class="mxch-field-label" for="<?php echo esc_attr($custom_field_id); ?>"><?php esc_html_e('Custom limit', 'mxchat'); ?></label>
1745 <input type="number"
1746 min="1"
1747 step="1"
1748 id="<?php echo esc_attr($custom_field_id); ?>"
1749 name="mxchat_options[rate_limits][<?php echo esc_attr($role_id); ?>][limit_custom]"
1750 class="mxch-input mxchat-autosave-field mxch-rate-limit-custom-input"
1751 placeholder="e.g. 250"
1752 value="<?php echo esc_attr($role_custom_value); ?>" />
1753 </div>
1754 <div class="mxch-field">
1755 <label class="mxch-field-label" for="rate_limits_<?php echo esc_attr($role_id); ?>_timeframe"><?php esc_html_e('Timeframe', 'mxchat'); ?></label>
1756 <select id="rate_limits_<?php echo esc_attr($role_id); ?>_timeframe"
1757 name="mxchat_options[rate_limits][<?php echo esc_attr($role_id); ?>][timeframe]"
1758 class="mxch-select mxchat-autosave-field">
1759 <?php foreach ($timeframes as $value => $label): ?>
1760 <option value="<?php echo esc_attr($value); ?>" <?php selected($selected_timeframe, $value); ?>><?php echo esc_html($label); ?></option>
1761 <?php endforeach; ?>
1762 </select>
1763 </div>
1764 </div>
1765 <div class="mxch-field">
1766 <label class="mxch-field-label" for="rate_limits_<?php echo esc_attr($role_id); ?>_message"><?php esc_html_e('Custom Message', 'mxchat'); ?></label>
1767 <textarea id="rate_limits_<?php echo esc_attr($role_id); ?>_message"
1768 name="mxchat_options[rate_limits][<?php echo esc_attr($role_id); ?>][message]"
1769 class="mxch-textarea mxchat-autosave-field"
1770 rows="2"
1771 placeholder="<?php esc_attr_e('Message shown when rate limit is reached', 'mxchat'); ?>"><?php echo esc_textarea($custom_message); ?></textarea>
1772 </div>
1773 </div>
1774 </div>
1775 <?php
1776 }
1777
1778 echo '</div>';
1779 }
1780