Initial commit: igny8 project

This commit is contained in:
igny8
2025-11-09 10:27:02 +00:00
commit 60b8188111
27265 changed files with 4360521 additions and 0 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
/**
* Igny8 Image Injection CSS
*
* Responsive image display styles for marker-based image injection
*
* @package Igny8
* @version 1.0.0
*/
/* Desktop and larger screens (769px+) */
@media (min-width: 769px) {
.igny8-article-image-desktop {
display: block !important;
}
.igny8-article-image-mobile {
display: none !important;
}
}
/* Mobile and smaller screens (768px and below) */
@media (max-width: 768px) {
.igny8-article-image-desktop {
display: none !important;
}
.igny8-article-image-mobile {
display: block !important;
}
}
/* Image wrapper styling */
.igny8-image-wrapper {
margin: 20px 0;
text-align: center;
}
.igny8-image-wrapper img {
max-width: 100%;
height: auto;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* Loading state */
.igny8-image-wrapper img[loading="lazy"] {
opacity: 0;
transition: opacity 0.3s ease;
}
.igny8-image-wrapper img[loading="lazy"].loaded {
opacity: 1;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,436 @@
/**
* Igny8 Image Queue Processor
* Sequential image generation with individual progress tracking
*/
// Process AI Image Generation for Drafts (Sequential Image Processing with Queue Modal)
function processAIImageGenerationDrafts(postIds) {
console.log('Igny8: processAIImageGenerationDrafts called with postIds:', postIds);
// Event 1: Generate Images button clicked
if (window.addImageGenDebugLog) {
window.addImageGenDebugLog('INFO', 'Generate Images button clicked', {
postIds: postIds,
timestamp: new Date().toISOString()
});
}
// Get image generation settings from saved options (passed via wp_localize_script)
const desktopEnabled = window.IGNY8_PAGE?.imageSettings?.desktop_enabled || false;
const mobileEnabled = window.IGNY8_PAGE?.imageSettings?.mobile_enabled || false;
const maxInArticleImages = window.IGNY8_PAGE?.imageSettings?.max_in_article_images || 1;
// Event 2: Settings retrieved
if (window.addImageGenDebugLog) {
window.addImageGenDebugLog('SUCCESS', 'Settings retrieved', {
desktop: desktopEnabled,
mobile: mobileEnabled,
maxImages: maxInArticleImages
});
}
// Build image queue based on settings
const imageQueue = [];
postIds.forEach((postId, postIndex) => {
// Featured image (always)
imageQueue.push({
post_id: postId,
post_number: postIndex + 1,
type: 'featured',
device: '',
label: 'Featured Image',
post_title: `Post ${postIndex + 1}`
});
// Desktop in-article images
if (desktopEnabled) {
for (let i = 1; i <= maxInArticleImages; i++) {
imageQueue.push({
post_id: postId,
post_number: postIndex + 1,
type: 'article',
device: 'desktop',
index: i,
label: `desktop-${i}`,
section: i,
post_title: `Post ${postIndex + 1}`
});
}
}
// Mobile in-article images
if (mobileEnabled) {
for (let i = 1; i <= maxInArticleImages; i++) {
imageQueue.push({
post_id: postId,
post_number: postIndex + 1,
type: 'article',
device: 'mobile',
index: i,
label: `mobile-${i}`,
section: i,
post_title: `Post ${postIndex + 1}`
});
}
}
});
console.log('Igny8: Image queue built:', imageQueue);
// Show queue modal
showImageQueueModal(imageQueue, imageQueue.length);
// Start processing queue
processImageQueue(imageQueue, 0);
}
// Show modal with image queue and individual progress bars
function showImageQueueModal(queue, totalImages) {
if (window.currentProgressModal) {
window.currentProgressModal.remove();
}
const modal = document.createElement('div');
modal.id = 'igny8-image-queue-modal';
modal.className = 'igny8-modal';
let queueHTML = '';
queue.forEach((item, index) => {
const itemId = `queue-item-${index}`;
queueHTML += `
<div class="queue-item" id="${itemId}" data-status="pending">
<div style="display: flex; gap: 10px; align-items: center;">
<div style="flex: 1;">
<div class="queue-item-header">
<span class="queue-number">${index + 1}</span>
<span class="queue-label">${item.label}</span>
<span class="queue-post-title">${item.post_title}</span>
<span class="queue-status">⏳ Pending</span>
</div>
<div class="queue-progress-bar">
<div class="queue-progress-fill" style="width: 0%"></div>
<div class="queue-progress-text">0%</div>
</div>
<div class="queue-error" style="display: none;"></div>
</div>
<div class="queue-thumbnail" style="width: 75px; height: 75px; background: #f0f0f0; border-radius: 4px; display: flex; align-items: center; justify-content: center; overflow: hidden; flex-shrink: 0;">
<span style="color: #999; font-size: 11px;">No image</span>
</div>
</div>
</div>
`;
});
modal.innerHTML = `
<div class="igny8-modal-content" style="max-width: 950px; max-height: 80vh; overflow-y: auto;">
<div class="igny8-modal-header">
<h3>🎨 Generating Images</h3>
<p style="margin: 5px 0; color: var(--text-light);">Total: ${totalImages} images in queue</p>
</div>
<div class="igny8-modal-body">
<div class="image-queue-container">
${queueHTML}
</div>
</div>
</div>
<style>
.queue-item {
margin-bottom: 12px;
padding: 12px;
background: var(--panel-2);
border-radius: 8px;
border: 1px solid var(--border);
}
.queue-item[data-status="processing"] {
background: rgba(59, 130, 246, 0.1);
border-color: rgb(59, 130, 246);
}
.queue-item[data-status="completed"] {
background: rgba(16, 185, 129, 0.1);
border-color: rgb(16, 185, 129);
}
.queue-item[data-status="failed"] {
background: rgba(239, 68, 68, 0.1);
border-color: rgb(239, 68, 68);
}
.queue-item-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 8px;
font-size: 13px;
}
.queue-number {
background: rgb(59, 130, 246);
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: bold;
}
.queue-label {
font-weight: 600;
}
.queue-post-title {
flex: 1;
font-size: 12px;
opacity: 0.7;
}
.queue-status {
font-size: 12px;
font-weight: 600;
}
.queue-progress-bar {
height: 20px;
background: rgba(0,0,0,0.1);
border-radius: 10px;
overflow: hidden;
position: relative;
}
.queue-progress-fill {
height: 100%;
background: rgb(59, 130, 246);
transition: width 0.3s ease;
}
.queue-progress-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 11px;
font-weight: bold;
color: #333;
text-shadow: 0 0 3px rgba(255,255,255,0.8);
}
.queue-item[data-status="completed"] .queue-progress-fill {
background: rgb(16, 185, 129);
}
.queue-item[data-status="failed"] .queue-progress-fill {
background: rgb(239, 68, 68);
}
.queue-error {
margin-top: 8px;
padding: 8px;
background: rgba(239, 68, 68, 0.1);
border-left: 3px solid rgb(239, 68, 68);
border-radius: 4px;
font-size: 12px;
}
</style>
`;
document.body.appendChild(modal);
modal.classList.add('open');
window.currentProgressModal = modal;
}
// Process image queue sequentially with progressive loading
function processImageQueue(queue, currentIndex) {
if (currentIndex >= queue.length) {
// All done
console.log('Igny8: All images processed');
// Log to Image Generation Debug
if (window.addImageGenDebugLog) {
window.addImageGenDebugLog('SUCCESS', 'All images processed', {
total: queue.length,
timestamp: new Date().toISOString()
});
}
setTimeout(() => {
if (window.currentProgressModal) {
window.currentProgressModal.remove();
window.currentProgressModal = null;
}
showNotification('Image generation complete!', 'success');
// Reload table
if (window.loadTableData && window.IGNY8_PAGE?.tableId) {
window.loadTableData(window.IGNY8_PAGE.tableId);
}
}, 2000);
return;
}
const item = queue[currentIndex];
const itemElement = document.getElementById(`queue-item-${currentIndex}`);
if (!itemElement) {
console.error('Queue item element not found:', currentIndex);
// Log to Image Generation Debug
if (window.addImageGenDebugLog) {
window.addImageGenDebugLog('ERROR', 'Queue item element not found', {
index: currentIndex,
itemId: `queue-item-${currentIndex}`
});
}
setTimeout(() => processImageQueue(queue, currentIndex + 1), 100);
return;
}
// Update UI to processing
itemElement.setAttribute('data-status', 'processing');
itemElement.querySelector('.queue-status').textContent = '⏳ Generating...';
const progressFill = itemElement.querySelector('.queue-progress-fill');
const progressText = itemElement.querySelector('.queue-progress-text');
// Log to Image Generation Debug
if (window.addImageGenDebugLog) {
window.addImageGenDebugLog('INFO', `Processing ${item.label}`, {
postId: item.post_id,
type: item.type,
device: item.device || 'N/A',
index: item.index || 1,
queuePosition: `${currentIndex + 1}/${queue.length}`
});
}
// Progressive loading: 50% in 7s, 75% in next 5s, then 5% every second until 95%
let currentProgress = 0;
let phase = 1;
let phaseStartTime = Date.now();
const progressInterval = setInterval(() => {
const elapsed = Date.now() - phaseStartTime;
if (phase === 1 && currentProgress < 50) {
// Phase 1: 0% to 50% in 7 seconds (7.14% per second)
currentProgress += 0.714;
if (currentProgress >= 50 || elapsed >= 7000) {
currentProgress = 50;
phase = 2;
phaseStartTime = Date.now();
}
} else if (phase === 2 && currentProgress < 75) {
// Phase 2: 50% to 75% in 5 seconds (5% per second)
currentProgress += 0.5;
if (currentProgress >= 75 || elapsed >= 5000) {
currentProgress = 75;
phase = 3;
phaseStartTime = Date.now();
}
} else if (phase === 3 && currentProgress < 95) {
// Phase 3: 75% to 95% - 5% every second
if (elapsed >= 1000) {
currentProgress = Math.min(95, currentProgress + 5);
phaseStartTime = Date.now();
}
}
progressFill.style.width = currentProgress + '%';
progressText.textContent = Math.round(currentProgress) + '%';
}, 100);
// Generate single image
const formData = new FormData();
formData.append('action', 'igny8_ai_generate_single_image');
formData.append('nonce', window.IGNY8_PAGE.nonce);
formData.append('post_id', item.post_id);
formData.append('type', item.type);
formData.append('device', item.device || '');
formData.append('index', item.index || 1);
// Add meta box integration fields
formData.append('image_label', item.label || '');
formData.append('section', item.section || '');
fetch(window.IGNY8_PAGE.ajaxUrl, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
// Stop progressive loading
clearInterval(progressInterval);
if (data.success) {
// Success - complete to 100%
progressFill.style.width = '100%';
progressText.textContent = '100%';
itemElement.setAttribute('data-status', 'completed');
itemElement.querySelector('.queue-status').textContent = '✅ Complete';
// Display thumbnail if image URL is available
if (data.data?.image_url) {
const thumbnailDiv = itemElement.querySelector('.queue-thumbnail');
if (thumbnailDiv) {
thumbnailDiv.innerHTML = `<img src="${data.data.image_url}" style="width: 100%; height: 100%; object-fit: cover; border-radius: 4px;" alt="Generated image">`;
}
}
console.log(`✓ Image ${currentIndex + 1} generated successfully`);
// Log to Image Generation Debug
if (window.addImageGenDebugLog) {
window.addImageGenDebugLog('SUCCESS', `${item.label} generated successfully`, {
postId: item.post_id,
attachmentId: data.data?.attachment_id,
provider: data.data?.provider,
queuePosition: `${currentIndex + 1}/${queue.length}`
});
}
// Process next image after short delay
setTimeout(() => processImageQueue(queue, currentIndex + 1), 500);
} else {
// Error - show at 90%
progressFill.style.width = '90%';
progressText.textContent = 'Failed';
itemElement.setAttribute('data-status', 'failed');
itemElement.querySelector('.queue-status').textContent = '❌ Failed';
const errorDiv = itemElement.querySelector('.queue-error');
errorDiv.textContent = data.data?.message || 'Unknown error';
errorDiv.style.display = 'block';
console.error(`✗ Image ${currentIndex + 1} failed:`, data.data?.message);
// Log to Image Generation Debug
if (window.addImageGenDebugLog) {
window.addImageGenDebugLog('ERROR', `${item.label} generation failed`, {
postId: item.post_id,
error: data.data?.message || 'Unknown error',
queuePosition: `${currentIndex + 1}/${queue.length}`
});
}
// Continue to next image despite error
setTimeout(() => processImageQueue(queue, currentIndex + 1), 1000);
}
})
.catch(error => {
// Exception - stop progressive loading
clearInterval(progressInterval);
progressFill.style.width = '90%';
progressText.textContent = 'Error';
itemElement.setAttribute('data-status', 'failed');
itemElement.querySelector('.queue-status').textContent = '❌ Error';
const errorDiv = itemElement.querySelector('.queue-error');
errorDiv.textContent = 'Exception: ' + error.message;
errorDiv.style.display = 'block';
console.error(`✗ Image ${currentIndex + 1} exception:`, error);
// Log to Image Generation Debug
if (window.addImageGenDebugLog) {
window.addImageGenDebugLog('ERROR', `${item.label} request exception`, {
postId: item.post_id,
error: error.message,
queuePosition: `${currentIndex + 1}/${queue.length}`
});
}
// Continue to next image despite error
setTimeout(() => processImageQueue(queue, currentIndex + 1), 1000);
});
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* ==============================
* 📁 Folder Scope Declaration
* ==============================
* Folder: /shortcodes/
* Purpose: All shortcode handler files (split by module)
* Rules:
* - Must be organized by module
* - Each shortcode must be self-contained
* - No cross-module dependencies
* - Must use components for rendering
* - Frontend-only functionality
*/

View File

@@ -0,0 +1,278 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : image-gallery.php
* @location : /assets/shortcodes/image-gallery.php
* @type : Shortcode
* @scope : Global
* @allowed : Shortcode registration, frontend rendering, image display
* @reusability : Globally Reusable
* @notes : Image gallery shortcodes for frontend display
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Display specific in-article image by ID
*
* Usage: [igny8-image id="desktop-1"]
*
* @param array $atts Shortcode attributes
* @return string HTML output
*/
add_shortcode('igny8-image', function($atts) {
$atts = shortcode_atts(['id' => ''], $atts);
$post_id = get_the_ID();
if (empty($post_id)) {
return '';
}
$images = get_post_meta($post_id, '_igny8_inarticle_images', true);
if (!is_array($images)) {
return '';
}
// Display specific image by ID
if (!empty($atts['id']) && isset($images[$atts['id']])) {
$image_data = $images[$atts['id']];
// Device detection - only show if device matches
$is_mobile = wp_is_mobile();
$is_desktop = !$is_mobile;
// Check if image should be displayed based on device
$should_display = false;
if (strpos($atts['id'], 'desktop-') === 0 && $is_desktop) {
$should_display = true;
} elseif (strpos($atts['id'], 'mobile-') === 0 && $is_mobile) {
$should_display = true;
}
if (!$should_display) {
return '';
}
$attachment_id = intval($image_data['attachment_id']);
if ($attachment_id > 0) {
return wp_get_attachment_image($attachment_id, 'large', false, [
'class' => 'igny8-inarticle-image',
'data-image-id' => esc_attr($atts['id']),
'data-device' => esc_attr($image_data['device']),
'alt' => esc_attr($image_data['label'])
]);
}
}
return '';
});
/**
* Display all in-article images
*
* Usage: [igny8-images]
*
* @param array $atts Shortcode attributes
* @return string HTML output
*/
add_shortcode('igny8-images', function($atts) {
$atts = shortcode_atts([
'device' => '', // Filter by device type (desktop/mobile)
'size' => 'large', // Image size
'class' => 'igny8-image-gallery' // CSS class
], $atts);
$post_id = get_the_ID();
if (empty($post_id)) {
return '';
}
$images = get_post_meta($post_id, '_igny8_inarticle_images', true);
if (!is_array($images) || empty($images)) {
return '';
}
$output = '<div class="' . esc_attr($atts['class']) . '">';
$output .= '<p style="background: #f0f0f0; padding: 10px; border-left: 4px solid #0073aa; margin: 10px 0; font-weight: bold;">This is coming from shortcode</p>';
foreach ($images as $label => $image_data) {
// Filter by device if specified
if (!empty($atts['device']) && $image_data['device'] !== $atts['device']) {
continue;
}
$attachment_id = intval($image_data['attachment_id']);
if ($attachment_id > 0) {
$output .= wp_get_attachment_image($attachment_id, $atts['size'], false, [
'class' => 'igny8-inarticle-image',
'data-image-id' => esc_attr($label),
'data-device' => esc_attr($image_data['device']),
'alt' => esc_attr($image_data['label'])
]);
}
}
$output .= '</div>';
return $output;
});
/**
* Display desktop images only
*
* Usage: [igny8-desktop-images]
*
* @param array $atts Shortcode attributes
* @return string HTML output
*/
add_shortcode('igny8-desktop-images', function($atts) {
$atts = shortcode_atts([
'size' => 'large',
'class' => 'igny8-desktop-gallery'
], $atts);
return do_shortcode('[igny8-images device="desktop" size="' . $atts['size'] . '" class="' . $atts['class'] . '"]');
});
/**
* Display mobile images only
*
* Usage: [igny8-mobile-images]
*
* @param array $atts Shortcode attributes
* @return string HTML output
*/
add_shortcode('igny8-mobile-images', function($atts) {
$atts = shortcode_atts([
'size' => 'large',
'class' => 'igny8-mobile-gallery'
], $atts);
return do_shortcode('[igny8-images device="mobile" size="' . $atts['size'] . '" class="' . $atts['class'] . '"]');
});
/**
* Display image count
*
* Usage: [igny8-image-count]
*
* @param array $atts Shortcode attributes
* @return string HTML output
*/
add_shortcode('igny8-image-count', function($atts) {
$atts = shortcode_atts(['device' => ''], $atts);
$post_id = get_the_ID();
if (empty($post_id)) {
return '0';
}
$images = get_post_meta($post_id, '_igny8_inarticle_images', true);
if (!is_array($images)) {
return '0';
}
if (!empty($atts['device'])) {
$count = 0;
foreach ($images as $image_data) {
if ($image_data['device'] === $atts['device']) {
$count++;
}
}
return (string) $count;
}
return (string) count($images);
});
/**
* Display image gallery with responsive design
*
* Usage: [igny8-responsive-gallery]
*
* @param array $atts Shortcode attributes
* @return string HTML output
*/
add_shortcode('igny8-responsive-gallery', function($atts) {
$atts = shortcode_atts([
'desktop_size' => 'large',
'mobile_size' => 'medium',
'class' => 'igny8-responsive-gallery'
], $atts);
$post_id = get_the_ID();
if (empty($post_id)) {
return '';
}
$images = get_post_meta($post_id, '_igny8_inarticle_images', true);
if (!is_array($images) || empty($images)) {
return '';
}
$output = '<div class="' . esc_attr($atts['class']) . '">';
// Desktop images
$desktop_images = array_filter($images, function($img) {
return $img['device'] === 'desktop';
});
if (!empty($desktop_images)) {
$output .= '<div class="igny8-desktop-images" style="display: block;">';
foreach ($desktop_images as $label => $image_data) {
$attachment_id = intval($image_data['attachment_id']);
if ($attachment_id > 0) {
$output .= wp_get_attachment_image($attachment_id, $atts['desktop_size'], false, [
'class' => 'igny8-desktop-image',
'data-image-id' => esc_attr($label)
]);
}
}
$output .= '</div>';
}
// Mobile images
$mobile_images = array_filter($images, function($img) {
return $img['device'] === 'mobile';
});
if (!empty($mobile_images)) {
$output .= '<div class="igny8-mobile-images" style="display: none;">';
foreach ($mobile_images as $label => $image_data) {
$attachment_id = intval($image_data['attachment_id']);
if ($attachment_id > 0) {
$output .= wp_get_attachment_image($attachment_id, $atts['mobile_size'], false, [
'class' => 'igny8-mobile-image',
'data-image-id' => esc_attr($label)
]);
}
}
$output .= '</div>';
}
$output .= '</div>';
// Add responsive CSS
$output .= '<style>
@media (max-width: 768px) {
.igny8-desktop-images { display: none !important; }
.igny8-mobile-images { display: block !important; }
}
@media (min-width: 769px) {
.igny8-desktop-images { display: block !important; }
.igny8-mobile-images { display: none !important; }
}
</style>';
return $output;
});

View File

@@ -0,0 +1,4 @@
cluster_name,sector_id,status,keyword_count,total_volume,avg_difficulty,mapped_pages_count
"Car Interior Accessories",1,"active",25,45000,42,0
"Car Storage Solutions",1,"active",18,32000,38,0
"Car Beverage Holders",1,"active",12,18000,35,0
1 cluster_name sector_id status keyword_count total_volume avg_difficulty mapped_pages_count
2 Car Interior Accessories 1 active 25 45000 42 0
3 Car Storage Solutions 1 active 18 32000 38 0
4 Car Beverage Holders 1 active 12 18000 35 0

View File

@@ -0,0 +1,4 @@
idea_title,idea_description,content_structure,content_type,keyword_cluster_id,target_keywords,status,estimated_word_count
"Top 10 Car Interior Accessories for 2024","A comprehensive list of the best car interior accessories available this year, including reviews and recommendations.","review","post",1,"car accessories, car storage solutions, car interior accessories","new",1200
"How to Organize Your Car Interior Like a Pro","Step-by-step guide to organizing your car interior for maximum efficiency and comfort.","guide_tutorial","post",2,"car organization, car storage tips, car interior organization","new",1500
"DIY Car Storage Solutions That Actually Work","Creative and practical DIY storage solutions you can make at home for your car.","guide_tutorial","post",2,"DIY car storage, car storage solutions, car organization tips","new",800
1 idea_title idea_description content_structure content_type keyword_cluster_id target_keywords status estimated_word_count
2 Top 10 Car Interior Accessories for 2024 A comprehensive list of the best car interior accessories available this year, including reviews and recommendations. review post 1 car accessories, car storage solutions, car interior accessories new 1200
3 How to Organize Your Car Interior Like a Pro Step-by-step guide to organizing your car interior for maximum efficiency and comfort. guide_tutorial post 2 car organization, car storage tips, car interior organization new 1500
4 DIY Car Storage Solutions That Actually Work Creative and practical DIY storage solutions you can make at home for your car. guide_tutorial post 2 DIY car storage, car storage solutions, car organization tips new 800

View File

@@ -0,0 +1,4 @@
keyword,search_volume,difficulty,cpc,intent,status,sector_id,cluster_id
"car accessories",12000,45,2.50,"commercial","unmapped",1,0
"car storage solutions",8500,38,1.80,"informational","unmapped",1,0
"car interior accessories",15000,52,3.20,"commercial","unmapped",1,0
1 keyword search_volume difficulty cpc intent status sector_id cluster_id
2 car accessories 12000 45 2.50 commercial unmapped 1 0
3 car storage solutions 8500 38 1.80 informational unmapped 1 0
4 car interior accessories 15000 52 3.20 commercial unmapped 1 0