>, limitation: string} */ public static function extract_post(WP_Post $post): array { if (!TheDotDevAgenticBridge_Content_Policy::is_eligible_post($post) || !function_exists('parse_blocks')) { return self::empty('not_applicable', 'Only eligible public content is inspected with WordPress block APIs.'); } $blocks = parse_blocks($post->post_content); if (!is_array($blocks)) { return self::empty('unknown', 'WordPress block parsing did not return a valid block tree.'); } return self::extract_blocks($post, $blocks); } /** @param list> $blocks @return array{source: string, status: string, candidates: list>, limitation: string} */ public static function extract_blocks(WP_Post $post, array $blocks): array { if (!TheDotDevAgenticBridge_Content_Policy::is_eligible_post($post)) { return self::empty('not_applicable', 'Only eligible public content is inspected.'); } $candidates = []; self::inspect_siblings($blocks, '', $post, $candidates); return [ 'source' => 'local_block_structure', 'status' => $candidates === [] ? 'not_applicable' : 'local_candidate', 'candidates' => $candidates, 'limitation' => 'Static block structure only; this does not judge answer quality or predict AI citation.', ]; } /** @param list> $blocks @param list> $out */ private static function inspect_siblings(array $blocks, string $prefix, WP_Post $post, array &$out): void { foreach ($blocks as $index => $block) { if (count($out) >= self::MAX_CANDIDATES || !is_array($block)) { return; } $path = $prefix === '' ? (string) $index : $prefix . '.' . $index; $name = (string) ($block['blockName'] ?? ''); if ($name === 'core/heading') { $answer = $blocks[$index + 1] ?? null; if (is_array($answer) && in_array((string) ($answer['blockName'] ?? ''), self::ANSWER_BLOCKS, true)) { $heading = self::text((string) ($block['innerHTML'] ?? '')); $answer_text = self::text((string) ($answer['innerHTML'] ?? '')); if ($heading !== '' && $answer_text !== '') { $out[] = [ 'postId' => (int) $post->ID, 'url' => esc_url_raw(get_permalink($post)), 'heading' => $heading, 'headingLevel' => max(1, min(6, (int) (($block['attrs']['level'] ?? 2)))), 'answerText' => $answer_text, 'answerType' => (string) $answer['blockName'], 'headingPath' => $path, 'answerPath' => $prefix === '' ? (string) ($index + 1) : $prefix . '.' . ($index + 1), 'status' => 'local_candidate', ]; } } } if (in_array($name, self::STATIC_CONTAINERS, true) && is_array($block['innerBlocks'] ?? null)) { self::inspect_siblings($block['innerBlocks'], $path, $post, $out); } } } private static function text(string $html): string { $text = sanitize_text_field(wp_strip_all_tags($html)); return function_exists('mb_substr') ? mb_substr($text, 0, self::MAX_TEXT_CHARS) : substr($text, 0, self::MAX_TEXT_CHARS); } /** @return array{source: string, status: string, candidates: list>, limitation: string} */ private static function empty(string $status, string $limitation): array { return ['source' => 'local_block_structure', 'status' => $status, 'candidates' => [], 'limitation' => $limitation]; } }