PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / trunk
MxChat – AI Chatbot & Content Generation for WordPress vtrunk
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
← All changes | js/mxchat_actions.js +422 -8 3.2.2trunk View file →
@@ -59,13 +59,427 @@
59 59 $('.mxch-mobile-menu').removeClass('open');
60 60 $('.mxch-mobile-overlay').removeClass('open');
61 61 });
62 62
63 + // ==========================================================================
64 + // AI Tools: autosave the function-calling toggle + per-tool checklist
65 + // (matches MxChat's autosave UX — no Save button). plan a41dee.
66 + // ==========================================================================
67 + var $fc = $('#mxch-fc-settings');
68 + if ($fc.length) {
69 + var fcNonce = $fc.data('fc-nonce');
70 + var $fcStatus = $('#mxch-fc-save-status');
71 + var fcTimer = null;
72 +
73 + function fcCollectAndSave() {
74 + // Presence = active (plan d450a7): the global on/off toggle is gone, so
75 + // the gate is derived from whether any tool is enabled. The server
76 + // derives this too; we send it for an immediate, correct nav badge.
77 + var enabled = $fc.find('input[name="mxchat_fc_tools[]"]:checked').length > 0 ? '1' : '0';
78 + var allTools = $fc.find('input[name="mxchat_fc_all_tools[]"]').map(function () { return $(this).val(); }).get();
79 + var checked = $fc.find('input[name="mxchat_fc_tools[]"]:checked').map(function () { return $(this).val(); }).get();
80 + // Per-tool "when to use" hints, keyed by callback.
81 + var hints = {};
82 + $fc.find('.mxch-fc-hint-input').each(function () {
83 + var cb = $(this).data('fc-callback');
84 + if (cb) { hints[cb] = $(this).val(); }
85 + });
86 +
87 + if ($fcStatus.length) { $fcStatus.text('Saving…').removeClass('is-saved is-error'); }
88 +
89 + $.post(mxchActionsData.ajaxUrl, {
90 + action: 'mxchat_fc_autosave',
91 + nonce: fcNonce,
92 + enabled: enabled,
93 + all_tools: allTools,
94 + tools: checked,
95 + hints: hints
96 + }).done(function () {
97 + if ($fcStatus.length) {
98 + $fcStatus.text('Saved').addClass('is-saved');
99 + setTimeout(function () { $fcStatus.text('').removeClass('is-saved'); }, 2000);
100 + }
101 + }).fail(function () {
102 + if ($fcStatus.length) { $fcStatus.text('Save failed — try again').addClass('is-error'); }
103 + });
104 + }
105 +
106 + // The hidden store's checkboxes are toggled programmatically by the
107 + // list/detail/modal flow (which calls fcCollectAndSave() directly). Keep
108 + // a change-bound autosave as a safety net for any direct mutation.
109 + $fc.on('change', 'input[name="mxchat_fc_tools[]"]', function () {
110 + clearTimeout(fcTimer);
111 + fcTimer = setTimeout(fcCollectAndSave, 200);
112 + });
113 +
114 + // ======================================================================
115 + // AI Tools MASTER-DETAIL list + detail panel (plan d450a7).
116 + // Clones the Trigger Phrases list/detail UX: the left list shows the
117 + // ACTIVE tools (a tool in the list = active — no global toggle, no
118 + // per-tool enable checkbox); selecting one shows its detail on the right
119 + // with Edit (usage note) + Remove. "Add" opens the same two-step
120 + // capability picker. Pure view layer over the hidden #mxch-fc-tool-store
121 + // inputs — the unchanged mxchat_fc_autosave contract (mxchat_fc_tools[] /
122 + // mxchat_fc_all_tools[] + hints) stays the source of truth. FC-specific
123 + // IDs/classes keep the Trigger Phrases JS off these elements.
124 + // ======================================================================
125 + var $fcStore = $('#mxch-fc-tool-store');
126 + if ($fcStore.length) {
127 + var $fcPanel = $('#mxch-fc-panel');
128 + var $fcList = $('#mxch-fc-list');
129 + var $fcCount = $('#mxch-fc-count');
130 + var $fcEmpty = $('#mxch-fc-empty');
131 + var $fcView = $('#mxch-fc-view');
132 + var $fcDetail = $('#mxch-fc-detail-panel');
133 + var $fcSearch = $('#mxch-fc-search');
134 + var $fcModal = $('#mxch-fc-tool-modal');
135 + var fcCurrentCb = null; // tool being added/edited in the modal
136 + var fcViewCb = null; // tool currently shown in the detail panel
137 + var fcGuardedClose = null; // dirty-checked close, re-armed on each open
138 + var fcSelected = new Set(); // callbacks ticked for bulk remove (plan 5f7409)
139 +
140 + var fcI18n = {
141 + tools: $fcPanel.data('i18n-tools') || 'tools',
142 + tool: $fcPanel.data('i18n-tool') || 'tool',
143 + active: $fcPanel.data('i18n-active') || 'Active',
144 + addon: $fcPanel.data('i18n-addon') || 'Add-on',
145 + sensitive: $fcPanel.data('i18n-sensitive') || 'Sensitive',
146 + noHint: $fcPanel.data('i18n-nohint') || 'No usage note — the AI decides on its own when to use this.'
147 + };
148 + // Localized Add/Edit titles + button labels read from the modal DOM.
149 + var $fcStep2Title = $('#mxch-fc-modal-step2-title');
150 + var $fcSaveBtn = $('#mxch-fc-modal-save');
151 + var FC_TITLE_ADD = $fcStep2Title.data('add-title') || 'Add Tool';
152 + var FC_TITLE_EDIT = $fcStep2Title.data('edit-title') || 'Edit Tool';
153 + var FC_BTN_ADD = $fcSaveBtn.data('add-label') || 'Add Tool';
154 + var FC_BTN_SAVE = $fcSaveBtn.data('save-label') || 'Save Changes';
155 +
156 + function fcEsc(s) { return $('<div>').text(s == null ? '' : String(s)).html(); }
157 + function fcEntry(cb) {
158 + return $fcStore.find('.mxch-fc-tool').filter(function () {
159 + return String($(this).data('fc-callback')) === String(cb);
160 + });
161 + }
162 + function fcIsOn($t) { return $t.find('input[name="mxchat_fc_tools[]"]').is(':checked'); }
163 + function fcIsMobile() { return window.innerWidth <= 782; }
164 +
165 + // Show the empty "Select a tool" state in the detail panel.
166 + function fcShowEmpty() {
167 + fcViewCb = null;
168 + if ($fcView.length) $fcView.hide();
169 + if ($fcEmpty.length) $fcEmpty.show();
170 + $fcList.find('.mxch-fc-list-item').removeClass('active');
171 + if (fcIsMobile()) { $fcDetail.removeClass('mobile-active'); $('body').removeClass('mxch-mobile-panel-open'); }
172 + }
173 +
174 + // Build the left list from the store (active tools only), applying the
175 + // current search filter. Updates the count + resets the view if the
176 + // shown tool was just removed.
177 + function fcRenderList() {
178 + if (!$fcList.length) return;
179 + var q = $.trim(($fcSearch.val() || '')).toLowerCase();
180 + $fcList.empty();
181 + var total = 0;
182 + $fcStore.find('.mxch-fc-tool').each(function () {
183 + var $t = $(this);
184 + if (!fcIsOn($t)) return;
185 + total++;
186 + var cb = $t.data('fc-callback');
187 + var label = $t.data('fc-label') || cb;
188 + var desc = $t.data('fc-desc') || '';
189 + var icon = $t.data('fc-icon') || 'admin-generic';
190 + var isAddon = String($t.data('fc-addon')) === '1';
191 + var isCautious = String($t.data('fc-cautious')) === '1';
192 + if (q && String(label).toLowerCase().indexOf(q) === -1 && String(desc).toLowerCase().indexOf(q) === -1) return;
193 + var typeBits = [];
194 + if (isAddon) typeBits.push(fcI18n.addon);
195 + if (isCautious) typeBits.push(fcI18n.sensitive);
196 + var typeLabel = typeBits.length ? typeBits.join(' · ') : fcI18n.active;
197 + var activeCls = (String(cb) === String(fcViewCb)) ? ' active' : '';
198 + var isSel = fcSelected.has(String(cb));
199 + var selCls = isSel ? ' selected' : '';
200 + $fcList.append(
201 + '<div class="mxch-fc-list-item' + activeCls + selCls + '" data-fc-callback="' + fcEsc(cb) + '">' +
202 + '<input type="checkbox" class="mxch-fc-checkbox"' + (isSel ? ' checked' : '') + '>' +
203 + '<div class="mxch-action-icon"><span class="dashicons dashicons-' + fcEsc(icon) + '"></span></div>' +
204 + '<div class="mxch-action-info">' +
205 + '<div class="mxch-action-name">' + fcEsc(label) + '</div>' +
206 + '<div class="mxch-action-type-label">' + fcEsc(typeLabel) + '</div>' +
207 + '</div>' +
208 + '<div class="mxch-action-meta">' +
209 + '<span class="mxch-action-status enabled">' + fcEsc(fcI18n.active) + '</span>' +
210 + '</div>' +
211 + '</div>'
212 + );
213 + });
214 + if ($fcCount.length) {
215 + $fcCount.text(total + ' ' + (total === 1 ? fcI18n.tool : fcI18n.tools));
216 + }
217 + // Drop any selected callbacks that are no longer in the list (e.g.
218 + // filtered out or removed) so the bulk count stays truthful.
219 + var fcPresent = {};
220 + $fcList.find('.mxch-fc-list-item').each(function () {
221 + fcPresent[String($(this).data('fc-callback'))] = true;
222 + });
223 + fcSelected.forEach(function (selCb) {
224 + if (!fcPresent[String(selCb)]) { fcSelected.delete(selCb); }
225 + });
226 + fcUpdateSelectionUI();
227 + // If the tool currently in the detail panel is no longer active, reset.
228 + if (fcViewCb && !fcIsOn(fcEntry(fcViewCb))) { fcShowEmpty(); }
229 + }
230 +
231 + // Populate + show the detail view for one tool.
232 + function fcSelectTool(cb) {
233 + var $t = fcEntry(cb);
234 + if (!$t.length || !fcIsOn($t)) { fcShowEmpty(); return; }
235 + fcViewCb = String(cb);
236 + var label = $t.data('fc-label') || cb;
237 + var desc = $t.data('fc-desc') || '';
238 + var icon = $t.data('fc-icon') || 'admin-generic';
239 + var isAddon = String($t.data('fc-addon')) === '1';
240 + var isCautious = String($t.data('fc-cautious')) === '1';
241 + var hint = $.trim($t.find('.mxch-fc-hint-input').val() || '');
242 + var typeBits = [];
243 + if (isAddon) typeBits.push(fcI18n.addon);
244 + if (isCautious) typeBits.push(fcI18n.sensitive);
245 + $('#mxch-fc-view-icon').html('<span class="dashicons dashicons-' + fcEsc(icon) + '"></span>');
246 + $('#mxch-fc-view-label').text(label);
247 + $('#mxch-fc-view-type').text(typeBits.join(' · '));
248 + $('#mxch-fc-view-desc').text(desc);
249 + var $hint = $('#mxch-fc-view-hint');
250 + if (hint) { $hint.text(hint).removeClass('is-muted'); }
251 + else { $hint.text(fcI18n.noHint).addClass('is-muted'); }
252 + // Setup requirement (plan 183856): show the tool's setup note (e.g. the
253 + // Brave-key requirement on Web/Image Search) when present, else hide it.
254 + var setup = $.trim(String($t.data('fc-setup') || ''));
255 + var $setupWrap = $('#mxch-fc-view-setup-wrap');
256 + if (setup) { $('#mxch-fc-view-setup').text(setup); $setupWrap.show(); }
257 + else { $setupWrap.hide(); }
258 + $fcEmpty.hide();
259 + $fcView.show();
260 + $fcList.find('.mxch-fc-list-item').removeClass('active')
261 + .filter(function () { return String($(this).data('fc-callback')) === String(cb); }).addClass('active');
262 + if (fcIsMobile()) { $fcDetail.addClass('mobile-active'); $('body').addClass('mxch-mobile-panel-open'); }
263 + }
264 +
265 + // ---- Add/Edit modal (two-step capability picker) ----
266 + function fcShowStep(n) {
267 + $fcModal.find('.mxch-fc-modal-step').hide();
268 + $fcModal.find('.mxch-fc-modal-step[data-step="' + n + '"]').show();
269 + }
270 + function fcCloseModal() { $fcModal.hide(); fcCurrentCb = null; fcGuardedClose = null; }
271 +
272 + // The modal holds an editable "when to use" hint, so it never closes on the
273 + // backdrop and confirms before discarding. fcArmGuard must run AFTER a step
274 + // populates its fields: mxchatGuardedClose snapshots at call time, so arming
275 + // it earlier would measure "dirty" against empty and confirm on every close.
276 + function fcArmGuard() {
277 + if (typeof mxchatGuardedClose !== 'function' || !$fcModal.length) return;
278 + fcGuardedClose = mxchatGuardedClose($fcModal[0], fcCloseModal);
279 + }
280 + function fcRequestClose(e) { (fcGuardedClose || fcCloseModal)(e); }
281 +
282 + // Step 1: grid of tools not yet added.
283 + function fcOpenAddModal() {
284 + // Back-to-step-1 keeps the step-2 baseline: re-arming here would treat a
285 + // hint typed before Back as clean and discard it silently on close.
286 + var fcWasOpen = $fcModal.is(':visible');
287 + var $grid = $('#mxch-fc-modal-grid').empty();
288 + var addable = 0;
289 + $fcStore.find('.mxch-fc-tool').each(function () {
290 + var $t = $(this);
291 + if (fcIsOn($t)) return; // already active
292 + addable++;
293 + var cb = $t.data('fc-callback');
294 + var label = $t.data('fc-label') || cb;
295 + var desc = $t.data('fc-desc') || '';
296 + var icon = $t.data('fc-icon') || 'admin-generic';
297 + var isAddon = String($t.data('fc-addon')) === '1';
298 + var isCautious = String($t.data('fc-cautious')) === '1';
299 + var badges = '';
300 + if (isAddon) { badges += ' <span class="mxch-fc-badge mxch-fc-badge-addon">' + fcEsc(fcI18n.addon) + '</span>'; }
301 + if (isCautious) { badges += ' <span class="mxch-fc-badge mxch-fc-badge-sensitive">' + fcEsc(fcI18n.sensitive) + '</span>'; }
302 + $grid.append(
303 + '<div class="mxch-type-card mxch-fc-type-card" data-fc-callback="' + fcEsc(cb) + '">' +
304 + '<div class="mxch-type-icon"><span class="dashicons dashicons-' + fcEsc(icon) + '"></span></div>' +
305 + '<div class="mxch-type-info">' +
306 + '<h4>' + fcEsc(label) + badges + '</h4>' +
307 + '<p>' + fcEsc(desc) + '</p>' +
308 + '</div>' +
309 + '</div>'
310 + );
311 + });
312 + $('#mxch-fc-modal-allset').toggle(addable === 0);
313 + $grid.toggle(addable > 0);
314 + fcShowStep(1);
315 + $fcModal.css('display', 'flex');
316 + if (!fcWasOpen) fcArmGuard();
317 + }
318 +
319 + // Step 2: confirm + when-to-use (shared by Add and Edit).
320 + function fcOpenConfigStep(cb) {
321 + var $t = fcEntry(cb);
322 + if (!$t.length) return;
323 + fcCurrentCb = cb;
324 + var editing = fcIsOn($t);
325 + $('#mxch-fc-modal-selected-icon').html('<span class="dashicons dashicons-' + fcEsc($t.data('fc-icon') || 'admin-generic') + '"></span>');
326 + $('#mxch-fc-modal-selected-label').text($t.data('fc-label') || cb);
327 + $('#mxch-fc-modal-selected-desc').text($t.data('fc-desc') || '');
328 + $('#mxch-fc-modal-hint').val($t.find('.mxch-fc-hint-input').val() || '');
329 + $('#mxch-fc-modal-sensitive').toggle(String($t.data('fc-cautious')) === '1');
330 + $fcStep2Title.text(editing ? FC_TITLE_EDIT : FC_TITLE_ADD);
331 + $fcSaveBtn.text(editing ? FC_BTN_SAVE : FC_BTN_ADD);
332 + fcShowStep(2);
333 + $fcModal.css('display', 'flex');
334 + fcArmGuard(); // after the hint is loaded: that value is the clean baseline
335 + }
336 +
337 + // ---- wiring ----
338 + // "Add" buttons (panel header + empty state) — delegated on $fc.
339 + $fc.on('click', '.js-mxch-fc-add', fcOpenAddModal);
340 + $fcModal.on('click', '.mxch-fc-type-card', function () { fcOpenConfigStep($(this).data('fc-callback')); });
341 + $('#mxch-fc-modal-back').on('click', fcOpenAddModal);
342 + $fcModal.on('click', '[data-fc-modal-close]', fcRequestClose); // X + Cancel
343 + // No backdrop close: a click dispatches on the common ancestor of its mousedown
344 + // and mouseup, so releasing a selection past the dialog edge targets the backdrop
345 + // and would discard the hint — and a plain backdrop click discarded it outright.
346 + $(document).on('keydown', function (e) { if (e.key === 'Escape' && $fcModal.is(':visible')) fcRequestClose(e); });
347 +
348 + // Modal Save (Add or Edit): write the store inputs, autosave, refresh
349 + // the list, and show the tool's detail on the right.
350 + $fcSaveBtn.on('click', function () {
351 + if (!fcCurrentCb) return;
352 + var $t = fcEntry(fcCurrentCb);
353 + if (!$t.length) return;
354 + var cb = fcCurrentCb;
355 + $t.find('.mxch-fc-hint-input').val($('#mxch-fc-modal-hint').val() || '');
356 + $t.find('input[name="mxchat_fc_tools[]"]').prop('checked', true);
357 + fcCloseModal();
358 + fcRenderList();
359 + fcSelectTool(cb);
360 + fcCollectAndSave();
361 + });
362 +
363 + // List item click → show that tool's detail.
364 + $fcList.on('click', '.mxch-fc-list-item', function () {
365 + fcSelectTool($(this).data('fc-callback'));
366 + });
367 +
368 + // Detail Edit → reopen the config step for the viewed tool.
369 + $('#mxch-fc-edit-btn').on('click', function () {
370 + if (fcViewCb) fcOpenConfigStep(fcViewCb);
371 + });
372 + // Detail Remove → disable the tool (keep its hint for a later re-add),
373 + // refresh the list, and return to the empty state.
374 + $('#mxch-fc-remove-btn').on('click', function () {
375 + if (!fcViewCb) return;
376 + var $t = fcEntry(fcViewCb);
377 + if (!$t.length) return;
378 + $t.find('input[name="mxchat_fc_tools[]"]').prop('checked', false);
379 + fcShowEmpty();
380 + fcRenderList();
381 + fcCollectAndSave();
382 + });
383 +
384 + // ---- Bulk selection + bulk remove ----
385 + // Clones the Trigger Phrases #all-actions bulk toolbar behavior, but
386 + // wires removal to the SAME path the single Remove button uses (uncheck
387 + // each tool's store checkbox, then one autosave). FC-specific IDs keep
388 + // this isolated from the Trigger Phrases bulk JS (plan 5f7409).
389 + var $fcSelectAll = $('#mxch-fc-select-all');
390 + var $fcBulkDelete = $('#mxch-fc-delete-selected');
391 + var $fcSelCount = $('#mxch-fc-selected-count');
392 +
393 + function fcUpdateSelectionUI() {
394 + var count = fcSelected.size;
395 + if (count > 0) {
396 + $fcSelCount.text(count + ' selected').addClass('has-selection');
397 + $fcBulkDelete.prop('disabled', false);
398 + $fcList.addClass('selection-mode');
399 + } else {
400 + $fcSelCount.removeClass('has-selection');
401 + $fcBulkDelete.prop('disabled', true);
402 + $fcList.removeClass('selection-mode');
403 + }
404 + var totalItems = $fcList.find('.mxch-fc-checkbox').length;
405 + var checkedItems = $fcList.find('.mxch-fc-checkbox:checked').length;
406 + $fcSelectAll.prop('checked', totalItems > 0 && checkedItems === totalItems);
407 + $fcSelectAll.prop('indeterminate', checkedItems > 0 && checkedItems < totalItems);
408 + }
409 +
410 + // Select-all toggles every visible tool row.
411 + $fcSelectAll.on('change', function () {
412 + var on = $(this).is(':checked');
413 + fcSelected.clear();
414 + $fcList.find('.mxch-fc-list-item').each(function () {
415 + var $row = $(this);
416 + var cb = String($row.data('fc-callback'));
417 + $row.find('.mxch-fc-checkbox').prop('checked', on);
418 + if (on) { fcSelected.add(cb); $row.addClass('selected'); }
419 + else { $row.removeClass('selected'); }
420 + });
421 + fcUpdateSelectionUI();
422 + });
423 +
424 + // Per-row checkbox: toggle selection without opening the detail view.
425 + $fcList.on('click', '.mxch-fc-checkbox', function (e) {
426 + e.stopPropagation();
427 + var $row = $(this).closest('.mxch-fc-list-item');
428 + var cb = String($row.data('fc-callback'));
429 + if ($(this).is(':checked')) { fcSelected.add(cb); $row.addClass('selected'); }
430 + else { fcSelected.delete(cb); $row.removeClass('selected'); }
431 + fcUpdateSelectionUI();
432 + });
433 +
434 + // Bulk remove: same removal path as single Remove — uncheck each tool's
435 + // store checkbox (presence = active), then refresh + autosave once.
436 + $fcBulkDelete.on('click', function () {
437 + if (!fcSelected.size) return;
438 + var msg = (window.mxchActionsData && mxchActionsData.i18n && mxchActionsData.i18n.confirmBulkDelete)
439 + || 'Remove the selected tools? They stay available to add back later.';
440 + if (!window.confirm(msg)) return;
441 + var removingViewed = false;
442 + fcSelected.forEach(function (cb) {
443 + var $t = fcEntry(cb);
444 + if ($t.length) { $t.find('input[name="mxchat_fc_tools[]"]').prop('checked', false); }
445 + if (String(cb) === String(fcViewCb)) { removingViewed = true; }
446 + });
447 + fcSelected.clear();
448 + if (removingViewed) { fcShowEmpty(); }
449 + fcRenderList();
450 + fcCollectAndSave();
451 + });
452 +
453 + // Search filter.
454 + $fcSearch.on('input', function () { fcRenderList(); });
455 +
456 + // Refresh — re-render the list from the store (parity with the Trigger
457 + // Phrases refresh affordance; the list is client-built so this re-syncs
458 + // the view rather than re-fetching).
459 + $('#mxch-fc-refresh').on('click', function () {
460 + var $btn = $(this);
461 + $btn.addClass('spinning');
462 + fcRenderList();
463 + setTimeout(function () { $btn.removeClass('spinning'); }, 500);
464 + });
465 +
466 + fcRenderList();
467 + }
468 + }
469 +
63 470 // Quick action buttons
64 471 $('.mxch-quick-action-btn[data-action="view-actions"]').on('click', function() {
65 472 $('.mxch-nav-link[data-target="all-actions"]').trigger('click');
66 473 });
67 474
475 + // Dashboard explainer cards — generic "jump to this section" (e.g. Set up AI Tools).
476 + $('.mxch-approach-btn[data-approach-target]').on('click', function() {
477 + var target = $(this).data('approach-target');
478 + $('.mxch-nav-link[data-target="' + target + '"]').trigger('click');
479 + });
480 +
481 + // "Add Trigger Phrase" on the dashboard → jump to Trigger Phrases + open the editor.
68 482 $('#mxch-add-action-dashboard-btn').on('click', function() {
69 483 $('.mxch-nav-link[data-target="all-actions"]').trigger('click');
70 484 setTimeout(function() {
71 485 openActionEditor();
@@ -432,15 +846,15 @@
432 846 totalPages = response.data.total_pages;
433 847 updateActionCount(response.data.showing_start, response.data.showing_end, response.data.total_actions);
434 848 renderPagination(response.data.page, response.data.total_pages, searchTerm, filter);
435 849 } else {
436 - $container.html('<div class="mxch-list-empty"><p>No actions found</p></div>');
850 + $container.html('<div class="mxch-list-empty"><p>No trigger phrases found</p></div>');
437 851 updateActionCount(0, 0, 0);
438 852 $('#mxch-actions-pagination').html('');
439 853 }
440 854 },
441 855 error: function() {
442 - $container.html('<div class="mxch-list-empty"><p>Error loading actions</p></div>');
856 + $container.html('<div class="mxch-list-empty"><p>Error loading trigger phrases</p></div>');
443 857 }
444 858 });
445 859 }
446 860
@@ -520,11 +934,11 @@
520 934
521 935 // Update action count display
522 936 function updateActionCount(start, end, total) {
523 937 if (total === 0) {
524 - $('#mxch-action-count').text('0 actions');
938 + $('#mxch-action-count').text('0 trigger phrases');
525 939 } else {
526 - $('#mxch-action-count').text(`${start}-${end} / ${total} actions`);
940 + $('#mxch-action-count').text(`${start}-${end} / ${total} trigger phrases`);
527 941 }
528 942 }
529 943
530 944 // Render pagination
@@ -709,9 +1123,9 @@
709 1123
710 1124 $('#mxch-selected-type-icon').html(iconHtml);
711 1125 $('#mxch-selected-type-label').text(typeLabel);
712 1126 $('#mxch-selected-type-desc').text(typeDesc);
713 - $('#mxch-config-title').text('Edit Action');
1127 + $('#mxch-config-title').text('Edit Trigger Phrase');
714 1128
715 1129 // Set enabled bots
716 1130 const enabledBots = Array.isArray(action.enabled_bots) ? action.enabled_bots : ['default'];
717 1131 $('#mxch-bot-selector input[type="checkbox"]').each(function() {
@@ -718,9 +1132,9 @@
718 1132 $(this).prop('checked', enabledBots.includes($(this).val()));
719 1133 });
720 1134
721 1135 // Update save button text
722 - $('#mxch-save-action').text('Update Action');
1136 + $('#mxch-save-action').text('Update Trigger Phrase');
723 1137 }
724 1138
725 1139 // Reset form
726 1140 function resetForm() {
@@ -733,10 +1147,10 @@
733 1147 // Clear phrase tags
734 1148 currentPhrases = [];
735 1149 renderPhraseTags();
736 1150 $('#mxch-threshold-value').text('85%');
737 - $('#mxch-config-title').text('Configure Action');
738 - $('#mxch-save-action').text('Save Action');
1151 + $('#mxch-config-title').text('Configure Trigger Phrase');
1152 + $('#mxch-save-action').text('Save Trigger Phrase');
739 1153
740 1154 // Reset bot selection
741 1155 $('#mxch-bot-selector input[type="checkbox"]').prop('checked', false);
742 1156 $('#mxch-bot-selector input[value="default"]').prop('checked', true);