settings = $settings; // Get the post ID from the URL $post_id = isset( $_GET[ 'post' ] ) ? intval( $_GET[ 'post' ] ) : 0; // phpcs:ignore if ( ! empty( $_GET[ 'post_type' ] ) ) { // phpcs:ignore $post_type = $_GET[ 'post_type' ]; // phpcs:ignore } elseif ( $post_id > 0 ) { $post_type = get_post_type( $post_id ); } else { $post_type = ''; } if ( ! empty( $this->isEnabledWriteWithAI() ) && 'docs' == $post_type ) { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_ai_edit_assets' ) ); } // Legacy AJAX handler kept for back-compat; the redesigned modal uses the REST route. add_action( 'wp_ajax_generate_openai_content', array( $this, 'generate_openai_content_callback' ) ); } public function enqueue_ai_edit_assets( $hook ) { if ( 'post.php' !== $hook && 'post-new.php' !== $hook ) { return; } global $post_type; if ( 'docs' !== $post_type ) { return; } $factory = new ProviderFactory( $this->settings ); $api_key = $factory->api_key_for( $factory->active_platform() ); $has_key = ! empty( $api_key ); // Resolve the *active* AI platform + model (multi-platform aware) so the // modal reflects the current Settings → AI selection instead of the legacy // OpenAI-only `write_with_ai_model` key. $active_platform = $factory->active_platform(); $active_model = $factory->active_model(); $platform_labels = ModelRegistry::platforms(); $model_labels = ModelRegistry::models( $active_platform ); // Glossary suggestions are existing-terms-only, and the glossaries taxonomy is only // registered for Pro (see Core\PostType::register_glossaries_taxonomy). So the "Suggest // glossaries" control must require, on top of the two settings, that Pro is active AND at // least one glossary term exists — otherwise the modal advertises an offer that can never // return anything. Mirrors the Docs-AI-suite availability check in Core\DocsAISuite. $glossary_count = wp_count_terms( array( 'taxonomy' => 'glossaries', 'hide_empty' => false ) ); $has_glossary_terms = ! is_wp_error( $glossary_count ) && (int) $glossary_count > 0; $glossary_suggestions_enabled = (bool) $this->settings->get( 'enable_glossaries', false ) && (bool) $this->settings->get( 'show_glossary_suggestions', true ) && betterdocs()->is_pro_active() && $has_glossary_terms; // Write with AI — loads even without a key so the modal can show its // "add an API key" banner (matches the legacy inline form behavior). betterdocs()->assets->enqueue( 'betterdocs-write-with-ai', 'blocks/write-with-ai.js' ); betterdocs()->assets->enqueue( 'betterdocs-write-with-ai-style', 'blocks/write-with-ai-style.css' ); wp_localize_script( 'betterdocs-write-with-ai', 'betterdocsWriteWithAI', array( 'rest_url' => esc_url_raw( rest_url( 'betterdocs/v1/write-with-ai' ) ), 'rest_nonce' => wp_create_nonce( 'wp_rest' ), 'post_id' => get_the_ID(), 'has_key' => $has_key, // Term-suggestion (Docs AI Suite) wiring reused by the Write-with-AI // preview step. Endpoint + gate mirror REST\DocsAISuite / Core\DocsAISuite. 'rest_suggest_url' => esc_url_raw( rest_url( 'betterdocs/v1/ai-suggest-terms' ) ), 'suggest_terms_enabled' => (bool) $this->settings->get( 'enable_docs_ai_suite', true ), // Glossary suggestions follow the glossary feature AND real availability // (Pro active + at least one glossary term); see the computation above. 'glossary_suggestions_enabled' => $glossary_suggestions_enabled, 'platform' => $active_platform, 'platform_label' => isset( $platform_labels[ $active_platform ] ) ? $platform_labels[ $active_platform ] : ucfirst( (string) $active_platform ), 'model' => $active_model, 'model_label' => isset( $model_labels[ $active_model ] ) ? $model_labels[ $active_model ] : $active_model, 'max_token' => (int) $this->settings->get( 'ai_autowrite_max_token', 2500 ), 'settings_url' => esc_url( admin_url( 'admin.php?page=betterdocs-settings#betterdocs-ai' ) ), 'woo_active' => class_exists( 'WooCommerce' ), 'is_multilingual_active' => Helper::is_multilingual_active(), 'language_options' => Helper::get_active_languages(), 'instructions' => $this->get_instruction_choices(), // From Git is a Pro feature. Pro is detected here; Git enabled/auth // status is filled in by Pro via the filter below (default: off). 'is_pro_active' => betterdocs()->is_pro_active(), 'git' => apply_filters( 'betterdocs_write_with_ai_git', array( 'enabled' => false, 'connected' => false, 'settings_url' => admin_url( 'admin.php?page=betterdocs-settings#git-sync' ), ) ), ) ); // AI Edit — only meaningful with a key configured. if ( ! $has_key ) { return; } betterdocs()->assets->enqueue( 'betterdocs-ai-edit', 'blocks/ai-edit.js' ); betterdocs()->assets->enqueue( 'betterdocs-ai-edit-style', 'blocks/ai-edit-style.css' ); wp_localize_script( 'betterdocs-ai-edit', 'betterdocsAIEdit', array( 'rest_url' => esc_url_raw( rest_url( 'betterdocs/v1/ai-edit' ) ), 'rest_nonce' => wp_create_nonce( 'wp_rest' ), 'post_id' => get_the_ID(), 'instructions' => $this->get_instruction_choices(), 'actions' => AIEdit::get_localized_actions() ) ); } public function isEnabledWriteWithAI() { $isEnableAutoWrite = $this->settings->get( 'enable_write_with_ai', true ); return $isEnableAutoWrite; } public function isValidAPIKey( $apiKey ) { if ( empty( $apiKey ) ) { return array( 'valid' => false, 'message' => 'Please Insert your API Key to use this Write with AI feature.' ); } $factory = new ProviderFactory( $this->settings ); return $factory->validate( $factory->active_platform(), $apiKey ); } public function get_api_key() { $factory = new ProviderFactory( $this->settings ); return $factory->api_key_for( $factory->active_platform() ); } /** * The built-in "Default" instruction body. * * Single source of truth shared by {@see Settings::get_default()} (which seeds * the editable "Default" instruction set) and {@see self::get_system_prompt()} * (the fallback when no saved Default content exists). * * @return string */ public static function default_instruction_content() { return <<<'PROMPT' You are a Senior Technical Writer specializing in comprehensive, high-quality documentation. Your goal is to produce documentation that scores 100/100 on clarity, completeness, and structure. ## Output format Return only HTML body content. Never wrap output in ``, ``, `
`, ``, or markdown code fences (no ```html ... ```). Use semantic, Gutenberg-friendly tags only: - Headings: `` for prose - Lists: `
`
- Code blocks: `...
` for multi-line code or commands
- Quotes: ``
- Tables: `` with ``, ``, ``, ``, ` `
- Images: `
`
Apply `key term` to important topic terms inside headings and to the first occurrence of each keyword in body text. Use it sparingly — never wrap whole sentences or wrap text inside `href`, `src`, `alt`, or other attributes.
Do not emit `