294 lines
8.5 KiB
JavaScript
294 lines
8.5 KiB
JavaScript
/**
|
|
* 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 = $( '<span>' + model.text + '</span>' );
|
|
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( $( '<option>', {
|
|
value: model.id,
|
|
text: cleanName + ( model.is_free ? ' (Free)' : '' )
|
|
} ) );
|
|
|
|
// Store pricing data for cost calculation
|
|
if ( model.pricing ) {
|
|
modelPricing[ model.id ] = model.pricing;
|
|
}
|
|
} );
|
|
}
|
|
|
|
// Ensure saved value exists in dropdown (legacy/custom models)
|
|
if ( currentValue && $select.find( `option[value="${currentValue}"]` ).length === 0 ) {
|
|
$select.prepend( $( '<option>', {
|
|
value: currentValue,
|
|
text: currentValue + ' (Saved)'
|
|
} ) );
|
|
}
|
|
|
|
// Set the current value
|
|
if ( currentValue ) {
|
|
$select.val( currentValue );
|
|
}
|
|
};
|
|
|
|
// Update chat model dropdown
|
|
const $chatSelect = $( '#chat_model' );
|
|
const currentChat = wpawSettings.currentModels?.chat || $chatSelect.val();
|
|
populateSelect( $chatSelect, models.chat?.all || [], currentChat );
|
|
|
|
// Update clarity model dropdown
|
|
const $claritySelect = $( '#clarity_model' );
|
|
const currentClarity = wpawSettings.currentModels?.clarity || $claritySelect.val();
|
|
populateSelect( $claritySelect, models.planning?.all || [], currentClarity );
|
|
|
|
// Update planning model dropdown
|
|
const $planningSelect = $( '#planning_model' );
|
|
const currentPlanning = wpawSettings.currentModels?.planning || $planningSelect.val();
|
|
populateSelect( $planningSelect, models.planning?.all || [], currentPlanning );
|
|
|
|
// Update writing model dropdown
|
|
const $writingSelect = $( '#writing_model' );
|
|
const currentWriting = wpawSettings.currentModels?.writing || wpawSettings.currentModels?.execution || $writingSelect.val();
|
|
populateSelect( $writingSelect, models.execution?.all || [], currentWriting );
|
|
|
|
// Update refinement model dropdown
|
|
const $refinementSelect = $( '#refinement_model' );
|
|
const currentRefinement = wpawSettings.currentModels?.refinement || $refinementSelect.val();
|
|
populateSelect( $refinementSelect, models.execution?.all || [], currentRefinement );
|
|
|
|
// Update image model dropdown
|
|
const $imageSelect = $( '#image_model' );
|
|
const currentImage = wpawSettings.currentModels?.image || $imageSelect.val();
|
|
populateSelect( $imageSelect, models.image?.all || [], currentImage );
|
|
|
|
// Reinitialize Select2 after updating dropdowns
|
|
initSelect2();
|
|
|
|
// Update cost estimate
|
|
updateCostEstimate();
|
|
}
|
|
|
|
/**
|
|
* Show error message.
|
|
*/
|
|
function showError( message ) {
|
|
$( '#wpaw-models-message' )
|
|
.removeClass( 'notice-success' )
|
|
.addClass( 'notice-error' )
|
|
.text( message )
|
|
.show();
|
|
}
|
|
} );
|