Files
wp-agentic-writer/includes/class-settings.php
2026-01-28 00:26:00 +07:00

1552 lines
65 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Settings Page
*
* Handles the plugin settings page in WordPress admin.
*
* @package WP_Agentic_Writer
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class WP_Agentic_Writer_Settings
*
* @since 0.1.0
*/
class WP_Agentic_Writer_Settings {
/**
* Get singleton instance.
*
* @since 0.1.0
* @return WP_Agentic_Writer_Settings
*/
public static function get_instance() {
static $instance = null;
if ( null === $instance ) {
$instance = new self();
}
return $instance;
}
/**
* Constructor.
*
* @since 0.1.0
*/
private function __construct() {
add_action( 'admin_menu', array( $this, 'add_settings_page' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'wp_ajax_wpaw_refresh_models', array( $this, 'ajax_refresh_models' ) );
}
/**
* Enqueue scripts for settings page.
*
* @since 0.1.0
*/
public function enqueue_scripts( $hook ) {
if ( 'settings_page_wp-agentic-writer' !== $hook ) {
return;
}
// Enqueue Select2 for searchable dropdowns.
wp_enqueue_style( 'select2', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css', array(), '4.1.0' );
wp_enqueue_script( 'select2', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js', array( 'jquery' ), '4.1.0', true );
wp_enqueue_style( 'wp-agentic-writer-admin', WP_AGENTIC_WRITER_URL . 'assets/css/admin.css', array( 'select2' ), WP_AGENTIC_WRITER_VERSION );
wp_enqueue_script( 'wp-agentic-writer-settings', WP_AGENTIC_WRITER_URL . 'assets/js/settings.js', array( 'jquery', 'select2' ), WP_AGENTIC_WRITER_VERSION, true );
$settings = get_option( 'wp_agentic_writer_settings', array() );
wp_localize_script( 'wp-agentic-writer-settings', 'wpawSettings', array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'wpaw_settings' ),
'models' => $this->get_models_for_select(),
'currentModels' => array(
'planning' => $settings['planning_model'] ?? 'google/gemini-2.0-flash-exp:free',
'writing' => $settings['writing_model'] ?? ( $settings['execution_model'] ?? 'anthropic/claude-3.5-sonnet' ),
'execution' => $settings['writing_model'] ?? ( $settings['execution_model'] ?? 'anthropic/claude-3.5-sonnet' ),
'clarity' => $settings['clarity_model'] ?? 'google/gemini-2.0-flash-exp:free',
'refinement' => $settings['refinement_model'] ?? 'anthropic/claude-3.5-sonnet',
'chat' => $settings['chat_model'] ?? 'google/gemini-2.0-flash-exp:free',
'image' => $settings['image_model'] ?? 'openai/gpt-4o',
),
) );
}
/**
* Get models for select dropdowns.
*
* @since 0.1.0
* @return array Models grouped by category.
*/
private function get_models_for_select() {
$provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance();
$models = $provider->get_cached_models();
if ( is_wp_error( $models ) ) {
// Return fallback defaults if API fails.
return array(
'planning' => array(
'recommended' => array(
array( 'id' => 'google/gemini-2.0-flash-exp:free', 'name' => 'Google Gemini 2.0 Flash' ),
),
'all' => array(
array( 'id' => 'google/gemini-2.0-flash-exp:free', 'name' => 'Google Gemini 2.0 Flash' ),
),
),
'execution' => array(
'recommended' => array(
array( 'id' => 'anthropic/claude-3.5-sonnet', 'name' => 'Anthropic Claude 3.5 Sonnet' ),
),
'all' => array(
array( 'id' => 'anthropic/claude-3.5-sonnet', 'name' => 'Anthropic Claude 3.5 Sonnet' ),
),
),
'image' => array(
'recommended' => array(
array( 'id' => 'openai/gpt-4o', 'name' => 'OpenAI GPT-4o' ),
),
'all' => array(
array( 'id' => 'openai/gpt-4o', 'name' => 'OpenAI GPT-4o' ),
),
),
);
}
// Transform model structure to match what JS expects.
return $this->transform_models_for_js( $models );
}
/**
* Transform models structure for JavaScript consumption.
*
* @since 0.1.0
* @param array $models Models from provider.
* @return array Transformed models.
*/
private function transform_models_for_js( $models ) {
// Handle flat model list from OpenRouter.
if ( ! empty( $models ) && array_keys( $models ) === range( 0, count( $models ) - 1 ) ) {
$settings = get_option( 'wp_agentic_writer_settings', array() );
$planning_id = $settings['planning_model'] ?? 'google/gemini-2.0-flash-exp:free';
$execution_id = $settings['execution_model'] ?? 'anthropic/claude-3.5-sonnet';
$image_id = $settings['image_model'] ?? 'openai/gpt-4o';
$text_models = array();
$image_models = array();
// Known image model prefixes
$image_model_prefixes = array( 'black-forest-labs/', 'stability-ai/', 'dall-e', 'midjourney' );
foreach ( $models as $model ) {
if ( empty( $model['id'] ) ) {
continue;
}
$prompt_price = isset( $model['pricing']['prompt'] ) ? (float) $model['pricing']['prompt'] : 0;
$completion_price = isset( $model['pricing']['completion'] ) ? (float) $model['pricing']['completion'] : 0;
$image_price = isset( $model['pricing']['image'] ) ? (float) $model['pricing']['image'] : 0;
$model_data = array(
'id' => $model['id'],
'name' => $model['name'] ?? $model['id'],
'is_free' => $prompt_price <= 0.0 && $completion_price <= 0.0,
'pricing' => array(
'prompt' => $prompt_price,
'completion' => $completion_price,
'image' => $image_price,
),
);
// Check if this is an image model
$is_image_model = false;
foreach ( $image_model_prefixes as $prefix ) {
if ( stripos( $model['id'], $prefix ) === 0 ) {
$is_image_model = true;
break;
}
}
// Also check if it has image pricing but no text pricing
if ( $image_price > 0 && $prompt_price <= 0 && $completion_price <= 0 ) {
$is_image_model = true;
}
if ( $is_image_model ) {
$image_models[] = $model_data;
} else {
$text_models[] = $model_data;
}
}
$find_model = function( $model_id ) use ( $text_models, $image_models ) {
foreach ( array_merge( $text_models, $image_models ) as $model ) {
if ( $model['id'] === $model_id ) {
return $model;
}
}
return null;
};
$chat_id = $settings['chat_model'] ?? 'google/gemini-2.0-flash-exp:free';
return array(
'planning' => array(
'recommended' => array_filter( array( $find_model( $planning_id ) ) ),
'all' => $text_models,
),
'execution' => array(
'recommended' => array_filter( array( $find_model( $execution_id ) ) ),
'all' => $text_models,
),
'chat' => array(
'recommended' => array_filter( array( $find_model( $chat_id ) ) ),
'all' => $text_models,
),
'image' => array(
'recommended' => array_filter( array( $find_model( $image_id ) ) ),
'all' => $image_models,
),
);
}
$transformed = array();
foreach ( $models as $type => $categories ) {
if ( ! isset( $transformed[ $type ] ) ) {
$transformed[ $type ] = array(
'recommended' => array(),
'all' => array(),
);
}
// Combine free and paid into 'all' array.
$all_models = array_merge(
$categories['free'] ?? array(),
$categories['paid'] ?? array()
);
// Remove duplicates (in case a model is in both recommended and all).
$recommended_ids = array();
foreach ( $categories['recommended'] ?? array() as $model ) {
$transformed[ $type ]['recommended'][] = $model;
$recommended_ids[ $model['id'] ] = true;
}
// Add all models, avoiding duplicates with recommended.
foreach ( $all_models as $model ) {
if ( ! isset( $recommended_ids[ $model['id'] ] ) ) {
$transformed[ $type ]['all'][] = $model;
}
}
}
return $transformed;
}
/**
* AJAX handler for refreshing models.
*
* @since 0.1.0
*/
public function ajax_refresh_models() {
check_ajax_referer( 'wpaw_settings', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => 'Permission denied' ) );
}
$provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance();
$models = $provider->fetch_and_cache_models( true );
if ( is_wp_error( $models ) ) {
wp_send_json_error( array( 'message' => $models->get_error_message() ) );
}
// Transform models for JS consumption.
$transformed = $this->transform_models_for_js( $models );
wp_send_json_success( array(
'models' => $transformed,
'message' => __( 'Models refreshed successfully!', 'wp-agentic-writer' ),
) );
}
/**
* Add settings page to admin menu.
*
* @since 0.1.0
*/
public function add_settings_page() {
add_options_page(
__( 'WP Agentic Writer', 'wp-agentic-writer' ),
__( 'Agentic Writer', 'wp-agentic-writer' ),
'manage_options',
'wp-agentic-writer',
array( $this, 'render_settings_page' )
);
}
/**
* Register settings.
*
* @since 0.1.0
*/
public function register_settings() {
register_setting(
'wp_agentic_writer_settings',
'wp_agentic_writer_settings',
array(
'sanitize_callback' => array( $this, 'sanitize_settings' ),
)
);
}
/**
* Sanitize settings.
*
* @since 0.1.0
* @param array $input Settings input.
* @return array Sanitized settings.
*/
public function sanitize_settings( $input ) {
$sanitized = array();
// Sanitize API key (don't strip tags, but trim).
$sanitized['openrouter_api_key'] = trim( $input['openrouter_api_key'] ?? '' );
// Sanitize model names (6 models as per model-preset-brief.md).
$sanitized['chat_model'] = sanitize_text_field( $input['chat_model'] ?? 'google/gemini-2.5-flash' );
$sanitized['clarity_model'] = sanitize_text_field( $input['clarity_model'] ?? 'google/gemini-2.5-flash' );
$sanitized['planning_model'] = sanitize_text_field( $input['planning_model'] ?? 'google/gemini-2.5-flash' );
$sanitized['writing_model'] = sanitize_text_field( $input['writing_model'] ?? 'anthropic/claude-3.5-sonnet' );
$sanitized['refinement_model'] = sanitize_text_field( $input['refinement_model'] ?? 'anthropic/claude-3.5-sonnet' );
$sanitized['image_model'] = sanitize_text_field( $input['image_model'] ?? 'openai/gpt-4o' );
// Legacy support: map execution_model to writing_model
if ( isset( $input['execution_model'] ) && ! isset( $input['writing_model'] ) ) {
$sanitized['writing_model'] = sanitize_text_field( $input['execution_model'] );
}
// Sanitize boolean values.
$sanitized['web_search_enabled'] = isset( $input['web_search_enabled'] ) && '1' === $input['web_search_enabled'];
$sanitized['cost_tracking_enabled'] = isset( $input['cost_tracking_enabled'] ) && '1' === $input['cost_tracking_enabled'];
$sanitized['enable_clarification_quiz'] = isset( $input['enable_clarification_quiz'] ) && '1' === $input['enable_clarification_quiz'];
// Sanitize search options.
$sanitized['search_engine'] = in_array( $input['search_engine'], array( 'auto', 'native', 'exa' ), true )
? $input['search_engine']
: 'auto';
$sanitized['search_depth'] = isset( $input['search_depth'] ) && in_array( $input['search_depth'], array( 'low', 'medium', 'high' ), true )
? $input['search_depth']
: 'medium';
// Sanitize budget.
$sanitized['monthly_budget'] = floatval( $input['monthly_budget'] ?? 600 );
// Sanitize chat history limit.
$chat_history_limit = isset( $input['chat_history_limit'] ) ? absint( $input['chat_history_limit'] ) : 20;
$sanitized['chat_history_limit'] = min( $chat_history_limit, 200 );
// Sanitize clarification quiz settings.
$sanitized['clarity_confidence_threshold'] = in_array( $input['clarity_confidence_threshold'], array( '0.5', '0.6', '0.7', '0.8', '0.9' ), true )
? $input['clarity_confidence_threshold']
: '0.6';
if ( isset( $input['required_context_categories'] ) && is_array( $input['required_context_categories'] ) ) {
$valid_categories = array( 'target_outcome', 'target_audience', 'tone', 'content_depth', 'expertise_level', 'content_type', 'pov' );
$sanitized['required_context_categories'] = array_intersect( $input['required_context_categories'], $valid_categories );
} else {
$sanitized['required_context_categories'] = array( 'target_outcome', 'target_audience', 'tone', 'content_depth', 'expertise_level', 'content_type', 'pov' );
}
// Sanitize preferred languages
if ( isset( $input['preferred_languages'] ) && is_array( $input['preferred_languages'] ) ) {
$sanitized['preferred_languages'] = array_map( 'sanitize_text_field', $input['preferred_languages'] );
} else {
$sanitized['preferred_languages'] = array( 'auto', 'English', 'Indonesian' );
}
// Sanitize custom languages
if ( isset( $input['custom_languages'] ) && is_array( $input['custom_languages'] ) ) {
$sanitized['custom_languages'] = array_filter( array_map( 'sanitize_text_field', $input['custom_languages'] ) );
} else {
$sanitized['custom_languages'] = array();
}
return $sanitized;
}
/**
* Render settings page.
*
* @since 0.1.0
*/
public function render_settings_page() {
$settings = get_option( 'wp_agentic_writer_settings', array() );
// Extract settings (6 models as per model-preset-brief.md).
$api_key = $settings['openrouter_api_key'] ?? '';
$chat_model = $settings['chat_model'] ?? 'google/gemini-2.5-flash';
$clarity_model = $settings['clarity_model'] ?? 'google/gemini-2.5-flash';
$planning_model = $settings['planning_model'] ?? 'google/gemini-2.5-flash';
$writing_model = $settings['writing_model'] ?? ( $settings['execution_model'] ?? 'anthropic/claude-3.5-sonnet' );
$refinement_model = $settings['refinement_model'] ?? 'anthropic/claude-3.5-sonnet';
$image_model = $settings['image_model'] ?? 'openai/gpt-4o';
$web_search_enabled = $settings['web_search_enabled'] ?? false;
$search_engine = $settings['search_engine'] ?? 'auto';
$search_depth = $settings['search_depth'] ?? 'medium';
$cost_tracking_enabled = $settings['cost_tracking_enabled'] ?? true;
$monthly_budget = $settings['monthly_budget'] ?? 600;
$chat_history_limit = $settings['chat_history_limit'] ?? 20;
// Clarification quiz settings.
$enable_clarification_quiz = $settings['enable_clarification_quiz'] ?? true;
$clarity_confidence_threshold = $settings['clarity_confidence_threshold'] ?? '0.6';
$required_context_categories = $settings['required_context_categories'] ?? array(
'target_outcome',
'target_audience',
'tone',
'content_depth',
'expertise_level',
'content_type',
'pov',
);
// Get cost tracking data.
$cost_tracker = WP_Agentic_Writer_Cost_Tracker::get_instance();
$monthly_used = $cost_tracker->get_monthly_total();
$budget_percent = $monthly_budget > 0 ? ( $monthly_used / $monthly_budget ) * 100 : 0;
$budget_status = $budget_percent > 90 ? 'danger' : ( $budget_percent > 70 ? 'warning' : '' );
?>
<div class="wpaw-settings-wrap">
<!-- Header -->
<div class="wpaw-settings-header">
<div class="wpaw-settings-logo">✨</div>
<div>
<h1 class="wpaw-settings-title"><?php esc_html_e( 'WP Agentic Writer', 'wp-agentic-writer' ); ?></h1>
<p class="wpaw-settings-subtitle"><?php esc_html_e( 'AI-powered writing assistant for WordPress', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<!-- Tab Navigation -->
<div class="wpaw-settings-nav">
<button type="button" class="wpaw-settings-nav-btn active" data-tab="general">
<span class="dashicons dashicons-admin-settings"></span>
<?php esc_html_e( 'General', 'wp-agentic-writer' ); ?>
</button>
<button type="button" class="wpaw-settings-nav-btn" data-tab="models">
<span class="dashicons dashicons-superhero"></span>
<?php esc_html_e( 'AI Models', 'wp-agentic-writer' ); ?>
</button>
<button type="button" class="wpaw-settings-nav-btn" data-tab="cost-log">
<span class="dashicons dashicons-chart-line"></span>
<?php esc_html_e( 'Cost Log', 'wp-agentic-writer' ); ?>
</button>
<button type="button" class="wpaw-settings-nav-btn" data-tab="guide">
<span class="dashicons dashicons-book"></span>
<?php esc_html_e( 'Model Guide', 'wp-agentic-writer' ); ?>
</button>
</div>
<form method="post" action="options.php">
<?php settings_fields( 'wp_agentic_writer_settings' ); ?>
<!-- ==================== GENERAL TAB ==================== -->
<div class="wpaw-tab-content active" data-tab="general">
<!-- API Configuration Section -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">🔑</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'API Configuration', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( 'Connect to OpenRouter to access AI models', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-section-body">
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'OpenRouter API Key', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'Required for all AI features', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<input type="password" id="openrouter_api_key" name="wp_agentic_writer_settings[openrouter_api_key]" value="<?php echo esc_attr( $api_key ); ?>" placeholder="sk-or-v1-..." />
<p class="description">
<?php printf( wp_kses_post( __( 'Get your API key from <a href="%s" target="_blank">OpenRouter</a>.', 'wp-agentic-writer' ) ), 'https://openrouter.ai/keys' ); ?>
</p>
</div>
</div>
</div>
</div>
<!-- Budget & Cost Section -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">💰</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'Budget & Cost Tracking', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( 'Monitor and control your API spending', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-section-body">
<div class="wpaw-budget-display">
<div class="wpaw-budget-stat">
<div class="wpaw-budget-stat-value">$<?php echo number_format( $monthly_used, 2 ); ?></div>
<div class="wpaw-budget-stat-label"><?php esc_html_e( 'Used This Month', 'wp-agentic-writer' ); ?></div>
</div>
<div class="wpaw-budget-bar-wrapper">
<div class="wpaw-budget-bar-label">
<span><?php esc_html_e( 'Monthly Budget', 'wp-agentic-writer' ); ?></span>
<span><?php echo number_format( $budget_percent, 1 ); ?>%</span>
</div>
<div class="wpaw-budget-bar-track">
<div class="wpaw-budget-bar-fill <?php echo esc_attr( $budget_status ); ?>" style="width: <?php echo min( $budget_percent, 100 ); ?>%"></div>
</div>
</div>
<div class="wpaw-budget-stat">
<div class="wpaw-budget-stat-value">$<?php echo number_format( max( 0, $monthly_budget - $monthly_used ), 2 ); ?></div>
<div class="wpaw-budget-stat-label"><?php esc_html_e( 'Remaining', 'wp-agentic-writer' ); ?></div>
</div>
</div>
<div class="wpaw-cost-shortcuts">
<a href="<?php echo esc_url( admin_url( 'options-general.php?page=wp-agentic-writer-settings&tab=cost-log' ) ); ?>" class="wpaw-cost-shortcut-link">
<span class="dashicons dashicons-chart-line"></span>
<?php esc_html_e( 'View Full Cost Log', 'wp-agentic-writer' ); ?> →
</a>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Monthly Budget', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'Maximum spend per month', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<input type="number" id="monthly_budget" name="wp_agentic_writer_settings[monthly_budget]" value="<?php echo esc_attr( $monthly_budget ); ?>" min="0" step="0.01" style="max-width: 120px;" /> USD
</div>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label"><?php esc_html_e( 'Cost Tracking', 'wp-agentic-writer' ); ?></div>
<div class="wpaw-field-input">
<label class="wpaw-toggle">
<input type="checkbox" name="wp_agentic_writer_settings[cost_tracking_enabled]" value="1" <?php checked( $cost_tracking_enabled ); ?> />
<span class="wpaw-toggle-switch"></span>
<span class="wpaw-toggle-label"><?php esc_html_e( 'Enable cost tracking in editor sidebar', 'wp-agentic-writer' ); ?></span>
</label>
</div>
</div>
</div>
</div>
<!-- Research & Web Search Section -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">🔍</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'Research & Web Search', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( 'Configure web search for up-to-date content', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-section-body">
<div class="wpaw-field-row">
<div class="wpaw-field-label"><?php esc_html_e( 'Web Search', 'wp-agentic-writer' ); ?></div>
<div class="wpaw-field-input">
<label class="wpaw-toggle">
<input type="checkbox" name="wp_agentic_writer_settings[web_search_enabled]" value="1" <?php checked( $web_search_enabled ); ?> />
<span class="wpaw-toggle-switch"></span>
<span class="wpaw-toggle-label"><?php esc_html_e( 'Enable Web Search (~$0.02 per search)', 'wp-agentic-writer' ); ?></span>
</label>
<p class="description"><?php esc_html_e( 'Search the web for current information. Can be toggled per-request in sidebar.', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label"><?php esc_html_e( 'Search Engine', 'wp-agentic-writer' ); ?></div>
<div class="wpaw-field-input">
<select id="search_engine" name="wp_agentic_writer_settings[search_engine]">
<option value="auto" <?php selected( $search_engine, 'auto' ); ?>><?php esc_html_e( 'Auto (Native if available, Exa fallback)', 'wp-agentic-writer' ); ?></option>
<option value="native" <?php selected( $search_engine, 'native' ); ?>><?php esc_html_e( 'Native (Provider\'s built-in search)', 'wp-agentic-writer' ); ?></option>
<option value="exa" <?php selected( $search_engine, 'exa' ); ?>><?php esc_html_e( 'Exa (Always use Exa search)', 'wp-agentic-writer' ); ?></option>
</select>
</div>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label"><?php esc_html_e( 'Search Depth', 'wp-agentic-writer' ); ?></div>
<div class="wpaw-field-input">
<select id="search_depth" name="wp_agentic_writer_settings[search_depth]">
<option value="low" <?php selected( $search_depth, 'low' ); ?>><?php esc_html_e( 'Low (Basic queries)', 'wp-agentic-writer' ); ?></option>
<option value="medium" <?php selected( $search_depth, 'medium' ); ?>><?php esc_html_e( 'Medium (General queries)', 'wp-agentic-writer' ); ?></option>
<option value="high" <?php selected( $search_depth, 'high' ); ?>><?php esc_html_e( 'High (Detailed research)', 'wp-agentic-writer' ); ?></option>
</select>
</div>
</div>
</div>
</div>
<!-- Clarification Quiz Section -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">❓</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'Clarification Quiz', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( 'Gather context before writing for better results', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-section-body">
<div class="wpaw-field-row">
<div class="wpaw-field-label"><?php esc_html_e( 'Enable Quiz', 'wp-agentic-writer' ); ?></div>
<div class="wpaw-field-input">
<label class="wpaw-toggle">
<input type="checkbox" name="wp_agentic_writer_settings[enable_clarification_quiz]" value="1" <?php checked( $enable_clarification_quiz ); ?> />
<span class="wpaw-toggle-switch"></span>
<span class="wpaw-toggle-label"><?php esc_html_e( 'Ask clarifying questions when context is missing', 'wp-agentic-writer' ); ?></span>
</label>
</div>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Confidence Threshold', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'When to trigger the quiz', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<select id="clarity_confidence_threshold" name="wp_agentic_writer_settings[clarity_confidence_threshold]" <?php disabled( ! $enable_clarification_quiz ); ?>>
<option value="0.5" <?php selected( $clarity_confidence_threshold, '0.5' ); ?>><?php esc_html_e( 'Very Sensitive (50%)', 'wp-agentic-writer' ); ?></option>
<option value="0.6" <?php selected( $clarity_confidence_threshold, '0.6' ); ?>><?php esc_html_e( 'Sensitive (60%) - Recommended', 'wp-agentic-writer' ); ?></option>
<option value="0.7" <?php selected( $clarity_confidence_threshold, '0.7' ); ?>><?php esc_html_e( 'Balanced (70%)', 'wp-agentic-writer' ); ?></option>
<option value="0.8" <?php selected( $clarity_confidence_threshold, '0.8' ); ?>><?php esc_html_e( 'Strict (80%)', 'wp-agentic-writer' ); ?></option>
<option value="0.9" <?php selected( $clarity_confidence_threshold, '0.9' ); ?>><?php esc_html_e( 'Very Strict (90%)', 'wp-agentic-writer' ); ?></option>
</select>
</div>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Context Categories', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'What to clarify', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<select id="required_context_categories" name="wp_agentic_writer_settings[required_context_categories][]" multiple="multiple" size="7" <?php disabled( ! $enable_clarification_quiz ); ?>>
<option value="target_outcome" <?php echo in_array( 'target_outcome', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Target Outcome', 'wp-agentic-writer' ); ?></option>
<option value="target_audience" <?php echo in_array( 'target_audience', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Target Audience', 'wp-agentic-writer' ); ?></option>
<option value="tone" <?php echo in_array( 'tone', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Tone of Voice', 'wp-agentic-writer' ); ?></option>
<option value="content_depth" <?php echo in_array( 'content_depth', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Content Depth', 'wp-agentic-writer' ); ?></option>
<option value="expertise_level" <?php echo in_array( 'expertise_level', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Expertise Level', 'wp-agentic-writer' ); ?></option>
<option value="content_type" <?php echo in_array( 'content_type', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Content Type', 'wp-agentic-writer' ); ?></option>
<option value="pov" <?php echo in_array( 'pov', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Point of View', 'wp-agentic-writer' ); ?></option>
</select>
<p class="description"><?php esc_html_e( 'Select which aspects the AI should clarify before writing.', 'wp-agentic-writer' ); ?></p>
</div>
</div>
</div>
</div>
<!-- Content Settings Section -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">🌍</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'Content Settings', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( 'Configure language preferences for your content', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-section-body">
<?php $this->render_language_preferences( $settings ); ?>
</div>
</div>
<!-- Advanced Section -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">⚙️</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'Advanced Settings', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( 'Additional configuration options', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-section-body">
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Chat History Limit', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'Messages stored per post', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<input type="number" id="chat_history_limit" name="wp_agentic_writer_settings[chat_history_limit]" value="<?php echo esc_attr( $chat_history_limit ); ?>" min="0" max="200" style="max-width: 80px;" />
<p class="description"><?php esc_html_e( 'Set to 0 to disable chat history.', 'wp-agentic-writer' ); ?></p>
</div>
</div>
</div>
</div>
</div><!-- END GENERAL TAB -->
<!-- ==================== MODELS TAB ==================== -->
<div class="wpaw-tab-content" data-tab="models">
<!-- Quick Presets Section (per model-preset-brief.md) -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">⚡</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'Quick Presets', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( 'One-click configurations for different budgets', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-section-body">
<div class="wpaw-preset-grid">
<div class="wpaw-preset-card" data-preset="budget" onclick="wpawApplyPreset('budget')">
<div class="wpaw-preset-name"><?php esc_html_e( 'Budget', 'wp-agentic-writer' ); ?> <span class="wpaw-preset-badge">💰 ~$0.06/article</span></div>
<div class="wpaw-preset-models">
Chat/Clarity/Planning/Refinement: Gemini 2.5 Flash<br>
Writing: Mistral Small Creative<br>
Image: GPT-4o
</div>
</div>
<div class="wpaw-preset-card" data-preset="balanced" onclick="wpawApplyPreset('balanced')">
<div class="wpaw-preset-name"><?php esc_html_e( 'Balanced', 'wp-agentic-writer' ); ?> <span class="wpaw-preset-badge recommended">⭐ ~$0.14/article</span></div>
<div class="wpaw-preset-models">
Chat/Clarity/Planning: Gemini 2.5 Flash<br>
Writing/Refinement: Claude 3.5 Sonnet<br>
Image: GPT-4o
</div>
</div>
<div class="wpaw-preset-card" data-preset="premium" onclick="wpawApplyPreset('premium')">
<div class="wpaw-preset-name"><?php esc_html_e( 'Premium', 'wp-agentic-writer' ); ?> <span class="wpaw-preset-badge">✨ ~$0.31/article</span></div>
<div class="wpaw-preset-models">
Chat/Planning: Gemini 3 Flash Preview<br>
Clarity: Claude Sonnet 4<br>
Writing/Refinement: GPT-4.1<br>
Image: GPT-4o
</div>
</div>
</div>
</div>
</div>
<!-- Individual Model Selectors (6 models per model-preset-brief.md) -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">🤖</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'AI Model Configuration', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( '6 specialized models for optimal results. Use search to find models.', 'wp-agentic-writer' ); ?></p>
</div>
<button type="button" id="wpaw-refresh-models" class="button" style="margin-left: auto;">
<?php esc_html_e( 'Refresh Models', 'wp-agentic-writer' ); ?>
</button>
<span id="wpaw-models-spinner" class="spinner" style="display: none;"></span>
</div>
<div class="wpaw-section-body">
<div id="wpaw-models-message" style="display: none; margin-bottom: 16px;"></div>
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Chat Model', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'Discussion, research, recommendations', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<select id="chat_model" name="wp_agentic_writer_settings[chat_model]" class="wpaw-select2-model" style="width: 100%;">
<option value="<?php echo esc_attr( $chat_model ); ?>"><?php echo esc_html( $chat_model ); ?></option>
</select>
</div>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Clarity Model', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'Prompt analysis, quiz generation', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<select id="clarity_model" name="wp_agentic_writer_settings[clarity_model]" class="wpaw-select2-model" style="width: 100%;">
<option value="<?php echo esc_attr( $clarity_model ); ?>"><?php echo esc_html( $clarity_model ); ?></option>
</select>
</div>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Planning Model', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'Article outline generation', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<select id="planning_model" name="wp_agentic_writer_settings[planning_model]" class="wpaw-select2-model" style="width: 100%;">
<option value="<?php echo esc_attr( $planning_model ); ?>"><?php echo esc_html( $planning_model ); ?></option>
</select>
</div>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Writing Model', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'Article draft generation (2-5k words)', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<select id="writing_model" name="wp_agentic_writer_settings[writing_model]" class="wpaw-select2-model" style="width: 100%;">
<option value="<?php echo esc_attr( $writing_model ); ?>"><?php echo esc_html( $writing_model ); ?></option>
</select>
</div>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Refinement Model', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'Paragraph edits, rewrites, polish', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<select id="refinement_model" name="wp_agentic_writer_settings[refinement_model]" class="wpaw-select2-model" style="width: 100%;">
<option value="<?php echo esc_attr( $refinement_model ); ?>"><?php echo esc_html( $refinement_model ); ?></option>
</select>
</div>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Image Model', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'Image generation', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<select id="image_model" name="wp_agentic_writer_settings[image_model]" class="wpaw-select2-model" style="width: 100%;">
<option value="<?php echo esc_attr( $image_model ); ?>"><?php echo esc_html( $image_model ); ?></option>
</select>
</div>
</div>
<!-- Cost Estimate Display -->
<div class="wpaw-cost-estimate-box">
<div class="wpaw-cost-estimate-label"><?php esc_html_e( 'Estimated Cost Per Article:', 'wp-agentic-writer' ); ?></div>
<div id="wpaw-cost-estimate" class="wpaw-cost-estimate-value">~$0.00 per article</div>
<p class="wpaw-cost-estimate-note"><?php esc_html_e( 'Based on ~2K planning tokens, ~4K execution tokens, and 1 image.', 'wp-agentic-writer' ); ?></p>
</div>
</div>
</div>
</div><!-- END MODELS TAB -->
<!-- ==================== COST LOG TAB ==================== -->
<div class="wpaw-tab-content" data-tab="cost-log">
<?php $this->render_cost_log_tab(); ?>
</div><!-- END COST LOG TAB -->
<!-- ==================== GUIDE TAB ==================== -->
<div class="wpaw-tab-content" data-tab="guide">
<!-- How Models Are Used -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">📖</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'How AI Models Are Used', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( 'Understanding which model handles each task', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-section-body">
<table class="wpaw-guide-table">
<thead>
<tr>
<th><?php esc_html_e( 'Task', 'wp-agentic-writer' ); ?></th>
<th><?php esc_html_e( 'Model Used', 'wp-agentic-writer' ); ?></th>
<th><?php esc_html_e( 'Est. Cost', 'wp-agentic-writer' ); ?></th>
<th><?php esc_html_e( 'Description', 'wp-agentic-writer' ); ?></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><?php esc_html_e( 'Planning', 'wp-agentic-writer' ); ?></strong></td>
<td><span class="wpaw-model-badge planning"><?php esc_html_e( 'Planning Model', 'wp-agentic-writer' ); ?></span></td>
<td>~$0.001</td>
<td><?php esc_html_e( 'Creates article outline with sections and structure', 'wp-agentic-writer' ); ?></td>
</tr>
<tr>
<td><strong><?php esc_html_e( 'Clarity Check', 'wp-agentic-writer' ); ?></strong></td>
<td><span class="wpaw-model-badge clarity"><?php esc_html_e( 'Clarity Model', 'wp-agentic-writer' ); ?></span></td>
<td>~$0.0005</td>
<td><?php esc_html_e( 'Analyzes your prompt and asks clarifying questions', 'wp-agentic-writer' ); ?></td>
</tr>
<tr>
<td><strong><?php esc_html_e( 'Writing', 'wp-agentic-writer' ); ?></strong></td>
<td><span class="wpaw-model-badge execution"><?php esc_html_e( 'Writing Model', 'wp-agentic-writer' ); ?></span></td>
<td>~$0.05-0.15</td>
<td><?php esc_html_e( 'Generates full article content section by section', 'wp-agentic-writer' ); ?></td>
</tr>
<tr>
<td><strong><?php esc_html_e( 'Chat', 'wp-agentic-writer' ); ?></strong></td>
<td><span class="wpaw-model-badge chat"><?php esc_html_e( 'Chat Model', 'wp-agentic-writer' ); ?></span></td>
<td>~$0.001</td>
<td><?php esc_html_e( 'Answers questions and provides quick assistance', 'wp-agentic-writer' ); ?></td>
</tr>
<tr>
<td><strong><?php esc_html_e( 'Block Refinement', 'wp-agentic-writer' ); ?></strong></td>
<td><span class="wpaw-model-badge refinement"><?php esc_html_e( 'Refinement Model', 'wp-agentic-writer' ); ?></span></td>
<td>~$0.01-0.03</td>
<td><?php esc_html_e( 'Improves or rewrites selected content blocks', 'wp-agentic-writer' ); ?></td>
</tr>
<tr>
<td><strong><?php esc_html_e( 'Web Search', 'wp-agentic-writer' ); ?></strong></td>
<td><span class="wpaw-model-badge search"><?php esc_html_e( 'OpenRouter Plugin', 'wp-agentic-writer' ); ?></span></td>
<td>~$0.02</td>
<td><?php esc_html_e( 'Searches web for current information (toggle in sidebar)', 'wp-agentic-writer' ); ?></td>
</tr>
<tr>
<td><strong><?php esc_html_e( 'Image Generation', 'wp-agentic-writer' ); ?></strong></td>
<td><span class="wpaw-model-badge image"><?php esc_html_e( 'Image Model', 'wp-agentic-writer' ); ?></span></td>
<td>$0.003-0.04</td>
<td><?php esc_html_e( 'Creates images for articles', 'wp-agentic-writer' ); ?></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Recommended Models -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">⭐</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'Recommended Models', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( 'Best models for each purpose based on quality and cost', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-section-body">
<table class="wpaw-guide-table">
<thead>
<tr>
<th><?php esc_html_e( 'Model', 'wp-agentic-writer' ); ?></th>
<th><?php esc_html_e( 'Best For', 'wp-agentic-writer' ); ?></th>
<th><?php esc_html_e( 'Cost (per 1M tokens)', 'wp-agentic-writer' ); ?></th>
<th><?php esc_html_e( 'Notes', 'wp-agentic-writer' ); ?></th>
</tr>
</thead>
<tbody>
<tr class="wpaw-guide-row-highlight">
<td><strong>Gemini 2.5 Flash</strong></td>
<td><?php esc_html_e( 'Chat, Clarity, Planning', 'wp-agentic-writer' ); ?></td>
<td>$0.15 / $0.60</td>
<td><?php esc_html_e( '⭐ Best balanced option. Fast reasoning.', 'wp-agentic-writer' ); ?></td>
</tr>
<tr>
<td><strong>Gemini 3 Flash Preview</strong></td>
<td><?php esc_html_e( 'Chat, Clarity, Planning (Premium)', 'wp-agentic-writer' ); ?></td>
<td>$0.50 / $3.00</td>
<td><?php esc_html_e( '🏆 Latest Gemini. Near Pro performance.', 'wp-agentic-writer' ); ?></td>
</tr>
<tr class="wpaw-guide-row-highlight">
<td><strong>Claude 3.5 Sonnet</strong></td>
<td><?php esc_html_e( 'Writing, Refinement', 'wp-agentic-writer' ); ?></td>
<td>$3.00 / $15.00</td>
<td><?php esc_html_e( '⭐ Best quality writing. Recommended.', 'wp-agentic-writer' ); ?></td>
</tr>
<tr>
<td><strong>Mistral Small Creative</strong></td>
<td><?php esc_html_e( 'Writing (Budget)', 'wp-agentic-writer' ); ?></td>
<td>$0.10 / $0.30</td>
<td><?php esc_html_e( '💰 Best budget writing. Creative style.', 'wp-agentic-writer' ); ?></td>
</tr>
<tr>
<td><strong>GPT-4.1</strong></td>
<td><?php esc_html_e( 'Writing, Refinement (Premium)', 'wp-agentic-writer' ); ?></td>
<td>$2.00 / $8.00</td>
<td><?php esc_html_e( '🏆 Premium option. Excellent instruction following.', 'wp-agentic-writer' ); ?></td>
</tr>
<tr>
<td><strong>Claude Sonnet 4</strong></td>
<td><?php esc_html_e( 'Clarity (Premium)', 'wp-agentic-writer' ); ?></td>
<td>$3.00 / $15.00</td>
<td><?php esc_html_e( '🏆 Latest Claude. Deep analysis.', 'wp-agentic-writer' ); ?></td>
</tr>
<tr class="wpaw-guide-row-highlight">
<td><strong>GPT-4o</strong></td>
<td><?php esc_html_e( 'Image Prompts', 'wp-agentic-writer' ); ?></td>
<td>$2.50 / $10.00</td>
<td><?php esc_html_e( '⭐ Generates image descriptions for prompts.', 'wp-agentic-writer' ); ?></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Cost Examples -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">💡</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'Cost Examples', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( 'Real-world cost estimates for common tasks', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-section-body">
<div class="wpaw-cost-examples">
<div class="wpaw-cost-example">
<div class="wpaw-cost-example-title"><?php esc_html_e( 'Single Article (Balanced)', 'wp-agentic-writer' ); ?></div>
<div class="wpaw-cost-example-breakdown">
<span><?php esc_html_e( 'Clarity Check', 'wp-agentic-writer' ); ?>: ~$0.001</span>
<span><?php esc_html_e( 'Planning', 'wp-agentic-writer' ); ?>: ~$0.001</span>
<span><?php esc_html_e( 'Writing (5 sections)', 'wp-agentic-writer' ); ?>: ~$0.10</span>
<span><?php esc_html_e( '1 Image', 'wp-agentic-writer' ); ?>: ~$0.003</span>
</div>
<div class="wpaw-cost-example-total"><?php esc_html_e( 'Total', 'wp-agentic-writer' ); ?>: <strong>~$0.10-0.15</strong></div>
</div>
<div class="wpaw-cost-example">
<div class="wpaw-cost-example-title"><?php esc_html_e( '10 Articles/Month (Balanced)', 'wp-agentic-writer' ); ?></div>
<div class="wpaw-cost-example-breakdown">
<span><?php esc_html_e( '10 articles × $0.15', 'wp-agentic-writer' ); ?></span>
<span><?php esc_html_e( 'Some chat interactions', 'wp-agentic-writer' ); ?>: ~$0.05</span>
<span><?php esc_html_e( 'Refinements', 'wp-agentic-writer' ); ?>: ~$0.20</span>
</div>
<div class="wpaw-cost-example-total"><?php esc_html_e( 'Total', 'wp-agentic-writer' ); ?>: <strong>~$1.50-2.00</strong></div>
</div>
<div class="wpaw-cost-example">
<div class="wpaw-cost-example-title"><?php esc_html_e( 'With Web Search', 'wp-agentic-writer' ); ?></div>
<div class="wpaw-cost-example-breakdown">
<span><?php esc_html_e( 'Add ~$0.02 per search request', 'wp-agentic-writer' ); ?></span>
<span><?php esc_html_e( 'Typical: 1-2 searches per article', 'wp-agentic-writer' ); ?></span>
</div>
<div class="wpaw-cost-example-total"><?php esc_html_e( 'Extra', 'wp-agentic-writer' ); ?>: <strong>~$0.02-0.04</strong></div>
</div>
</div>
</div>
</div>
</div><!-- END GUIDE TAB -->
<!-- Submit Section -->
<div class="wpaw-submit-section">
<div class="wpaw-submit-info">
<?php esc_html_e( 'Changes are saved immediately after clicking Save.', 'wp-agentic-writer' ); ?>
</div>
<?php submit_button( __( 'Save Settings', 'wp-agentic-writer' ), 'primary', 'submit', false ); ?>
</div>
</form>
</div>
<script>
function wpawApplyPreset(preset) {
// Preset configurations with valid OpenRouter model IDs
const presets = {
budget: {
chat: 'google/gemini-2.5-flash',
clarity: 'google/gemini-2.5-flash',
planning: 'google/gemini-2.5-flash',
writing: 'mistralai/mistral-small-creative',
refinement: 'google/gemini-2.5-flash',
image: 'openai/gpt-4o'
},
balanced: {
chat: 'google/gemini-2.5-flash',
clarity: 'google/gemini-2.5-flash',
planning: 'google/gemini-2.5-flash',
writing: 'anthropic/claude-3.5-sonnet',
refinement: 'anthropic/claude-3.5-sonnet',
image: 'openai/gpt-4o'
},
premium: {
chat: 'google/gemini-3-flash-preview',
clarity: 'anthropic/claude-sonnet-4',
planning: 'google/gemini-3-flash-preview',
writing: 'openai/gpt-4.1',
refinement: 'openai/gpt-4.1',
image: 'openai/gpt-4o'
}
};
const config = presets[preset];
if (!config) return;
const $ = jQuery;
const selectors = {
chat: $('#chat_model'),
clarity: $('#clarity_model'),
planning: $('#planning_model'),
writing: $('#writing_model'),
refinement: $('#refinement_model'),
image: $('#image_model')
};
// Helper to set model value, adding option if needed
const setModelValue = ($select, targetId) => {
if (!$select.length) return;
// Check if option exists
if (!$select.find(`option[value="${targetId}"]`).length) {
// Add the option if it doesn't exist
const displayName = targetId.split('/').pop().replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
$select.append($('<option>', { value: targetId, text: displayName }));
}
// Set value and trigger change for Select2
$select.val(targetId).trigger('change');
};
// Apply each model
Object.keys(config).forEach(type => {
const $select = selectors[type];
if (!$select || !$select.length) return;
setModelValue($select, config[type]);
});
// Update selected state
document.querySelectorAll('.wpaw-preset-card').forEach(card => {
card.classList.remove('selected');
});
document.querySelector(`[data-preset="${preset}"]`)?.classList.add('selected');
}
</script>
<?php
}
/**
* Render language preferences section.
*
* @since 0.1.0
* @param array $settings Plugin settings.
*/
private function render_language_preferences( $settings ) {
$preferred_languages = $settings['preferred_languages'] ?? array( 'auto', 'English', 'Indonesian' );
$custom_languages = $settings['custom_languages'] ?? array();
// Predefined language list
$available_languages = array(
'auto' => 'Auto-detect',
'English' => 'English',
'Indonesian' => 'Indonesian (Bahasa Indonesia)',
'Javanese' => 'Javanese (Basa Jawa)',
'Sundanese' => 'Sundanese (Basa Sunda)',
'Spanish' => 'Spanish (Español)',
'French' => 'French (Français)',
'Arabic' => 'Arabic (العربية)',
'Chinese' => 'Chinese (中文)',
'Japanese' => 'Japanese (日本語)',
'Portuguese' => 'Portuguese (Português)',
'German' => 'German (Deutsch)',
'Hindi' => 'Hindi (हिंदी)',
'Korean' => 'Korean (한국어)',
'Vietnamese' => 'Vietnamese (Tiếng Việt)',
'Thai' => 'Thai (ไทย)',
'Tagalog' => 'Tagalog',
'Malay' => 'Malay (Bahasa Melayu)',
'Russian' => 'Russian (Русский)',
'Italian' => 'Italian (Italiano)',
'Dutch' => 'Dutch (Nederlands)',
'Polish' => 'Polish (Polski)',
'Turkish' => 'Turkish (Türkçe)',
'Swedish' => 'Swedish (Svenska)',
);
?>
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Preferred Languages', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'Languages available when creating articles', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<div class="wpaw-language-grid">
<?php foreach ( $available_languages as $code => $label ) : ?>
<label class="wpaw-language-checkbox">
<input
type="checkbox"
name="wp_agentic_writer_settings[preferred_languages][]"
value="<?php echo esc_attr( $code ); ?>"
<?php checked( in_array( $code, $preferred_languages, true ) ); ?>
/>
<span><?php echo esc_html( $label ); ?></span>
</label>
<?php endforeach; ?>
</div>
<p class="description">
<?php esc_html_e( 'Select which languages your site will use. These will appear in the language selector when creating articles.', 'wp-agentic-writer' ); ?>
</p>
</div>
</div>
<div class="wpaw-field-row">
<div class="wpaw-field-label">
<?php esc_html_e( 'Custom Languages', 'wp-agentic-writer' ); ?>
<small><?php esc_html_e( 'Add languages not in the list above', 'wp-agentic-writer' ); ?></small>
</div>
<div class="wpaw-field-input">
<div id="wpaw-custom-languages-list">
<?php if ( ! empty( $custom_languages ) ) : ?>
<?php foreach ( $custom_languages as $index => $lang ) : ?>
<div class="wpaw-custom-language-item">
<input
type="text"
name="wp_agentic_writer_settings[custom_languages][]"
value="<?php echo esc_attr( $lang ); ?>"
placeholder="<?php esc_attr_e( 'e.g., Betawi, Minangkabau', 'wp-agentic-writer' ); ?>"
/>
<button type="button" class="button wpaw-remove-language">✗</button>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<button type="button" id="wpaw-add-custom-language" class="button">
<?php esc_html_e( '+ Add Custom Language', 'wp-agentic-writer' ); ?>
</button>
<p class="description">
<?php esc_html_e( 'Add any language not listed above (e.g., regional dialects, minority languages).', 'wp-agentic-writer' ); ?>
</p>
</div>
</div>
<script>
jQuery(document).ready(function($) {
// Add custom language
$('#wpaw-add-custom-language').on('click', function() {
const html = `
<div class="wpaw-custom-language-item">
<input
type="text"
name="wp_agentic_writer_settings[custom_languages][]"
value=""
placeholder="<?php esc_attr_e( 'e.g., Betawi, Minangkabau', 'wp-agentic-writer' ); ?>"
/>
<button type="button" class="button wpaw-remove-language">✗</button>
</div>
`;
$('#wpaw-custom-languages-list').append(html);
});
// Remove custom language
$(document).on('click', '.wpaw-remove-language', function() {
$(this).closest('.wpaw-custom-language-item').remove();
});
});
</script>
<?php
}
/**
* Render Cost Log tab content.
*
* @since 0.1.0
*/
private function render_cost_log_tab() {
global $wpdb;
$table_name = $wpdb->prefix . 'wpaw_cost_tracking';
// Get filter parameters
$filter_post = isset( $_GET['filter_post'] ) ? intval( $_GET['filter_post'] ) : 0;
$filter_model = isset( $_GET['filter_model'] ) ? sanitize_text_field( $_GET['filter_model'] ) : '';
$filter_type = isset( $_GET['filter_type'] ) ? sanitize_text_field( $_GET['filter_type'] ) : '';
$filter_date_from = isset( $_GET['filter_date_from'] ) ? sanitize_text_field( $_GET['filter_date_from'] ) : '';
$filter_date_to = isset( $_GET['filter_date_to'] ) ? sanitize_text_field( $_GET['filter_date_to'] ) : '';
$per_page = 50;
$paged = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
$offset = ( $paged - 1 ) * $per_page;
// Build query
$where = array( '1=1' );
if ( $filter_post > 0 ) {
$where[] = $wpdb->prepare( 'post_id = %d', $filter_post );
}
if ( ! empty( $filter_model ) ) {
$where[] = $wpdb->prepare( 'model = %s', $filter_model );
}
if ( ! empty( $filter_type ) ) {
$where[] = $wpdb->prepare( 'action = %s', $filter_type );
}
if ( ! empty( $filter_date_from ) ) {
$where[] = $wpdb->prepare( 'DATE(created_at) >= %s', $filter_date_from );
}
if ( ! empty( $filter_date_to ) ) {
$where[] = $wpdb->prepare( 'DATE(created_at) <= %s', $filter_date_to );
}
$where_clause = implode( ' AND ', $where );
// Get total count
$total_items = $wpdb->get_var( "SELECT COUNT(*) FROM {$table_name} WHERE {$where_clause}" );
$total_pages = ceil( $total_items / $per_page );
// Get cost records
$costs = $wpdb->get_results(
"SELECT * FROM {$table_name} WHERE {$where_clause} ORDER BY created_at DESC LIMIT {$per_page} OFFSET {$offset}"
);
// Get summary stats
$cost_tracker = WP_Agentic_Writer_Cost_Tracker::get_instance();
$total_all_time = $wpdb->get_var( "SELECT SUM(cost) FROM {$table_name}" );
$monthly_total = $cost_tracker->get_monthly_total();
$today_total = $wpdb->get_var(
$wpdb->prepare(
"SELECT SUM(cost) FROM {$table_name} WHERE DATE(created_at) = %s",
current_time( 'Y-m-d' )
)
);
$total_posts = $wpdb->get_var( "SELECT COUNT(DISTINCT post_id) FROM {$table_name} WHERE post_id > 0" );
$avg_per_post = $total_posts > 0 ? $total_all_time / $total_posts : 0;
// Get unique models and types for filters
$models = $wpdb->get_col( "SELECT DISTINCT model FROM {$table_name} ORDER BY model" );
$types = $wpdb->get_col( "SELECT DISTINCT action FROM {$table_name} ORDER BY action" );
?>
<!-- Summary Stats -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">📊</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'Cost Summary', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc"><?php esc_html_e( 'Overview of your API spending', 'wp-agentic-writer' ); ?></p>
</div>
</div>
<div class="wpaw-section-body">
<div class="wpaw-cost-stats-grid">
<div class="wpaw-cost-stat-card">
<div class="wpaw-cost-stat-icon">💰</div>
<div class="wpaw-cost-stat-value">$<?php echo number_format( (float) $total_all_time, 4 ); ?></div>
<div class="wpaw-cost-stat-label"><?php esc_html_e( 'All Time', 'wp-agentic-writer' ); ?></div>
</div>
<div class="wpaw-cost-stat-card">
<div class="wpaw-cost-stat-icon">📅</div>
<div class="wpaw-cost-stat-value">$<?php echo number_format( (float) $monthly_total, 4 ); ?></div>
<div class="wpaw-cost-stat-label"><?php esc_html_e( 'This Month', 'wp-agentic-writer' ); ?></div>
</div>
<div class="wpaw-cost-stat-card">
<div class="wpaw-cost-stat-icon">☀️</div>
<div class="wpaw-cost-stat-value">$<?php echo number_format( (float) $today_total, 4 ); ?></div>
<div class="wpaw-cost-stat-label"><?php esc_html_e( 'Today', 'wp-agentic-writer' ); ?></div>
</div>
<div class="wpaw-cost-stat-card">
<div class="wpaw-cost-stat-icon">📝</div>
<div class="wpaw-cost-stat-value">$<?php echo number_format( (float) $avg_per_post, 4 ); ?></div>
<div class="wpaw-cost-stat-label"><?php esc_html_e( 'Avg Per Post', 'wp-agentic-writer' ); ?></div>
</div>
</div>
</div>
</div>
<!-- Filters -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">🔍</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'Filter Cost Log', 'wp-agentic-writer' ); ?></h2>
</div>
</div>
<div class="wpaw-section-body">
<form method="get" class="wpaw-cost-filters">
<input type="hidden" name="page" value="wp-agentic-writer-settings" />
<input type="hidden" name="tab" value="cost-log" />
<div class="wpaw-filter-row">
<div class="wpaw-filter-field">
<label><?php esc_html_e( 'Post ID', 'wp-agentic-writer' ); ?></label>
<input type="number" name="filter_post" value="<?php echo esc_attr( $filter_post ); ?>" placeholder="<?php esc_attr_e( 'All posts', 'wp-agentic-writer' ); ?>" />
</div>
<div class="wpaw-filter-field">
<label><?php esc_html_e( 'Model', 'wp-agentic-writer' ); ?></label>
<select name="filter_model">
<option value=""><?php esc_html_e( 'All models', 'wp-agentic-writer' ); ?></option>
<?php foreach ( $models as $model ) : ?>
<option value="<?php echo esc_attr( $model ); ?>" <?php selected( $filter_model, $model ); ?>>
<?php echo esc_html( $model ); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="wpaw-filter-field">
<label><?php esc_html_e( 'Type', 'wp-agentic-writer' ); ?></label>
<select name="filter_type">
<option value=""><?php esc_html_e( 'All types', 'wp-agentic-writer' ); ?></option>
<?php foreach ( $types as $type ) : ?>
<option value="<?php echo esc_attr( $type ); ?>" <?php selected( $filter_type, $type ); ?>>
<?php echo esc_html( ucfirst( str_replace( '_', ' ', $type ) ) ); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="wpaw-filter-field">
<label><?php esc_html_e( 'Date From', 'wp-agentic-writer' ); ?></label>
<input type="date" name="filter_date_from" value="<?php echo esc_attr( $filter_date_from ); ?>" />
</div>
<div class="wpaw-filter-field">
<label><?php esc_html_e( 'Date To', 'wp-agentic-writer' ); ?></label>
<input type="date" name="filter_date_to" value="<?php echo esc_attr( $filter_date_to ); ?>" />
</div>
<div class="wpaw-filter-actions">
<button type="submit" class="button button-primary"><?php esc_html_e( 'Apply Filters', 'wp-agentic-writer' ); ?></button>
<a href="<?php echo esc_url( admin_url( 'options-general.php?page=wp-agentic-writer-settings&tab=cost-log' ) ); ?>" class="button"><?php esc_html_e( 'Clear', 'wp-agentic-writer' ); ?></a>
</div>
</div>
</form>
</div>
</div>
<!-- Cost Log Table -->
<div class="wpaw-settings-section">
<div class="wpaw-section-header">
<div class="wpaw-section-icon">📋</div>
<div>
<h2 class="wpaw-section-title"><?php esc_html_e( 'Detailed Cost Log', 'wp-agentic-writer' ); ?></h2>
<p class="wpaw-section-desc">
<?php
printf(
esc_html__( 'Showing %d-%d of %d records', 'wp-agentic-writer' ),
$offset + 1,
min( $offset + $per_page, $total_items ),
$total_items
);
?>
</p>
</div>
<div style="margin-left: auto;">
<button type="button" class="button" id="wpaw-export-csv">
<span class="dashicons dashicons-download"></span>
<?php esc_html_e( 'Export CSV', 'wp-agentic-writer' ); ?>
</button>
</div>
</div>
<div class="wpaw-section-body">
<div class="wpaw-cost-log-table-wrapper">
<table class="wpaw-cost-log-table">
<thead>
<tr>
<th><?php _e( 'Date/Time', 'wp-agentic-writer' ); ?></th>
<th><?php _e( 'Post', 'wp-agentic-writer' ); ?></th>
<th><?php _e( 'Model', 'wp-agentic-writer' ); ?></th>
<th><?php _e( 'Type', 'wp-agentic-writer' ); ?></th>
<th><?php _e( 'Input Tokens', 'wp-agentic-writer' ); ?></th>
<th><?php _e( 'Output Tokens', 'wp-agentic-writer' ); ?></th>
<th><?php _e( 'Cost', 'wp-agentic-writer' ); ?></th>
</tr>
</thead>
<tbody>
<?php if ( empty( $costs ) ) : ?>
<tr>
<td colspan="7" style="text-align: center; padding: 2rem; color: #999;">
<?php esc_html_e( 'No cost records found.', 'wp-agentic-writer' ); ?>
</td>
</tr>
<?php else : ?>
<?php foreach ( $costs as $cost ) : ?>
<?php
$post_title = get_the_title( $cost->post_id );
if ( ! $post_title && $cost->post_id > 0 ) {
$post_title = sprintf( __( '[Removed Post #%d]', 'wp-agentic-writer' ), $cost->post_id );
$post_link = '#';
$post_class = 'wpaw-removed-post';
} elseif ( $cost->post_id > 0 ) {
$post_link = get_edit_post_link( $cost->post_id );
$post_class = '';
} else {
$post_title = '-';
$post_link = '#';
$post_class = '';
}
?>
<tr>
<td><?php echo esc_html( date_i18n( 'Y-m-d H:i:s', strtotime( $cost->created_at ) ) ); ?></td>
<td>
<?php if ( $post_link !== '#' ) : ?>
<a href="<?php echo esc_url( $post_link ); ?>" class="<?php echo esc_attr( $post_class ); ?>">
<?php echo esc_html( $post_title ); ?>
</a>
<?php else : ?>
<span class="<?php echo esc_attr( $post_class ); ?>"><?php echo esc_html( $post_title ); ?></span>
<?php endif; ?>
</td>
<td><code><?php echo esc_html( $cost->model ); ?></code></td>
<td><?php echo esc_html( ucfirst( str_replace( '_', ' ', $cost->action ) ) ); ?></td>
<td><?php echo number_format( $cost->input_tokens ); ?></td>
<td><?php echo number_format( $cost->output_tokens ); ?></td>
<td><strong>$<?php echo number_format( $cost->cost, 4 ); ?></strong></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if ( $total_pages > 1 ) : ?>
<div class="wpaw-pagination">
<?php
$base_url = add_query_arg(
array(
'page' => 'wp-agentic-writer-settings',
'tab' => 'cost-log',
'filter_post' => $filter_post,
'filter_model' => $filter_model,
'filter_type' => $filter_type,
'filter_date_from' => $filter_date_from,
'filter_date_to' => $filter_date_to,
),
admin_url( 'options-general.php' )
);
if ( $paged > 1 ) {
printf(
'<a href="%s" class="button">« %s</a>',
esc_url( add_query_arg( 'paged', $paged - 1, $base_url ) ),
esc_html__( 'Previous', 'wp-agentic-writer' )
);
}
printf(
'<span class="wpaw-pagination-info">%s %d / %d</span>',
esc_html__( 'Page', 'wp-agentic-writer' ),
$paged,
$total_pages
);
if ( $paged < $total_pages ) {
printf(
'<a href="%s" class="button">%s »</a>',
esc_url( add_query_arg( 'paged', $paged + 1, $base_url ) ),
esc_html__( 'Next', 'wp-agentic-writer' )
);
}
?>
</div>
<?php endif; ?>
</div>
</div>
<script>
jQuery(document).ready(function($) {
// Export CSV functionality
$('#wpaw-export-csv').on('click', function() {
const table = $('.wpaw-cost-log-table');
let csv = [];
// Headers
const headers = [];
table.find('thead th').each(function() {
headers.push($(this).text());
});
csv.push(headers.join(','));
// Rows
table.find('tbody tr').each(function() {
const row = [];
$(this).find('td').each(function() {
let text = $(this).text().trim();
text = text.replace(/"/g, '""'); // Escape quotes
row.push('"' + text + '"');
});
if (row.length > 0) {
csv.push(row.join(','));
}
});
// Download
const csvContent = csv.join('\n');
const blob = new Blob([csvContent], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'wp-agentic-writer-costs-' + new Date().toISOString().split('T')[0] + '.csv';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
});
});
</script>
<?php
}
}