or
 tag - if so, return it unchanged.
	// We need to check the raw post content before shortcodes are processed.
	if ( $post instanceof WP_Post ) {
		$raw_content = $post->post_content;

		// Build a pattern to match the shortcode with any attributes.
		$shortcode_name    = RWPAI_SHORTCODE;
		$shortcode_pattern = '/\[' . preg_quote( $shortcode_name, '/' ) . '(\s+[^\]]*)?\]/';

		// Check if this shortcode pattern appears inside  or 
 tags.
		// Match code/pre tags and their content.
		if ( preg_match_all( '/<(code|pre)[^>]*>(.*?)<\/\1>/is', $raw_content, $code_matches ) ) {
			foreach ( $code_matches[2] as $code_content ) {
				// Check if our shortcode pattern appears in this code block.
				if ( preg_match( $shortcode_pattern, $code_content ) ) {
					// Shortcode is inside a code tag, return it as-is.
					// Rebuild the shortcode text from attributes.
					$attr_string = '';
					if ( ! empty( $atts ) && is_array( $atts ) ) {
						foreach ( $atts as $key => $value ) {
							if ( null !== $value && '' !== $value ) {
								$attr_string .= ' ' . $key . '="' . esc_attr( $value ) . '"';
							}
						}
					}
					return '[' . $shortcode_name . $attr_string . ']';
				}
			}
		}
	}

	// Read more about shortcode attributes: https://wordpress.org/plugins/natural-text-to-speech.
	$default_attribute = array(
		'description' => null,
	);

	$atts = shortcode_atts( $default_attribute, $atts );

	// Category exclusion logic.
	$post_categories = array();

	if ( $post instanceof WP_Post ) {
		$post_categories = wp_get_post_terms(
			$post->ID,
			'category',
			array( 'fields' => 'slugs' )
		);
	}

	// Build HTML attributes.
	$attributes = '';
	foreach ( $default_attribute as $key => $default ) {
		if ( ! empty( $atts[ $key ] ) ) {
			$data_key    = esc_attr( "data-$key" );
			$data_val    = esc_attr( $atts[ $key ] );
			$attributes .= " {$data_key}=\"{$data_val}\"";
		}
	}

	if ( $content ) {
		$config = json_decode( rwpai_normalize_json_string_for_decode( $content ), true );

		if ( json_last_error() !== JSON_ERROR_NONE ) {
			return '
' . esc_html__( 'Invalid JSON in shortcode', 'ai-agent' ) . '
'; } } else { $config = rwpai_get_plugin_config(); } if ( $config ) { if ( isset( $config['exclude_from_post_categories'] ) ) { $excluded = (array) $config['exclude_from_post_categories']; // If any category matches, don't render the shortcode. if ( array_intersect( $excluded, $post_categories ) ) { return ''; // skip rendering. } } } if ( ! $content ) { return sprintf( '
๐Ÿ”Š
', $attributes ); } $encoded = esc_attr( wp_json_encode( $config ) ); return sprintf( '
๐Ÿ”Š
', $attributes, $encoded ); } add_shortcode( RWPAI_SHORTCODE, 'rwpai_plugin_shortcode' ); /** * Normalize a "dirty" JSON string so it uses straight quotes and has no trailing commas. * * @param string $json Raw JSON input, possibly containing HTML entities or smart quotes. * @return string Normalized JSON string safe for json_decode(). */ function rwpai_normalize_json_string_for_decode( string $json ): string { if ( '' === $json ) { return $json; } $json = str_replace( array( '

', '

', '
', '
' ), '', $json ); // 1) Remove UTF-8 BOM if present. if ( "\xEF\xBB\xBF" === substr( $json, 0, 3 ) ) { $json = substr( $json, 3 ); } // 2) Decode HTML entities (e.g. “, “, etc.). $json = html_entity_decode( $json, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); // 3) Replace common smart/directional quotes and apostrophes (UTF-8 byte sequences). $search = array( // double quotes variants. "\xE2\x80\x9C", // U+201C โ€œ . "\xE2\x80\x9D", // U+201D โ€ . "\xE2\x80\x9E", // U+201E โ€ž . "\xE2\x80\x9F", // U+201F โ€Ÿ . // single quotes / apostrophes. "\xE2\x80\x98", // U+2018 โ€˜ . "\xE2\x80\x99", // U+2019 โ€™ . "\xE2\x80\xB9", // U+2039 โ€น . "\xE2\x80\xBA", // U+203A โ€บ . // left/right double angle quotes (often used in languages). "\xC2\xAB", // U+00AB ยซ (in UTF-8) . "\xC2\xBB", // U+00BB ยป . ); $replace = array( '"', '"', '"', '"', "'", "'", "'", "'", '"', '"', ); $json = str_replace( $search, $replace, $json ); // 5) Remove trailing commas before } or ] which break JSON. $json = preg_replace( '/,\s*(?=[}\]])/u', '', $json ); // 6) Trim whitespace. $json = trim( $json ); return $json; }