# Implementation Plan: Hybrid Block Refinement with @ Mention Support ## Overview Implement a hybrid refinement system that combines: 1. **Current workflow**: "AI Refine" button in block toolbar (beginner-friendly) 2. **New workflow**: `@block` mentions in chat (power-user friendly) This provides both discoverability for beginners and speed for experts. --- ## Current State Analysis ### Existing Refinement Flow - **Location**: [assets/js/block-refine.js](assets/js/block-refine.js) - **Trigger**: Click "AI Refine" in block toolbar - **Flow**: Modal opens → Type request → Submit → Block replaced - **Status**: ✅ Working correctly after recent fixes ### Chat System - **Location**: [assets/js/sidebar.js](assets/js/sidebar.js) - **Current behavior**: Article generation and chat messages - **Status**: ✅ Working with tabbed interface --- ## Implementation Plan ### Phase 1: Backend - Add Refinement Endpoint to Chat System **File**: [includes/class-gutenberg-sidebar.php](includes/class-gutenberg-sidebar.php) **Changes Needed:** 1. **Add new REST endpoint**: `/refine-from-chat` (or modify `/generate-plan` to handle refinement requests) 2. **Parse mentions from chat messages**: Extract `@block-ref` patterns 3. **Handle special mention syntax**: - `@this` → Currently selected block - `@previous` → Previous block - `@next` → Next block - `@all` → All blocks - `@paragraph-1`, `@heading-2` → Block by sequential ID **API Structure:** ```json { "topic": "Refine @this to be more engaging", "context": "User's full message with mentions", "postId": 123, "selectedBlockClientId": "abc123", "stream": true } ``` --- ### Phase 2: Frontend - Add @ Mention Detection **File**: [assets/js/sidebar.js](assets/js/sidebar.js) **Add to `sendMessage()` function:** 1. **Detect `@` mentions** in user input 2. **Extract mention patterns**: ```javascript const mentionRegex = /@(\w+(?:-\d+)?|this|previous|next|all)/g; ``` 3. **Check if message contains refinement keywords**: - "refine", "rewrite", "edit", "improve", "change", "make it" 4. **If both detected → Treat as refinement request** **New Message Handler:** ```javascript const handleRefinementRequest = async (message) => { // Extract mentions const mentions = message.match( /@(\w+(?:-\d+)?|this|previous|next|all)/g ); // Resolve to actual block client IDs const blocksToRefine = resolveMentionsToBlocks( mentions ); if ( blocksToRefine.length === 0 ) { // No valid mentions, treat as normal chat return false; } // Call refinement endpoint await refineBlocksFromChat( blocksToRefine, message ); return true; // Handled as refinement }; ``` --- ### Phase 3: Backend - Chat Refinement Endpoint **New Method**: `refine_from_chat()` in `class-gutenberg-sidebar.php` **Process:** 1. **Receive chat message with mentions** 2. **Resolve mentions to actual blocks** 3. **Call existing `stream_block_refine()` for each block** 4. **Stream responses back to chat** **Implementation:** ```php public function handle_refine_from_chat( $request ) { $params = $request->get_json_params(); $message = $params['topic'] ?? ''; $selected_block = $params['selectedBlockClientId'] ?? ''; $post_id = $params['postId'] ?? 0; // Parse mentions from message preg_match_all( '/@(\w+(?:-\d+)?|this|previous|next|all)/i', $message, $matches ); // Resolve mentions to block client IDs $blocks_to_refine = $this->resolve_mentions_to_blocks( $matches, $selected_block, $post_id ); if ( empty( $blocks_to_refine ) ) { // No blocks mentioned, treat as normal chat return $this->handle_generate_plan( $request ); } // Stream refinement for each mentioned block $this->stream_refinement_from_chat( $blocks_to_refine, $message, $post_id ); } ``` --- ### Phase 4: Block Resolution Logic **New Method**: `resolve_mentions_to_blocks()` in `class-gutenberg-sidebar.php` **Resolution Rules:** ```php private function resolve_mentions_to_blocks( $mentions, $selected_block, $post_id ) { $all_blocks = get_blocks( $post_id ); $resolved = array(); foreach ( $mentions as $mention ) { $mention_type = strtolower( $mention[1] ); switch ( $mention_type ) { case 'this': if ( $selected_block ) { $resolved[] = $selected_block; } break; case 'previous': if ( $selected_block ) { $index = array_search( $selected_block, $all_blocks ); if ( $index > 0 ) { $resolved[] = $all_blocks[ $index - 1 ]['clientId']; } } break; case 'next': if ( $selected_block ) { $index = array_search( $selected_block, $all_blocks ); if ( $index < count( $all_blocks ) - 1 ) { $resolved[] = $all_blocks[ $index + 1 ]['clientId']; } } break; case 'all': // Return all paragraph and heading blocks foreach ( $all_blocks as $block ) { if ( in_array( $block['name'], array( 'core/paragraph', 'core/heading' ) ) ) { $resolved[] = $block['clientId']; } } break; default: // Handle sequential mentions like "paragraph-1", "heading-2" if ( preg_match( '/^(\w+)-(\d+)$/', $mention_type, $matches ) ) { $block_type = $matches[1]; // "paragraph", "heading" $index = (int) $matches[2] - 1; // Convert to 0-based foreach ( $all_blocks as $block ) { if ( $block['name'] === 'core/' . $block_type ) ) { if ( $index === 0 ) { $resolved[] = $block['clientId']; $index--; } } } } break; } } return array_unique( $resolved ); } ``` --- ### Phase 5: Visual Feedback #### 5.1. Block Highlighting **File**: [assets/css/editor.css](assets/css/editor.css) **Add CSS for mentioned blocks:** ```css .wpaw-block-mentioned { outline: 2px solid #2271b1 !important; outline-offset: 2px; box-shadow: 0 0 0 4px rgba(34, 113, 177, 0.2); animation: wpaw-pulse 1.5s infinite; } @keyframes wpaw-pulse { 0%, 100% { box-shadow: 0 0 0 0px rgba(34, 113, 177, 0.2); } 50% { box-shadow: 0 0 0 6px rgba(34, 113, 177, 0.4); } } ``` #### 5.2. Autocomplete UI **File**: [assets/js/sidebar.js](assets/js/sidebar.js) **Add mention autocomplete:** ```javascript // When user types @ in chat input const showMentionAutocomplete = (searchTerm) => { const allBlocks = wp.data.select( 'core/block-editor' ).getBlocks(); const filtered = allBlocks .filter( b => b.name === 'core/paragraph' || b.name === 'core/heading' ) .map( ( b, index ) => ( { const label = b.attributes.content || b.name; const shortLabel = b.name === 'core/paragraph' ? `Paragraph ${index + 1}` : `Heading ${index + 1}: ${label}`; return { id: `${b.name}-${index}`, label: `${shortLabel}: "${label.substring( 0, 30 )}"`, clientId: b.clientId, }; } ) ); // Show dropdown with filtered options showAutocompleteDropdown( filtered ); }; ``` --- ### Phase 6: Chat Integration **Modify**: `sendMessage()` in [assets/js/sidebar.js](assets/js/sidebar.js) **Flow:** ```javascript const sendMessage = async () => { const userMessage = input.trim(); // Check if this is a refinement request const isRefinement = /refine|rewrite|edit|improve|change|make (it|them|this)/i.test( userMessage ); const hasMentions = /@/.test( userMessage ); if ( isRefinement && hasMentions ) { // Handle as refinement request await handleRefinementRequest( userMessage ); } else { // Handle as normal chat/generation // ...existing chat logic... } }; ``` --- ### Phase 7: Enhanced Chat Experience **Add refinement summary to chat:** When refinement is requested: 1. Add user message as chat bubble: "Refine @paragraph-3 to be more engaging" 2. Add AI response: "✓ I've refined paragraph 3" 3. Update block content in editor 4. Show cost in Cost tab **Example Chat Flow:** ``` User: Refine @this to be more concise [Message added to chat] AI: ✓ Refining current paragraph... [Status timeline showing progress] AI: ✅ Done! I've made the paragraph more concise while keeping the key information. [Block updated in editor] ``` --- ## Detailed Implementation ### Step 1: Backend Chat Refinement Endpoint **File**: [includes/class-gutenberg-sidebar.php](includes/class-gutenberg-sidebar.php) **Add new REST route:** ```php register_rest_route( 'wp-agentic-writer/v1', '/refine-from-chat', array( 'methods' => 'POST', 'callback' => array( $this, 'handle_refine_from_chat' ), 'permission_callback' => array( $this, 'check_edit_permission' ), ) ); ``` **Implementation:** - Handle chat messages with `@` mentions - Parse and resolve mentions to block client IDs - Stream refinement responses back to chat - Update blocks in real-time ### Step 2: Frontend Mention Detection **File**: [assets/js/sidebar.js](assets/js/sidebar.js) **Add to `sendMessage()` function:** ```javascript // Detect if message is refinement request with mentions const isRefinementWithMentions = /@(\w+(?:-\d+)?|this|previous|next|all)/i.test( userMessage ) && /refine|rewrite|edit|improve|change|make/i.test( userMessage ); if ( isRefinementWithMentions ) { // Handle as refinement await handleChatRefinement( userMessage, selectedBlockClientId ); return; } ``` ### Step 3: Block Mention Resolution **Create helper function** in [assets/js/sidebar.js](assets/js/sidebar.js): ```javascript const resolveBlockMentions = ( mentions, selectedBlockId ) => { const allBlocks = wp.data.select( 'core/block-editor' ).getBlocks(); const resolved = []; mentions.forEach( mention => { const type = mention.toLowerCase(); const match = mention.match( /^(\w+)-(\d+)$/ ); switch ( type ) { case 'this': if ( selectedBlockId ) resolved.push( selectedBlockId ); break; case 'previous': const selectedIndex = allBlocks.findIndex( b => b.clientId === selectedBlockId ); if ( selectedIndex > 0 ) { resolved.push( allBlocks[ selectedIndex - 1 ].clientId ); } break; case 'next': const selectedIndex = allBlocks.findIndex( b => b.clientId === selectedBlockId ); if ( selectedIndex < allBlocks.length - 1 ) { resolved.push( allBlocks[ selectedIndex + 1 ].clientId ); } break; case 'all': allBlocks.forEach( ( block, index ) => { if ( block.name === 'core/paragraph' || block.name === 'core/heading' ) { resolved.push( block.clientId ); } } ); break; default: // Handle "paragraph-1", "heading-2" format if ( match ) { const blockType = 'core/' + match[1]; // paragraph or heading const blockIndex = parseInt( match[2] ) - 1; // 1-based to 0-based let currentIndex = 0; allBlocks.forEach( ( block ) => { if ( block.name === blockType ) { if ( currentIndex === blockIndex ) { resolved.push( block.clientId ); } currentIndex++; } } ); } break; } } ); return [ ...new Set( resolved ) ]; // Remove duplicates }; ``` ### Step 4: Chat Refinement Handler **Create new function** in [assets/js/sidebar.js](assets/js/sidebar.js): ```javascript const handleChatRefinement = async ( message, selectedBlockId ) => { // Parse mentions from message const mentionRegex = /@(\w+(?:-\d+)?|this|previous|next|all)/gi; const mentions = [...message.matchAll( mentionRegex )].map( m => m[0] ); // Resolve to block client IDs const blocksToRefine = resolveBlockMentions( mentions, selectedBlockId ); if ( blocksToRefine.length === 0 ) { // No valid mentions found alert( 'No valid blocks found to refine. Try mentioning blocks like @this, @previous, or specific blocks like @paragraph-1' ); return; } // Add user message to chat setMessages( [ ...messages, { role: 'user', content: message } ] ); // Add timeline entry setMessages( prev => [ ...prev, { role: 'system', type: 'timeline', status: 'refining', message: `Refining ${blocksToRefine.length} block(s)...`, icon: '✏️', } ] ); setIsLoading( true ); try { // Call refinement endpoint const response = await fetch( wpAgenticWriter.apiUrl + '/refine-from-chat', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpAgenticWriter.nonce, }, body: JSON.stringify( { topic: message, context: message, selectedBlockClientId: selectedBlockId, blocksToRefine: blocksToRefine, postId: wp.data.select( 'core/editor' ).getCurrentPostId(), stream: true, } ), } ); if ( ! response.ok ) { throw new Error( 'Refinement failed' ); } // Handle streaming response const reader = response.body.getReader(); const decoder = new TextDecoder(); let refinedCount = 0; while ( true ) { const { done, value } = await reader.read(); if ( done ) break; const chunk = decoder.decode( value, { stream: true } ); const lines = chunk.split( '\n' ); for ( const line of lines ) { if ( line.startsWith( 'data: ' ) ) { try { const data = JSON.parse( line.slice( 6 ) ); if ( data.type === 'error' ) { throw new Error( data.message ); } else if ( data.type === 'block' ) { // Replace block in editor const newBlock = createBlockFromData( data.block ); const { replaceBlocks } = wp.data.dispatch( 'core/block-editor' ); data.block.blocks.forEach( ( blockData, idx ) => { if ( blockData.clientId ) { replaceBlocks( blockData.clientId, createBlockFromData( blockData ) ); } } ); refinedCount++; } else if ( data.type === 'complete' ) { // Show completion message setMessages( prev => [ ...prev, { role: 'assistant', content: `✅ Done! I've refined ${refinedCount} block(s) as requested.`, } ] ); setMessages( prev => { const newMessages = [ ...prev ]; const lastTimelineIndex = newMessages.findIndex( m => m.type === 'timeline' && m.status !== 'complete' ); if ( lastTimelineIndex !== -1 ) { newMessages[lastTimelineIndex] = { ...newMessages[lastTimelineIndex], status: 'complete', message: `Refined ${refinedCount} block(s) successfully`, icon: '✅', }; } return newMessages; } ); } } catch ( e ) { console.error( 'Failed to parse streaming data:', line, e ); } } } } } catch ( error ) { setMessages( prev => [ ...prev, { role: 'system', type: 'error', content: 'Error: ' + error.message, } ] ); } finally { setIsLoading( false ); } }; ``` ### Step 5: Visual Feedback **File**: [assets/css/sidebar.css](assets/css/sidebar.css) **Add block mention styles:** ```css /* Block mention styles */ .wpaw-block-mentioned { outline: 2px solid #2271b1 !important; outline-offset: 2px; box-shadow: 0 0 0 4px rgba(34, 113, 177, 0.2); animation: wpaw-pulse 1.5s infinite; transition: all 0.3s ease; } .wpaw-block-mentioned:hover { outline-width: 3px; box-shadow: 0 0 0 8px rgba(34, 113, 177, 0.3); } @keyframes wpaw-pulse { 0%, 100% { box-shadow: 0 0 0 0px rgba( 34, 113, 177, 0.2); } 50% { box-shadow: 0 0 0 6px rgba(34, 113, 177, 0.4); } } /* Mention autocomplete */ .wpaw-mention-autocomplete { position: absolute; background: white; border: 1px solid #ddd; border-radius: 4px; max-height: 200px; overflow-y: auto; z-index: 1000; box-shadow: 0 4px 6px rgba(0,0,0,0.1); } .wpaw-mention-option { padding: 8px 12px; cursor: pointer; border-bottom: 1px solid #eee; } .wpaw-mention-option:hover { background: #f0f0f0; } .wpaw-mention-option strong { display: block; color: #333; font-size: 13px; } .wpaw-mention-option span { display: block; color: #666; font-size: 12px; margin-top: 2px; } ``` --- ## User Experience ### Scenario 1: Beginner (Toolbar Button) **Flow:** 1. Click block to select it 2. Click "AI Refine" in toolbar 3. Type: "Make this more engaging" 4. Click "Refine" 5. Block is refined **Experience:** Clear, guided, discoverable ### Scenario 2: Power User (Chat Mention) **Flow:** 1. Type in chat: "Refine @this to be more engaging" 2. System highlights mentioned block 3. Press Enter or click Send 4. Chat shows: "✅ Done! I've refined the current block." 5. Block is refined immediately **Experience:** Fast, conversational, efficient ### Scenario 3: Multi-Block Refinement **Flow:** 1. Type: "Refine @paragraph-1, @paragraph-2, and @paragraph-3 to be more concise" 2. All three blocks get highlighted 3. Send message 4. All three blocks refined in sequence 5. Chat shows summary **Experience:** Powerful, batch operation --- ## File Modifications Summary ### New Files - None (all modifications to existing files) ### Modified Files 1. **[includes/class-gutenberg-sidebar.php](includes/class-gutenberg-sidebar.php)** - Add `handle_refine_from_chat()` method - Add `resolve_mentions_to_blocks()` method - Add `/refine-from-chat` REST route - Add `stream_refinement_from_chat()` method 2. **[assets/js/sidebar.js](assets/js/sidebar.js)** - Modify `sendMessage()` to detect refinement mentions - Add `resolveBlockMentions()` function - Add `handleChatRefinement()` function - Add mention detection and autocomplete UI 3. **[assets/css/sidebar.css](assets/css/sidebar.css)** - Add `.wpaw-block-mentioned` styles - Add `.wpaw-pulse` animation - Add mention autocomplete dropdown styles 4. **[assets/css/editor.css](assets/css/editor.css)** - Add block highlighting styles for mentioned blocks --- ## Technical Details ### Mention Syntax Reference | Syntax | Description | Example | |-------|-------------|---------| | `@this` | Current selected block | "Refine @this to be more engaging" | | `@previous` | Block before current | "Refine @previous to match tone" | | `@next` | Block after current | "Refine @next for consistency" | | `@all` | All blocks | "Refine @all to be more concise" | | `@paragraph-1` | 1st paragraph | "Refine @paragraph-1 to be more exciting" | | `@heading-2` | 2nd heading | "Refine @heading-2 to be more descriptive" | | `@list-1` | 1st list | "Refine @list-1 to add more items" | ### Block Type Aliases For user-friendly mentions: - `@paragraph` = `@paragraph-1` (current paragraph of type paragraph) - `@heading` = `@heading-1` (current heading of any level) - `@list` = `@list-1` (current list) --- ## Benefits ✅ **Hybrid approach** - Best of both worlds ✅ **Beginner-friendly** - Toolbar button remains discoverable ✅ **Power-user features** - `@` mentions for speed ✅ **Natural chat integration** - Refinement becomes conversational ✅ **Multi-block refinement** - Refine multiple blocks at once ✅ **Visual feedback** - Block highlighting shows what's being refined ✅ **Backward compatible** - Current workflow preserved --- ## Testing Checklist ### Basic Functionality: - [ ] Toolbar button still works - [ ] `@this` refines selected block - [ ] `@previous` refines previous block - [ ] `@next` refines next block - [ ] `@all` refines all blocks - [ ] `@paragraph-1` refines specific paragraph - [ ] `@heading-2` refines specific heading ### User Experience: - [ ] Mention autocomplete appears when typing `@` - ] Mentioned blocks get highlighted with pulse animation - [ ] Multiple blocks can be refined in one message - [ ] Chat shows refinement summary - [ ] Cost tracking includes refinement costs - [ ] Error messages when blocks not found ### Edge Cases: - [ ] Invalid block reference: `@paragraph-99` (out of range) - [ ] No blocks match: `@list-5` when only 2 lists exist - [ ] Refinement request without mention: "Make this more concise" (uses selected block) - [ ] Mixed: "Refine @paragraph-1 and @paragraph-2 to be more concise" - [ ] Multiple mentions of same block: "Refine @this @this" (only refines once) --- ## Rollback Plan If issues occur: 1. Remove mention detection from `sendMessage()` in sidebar.js 2. Remove new endpoints from class-gutenberg-sidebar.php 3. Remove mention CSS styles 4. Toolbar button continues to work as fallback **Git commands:** ```bash # Rollback frontend changes git checkout HEAD~1 assets/js/sidebar.js assets/css/sidebar.css # Rollback backend changes git checkout HEAD~1 includes/class-gutenberg-sidebar.php ``` --- ## Timeline Estimate - Phase 1 (Backend endpoint): 2-3 hours - Phase 2 (Frontend detection): 2-3 hours - Phase 3 (Block resolution): 1-2 hours - Phase 4 (Visual feedback): 1-2 hours - Phase 5 (Testing): 1-2 hours **Total: 7-12 hours** --- ## Success Criteria ✅ Toolbar button works as before (no regression) ✅ `@this` mentions refine currently selected block ✅ `@previous` and `@next` work relative to selection ✅ `@all` refines all content blocks ✅ `@paragraph-N` syntax works for specific blocks ✅ Chat shows refinement summaries ✅ Mentioned blocks get visual highlight ✅ Autocomplete suggests available blocks ✅ Multi-block refinement works correctly ✅ Cost tracking includes refinement costs ✅ Error handling for invalid mentions --- ## Future Enhancements ### Phase 2 Ideas: - **Smart suggestions**: "Refine all headings to be more descriptive" - **Batch operations**: "Refine all lists to be more concise" - **Quick refinement**: Click block → shows quick-refinement options in popover - **Refinement history**: Undo/redo refinement changes ### Advanced Features: - **Block type suggestions**: "Suggest making this a list" - **Style transfer**: "Make @paragraph-1 match the tone of @heading-2" - **Bulk refinement**: "Refine all code blocks to use simpler language" - **Refinement templates**: Predefined refinement options --- ## Open Questions 1. **Should `@paragraph-1` be 1-based or 0-based?** - I suggest **1-based** (more intuitive for non-technical users) 2. **Should we support content-based references?** - "Refine the block about SEO" (searches block content) - "Refine the third paragraph" (counts paragraphs automatically) 3. **Should mentions work during article generation?** - During initial article creation: "Refine @this section to add more examples" - Could interrupt plan generation flow 4. **Should we support block attributes?** - "Refine all blocks with 'team' to match company tone" - "Refine code blocks to use simpler variable names" --- ## Recommendation **Implement Phase 1-5 for MVP** with: - Core mention syntax (`@this`, `@previous`, `@next`, `@all`, `@type-N`) - Basic visual feedback (highlighting) - Toolbar button preservation (current workflow) - Chat integration with summaries **Defer Phase 2 features** (advanced usage patterns) to future iteration. --- Ready to implement? Let me know if you want to: 1. Proceed with implementation 2. Adjust the approach 3. Add/remove features 4. Explore specific aspects in more detail