render_styles(); echo '
'; echo '

' . esc_html__( 'PR Vision — Settings', 'pr-vision' ) . '

'; $this->render_notices(); echo '

' . esc_html__( 'Model Manager', 'pr-vision' ) . '

'; ( new PRV_Model_Manager_Table() )->render(); echo '
'; $this->render_config_form(); $this->render_save_bar(); $this->render_scripts(); echo '
'; } /** * Render dark CSS variables and component styles. * * @return void */ private function render_styles(): void { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo ''; } /** * Render POST-redirect notices (saved, run done, model CRUD, lock). * * @return void */ private function render_notices(): void { // phpcs:disable WordPress.Security.NonceVerification.Recommended if ( ! empty( $_GET['prv_saved'] ) ) { echo '
' . esc_html__( 'Settings saved.', 'pr-vision' ) . '
'; } if ( ! empty( $_GET['prv_run_done'] ) ) { $p = absint( $_GET['prv_probed'] ?? 0 ); $s = absint( $_GET['prv_skipped'] ?? 0 ); $trn = ! empty( $_GET['prv_truncated'] ); $msg = sprintf( 'Run complete: %d probed, %d skipped.%s', $p, $s, $trn ? ' Last run was truncated by the monthly budget cap.' : '' ); echo '
' . esc_html( $msg ) . '
'; } if ( ! empty( $_GET['prv_run_locked'] ) ) { echo '
' . esc_html__( 'A run is already in progress — try again when it finishes.', 'pr-vision' ) . '
'; } if ( ! empty( $_GET['prv_model_added'] ) ) { echo '
' . esc_html__( 'Model added.', 'pr-vision' ) . '
'; } if ( ! empty( $_GET['prv_model_updated'] ) ) { echo '
' . esc_html__( 'Model updated.', 'pr-vision' ) . '
'; } if ( ! empty( $_GET['prv_model_removed'] ) ) { echo '
' . esc_html__( 'Model removed.', 'pr-vision' ) . '
'; } // phpcs:enable WordPress.Security.NonceVerification.Recommended } /** * Render the secondary config form. * * @return void */ private function render_config_form(): void { echo '
'; echo ''; wp_nonce_field( PRV_Settings_Page::NONCE_SAVE, 'prv_nonce' ); echo '

' . esc_html__( 'Probe Configuration', 'pr-vision' ) . '

'; $this->render_cadence_field(); $this->render_budget_field(); $this->render_projected_cost(); $this->render_peptides_field(); $this->render_intents_field(); $this->render_api_key_status(); echo '
'; } /** * Render cadence selector with next-run timestamp. * * @return void */ private function render_cadence_field(): void { $cadence = PRV_Config::get_cadence(); $next_ts = PRV_Cron::next_run_timestamp(); $next_str = $next_ts ? gmdate( 'Y-m-d H:i T', (int) $next_ts ) : 'Not scheduled'; echo '
'; echo ''; echo '

' . esc_html__( 'Next run: ', 'pr-vision' ) . esc_html( $next_str ) . '

'; } /** * Render monthly budget cap input. * * @return void */ private function render_budget_field(): void { $budget = PRV_Config::get_monthly_budget_usd(); echo '
'; echo '
'; } /** * Render live projected-cost inset panel. * * @return void */ private function render_projected_cost(): void { $cost = PRV_Config::get_projected_cost(); $cls = $cost['over_cap'] ? 'prv-cost-inset over-cap' : 'prv-cost-inset'; echo '
'; echo 'Projected cost: ~$' . esc_html( number_format( $cost['per_run_usd'], 4 ) ) . '/run  ·  '; echo '~$' . esc_html( number_format( $cost['per_month_usd'], 4 ) ) . '/month  ·  '; echo esc_html( (string) $cost['probe_count'] ) . ' probes/run'; if ( $cost['over_cap'] ) { echo '  ⚠ Over cap — runs will truncate'; } echo '
'; } /** * Render peptide set textarea (JSON). * * @return void */ private function render_peptides_field(): void { $peptides = PRV_Config::get_peptides(); echo '
'; echo '

' . esc_html( count( $peptides ) ) . ' peptides (JSON array of {slug, label}).

'; echo '
'; } /** * Render prompt intents textarea. * * @return void */ private function render_intents_field(): void { $intents = PRV_Config::get_prompt_intents(); echo '
'; echo '
'; } /** * Render the read-only API-key three-state status. * * @return void */ private function render_api_key_status(): void { $defined = defined( 'PRV_OPENROUTER_API_KEY' ) && '' !== constant( 'PRV_OPENROUTER_API_KEY' ); $status = (string) get_option( 'prv_api_key_status', 'unknown' ); echo '
' . esc_html__( 'API Key Status (read-only)', 'pr-vision' ) . '
'; if ( ! $defined ) { echo '● ' . esc_html__( 'Not defined — set PRV_OPENROUTER_API_KEY in wp-config.php', 'pr-vision' ) . ''; } elseif ( 'failed' === $status ) { echo '● ' . esc_html__( 'Defined — last run failed (key may be invalid or revoked)', 'pr-vision' ) . ''; } else { echo '● ' . esc_html__( 'Defined — last run OK', 'pr-vision' ) . ''; } echo '
'; } /** * Render the sticky save-bar with scoring-change warning and Run now. * * @return void */ private function render_save_bar(): void { echo '
'; echo ''; echo ''; echo '
'; echo ''; wp_nonce_field( PRV_Settings_Page::NONCE_RUN, 'prv_nonce' ); $locked = PRV_Run_Lock::is_locked(); echo ''; echo '
'; $next_ts = PRV_Cron::next_run_timestamp(); if ( $next_ts ) { echo '' . esc_html__( 'Next auto-run: ', 'pr-vision' ) . esc_html( gmdate( 'Y-m-d H:i T', (int) $next_ts ) ) . ''; } echo '
'; } /** * Render inline JS: scoring-change warn, sort headers, Test AJAX, Run-now guard. * * @return void */ private function render_scripts(): void { $ajax_url = admin_url( 'admin-ajax.php' ); $nonce = wp_create_nonce( PRV_Settings_Page::NONCE_TEST ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo ''; } }