/** * WP Agentic Writer - Settings Page Scripts * * @package WP_Agentic_Writer */ jQuery( document ).ready( function( $ ) { 'use strict'; // =========================== // TAB NAVIGATION // =========================== $( '.wpaw-settings-nav-btn' ).on( 'click', function() { const tab = $( this ).data( 'tab' ); // Update nav buttons $( '.wpaw-settings-nav-btn' ).removeClass( 'active' ); $( this ).addClass( 'active' ); // Update tab content $( '.wpaw-tab-content' ).removeClass( 'active' ); $( `.wpaw-tab-content[data-tab="${tab}"]` ).addClass( 'active' ); } ); // =========================== // SELECT2 INITIALIZATION // =========================== function initSelect2() { if ( ! $.fn.select2 ) { return; } // Initialize Select2 for model dropdowns $( '.wpaw-select2-model' ).each( function() { const $select = $( this ); // Destroy existing Select2 if present if ( $select.hasClass( 'select2-hidden-accessible' ) ) { $select.select2( 'destroy' ); } $select.select2( { placeholder: 'Search for a model...', allowClear: false, width: '100%', templateResult: formatModelOption, templateSelection: formatModelSelection, } ); } ); // Initialize Select2 for Context Categories (multiple with tags) const $contextCategories = $( '#required_context_categories' ); if ( $contextCategories.length && ! $contextCategories.hasClass( 'select2-hidden-accessible' ) ) { $contextCategories.select2( { placeholder: 'Select categories...', allowClear: true, width: '100%', tags: false, closeOnSelect: false, } ); } } function formatModelOption( model ) { if ( ! model.id ) { return model.text; } const isFree = model.text && model.text.includes( '(Free)' ); const $option = $( '' + model.text + '' ); if ( isFree ) { $option.css( 'color', '#28a745' ); } return $option; } function formatModelSelection( model ) { return model.text || model.id; } // Store model pricing data from API (populated by updateModelDropdowns) let modelPricing = {}; // Estimate cost per article using dynamic pricing from API (6 models) // Estimates: chat 200 tokens, clarity 300, planning 500, writing 3K, refinement 1K, 1 image function estimateArticleCost() { const chatModel = $( '#chat_model' ).val(); const clarityModel = $( '#clarity_model' ).val(); const planningModel = $( '#planning_model' ).val(); const writingModel = $( '#writing_model' ).val(); const refinementModel = $( '#refinement_model' ).val(); const imageModel = $( '#image_model' ).val(); let totalCost = 0; // Helper to calculate cost for a model const calcCost = ( modelId, inputTokens, outputTokens ) => { const pricing = modelPricing[modelId]; if ( pricing ) { return ( pricing.prompt * inputTokens ) + ( pricing.completion * outputTokens ); } return 0; }; // Chat cost (minimal - 100 in, 100 out) totalCost += calcCost( chatModel, 100, 100 ); // Clarity cost (300 in, 300 out) totalCost += calcCost( clarityModel, 300, 300 ); // Planning cost (500 in, 500 out) totalCost += calcCost( planningModel, 500, 500 ); // Writing cost (1.5K in, 1.5K out - main article generation) totalCost += calcCost( writingModel, 1500, 1500 ); // Refinement cost (500 in, 500 out) totalCost += calcCost( refinementModel, 500, 500 ); // Image cost (1 image) const imagePricing = modelPricing[imageModel]; if ( imagePricing && imagePricing.image > 0 ) { totalCost += imagePricing.image; } return totalCost; } // Update cost estimation display function updateCostEstimate() { const cost = estimateArticleCost(); const $display = $( '#wpaw-cost-estimate' ); if ( $display.length ) { if ( cost > 0 ) { $display.text( '~$' + cost.toFixed(4) + ' per article' ); } else { $display.text( 'Free or pricing unavailable' ); } } } // Listen for model changes (6 models) $( '#chat_model, #clarity_model, #planning_model, #writing_model, #refinement_model, #image_model' ).on( 'change', function() { updateCostEstimate(); } ); // Initialize on page load updateCostEstimate(); initSelect2(); // Refresh models button. $( '#wpaw-refresh-models' ).on( 'click', function() { const $button = $( this ); const $spinner = $( '#wpaw-models-spinner' ); $button.prop( 'disabled', true ); $spinner.show(); $.ajax( { url: wpawSettings.ajaxUrl, type: 'POST', data: { action: 'wpaw_refresh_models', nonce: wpawSettings.nonce, }, success: function( response ) { if ( response.success ) { // Update model dropdowns. updateModelDropdowns( response.data.models ); // Show success message. $( '#wpaw-models-message' ) .removeClass( 'notice-error' ) .addClass( 'notice-success' ) .text( response.data.message ) .show(); } else { showError( response.data.message || 'Unknown error' ); } }, error: function() { showError( 'Failed to refresh models. Please try again.' ); }, complete: function() { $button.prop( 'disabled', false ); $spinner.hide(); // Auto-hide message after 3 seconds. setTimeout( function() { $( '#wpaw-models-message' ).fadeOut(); }, 3000 ); } } ); } ); // Initialize model dropdowns on page load. if ( wpawSettings.models ) { updateModelDropdowns( wpawSettings.models ); } /** * Update model dropdowns with fetched models. * Shows all models in a flat list (no optgroups). */ function updateModelDropdowns( models ) { /** * Populate a select dropdown with models and store pricing. * @param {jQuery} $select - The select element * @param {Array} allModels - Array of all models * @param {string} currentValue - Currently saved value */ const populateSelect = ( $select, allModels, currentValue ) => { $select.empty(); // Add all models to dropdown and store pricing if ( allModels && allModels.length ) { allModels.forEach( function( model ) { // Clean model name - remove existing (free) suffix to avoid duplication let cleanName = model.name.replace( /\s*\(free\)\s*/gi, '' ).trim(); $select.append( $( '