' . esc_html__( 'Token recovered!', 'ai-search' ) . ' ' . esc_html__( 'A fresh API token was issued for this site. Your existing usage limits and quotas were preserved.', 'ai-search' ) . '

'; } elseif ( $recovery_status === 'failed' ) { echo '

' . esc_html__( 'Token recovery failed.', 'ai-search' ) . ' ' . esc_html__( 'Could not issue a new token. Please try again in a few minutes or contact support.', 'ai-search' ) . '

'; } } // Handle connection check messages if ( isset( $_GET['token_validation'] ) ) { $validation_status = sanitize_text_field( $_GET['token_validation'] ); if ( $validation_status === 'success' ) { $validation_data = get_transient( 'ai_search_validation_data' ); if ( $validation_data ) { echo '

' . esc_html__( 'Connection successful!', 'ai-search' ) . ' ' . esc_html__( 'AI Search Service is working correctly.', 'ai-search' ) . '

'; if ( isset( $validation_data['client'] ) ) { $client = $validation_data['client']; $used_total = intval( $client['used_total'] ?? 0 ); $total_limit = intval( $client['total_limit'] ?? 10000 ); $remaining = $total_limit - $used_total; echo '

' . sprintf( esc_html__( 'Embeddings used: %1$s / %2$s (%3$s remaining)', 'ai-search' ), '' . esc_html( number_format( $used_total ) ) . '', esc_html( number_format( $total_limit ) ), esc_html( number_format( $remaining ) ) ) . '

'; } echo '
'; delete_transient( 'ai_search_validation_data' ); } } elseif ( $validation_status === 'invalid' ) { $error_data = get_transient( 'ai_search_validation_error' ); echo '

' . esc_html__( 'Connection failed!', 'ai-search' ) . '

'; if ( $error_data && isset( $error_data['details'] ) ) { echo '

' . esc_html( $error_data['details'] ) . '

'; } // The service returns error: "Invalid token" specifically when the // token isn't found in its DB (vs. "Origin mismatch" which has its // own dedicated reconnect flow). Offer one-click recovery only for // that case so we don't show a misleading button for other errors. $is_invalid_token = $error_data && isset( $error_data['error'] ) && $error_data['error'] === 'Invalid token'; if ( $is_invalid_token ) { $recover_url = wp_nonce_url( admin_url( 'admin-post.php?action=ai_search_recover_token' ), 'ai_search_recover_token' ); echo '

' . esc_html__( 'Your token is no longer recognized by the service. You can reconnect by issuing a fresh token — your usage limits and quotas will be preserved.', 'ai-search' ) . '

'; echo '

' . esc_html__( 'Reconnect with new token', 'ai-search' ) . '

'; } echo '
'; delete_transient( 'ai_search_validation_error' ); } elseif ( $validation_status === 'error' ) { echo '

' . esc_html__( 'Connection failed!', 'ai-search' ) . ' ' . esc_html__( 'Unable to reach AI Search Service. Please try again later.', 'ai-search' ) . '

'; } elseif ( $validation_status === 'no_token' ) { echo '

' . esc_html__( 'Not registered.', 'ai-search' ) . ' ' . esc_html__( 'Please save your settings to register with the AI Search Service.', 'ai-search' ) . '

'; } } // Subscription panel + activation form moved to a dedicated Upgrade / // Subscription tab in v1.37.0. Badge visibility + tooltip customisation // moved to a dedicated Appearance tab in v1.37.0. // General now hosts only the notices + service status. // Blocks shipped in 1.33.0 and are entirely opt-in, which means a store // can update and never discover the headline feature. Announce it once, // dismissible, and only while it has not been dismissed. // // v1.35.0 auto-dismiss: if the site already has an AI Search block // somewhere (post, page, template, pattern - any post_type with the // block marker in post_content), the user found the feature on their // own and the promo card is just noise. Mark it dismissed and never // render it again. Cheap LIKE query with LIMIT 1, runs once per view. if ( ! get_option( 'ai_search_blocks_notice_dismissed', false ) ) { global $wpdb; $ais_block_used = $wpdb->get_var( "SELECT 1 FROM {$wpdb->posts} WHERE post_status IN ( 'publish', 'draft', 'inherit' ) AND ( post_content LIKE '%wp:ai-search/search-box%' OR post_content LIKE '%wp:ai-search/search-button%' ) LIMIT 1" ); if ( $ais_block_used ) { update_option( 'ai_search_blocks_notice_dismissed', true ); } } // v1.44.0: Smart Matching spotlight on the General tab. Uses the same // --highlight panel style as the blocks announcement next to it so the // two sit as peers in the admin's own visual language — no gradient // shell, no shouting. Hidden once Smart Matching is already active. $sm_already_on = AI_Search::is_smart_embeddings_enabled(); $sm_locked = ! AI_Search::plan_meets( 'standard' ); if ( ! $sm_already_on ) : $sm_cta_url = $sm_locked ? 'https://wp-search.ai/pricing/?utm_source=plugin&utm_medium=spotlight&utm_campaign=smart_matching' : admin_url( 'admin.php?page=ai-search&tab=embeddings#ai_search_smart_embeddings' ); $sm_cta_label = $sm_locked ? __( 'See plans', 'ai-search' ) : __( 'Enable Smart Matching', 'ai-search' ); ?>

>