chore: sync MediaPromptEngine.php to container version (v2.4.0)
This commit is contained in:
@@ -6,145 +6,90 @@ defined('ABSPATH') || exit;
|
||||
* MediaPromptEngine — AI-aware prompt enrichment for image and video generation.
|
||||
*
|
||||
* Inspired by MotionSites.ai design vocabulary:
|
||||
* - cinematic dark heroes, left-aligned content zones, glass effects (SaaS/tech)
|
||||
* - warm photorealistic imagery for local business / service industries
|
||||
* - cinematic dark heroes with video backgrounds and glass effects
|
||||
* - left-aligned content composition with clear text overlay zones
|
||||
* - industry-matched visual language (dark/SaaS vs warm/local-business)
|
||||
*
|
||||
* Called by: pexels_search_photos, pexels_search_videos,
|
||||
* generate_gpt_image, generate_gemini_image
|
||||
*
|
||||
* Every call needs just 3 things from Claude:
|
||||
* 1. raw concept — "dental clinic interior"
|
||||
* 2. usage_context — "hero_background", "card_image", "team_photo", etc.
|
||||
* 3. industry — "dental", "saas", "restaurant", "gym", etc.
|
||||
*
|
||||
* The engine auto-resolves: enriched prompt, dimensions, GPT size param,
|
||||
* orientation, quality, Pexels query, video duration, and admin style profile.
|
||||
*/
|
||||
class MediaPromptEngine {
|
||||
|
||||
// ─── USAGE CONTEXT → DIMENSION MAP ───────────────────────────────────────
|
||||
|
||||
const CONTEXT_MAP = [
|
||||
'hero_background' => [
|
||||
'width' => 1920,
|
||||
'height' => 1080,
|
||||
'aspect' => '16:9',
|
||||
'orientation' => 'landscape',
|
||||
'size' => '1536x1024',
|
||||
'quality' => 'high',
|
||||
'width' => 1920, 'height' => 1080, 'aspect' => '16:9',
|
||||
'orientation' => 'landscape', 'size' => '1536x1024', 'quality' => 'high',
|
||||
'composition' => 'wide angle, cinematic, left third of frame clear for text overlay, subject or scene on right, dark gradient on left edge',
|
||||
'format_note' => 'full-screen website hero background, 16:9 landscape',
|
||||
],
|
||||
'section_background' => [
|
||||
'width' => 1920,
|
||||
'height' => 700,
|
||||
'aspect' => '16:6',
|
||||
'orientation' => 'landscape',
|
||||
'size' => '1536x1024',
|
||||
'quality' => 'medium',
|
||||
'width' => 1920, 'height' => 700, 'aspect' => '16:6',
|
||||
'orientation' => 'landscape', 'size' => '1536x1024', 'quality' => 'medium',
|
||||
'composition' => 'wide panoramic, soft and low-contrast, content sits on top so avoid busy center, muted colors acceptable',
|
||||
'format_note' => 'website section background strip, panoramic landscape',
|
||||
],
|
||||
'card_image' => [
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'aspect' => '4:3',
|
||||
'orientation' => 'landscape',
|
||||
'size' => '1024x1024',
|
||||
'quality' => 'medium',
|
||||
'width' => 800, 'height' => 600, 'aspect' => '4:3',
|
||||
'orientation' => 'landscape', 'size' => '1024x1024', 'quality' => 'medium',
|
||||
'composition' => 'subject centered, clean background, sharp focus, good contrast',
|
||||
'format_note' => 'website card or feature image, 4:3 ratio',
|
||||
],
|
||||
'team_photo' => [
|
||||
'width' => 600,
|
||||
'height' => 750,
|
||||
'aspect' => '4:5',
|
||||
'orientation' => 'portrait',
|
||||
'size' => '1024x1536',
|
||||
'quality' => 'high',
|
||||
'width' => 600, 'height' => 750, 'aspect' => '4:5',
|
||||
'orientation' => 'portrait', 'size' => '1024x1536', 'quality' => 'high',
|
||||
'composition' => 'professional headshot or half-body portrait, neutral or blurred background, face sharp and well-lit, friendly expression',
|
||||
'format_note' => 'team member or staff portrait, 4:5 portrait',
|
||||
],
|
||||
'feature_icon' => [
|
||||
'width' => 500,
|
||||
'height' => 500,
|
||||
'aspect' => '1:1',
|
||||
'orientation' => 'square',
|
||||
'size' => '1024x1024',
|
||||
'quality' => 'medium',
|
||||
'width' => 500, 'height' => 500, 'aspect' => '1:1',
|
||||
'orientation' => 'square', 'size' => '1024x1024', 'quality' => 'medium',
|
||||
'composition' => 'flat design or minimal illustration, single subject, centered on white or transparent background, icon-style, clean lines',
|
||||
'format_note' => 'feature section icon, square 1:1',
|
||||
],
|
||||
'gallery_image' => [
|
||||
'width' => 900,
|
||||
'height' => 600,
|
||||
'aspect' => '3:2',
|
||||
'orientation' => 'landscape',
|
||||
'size' => '1536x1024',
|
||||
'quality' => 'high',
|
||||
'width' => 900, 'height' => 600, 'aspect' => '3:2',
|
||||
'orientation' => 'landscape', 'size' => '1536x1024', 'quality' => 'high',
|
||||
'composition' => 'editorial photography style, strong composition, vibrant or intentional color palette, high detail',
|
||||
'format_note' => 'gallery or portfolio item, 3:2 landscape',
|
||||
],
|
||||
'cta_background' => [
|
||||
'width' => 1920,
|
||||
'height' => 500,
|
||||
'aspect' => '16:5',
|
||||
'orientation' => 'landscape',
|
||||
'size' => '1536x1024',
|
||||
'quality' => 'medium',
|
||||
'width' => 1920, 'height' => 500, 'aspect' => '16:5',
|
||||
'orientation' => 'landscape', 'size' => '1536x1024', 'quality' => 'medium',
|
||||
'composition' => 'wide panoramic, very subtle and blurred, will have overlaid text and button — avoid any text or detail in center',
|
||||
'format_note' => 'CTA strip background, very wide panoramic',
|
||||
],
|
||||
'blog_thumbnail' => [
|
||||
'width' => 1200,
|
||||
'height' => 675,
|
||||
'aspect' => '16:9',
|
||||
'orientation' => 'landscape',
|
||||
'size' => '1536x1024',
|
||||
'quality' => 'medium',
|
||||
'width' => 1200, 'height' => 675, 'aspect' => '16:9',
|
||||
'orientation' => 'landscape', 'size' => '1536x1024', 'quality' => 'medium',
|
||||
'composition' => 'editorial, subject left or center, room for text overlay on right side',
|
||||
'format_note' => 'blog post thumbnail, 16:9',
|
||||
],
|
||||
'logo_placeholder' => [
|
||||
'width' => 400,
|
||||
'height' => 200,
|
||||
'aspect' => '2:1',
|
||||
'orientation' => 'landscape',
|
||||
'size' => '1024x1024',
|
||||
'quality' => 'low',
|
||||
'width' => 400, 'height' => 200, 'aspect' => '2:1',
|
||||
'orientation' => 'landscape', 'size' => '1024x1024', 'quality' => 'low',
|
||||
'composition' => 'minimal logo-style illustration, centered, white or transparent background, clean vector look',
|
||||
'format_note' => 'brand logo placeholder, 2:1',
|
||||
],
|
||||
'video_hero' => [
|
||||
'width' => 1920,
|
||||
'height' => 1080,
|
||||
'aspect' => '16:9',
|
||||
'orientation' => 'landscape',
|
||||
'duration' => [15, 30],
|
||||
'motion' => 'slow cinematic motion, subtle parallax, ambient loop, no jump cuts',
|
||||
'width' => 1920, 'height' => 1080, 'aspect' => '16:9',
|
||||
'orientation' => 'landscape', 'duration' => [15, 30],
|
||||
'motion' => 'slow cinematic motion, subtle parallax, ambient loop, no jump cuts',
|
||||
'format_note' => 'full-screen hero video background loop, 16:9, 15-30 seconds',
|
||||
],
|
||||
'video_section' => [
|
||||
'width' => 1920,
|
||||
'height' => 1080,
|
||||
'aspect' => '16:9',
|
||||
'orientation' => 'landscape',
|
||||
'duration' => [5, 15],
|
||||
'motion' => 'ambient motion, slow pan or zoom, atmospheric, loops cleanly',
|
||||
'width' => 1920, 'height' => 1080, 'aspect' => '16:9',
|
||||
'orientation' => 'landscape', 'duration' => [5, 15],
|
||||
'motion' => 'ambient motion, slow pan or zoom, atmospheric, loops cleanly',
|
||||
'format_note' => 'section video accent, 16:9, 5-15 seconds',
|
||||
],
|
||||
];
|
||||
|
||||
// ─── INDUSTRY STYLE PROFILES ─────────────────────────────────────────────
|
||||
|
||||
const INDUSTRY_PROFILES = [
|
||||
|
||||
// ── DARK / CINEMATIC — MotionSites.ai SaaS vocabulary ─────────────
|
||||
'saas' => [
|
||||
'style' => 'dark cinematic, premium SaaS aesthetic, deep space or dark backgrounds, purple-black tones (#070612 style), glassmorphism accents, subtle glow effects',
|
||||
'lighting' => 'dramatic rim lighting, blue-purple ambience, volumetric light rays',
|
||||
'mood' => 'futuristic, powerful, cutting-edge',
|
||||
'avoid' => 'bright white backgrounds, cartoonish, stock-photo cliches, clip art',
|
||||
'avoid' => 'bright white backgrounds, cartoonish, stock-photo clichés, clip art',
|
||||
],
|
||||
'tech' => [
|
||||
'style' => 'dark high-tech, circuit board aesthetic, deep navy or charcoal backgrounds, electric blue accents, clean lines',
|
||||
@@ -156,7 +101,7 @@ class MediaPromptEngine {
|
||||
'style' => 'premium agency aesthetic, bold typography composition, dark editorial, high contrast, cinematic film-like quality',
|
||||
'lighting' => 'directional cinematic lighting, strong shadows, dramatic contrast',
|
||||
'mood' => 'creative, bold, premium',
|
||||
'avoid' => 'generic stock, soft pastels, corporate cliches',
|
||||
'avoid' => 'generic stock, soft pastels, corporate clichés',
|
||||
],
|
||||
'finance' => [
|
||||
'style' => 'clean professional, dark navy or deep charcoal, gold accents, architectural minimalism',
|
||||
@@ -170,8 +115,6 @@ class MediaPromptEngine {
|
||||
'mood' => 'decentralized, innovative, futuristic',
|
||||
'avoid' => 'corporate, traditional, stock photography',
|
||||
],
|
||||
|
||||
// ── WARM / PHOTOREALISTIC — local business, service industries ────
|
||||
'medical' => [
|
||||
'style' => 'clean medical professional, bright whites and soft blues, modern clinical interior, sterile but warm',
|
||||
'lighting' => 'bright natural or clinical lighting, soft fill, no harsh shadows',
|
||||
@@ -194,7 +137,7 @@ class MediaPromptEngine {
|
||||
'style' => 'premium fitness aesthetic, high-contrast black and red or dark with neon, athletic energy, motion blur acceptable',
|
||||
'lighting' => 'dramatic spotlighting on subject, dark moody gym background',
|
||||
'mood' => 'powerful, intense, motivating',
|
||||
'avoid' => 'soft pastels, low energy, stock-photo cliches',
|
||||
'avoid' => 'soft pastels, low energy, stock-photo clichés',
|
||||
],
|
||||
'restaurant' => [
|
||||
'style' => 'warm restaurant atmosphere, rich food photography colors, ambient candlelit or warm Edison bulb tones, appetizing',
|
||||
@@ -242,7 +185,7 @@ class MediaPromptEngine {
|
||||
'style' => 'destination travel photography, golden hour landscapes, immersive wide vistas, vibrant local color',
|
||||
'lighting' => 'golden hour, magic hour, dramatic natural light',
|
||||
'mood' => 'wanderlust, adventurous, escapist',
|
||||
'avoid' => 'tourist trap cliches, overprocessed HDR',
|
||||
'avoid' => 'tourist trap clichés, overprocessed HDR',
|
||||
],
|
||||
'construction' => [
|
||||
'style' => 'industrial construction professional, bold and powerful, concrete and steel, strong workers or architecture',
|
||||
@@ -267,19 +210,16 @@ class MediaPromptEngine {
|
||||
const UNIVERSAL_IMAGE_SUFFIX = 'No text overlays. No watermarks. No logos. No borders. Photorealistic unless otherwise specified. Award-winning photography quality. Ultra-detailed.';
|
||||
const UNIVERSAL_VIDEO_SUFFIX = 'No text overlays. No logos. No watermarks. Seamlessly loopable. Cinematic quality. 4K resolution preferred. Smooth motion, no jump cuts.';
|
||||
|
||||
// ─── MAIN BUILD METHOD ────────────────────────────────────────────────────
|
||||
|
||||
public static function build(
|
||||
string $raw_prompt,
|
||||
string $usage_context = 'hero_background',
|
||||
string $industry = 'general',
|
||||
array $overrides = []
|
||||
): array {
|
||||
|
||||
$ctx = self::CONTEXT_MAP[$usage_context] ?? self::CONTEXT_MAP['hero_background'];
|
||||
$industry_key = self::resolveIndustry($industry);
|
||||
$profile = self::INDUSTRY_PROFILES[$industry_key];
|
||||
$admin_profile= self::getAdminStyleProfile();
|
||||
$admin_profile = self::getAdminStyleProfile();
|
||||
$is_video = str_starts_with($usage_context, 'video_');
|
||||
|
||||
$enriched = $is_video
|
||||
@@ -312,32 +252,28 @@ class MediaPromptEngine {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// ─── PROMPT BUILDERS ─────────────────────────────────────────────────────
|
||||
|
||||
private static function buildImagePrompt(string $raw, array $ctx, array $p, string $admin, array $ov): string {
|
||||
private static function buildImagePrompt(string $raw, array $ctx, array $profile, string $admin, array $ov): string {
|
||||
$parts = [
|
||||
ucfirst(trim($raw)),
|
||||
$ctx['composition'],
|
||||
'Visual style: ' . ($ov['style_override'] ?? $p['style']),
|
||||
'Lighting: ' . $p['lighting'],
|
||||
'Mood: ' . $p['mood'],
|
||||
'Visual style: ' . ($ov['style_override'] ?? $profile['style']),
|
||||
'Lighting: ' . $profile['lighting'],
|
||||
'Mood: ' . $profile['mood'],
|
||||
'Format: ' . $ctx['format_note'],
|
||||
$admin ? "Brand profile: {$admin}" : null,
|
||||
isset($ov['color_hint']) ? "Color palette: {$ov['color_hint']}" : null,
|
||||
self::UNIVERSAL_IMAGE_SUFFIX,
|
||||
'Do NOT include: ' . $p['avoid'],
|
||||
'Do NOT include: ' . $profile['avoid'],
|
||||
];
|
||||
return implode('. ', array_filter($parts));
|
||||
}
|
||||
|
||||
private static function buildVideoPrompt(string $raw, array $ctx, array $p, string $admin, array $ov): string {
|
||||
$dur_min = $ctx['duration'][0] ?? 10;
|
||||
$dur_max = $ctx['duration'][1] ?? 30;
|
||||
private static function buildVideoPrompt(string $raw, array $ctx, array $profile, string $admin, array $ov): string {
|
||||
$parts = [
|
||||
ucfirst(trim($raw)),
|
||||
$ctx['motion'] ?? 'slow ambient motion',
|
||||
'Visual style: ' . ($ov['style_override'] ?? $p['style']),
|
||||
"Duration: {$dur_min}-{$dur_max} seconds",
|
||||
'Visual style: ' . ($ov['style_override'] ?? $profile['style']),
|
||||
'Duration: ' . ($ctx['duration'][0] ?? 10) . '-' . ($ctx['duration'][1] ?? 30) . ' seconds',
|
||||
'Format: ' . $ctx['format_note'],
|
||||
$admin ? "Brand profile: {$admin}" : null,
|
||||
self::UNIVERSAL_VIDEO_SUFFIX,
|
||||
@@ -345,26 +281,24 @@ class MediaPromptEngine {
|
||||
return implode('. ', array_filter($parts));
|
||||
}
|
||||
|
||||
// ─── PEXELS QUERY BUILDER ────────────────────────────────────────────────
|
||||
|
||||
private static function buildPexelsQuery(string $raw, string $context, string $industry): string {
|
||||
$ctx_suffix = match(true) {
|
||||
$context === 'hero_background' => 'wide cinematic',
|
||||
$context === 'section_background' => 'wide background',
|
||||
$context === 'team_photo' => 'professional portrait',
|
||||
$context_suffix = match(true) {
|
||||
$context === 'hero_background' => 'wide cinematic',
|
||||
$context === 'section_background' => 'wide background',
|
||||
$context === 'team_photo' => 'professional portrait',
|
||||
str_starts_with($context, 'video_') => 'cinematic video',
|
||||
default => '',
|
||||
default => '',
|
||||
};
|
||||
$ind_suffix = match($industry) {
|
||||
'saas', 'tech' => 'technology dark',
|
||||
'medical', 'dental' => 'medical professional clean',
|
||||
'restaurant', 'food' => 'restaurant food warm',
|
||||
'gym', 'health' => 'fitness active',
|
||||
'real_estate' => 'architecture interior',
|
||||
'legal' => 'professional office',
|
||||
default => '',
|
||||
$industry_suffix = match($industry) {
|
||||
'saas', 'tech' => 'technology dark',
|
||||
'medical', 'dental' => 'medical professional clean',
|
||||
'restaurant', 'food' => 'restaurant food warm',
|
||||
'gym', 'health' => 'fitness active',
|
||||
'real_estate' => 'architecture interior',
|
||||
'legal' => 'professional office',
|
||||
default => '',
|
||||
};
|
||||
$parts = array_filter([trim($raw), $ctx_suffix, $ind_suffix]);
|
||||
$parts = array_filter([trim($raw), $context_suffix, $industry_suffix]);
|
||||
$query = implode(' ', $parts);
|
||||
if (str_word_count($query) > 6) {
|
||||
$query = implode(' ', array_slice(explode(' ', $query), 0, 5));
|
||||
@@ -372,48 +306,37 @@ class MediaPromptEngine {
|
||||
return $query;
|
||||
}
|
||||
|
||||
// ─── INDUSTRY RESOLVER ───────────────────────────────────────────────────
|
||||
|
||||
public static function resolveIndustry(string $industry): string {
|
||||
$industry = strtolower(trim($industry));
|
||||
|
||||
// Specific phrases MUST come before single-word keywords to avoid false matches.
|
||||
// e.g. 'dental clinic' before 'clinic', 'hair salon' before 'ai' substring in 'hair'.
|
||||
$map = [
|
||||
'saas'=>'saas','software'=>'saas','startup'=>'saas','app'=>'saas',
|
||||
'tech'=>'tech','technology'=>'tech','cybersecurity'=>'tech','data'=>'tech',
|
||||
'agency'=>'agency','design'=>'agency','creative'=>'agency','marketing'=>'agency','portfolio'=>'agency','studio'=>'agency',
|
||||
'finance'=>'finance','fintech'=>'finance','banking'=>'finance','insurance'=>'finance','investment'=>'finance',
|
||||
'web3'=>'web3','crypto'=>'web3','blockchain'=>'web3','defi'=>'web3','nft'=>'web3',
|
||||
'medical'=>'medical','dental clinic'=>'dental','dental center'=>'dental','clinic'=>'medical','hospital'=>'medical','healthcare'=>'medical','pharmacy'=>'medical',
|
||||
'dental'=>'dental','dentist'=>'dental','orthodontic'=>'dental',
|
||||
'health'=>'health','wellness'=>'health','yoga'=>'health','spa'=>'health','therapy'=>'health',
|
||||
'gym'=>'gym','fitness'=>'gym','crossfit'=>'gym','boot camp'=>'gym','sport'=>'gym','training'=>'gym',
|
||||
'restaurant'=>'restaurant','cafe'=>'restaurant','coffee'=>'restaurant','bar'=>'restaurant','bakery'=>'restaurant',
|
||||
'food'=>'food','catering'=>'food',
|
||||
'real_estate'=>'real_estate','real estate'=>'real_estate','property'=>'real_estate','architecture'=>'real_estate','interior'=>'real_estate',
|
||||
'legal'=>'legal','law'=>'legal','lawyer'=>'legal','attorney'=>'legal',
|
||||
'education'=>'education','school'=>'education','university'=>'education','course'=>'education','academy'=>'education','elearning'=>'education',
|
||||
'hair salon'=>'beauty','nail salon'=>'beauty','beauty'=>'beauty','salon'=>'beauty','cosmetics'=>'beauty','skincare'=>'beauty','hair'=>'beauty','barbershop'=>'beauty',
|
||||
'fashion'=>'fashion','clothing'=>'fashion','apparel'=>'fashion',
|
||||
'travel'=>'travel','hotel'=>'travel','tourism'=>'travel','resort'=>'travel',
|
||||
'construction'=>'construction','contractor'=>'construction','building'=>'construction',
|
||||
'automotive'=>'automotive','car'=>'automotive','vehicle'=>'automotive',
|
||||
'saas' => 'saas', 'software' => 'saas', 'ai' => 'saas', 'startup' => 'saas', 'app' => 'saas',
|
||||
'tech' => 'tech', 'technology' => 'tech', 'cybersecurity' => 'tech', 'data' => 'tech',
|
||||
'agency' => 'agency', 'design' => 'agency', 'creative' => 'agency', 'marketing' => 'agency', 'portfolio' => 'agency', 'studio' => 'agency',
|
||||
'finance' => 'finance', 'fintech' => 'finance', 'banking' => 'finance', 'insurance' => 'finance', 'investment' => 'finance',
|
||||
'web3' => 'web3', 'crypto' => 'web3', 'blockchain' => 'web3', 'defi' => 'web3', 'nft' => 'web3',
|
||||
'medical' => 'medical', 'dental clinic' => 'dental', 'dental center' => 'dental', 'clinic' => 'medical', 'hospital' => 'medical', 'healthcare' => 'medical', 'pharmacy' => 'medical',
|
||||
'dental' => 'dental', 'dentist' => 'dental', 'orthodontic' => 'dental',
|
||||
'health' => 'health', 'wellness' => 'health', 'yoga' => 'health', 'spa' => 'health', 'therapy' => 'health',
|
||||
'gym' => 'gym', 'fitness' => 'gym', 'crossfit' => 'gym', 'boot camp' => 'gym', 'sport' => 'gym', 'training' => 'gym',
|
||||
'restaurant' => 'restaurant', 'cafe' => 'restaurant', 'coffee' => 'restaurant', 'bar' => 'restaurant', 'bakery' => 'restaurant',
|
||||
'food' => 'food', 'catering' => 'food',
|
||||
'real_estate' => 'real_estate', 'real estate' => 'real_estate', 'property' => 'real_estate', 'architecture' => 'real_estate', 'interior' => 'real_estate',
|
||||
'legal' => 'legal', 'law' => 'legal', 'lawyer' => 'legal', 'attorney' => 'legal',
|
||||
'education' => 'education', 'school' => 'education', 'university' => 'education', 'course' => 'education', 'academy' => 'education', 'elearning' => 'education',
|
||||
'hair salon' => 'beauty', 'nail salon' => 'beauty', 'beauty' => 'beauty', 'salon' => 'beauty', 'cosmetics' => 'beauty', 'skincare' => 'beauty', 'hair' => 'beauty', 'barbershop' => 'beauty',
|
||||
'fashion' => 'fashion', 'clothing' => 'fashion', 'apparel' => 'fashion',
|
||||
'travel' => 'travel', 'hotel' => 'travel', 'tourism' => 'travel', 'resort' => 'travel',
|
||||
'construction' => 'construction', 'contractor' => 'construction', 'building' => 'construction',
|
||||
'automotive' => 'automotive', 'car' => 'automotive', 'vehicle' => 'automotive',
|
||||
];
|
||||
|
||||
if (isset($map[$industry])) return $map[$industry];
|
||||
|
||||
// Partial fuzzy match — skip keywords under 3 chars to avoid 'ai' matching 'hair'
|
||||
foreach ($map as $keyword => $profile) {
|
||||
if (strlen($keyword) < 3) continue;
|
||||
if (str_contains($industry, $keyword)) return $profile;
|
||||
}
|
||||
|
||||
return 'general';
|
||||
}
|
||||
|
||||
// ─── ADMIN STYLE PROFILE ─────────────────────────────────────────────────
|
||||
|
||||
public static function getAdminStyleProfile(): string {
|
||||
$profiles = get_option('asktam_mcp_media_style_profiles', []);
|
||||
if (!is_array($profiles)) return '';
|
||||
@@ -422,8 +345,6 @@ class MediaPromptEngine {
|
||||
return $profiles['profiles'][$active]['description'] ?? '';
|
||||
}
|
||||
|
||||
// ─── CONVENIENCE WRAPPERS ────────────────────────────────────────────────
|
||||
|
||||
public static function imagePrompt(string $raw, string $context = 'hero_background', string $industry = 'general'): string {
|
||||
return self::build($raw, $context, $industry)['enriched_prompt'];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user