'https://chatgpt.com/?q={prompt}', 'claude' => 'https://claude.ai/new', 'gemini' => 'https://gemini.google.com/app', 'perplexity' => 'https://www.perplexity.ai/search?q={prompt}', ) ); wp_localize_script( 'agentic-markdown-bridge-admin-settings', 'AgenticMarkdownAdminSettings', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'agentic_mb_generate_llms_preview' ), 'homeUrl' => home_url( '/' ), 'llmsUrl' => home_url( '/llms.txt' ), 'providerUrls' => $provider_urls, 'strings' => array( 'generating' => __( 'Generating…', 'agentic-markdown-bridge' ), 'failed' => __( 'Failed', 'agentic-markdown-bridge' ), 'draftGenerated' => __( 'Draft generated. Review it, then Save Changes.', 'agentic-markdown-bridge' ), 'promptCopied' => __( 'Prompt copied.', 'agentic-markdown-bridge' ), 'copyFailedManual' => __( 'Copy failed. Select the prompt above and copy it manually.', 'agentic-markdown-bridge' ), 'claudeCopied' => __( 'Claude opened. The prompt was copied — paste it into Claude and submit.', 'agentic-markdown-bridge' ), 'geminiCopied' => __( 'Gemini opened. The prompt was copied — paste it into Gemini and submit.', 'agentic-markdown-bridge' ), 'claudeManual' => __( 'Claude opened. Copy the prompt from the preview below and paste it into Claude.', 'agentic-markdown-bridge' ), 'geminiManual' => __( 'Gemini opened. Copy the prompt from the preview below and paste it into Gemini.', 'agentic-markdown-bridge' ), 'openedProvider' => __( 'Opened in a new tab. Review the AI output before saving it as llms.txt.', 'agentic-markdown-bridge' ), ), ) ); } if ( 'tools_page_agentic-markdown-content' === $hook_suffix ) { wp_enqueue_script( 'agentic-markdown-bridge-admin-content', $base_url . 'admin-content.js', array(), AGENTIC_MB_VERSION, true ); } if ( 'tools_page_agentic-markdown-diagnostics' === $hook_suffix ) { wp_enqueue_style( 'agentic-markdown-bridge-admin-diagnostics', $base_url . 'admin-diagnostics.css', array(), AGENTIC_MB_VERSION ); wp_enqueue_script( 'agentic-markdown-bridge-admin-diagnostics', $base_url . 'admin-diagnostics.js', array(), AGENTIC_MB_VERSION, true ); wp_localize_script( 'agentic-markdown-bridge-admin-diagnostics', 'AgenticMarkdownDiagnostics', array( 'running' => __( 'Running…', 'agentic-markdown-bridge' ), 'failed' => __( 'FAILED', 'agentic-markdown-bridge' ), 'noContentType' => __( 'no content-type', 'agentic-markdown-bridge' ), 'tests' => array( array( 'label' => __( 'Homepage Markdown', 'agentic-markdown-bridge' ), 'url' => Agentic_Markdown_Bridge::get_front_markdown_url(), ), array( 'label' => __( 'llms.txt', 'agentic-markdown-bridge' ), 'url' => Agentic_Markdown_Bridge::get_llms_txt_url(), ), ), ) ); } if ( in_array( $hook_suffix, array( 'post.php', 'post-new.php' ), true ) ) { $screen = get_current_screen(); if ( $screen && isset( $screen->post_type ) && array_key_exists( $screen->post_type, Agentic_Markdown_Bridge::get_available_post_types() ) ) { wp_enqueue_style( 'agentic-markdown-bridge-admin-editor', $base_url . 'admin-editor.css', array(), AGENTIC_MB_VERSION ); wp_enqueue_script( 'agentic-markdown-bridge-admin-editor', $base_url . 'admin-editor.js', array(), AGENTIC_MB_VERSION, true ); } } } /** Register plugin setting and fields. */ public static function register_settings() { register_setting( 'agentic_markdown_bridge', Agentic_Markdown_Bridge::SETTINGS_OPTION, array( 'type' => 'array', 'sanitize_callback' => array( __CLASS__, 'sanitize_settings' ), 'default' => Agentic_Markdown_Bridge::get_default_settings(), ) ); add_settings_section( 'amb_general', __( 'Markdown representations', 'agentic-markdown-bridge' ), array( __CLASS__, 'section_general' ), 'agentic-markdown-bridge' ); self::add_checkbox( 'enabled', __( 'Markdown output', 'agentic-markdown-bridge' ), __( 'Enable Markdown representations for eligible public content.', 'agentic-markdown-bridge' ), 'amb_general' ); add_settings_field( 'post_types', __( 'Content types', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_post_types' ), 'agentic-markdown-bridge', 'amb_general' ); add_settings_field( 'title_mode', __( 'Post title in Markdown', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_title_mode' ), 'agentic-markdown-bridge', 'amb_general' ); add_settings_field( 'pre_h1_mode', __( 'Content before first H1', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_pre_h1_mode' ), 'agentic-markdown-bridge', 'amb_general' ); add_settings_field( 'exclude_classes', __( 'Exclude sections', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_exclusion_classes' ), 'agentic-markdown-bridge', 'amb_general' ); add_settings_section( 'amb_output', __( 'Discovery, metadata & performance', 'agentic-markdown-bridge' ), array( __CLASS__, 'section_output' ), 'agentic-markdown-bridge' ); self::add_checkbox( 'alternate_link', __( 'Markdown discovery', 'agentic-markdown-bridge' ), __( 'Add rel="alternate" type="text/markdown" to eligible HTML pages.', 'agentic-markdown-bridge' ), 'amb_output' ); self::add_checkbox( 'content_negotiation', __( 'Content negotiation', 'agentic-markdown-bridge' ), __( 'Serve Markdown from the canonical URL when a client explicitly prefers Accept: text/markdown.', 'agentic-markdown-bridge' ), 'amb_output' ); self::add_checkbox( 'front_matter', __( 'YAML front matter', 'agentic-markdown-bridge' ), __( 'Prepend title, canonical URL, author, dates and language to generated Markdown.', 'agentic-markdown-bridge' ), 'amb_output' ); self::add_checkbox( 'cache_enabled', __( 'Markdown cache', 'agentic-markdown-bridge' ), __( 'Cache generated Markdown in WordPress transients.', 'agentic-markdown-bridge' ), 'amb_output' ); add_settings_field( 'cache_ttl', __( 'Generation cache lifetime', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_number' ), 'agentic-markdown-bridge', 'amb_output', array( 'key' => 'cache_ttl', 'min' => 60, 'max' => 604800, 'suffix' => __( 'seconds', 'agentic-markdown-bridge' ) ) ); add_settings_field( 'http_cache_seconds', __( 'Browser/crawler cache', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_number' ), 'agentic-markdown-bridge', 'amb_output', array( 'key' => 'http_cache_seconds', 'min' => 0, 'max' => 86400, 'suffix' => __( 'seconds', 'agentic-markdown-bridge' ) ) ); add_settings_field( 'x_robots', __( 'Markdown search indexing', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_x_robots' ), 'agentic-markdown-bridge', 'amb_output' ); self::add_checkbox( 'analytics', __( 'Local view counters', 'agentic-markdown-bridge' ), __( 'Count successful Markdown and managed llms.txt GET requests locally. No IP addresses, user agents or external analytics are stored.', 'agentic-markdown-bridge' ), 'amb_output' ); add_settings_section( 'amb_frontend', __( 'Front-end actions', 'agentic-markdown-bridge' ), array( __CLASS__, 'section_frontend' ), 'agentic-markdown-bridge' ); self::add_checkbox( 'frontend_button', __( 'Front-end actions', 'agentic-markdown-bridge' ), __( 'Show fixed Markdown actions on eligible pages.', 'agentic-markdown-bridge' ), 'amb_frontend' ); add_settings_field( 'button_label', __( 'View Markdown label', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_text' ), 'agentic-markdown-bridge', 'amb_frontend', array( 'key' => 'button_label', 'placeholder' => __( 'View Markdown', 'agentic-markdown-bridge' ) ) ); self::add_checkbox( 'button_copy', __( 'Copy Markdown action', 'agentic-markdown-bridge' ), __( 'Add a button that fetches and copies the current Markdown representation.', 'agentic-markdown-bridge' ), 'amb_frontend' ); add_settings_field( 'copy_label', __( 'Copy label', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_text' ), 'agentic-markdown-bridge', 'amb_frontend', array( 'key' => 'copy_label', 'placeholder' => __( 'Copy Markdown', 'agentic-markdown-bridge' ) ) ); self::add_checkbox( 'button_llms', __( 'llms.txt action', 'agentic-markdown-bridge' ), __( 'Add a front-end link to the discovered llms.txt resource.', 'agentic-markdown-bridge' ), 'amb_frontend' ); add_settings_field( 'llms_button_label', __( 'llms.txt label', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_text' ), 'agentic-markdown-bridge', 'amb_frontend', array( 'key' => 'llms_button_label', 'placeholder' => __( 'View llms.txt', 'agentic-markdown-bridge' ) ) ); add_settings_field( 'button_position', __( 'Position', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_select' ), 'agentic-markdown-bridge', 'amb_frontend', array( 'key' => 'button_position', 'options' => array( 'bottom-left' => __( 'Bottom left', 'agentic-markdown-bridge' ), 'bottom-center' => __( 'Bottom center', 'agentic-markdown-bridge' ), 'bottom-right' => __( 'Bottom right', 'agentic-markdown-bridge' ) ) ) ); add_settings_field( 'button_style', __( 'Style', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_select' ), 'agentic-markdown-bridge', 'amb_frontend', array( 'key' => 'button_style', 'options' => array( 'dark' => __( 'Dark', 'agentic-markdown-bridge' ), 'light' => __( 'Light', 'agentic-markdown-bridge' ), 'auto' => __( 'Automatic light/dark', 'agentic-markdown-bridge' ) ) ) ); add_settings_field( 'button_shape', __( 'Shape', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_select' ), 'agentic-markdown-bridge', 'amb_frontend', array( 'key' => 'button_shape', 'options' => array( 'pill' => __( 'Pill', 'agentic-markdown-bridge' ), 'rounded' => __( 'Rounded', 'agentic-markdown-bridge' ), 'square' => __( 'Square', 'agentic-markdown-bridge' ) ) ) ); add_settings_section( 'amb_llms', __( 'llms.txt manager', 'agentic-markdown-bridge' ), array( __CLASS__, 'section_llms' ), 'agentic-markdown-bridge' ); self::add_checkbox( 'manage_llms', __( 'Managed llms.txt', 'agentic-markdown-bridge' ), __( 'Create and serve /llms.txt from WordPress when there is no physical root file.', 'agentic-markdown-bridge' ), 'amb_llms' ); add_settings_field( 'llms_content', __( 'llms.txt content', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_llms_content' ), 'agentic-markdown-bridge', 'amb_llms' ); self::add_checkbox( 'describedby_link', __( 'External llms.txt discovery', 'agentic-markdown-bridge' ), __( 'Advertise an externally managed llms.txt when the built-in manager is off.', 'agentic-markdown-bridge' ), 'amb_llms' ); add_settings_field( 'llms_url', __( 'External llms.txt URL', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_text' ), 'agentic-markdown-bridge', 'amb_llms', array( 'key' => 'llms_url', 'placeholder' => '/llms.txt', 'class' => 'regular-text code' ) ); add_settings_field( 'llms_generator', __( 'llms.txt generator', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_llms_generator' ), 'agentic-markdown-bridge', 'amb_llms' ); } /** Helper: checkbox field registration. */ private static function add_checkbox( $key, $title, $label, $section ) { add_settings_field( 'amb_' . $key, $title, array( __CLASS__, 'render_checkbox' ), 'agentic-markdown-bridge', $section, array( 'key' => $key, 'label' => $label ) ); } /** Sanitize all settings. */ public static function sanitize_settings( $input ) { // The Settings API verifies the option-page nonce and manage_options // capability in options.php before this callback runs for a form save. $input = is_array( $input ) ? $input : array(); $defaults = Agentic_Markdown_Bridge::get_default_settings(); $current = Agentic_Markdown_Bridge::get_settings(); $types = array_keys( Agentic_Markdown_Bridge::get_available_post_types() ); $post_types = isset( $input['post_types'] ) && is_array( $input['post_types'] ) ? array_values( array_intersect( array_map( 'sanitize_key', $input['post_types'] ), $types ) ) : array(); $out = $defaults; foreach ( array( 'enabled', 'alternate_link', 'content_negotiation', 'front_matter', 'cache_enabled', 'analytics', 'frontend_button', 'button_copy', 'button_llms', 'manage_llms', 'describedby_link', 'llms_generator_use_markdown' ) as $key ) { $out[ $key ] = empty( $input[ $key ] ) ? 0 : 1; } $out['post_types'] = $post_types; $title_mode = isset( $input['title_mode'] ) ? sanitize_key( $input['title_mode'] ) : 'auto'; $out['title_mode'] = in_array( $title_mode, array( 'auto', 'always', 'never' ), true ) ? $title_mode : 'auto'; $pre_h1_mode = isset( $input['pre_h1_mode'] ) ? sanitize_key( $input['pre_h1_mode'] ) : 'ignore'; $out['pre_h1_mode'] = in_array( $pre_h1_mode, array( 'ignore', 'include' ), true ) ? $pre_h1_mode : 'ignore'; $exclude_raw = isset( $input['exclude_classes'] ) ? sanitize_text_field( $input['exclude_classes'] ) : ''; $exclude_classes = array(); foreach ( preg_split( '/[\s,]+/', $exclude_raw, -1, PREG_SPLIT_NO_EMPTY ) as $token ) { $token = ltrim( trim( (string) $token ), '.' ); $token = preg_replace( '/[^A-Za-z0-9_-]/', '', $token ); if ( '' !== $token ) { $exclude_classes[] = $token; } } $exclude_classes = array_values( array_unique( array_slice( $exclude_classes, 0, 50 ) ) ); $out['exclude_classes'] = implode( ', ', array_map( static function ( $class ) { return '.' . $class; }, $exclude_classes ) ); $out['cache_ttl'] = min( 604800, max( 60, isset( $input['cache_ttl'] ) ? absint( $input['cache_ttl'] ) : 43200 ) ); $out['http_cache_seconds'] = min( 86400, max( 0, isset( $input['http_cache_seconds'] ) ? absint( $input['http_cache_seconds'] ) : 300 ) ); $x_robots = isset( $input['x_robots'] ) ? sanitize_key( $input['x_robots'] ) : 'none'; $out['x_robots'] = in_array( $x_robots, array( 'none', 'noindex', 'index' ), true ) ? $x_robots : 'none'; foreach ( array( 'button_label', 'copy_label', 'llms_button_label' ) as $key ) { if ( isset( $input[ $key ] ) ) { $value = sanitize_text_field( $input[ $key ] ); } elseif ( isset( $current[ $key ] ) ) { $value = sanitize_text_field( (string) $current[ $key ] ); } else { $value = ''; } $out[ $key ] = trim( $value ); } $position = isset( $input['button_position'] ) ? sanitize_key( $input['button_position'] ) : 'bottom-center'; $out['button_position'] = in_array( $position, array( 'bottom-left', 'bottom-center', 'bottom-right' ), true ) ? $position : 'bottom-center'; $style = isset( $input['button_style'] ) ? sanitize_key( $input['button_style'] ) : 'dark'; $out['button_style'] = in_array( $style, array( 'dark', 'light', 'auto' ), true ) ? $style : 'dark'; $shape = isset( $input['button_shape'] ) ? sanitize_key( $input['button_shape'] ) : 'pill'; $out['button_shape'] = in_array( $shape, array( 'pill', 'rounded', 'square' ), true ) ? $shape : 'pill'; $llms_url = isset( $input['llms_url'] ) ? trim( sanitize_text_field( $input['llms_url'] ) ) : '/llms.txt'; if ( $llms_url && 0 !== strpos( $llms_url, '/' ) ) { $llms_url = esc_url_raw( $llms_url, array( 'http', 'https' ) ); } $out['llms_url'] = $llms_url ? $llms_url : '/llms.txt'; $out['llms_generator_summary'] = isset( $input['llms_generator_summary'] ) ? sanitize_textarea_field( $input['llms_generator_summary'] ) : ''; $out['llms_generator_ids'] = isset( $input['llms_generator_ids'] ) && is_array( $input['llms_generator_ids'] ) ? array_values( array_filter( array_unique( array_map( 'absint', $input['llms_generator_ids'] ) ) ) ) : array(); $llms_content = isset( $input['llms_content'] ) ? Agentic_Markdown_Bridge::sanitize_markdown_text( $input['llms_content'] ) : (string) $current['llms_content']; if ( $out['manage_llms'] && '' === trim( $llms_content ) ) { $llms_content = Agentic_Markdown_Bridge::get_default_llms_content(); } if ( ( $out['manage_llms'] || Agentic_Markdown_Bridge::physical_llms_exists() ) && ! preg_match( '/^(?:\xEF\xBB\xBF)?\s*#\s+\S+/u', $llms_content ) ) { add_settings_error( Agentic_Markdown_Bridge::SETTINGS_OPTION, 'amb_llms_h1', __( 'llms.txt should begin with an H1 heading (# Site Name).', 'agentic-markdown-bridge' ), 'warning' ); } $out['llms_content'] = $llms_content; return $out; } /** * Apply settings side effects only after the sanitized option is stored. * * @param mixed $old_value Previous option value. * @param mixed $new_value New option value. */ public static function after_settings_update( $old_value, $new_value ) { $old_value = is_array( $old_value ) ? $old_value : array(); $new_value = is_array( $new_value ) ? $new_value : array(); $old_content = isset( $old_value['llms_content'] ) ? (string) $old_value['llms_content'] : ''; $new_content = isset( $new_value['llms_content'] ) ? (string) $new_value['llms_content'] : ''; $updated = false; Agentic_Markdown_Bridge::reset_settings_cache(); if ( Agentic_Markdown_Bridge::physical_llms_exists() ) { $physical = Agentic_Markdown_Bridge::read_physical_llms_content(); if ( false !== $physical && $physical !== $new_content ) { $result = Agentic_Markdown_Bridge::write_physical_llms_content( $new_content ); if ( is_wp_error( $result ) ) { add_settings_error( Agentic_Markdown_Bridge::SETTINGS_OPTION, 'amb_llms_write', $result->get_error_message(), 'error' ); } else { $updated = true; add_settings_error( Agentic_Markdown_Bridge::SETTINGS_OPTION, 'amb_llms_updated', __( 'The physical llms.txt file was updated safely.', 'agentic-markdown-bridge' ), 'success' ); } } } elseif ( $old_content !== $new_content ) { $updated = true; } if ( $updated ) { update_option( 'agentic_markdown_bridge_llms_modified', time(), false ); } } /** Admin menus. */ public static function add_admin_pages() { add_options_page( __( 'Agentic Markdown Bridge', 'agentic-markdown-bridge' ), __( 'Agentic Markdown', 'agentic-markdown-bridge' ), 'manage_options', 'agentic-markdown-bridge', array( __CLASS__, 'render_settings_page' ) ); add_management_page( __( 'Agentic Markdown Content', 'agentic-markdown-bridge' ), __( 'Markdown Content', 'agentic-markdown-bridge' ), 'edit_posts', 'agentic-markdown-content', array( __CLASS__, 'render_content_page' ) ); add_management_page( __( 'Agentic Markdown Diagnostics', 'agentic-markdown-bridge' ), __( 'Markdown Diagnostics', 'agentic-markdown-bridge' ), 'manage_options', 'agentic-markdown-diagnostics', array( __CLASS__, 'render_diagnostics_page' ) ); } /** Plugin Settings action link. */ public static function add_settings_link( $links ) { array_unshift( $links, '' . esc_html__( 'Settings', 'agentic-markdown-bridge' ) . '' ); return $links; } /** Post list row action. */ public static function add_post_row_action( $actions, $post ) { if ( Agentic_Markdown_Bridge::is_post_enabled( $post ) ) { $actions['agentic_markdown'] = '' . esc_html__( 'View Markdown', 'agentic-markdown-bridge' ) . ''; } return $actions; } /** Settings page. */ public static function render_settings_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } ?>

' . esc_html__( 'Markdown is generated from rendered WordPress content. Individual posts can override availability, title handling, and the generated Markdown.', 'agentic-markdown-bridge' ) . '

'; } public static function section_output() { echo '

' . esc_html__( 'Control discovery, optional content negotiation, metadata, caching and search-engine headers.', 'agentic-markdown-bridge' ) . '

'; } public static function section_frontend() { echo '

' . esc_html__( 'Optional visitor-facing actions. These are off by default and do not affect machine discovery.', 'agentic-markdown-bridge' ) . '

'; } public static function section_llms() { $physical = Agentic_Markdown_Bridge::physical_llms_exists(); echo '

' . esc_html__( 'Create, edit or discover llms.txt without requiring FTP or cPanel.', 'agentic-markdown-bridge' ) . '

'; if ( $physical ) { if ( Agentic_Markdown_Bridge::physical_llms_is_symlink() ) { echo '

' . esc_html__( 'A physical llms.txt symbolic link exists. It can be previewed, but the plugin will not edit symlinks for safety.', 'agentic-markdown-bridge' ) . '

'; } elseif ( Agentic_Markdown_Bridge::physical_llms_is_writable() ) { echo '

' . esc_html__( 'A physical root llms.txt file exists. Its content is loaded below, and saving updates it using a temporary backup and atomic replacement.', 'agentic-markdown-bridge' ) . '

'; } else { echo '

' . esc_html__( 'A physical root llms.txt file exists but WordPress cannot safely write to it.', 'agentic-markdown-bridge' ) . '

'; } } $url = Agentic_Markdown_Bridge::physical_llms_exists() ? home_url( '/llms.txt' ) : Agentic_Markdown_Bridge::get_llms_txt_url(); if ( $url ) { echo '

' . esc_html__( 'View public llms.txt', 'agentic-markdown-bridge' ) . '

'; } } /** Generic checkbox renderer. */ public static function render_checkbox( $args ) { $s = Agentic_Markdown_Bridge::get_settings(); $key = sanitize_key( $args['key'] ); ?> $object ) { echo ''; } } public static function render_title_mode() { self::render_select( array( 'key' => 'title_mode', 'options' => array( 'auto' => __( 'Auto — add only when content has no H1', 'agentic-markdown-bridge' ), 'always' => __( 'Always include WordPress title', 'agentic-markdown-bridge' ), 'never' => __( 'Never include WordPress title', 'agentic-markdown-bridge' ) ) ) ); } public static function render_pre_h1_mode() { self::render_select( array( 'key' => 'pre_h1_mode', 'options' => array( 'ignore' => __( 'Ignore content before first H1 — Recommended', 'agentic-markdown-bridge' ), 'include' => __( 'Include content before first H1', 'agentic-markdown-bridge' ) ) ) ); echo '

' . esc_html__( 'Applies to automatically generated Markdown only. If no H1 exists, no content is removed.', 'agentic-markdown-bridge' ) . '

'; } public static function render_exclusion_classes() { $s = Agentic_Markdown_Bridge::get_settings(); $value = isset( $s['exclude_classes'] ) ? (string) $s['exclude_classes'] : ''; echo ''; echo '

' . esc_html__( 'Optional additional CSS classes to remove, including their entire contents, from automatically generated Markdown. Separate classes with commas.', 'agentic-markdown-bridge' ) . '

'; echo '

agentic-markdown-exclude — ' . esc_html__( 'built-in class that always excludes a Gutenberg block or HTML element.', 'agentic-markdown-bridge' ) . '

data-agentic-markdown="exclude" — ' . esc_html__( 'developer-friendly equivalent for custom markup.', 'agentic-markdown-bridge' ) . '

'; } public static function render_x_robots() { self::render_select( array( 'key' => 'x_robots', 'options' => array( 'none' => __( 'No X-Robots-Tag (recommended default)', 'agentic-markdown-bridge' ), 'noindex' => __( 'noindex, follow', 'agentic-markdown-bridge' ), 'index' => __( 'index, follow', 'agentic-markdown-bridge' ) ) ) ); } public static function render_text( $args ) { $s = Agentic_Markdown_Bridge::get_settings(); $key = sanitize_key( $args['key'] ); $class = isset( $args['class'] ) ? $args['class'] : 'regular-text'; echo ''; } public static function render_number( $args ) { $s = Agentic_Markdown_Bridge::get_settings(); $key = sanitize_key( $args['key'] ); echo ' ' . esc_html( $args['suffix'] ); } public static function render_select( $args ) { $s = Agentic_Markdown_Bridge::get_settings(); $key = sanitize_key( $args['key'] ); $value = (string) $s[ $key ]; echo ''; } /** llms editor. */ public static function render_llms_content() { $s = Agentic_Markdown_Bridge::get_settings(); $value = (string) $s['llms_content']; $physical = Agentic_Markdown_Bridge::read_physical_llms_content(); if ( false !== $physical ) { $value = $physical; } elseif ( '' === trim( $value ) ) { $value = Agentic_Markdown_Bridge::get_default_llms_content(); } echo ''; echo '

' . esc_html__( 'Source:', 'agentic-markdown-bridge' ) . ' ' . esc_html( false !== $physical ? __( 'physical /llms.txt file', 'agentic-markdown-bridge' ) : __( 'WordPress-managed content', 'agentic-markdown-bridge' ) ) . '

'; } /** llms generator controls. */ public static function render_llms_generator() { $s = Agentic_Markdown_Bridge::get_settings(); $selected = array_map( 'absint', (array) $s['llms_generator_ids'] ); $candidates = get_posts( array( 'post_type' => array_keys( Agentic_Markdown_Bridge::get_available_post_types() ), 'post_status' => 'publish', 'numberposts' => 200, 'orderby' => 'title', 'order' => 'ASC', ) ); ?>


__( 'You are not allowed to generate llms.txt.', 'agentic-markdown-bridge' ) ), 403 ); } check_ajax_referer( 'agentic_mb_generate_llms_preview' ); $settings = Agentic_Markdown_Bridge::get_settings(); $settings['llms_generator_summary'] = isset( $_POST['summary'] ) ? sanitize_textarea_field( wp_unslash( $_POST['summary'] ) ) : ''; $use_markdown = isset( $_POST['use_markdown'] ) ? sanitize_text_field( wp_unslash( $_POST['use_markdown'] ) ) : ''; $settings['llms_generator_use_markdown'] = '1' === $use_markdown ? 1 : 0; $posted_ids = isset( $_POST['ids'] ) && is_array( $_POST['ids'] ) ? map_deep( wp_unslash( $_POST['ids'] ), 'sanitize_text_field' ) : array(); $posted_ids = array_map( 'absint', array_filter( $posted_ids, 'is_scalar' ) ); $settings['llms_generator_ids'] = array_values( array_filter( array_unique( $posted_ids ) ) ); wp_send_json_success( array( 'content' => Agentic_Markdown_Bridge::generate_llms_content( $settings ) ) ); } /** Add editor meta boxes. */ public static function add_meta_boxes() { foreach ( Agentic_Markdown_Bridge::get_available_post_types() as $type => $object ) { add_meta_box( 'agentic-markdown-bridge-options', __( 'Agentic Markdown', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_options_meta_box' ), $type, 'side', 'default', array( '__block_editor_compatible_meta_box' => true ) ); add_meta_box( 'agentic-markdown-bridge-custom', __( 'Custom Markdown Override', 'agentic-markdown-bridge' ), array( __CLASS__, 'render_custom_meta_box' ), $type, 'normal', 'default', array( '__block_editor_compatible_meta_box' => true ) ); } } /** Editor side controls. */ public static function render_options_meta_box( $post ) { wp_nonce_field( 'agentic_mb_save_post_options', 'agentic_mb_post_options_nonce' ); $enabled = Agentic_Markdown_Bridge::get_post_enabled_mode( $post->ID ); $title = get_post_meta( $post->ID, Agentic_Markdown_Bridge::TITLE_MODE_META, true ); if ( ! in_array( $title, array( 'auto', 'include', 'exclude' ), true ) ) { $title = 'inherit'; } $pre_h1 = get_post_meta( $post->ID, Agentic_Markdown_Bridge::PRE_H1_MODE_META, true ); if ( ! in_array( $pre_h1, array( 'ignore', 'include' ), true ) ) { $pre_h1 = 'inherit'; } $frontend = Agentic_Markdown_Bridge::get_post_toggle_mode( $post->ID, Agentic_Markdown_Bridge::FRONTEND_MODE_META ); $copy = Agentic_Markdown_Bridge::get_post_toggle_mode( $post->ID, Agentic_Markdown_Bridge::COPY_MODE_META ); $llms = Agentic_Markdown_Bridge::get_post_toggle_mode( $post->ID, Agentic_Markdown_Bridge::LLMS_ACTION_MODE_META ); ?>


ID, Agentic_Markdown_Bridge::CUSTOM_MARKDOWN_META, true ); $custom_mode = get_post_meta( $post->ID, Agentic_Markdown_Bridge::CUSTOM_MODE_META, true ); $custom_mode = 'custom' === $custom_mode ? 'custom' : 'generated'; ?>


>

array_keys( Agentic_Markdown_Bridge::get_available_post_types() ), 'post_status' => 'publish', 'posts_per_page' => 30, 'paged' => $paged, 's' => $search, 'orderby' => 'modified', 'order' => 'DESC' ) ); ?>

posts as $post ) : $mode=Agentic_Markdown_Bridge::get_post_enabled_mode($post->ID); $title=get_post_meta($post->ID,Agentic_Markdown_Bridge::TITLE_MODE_META,true); $source='custom'===get_post_meta($post->ID,Agentic_Markdown_Bridge::CUSTOM_MODE_META,true)?__('Custom','agentic-markdown-bridge'):__('Generated','agentic-markdown-bridge'); ?>
post_type ); ?>ID,Agentic_Markdown_Bridge::VIEW_COUNT_META,true) ) ); ?>
'agentic-markdown-content','s'=>$search ), admin_url('tools.php') ); echo '
' . wp_kses_post( paginate_links( array( 'base'=>add_query_arg('paged','%#%',$base),'format'=>'','current'=>$paged,'total'=>max(1,(int)$query->max_num_pages) ) ) ) . '
'; ?>
$target ) { if ( '^index\\.md/?$' === $regex && false !== strpos( $target, Agentic_Markdown_Bridge::QUERY_VAR ) ) { $index_rule_ok = true; } if ( '^(.+?)/index\\.md/?$' === $regex && false !== strpos( $target, Agentic_Markdown_Bridge::QUERY_VAR ) ) { $path_rule_ok = true; } if ( '^llms\\.txt/?$' === $regex && false !== strpos( $target, Agentic_Markdown_Bridge::QUERY_LLMS_VAR ) ) { $llms_rule_ok = true; } if ( false !== strpos( $regex, 'index\\.md' ) && false === strpos( $target, Agentic_Markdown_Bridge::QUERY_VAR ) ) { ++$foreign_markdown_rules; } if ( false !== strpos( $regex, 'llms' ) && false === strpos( $target, Agentic_Markdown_Bridge::QUERY_LLMS_VAR ) ) { ++$foreign_llms_rules; } } $llms_content = Agentic_Markdown_Bridge::physical_llms_exists() ? Agentic_Markdown_Bridge::read_physical_llms_content() : $s['llms_content']; $markdown_conflict_message = sprintf( /* translators: %d is the number of competing Markdown rewrite rules detected. */ _n( '%d competing Markdown rewrite rule detected', '%d competing Markdown rewrite rules detected', $foreign_markdown_rules, 'agentic-markdown-bridge' ), $foreign_markdown_rules ); $llms_conflict_message = sprintf( /* translators: %d is the number of competing llms.txt rewrite rules detected. */ _n( '%d competing llms.txt rewrite rule detected', '%d competing llms.txt rewrite rules detected', $foreign_llms_rules, 'agentic-markdown-bridge' ), $foreign_llms_rules ); $has_llms_h1 = ! $llms_content || preg_match( '/^(?:\\xEF\\xBB\\xBF)?\\s*#\\s+\\S+/u', (string) $llms_content ); $checks = array( array( ! empty( $s['enabled'] ), __( 'Markdown output', 'agentic-markdown-bridge' ), ! empty( $s['enabled'] ) ? __( 'Enabled', 'agentic-markdown-bridge' ) : __( 'Disabled', 'agentic-markdown-bridge' ), ), array( class_exists( 'DOMDocument' ), __( 'PHP DOM extension', 'agentic-markdown-bridge' ), class_exists( 'DOMDocument' ) ? __( 'Available', 'agentic-markdown-bridge' ) : __( 'Missing — fallback text conversion will be used', 'agentic-markdown-bridge' ), ), array( $index_rule_ok && $path_rule_ok, __( 'Markdown rewrite rules', 'agentic-markdown-bridge' ), $index_rule_ok && $path_rule_ok ? __( 'Registered', 'agentic-markdown-bridge' ) : __( 'Missing — save Permalinks or reactivate the plugin', 'agentic-markdown-bridge' ), ), array( 0 === $foreign_markdown_rules, __( 'Markdown route conflicts', 'agentic-markdown-bridge' ), 0 === $foreign_markdown_rules ? __( 'No competing index.md rewrite rules detected', 'agentic-markdown-bridge' ) : $markdown_conflict_message, ), array( 0 === $foreign_llms_rules, __( 'llms.txt rewrite conflicts', 'agentic-markdown-bridge' ), 0 === $foreign_llms_rules ? __( 'No competing llms.txt rewrite rules detected', 'agentic-markdown-bridge' ) : $llms_conflict_message, ), array( $llms_rule_ok || Agentic_Markdown_Bridge::physical_llms_exists(), __( 'llms.txt route/source', 'agentic-markdown-bridge' ), Agentic_Markdown_Bridge::physical_llms_exists() ? __( 'Physical root file', 'agentic-markdown-bridge' ) : ( ! empty( $s['manage_llms'] ) ? __( 'Managed virtual route', 'agentic-markdown-bridge' ) : __( 'External/disabled', 'agentic-markdown-bridge' ) ), ), array( ! Agentic_Markdown_Bridge::physical_llms_is_symlink(), __( 'Physical llms.txt safety', 'agentic-markdown-bridge' ), Agentic_Markdown_Bridge::physical_llms_is_symlink() ? __( 'Symlink detected — editing blocked', 'agentic-markdown-bridge' ) : __( 'OK', 'agentic-markdown-bridge' ), ), array( (bool) $has_llms_h1, __( 'llms.txt H1', 'agentic-markdown-bridge' ), ! $llms_content ? __( 'No local content configured', 'agentic-markdown-bridge' ) : ( $has_llms_h1 ? __( 'Present', 'agentic-markdown-bridge' ) : __( 'Missing', 'agentic-markdown-bridge' ) ), ), ); ?>