// Extract JSON from text with improved error handling
function wcsb_extract_json_from_text($text) {
// First, try to parse the entire response as JSON
$json_data = json_decode($text, true);
if (json_last_error() === JSON_ERROR_NONE) {
return $json_data;
}
// If that fails, try to extract JSON using regex
if (preg_match('/\{(?:[^{}]|(?R))*\}/s', $text, $matches)) {
$json_string = $matches[0];
// Try to fix common JSON issues
$fixed_json = wcsb_fix_json_string($json_string);
$json_data = json_decode($fixed_json, true);
if (json_last_error() === JSON_ERROR_NONE) {
return $json_data;
}
}
// If we have a JSON parsing error, try to extract and fix the structure manually
if (strpos($text, '"title"') !== false && strpos($text, '"pages"') !== false) {
try {
$manual_json = wcsb_manually_extract_json($text);
return $manual_json;
} catch (Exception $e) {
error_log('Manual JSON extraction failed: ' . $e->getMessage());
}
}
// If all else fails, create a basic structure with the text as HTML
$fallback_data = array(
'title' => 'Generated Website',
'pages' => array(
array(
'name' => 'Home',
'slug' => 'home',
'html' => '
JSON Parsing Error
The AI generated content that couldn\'t be properly formatted. Here\'s the raw response:
' . esc_html(substr($text, 0, 1000)) . '...
',
'css' => '.wcsb-error-container { padding: 20px; border: 1px solid #ddd; background: #f9f9f9; }
.wcsb-error-container pre { overflow: auto; background: #f0f0f0; padding: 10px; }'
)
),
'globalCss' => '',
'globalJs' => '',
'plugins' => array(),
'theme' => 'default'
);
// Log the error for debugging
error_log('Failed to parse JSON from Claude response: ' . json_last_error_msg());
error_log('Raw response: ' . substr($text, 0, 500) . '...');
return $fallback_data;
}
// Function to fix common JSON string issues
function wcsb_fix_json_string($json_string) {
// Replace newlines in string values
$json_string = preg_replace('/"\s*\n\s*([^"]+)\s*\n\s*"/s', '" $1 "', $json_string);
// Fix escaped quotes
$json_string = str_replace('\\"', '"', $json_string);
// Fix unescaped newlines in strings
$json_string = preg_replace_callback('/"([^"]*)"/', function($matches) {
return '"' . str_replace("\n", "\\n", $matches[1]) . '"';
}, $json_string);
return $json_string;
}
// Function to manually extract JSON structure
function wcsb_manually_extract_json($text) {
$result = array();
// Extract title
if (preg_match('/"title":\s*"([^"]+)"/', $text, $matches)) {
$result['title'] = $matches[1];
} else {
$result['title'] = 'Generated Website';
}
// Extract pages
$result['pages'] = array();
// Extract HTML content
if (preg_match('/"html":\s*"(.*?)(?:"|",\s*"css")/', $text, $matches)) {
$html = $matches[1];
// Clean up the HTML
$html = str_replace('\n', "\n", $html);
$html = str_replace('\"', '"', $html);
} else {
$html = '
No content available
';
}
// Extract CSS content
if (preg_match('/"css":\s*"(.*?)(?:"|",\s*"js")/', $text, $matches)) {
$css = $matches[1];
// Clean up the CSS
$css = str_replace('\n', "\n", $css);
$css = str_replace('\"', '"', $css);
} else {
$css = '';
}
// Extract JS content
if (preg_match('/"js":\s*"(.*?)(?:"|"\s*})/', $text, $matches)) {
$js = $matches[1];
// Clean up the JS
$js = str_replace('\n', "\n", $js);
$js = str_replace('\"', '"', $js);
} else {
$js = '';
}
$result['pages'][] = array(
'name' => 'Home',
'slug' => 'home',
'html' => $html,
'css' => $css,
'js' => $js
);
// Extract global CSS
if (preg_match('/"globalCss":\s*"(.*?)(?:"|",\s*"globalJs")/', $text, $matches)) {
$globalCss = $matches[1];
// Clean up the global CSS
$globalCss = str_replace('\n', "\n", $globalCss);
$globalCss = str_replace('\"', '"', $globalCss);
$result['globalCss'] = $globalCss;
} else {
$result['globalCss'] = '';
}
// Extract global JS
if (preg_match('/"globalJs":\s*"(.*?)(?:"|",\s*"plugins")/', $text, $matches)) {
$globalJs = $matches[1];
// Clean up the global JS
$globalJs = str_replace('\n', "\n", $globalJs);
$globalJs = str_replace('\"', '"', $globalJs);
$result['globalJs'] = $globalJs;
} else {
$result['globalJs'] = '';
}
// Extract plugins
if (preg_match('/"plugins":\s*\[(.*?)\]/', $text, $matches)) {
$plugins_str = $matches[1];
$plugins = array();
preg_match_all('/"([^"]+)"/', $plugins_str, $plugin_matches);
if (!empty($plugin_matches[1])) {
$plugins = $plugin_matches[1];
}
$result['plugins'] = $plugins;
} else {
$result['plugins'] = array();
}
// Extract theme
if (preg_match('/"theme":\s*"([^"]+)"/', $text, $matches)) {
$result['theme'] = $matches[1];
} else {
$result['theme'] = 'custom';
}
return $result;
}
// Call Claude API directly with improved JSON handling
function wcsb_call_claude_api_directly($prompt, $api_key, $max_tokens = 4000, $system = '') {
// Claude API endpoint
$api_url = 'https://api.anthropic.com/v1/messages';
// Get selected model
$model = get_option('wcsb_claude_model', 'claude-3-5-sonnet-20240620');
// Add specific instructions for JSON formatting
$json_instructions = "\n\nIMPORTANT: Your response must be a valid JSON object. Make sure to properly escape all quotes and special characters in HTML, CSS, and JavaScript strings.";
// Prepare request body
$request_body = array(
'model' => $model,
'max_tokens' => $max_tokens,
'temperature' => 0.5, // Lower temperature for more reliable JSON generation
'messages' => array(
array(
'role' => 'user',
'content' => $prompt . $json_instructions
)
)
);
// Add system prompt if provided
if (!empty($system)) {
$request_body['system'] = $system . "\n\nYour response MUST be valid JSON with properly escaped strings.";
}
// Make request to Claude API
$response = wp_remote_post($api_url, array(
'headers' => array(
'Content-Type' => 'application/json',
'x-api-key' => $api_key,
'anthropic-version' => '2023-06-01'
),
'body' => json_encode($request_body),
'timeout' => 120
));
if (is_wp_error($response)) {
return $response;
}
$response_code = wp_remote_retrieve_response_code($response);
if ($response_code !== 200) {
$error_body = wp_remote_retrieve_body($response);
$error_data = json_decode($error_body, true);
$error_message = isset($error_data['error']['message']) ? $error_data['error']['message'] : 'Unknown error';
return new WP_Error('claude_api_error', 'Claude API error: ' . $error_message);
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if (json_last_error() !== JSON_ERROR_NONE) {
return new WP_Error('json_error', 'Invalid JSON response from Claude API: ' . json_last_error_msg());
}
// Extract the content from the response
if (isset($body['content']) && is_array($body['content']) && !empty($body['content'])) {
foreach ($body['content'] as $content) {
if ($content['type'] === 'text') {
return $content['text'];
}
}
}
return new WP_Error('invalid_response', 'Invalid response format from Claude API');
}
// Get template-specific system prompt with improved JSON formatting instructions
function wcsb_get_template_prompt($template) {
$templates = array(
'modern' => "You are an expert WordPress developer and designer specializing in modern, eye-catching websites.
Generate a complete WordPress website based on the user's description.
Your design MUST follow these principles:
- Use modern, clean design with ample white space
- Create responsive layouts that work on all devices
- Use attractive color schemes with good contrast
- Implement modern typography with readable font sizes
- Include subtle animations and transitions
- Use high-quality imagery and icons
- Follow current web design trends
Your response MUST be a valid JSON object with the following structure:
{
\"title\": \"Site Title\",
\"pages\": [
{
\"name\": \"Home\",
\"slug\": \"home\",
\"html\": \"Full HTML content\",
\"css\": \"Page-specific CSS\",
\"js\": \"Page-specific JavaScript\"
}
],
\"globalCss\": \"Global CSS styles\",
\"globalJs\": \"Global JavaScript\",
\"plugins\": [\"plugin-1\", \"plugin-2\"],
\"theme\": \"Suggested theme or 'custom'\"
}
For the HTML and CSS:
- Use modern CSS features like flexbox and grid for layouts
- Include responsive design with media queries
- Use CSS variables for consistent colors and styling
- Create visually appealing hero sections with gradients or background images
- Add subtle hover effects and transitions
- Include modern UI components like cards, badges, and tooltips
- Use !important for critical CSS properties to prevent theme overrides
- Ensure all CSS selectors are prefixed to prevent conflicts
- Make sure the design is visually appealing with proper colors and styling
For JavaScript:
- DO NOT include