| @@ -73,9 +73,9 @@ | ||
| 73 | 73 | <span><?php esc_html_e('Auto-Sync', 'mxchat'); ?></span> |
| 74 | 74 | </button> |
| 75 | 75 | <button class="mxch-mobile-nav-link" data-target="chunking"> |
| 76 | 76 | <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"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg> |
| 77 | - <span><?php esc_html_e('Chunking & Retrieval', 'mxchat'); ?></span> | |
| 77 | + <span><?php esc_html_e('Content Chunking', 'mxchat'); ?></span> | |
| 78 | 78 | </button> |
| 79 | 79 | <button class="mxch-mobile-nav-link" data-target="role-restrictions"> |
| 80 | 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="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg> |
| 81 | 81 | <span><?php esc_html_e('Role Restrictions', 'mxchat'); ?></span> |
| @@ -172,9 +172,9 @@ | ||
| 172 | 172 | <button class="mxch-nav-link" data-target="chunking"> |
| 173 | 173 | <span class="mxch-nav-link-icon"> |
| 174 | 174 | <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"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg> |
| 175 | 175 | </span> |
| 176 | - <span class="mxch-nav-link-text"><?php esc_html_e('Chunking & Retrieval', 'mxchat'); ?></span> | |
| 176 | + <span class="mxch-nav-link-text"><?php esc_html_e('Content Chunking', 'mxchat'); ?></span> | |
| 177 | 177 | </button> |
| 178 | 178 | </div> |
| 179 | 179 | |
| 180 | 180 | <div class="mxch-nav-item" data-section="role-restrictions"> |
| @@ -272,14 +272,8 @@ | ||
| 272 | 272 | <?php |
| 273 | 273 | // Render the Content Selector Modal |
| 274 | 274 | mxchat_render_content_selector_modal(); |
| 275 | 275 | |
| 276 | - // Render the Edit Entry Modal | |
| 277 | - mxchat_render_edit_entry_modal(); | |
| 278 | - | |
| 279 | - // Render the read-only Knowledge Inspector modal (plan-d8cb4b) | |
| 280 | - mxchat_render_inspect_entry_modal(); | |
| 281 | - | |
| 282 | 276 | // Render the navigation JavaScript |
| 283 | 277 | mxchat_render_knowledge_page_scripts(); |
| 284 | 278 | } |
| 285 | 279 | |
| @@ -534,40 +528,18 @@ | ||
| 534 | 528 | <p><?php esc_html_e('Submit content to be vectorized.', 'mxchat'); ?></p> |
| 535 | 529 | </div> |
| 536 | 530 | </button> |
| 537 | 531 | |
| 538 | - <!-- PDF Import Option (URL) --> | |
| 539 | - <button type="button" class="mxchat-import-box" data-option="pdf-url" data-placeholder="<?php esc_attr_e('Enter PDF URL here', 'mxchat'); ?>" data-type="pdf"> | |
| 532 | + <!-- PDF Import Option --> | |
| 533 | + <button type="button" class="mxchat-import-box" data-option="pdf" data-placeholder="<?php esc_attr_e('Enter PDF URL here', 'mxchat'); ?>" data-type="pdf"> | |
| 540 | 534 | <div class="mxchat-import-icon"> |
| 541 | 535 | <span class="dashicons dashicons-media-document"></span> |
| 542 | 536 | </div> |
| 543 | 537 | <div class="mxchat-import-content"> |
| 544 | 538 | <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4> |
| 545 | - <p><?php esc_html_e('Import knowledge from PDF URL.', 'mxchat'); ?></p> | |
| 539 | + <p><?php esc_html_e('Import knowledge from PDF documents.', 'mxchat'); ?></p> | |
| 546 | 540 | </div> |
| 547 | 541 | </button> |
| 548 | - | |
| 549 | - <!-- PDF File Upload Option --> | |
| 550 | - <button type="button" class="mxchat-import-box" data-option="pdf-upload" data-type="pdf-upload"> | |
| 551 | - <div class="mxchat-import-icon"> | |
| 552 | - <span class="dashicons dashicons-upload"></span> | |
| 553 | - </div> | |
| 554 | - <div class="mxchat-import-content"> | |
| 555 | - <h4><?php esc_html_e('PDF Upload', 'mxchat'); ?></h4> | |
| 556 | - <p><?php esc_html_e('Upload a PDF file from your computer.', 'mxchat'); ?></p> | |
| 557 | - </div> | |
| 558 | - </button> | |
| 559 | - | |
| 560 | - <!-- YouTube Video Import Option --> | |
| 561 | - <button type="button" class="mxchat-import-box" data-option="youtube" data-type="youtube"> | |
| 562 | - <div class="mxchat-import-icon"> | |
| 563 | - <span class="dashicons dashicons-youtube"></span> | |
| 564 | - </div> | |
| 565 | - <div class="mxchat-import-content"> | |
| 566 | - <h4><?php esc_html_e('YouTube', 'mxchat'); ?></h4> | |
| 567 | - <p><?php esc_html_e('Index a video so the chat can surface it as a playable embed.', 'mxchat'); ?></p> | |
| 568 | - </div> | |
| 569 | - </button> | |
| 570 | 542 | </div> |
| 571 | 543 | |
| 572 | 544 | <!-- Detected Sitemaps Section (hidden by default, shown when Sitemap Import is clicked) --> |
| 573 | 545 | <div id="mxchat-detected-sitemaps" class="mxchat-detected-sitemaps" style="display: none;"> |
| @@ -651,72 +623,8 @@ | ||
| 651 | 623 | <?php esc_html_e('Import Content', 'mxchat'); ?> |
| 652 | 624 | </button> |
| 653 | 625 | </form> |
| 654 | 626 | </div> |
| 655 | - | |
| 656 | - <div class="mxchat-import-input-area" id="mxchat-pdf-upload-area" style="display: none; margin-top: 20px;"> | |
| 657 | - <form id="mxchat-pdf-upload-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_pdf_file')); ?>" enctype="multipart/form-data"> | |
| 658 | - <?php wp_nonce_field('mxchat_submit_pdf_file_action', 'mxchat_submit_pdf_file_nonce'); ?> | |
| 659 | - <?php if ($multibot_active && $current_bot_id !== 'default') : ?> | |
| 660 | - <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>"> | |
| 661 | - <?php endif; ?> | |
| 662 | - <div class="mxch-field"> | |
| 663 | - <input type="file" name="pdf_file" id="mxchat-pdf-file-input" accept=".pdf" required> | |
| 664 | - </div> | |
| 665 | - <p class="mxch-field-description"> | |
| 666 | - <?php esc_html_e('Select a PDF file from your computer to import into the knowledge base. Maximum file size depends on your server settings.', 'mxchat'); ?> | |
| 667 | - </p> | |
| 668 | - <button type="submit" name="submit_pdf_file" class="mxch-btn mxch-btn-primary"> | |
| 669 | - <?php esc_html_e('Import PDF', 'mxchat'); ?> | |
| 670 | - </button> | |
| 671 | - </form> | |
| 672 | - </div> | |
| 673 | - | |
| 674 | - <?php | |
| 675 | - // Prefill state: set when an auto-import found no transcript — the | |
| 676 | - // handler bounced back here so the admin can augment the entry with | |
| 677 | - // a manual description (re-importing the same URL updates the row). | |
| 678 | - $yt_prefill = isset($_GET['mxchat_yt_prefill']) && $_GET['mxchat_yt_prefill'] === '1'; | |
| 679 | - $yt_prefill_url = $yt_prefill && isset($_GET['yt_url']) ? esc_url_raw(rawurldecode(wp_unslash($_GET['yt_url']))) : ''; | |
| 680 | - $yt_prefill_title = $yt_prefill && isset($_GET['yt_title']) ? sanitize_text_field(rawurldecode(wp_unslash($_GET['yt_title']))) : ''; | |
| 681 | - ?> | |
| 682 | - <div class="mxchat-import-input-area" id="mxchat-youtube-input-area" style="display: none; margin-top: 20px;"> | |
| 683 | - <form id="mxchat-youtube-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_youtube')); ?>"<?php echo $yt_prefill ? ' data-yt-prefill="1"' : ''; ?>> | |
| 684 | - <?php wp_nonce_field('mxchat_submit_youtube_action', 'mxchat_submit_youtube_nonce'); ?> | |
| 685 | - <?php if ($multibot_active && $current_bot_id !== 'default') : ?> | |
| 686 | - <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>"> | |
| 687 | - <?php endif; ?> | |
| 688 | - <div class="mxch-field"> | |
| 689 | - <input type="url" name="youtube_url" id="mxchat-youtube-url" class="mxch-input" placeholder="<?php esc_attr_e('Paste a YouTube video URL (watch, youtu.be, or Shorts link)', 'mxchat'); ?>" required value="<?php echo esc_attr($yt_prefill_url); ?>"> | |
| 690 | - </div> | |
| 691 | - <div class="mxch-field"> | |
| 692 | - <span class="mxch-field-label"><?php esc_html_e('How should this video be described for matching?', 'mxchat'); ?></span> | |
| 693 | - <div style="display: flex; flex-direction: column; gap: 6px; margin-top: 8px;"> | |
| 694 | - <label style="display: flex; align-items: baseline; gap: 8px; cursor: pointer;"> | |
| 695 | - <input type="radio" name="youtube_description_mode" value="auto" <?php checked(!$yt_prefill); ?>> | |
| 696 | - <span> | |
| 697 | - <strong><?php esc_html_e('Auto-fetch', 'mxchat'); ?></strong> | |
| 698 | - <span class="mxch-field-description" style="display: block; margin: 2px 0 0;"><?php esc_html_e('We pull the video\'s title, channel, and captions (when available) to describe it.', 'mxchat'); ?></span> | |
| 699 | - </span> | |
| 700 | - </label> | |
| 701 | - <label style="display: flex; align-items: baseline; gap: 8px; cursor: pointer;"> | |
| 702 | - <input type="radio" name="youtube_description_mode" value="manual" <?php checked($yt_prefill); ?>> | |
| 703 | - <span> | |
| 704 | - <strong><?php esc_html_e('Write my own', 'mxchat'); ?></strong> | |
| 705 | - <span class="mxch-field-description" style="display: block; margin: 2px 0 0;"><?php esc_html_e('You write the description the chatbot matches against — best for curated guides.', 'mxchat'); ?></span> | |
| 706 | - </span> | |
| 707 | - </label> | |
| 708 | - </div> | |
| 709 | - </div> | |
| 710 | - <div class="mxch-field" id="mxchat-youtube-description-field" style="<?php echo $yt_prefill ? '' : 'display: none;'; ?>"> | |
| 711 | - <textarea name="youtube_description" id="mxchat-youtube-description" class="mxch-textarea" rows="5" placeholder="<?php esc_attr_e('Describe what this video covers and the questions it answers...', 'mxchat'); ?>"><?php echo esc_textarea($yt_prefill_title !== '' ? $yt_prefill_title . "\n\n" : ''); ?></textarea> | |
| 712 | - <p class="mxch-field-description"><?php esc_html_e('This text is what gets matched against visitor questions. Importing the same video URL again updates its existing entry.', 'mxchat'); ?></p> | |
| 713 | - </div> | |
| 714 | - <button type="submit" name="submit_youtube" value="1" class="mxch-btn mxch-btn-primary"> | |
| 715 | - <?php esc_html_e('Import Video', 'mxchat'); ?> | |
| 716 | - </button> | |
| 717 | - </form> | |
| 718 | - </div> | |
| 719 | 627 | <?php endif; ?> |
| 720 | 628 | </div> |
| 721 | 629 | </div> |
| 722 | 630 | </div> |
| @@ -885,24 +793,21 @@ | ||
| 885 | 793 | <?php esc_html_e('WordPress DB', 'mxchat'); ?> |
| 886 | 794 | </span> |
| 887 | 795 | <?php endif; ?> |
| 888 | 796 | </h3> |
| 889 | - <div class="mxch-kb-header-controls"> | |
| 890 | - <form method="get" id="knowledge-search" class="mxch-kb-search-form"> | |
| 797 | + <div style="display: flex; gap: 10px; align-items: center;"> | |
| 798 | + <form method="get" id="knowledge-search" style="display: flex; gap: 10px;"> | |
| 891 | 799 | <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?> |
| 892 | 800 | <input type="hidden" name="page" value="mxchat-prompts" /> |
| 893 | 801 | <?php if ($multibot_active && !empty($current_bot_id) && $current_bot_id !== 'default') : ?> |
| 894 | 802 | <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>" /> |
| 895 | 803 | <?php endif; ?> |
| 896 | - <input type="text" name="search" class="mxch-input mxch-input-sm mxch-kb-search-input" placeholder="<?php esc_attr_e('Search...', 'mxchat'); ?>" value="<?php echo esc_attr($search_query); ?>" /> | |
| 897 | - <select name="content_type" class="mxch-select mxch-kb-type-filter" onchange="this.form.submit()"> | |
| 804 | + <input type="text" name="search" class="mxch-input mxch-input-sm" placeholder="<?php esc_attr_e('Search...', 'mxchat'); ?>" value="<?php echo esc_attr($search_query); ?>" style="width: 200px;" /> | |
| 805 | + <select name="content_type" class="mxch-select" style="width: auto;" onchange="this.form.submit()"> | |
| 898 | 806 | <option value=""><?php esc_html_e('All Types', 'mxchat'); ?></option> |
| 899 | - <?php | |
| 900 | - $post_types = get_post_types(array('public' => true), 'objects'); | |
| 901 | - foreach ($post_types as $post_type) { | |
| 902 | - echo '<option value="' . esc_attr($post_type->name) . '" ' . selected($content_type_filter, $post_type->name, false) . '>' . esc_html($post_type->label) . '</option>'; | |
| 903 | - } | |
| 904 | - ?> | |
| 807 | + <option value="post" <?php selected($content_type_filter, 'post'); ?>><?php esc_html_e('Posts', 'mxchat'); ?></option> | |
| 808 | + <option value="page" <?php selected($content_type_filter, 'page'); ?>><?php esc_html_e('Pages', 'mxchat'); ?></option> | |
| 809 | + <option value="product" <?php selected($content_type_filter, 'product'); ?>><?php esc_html_e('Products', 'mxchat'); ?></option> | |
| 905 | 810 | <option value="pdf" <?php selected($content_type_filter, 'pdf'); ?>><?php esc_html_e('PDFs', 'mxchat'); ?></option> |
| 906 | 811 | <option value="url" <?php selected($content_type_filter, 'url'); ?>><?php esc_html_e('URLs', 'mxchat'); ?></option> |
| 907 | 812 | </select> |
| 908 | 813 | </form> |
| @@ -908,39 +813,15 @@ | ||
| 908 | 813 | </form> |
| 909 | 814 | <!-- Delete Button Container - transforms between Delete All and Delete Selected --> |
| 910 | 815 | <div class="mxchat-delete-container" id="mxchat-delete-container"> |
| 911 | 816 | <!-- Delete All Form (shown when nothing selected) --> |
| 912 | - <?php | |
| 913 | - // Build confirm message and button text based on active filter | |
| 914 | - $delete_all_label = __('Delete All', 'mxchat'); | |
| 915 | - $delete_all_confirm = __('Are you sure you want to delete all knowledge?', 'mxchat'); | |
| 916 | - if (!empty($content_type_filter)) { | |
| 917 | - $filter_display = $content_type_filter; | |
| 918 | - // Try to get a human-readable label | |
| 919 | - $pt_obj = get_post_type_object($content_type_filter); | |
| 920 | - if ($pt_obj) { | |
| 921 | - $filter_display = $pt_obj->label; | |
| 922 | - } elseif ($content_type_filter === 'pdf') { | |
| 923 | - $filter_display = 'PDFs'; | |
| 924 | - } elseif ($content_type_filter === 'url') { | |
| 925 | - $filter_display = 'URLs'; | |
| 926 | - } | |
| 927 | - /* translators: %s: content type label (e.g. "Pages", "PDFs") */ | |
| 928 | - $delete_all_label = sprintf(__('Delete All %s', 'mxchat'), $filter_display); | |
| 929 | - /* translators: %s: content type label */ | |
| 930 | - $delete_all_confirm = sprintf(__('Are you sure you want to delete all %s from the knowledge base?', 'mxchat'), $filter_display); | |
| 931 | - } | |
| 932 | - ?> | |
| 933 | - <form method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>" id="mxchat-delete-all-form" onsubmit="return confirm('<?php echo esc_attr($delete_all_confirm); ?>');"> | |
| 817 | + <form method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>" id="mxchat-delete-all-form" onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all knowledge?', 'mxchat'); ?>');"> | |
| 934 | 818 | <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?> |
| 935 | 819 | <input type="hidden" name="data_source" value="<?php echo esc_attr($data_source); ?>" /> |
| 936 | 820 | <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>" /> |
| 937 | - <?php if (!empty($content_type_filter)) : ?> | |
| 938 | - <input type="hidden" name="content_type_filter" value="<?php echo esc_attr($content_type_filter); ?>" /> | |
| 939 | - <?php endif; ?> | |
| 940 | 821 | <button type="submit" class="mxch-btn mxch-btn-secondary mxch-btn-sm" style="color: var(--mxch-error);"> |
| 941 | 822 | <span class="dashicons dashicons-trash" style="font-size: 14px;"></span> |
| 942 | - <?php echo esc_html($delete_all_label); ?> | |
| 823 | + <?php esc_html_e('Delete All', 'mxchat'); ?> | |
| 943 | 824 | </button> |
| 944 | 825 | </form> |
| 945 | 826 | <!-- Delete Selected Button (shown when items selected) --> |
| 946 | 827 | <button type="button" |
| @@ -1048,32 +929,10 @@ | ||
| 1048 | 929 | <?php else : ?> |
| 1049 | 930 | <span style="color: var(--mxch-text-muted);"><?php esc_html_e('Manual Content', 'mxchat'); ?></span> |
| 1050 | 931 | <?php endif; ?> |
| 1051 | 932 | </td> |
| 1052 | - <td class="mxchat-actions-cell" style="padding: 12px 16px; white-space: nowrap;"> | |
| 933 | + <td class="mxchat-actions-cell" style="padding: 12px 16px;"> | |
| 1053 | 934 | <button type="button" |
| 1054 | - class="mxch-btn mxch-btn-ghost mxch-btn-sm mxchat-inspect-entry-btn" | |
| 1055 | - data-source-url="<?php echo esc_attr($source_url); ?>" | |
| 1056 | - data-entry-id="<?php echo esc_attr($first_prompt->id); ?>" | |
| 1057 | - data-data-source="<?php echo esc_attr($data_source); ?>" | |
| 1058 | - data-bot-id="<?php echo esc_attr($current_bot_id); ?>" | |
| 1059 | - data-nonce="<?php echo wp_create_nonce('mxchat_inspect_entry_nonce'); ?>" | |
| 1060 | - title="<?php esc_attr_e('View indexed content', 'mxchat'); ?>"> | |
| 1061 | - <span class="dashicons dashicons-visibility" style="font-size: 14px;"></span> | |
| 1062 | - </button> | |
| 1063 | - <?php if ($data_source !== 'pinecone') : ?> | |
| 1064 | - <button type="button" | |
| 1065 | - class="mxch-btn mxch-btn-ghost mxch-btn-sm mxchat-edit-entry-btn" | |
| 1066 | - data-source-url="<?php echo esc_attr($source_url); ?>" | |
| 1067 | - data-entry-id="<?php echo esc_attr($first_prompt->id); ?>" | |
| 1068 | - data-data-source="<?php echo esc_attr($data_source); ?>" | |
| 1069 | - data-bot-id="<?php echo esc_attr($current_bot_id); ?>" | |
| 1070 | - data-nonce="<?php echo wp_create_nonce('mxchat_edit_entry_nonce'); ?>" | |
| 1071 | - title="<?php esc_attr_e('Edit content', 'mxchat'); ?>"> | |
| 1072 | - <span class="dashicons dashicons-edit" style="font-size: 14px;"></span> | |
| 1073 | - </button> | |
| 1074 | - <?php endif; ?> | |
| 1075 | - <button type="button" | |
| 1076 | 935 | class="mxch-btn mxch-btn-ghost mxch-btn-sm delete-button-group" |
| 1077 | 936 | data-source-url="<?php echo esc_attr($source_url); ?>" |
| 1078 | 937 | data-chunk-count="<?php echo esc_attr($chunk_count); ?>" |
| 1079 | 938 | data-data-source="<?php echo esc_attr($data_source); ?>" |
| @@ -1208,31 +1067,9 @@ | ||
| 1208 | 1067 | <?php else : ?> |
| 1209 | 1068 | <span style="color: var(--mxch-text-muted);"><?php esc_html_e('Manual', 'mxchat'); ?></span> |
| 1210 | 1069 | <?php endif; ?> |
| 1211 | 1070 | </td> |
| 1212 | - <td style="padding: 12px 16px; white-space: nowrap;"> | |
| 1213 | - <button type="button" | |
| 1214 | - class="mxch-btn mxch-btn-ghost mxch-btn-sm mxchat-inspect-entry-btn" | |
| 1215 | - data-source-url="<?php echo esc_attr($prompt->source_url ?? ''); ?>" | |
| 1216 | - data-entry-id="<?php echo esc_attr($prompt->id); ?>" | |
| 1217 | - data-data-source="<?php echo esc_attr($data_source); ?>" | |
| 1218 | - data-bot-id="<?php echo esc_attr($current_bot_id); ?>" | |
| 1219 | - data-nonce="<?php echo wp_create_nonce('mxchat_inspect_entry_nonce'); ?>" | |
| 1220 | - title="<?php esc_attr_e('View indexed content', 'mxchat'); ?>"> | |
| 1221 | - <span class="dashicons dashicons-visibility" style="font-size: 14px;"></span> | |
| 1222 | - </button> | |
| 1223 | - <?php if ($data_source !== 'pinecone') : ?> | |
| 1224 | - <button type="button" | |
| 1225 | - class="mxch-btn mxch-btn-ghost mxch-btn-sm mxchat-edit-entry-btn" | |
| 1226 | - data-source-url="<?php echo esc_attr($prompt->source_url ?? ''); ?>" | |
| 1227 | - data-entry-id="<?php echo esc_attr($prompt->id); ?>" | |
| 1228 | - data-data-source="<?php echo esc_attr($data_source); ?>" | |
| 1229 | - data-bot-id="<?php echo esc_attr($current_bot_id); ?>" | |
| 1230 | - data-nonce="<?php echo wp_create_nonce('mxchat_edit_entry_nonce'); ?>" | |
| 1231 | - title="<?php esc_attr_e('Edit content', 'mxchat'); ?>"> | |
| 1232 | - <span class="dashicons dashicons-edit" style="font-size: 14px;"></span> | |
| 1233 | - </button> | |
| 1234 | - <?php endif; ?> | |
| 1071 | + <td style="padding: 12px 16px;"> | |
| 1235 | 1072 | <?php if ($data_source === 'pinecone') : ?> |
| 1236 | 1073 | <button type="button" class="mxch-btn mxch-btn-ghost mxch-btn-sm delete-button-ajax" data-vector-id="<?php echo esc_attr($prompt->id); ?>" data-bot-id="<?php echo esc_attr($current_bot_id); ?>" data-nonce="<?php echo wp_create_nonce('mxchat_delete_pinecone_prompt_nonce'); ?>" style="color: var(--mxch-error);"> |
| 1237 | 1074 | <span class="dashicons dashicons-trash" style="font-size: 14px;"></span> |
| 1238 | 1075 | </button> |
| @@ -1326,19 +1163,8 @@ | ||
| 1326 | 1163 | <span style="font-weight: 500;"><?php esc_html_e('Auto-sync Pages', 'mxchat'); ?></span> |
| 1327 | 1164 | </div> |
| 1328 | 1165 | </div> |
| 1329 | 1166 | |
| 1330 | - <div class="mxch-field" style="margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--mxch-card-border);"> | |
| 1331 | - <div style="display: flex; align-items: center; gap: 12px;"> | |
| 1332 | - <label class="mxchat-toggle-switch"> | |
| 1333 | - <input type="checkbox" name="mxchat_auto_sync_acf_pdfs" class="mxchat-autosave-field" value="1" data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>" <?php checked(get_option('mxchat_auto_sync_acf_pdfs', '0'), '1'); ?>> | |
| 1334 | - <span class="mxchat-toggle-slider"></span> | |
| 1335 | - </label> | |
| 1336 | - <span style="font-weight: 500;"><?php esc_html_e('Extract text from PDFs in ACF fields on save', 'mxchat'); ?></span> | |
| 1337 | - </div> | |
| 1338 | - <p class="mxch-field-description" style="margin-top: 8px;"><?php esc_html_e('When ON, editor saves re-extract every PDF referenced in ACF fields. Default OFF — saves stay fast and don\'t re-parse the same PDFs on every edit. The manual content selector has its own per-batch checkbox; this setting only controls the auto-sync path.', 'mxchat'); ?></p> | |
| 1339 | - </div> | |
| 1340 | - | |
| 1341 | 1167 | <div style="margin-top: 24px;"> |
| 1342 | 1168 | <button id="mxchat-custom-post-types-toggle" class="mxch-btn mxch-btn-secondary"> |
| 1343 | 1169 | <?php esc_html_e('Advanced Custom Post Sync Settings', 'mxchat'); ?> |
| 1344 | 1170 | <span style="margin-left: 5px;">▼</span> |
| @@ -1384,16 +1210,13 @@ | ||
| 1384 | 1210 | $chunk_size = $chunking_settings['chunk_size']; |
| 1385 | 1211 | ?> |
| 1386 | 1212 | <div id="chunking" class="mxch-section"> |
| 1387 | 1213 | <div class="mxch-content-header"> |
| 1388 | - <h1 class="mxch-content-title"><?php esc_html_e('Chunking & Retrieval', 'mxchat'); ?></h1> | |
| 1389 | - <p class="mxch-content-subtitle"><?php esc_html_e('Control how content is split for indexing and how the knowledge base is searched.', 'mxchat'); ?></p> | |
| 1214 | + <h1 class="mxch-content-title"><?php esc_html_e('Content Chunking', 'mxchat'); ?></h1> | |
| 1215 | + <p class="mxch-content-subtitle"><?php esc_html_e('Split large content into smaller segments for more accurate semantic search.', 'mxchat'); ?></p> | |
| 1390 | 1216 | </div> |
| 1391 | 1217 | |
| 1392 | 1218 | <div class="mxch-card"> |
| 1393 | - <div class="mxch-card-header"> | |
| 1394 | - <h2 class="mxch-card-title"><?php esc_html_e('Chunking', 'mxchat'); ?></h2> | |
| 1395 | - </div> | |
| 1396 | 1219 | <div class="mxch-card-body mxchat-autosave-section"> |
| 1397 | 1220 | <div class="mxch-notice mxch-notice-info" style="margin-bottom: 20px;"> |
| 1398 | 1221 | <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> |
| 1399 | 1222 | <span><?php esc_html_e('Chunking improves retrieval quality for long documents. All chunks are reassembled before sending to the AI. Only applies to new submissions.', 'mxchat'); ?></span> |
| @@ -1415,34 +1238,8 @@ | ||
| 1415 | 1238 | <p class="mxch-field-description"><?php esc_html_e('Recommended: 4000 characters (~1000 tokens). Range: 1000-10000. Larger chunks preserve more context.', 'mxchat'); ?></p> |
| 1416 | 1239 | </div> |
| 1417 | 1240 | </div> |
| 1418 | 1241 | </div> |
| 1419 | - | |
| 1420 | - <?php | |
| 1421 | - // Retrieval tuning (plan 11720c) — the hybrid keyword boost moved here | |
| 1422 | - // from Settings → Behavior: it is KB retrieval tuning, so it lives with | |
| 1423 | - // its natural siblings. SAME option key and save transport as before — | |
| 1424 | - // the name routes to the mxchat_save_setting handler, whose | |
| 1425 | - // mxchat_hybrid_keyword_toggle case still runs capability detection. | |
| 1426 | - ?> | |
| 1427 | - <div class="mxch-card"> | |
| 1428 | - <div class="mxch-card-header"> | |
| 1429 | - <h2 class="mxch-card-title"><?php esc_html_e('Retrieval', 'mxchat'); ?></h2> | |
| 1430 | - </div> | |
| 1431 | - <div class="mxch-card-body mxchat-autosave-section"> | |
| 1432 | - <div class="mxch-field"> | |
| 1433 | - <div style="display: flex; align-items: center; gap: 12px;"> | |
| 1434 | - <label class="mxchat-toggle-switch"> | |
| 1435 | - <input type="checkbox" name="mxchat_hybrid_keyword_toggle" id="mxchat_hybrid_keyword_toggle" class="mxchat-autosave-field" value="on" <?php checked(get_option('mxchat_hybrid_keyword_toggle', 'off'), 'on'); ?>> | |
| 1436 | - <span class="mxchat-toggle-slider"></span> | |
| 1437 | - </label> | |
| 1438 | - <span style="font-weight: 500;"><?php esc_html_e('Hybrid keyword boost (WP-DB knowledge base)', 'mxchat'); ?></span> | |
| 1439 | - </div> | |
| 1440 | - <p class="mxch-field-description" style="margin-top: 8px;"><?php esc_html_e('Combines keyword matching with vector similarity when searching your WordPress-database knowledge base. Helps exact-token questions — product codes, SKUs, error codes, names — find the right document even when semantic similarity alone would miss it.', 'mxchat'); ?></p> | |
| 1441 | - <p class="mxch-field-hint"><?php esc_html_e('Only affects the WordPress-database knowledge base; Pinecone is unaffected. Changes which sources are retrieved on existing installs, so test after enabling. The Transcripts sources panel labels how each match was found while this is on.', 'mxchat'); ?></p> | |
| 1442 | - </div> | |
| 1443 | - </div> | |
| 1444 | - </div> | |
| 1445 | 1242 | </div> |
| 1446 | 1243 | <?php |
| 1447 | 1244 | } |
| 1448 | 1245 | |
| @@ -1475,11 +1272,10 @@ | ||
| 1475 | 1272 | |
| 1476 | 1273 | <h4 style="margin: 0 0 16px 0;"><?php esc_html_e('Add Tag-Role Mapping', 'mxchat'); ?></h4> |
| 1477 | 1274 | <div style="display: flex; gap: 15px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 24px;"> |
| 1478 | 1275 | <div class="mxch-field" style="flex: 1; min-width: 200px; margin-bottom: 0;"> |
| 1479 | - <label class="mxch-field-label" for="mxchat-tag-input"><?php esc_html_e('Tag (name or slug)', 'mxchat'); ?></label> | |
| 1480 | - <input type="text" id="mxchat-tag-input" class="mxch-input" placeholder="<?php esc_attr_e('e.g., Premium Content or premium-content', 'mxchat'); ?>"> | |
| 1481 | - <span class="mxch-field-hint"><?php esc_html_e('Enter an existing post tag by its display name or its slug.', 'mxchat'); ?></span> | |
| 1276 | + <label class="mxch-field-label" for="mxchat-tag-input"><?php esc_html_e('Tag Name', 'mxchat'); ?></label> | |
| 1277 | + <input type="text" id="mxchat-tag-input" class="mxch-input" placeholder="<?php esc_attr_e('e.g., premium, members-only', 'mxchat'); ?>"> | |
| 1482 | 1278 | </div> |
| 1483 | 1279 | <div class="mxch-field" style="flex: 1; min-width: 200px; margin-bottom: 0;"> |
| 1484 | 1280 | <label class="mxch-field-label" for="mxchat-role-select"><?php esc_html_e('Required Role', 'mxchat'); ?></label> |
| 1485 | 1281 | <select id="mxchat-role-select" class="mxch-select"> |
| @@ -1542,23 +1338,8 @@ | ||
| 1542 | 1338 | </div> |
| 1543 | 1339 | |
| 1544 | 1340 | <div class="mxch-card"> |
| 1545 | 1341 | <div class="mxch-card-body mxchat-autosave-section"> |
| 1546 | - <?php | |
| 1547 | - // Install-level ACF→PDF import extraction (plan 11720c) — moved | |
| 1548 | - // here from a per-import checkbox in the content-selector modal. | |
| 1549 | - // Default OFF, matching its own "recommended only if…" guidance. | |
| 1550 | - ?> | |
| 1551 | - <div class="mxch-field" style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid var(--mxch-card-border);"> | |
| 1552 | - <div style="display: flex; align-items: center; gap: 12px;"> | |
| 1553 | - <label class="mxchat-toggle-switch"> | |
| 1554 | - <input type="checkbox" name="mxchat_acf_pdf_extraction" id="mxchat_acf_pdf_extraction" class="mxchat-autosave-field" value="on" <?php checked(get_option('mxchat_acf_pdf_extraction', '0'), '1'); ?>> | |
| 1555 | - <span class="mxchat-toggle-slider"></span> | |
| 1556 | - </label> | |
| 1557 | - <span style="font-weight: 500;"><?php esc_html_e('Extract text from PDFs linked in ACF File/Image fields during imports', 'mxchat'); ?></span> | |
| 1558 | - </div> | |
| 1559 | - <p class="mxch-field-description" style="margin-top: 8px;"><?php esc_html_e('Applies to WordPress Content imports. Adds 200-500ms per post and ~30 KB to each knowledge base entry. Recommended only if your ACF setup uses PDF attachments for primary content. Editor-save extraction has its own toggle under Auto-Sync.', 'mxchat'); ?></p> | |
| 1560 | - </div> | |
| 1561 | 1342 | <?php if (!function_exists('acf_get_field_groups')): ?> |
| 1562 | 1343 | <div class="mxch-notice mxch-notice-info"> |
| 1563 | 1344 | <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> |
| 1564 | 1345 | <span><?php esc_html_e('Advanced Custom Fields (ACF) plugin is not detected. Install and activate ACF to use this feature.', 'mxchat'); ?></span> |
| @@ -1633,28 +1414,8 @@ | ||
| 1633 | 1414 | <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> |
| 1634 | 1415 | <span><?php esc_html_e('Enter post meta keys to include in embeddings. This is useful for OptionTree fields, theme meta boxes, or any custom post meta that is not managed by ACF.', 'mxchat'); ?></span> |
| 1635 | 1416 | </div> |
| 1636 | 1417 | |
| 1637 | - <!-- Meta-key discovery scanner (plan-mxchat-20260709-fe8e4e): mirror the ACF picker's | |
| 1638 | - discover-for-you UX for non-ACF meta, so owners click keys instead of typing them blind. --> | |
| 1639 | - <div class="mxch-field mxchat-meta-scan"> | |
| 1640 | - <label class="mxch-field-label"><?php esc_html_e('Discover meta keys', 'mxchat'); ?></label> | |
| 1641 | - <p class="mxch-field-description" style="margin-top: 0;"> | |
| 1642 | - <?php esc_html_e('Scan your published content for custom meta keys, then click any you want to include. This gives non-ACF meta the same discover-and-click experience as the ACF picker.', 'mxchat'); ?> | |
| 1643 | - </p> | |
| 1644 | - <div class="mxchat-meta-scan-controls"> | |
| 1645 | - <button type="button" id="mxchat-scan-meta-btn" class="mxch-btn mxch-btn-secondary" data-nonce="<?php echo esc_attr(wp_create_nonce('mxchat_prompts_setting_nonce')); ?>"> | |
| 1646 | - <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"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> | |
| 1647 | - <span class="mxchat-scan-label"><?php esc_html_e('Scan my content for meta keys', 'mxchat'); ?></span> | |
| 1648 | - </button> | |
| 1649 | - <label class="mxchat-meta-scan-internal"> | |
| 1650 | - <input type="checkbox" id="mxchat-scan-internal"> | |
| 1651 | - <?php esc_html_e('Include internal keys (underscore-prefixed)', 'mxchat'); ?> | |
| 1652 | - </label> | |
| 1653 | - </div> | |
| 1654 | - <div id="mxchat-meta-scan-results" class="mxchat-meta-scan-results" aria-live="polite"></div> | |
| 1655 | - </div> | |
| 1656 | - | |
| 1657 | 1418 | <div class="mxch-field"> |
| 1658 | 1419 | <label class="mxch-field-label" for="mxchat_custom_meta_whitelist"> |
| 1659 | 1420 | <?php esc_html_e('Meta Key Whitelist', 'mxchat'); ?> |
| 1660 | 1421 | </label> |
| @@ -1667,9 +1428,9 @@ | ||
| 1667 | 1428 | data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>" |
| 1668 | 1429 | style="font-family: monospace;" |
| 1669 | 1430 | ><?php echo esc_textarea($whitelist); ?></textarea> |
| 1670 | 1431 | <p class="mxch-field-description"> |
| 1671 | - <?php esc_html_e('Enter one meta key per line. These fields will be appended to the content during embedding. Text values are added as-is; array or repeater values are flattened into a comma-separated list. Empty values are skipped.', 'mxchat'); ?> | |
| 1432 | + <?php esc_html_e('Enter one meta key per line. These fields will be appended to the content during embedding. Only string values are included.', 'mxchat'); ?> | |
| 1672 | 1433 | </p> |
| 1673 | 1434 | </div> |
| 1674 | 1435 | |
| 1675 | 1436 | <div style="margin-top: 20px; padding: 16px; background: #f8fafc; border-radius: var(--mxch-radius-md);"> |
| @@ -1676,9 +1437,9 @@ | ||
| 1676 | 1437 | <h4 style="margin: 0 0 12px 0; font-size: 14px;"><?php esc_html_e('How to find meta keys:', 'mxchat'); ?></h4> |
| 1677 | 1438 | <ul style="margin: 0; padding-left: 20px; color: var(--mxch-text-secondary); font-size: 13px;"> |
| 1678 | 1439 | <li><?php esc_html_e('Check your theme documentation for meta key names', 'mxchat'); ?></li> |
| 1679 | 1440 | <li><?php esc_html_e('Look in the wp_postmeta database table', 'mxchat'); ?></li> |
| 1680 | - <li><?php esc_html_e('Use the Scan my content for meta keys button above to discover the keys on your published content automatically', 'mxchat'); ?></li> | |
| 1441 | + <li><?php esc_html_e('Use a plugin like "Show Post Meta" to view meta keys on any post', 'mxchat'); ?></li> | |
| 1681 | 1442 | <li><?php esc_html_e('Meta keys starting with underscore (_) are usually hidden/internal', 'mxchat'); ?></li> |
| 1682 | 1443 | </ul> |
| 1683 | 1444 | </div> |
| 1684 | 1445 | </div> |
| @@ -1780,9 +1541,9 @@ | ||
| 1780 | 1541 | $max_results = $vectorstore_options['mxchat_vectorstore_max_results'] ?? 5; |
| 1781 | 1542 | |
| 1782 | 1543 | // Get current chat model to check compatibility |
| 1783 | 1544 | $mxchat_options = get_option('mxchat_options', array()); |
| 1784 | - $current_model = $mxchat_options['model'] ?? 'gpt-5.6-sol'; | |
| 1545 | + $current_model = $mxchat_options['model'] ?? 'gpt-4o'; | |
| 1785 | 1546 | $is_openai_model = preg_match('/^(gpt-|o1-|o3-)/', $current_model); |
| 1786 | 1547 | ?> |
| 1787 | 1548 | <div id="openai-vectorstore" class="mxch-section"> |
| 1788 | 1549 | <div class="mxch-content-header"> |
| @@ -1793,9 +1554,9 @@ | ||
| 1793 | 1554 | <div class="mxch-card"> |
| 1794 | 1555 | <div class="mxch-card-body"> |
| 1795 | 1556 | <div class="mxch-notice mxch-notice-info" style="margin-bottom: 20px;"> |
| 1796 | 1557 | <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> |
| 1797 | - <span><strong><?php esc_html_e('Requires OpenAI Chat Model.', 'mxchat'); ?></strong> <?php esc_html_e('Vector Store search only works with OpenAI models (gpt-5.6-sol, gpt-5.6-terra, etc.). Create Vector Stores in your OpenAI Dashboard.', 'mxchat'); ?></span> | |
| 1558 | + <span><strong><?php esc_html_e('Requires OpenAI Chat Model.', 'mxchat'); ?></strong> <?php esc_html_e('Vector Store search only works with OpenAI models (gpt-4o, gpt-4o-mini, etc.). Create Vector Stores in your OpenAI Dashboard.', 'mxchat'); ?></span> | |
| 1798 | 1559 | </div> |
| 1799 | 1560 | |
| 1800 | 1561 | <?php if (!$is_openai_model && $use_vectorstore === '1'): ?> |
| 1801 | 1562 | <div class="mxch-notice mxch-notice-warning" style="margin-bottom: 20px;"> |
| @@ -1937,257 +1698,8 @@ | ||
| 1937 | 1698 | <?php |
| 1938 | 1699 | } |
| 1939 | 1700 | |
| 1940 | 1701 | /** |
| 1941 | - * Render Edit Entry Modal | |
| 1942 | - */ | |
| 1943 | -function mxchat_render_edit_entry_modal() { | |
| 1944 | - ?> | |
| 1945 | - <div id="mxchat-kb-edit-modal" class="mxchat-kb-modal"> | |
| 1946 | - <div class="mxchat-kb-modal-content mxchat-kb-edit-modal-content"> | |
| 1947 | - <div class="mxchat-kb-modal-header"> | |
| 1948 | - <h3 id="mxchat-kb-edit-title"><?php esc_html_e('Edit Knowledge Entry', 'mxchat'); ?></h3> | |
| 1949 | - <span class="mxchat-kb-modal-close" id="mxchat-kb-edit-close">×</span> | |
| 1950 | - </div> | |
| 1951 | - <div class="mxchat-kb-edit-body"> | |
| 1952 | - <div class="mxchat-kb-edit-source" id="mxchat-kb-edit-source"></div> | |
| 1953 | - <textarea id="mxchat-kb-edit-textarea" class="mxchat-kb-edit-textarea" placeholder="<?php esc_attr_e('Loading content...', 'mxchat'); ?>"></textarea> | |
| 1954 | - <div class="mxchat-kb-edit-meta"> | |
| 1955 | - <span id="mxchat-kb-edit-charcount"></span> | |
| 1956 | - <span id="mxchat-kb-edit-chunkinfo"></span> | |
| 1957 | - </div> | |
| 1958 | - </div> | |
| 1959 | - <div class="mxchat-kb-edit-footer"> | |
| 1960 | - <div class="mxchat-kb-edit-notice" id="mxchat-kb-edit-notice"></div> | |
| 1961 | - <div class="mxchat-kb-edit-actions"> | |
| 1962 | - <button type="button" id="mxchat-kb-edit-cancel" class="mxch-btn mxch-btn-ghost"> | |
| 1963 | - <?php esc_html_e('Cancel', 'mxchat'); ?> | |
| 1964 | - </button> | |
| 1965 | - <button type="button" id="mxchat-kb-edit-save" class="mxch-btn mxch-btn-primary"> | |
| 1966 | - <span class="mxchat-kb-edit-save-text"><?php esc_html_e('Save & Re-embed', 'mxchat'); ?></span> | |
| 1967 | - <span class="mxchat-kb-edit-save-spinner" style="display:none;"> | |
| 1968 | - <span class="mxchat-kb-spinner is-active" style="margin: 0;"></span> | |
| 1969 | - <?php esc_html_e('Saving...', 'mxchat'); ?> | |
| 1970 | - </span> | |
| 1971 | - </button> | |
| 1972 | - </div> | |
| 1973 | - </div> | |
| 1974 | - </div> | |
| 1975 | - </div> | |
| 1976 | - <?php | |
| 1977 | -} | |
| 1978 | - | |
| 1979 | -/** | |
| 1980 | - * Render the read-only Knowledge Inspector modal (plan-mxchat-20260628-d8cb4b). | |
| 1981 | - * Shows exactly what was indexed for an entry: the per-chunk stored text + lengths | |
| 1982 | - * and (for Pinecone) the stored metadata fields, rendered with the Testing tab's | |
| 1983 | - * own card components so the two surfaces feel like one system. Self-contained | |
| 1984 | - * markup + JS so it doesn't entangle the edit-modal init. | |
| 1985 | - */ | |
| 1986 | -function mxchat_render_inspect_entry_modal() { | |
| 1987 | - ?> | |
| 1988 | - <div id="mxchat-kb-inspect-modal" class="mxchat-kb-modal"> | |
| 1989 | - <div class="mxchat-kb-modal-content mxchat-kb-inspect-modal-content"> | |
| 1990 | - <div class="mxchat-kb-modal-header"> | |
| 1991 | - <h3 id="mxchat-kb-inspect-title"><?php esc_html_e('Indexed Content', 'mxchat'); ?></h3> | |
| 1992 | - <span class="mxchat-kb-modal-close" id="mxchat-kb-inspect-close">×</span> | |
| 1993 | - </div> | |
| 1994 | - <div class="mxchat-kb-inspect-body"> | |
| 1995 | - <div class="mxchat-kb-edit-source" id="mxchat-kb-inspect-source"></div> | |
| 1996 | - <div class="mxch-testing-results" id="mxchat-kb-inspect-results"> | |
| 1997 | - <div class="mxch-testing-no-data"><?php esc_html_e('Loading indexed content…', 'mxchat'); ?></div> | |
| 1998 | - </div> | |
| 1999 | - </div> | |
| 2000 | - <div class="mxchat-kb-edit-footer"> | |
| 2001 | - <div class="mxchat-kb-edit-notice" id="mxchat-kb-inspect-notice"></div> | |
| 2002 | - <div class="mxchat-kb-edit-actions"> | |
| 2003 | - <button type="button" id="mxchat-kb-inspect-close-btn" class="mxch-btn mxch-btn-primary"> | |
| 2004 | - <?php esc_html_e('Close', 'mxchat'); ?> | |
| 2005 | - </button> | |
| 2006 | - </div> | |
| 2007 | - </div> | |
| 2008 | - </div> | |
| 2009 | - </div> | |
| 2010 | - <script> | |
| 2011 | - (function() { | |
| 2012 | - 'use strict'; | |
| 2013 | - document.addEventListener('DOMContentLoaded', function() { | |
| 2014 | - var modal = document.getElementById('mxchat-kb-inspect-modal'); | |
| 2015 | - if (!modal) return; | |
| 2016 | - var closeX = document.getElementById('mxchat-kb-inspect-close'); | |
| 2017 | - var closeBtn = document.getElementById('mxchat-kb-inspect-close-btn'); | |
| 2018 | - var sourceEl = document.getElementById('mxchat-kb-inspect-source'); | |
| 2019 | - var resultsEl= document.getElementById('mxchat-kb-inspect-results'); | |
| 2020 | - var titleEl = document.getElementById('mxchat-kb-inspect-title'); | |
| 2021 | - var noticeEl = document.getElementById('mxchat-kb-inspect-notice'); | |
| 2022 | - | |
| 2023 | - var I18N = { | |
| 2024 | - title: <?php echo wp_json_encode( esc_html__('Indexed Content', 'mxchat') ); ?>, | |
| 2025 | - loading: <?php echo wp_json_encode( esc_html__('Loading indexed content…', 'mxchat') ); ?>, | |
| 2026 | - manual: <?php echo wp_json_encode( esc_html__('Manual Content', 'mxchat') ); ?>, | |
| 2027 | - source: <?php echo wp_json_encode( esc_html__('Source', 'mxchat') ); ?>, | |
| 2028 | - store: <?php echo wp_json_encode( esc_html__('Storage', 'mxchat') ); ?>, | |
| 2029 | - type: <?php echo wp_json_encode( esc_html__('Content type', 'mxchat') ); ?>, | |
| 2030 | - chunksLbl:<?php echo wp_json_encode( esc_html__('Chunks', 'mxchat') ); ?>, | |
| 2031 | - totalLen: <?php echo wp_json_encode( esc_html__('Total indexed length', 'mxchat') ); ?>, | |
| 2032 | - chunk: <?php echo wp_json_encode( esc_html__('Chunk', 'mxchat') ); ?>, | |
| 2033 | - indexed: <?php echo wp_json_encode( esc_html__('Indexed', 'mxchat') ); ?>, | |
| 2034 | - chars: <?php echo wp_json_encode( esc_html__('chars', 'mxchat') ); ?>, | |
| 2035 | - showText: <?php echo wp_json_encode( esc_html__('Show indexed text', 'mxchat') ); ?>, | |
| 2036 | - hideText: <?php echo wp_json_encode( esc_html__('Hide indexed text', 'mxchat') ); ?>, | |
| 2037 | - wpStore: <?php echo wp_json_encode( esc_html__('Local WordPress database', 'mxchat') ); ?>, | |
| 2038 | - pcStore: <?php echo wp_json_encode( esc_html__('Pinecone', 'mxchat') ); ?>, | |
| 2039 | - meta: <?php echo wp_json_encode( esc_html__('Vector metadata', 'mxchat') ); ?>, | |
| 2040 | - loadFail: <?php echo wp_json_encode( esc_html__('Failed to load indexed content.', 'mxchat') ); ?>, | |
| 2041 | - netErr: <?php echo wp_json_encode( esc_html__('Network error: ', 'mxchat') ); ?> | |
| 2042 | - }; | |
| 2043 | - | |
| 2044 | - function esc(str) { | |
| 2045 | - var d = document.createElement('div'); | |
| 2046 | - d.textContent = (str === null || str === undefined) ? '' : String(str); | |
| 2047 | - return d.innerHTML; | |
| 2048 | - } | |
| 2049 | - function fmt(n) { try { return Number(n).toLocaleString(); } catch (e) { return n; } } | |
| 2050 | - | |
| 2051 | - document.addEventListener('click', function(e) { | |
| 2052 | - var btn = e.target.closest('.mxchat-inspect-entry-btn'); | |
| 2053 | - if (btn) { e.preventDefault(); open(btn); } | |
| 2054 | - }); | |
| 2055 | - if (closeX) closeX.addEventListener('click', close); | |
| 2056 | - if (closeBtn) closeBtn.addEventListener('click', close); | |
| 2057 | - // Overlay click closes, but only when the whole gesture happened on the | |
| 2058 | - // overlay — otherwise selecting indexed text and releasing past the | |
| 2059 | - // dialog edge dispatches the click on the overlay and closes it. | |
| 2060 | - var downOnOverlay = false; | |
| 2061 | - modal.addEventListener('mousedown', function(e) { downOnOverlay = (e.target === modal); }); | |
| 2062 | - modal.addEventListener('click', function(e) { | |
| 2063 | - var overlayGesture = downOnOverlay; | |
| 2064 | - downOnOverlay = false; | |
| 2065 | - if (e.target === modal && overlayGesture) close(); | |
| 2066 | - }); | |
| 2067 | - document.addEventListener('keydown', function(e) { | |
| 2068 | - if (e.key === 'Escape' && modal.classList.contains('active')) close(); | |
| 2069 | - }); | |
| 2070 | - | |
| 2071 | - function close() { modal.classList.remove('active'); } | |
| 2072 | - | |
| 2073 | - function open(btn) { | |
| 2074 | - var entry = { | |
| 2075 | - sourceUrl: btn.getAttribute('data-source-url') || '', | |
| 2076 | - entryId: btn.getAttribute('data-entry-id') || '', | |
| 2077 | - dataSource: btn.getAttribute('data-data-source') || 'wordpress', | |
| 2078 | - botId: btn.getAttribute('data-bot-id') || 'default', | |
| 2079 | - nonce: btn.getAttribute('data-nonce') || '' | |
| 2080 | - }; | |
| 2081 | - | |
| 2082 | - titleEl.textContent = I18N.title; | |
| 2083 | - noticeEl.textContent = ''; | |
| 2084 | - noticeEl.className = 'mxchat-kb-edit-notice'; | |
| 2085 | - resultsEl.innerHTML = '<div class="mxch-testing-no-data">' + esc(I18N.loading) + '</div>'; | |
| 2086 | - | |
| 2087 | - if (entry.sourceUrl && entry.sourceUrl.indexOf('mxchat://') !== 0 && entry.sourceUrl.indexOf('_ungrouped_') !== 0) { | |
| 2088 | - sourceEl.innerHTML = esc(I18N.source) + ': <a href="' + esc(entry.sourceUrl) + '" target="_blank" rel="noopener">' + esc(entry.sourceUrl) + '</a>'; | |
| 2089 | - } else { | |
| 2090 | - sourceEl.textContent = I18N.manual; | |
| 2091 | - } | |
| 2092 | - | |
| 2093 | - modal.classList.add('active'); | |
| 2094 | - | |
| 2095 | - var fd = new FormData(); | |
| 2096 | - fd.append('action', 'mxchat_inspect_entry'); | |
| 2097 | - fd.append('nonce', entry.nonce); | |
| 2098 | - fd.append('source_url', entry.sourceUrl); | |
| 2099 | - fd.append('entry_id', entry.entryId); | |
| 2100 | - fd.append('data_source', entry.dataSource); | |
| 2101 | - fd.append('bot_id', entry.botId); | |
| 2102 | - | |
| 2103 | - fetch(ajaxurl, { method: 'POST', body: fd }) | |
| 2104 | - .then(function(r) { return r.json(); }) | |
| 2105 | - .then(function(resp) { | |
| 2106 | - if (resp && resp.success) { | |
| 2107 | - render(resp.data); | |
| 2108 | - } else { | |
| 2109 | - var msg = (resp && resp.data && resp.data.message) || I18N.loadFail; | |
| 2110 | - resultsEl.innerHTML = ''; | |
| 2111 | - showNotice(msg, 'error'); | |
| 2112 | - } | |
| 2113 | - }) | |
| 2114 | - .catch(function(err) { | |
| 2115 | - resultsEl.innerHTML = ''; | |
| 2116 | - showNotice(I18N.netErr + err.message, 'error'); | |
| 2117 | - }); | |
| 2118 | - } | |
| 2119 | - | |
| 2120 | - function render(data) { | |
| 2121 | - var storeLabel = data.store === 'pinecone' ? I18N.pcStore : I18N.wpStore; | |
| 2122 | - var html = ''; | |
| 2123 | - | |
| 2124 | - // Summary card — what this entry is + how much was indexed. | |
| 2125 | - html += '<div class="match-card above-threshold">' + | |
| 2126 | - '<div class="match-header">' + | |
| 2127 | - '<div class="match-title"><span class="status-icon">📦</span> ' + esc(data.chunk_count) + ' ' + esc(I18N.chunksLbl.toLowerCase()) + '</div>' + | |
| 2128 | - '<span class="context-label">' + esc(storeLabel) + '</span>' + | |
| 2129 | - '</div>' + | |
| 2130 | - '<div class="mxch-testing-field"><label>' + esc(I18N.type) + '</label><code>' + esc(data.content_type || '—') + '</code> ' + | |
| 2131 | - ' <label style="display:inline; text-transform:none; letter-spacing:0; font-weight:600;">' + esc(I18N.totalLen) + ':</label> ' + esc(fmt(data.assembled_length)) + ' ' + esc(I18N.chars) + '</div>'; | |
| 2132 | - if (data.metadata_note) { | |
| 2133 | - html += '<div class="match-source"><span class="source-icon">ℹ️</span> ' + esc(data.metadata_note) + '</div>'; | |
| 2134 | - } | |
| 2135 | - html += '</div>'; | |
| 2136 | - | |
| 2137 | - // One card per stored chunk — the actual embedded text. | |
| 2138 | - var chunks = data.chunks || []; | |
| 2139 | - chunks.forEach(function(chunk, i) { | |
| 2140 | - var num = (chunk.index !== null && chunk.index !== undefined) ? (chunk.index + 1) : (i + 1); | |
| 2141 | - var metaRows = ''; | |
| 2142 | - if (chunk.metadata && typeof chunk.metadata === 'object') { | |
| 2143 | - var keys = Object.keys(chunk.metadata); | |
| 2144 | - if (keys.length) { | |
| 2145 | - metaRows += '<div class="chunk-detail-row chunk-used" style="display:block;"><span class="chunk-detail-num">' + esc(I18N.meta) + '</span></div>'; | |
| 2146 | - keys.forEach(function(k) { | |
| 2147 | - metaRows += '<div class="chunk-detail-row"><span class="chunk-detail-num">' + esc(k) + '</span><span class="chunk-detail-score">' + esc(chunk.metadata[k]) + '</span></div>'; | |
| 2148 | - }); | |
| 2149 | - } | |
| 2150 | - } | |
| 2151 | - html += '<div class="match-card above-threshold">' + | |
| 2152 | - '<div class="match-header">' + | |
| 2153 | - '<div class="match-title"><span class="status-icon">✓</span> ' + esc(I18N.chunk) + ' ' + esc(num) + | |
| 2154 | - '<span class="chunk-summary">' + esc(fmt(chunk.length)) + ' ' + esc(I18N.chars) + '</span>' + | |
| 2155 | - '</div>' + | |
| 2156 | - '<span class="context-label">' + esc(I18N.indexed) + '</span>' + | |
| 2157 | - '</div>' + | |
| 2158 | - '<span class="chunk-expand-toggle" data-chunk="' + i + '">▶ ' + esc(I18N.showText) + '</span>' + | |
| 2159 | - '<div class="chunk-details" data-chunk="' + i + '">' + | |
| 2160 | - '<pre class="mxch-kb-inspect-chunk-text">' + esc(chunk.text) + '</pre>' + | |
| 2161 | - metaRows + | |
| 2162 | - '</div>' + | |
| 2163 | - '</div>'; | |
| 2164 | - }); | |
| 2165 | - | |
| 2166 | - resultsEl.innerHTML = html; | |
| 2167 | - | |
| 2168 | - resultsEl.querySelectorAll('.chunk-expand-toggle').forEach(function(toggle) { | |
| 2169 | - toggle.addEventListener('click', function() { | |
| 2170 | - var id = this.getAttribute('data-chunk'); | |
| 2171 | - var details = resultsEl.querySelector('.chunk-details[data-chunk="' + id + '"]'); | |
| 2172 | - if (!details) return; | |
| 2173 | - var expanded = details.classList.toggle('expanded'); | |
| 2174 | - this.innerHTML = (expanded ? '▼ ' + esc(I18N.hideText) : '▶ ' + esc(I18N.showText)); | |
| 2175 | - }); | |
| 2176 | - }); | |
| 2177 | - } | |
| 2178 | - | |
| 2179 | - function showNotice(msg, type) { | |
| 2180 | - noticeEl.textContent = msg; | |
| 2181 | - noticeEl.className = 'mxchat-kb-edit-notice' + (type ? ' ' + type : ''); | |
| 2182 | - } | |
| 2183 | - }); | |
| 2184 | - })(); | |
| 2185 | - </script> | |
| 2186 | - <?php | |
| 2187 | -} | |
| 2188 | - | |
| 2189 | -/** | |
| 2190 | 1702 | * Render Knowledge Page Navigation Scripts |
| 2191 | 1703 | */ |
| 2192 | 1704 | function mxchat_render_knowledge_page_scripts() { |
| 2193 | 1705 | ?> |
| @@ -2362,216 +1874,8 @@ | ||
| 2362 | 1874 | } |
| 2363 | 1875 | |
| 2364 | 1876 | // Sitemap detection is now handled by knowledge-processing.js |
| 2365 | 1877 | // It will be initialized when user clicks "Sitemap Import" option |
| 2366 | - | |
| 2367 | - // ─── Edit Entry Modal ──────────────────────────────────── | |
| 2368 | - initEditModal(); | |
| 2369 | - | |
| 2370 | - function initEditModal() { | |
| 2371 | - var modal = document.getElementById('mxchat-kb-edit-modal'); | |
| 2372 | - var textarea = document.getElementById('mxchat-kb-edit-textarea'); | |
| 2373 | - var saveBtn = document.getElementById('mxchat-kb-edit-save'); | |
| 2374 | - var cancelBtn = document.getElementById('mxchat-kb-edit-cancel'); | |
| 2375 | - var closeBtn = document.getElementById('mxchat-kb-edit-close'); | |
| 2376 | - var notice = document.getElementById('mxchat-kb-edit-notice'); | |
| 2377 | - var charCount = document.getElementById('mxchat-kb-edit-charcount'); | |
| 2378 | - var chunkInfo = document.getElementById('mxchat-kb-edit-chunkinfo'); | |
| 2379 | - var sourceEl = document.getElementById('mxchat-kb-edit-source'); | |
| 2380 | - var titleEl = document.getElementById('mxchat-kb-edit-title'); | |
| 2381 | - var saveText = saveBtn ? saveBtn.querySelector('.mxchat-kb-edit-save-text') : null; | |
| 2382 | - var saveSpin = saveBtn ? saveBtn.querySelector('.mxchat-kb-edit-save-spinner') : null; | |
| 2383 | - | |
| 2384 | - if (!modal) return; | |
| 2385 | - | |
| 2386 | - var currentEntry = {}; | |
| 2387 | - var loadedContent = ''; | |
| 2388 | - | |
| 2389 | - var EDIT_I18N = { | |
| 2390 | - discard: <?php echo wp_json_encode( __('Discard your unsaved changes to this entry?', 'mxchat') ); ?> | |
| 2391 | - }; | |
| 2392 | - | |
| 2393 | - // Bind edit buttons (delegated) | |
| 2394 | - document.addEventListener('click', function(e) { | |
| 2395 | - var btn = e.target.closest('.mxchat-edit-entry-btn'); | |
| 2396 | - if (btn) { | |
| 2397 | - e.preventDefault(); | |
| 2398 | - openEditModal(btn); | |
| 2399 | - } | |
| 2400 | - }); | |
| 2401 | - | |
| 2402 | - // Close handlers. The overlay deliberately has no click-to-close: a click | |
| 2403 | - // fires on the common ancestor of its mousedown and mouseup, so releasing | |
| 2404 | - // a text-selection drag past the dialog edge targets the overlay and would | |
| 2405 | - // discard the edit. Closing is explicit only, and confirms when dirty. | |
| 2406 | - if (closeBtn) closeBtn.addEventListener('click', requestClose); | |
| 2407 | - if (cancelBtn) cancelBtn.addEventListener('click', requestClose); | |
| 2408 | - document.addEventListener('keydown', function(e) { | |
| 2409 | - if (e.key === 'Escape' && modal.classList.contains('active')) requestClose(); | |
| 2410 | - }); | |
| 2411 | - | |
| 2412 | - // Character count | |
| 2413 | - if (textarea) { | |
| 2414 | - textarea.addEventListener('input', function() { | |
| 2415 | - updateCharCount(); | |
| 2416 | - }); | |
| 2417 | - } | |
| 2418 | - | |
| 2419 | - // Save handler | |
| 2420 | - if (saveBtn) saveBtn.addEventListener('click', saveContent); | |
| 2421 | - | |
| 2422 | - function openEditModal(btn) { | |
| 2423 | - currentEntry = { | |
| 2424 | - sourceUrl: btn.getAttribute('data-source-url') || '', | |
| 2425 | - entryId: btn.getAttribute('data-entry-id') || '', | |
| 2426 | - dataSource: btn.getAttribute('data-data-source') || 'wordpress', | |
| 2427 | - botId: btn.getAttribute('data-bot-id') || 'default', | |
| 2428 | - nonce: btn.getAttribute('data-nonce') || '' | |
| 2429 | - }; | |
| 2430 | - | |
| 2431 | - // Reset state | |
| 2432 | - textarea.value = ''; | |
| 2433 | - loadedContent = ''; | |
| 2434 | - textarea.placeholder = 'Loading content...'; | |
| 2435 | - textarea.disabled = true; | |
| 2436 | - saveBtn.disabled = true; | |
| 2437 | - notice.textContent = ''; | |
| 2438 | - notice.className = 'mxchat-kb-edit-notice'; | |
| 2439 | - charCount.textContent = ''; | |
| 2440 | - chunkInfo.textContent = ''; | |
| 2441 | - | |
| 2442 | - // Show source | |
| 2443 | - if (currentEntry.sourceUrl && currentEntry.sourceUrl.indexOf('mxchat://') !== 0) { | |
| 2444 | - sourceEl.innerHTML = 'Source: <a href="' + escapeHtml(currentEntry.sourceUrl) + '" target="_blank">' + escapeHtml(truncate(currentEntry.sourceUrl, 60)) + '</a>'; | |
| 2445 | - } else { | |
| 2446 | - sourceEl.textContent = 'Manual Content'; | |
| 2447 | - } | |
| 2448 | - | |
| 2449 | - // Show modal | |
| 2450 | - modal.classList.add('active'); | |
| 2451 | - | |
| 2452 | - // Fetch content | |
| 2453 | - var formData = new FormData(); | |
| 2454 | - formData.append('action', 'mxchat_get_entry_content'); | |
| 2455 | - formData.append('nonce', currentEntry.nonce); | |
| 2456 | - formData.append('source_url', currentEntry.sourceUrl); | |
| 2457 | - formData.append('entry_id', currentEntry.entryId); | |
| 2458 | - formData.append('data_source', currentEntry.dataSource); | |
| 2459 | - formData.append('bot_id', currentEntry.botId); | |
| 2460 | - | |
| 2461 | - fetch(ajaxurl, { method: 'POST', body: formData }) | |
| 2462 | - .then(function(r) { return r.json(); }) | |
| 2463 | - .then(function(resp) { | |
| 2464 | - if (resp.success) { | |
| 2465 | - textarea.value = resp.data.content || ''; | |
| 2466 | - loadedContent = textarea.value; | |
| 2467 | - textarea.disabled = false; | |
| 2468 | - textarea.placeholder = 'Edit your content here...'; | |
| 2469 | - saveBtn.disabled = false; | |
| 2470 | - currentEntry.contentType = resp.data.content_type || 'content'; | |
| 2471 | - | |
| 2472 | - updateCharCount(); | |
| 2473 | - | |
| 2474 | - if (resp.data.is_chunked) { | |
| 2475 | - chunkInfo.textContent = resp.data.chunk_count + ' chunks — will be re-chunked on save'; | |
| 2476 | - titleEl.textContent = 'Edit Knowledge Entry (' + resp.data.chunk_count + ' chunks)'; | |
| 2477 | - } else { | |
| 2478 | - chunkInfo.textContent = ''; | |
| 2479 | - titleEl.textContent = 'Edit Knowledge Entry'; | |
| 2480 | - } | |
| 2481 | - | |
| 2482 | - textarea.focus(); | |
| 2483 | - } else { | |
| 2484 | - textarea.placeholder = ''; | |
| 2485 | - showNotice((resp.data && resp.data.message) || 'Failed to load content.', 'error'); | |
| 2486 | - } | |
| 2487 | - }) | |
| 2488 | - .catch(function(err) { | |
| 2489 | - textarea.placeholder = ''; | |
| 2490 | - showNotice('Network error: ' + err.message, 'error'); | |
| 2491 | - }); | |
| 2492 | - } | |
| 2493 | - | |
| 2494 | - function saveContent() { | |
| 2495 | - if (!textarea.value.trim()) { | |
| 2496 | - showNotice('Content cannot be empty.', 'error'); | |
| 2497 | - return; | |
| 2498 | - } | |
| 2499 | - | |
| 2500 | - saveBtn.disabled = true; | |
| 2501 | - if (saveText) saveText.style.display = 'none'; | |
| 2502 | - if (saveSpin) saveSpin.style.display = ''; | |
| 2503 | - showNotice('Saving and re-embedding...', ''); | |
| 2504 | - | |
| 2505 | - var formData = new FormData(); | |
| 2506 | - formData.append('action', 'mxchat_save_entry_content'); | |
| 2507 | - formData.append('nonce', currentEntry.nonce); | |
| 2508 | - formData.append('source_url', currentEntry.sourceUrl); | |
| 2509 | - formData.append('entry_id', currentEntry.entryId); | |
| 2510 | - formData.append('content', textarea.value); | |
| 2511 | - formData.append('data_source', currentEntry.dataSource); | |
| 2512 | - formData.append('bot_id', currentEntry.botId); | |
| 2513 | - formData.append('content_type', currentEntry.contentType || 'content'); | |
| 2514 | - | |
| 2515 | - fetch(ajaxurl, { method: 'POST', body: formData }) | |
| 2516 | - .then(function(r) { return r.json(); }) | |
| 2517 | - .then(function(resp) { | |
| 2518 | - if (saveText) saveText.style.display = ''; | |
| 2519 | - if (saveSpin) saveSpin.style.display = 'none'; | |
| 2520 | - saveBtn.disabled = false; | |
| 2521 | - | |
| 2522 | - if (resp.success) { | |
| 2523 | - showNotice('Saved successfully!', 'success'); | |
| 2524 | - setTimeout(function() { | |
| 2525 | - closeModal(); | |
| 2526 | - window.location.reload(); | |
| 2527 | - }, 1000); | |
| 2528 | - } else { | |
| 2529 | - showNotice((resp.data && resp.data.message) || 'Save failed.', 'error'); | |
| 2530 | - } | |
| 2531 | - }) | |
| 2532 | - .catch(function(err) { | |
| 2533 | - if (saveText) saveText.style.display = ''; | |
| 2534 | - if (saveSpin) saveSpin.style.display = 'none'; | |
| 2535 | - saveBtn.disabled = false; | |
| 2536 | - showNotice('Network error: ' + err.message, 'error'); | |
| 2537 | - }); | |
| 2538 | - } | |
| 2539 | - | |
| 2540 | - function isDirty() { | |
| 2541 | - return !textarea.disabled && textarea.value !== loadedContent; | |
| 2542 | - } | |
| 2543 | - | |
| 2544 | - function requestClose() { | |
| 2545 | - if (isDirty() && !window.confirm(EDIT_I18N.discard)) return; | |
| 2546 | - closeModal(); | |
| 2547 | - } | |
| 2548 | - | |
| 2549 | - function closeModal() { | |
| 2550 | - modal.classList.remove('active'); | |
| 2551 | - } | |
| 2552 | - | |
| 2553 | - function updateCharCount() { | |
| 2554 | - var len = textarea.value.length; | |
| 2555 | - charCount.textContent = len.toLocaleString() + ' characters'; | |
| 2556 | - } | |
| 2557 | - | |
| 2558 | - function showNotice(msg, type) { | |
| 2559 | - notice.textContent = msg; | |
| 2560 | - notice.className = 'mxchat-kb-edit-notice' + (type ? ' ' + type : ''); | |
| 2561 | - } | |
| 2562 | - | |
| 2563 | - function escapeHtml(str) { | |
| 2564 | - var div = document.createElement('div'); | |
| 2565 | - div.textContent = str; | |
| 2566 | - return div.innerHTML; | |
| 2567 | - } | |
| 2568 | - | |
| 2569 | - function truncate(str, max) { | |
| 2570 | - return str.length > max ? str.substring(0, max) + '...' : str; | |
| 2571 | - } | |
| 2572 | - } | |
| 2573 | - | |
| 2574 | 1878 | })(); |
| 2575 | 1879 | </script> |
| 2576 | 1880 | <?php |
| 2577 | 1881 | } |