chore: slim docs by removing retrace and planning artifacts
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,833 +0,0 @@
|
||||
# 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
|
||||
@@ -1,613 +0,0 @@
|
||||
# Implementation Plan: Enhanced Clarification Quiz System
|
||||
|
||||
## Overview
|
||||
Improve the clarification quiz to appear more frequently and gather comprehensive contextual information (target outcome, education level, marketing type, etc.) using predefined options users can select from.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Add Settings Configuration
|
||||
|
||||
### File: `includes/class-settings.php`
|
||||
|
||||
**Location:** Add new section after existing settings (around line 200+)
|
||||
|
||||
**New Settings to Add:**
|
||||
|
||||
```php
|
||||
/**
|
||||
* Clarification Quiz Settings Section
|
||||
*/
|
||||
public function add_clarification_quiz_settings() {
|
||||
add_settings_section(
|
||||
'wp_aw_clarification_quiz',
|
||||
__( 'Clarification Quiz', 'wp-agentic-writer' ),
|
||||
array( $this, 'clarification_quiz_section_callback' ),
|
||||
'wp-agentic-writer'
|
||||
);
|
||||
|
||||
// Enable/Disable Quiz
|
||||
add_settings_field(
|
||||
'enable_clarification_quiz',
|
||||
__( 'Enable Clarification Quiz', 'wp-agentic-writer' ),
|
||||
array( $this, 'render_checkbox' ),
|
||||
'wp-agentic-writer',
|
||||
'wp_aw_clarification_quiz',
|
||||
array(
|
||||
'label_for' => 'enable_clarification_quiz',
|
||||
'default' => true,
|
||||
'description' => __( 'Automatically ask clarifying questions when context is missing.', 'wp-agentic-writer' ),
|
||||
)
|
||||
);
|
||||
|
||||
// Confidence Threshold
|
||||
add_settings_field(
|
||||
'clarity_confidence_threshold',
|
||||
__( 'Confidence Threshold', 'wp-agentic-writer' ),
|
||||
array( $this, 'render_select' ),
|
||||
'wp-agentic-writer',
|
||||
'wp_aw_clarification_quiz',
|
||||
array(
|
||||
'label_for' => 'clarity_confidence_threshold',
|
||||
'default' => '0.6',
|
||||
'options' => array(
|
||||
'0.5' => __( 'Very Sensitive (50%) - Quiz appears frequently', 'wp-agentic-writer' ),
|
||||
'0.6' => __( 'Sensitive (60%) - Recommended', 'wp-agentic-writer' ),
|
||||
'0.7' => __( 'Balanced (70%)', 'wp-agentic-writer' ),
|
||||
'0.8' => __( 'Strict (80%) - Current default', 'wp-agentic-writer' ),
|
||||
'0.9' => __( 'Very Strict (90%) - Quiz rarely appears', 'wp-agentic-writer' ),
|
||||
),
|
||||
'description' => __( 'Lower threshold = quiz appears more often. Higher threshold = only very unclear requests trigger quiz.', 'wp-agentic-writer' ),
|
||||
)
|
||||
);
|
||||
|
||||
// Required Context Categories
|
||||
add_settings_field(
|
||||
'required_context_categories',
|
||||
__( 'Required Context', 'wp-agentic-writer' ),
|
||||
array( $this, 'render_multiselect' ),
|
||||
'wp-agentic-writer',
|
||||
'wp_aw_clarification_quiz',
|
||||
array(
|
||||
'label_for' => 'required_context_categories',
|
||||
'default' => array( 'target_outcome', 'target_audience', 'tone', 'content_depth', 'expertise_level', 'content_type', 'pov' ),
|
||||
'options' => array(
|
||||
'target_outcome' => __( 'Target Outcome (education/marketing/sales)', 'wp-agentic-writer' ),
|
||||
'target_audience' => __( 'Target Audience (who reads this)', 'wp-agentic-writer' ),
|
||||
'tone' => __( 'Tone of Voice (formal/casual/technical)', 'wp-agentic-writer' ),
|
||||
'content_depth' => __( 'Content Depth (overview/guide/analysis)', 'wp-agentic-writer' ),
|
||||
'expertise_level' => __( 'Expertise Level (beginner/intermediate/advanced)', 'wp-agentic-writer' ),
|
||||
'content_type' => __( 'Content Type (tutorial/opinion/how-to)', 'wp-agentic-writer' ),
|
||||
'pov' => __( 'Point of View (first/third person)', 'wp-agentic-writer' ),
|
||||
),
|
||||
'description' => __( 'Select which context categories must be clear before writing. Uncheck categories you don\'t need.', 'wp-agentic-writer' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_setting( 'wp-agentic-writer', 'enable_clarification_quiz' );
|
||||
register_setting( 'wp-agentic-writer', 'clarity_confidence_threshold' );
|
||||
register_setting( 'wp-agentic-writer', 'required_context_categories' );
|
||||
}
|
||||
```
|
||||
|
||||
**Helper Methods to Add:**
|
||||
|
||||
```php
|
||||
/**
|
||||
* Render multiselect field
|
||||
*/
|
||||
public function render_multiselect( $args ) {
|
||||
$name = $args['label_for'];
|
||||
$value = get_option( $name, $args['default'] );
|
||||
if ( ! is_array( $value ) ) {
|
||||
$value = $args['default'];
|
||||
}
|
||||
|
||||
echo '<select multiple name="' . esc_attr( $name ) . '[]" id="' . esc_attr( $name ) . '" class="regular-text">';
|
||||
foreach ( $args['options'] as $key => $label ) {
|
||||
$selected = in_array( $key, $value ) ? 'selected' : '';
|
||||
echo '<option value="' . esc_attr( $key ) . '" ' . $selected . '>' . esc_html( $label ) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
|
||||
if ( isset( $args['description'] ) ) {
|
||||
echo '<p class="description">' . wp_kses_post( $args['description'] ) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
public function clarification_quiz_section_callback() {
|
||||
echo '<p>' . __( 'Configure when and how the clarification quiz appears to gather context for better article generation.', 'wp-agentic-writer' ) . '</p>';
|
||||
}
|
||||
```
|
||||
|
||||
**Hook Integration:**
|
||||
Add to `__construct()` or settings initialization:
|
||||
```php
|
||||
add_action( 'admin_init', array( $this, 'add_clarification_quiz_settings' ), 20 );
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Update Clarity Check System Prompt
|
||||
|
||||
### File: `includes/class-gutenberg-sidebar.php`
|
||||
|
||||
**Location:** Lines 1195-1231 (check_clarity method)
|
||||
|
||||
**Replace the current system prompt with:**
|
||||
|
||||
```php
|
||||
$required_categories = get_option( 'required_context_categories', array(
|
||||
'target_outcome',
|
||||
'target_audience',
|
||||
'tone',
|
||||
'content_depth',
|
||||
'expertise_level',
|
||||
'content_type',
|
||||
'pov'
|
||||
) );
|
||||
|
||||
$threshold = get_option( 'clarity_confidence_threshold', '0.6' );
|
||||
$enabled = get_option( 'enable_clarification_quiz', true );
|
||||
|
||||
// If quiz is disabled, always return clear
|
||||
if ( ! $enabled ) {
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'result' => array(
|
||||
'is_clear' => true,
|
||||
'confidence' => 1.0,
|
||||
'questions' => array()
|
||||
),
|
||||
),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
$system_prompt = "You are an expert editor who determines if an article request has sufficient context to write effectively.
|
||||
|
||||
Evaluate the user's request and determine which context categories are clear:
|
||||
|
||||
CATEGORIES TO EVALUATE:
|
||||
1. target_outcome - What should this content achieve? (education/marketing/sales/entertainment/brand_awareness)
|
||||
2. target_audience - Who is reading this? (demographics, role, knowledge level)
|
||||
3. tone - How should we sound? (formal/casual/technical/friendly/professional/conversational)
|
||||
4. content_depth - How comprehensive? (quick_overview/standard_guide/detailed_analysis/comprehensive)
|
||||
5. expertise_level - Reader's knowledge? (beginner/intermediate/advanced/expert)
|
||||
6. content_type - What format? (tutorial/how_to/opinion/comparison/listicle/case_study/news_analysis)
|
||||
7. pov - Whose perspective? (first_person/third_person/expert_voice/neutral)
|
||||
|
||||
For each MISSING category, generate a clarifying question using PREDEFINED OPTIONS.
|
||||
Use 'single_choice' or 'multiple_choice' types - NEVER 'open_text'.
|
||||
|
||||
QUESTION STRUCTURE:
|
||||
{
|
||||
'id': 'q1',
|
||||
'category': 'target_outcome',
|
||||
'question': 'What is the primary goal of this content?',
|
||||
'type': 'single_choice',
|
||||
'options': [
|
||||
{ 'value': 'Education - Teach something new', 'default': true },
|
||||
{ 'value': 'Marketing - Promote a product/service', 'default': false },
|
||||
{ 'value': 'Sales - Drive conversions/signups', 'default': false },
|
||||
{ 'value': 'Entertainment - Engage and entertain', 'default': false },
|
||||
{ 'value': 'Brand Awareness - Build authority/trust', 'default': false }
|
||||
]
|
||||
}
|
||||
|
||||
CONFIDENCE CALCULATION:
|
||||
- Start at 100% (1.0)
|
||||
- Subtract 15% for each missing required category
|
||||
- If confidence < {$threshold}, generate questions for ALL missing categories
|
||||
|
||||
Return ONLY valid JSON with this structure:
|
||||
{
|
||||
'is_clear': true/false,
|
||||
'confidence': 0.0-1.0,
|
||||
'missing_categories': ['category1', 'category2'],
|
||||
'questions': [ ... ]
|
||||
}
|
||||
|
||||
No markdown, no explanation - just JSON.";
|
||||
|
||||
$messages = array(
|
||||
array(
|
||||
'role' => 'system',
|
||||
'content' => $system_prompt,
|
||||
),
|
||||
array(
|
||||
'role' => 'user',
|
||||
'content' => "Topic: {$topic}\n\nRequired Categories: " . implode( ', ', $required_categories ) . "\n\nEvaluate this request and determine which context is missing.",
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Improve Fallback Behavior
|
||||
|
||||
### File: `includes/class-gutenberg-sidebar.php`
|
||||
|
||||
**Location:** Around lines 1603-1615
|
||||
|
||||
**Add new helper method:**
|
||||
|
||||
```php
|
||||
/**
|
||||
* Get default clarification questions when AI fails
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @param string $topic User's topic.
|
||||
* @return array Clarification result with default questions.
|
||||
*/
|
||||
private function get_default_clarification_questions( $topic ) {
|
||||
$required_categories = get_option( 'required_context_categories', array(
|
||||
'target_outcome',
|
||||
'target_audience',
|
||||
'tone',
|
||||
'content_depth',
|
||||
'expertise_level',
|
||||
'content_type',
|
||||
'pov'
|
||||
) );
|
||||
|
||||
$questions = array();
|
||||
$question_id = 1;
|
||||
|
||||
$question_templates = array(
|
||||
'target_outcome' => array(
|
||||
'category' => 'target_outcome',
|
||||
'question' => 'What is the primary goal of this content?',
|
||||
'type' => 'single_choice',
|
||||
'options' => array(
|
||||
array( 'value' => 'Education - Teach something new', 'default' => true ),
|
||||
array( 'value' => 'Marketing - Promote a product/service', 'default' => false ),
|
||||
array( 'value' => 'Sales - Drive conversions', 'default' => false ),
|
||||
array( 'value' => 'Entertainment - Engage readers', 'default' => false ),
|
||||
array( 'value' => 'Brand Awareness - Build authority', 'default' => false ),
|
||||
)
|
||||
),
|
||||
'target_audience' => array(
|
||||
'category' => 'target_audience',
|
||||
'question' => 'Who is the primary audience for this content?',
|
||||
'type' => 'single_choice',
|
||||
'options' => array(
|
||||
array( 'value' => 'General public / Beginners', 'default' => true ),
|
||||
array( 'value' => 'Professionals in the field', 'default' => false ),
|
||||
array( 'value' => 'Potential customers', 'default' => false ),
|
||||
array( 'value' => 'Existing customers/users', 'default' => false ),
|
||||
array( 'value' => 'Industry peers / Experts', 'default' => false ),
|
||||
)
|
||||
),
|
||||
'tone' => array(
|
||||
'category' => 'tone',
|
||||
'question' => 'What tone should this content have?',
|
||||
'type' => 'single_choice',
|
||||
'options' => array(
|
||||
array( 'value' => 'Professional & Authoritative', 'default' => true ),
|
||||
array( 'value' => 'Friendly & Conversational', 'default' => false ),
|
||||
array( 'value' => 'Technical & Detailed', 'default' => false ),
|
||||
array( 'value' => 'Casual & Entertaining', 'default' => false ),
|
||||
array( 'value' => 'Formal & Academic', 'default' => false ),
|
||||
)
|
||||
),
|
||||
'content_depth' => array(
|
||||
'category' => 'content_depth',
|
||||
'question' => 'How comprehensive should this content be?',
|
||||
'type' => 'single_choice',
|
||||
'options' => array(
|
||||
array( 'value' => 'Quick overview (500-800 words)', 'default' => false ),
|
||||
array( 'value' => 'Standard guide (800-1500 words)', 'default' => true ),
|
||||
array( 'value' => 'Detailed analysis (1500-2500 words)', 'default' => false ),
|
||||
array( 'value' => 'Comprehensive deep-dive (2500+ words)', 'default' => false ),
|
||||
)
|
||||
),
|
||||
'expertise_level' => array(
|
||||
'category' => 'expertise_level',
|
||||
'question' => 'What is the target audience\'s expertise level?',
|
||||
'type' => 'single_choice',
|
||||
'options' => array(
|
||||
array( 'value' => 'Beginner - No prior knowledge', 'default' => true ),
|
||||
array( 'value' => 'Intermediate - Basic understanding', 'default' => false ),
|
||||
array( 'value' => 'Advanced - Deep technical knowledge', 'default' => false ),
|
||||
array( 'value' => 'Expert - Industry professional', 'default' => false ),
|
||||
)
|
||||
),
|
||||
'content_type' => array(
|
||||
'category' => 'content_type',
|
||||
'question' => 'What type of content works best for this topic?',
|
||||
'type' => 'single_choice',
|
||||
'options' => array(
|
||||
array( 'value' => 'Tutorial / How-to guide', 'default' => true ),
|
||||
array( 'value' => 'Opinion / Commentary', 'default' => false ),
|
||||
array( 'value' => 'Comparison / Review', 'default' => false ),
|
||||
array( 'value' => 'Listicle / Tips', 'default' => false ),
|
||||
array( 'value' => 'Case study', 'default' => false ),
|
||||
array( 'value' => 'News analysis', 'default' => false ),
|
||||
)
|
||||
),
|
||||
'pov' => array(
|
||||
'category' => 'pov',
|
||||
'question' => 'From what perspective should this be written?',
|
||||
'type' => 'single_choice',
|
||||
'options' => array(
|
||||
array( 'value' => 'Third person (objective, "it", "they")', 'default' => true ),
|
||||
array( 'value' => 'First person (personal, "I", "my")', 'default' => false ),
|
||||
array( 'value' => 'Expert voice (authoritative, experienced)', 'default' => false ),
|
||||
array( 'value' => 'Neutral / Unbiased', 'default' => false ),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
foreach ( $required_categories as $category ) {
|
||||
if ( isset( $question_templates[ $category ] ) ) {
|
||||
$q = $question_templates[ $category ];
|
||||
$q['id'] = 'q' . $question_id++;
|
||||
$questions[] = $q;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'is_clear' => false,
|
||||
'confidence' => 0.0,
|
||||
'missing_categories' => $required_categories,
|
||||
'questions' => $questions
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Update error handling (lines 1603-1615):**
|
||||
|
||||
```php
|
||||
if ( is_wp_error( $response ) ) {
|
||||
// Log error
|
||||
error_log( 'WP Agentic Writer: Clarity check API error - ' . $response->get_error_message() );
|
||||
// Use default questions instead of skipping
|
||||
return $this->get_default_clarification_questions( $topic );
|
||||
}
|
||||
|
||||
if ( null === $result ) {
|
||||
// Log parse error
|
||||
error_log( 'WP Agentic Writer: Failed to parse clarity check JSON' );
|
||||
// Use default questions instead of skipping
|
||||
return $this->get_default_clarification_questions( $topic );
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Update Frontend Quiz Display
|
||||
|
||||
### File: `assets/js/sidebar.js`
|
||||
|
||||
**Enhancement 1: Add category labels to questions**
|
||||
|
||||
Find the question rendering code and add category display:
|
||||
|
||||
```javascript
|
||||
// Inside renderClarificationQuestion function
|
||||
const categoryLabels = {
|
||||
'target_outcome': '🎯 Target Outcome',
|
||||
'target_audience': '👥 Target Audience',
|
||||
'tone': '🎨 Tone of Voice',
|
||||
'content_depth': '📏 Content Depth',
|
||||
'expertise_level': '📊 Expertise Level',
|
||||
'content_type': '📝 Content Type',
|
||||
'pov': '👁️ Point of View'
|
||||
};
|
||||
|
||||
// Add category badge above question
|
||||
if (question.category && categoryLabels[question.category]) {
|
||||
html += '<div class="clarification-category-badge">';
|
||||
html += categoryLabels[question.category];
|
||||
html += '</div>';
|
||||
}
|
||||
```
|
||||
|
||||
**Enhancement 2: Show which categories are clear**
|
||||
|
||||
Update progress display to show collected vs missing context:
|
||||
|
||||
```javascript
|
||||
// Update progress display
|
||||
function updateClarificationProgress(total, current, clearCategories = []) {
|
||||
const progressEl = document.querySelector('.clarification-progress');
|
||||
if (!progressEl) return;
|
||||
|
||||
let html = `<div class="progress-info">`;
|
||||
html += `<span>Question ${current} of ${total}</span>`;
|
||||
|
||||
// Show what we already know
|
||||
if (clearCategories.length > 0) {
|
||||
html += `<div class="clear-context">`;
|
||||
html += `<strong>Already clear:</strong> `;
|
||||
html += clearCategories.map(cat => {
|
||||
const labels = {
|
||||
'target_outcome': 'Goal',
|
||||
'target_audience': 'Audience',
|
||||
'tone': 'Tone',
|
||||
'content_depth': 'Depth',
|
||||
'expertise_level': 'Level',
|
||||
'content_type': 'Format',
|
||||
'pov': 'Perspective'
|
||||
};
|
||||
return labels[cat] || cat;
|
||||
}).join(', ');
|
||||
html += `</div>`;
|
||||
}
|
||||
|
||||
html += `</div>`;
|
||||
html += `<div class="progress-bar"><div class="progress-fill" style="width: ${(current/total)*100}%"></div></div>`;
|
||||
|
||||
progressEl.innerHTML = html;
|
||||
}
|
||||
```
|
||||
|
||||
**Enhancement 3: Add "Skip this question" option**
|
||||
|
||||
```javascript
|
||||
// Add skip button to question card
|
||||
html += `<button class="skip-question-btn" data-question-id="${question.id}">`;
|
||||
html += `Skip (not applicable)`;
|
||||
html += `</button>`;
|
||||
|
||||
// Handle skip
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('skip-question-btn')) {
|
||||
const questionId = e.target.dataset.questionId;
|
||||
skipQuestion(questionId);
|
||||
}
|
||||
});
|
||||
|
||||
function skipQuestion(questionId) {
|
||||
// Mark as skipped with "Not applicable" value
|
||||
clarificationAnswers[questionId] = {
|
||||
skipped: true,
|
||||
value: 'Not applicable'
|
||||
};
|
||||
nextClarificationQuestion();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Pass Clarification Context to Plan Generation
|
||||
|
||||
### File: `includes/class-gutenberg-sidebar.php`
|
||||
|
||||
**Location:** Find `generate_plan` method (around line 900+)
|
||||
|
||||
**Add clarification context integration:**
|
||||
|
||||
```php
|
||||
// Before generating the plan, check if we have clarification answers
|
||||
$clarity_context = '';
|
||||
if ( ! empty( $params['clarificationAnswers'] ) && is_array( $params['clarificationAnswers'] ) ) {
|
||||
$clarity_context = "\n\n=== CONTEXT FROM CLARIFICATION QUIZ ===\n";
|
||||
|
||||
// Group by category
|
||||
$grouped = array();
|
||||
foreach ( $params['clarificationAnswers'] as $answer ) {
|
||||
$category = $answer['category'] ?? 'other';
|
||||
$value = $answer['value'] ?? $answer['answer'] ?? '';
|
||||
$skipped = $answer['skipped'] ?? false;
|
||||
|
||||
if ( ! $skipped && ! empty( $value ) ) {
|
||||
$grouped[ $category ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Format for prompt
|
||||
$category_labels = array(
|
||||
'target_outcome' => 'Primary Goal',
|
||||
'target_audience' => 'Target Audience',
|
||||
'tone' => 'Tone of Voice',
|
||||
'content_depth' => 'Content Depth',
|
||||
'expertise_level' => 'Expertise Level',
|
||||
'content_type' => 'Content Type',
|
||||
'pov' => 'Point of View',
|
||||
);
|
||||
|
||||
foreach ( $grouped as $category => $value ) {
|
||||
$label = $category_labels[ $category ] ?? ucwords( str_replace( '_', ' ', $category ) );
|
||||
$clarity_context .= "- {$label}: {$value}\n";
|
||||
}
|
||||
|
||||
$clarity_context .= "=== END CONTEXT ===\n";
|
||||
}
|
||||
|
||||
// Add to planning prompt
|
||||
$plan_prompt = $clarity_context . "\n" . $plan_prompt;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Testing Checklist
|
||||
|
||||
### Manual Testing Steps:
|
||||
|
||||
1. **Settings Page Test:**
|
||||
- Go to WP Agentic Writer settings
|
||||
- Verify new "Clarification Quiz" section appears
|
||||
- Test enable/disable toggle
|
||||
- Change confidence threshold to different values
|
||||
- Select/deselect required context categories
|
||||
- Save settings and verify they persist
|
||||
|
||||
2. **Vague Topic Test:**
|
||||
- Enter very vague topic: "write about AI"
|
||||
- Verify quiz appears with all missing categories
|
||||
- Verify all questions have predefined options (no text inputs)
|
||||
- Answer all questions and verify plan reflects choices
|
||||
|
||||
3. **Specific Topic Test:**
|
||||
- Enter detailed topic with all context
|
||||
- Verify quiz doesn't appear (or only asks for truly missing info)
|
||||
- Plan generation proceeds immediately
|
||||
|
||||
4. **Threshold Test:**
|
||||
- Set threshold to 0.5 (very sensitive)
|
||||
- Enter semi-clear topic
|
||||
- Verify quiz appears
|
||||
- Set threshold to 0.9 (very strict)
|
||||
- Enter same topic
|
||||
- Verify quiz doesn't appear
|
||||
|
||||
5. **Category Filter Test:**
|
||||
- Uncheck some categories in settings
|
||||
- Enter vague topic
|
||||
- Verify quiz only asks for checked categories
|
||||
- Unchecked categories are skipped
|
||||
|
||||
6. **API Failure Test:**
|
||||
- Temporarily break API connection (invalid key)
|
||||
- Enter vague topic
|
||||
- Verify fallback questions appear
|
||||
- All categories show default predefined options
|
||||
|
||||
7. **Skip Question Test:**
|
||||
- Start quiz
|
||||
- Click "Skip" on a question
|
||||
- Verify it moves to next question
|
||||
- Verify skipped answer marked as "Not applicable"
|
||||
|
||||
8. **Plan Integration Test:**
|
||||
- Complete full quiz with specific answers
|
||||
- Generate plan
|
||||
- Verify plan reflects quiz answers (tone, audience, goal, etc.)
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
✅ Settings page has Clarification Quiz section with all 3 fields
|
||||
✅ Quiz triggers more frequently with 0.6 threshold (vs 0.8)
|
||||
✅ All quiz questions use predefined options (no open_text type)
|
||||
✅ Fallback questions appear when AI fails
|
||||
✅ Clarification answers appear in generated plan
|
||||
✅ Users can configure which categories are required
|
||||
✅ Users can enable/disable quiz entirely
|
||||
✅ Frontend shows category labels and progress
|
||||
✅ Can skip individual questions
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If issues occur:
|
||||
1. Revert `class-settings.php` changes (remove new settings)
|
||||
2. Revert system prompt to original (simple 3-criteria check)
|
||||
3. Revert threshold to hardcoded 0.8
|
||||
4. Revert fallback to original "assume clear" behavior
|
||||
|
||||
Keep Git commits organized by phase for easy partial rollback.
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements (Out of Scope)
|
||||
|
||||
- Add custom question types via filters
|
||||
- Save quiz answers per user/site for faster future generations
|
||||
- A/B test different thresholds
|
||||
- Analytics on which categories are most often missing
|
||||
- Import/export context presets
|
||||
- Multi-language support for question options
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user