Plugin Directory

Changeset 3482407


Ignore:
Timestamp:
03/14/2026 08:30:57 AM (3 weeks ago)
Author:
bsolveit
Message:

v1.2.1: AJAX page search autocomplete, Plugin Check fixes

Location:
ai-discovery-files
Files:
45 added
13 edited

Legend:

Unmodified
Added
Removed
  • ai-discovery-files/tags/1.2.0/admin/class-admin.php

    r3475304 r3482407  
    3838        add_action( 'wp_ajax_aidf_save_verify_code', array( __CLASS__, 'ajax_save_verify_code' ) );
    3939        add_action( 'wp_ajax_aidf_dismiss_review', array( __CLASS__, 'ajax_dismiss_review' ) );
     40        add_action( 'wp_ajax_aidf_search_pages', array( __CLASS__, 'ajax_search_pages' ) );
    4041        add_action( 'admin_notices', array( __CLASS__, 'maybe_show_welcome_notice' ) );
    4142        add_action( 'admin_notices', array( __CLASS__, 'maybe_show_conflict_notice' ) );
     
    118119                    'verifyFileActive'   => __( 'Verification file is live at', 'ai-discovery-files' ),
    119120                    'verifyFileInactive' => __( 'No verification code set. Enter your code above to activate.', 'ai-discovery-files' ),
     121                    'searchPages'     => __( 'Search pages…', 'ai-discovery-files' ),
     122                    'searching'       => __( 'Searching…', 'ai-discovery-files' ),
     123                    'noResults'       => __( 'No pages found', 'ai-discovery-files' ),
     124                    'typeToSearch'    => __( 'Type to search…', 'ai-discovery-files' ),
    120125                ),
    121126            )
     
    599604        wp_send_json_success();
    600605    }
     606
     607    /**
     608     * AJAX handler: search pages for the page picker autocomplete.
     609     *
     610     * @return void
     611     */
     612    public static function ajax_search_pages() {
     613        check_ajax_referer( 'aidf_admin_nonce', 'nonce' );
     614
     615        if ( ! current_user_can( 'manage_options' ) ) {
     616            wp_send_json_error( __( 'Permission denied.', 'ai-discovery-files' ) );
     617        }
     618
     619        $search = isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '';
     620
     621        if ( strlen( $search ) < 2 ) {
     622            wp_send_json_success( array() );
     623        }
     624
     625        // Determine post types — defaults to page, but caller can request additional types.
     626        $raw_types  = isset( $_POST['post_types'] ) ? sanitize_text_field( wp_unslash( $_POST['post_types'] ) ) : 'page';
     627        $allowed    = array( 'page', 'product' );
     628        $post_types = array_intersect(
     629            array_map( 'trim', explode( ',', $raw_types ) ),
     630            $allowed
     631        );
     632
     633        if ( empty( $post_types ) ) {
     634            $post_types = array( 'page' );
     635        }
     636
     637        $query = new WP_Query(
     638            array(
     639                's'              => $search,
     640                'post_type'      => $post_types,
     641                'post_status'    => 'publish',
     642                'posts_per_page' => 20,
     643                'orderby'        => 'relevance',
     644                'no_found_rows'  => true,
     645            )
     646        );
     647
     648        $results = array();
     649        foreach ( $query->posts as $post ) {
     650            $results[] = array(
     651                'id'    => $post->ID,
     652                'title' => $post->post_title,
     653                'url'   => get_permalink( $post ),
     654                'type'  => $post->post_type,
     655            );
     656        }
     657
     658        wp_send_json_success( $results );
     659    }
    601660}
  • ai-discovery-files/tags/1.2.0/admin/css/admin.css

    r3475304 r3482407  
    580580
    581581/* --------------------------------------------------------
    582    9c. Page Picker (URL + dropdown)
     582   9c. Page Search Autocomplete (URL + AJAX search)
    583583   -------------------------------------------------------- */
    584584.aidf-url-with-picker {
     
    594594}
    595595
    596 .aidf-page-picker {
    597     display: block;
    598     width: auto;
     596.aidf-page-search {
     597    position: relative;
     598    flex-shrink: 0;
    599599    min-width: 160px;
    600600    max-width: 220px;
    601     padding: var(--aidf-sp-2) 36px var(--aidf-sp-2) var(--aidf-sp-3);
     601}
     602
     603.aidf-page-search__input {
     604    display: block;
     605    width: 100%;
     606    padding: var(--aidf-sp-2) var(--aidf-sp-3);
    602607    font-family: var(--aidf-font);
    603608    font-size: var(--aidf-text-sm);
    604     color: var(--aidf-text-secondary);
     609    color: var(--aidf-text);
    605610    background: var(--aidf-white);
    606611    border: 1px solid var(--aidf-border);
     
    608613    transition: border-color var(--aidf-transition), box-shadow var(--aidf-transition);
    609614    line-height: 1.5;
    610     appearance: none;
    611     background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M6 8L1 3h10z' fill='%238b8fa3'/%3E%3C/svg%3E");
    612     background-repeat: no-repeat;
    613     background-position: right 12px center;
    614     cursor: pointer;
    615     flex-shrink: 0;
    616 }
    617 
    618 .aidf-page-picker:focus {
     615    box-sizing: border-box;
     616}
     617
     618.aidf-page-search__input:focus {
    619619    outline: none;
    620620    border-color: var(--aidf-orange);
    621621    box-shadow: 0 0 0 3px var(--aidf-orange-glow);
     622}
     623
     624.aidf-page-search__results {
     625    display: none;
     626    position: absolute;
     627    top: 100%;
     628    left: 0;
     629    right: 0;
     630    margin: 4px 0 0;
     631    padding: 0;
     632    list-style: none;
     633    background: var(--aidf-white);
     634    border: 1px solid var(--aidf-border);
     635    border-radius: var(--aidf-radius);
     636    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
     637    max-height: 240px;
     638    overflow-y: auto;
     639    z-index: 100;
     640}
     641
     642.aidf-page-search__results--open {
     643    display: block;
     644}
     645
     646.aidf-page-search__item {
     647    display: flex;
     648    align-items: center;
     649    justify-content: space-between;
     650    gap: var(--aidf-sp-2);
     651    padding: var(--aidf-sp-2) var(--aidf-sp-3);
     652    cursor: pointer;
     653    transition: background-color 0.15s ease;
     654}
     655
     656.aidf-page-search__item:hover,
     657.aidf-page-search__item--active {
     658    background-color: var(--aidf-orange-glow);
     659}
     660
     661.aidf-page-search__title {
     662    flex: 1;
     663    min-width: 0;
     664    overflow: hidden;
     665    text-overflow: ellipsis;
     666    white-space: nowrap;
     667    font-size: var(--aidf-text-sm);
     668    color: var(--aidf-text);
     669}
     670
     671.aidf-page-search__type {
     672    flex-shrink: 0;
     673    font-size: 0.6875rem;
     674    color: var(--aidf-text-secondary);
     675    background: var(--aidf-bg);
     676    padding: 1px 6px;
     677    border-radius: 3px;
     678    text-transform: capitalize;
     679}
     680
     681.aidf-page-search__status {
     682    padding: var(--aidf-sp-2) var(--aidf-sp-3);
     683    font-size: var(--aidf-text-sm);
     684    color: var(--aidf-text-secondary);
     685    font-style: italic;
    622686}
    623687
     
    628692    }
    629693
    630     .aidf-page-picker {
     694    .aidf-page-search {
    631695        max-width: none;
    632696        min-width: 0;
  • ai-discovery-files/tags/1.2.0/admin/js/admin.js

    r3475304 r3482407  
    401401
    402402        /* -------------------------------------------------------
    403            Page Picker — populate URL input from dropdown
    404            ------------------------------------------------------- */
    405         $('.aidf-page-picker').on('change', function () {
    406             var $select = $(this);
    407             var url = $select.val();
    408 
    409             if (url) {
    410                 $('#' + $select.data('target')).val(url);
    411                 $select.val('');
    412             }
    413         });
     403           Page Search Autocomplete
     404           ------------------------------------------------------- */
     405        (function () {
     406            var debounceTimers = {};
     407
     408            /**
     409             * Perform the AJAX search and render results.
     410             *
     411             * @param {jQuery} $wrapper  The .aidf-page-search container.
     412             * @param {string} query     The search string.
     413             */
     414            function doSearch($wrapper, query) {
     415                var $results   = $wrapper.find('.aidf-page-search__results');
     416                var postTypes  = $wrapper.data('post-types') || 'page';
     417
     418                if (query.length < 2) {
     419                    $results.empty().removeClass('aidf-page-search__results--open');
     420                    $wrapper.find('.aidf-page-search__input').attr('aria-expanded', 'false');
     421                    return;
     422                }
     423
     424                $results.html('<li class="aidf-page-search__status">' + escAttr(aidfAdmin.i18n.searching) + '</li>')
     425                        .addClass('aidf-page-search__results--open');
     426                $wrapper.find('.aidf-page-search__input').attr('aria-expanded', 'true');
     427
     428                $.post(aidfAdmin.ajaxUrl, {
     429                    action:     'aidf_search_pages',
     430                    nonce:      aidfAdmin.nonce,
     431                    search:     query,
     432                    post_types: postTypes
     433                }, function (response) {
     434                    $results.empty();
     435
     436                    if (!response.success || !response.data.length) {
     437                        $results.html('<li class="aidf-page-search__status">' + escAttr(aidfAdmin.i18n.noResults) + '</li>');
     438                        return;
     439                    }
     440
     441                    $.each(response.data, function (i, page) {
     442                        $results.append(
     443                            '<li class="aidf-page-search__item" role="option" data-url="' + escAttr(page.url) + '">' +
     444                                '<span class="aidf-page-search__title">' + escAttr(page.title) + '</span>' +
     445                                (page.type !== 'page' ? '<span class="aidf-page-search__type">' + escAttr(page.type) + '</span>' : '') +
     446                            '</li>'
     447                        );
     448                    });
     449                }).fail(function () {
     450                    $results.html('<li class="aidf-page-search__status">' + escAttr(aidfAdmin.i18n.noResults) + '</li>');
     451                });
     452            }
     453
     454            // Debounced input handler.
     455            $(document).on('input', '.aidf-page-search__input', function () {
     456                var $input   = $(this);
     457                var $wrapper = $input.closest('.aidf-page-search');
     458                var id       = $wrapper.data('target');
     459
     460                clearTimeout(debounceTimers[id]);
     461                debounceTimers[id] = setTimeout(function () {
     462                    doSearch($wrapper, $.trim($input.val()));
     463                }, 300);
     464            });
     465
     466            // Click on a result.
     467            $(document).on('click', '.aidf-page-search__item', function () {
     468                var $item    = $(this);
     469                var $wrapper = $item.closest('.aidf-page-search');
     470                var targetId = $wrapper.data('target');
     471
     472                $('#' + targetId).val($item.data('url'));
     473                $wrapper.find('.aidf-page-search__input').val('').attr('aria-expanded', 'false');
     474                $wrapper.find('.aidf-page-search__results').empty().removeClass('aidf-page-search__results--open');
     475            });
     476
     477            // Keyboard navigation.
     478            $(document).on('keydown', '.aidf-page-search__input', function (e) {
     479                var $wrapper = $(this).closest('.aidf-page-search');
     480                var $results = $wrapper.find('.aidf-page-search__results');
     481                var $items   = $results.find('.aidf-page-search__item');
     482                var $active  = $items.filter('.aidf-page-search__item--active');
     483                var index    = $items.index($active);
     484
     485                if (e.keyCode === 40) { // Down
     486                    e.preventDefault();
     487                    index = index < $items.length - 1 ? index + 1 : 0;
     488                    $items.removeClass('aidf-page-search__item--active');
     489                    $items.eq(index).addClass('aidf-page-search__item--active');
     490                } else if (e.keyCode === 38) { // Up
     491                    e.preventDefault();
     492                    index = index > 0 ? index - 1 : $items.length - 1;
     493                    $items.removeClass('aidf-page-search__item--active');
     494                    $items.eq(index).addClass('aidf-page-search__item--active');
     495                } else if (e.keyCode === 13) { // Enter
     496                    e.preventDefault();
     497                    if ($active.length) {
     498                        $active.trigger('click');
     499                    }
     500                } else if (e.keyCode === 27) { // Escape
     501                    $results.empty().removeClass('aidf-page-search__results--open');
     502                    $(this).attr('aria-expanded', 'false');
     503                }
     504            });
     505
     506            // Close on click outside.
     507            $(document).on('click', function (e) {
     508                if (!$(e.target).closest('.aidf-page-search').length) {
     509                    $('.aidf-page-search__results').empty().removeClass('aidf-page-search__results--open');
     510                    $('.aidf-page-search__input').attr('aria-expanded', 'false');
     511                }
     512            });
     513        })();
    414514
    415515        /* -------------------------------------------------------
  • ai-discovery-files/tags/1.2.0/admin/views/tab-identity.php

    r3472044 r3482407  
    1414
    1515$settings   = AIDF_Plugin::get_settings();
    16 $site_pages = get_pages( array( 'post_status' => 'publish', 'sort_column' => 'post_title', 'number' => 100 ) );
    17 if ( ! is_array( $site_pages ) ) {
    18     $site_pages = array();
    19 }
    2016
    2117// Normalise legacy services string to array for the repeater.
     
    157153                       class="aidf-input"
    158154                       placeholder="https://">
    159                 <select class="aidf-page-picker" data-target="aidf-contact-url">
    160                     <option value=""><?php esc_html_e( 'Select a page…', 'ai-discovery-files' ); ?></option>
    161                     <?php foreach ( $site_pages as $page ) : ?>
    162                         <option value="<?php echo esc_url( get_permalink( $page ) ); ?>"><?php echo esc_html( $page->post_title ); ?></option>
    163                     <?php endforeach; ?>
    164                 </select>
     155                <div class="aidf-page-search" data-target="aidf-contact-url">
     156                    <input type="text" class="aidf-page-search__input" placeholder="<?php esc_attr_e( 'Search pages…', 'ai-discovery-files' ); ?>" autocomplete="off" role="combobox" aria-expanded="false" aria-autocomplete="list">
     157                    <ul class="aidf-page-search__results" role="listbox" aria-live="polite"></ul>
     158                </div>
    165159            </div>
    166160        </div>
     
    410404                <div class="aidf-url-with-picker">
    411405                    <input type="url" id="aidf-products-url" name="aidf_settings[products_url]" value="<?php echo esc_url( $settings['products_url'] ); ?>" class="aidf-input" placeholder="https://">
    412                     <select class="aidf-page-picker" data-target="aidf-products-url">
    413                         <option value=""><?php esc_html_e( 'Select a page…', 'ai-discovery-files' ); ?></option>
    414                         <?php foreach ( $site_pages as $page ) : ?>
    415                             <option value="<?php echo esc_url( get_permalink( $page ) ); ?>"><?php echo esc_html( $page->post_title ); ?></option>
    416                         <?php endforeach; ?>
    417                     </select>
     406                    <div class="aidf-page-search" data-target="aidf-products-url" data-post-types="page,product">
     407                        <input type="text" class="aidf-page-search__input" placeholder="<?php esc_attr_e( 'Search pages…', 'ai-discovery-files' ); ?>" autocomplete="off" role="combobox" aria-expanded="false" aria-autocomplete="list">
     408                        <ul class="aidf-page-search__results" role="listbox" aria-live="polite"></ul>
     409                    </div>
    418410                </div>
    419411                <p class="aidf-description"><?php esc_html_e( 'Linked from llms.txt as a key commercial page.', 'ai-discovery-files' ); ?></p>
     
    602594            <div class="aidf-url-with-picker">
    603595                <input type="url" id="aidf-privacy-url" name="aidf_settings[privacy_policy_url]" value="<?php echo esc_url( $settings['privacy_policy_url'] ); ?>" class="aidf-input" placeholder="https://">
    604                 <select class="aidf-page-picker" data-target="aidf-privacy-url">
    605                     <option value=""><?php esc_html_e( 'Select a page…', 'ai-discovery-files' ); ?></option>
    606                     <?php foreach ( $site_pages as $page ) : ?>
    607                         <option value="<?php echo esc_url( get_permalink( $page ) ); ?>"><?php echo esc_html( $page->post_title ); ?></option>
    608                     <?php endforeach; ?>
    609                 </select>
     596                <div class="aidf-page-search" data-target="aidf-privacy-url">
     597                    <input type="text" class="aidf-page-search__input" placeholder="<?php esc_attr_e( 'Search pages…', 'ai-discovery-files' ); ?>" autocomplete="off" role="combobox" aria-expanded="false" aria-autocomplete="list">
     598                    <ul class="aidf-page-search__results" role="listbox" aria-live="polite"></ul>
     599                </div>
    610600            </div>
    611601            <p class="aidf-description"><?php esc_html_e( 'Auto-detected from WordPress Settings if set.', 'ai-discovery-files' ); ?></p>
     
    616606            <div class="aidf-url-with-picker">
    617607                <input type="url" id="aidf-terms-url" name="aidf_settings[terms_url]" value="<?php echo esc_url( $settings['terms_url'] ); ?>" class="aidf-input" placeholder="https://">
    618                 <select class="aidf-page-picker" data-target="aidf-terms-url">
    619                     <option value=""><?php esc_html_e( 'Select a page…', 'ai-discovery-files' ); ?></option>
    620                     <?php foreach ( $site_pages as $page ) : ?>
    621                         <option value="<?php echo esc_url( get_permalink( $page ) ); ?>"><?php echo esc_html( $page->post_title ); ?></option>
    622                     <?php endforeach; ?>
    623                 </select>
     608                <div class="aidf-page-search" data-target="aidf-terms-url">
     609                    <input type="text" class="aidf-page-search__input" placeholder="<?php esc_attr_e( 'Search pages…', 'ai-discovery-files' ); ?>" autocomplete="off" role="combobox" aria-expanded="false" aria-autocomplete="list">
     610                    <ul class="aidf-page-search__results" role="listbox" aria-live="polite"></ul>
     611                </div>
    624612            </div>
    625613        </div>
  • ai-discovery-files/tags/1.2.0/includes/class-data-collector.php

    r3472044 r3482407  
    167167                'sort_column' => 'menu_order,post_title',
    168168                'post_status' => 'publish',
    169                 'number'      => 50,
     169                'number'      => 200,
    170170            )
    171171        );
  • ai-discovery-files/trunk/admin/class-admin.php

    r3475304 r3482407  
    3838        add_action( 'wp_ajax_aidf_save_verify_code', array( __CLASS__, 'ajax_save_verify_code' ) );
    3939        add_action( 'wp_ajax_aidf_dismiss_review', array( __CLASS__, 'ajax_dismiss_review' ) );
     40        add_action( 'wp_ajax_aidf_search_pages', array( __CLASS__, 'ajax_search_pages' ) );
    4041        add_action( 'admin_notices', array( __CLASS__, 'maybe_show_welcome_notice' ) );
    4142        add_action( 'admin_notices', array( __CLASS__, 'maybe_show_conflict_notice' ) );
     
    118119                    'verifyFileActive'   => __( 'Verification file is live at', 'ai-discovery-files' ),
    119120                    'verifyFileInactive' => __( 'No verification code set. Enter your code above to activate.', 'ai-discovery-files' ),
     121                    'searchPages'     => __( 'Search pages…', 'ai-discovery-files' ),
     122                    'searching'       => __( 'Searching…', 'ai-discovery-files' ),
     123                    'noResults'       => __( 'No pages found', 'ai-discovery-files' ),
     124                    'typeToSearch'    => __( 'Type to search…', 'ai-discovery-files' ),
    120125                ),
    121126            )
     
    599604        wp_send_json_success();
    600605    }
     606
     607    /**
     608     * AJAX handler: search pages for the page picker autocomplete.
     609     *
     610     * @return void
     611     */
     612    public static function ajax_search_pages() {
     613        check_ajax_referer( 'aidf_admin_nonce', 'nonce' );
     614
     615        if ( ! current_user_can( 'manage_options' ) ) {
     616            wp_send_json_error( __( 'Permission denied.', 'ai-discovery-files' ) );
     617        }
     618
     619        $search = isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '';
     620
     621        if ( strlen( $search ) < 2 ) {
     622            wp_send_json_success( array() );
     623        }
     624
     625        // Determine post types — defaults to page, but caller can request additional types.
     626        $raw_types  = isset( $_POST['post_types'] ) ? sanitize_text_field( wp_unslash( $_POST['post_types'] ) ) : 'page';
     627        $allowed    = array( 'page', 'product' );
     628        $post_types = array_intersect(
     629            array_map( 'trim', explode( ',', $raw_types ) ),
     630            $allowed
     631        );
     632
     633        if ( empty( $post_types ) ) {
     634            $post_types = array( 'page' );
     635        }
     636
     637        $query = new WP_Query(
     638            array(
     639                's'              => $search,
     640                'post_type'      => $post_types,
     641                'post_status'    => 'publish',
     642                'posts_per_page' => 20,
     643                'orderby'        => 'relevance',
     644                'no_found_rows'  => true,
     645            )
     646        );
     647
     648        $results = array();
     649        foreach ( $query->posts as $post ) {
     650            $results[] = array(
     651                'id'    => $post->ID,
     652                'title' => $post->post_title,
     653                'url'   => get_permalink( $post ),
     654                'type'  => $post->post_type,
     655            );
     656        }
     657
     658        wp_send_json_success( $results );
     659    }
    601660}
  • ai-discovery-files/trunk/admin/css/admin.css

    r3475304 r3482407  
    580580
    581581/* --------------------------------------------------------
    582    9c. Page Picker (URL + dropdown)
     582   9c. Page Search Autocomplete (URL + AJAX search)
    583583   -------------------------------------------------------- */
    584584.aidf-url-with-picker {
     
    594594}
    595595
    596 .aidf-page-picker {
    597     display: block;
    598     width: auto;
     596.aidf-page-search {
     597    position: relative;
     598    flex-shrink: 0;
    599599    min-width: 160px;
    600600    max-width: 220px;
    601     padding: var(--aidf-sp-2) 36px var(--aidf-sp-2) var(--aidf-sp-3);
     601}
     602
     603.aidf-page-search__input {
     604    display: block;
     605    width: 100%;
     606    padding: var(--aidf-sp-2) var(--aidf-sp-3);
    602607    font-family: var(--aidf-font);
    603608    font-size: var(--aidf-text-sm);
    604     color: var(--aidf-text-secondary);
     609    color: var(--aidf-text);
    605610    background: var(--aidf-white);
    606611    border: 1px solid var(--aidf-border);
     
    608613    transition: border-color var(--aidf-transition), box-shadow var(--aidf-transition);
    609614    line-height: 1.5;
    610     appearance: none;
    611     background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M6 8L1 3h10z' fill='%238b8fa3'/%3E%3C/svg%3E");
    612     background-repeat: no-repeat;
    613     background-position: right 12px center;
    614     cursor: pointer;
    615     flex-shrink: 0;
    616 }
    617 
    618 .aidf-page-picker:focus {
     615    box-sizing: border-box;
     616}
     617
     618.aidf-page-search__input:focus {
    619619    outline: none;
    620620    border-color: var(--aidf-orange);
    621621    box-shadow: 0 0 0 3px var(--aidf-orange-glow);
     622}
     623
     624.aidf-page-search__results {
     625    display: none;
     626    position: absolute;
     627    top: 100%;
     628    left: 0;
     629    right: 0;
     630    margin: 4px 0 0;
     631    padding: 0;
     632    list-style: none;
     633    background: var(--aidf-white);
     634    border: 1px solid var(--aidf-border);
     635    border-radius: var(--aidf-radius);
     636    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
     637    max-height: 240px;
     638    overflow-y: auto;
     639    z-index: 100;
     640}
     641
     642.aidf-page-search__results--open {
     643    display: block;
     644}
     645
     646.aidf-page-search__item {
     647    display: flex;
     648    align-items: center;
     649    justify-content: space-between;
     650    gap: var(--aidf-sp-2);
     651    padding: var(--aidf-sp-2) var(--aidf-sp-3);
     652    cursor: pointer;
     653    transition: background-color 0.15s ease;
     654}
     655
     656.aidf-page-search__item:hover,
     657.aidf-page-search__item--active {
     658    background-color: var(--aidf-orange-glow);
     659}
     660
     661.aidf-page-search__title {
     662    flex: 1;
     663    min-width: 0;
     664    overflow: hidden;
     665    text-overflow: ellipsis;
     666    white-space: nowrap;
     667    font-size: var(--aidf-text-sm);
     668    color: var(--aidf-text);
     669}
     670
     671.aidf-page-search__type {
     672    flex-shrink: 0;
     673    font-size: 0.6875rem;
     674    color: var(--aidf-text-secondary);
     675    background: var(--aidf-bg);
     676    padding: 1px 6px;
     677    border-radius: 3px;
     678    text-transform: capitalize;
     679}
     680
     681.aidf-page-search__status {
     682    padding: var(--aidf-sp-2) var(--aidf-sp-3);
     683    font-size: var(--aidf-text-sm);
     684    color: var(--aidf-text-secondary);
     685    font-style: italic;
    622686}
    623687
     
    628692    }
    629693
    630     .aidf-page-picker {
     694    .aidf-page-search {
    631695        max-width: none;
    632696        min-width: 0;
  • ai-discovery-files/trunk/admin/js/admin.js

    r3475304 r3482407  
    401401
    402402        /* -------------------------------------------------------
    403            Page Picker — populate URL input from dropdown
    404            ------------------------------------------------------- */
    405         $('.aidf-page-picker').on('change', function () {
    406             var $select = $(this);
    407             var url = $select.val();
    408 
    409             if (url) {
    410                 $('#' + $select.data('target')).val(url);
    411                 $select.val('');
    412             }
    413         });
     403           Page Search Autocomplete
     404           ------------------------------------------------------- */
     405        (function () {
     406            var debounceTimers = {};
     407
     408            /**
     409             * Perform the AJAX search and render results.
     410             *
     411             * @param {jQuery} $wrapper  The .aidf-page-search container.
     412             * @param {string} query     The search string.
     413             */
     414            function doSearch($wrapper, query) {
     415                var $results   = $wrapper.find('.aidf-page-search__results');
     416                var postTypes  = $wrapper.data('post-types') || 'page';
     417
     418                if (query.length < 2) {
     419                    $results.empty().removeClass('aidf-page-search__results--open');
     420                    $wrapper.find('.aidf-page-search__input').attr('aria-expanded', 'false');
     421                    return;
     422                }
     423
     424                $results.html('<li class="aidf-page-search__status">' + escAttr(aidfAdmin.i18n.searching) + '</li>')
     425                        .addClass('aidf-page-search__results--open');
     426                $wrapper.find('.aidf-page-search__input').attr('aria-expanded', 'true');
     427
     428                $.post(aidfAdmin.ajaxUrl, {
     429                    action:     'aidf_search_pages',
     430                    nonce:      aidfAdmin.nonce,
     431                    search:     query,
     432                    post_types: postTypes
     433                }, function (response) {
     434                    $results.empty();
     435
     436                    if (!response.success || !response.data.length) {
     437                        $results.html('<li class="aidf-page-search__status">' + escAttr(aidfAdmin.i18n.noResults) + '</li>');
     438                        return;
     439                    }
     440
     441                    $.each(response.data, function (i, page) {
     442                        $results.append(
     443                            '<li class="aidf-page-search__item" role="option" data-url="' + escAttr(page.url) + '">' +
     444                                '<span class="aidf-page-search__title">' + escAttr(page.title) + '</span>' +
     445                                (page.type !== 'page' ? '<span class="aidf-page-search__type">' + escAttr(page.type) + '</span>' : '') +
     446                            '</li>'
     447                        );
     448                    });
     449                }).fail(function () {
     450                    $results.html('<li class="aidf-page-search__status">' + escAttr(aidfAdmin.i18n.noResults) + '</li>');
     451                });
     452            }
     453
     454            // Debounced input handler.
     455            $(document).on('input', '.aidf-page-search__input', function () {
     456                var $input   = $(this);
     457                var $wrapper = $input.closest('.aidf-page-search');
     458                var id       = $wrapper.data('target');
     459
     460                clearTimeout(debounceTimers[id]);
     461                debounceTimers[id] = setTimeout(function () {
     462                    doSearch($wrapper, $.trim($input.val()));
     463                }, 300);
     464            });
     465
     466            // Click on a result.
     467            $(document).on('click', '.aidf-page-search__item', function () {
     468                var $item    = $(this);
     469                var $wrapper = $item.closest('.aidf-page-search');
     470                var targetId = $wrapper.data('target');
     471
     472                $('#' + targetId).val($item.data('url'));
     473                $wrapper.find('.aidf-page-search__input').val('').attr('aria-expanded', 'false');
     474                $wrapper.find('.aidf-page-search__results').empty().removeClass('aidf-page-search__results--open');
     475            });
     476
     477            // Keyboard navigation.
     478            $(document).on('keydown', '.aidf-page-search__input', function (e) {
     479                var $wrapper = $(this).closest('.aidf-page-search');
     480                var $results = $wrapper.find('.aidf-page-search__results');
     481                var $items   = $results.find('.aidf-page-search__item');
     482                var $active  = $items.filter('.aidf-page-search__item--active');
     483                var index    = $items.index($active);
     484
     485                if (e.keyCode === 40) { // Down
     486                    e.preventDefault();
     487                    index = index < $items.length - 1 ? index + 1 : 0;
     488                    $items.removeClass('aidf-page-search__item--active');
     489                    $items.eq(index).addClass('aidf-page-search__item--active');
     490                } else if (e.keyCode === 38) { // Up
     491                    e.preventDefault();
     492                    index = index > 0 ? index - 1 : $items.length - 1;
     493                    $items.removeClass('aidf-page-search__item--active');
     494                    $items.eq(index).addClass('aidf-page-search__item--active');
     495                } else if (e.keyCode === 13) { // Enter
     496                    e.preventDefault();
     497                    if ($active.length) {
     498                        $active.trigger('click');
     499                    }
     500                } else if (e.keyCode === 27) { // Escape
     501                    $results.empty().removeClass('aidf-page-search__results--open');
     502                    $(this).attr('aria-expanded', 'false');
     503                }
     504            });
     505
     506            // Close on click outside.
     507            $(document).on('click', function (e) {
     508                if (!$(e.target).closest('.aidf-page-search').length) {
     509                    $('.aidf-page-search__results').empty().removeClass('aidf-page-search__results--open');
     510                    $('.aidf-page-search__input').attr('aria-expanded', 'false');
     511                }
     512            });
     513        })();
    414514
    415515        /* -------------------------------------------------------
  • ai-discovery-files/trunk/admin/views/partials/review-banner.php

    r3475304 r3482407  
    1919        <span class="aidf-review-banner__text"><?php esc_html_e( 'Enjoying AI Discovery Files?', 'ai-discovery-files' ); ?></span>
    2020        <span class="aidf-review-banner__stars" id="aidf-review-stars" role="radiogroup" aria-label="<?php esc_attr_e( 'Rate from 1 to 5 stars', 'ai-discovery-files' ); ?>">
    21             <?php for ( $i = 1; $i <= 5; $i++ ) : ?>
    22                 <button type="button" class="aidf-review-star" data-rating="<?php echo esc_attr( $i ); ?>" aria-label="<?php echo esc_attr( sprintf( __( '%d star', 'ai-discovery-files' ), $i ) ); ?>">
     21            <?php for ( $aidf_star = 1; $aidf_star <= 5; $aidf_star++ ) : ?>
     22                <?php /* translators: %d: star rating number (1-5) */ ?>
     23                <button type="button" class="aidf-review-star" data-rating="<?php echo esc_attr( $aidf_star ); ?>" aria-label="<?php echo esc_attr( sprintf( __( '%d star', 'ai-discovery-files' ), $aidf_star ) ); ?>">
    2324                    <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
    2425                        <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
  • ai-discovery-files/trunk/admin/views/tab-identity.php

    r3472044 r3482407  
    1414
    1515$settings   = AIDF_Plugin::get_settings();
    16 $site_pages = get_pages( array( 'post_status' => 'publish', 'sort_column' => 'post_title', 'number' => 100 ) );
    17 if ( ! is_array( $site_pages ) ) {
    18     $site_pages = array();
    19 }
    2016
    2117// Normalise legacy services string to array for the repeater.
     
    157153                       class="aidf-input"
    158154                       placeholder="https://">
    159                 <select class="aidf-page-picker" data-target="aidf-contact-url">
    160                     <option value=""><?php esc_html_e( 'Select a page…', 'ai-discovery-files' ); ?></option>
    161                     <?php foreach ( $site_pages as $page ) : ?>
    162                         <option value="<?php echo esc_url( get_permalink( $page ) ); ?>"><?php echo esc_html( $page->post_title ); ?></option>
    163                     <?php endforeach; ?>
    164                 </select>
     155                <div class="aidf-page-search" data-target="aidf-contact-url">
     156                    <input type="text" class="aidf-page-search__input" placeholder="<?php esc_attr_e( 'Search pages…', 'ai-discovery-files' ); ?>" autocomplete="off" role="combobox" aria-expanded="false" aria-autocomplete="list">
     157                    <ul class="aidf-page-search__results" role="listbox" aria-live="polite"></ul>
     158                </div>
    165159            </div>
    166160        </div>
     
    410404                <div class="aidf-url-with-picker">
    411405                    <input type="url" id="aidf-products-url" name="aidf_settings[products_url]" value="<?php echo esc_url( $settings['products_url'] ); ?>" class="aidf-input" placeholder="https://">
    412                     <select class="aidf-page-picker" data-target="aidf-products-url">
    413                         <option value=""><?php esc_html_e( 'Select a page…', 'ai-discovery-files' ); ?></option>
    414                         <?php foreach ( $site_pages as $page ) : ?>
    415                             <option value="<?php echo esc_url( get_permalink( $page ) ); ?>"><?php echo esc_html( $page->post_title ); ?></option>
    416                         <?php endforeach; ?>
    417                     </select>
     406                    <div class="aidf-page-search" data-target="aidf-products-url" data-post-types="page,product">
     407                        <input type="text" class="aidf-page-search__input" placeholder="<?php esc_attr_e( 'Search pages…', 'ai-discovery-files' ); ?>" autocomplete="off" role="combobox" aria-expanded="false" aria-autocomplete="list">
     408                        <ul class="aidf-page-search__results" role="listbox" aria-live="polite"></ul>
     409                    </div>
    418410                </div>
    419411                <p class="aidf-description"><?php esc_html_e( 'Linked from llms.txt as a key commercial page.', 'ai-discovery-files' ); ?></p>
     
    602594            <div class="aidf-url-with-picker">
    603595                <input type="url" id="aidf-privacy-url" name="aidf_settings[privacy_policy_url]" value="<?php echo esc_url( $settings['privacy_policy_url'] ); ?>" class="aidf-input" placeholder="https://">
    604                 <select class="aidf-page-picker" data-target="aidf-privacy-url">
    605                     <option value=""><?php esc_html_e( 'Select a page…', 'ai-discovery-files' ); ?></option>
    606                     <?php foreach ( $site_pages as $page ) : ?>
    607                         <option value="<?php echo esc_url( get_permalink( $page ) ); ?>"><?php echo esc_html( $page->post_title ); ?></option>
    608                     <?php endforeach; ?>
    609                 </select>
     596                <div class="aidf-page-search" data-target="aidf-privacy-url">
     597                    <input type="text" class="aidf-page-search__input" placeholder="<?php esc_attr_e( 'Search pages…', 'ai-discovery-files' ); ?>" autocomplete="off" role="combobox" aria-expanded="false" aria-autocomplete="list">
     598                    <ul class="aidf-page-search__results" role="listbox" aria-live="polite"></ul>
     599                </div>
    610600            </div>
    611601            <p class="aidf-description"><?php esc_html_e( 'Auto-detected from WordPress Settings if set.', 'ai-discovery-files' ); ?></p>
     
    616606            <div class="aidf-url-with-picker">
    617607                <input type="url" id="aidf-terms-url" name="aidf_settings[terms_url]" value="<?php echo esc_url( $settings['terms_url'] ); ?>" class="aidf-input" placeholder="https://">
    618                 <select class="aidf-page-picker" data-target="aidf-terms-url">
    619                     <option value=""><?php esc_html_e( 'Select a page…', 'ai-discovery-files' ); ?></option>
    620                     <?php foreach ( $site_pages as $page ) : ?>
    621                         <option value="<?php echo esc_url( get_permalink( $page ) ); ?>"><?php echo esc_html( $page->post_title ); ?></option>
    622                     <?php endforeach; ?>
    623                 </select>
     608                <div class="aidf-page-search" data-target="aidf-terms-url">
     609                    <input type="text" class="aidf-page-search__input" placeholder="<?php esc_attr_e( 'Search pages…', 'ai-discovery-files' ); ?>" autocomplete="off" role="combobox" aria-expanded="false" aria-autocomplete="list">
     610                    <ul class="aidf-page-search__results" role="listbox" aria-live="polite"></ul>
     611                </div>
    624612            </div>
    625613        </div>
  • ai-discovery-files/trunk/ai-discovery-files.php

    r3479765 r3482407  
    44 * Plugin URI:        https://www.ai-visibility.org.uk/wordpress-plugin/ai-discovery-files/
    55 * Description:       Generate AI Discovery Files so AI systems like ChatGPT, Claude, and Gemini can correctly discover and cite your website. Built by 365i.
    6  * Version:           1.2.0
     6 * Version:           1.2.1
    77 * Requires at least: 6.2
    88 * Requires PHP:      8.0
     
    2424 * Plugin constants.
    2525 */
    26 define( 'AIDF_VERSION', '1.2.0' );
     26define( 'AIDF_VERSION', '1.2.1' );
    2727define( 'AIDF_PLUGIN_FILE', __FILE__ );
    2828define( 'AIDF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
  • ai-discovery-files/trunk/includes/class-data-collector.php

    r3472044 r3482407  
    167167                'sort_column' => 'menu_order,post_title',
    168168                'post_status' => 'publish',
    169                 'number'      => 50,
     169                'number'      => 200,
    170170            )
    171171        );
  • ai-discovery-files/trunk/readme.txt

    r3479765 r3482407  
    55Tested up to: 6.9
    66Requires PHP: 8.0
    7 Stable tag: 1.2.0
     7Stable tag: 1.2.1
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    167167
    168168== Changelog ==
     169
     170= 1.2.1 =
     171* Replace static page picker dropdowns with AJAX-powered search autocomplete
     172* Page pickers now scale to sites with hundreds or thousands of pages
     173* Products/Pricing field also searches WooCommerce product pages
     174* Full keyboard navigation (arrow keys, Enter, Escape) and ARIA accessibility
     175* Increase page limit in generated AI discovery files from 50 to 200
    169176
    170177= 1.2.0 =
Note: See TracChangeset for help on using the changeset viewer.