Add AI writing assistant plugin with local backend, brave search, and image generation support
- Implement local backend AI provider with Ollama integration - Add Brave Search API integration for real-time search suggestions - Add image generation manager with multiple AI providers - Create hybrid provider system with local/cloud fallback - Add comprehensive settings UI with provider management - Implement Gutenberg sidebar with writing assistance controls - Add SEO schema generation for AI-generated content - Multiple provider support: OpenRouter, local backend, Codex
This commit is contained in:
@@ -23,9 +23,10 @@ class WP_Agentic_Writer_Markdown_Parser {
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @param string $markdown Markdown content.
|
||||
* @param array $image_placeholders Optional. Array of image placeholder data with agent_image_id.
|
||||
* @return array Array of Gutenberg blocks.
|
||||
*/
|
||||
public static function parse( $markdown ) {
|
||||
public static function parse( $markdown, $image_placeholders = array() ) {
|
||||
$markdown = self::normalize_markdown( $markdown );
|
||||
$blocks = array();
|
||||
$lines = explode( "\n", $markdown );
|
||||
@@ -39,6 +40,7 @@ class WP_Agentic_Writer_Markdown_Parser {
|
||||
$in_auto_code_block = false;
|
||||
$auto_code_lines = array();
|
||||
$auto_code_language = 'text';
|
||||
$image_index = 0;
|
||||
$is_code_like_line = function( $trimmed ) {
|
||||
if ( '' === $trimmed ) {
|
||||
return false;
|
||||
@@ -92,8 +94,15 @@ class WP_Agentic_Writer_Markdown_Parser {
|
||||
$in_list = false;
|
||||
}
|
||||
|
||||
// Create image placeholder block.
|
||||
$blocks[] = self::create_image_placeholder_block( $matches[1] );
|
||||
// Get agent_image_id from placeholders array if available
|
||||
$agent_image_id = null;
|
||||
if ( ! empty( $image_placeholders[ $image_index ] ) ) {
|
||||
$agent_image_id = $image_placeholders[ $image_index ]['agent_image_id'] ?? null;
|
||||
}
|
||||
|
||||
// Create image placeholder block with agent_image_id
|
||||
$blocks[] = self::create_image_placeholder_block( $matches[1], $agent_image_id );
|
||||
$image_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -258,6 +267,23 @@ class WP_Agentic_Writer_Markdown_Parser {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle numbered items with bold title (treat as paragraph, not list).
|
||||
if ( preg_match( '/^(\d+)\.\s+\*\*(.+?)\*\*\s*$/', $trimmed, $matches ) ) {
|
||||
if ( ! empty( $current_paragraph ) ) {
|
||||
$blocks[] = self::create_paragraph_block( $current_paragraph );
|
||||
$current_paragraph = '';
|
||||
}
|
||||
if ( $in_list ) {
|
||||
$blocks[] = self::create_list_block( $list_type, $list_items );
|
||||
$list_items = array();
|
||||
$in_list = false;
|
||||
}
|
||||
// Create paragraph with manual numbering and bold title.
|
||||
$content = $matches[1] . '. <strong>' . self::parse_inline_markdown( $matches[2] ) . '</strong>';
|
||||
$blocks[] = self::create_paragraph_block( $content );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle ordered lists.
|
||||
if ( preg_match( '/^\d+\.\s+(.+)$/', $trimmed, $matches ) ) {
|
||||
if ( ! empty( $current_paragraph ) ) {
|
||||
@@ -639,20 +665,35 @@ class WP_Agentic_Writer_Markdown_Parser {
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @param string $description Image description/alt text.
|
||||
* @param string $agent_image_id Optional. Agent-assigned image ID for tracking.
|
||||
* @return array Gutenberg block.
|
||||
*/
|
||||
private static function create_image_placeholder_block( $description ) {
|
||||
private static function create_image_placeholder_block( $description, $agent_image_id = null ) {
|
||||
$alt = trim( $description );
|
||||
|
||||
// Build className with agent image ID (WordPress preserves className reliably)
|
||||
$class_name = '';
|
||||
if ( ! empty( $agent_image_id ) ) {
|
||||
$class_name = 'wpaw-agent-img-' . esc_attr( $agent_image_id );
|
||||
}
|
||||
|
||||
$attrs = array(
|
||||
'id' => 0,
|
||||
'url' => '',
|
||||
'alt' => $alt,
|
||||
'alt' => '[Image: ' . $alt . ']', // Mark as placeholder
|
||||
'caption' => '',
|
||||
'sizeSlug' => 'large',
|
||||
'linkDestination' => 'none',
|
||||
);
|
||||
|
||||
$html = '<figure class="wp-block-image size-large"><img alt="' . esc_attr( $alt ) . '" /></figure>';
|
||||
// Add className and data attribute if agent_image_id provided
|
||||
if ( ! empty( $agent_image_id ) ) {
|
||||
$attrs['className'] = $class_name;
|
||||
$attrs['data-agent-image-id'] = $agent_image_id;
|
||||
}
|
||||
|
||||
$figure_class = 'wp-block-image size-large' . ( $class_name ? ' ' . $class_name : '' );
|
||||
$html = '<figure class="' . esc_attr( $figure_class ) . '"><img alt="[Image: ' . esc_attr( $alt ) . ']" data-agent-image-id="' . esc_attr( $agent_image_id ) . '" /></figure>';
|
||||
|
||||
return array(
|
||||
'blockName' => 'core/image',
|
||||
|
||||
Reference in New Issue
Block a user