798 lines
24 KiB
JavaScript
798 lines
24 KiB
JavaScript
/**
|
|
* Image Generation Modal Component
|
|
*
|
|
* Handles image review, generation, variant selection, and commitment.
|
|
*
|
|
* @package WP_Agentic_Writer
|
|
*/
|
|
|
|
(function() {
|
|
const { Modal, Button, Spinner, TextControl, TextareaControl } = wp.components;
|
|
const { useState, useEffect, render, createRoot } = wp.element;
|
|
|
|
window.wpAgenticWriter = window.wpAgenticWriter || {};
|
|
|
|
/**
|
|
* Image Review Modal
|
|
* Shows after article generation with image recommendations
|
|
*/
|
|
window.wpAgenticWriter.ImageReviewModal = function({ postId, initialImageId, initialBlockId, onClose, onComplete }) {
|
|
const [step, setStep] = useState('loading');
|
|
const [images, setImages] = useState([]);
|
|
const [generatedImages, setGeneratedImages] = useState([]);
|
|
const [selectedImages, setSelectedImages] = useState([]);
|
|
const [variantCounts, setVariantCounts] = useState({});
|
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
const [committingVariantId, setCommittingVariantId] = useState(null);
|
|
const [extraVariantCounts, setExtraVariantCounts] = useState({});
|
|
const [generatingMoreFor, setGeneratingMoreFor] = useState(null);
|
|
const [error, setError] = useState(null);
|
|
|
|
useEffect(() => {
|
|
loadImageRecommendations();
|
|
}, []);
|
|
|
|
const scopedImages = (() => {
|
|
if (initialImageId) {
|
|
const direct = images.filter((img) => String(img.agent_image_id || '').trim() === String(initialImageId).trim());
|
|
if (direct.length > 0) {
|
|
return direct;
|
|
}
|
|
}
|
|
|
|
if (!initialBlockId) {
|
|
return images;
|
|
}
|
|
|
|
const allBlocks = wp.data.select('core/block-editor').getBlocks() || [];
|
|
const flatImageBlocks = [];
|
|
const walk = (blocks) => {
|
|
blocks.forEach((block) => {
|
|
if (block?.name === 'core/image') {
|
|
flatImageBlocks.push(block);
|
|
}
|
|
if (Array.isArray(block?.innerBlocks) && block.innerBlocks.length > 0) {
|
|
walk(block.innerBlocks);
|
|
}
|
|
});
|
|
};
|
|
walk(allBlocks);
|
|
|
|
const slotIndex = flatImageBlocks.findIndex((block) => block?.clientId === initialBlockId);
|
|
if (slotIndex < 0) {
|
|
return images;
|
|
}
|
|
|
|
const placement = `slot_${slotIndex + 1}`;
|
|
const placementScoped = images.filter((img) => String(img.placement || '').toLowerCase() === placement);
|
|
return placementScoped.length > 0 ? placementScoped : images;
|
|
})();
|
|
|
|
useEffect(() => {
|
|
if ((!initialImageId && !initialBlockId) || scopedImages.length === 0) {
|
|
return;
|
|
}
|
|
const scopedIds = scopedImages.map((img) => img.agent_image_id).filter(Boolean);
|
|
if (scopedIds.length === 0) {
|
|
return;
|
|
}
|
|
const normalize = (items) => [...items].sort().join('|');
|
|
setSelectedImages((prev) => {
|
|
if (normalize(prev) === normalize(scopedIds)) {
|
|
return prev;
|
|
}
|
|
return scopedIds;
|
|
});
|
|
}, [initialImageId, initialBlockId, scopedImages.length]);
|
|
|
|
const loadImageRecommendations = async () => {
|
|
try {
|
|
const response = await fetch(
|
|
`${wpAgenticWriter.apiUrl}/image-recommendations/${postId}`,
|
|
{
|
|
headers: {
|
|
'X-WP-Nonce': wpAgenticWriter.nonce,
|
|
},
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(await extractApiErrorMessage(response, 'Failed to load image recommendations'));
|
|
}
|
|
|
|
const data = await response.json();
|
|
const imgs = data.images || [];
|
|
setImages(imgs);
|
|
|
|
const initialCounts = {};
|
|
imgs.forEach(img => {
|
|
initialCounts[img.agent_image_id] = 2;
|
|
});
|
|
setVariantCounts(initialCounts);
|
|
|
|
if (initialImageId) {
|
|
const preferred = imgs.filter((img) => String(img.agent_image_id || '').trim() === String(initialImageId).trim());
|
|
if (preferred.length > 0) {
|
|
setSelectedImages(preferred.map((img) => img.agent_image_id));
|
|
}
|
|
}
|
|
|
|
setStep('review');
|
|
} catch (err) {
|
|
setError(err.message);
|
|
setStep('review');
|
|
}
|
|
};
|
|
|
|
const handleEditPrompt = (imageId, newPrompt) => {
|
|
setImages(prev => prev.map(img =>
|
|
img.agent_image_id === imageId
|
|
? { ...img, prompt_edited: newPrompt }
|
|
: img
|
|
));
|
|
};
|
|
|
|
const handleEditAlt = (imageId, newAlt) => {
|
|
setImages(prev => prev.map(img =>
|
|
img.agent_image_id === imageId
|
|
? { ...img, alt_text_edited: newAlt }
|
|
: img
|
|
));
|
|
};
|
|
|
|
const handleVariantCountChange = (imageId, count) => {
|
|
setVariantCounts(prev => ({
|
|
...prev,
|
|
[imageId]: parseInt(count, 10)
|
|
}));
|
|
};
|
|
|
|
const calculateTotalCost = () => {
|
|
const settings = wpAgenticWriter.settings || {};
|
|
const imageModel = settings.image_model || 'sourceful/riverflow-v2-max';
|
|
|
|
const costPerImage = {
|
|
'black-forest-labs/flux.2-klein': 0.02,
|
|
'sourceful/riverflow-v2-max': 0.03,
|
|
'black-forest-labs/flux.2-max': 0.15,
|
|
};
|
|
|
|
const baseCost = costPerImage[imageModel] || 0.03;
|
|
|
|
let total = 0;
|
|
selectedImages.forEach(imageId => {
|
|
const count = variantCounts[imageId] || 1;
|
|
total += baseCost * count;
|
|
});
|
|
|
|
return total.toFixed(3);
|
|
};
|
|
|
|
const calculateTotalVariants = () => {
|
|
let total = 0;
|
|
selectedImages.forEach((imageId) => {
|
|
total += variantCounts[imageId] || 2;
|
|
});
|
|
return total;
|
|
};
|
|
|
|
const handleGenerateSelected = async () => {
|
|
if (selectedImages.length === 0) {
|
|
alert('Please select at least one image to generate');
|
|
return;
|
|
}
|
|
|
|
setIsGenerating(true);
|
|
setStep('generating');
|
|
|
|
try {
|
|
const generated = [];
|
|
for (const imageId of selectedImages) {
|
|
const image = images.find(img => img.agent_image_id === imageId);
|
|
if (!image) {
|
|
throw new Error(`Image recommendation was not found for ${imageId}`);
|
|
}
|
|
|
|
const response = await fetch(
|
|
`${wpAgenticWriter.apiUrl}/generate-image`,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-WP-Nonce': wpAgenticWriter.nonce,
|
|
},
|
|
body: JSON.stringify({
|
|
post_id: postId,
|
|
agent_image_id: imageId,
|
|
prompt: image.prompt_edited || image.prompt_initial,
|
|
alt: image.alt_text_edited || image.alt_text_initial,
|
|
variant_count: variantCounts[imageId] || 2,
|
|
}),
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
const detailedError = await extractApiErrorMessage(
|
|
response,
|
|
`Failed to generate image: ${imageId}`
|
|
);
|
|
throw new Error(detailedError);
|
|
}
|
|
|
|
const result = await response.json();
|
|
const imageWithVariants = { ...image, variants: result.variants || [] };
|
|
generated.push(imageWithVariants);
|
|
|
|
setImages(prev => prev.map(img =>
|
|
img.agent_image_id === imageId
|
|
? imageWithVariants
|
|
: img
|
|
));
|
|
}
|
|
|
|
setGeneratedImages(generated);
|
|
setStep('selecting');
|
|
} catch (err) {
|
|
setError(err.message);
|
|
setStep('review');
|
|
} finally {
|
|
setIsGenerating(false);
|
|
}
|
|
};
|
|
|
|
const handleSelectVariant = async (imageId, variantId) => {
|
|
const image = generatedImages.find(img => img.agent_image_id === imageId)
|
|
|| images.find(img => img.agent_image_id === imageId);
|
|
|
|
try {
|
|
setCommittingVariantId(variantId);
|
|
const response = await fetch(
|
|
`${wpAgenticWriter.apiUrl}/commit-image`,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-WP-Nonce': wpAgenticWriter.nonce,
|
|
},
|
|
body: JSON.stringify({
|
|
post_id: postId,
|
|
agent_image_id: imageId,
|
|
variant_id: variantId,
|
|
alt: image.alt_text_edited || image.alt_text_initial,
|
|
}),
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(await extractApiErrorMessage(response, 'Failed to commit image'));
|
|
}
|
|
|
|
const result = await response.json();
|
|
|
|
const didUpdateBlock = updateGutenbergBlock(imageId, result);
|
|
|
|
setImages(prev => prev.map(img =>
|
|
img.agent_image_id === imageId
|
|
? { ...img, status: 'committed', attachment_id: result.attachment_id }
|
|
: img
|
|
));
|
|
setGeneratedImages(prev => prev.map(img =>
|
|
img.agent_image_id === imageId
|
|
? { ...img, status: 'committed', attachment_id: result.attachment_id }
|
|
: img
|
|
));
|
|
if (!didUpdateBlock) {
|
|
throw new Error('Image committed to Media Library, but the matching image block could not be found.');
|
|
}
|
|
onComplete();
|
|
} catch (err) {
|
|
alert('Failed to commit image: ' + err.message);
|
|
setCommittingVariantId(null);
|
|
}
|
|
};
|
|
|
|
const handleGenerateMore = async (imageId) => {
|
|
const image = generatedImages.find(img => img.agent_image_id === imageId)
|
|
|| images.find(img => img.agent_image_id === imageId);
|
|
if (!image) {
|
|
alert('Image recommendation not found.');
|
|
return;
|
|
}
|
|
|
|
const moreCount = extraVariantCounts[imageId] || 1;
|
|
setGeneratingMoreFor(imageId);
|
|
try {
|
|
const response = await fetch(
|
|
`${wpAgenticWriter.apiUrl}/generate-image`,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-WP-Nonce': wpAgenticWriter.nonce,
|
|
},
|
|
body: JSON.stringify({
|
|
post_id: postId,
|
|
agent_image_id: imageId,
|
|
prompt: image.prompt_edited || image.prompt_initial,
|
|
alt: image.alt_text_edited || image.alt_text_initial,
|
|
variant_count: moreCount,
|
|
}),
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(await extractApiErrorMessage(response, `Failed to generate more variants for ${imageId}`));
|
|
}
|
|
|
|
const result = await response.json();
|
|
const newVariants = Array.isArray(result?.variants) ? result.variants : [];
|
|
if (newVariants.length === 0) {
|
|
throw new Error('No additional variants returned.');
|
|
}
|
|
|
|
setGeneratedImages(prev => prev.map(img =>
|
|
img.agent_image_id === imageId
|
|
? { ...img, variants: [...(img.variants || []), ...newVariants] }
|
|
: img
|
|
));
|
|
setImages(prev => prev.map(img =>
|
|
img.agent_image_id === imageId
|
|
? { ...img, variants: [...(img.variants || []), ...newVariants] }
|
|
: img
|
|
));
|
|
} catch (err) {
|
|
alert('Failed to generate more variants: ' + err.message);
|
|
} finally {
|
|
setGeneratingMoreFor(null);
|
|
}
|
|
};
|
|
|
|
const extractApiErrorMessage = async (response, fallback) => {
|
|
try {
|
|
const data = await response.clone().json();
|
|
const message = data?.message || data?.error || '';
|
|
const details = data?.details || data?.reason || data?.provider_error || '';
|
|
const trace = data?.data?.trace || data?.trace || null;
|
|
const traceText = formatTraceForDisplay(trace);
|
|
const combined = [message, details, traceText].filter(Boolean).join('\n\n').trim();
|
|
if (combined) {
|
|
return combined;
|
|
}
|
|
} catch (jsonError) {
|
|
// Continue to text fallback.
|
|
}
|
|
|
|
try {
|
|
const text = (await response.text()).trim();
|
|
if (text) {
|
|
return text;
|
|
}
|
|
} catch (textError) {
|
|
// Ignore read error.
|
|
}
|
|
|
|
return fallback;
|
|
};
|
|
|
|
const formatTraceForDisplay = (trace) => {
|
|
if (!trace || typeof trace !== 'object') {
|
|
return '';
|
|
}
|
|
|
|
const openRouterMessage = trace.openrouter_response?.error?.message
|
|
|| trace.openrouter_response?.message
|
|
|| '';
|
|
const lines = [
|
|
'Trace:',
|
|
`selected_model: ${trace.selected_model || ''}`,
|
|
`settings_image_model: ${trace.settings_image_model || ''}`,
|
|
`image_task_provider: ${trace.image_task_provider || ''}`,
|
|
`custom_model_match: ${trace.custom_model_match ? 'yes' : 'no'}`,
|
|
`custom_model_ids: ${Array.isArray(trace.custom_model_ids) ? trace.custom_model_ids.join(', ') : ''}`,
|
|
`model_cache_loaded: ${trace.model_cache_loaded ? 'yes' : 'no'}`,
|
|
`model_cache_has_model: ${trace.model_cache_has_model ? 'yes' : 'no'}`,
|
|
`refreshed_has_model: ${trace.refreshed_has_model === null ? 'n/a' : (trace.refreshed_has_model ? 'yes' : 'no')}`,
|
|
`endpoint: ${trace.endpoint || ''}`,
|
|
`request_model: ${trace.request_model || ''}`,
|
|
`request_size: ${trace.request_size || ''}`,
|
|
`request_quality: ${trace.request_quality || ''}`,
|
|
`request_n: ${trace.request_n || ''}`,
|
|
`openrouter_http: ${trace.openrouter_http || ''}`,
|
|
];
|
|
|
|
if (openRouterMessage) {
|
|
lines.push(`openrouter_message: ${openRouterMessage}`);
|
|
}
|
|
|
|
return lines.filter((line) => !line.endsWith(': ')).join('\n');
|
|
};
|
|
|
|
const updateGutenbergBlock = (agentImageId, attachmentData) => {
|
|
const editor = wp.data.select('core/block-editor');
|
|
const dispatcher = wp.data.dispatch('core/block-editor');
|
|
const blocks = editor.getBlocks();
|
|
const updateBlock = (clientId) => {
|
|
if (!clientId) {
|
|
return false;
|
|
}
|
|
dispatcher.updateBlockAttributes(
|
|
clientId,
|
|
{
|
|
id: attachmentData.attachment_id,
|
|
url: attachmentData.attachment_url,
|
|
alt: attachmentData.alt,
|
|
caption: '',
|
|
'data-agent-image-id': agentImageId,
|
|
}
|
|
);
|
|
return true;
|
|
};
|
|
|
|
const findAndUpdateBlock = (blocks) => {
|
|
for (const block of blocks) {
|
|
if (block.name === 'core/image' &&
|
|
block.attributes['data-agent-image-id'] === agentImageId) {
|
|
return updateBlock(block.clientId);
|
|
}
|
|
|
|
if (block.innerBlocks && block.innerBlocks.length > 0) {
|
|
if (findAndUpdateBlock(block.innerBlocks)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if (findAndUpdateBlock(blocks)) {
|
|
return true;
|
|
}
|
|
|
|
const initialBlock = initialBlockId ? editor.getBlock(initialBlockId) : null;
|
|
if (initialBlock?.name === 'core/image') {
|
|
return updateBlock(initialBlockId);
|
|
}
|
|
|
|
const selectedBlock = editor.getSelectedBlock();
|
|
if (selectedBlock?.name === 'core/image') {
|
|
return updateBlock(selectedBlock.clientId);
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
if (step === 'loading') {
|
|
return wp.element.createElement(Modal, {
|
|
title: 'Loading Image Recommendations',
|
|
onRequestClose: onClose,
|
|
},
|
|
wp.element.createElement('div', { style: { padding: '20px', textAlign: 'center' } },
|
|
wp.element.createElement(Spinner)
|
|
)
|
|
);
|
|
}
|
|
|
|
if (step === 'review') {
|
|
return wp.element.createElement(Modal, {
|
|
title: `Image Recommendations (${scopedImages.length})`,
|
|
onRequestClose: onClose,
|
|
style: { maxWidth: '800px' },
|
|
},
|
|
wp.element.createElement('div', { className: 'wpaw-image-review' },
|
|
error && wp.element.createElement('div', {
|
|
className: 'notice notice-error',
|
|
style: { marginBottom: '20px' }
|
|
}, error),
|
|
|
|
scopedImages.length === 0 && wp.element.createElement('div', {
|
|
style: {
|
|
padding: '40px 20px',
|
|
textAlign: 'center',
|
|
color: '#666',
|
|
}
|
|
},
|
|
wp.element.createElement('p', { style: { fontSize: '16px', marginBottom: '10px' } },
|
|
'📷 No image recommendations available'
|
|
),
|
|
wp.element.createElement('p', { style: { fontSize: '14px', marginBottom: '20px' } },
|
|
'Images are generated during article writing. You can add images manually or generate them later.'
|
|
),
|
|
wp.element.createElement(Button, {
|
|
variant: 'primary',
|
|
onClick: onClose,
|
|
}, 'Continue Without Images')
|
|
),
|
|
|
|
scopedImages.map(image =>
|
|
wp.element.createElement('div', {
|
|
key: image.agent_image_id,
|
|
className: 'wpaw-image-card',
|
|
style: {
|
|
border: '1px solid #ddd',
|
|
padding: '15px',
|
|
marginBottom: '15px',
|
|
borderRadius: '4px',
|
|
},
|
|
},
|
|
wp.element.createElement('h3', null,
|
|
`Image: ${image.section_title || image.placement}`
|
|
),
|
|
|
|
wp.element.createElement(TextareaControl, {
|
|
label: 'Prompt',
|
|
value: image.prompt_edited || image.prompt_initial,
|
|
onChange: (value) => handleEditPrompt(image.agent_image_id, value),
|
|
rows: 3,
|
|
}),
|
|
|
|
wp.element.createElement(TextControl, {
|
|
label: 'Alt Text',
|
|
value: image.alt_text_edited || image.alt_text_initial,
|
|
onChange: (value) => handleEditAlt(image.agent_image_id, value),
|
|
}),
|
|
|
|
wp.element.createElement('div', {
|
|
style: { marginTop: '10px', marginBottom: '10px' }
|
|
},
|
|
wp.element.createElement('label', {
|
|
style: { display: 'block', marginBottom: '5px', fontWeight: '600' }
|
|
}, 'Variant Count'),
|
|
wp.element.createElement('select', {
|
|
value: variantCounts[image.agent_image_id] || 2,
|
|
onChange: (e) => handleVariantCountChange(image.agent_image_id, e.target.value),
|
|
style: {
|
|
padding: '5px',
|
|
borderRadius: '3px',
|
|
border: '1px solid #ddd',
|
|
minWidth: '150px'
|
|
}
|
|
},
|
|
wp.element.createElement('option', { value: '1' }, '1 variant'),
|
|
wp.element.createElement('option', { value: '2' }, '2 variants'),
|
|
wp.element.createElement('option', { value: '3' }, '3 variants')
|
|
),
|
|
wp.element.createElement('p', {
|
|
style: { fontSize: '12px', color: '#666', margin: '5px 0 0' }
|
|
}, `Cost: ~$${((variantCounts[image.agent_image_id] || 2) * 0.03).toFixed(3)}`)
|
|
),
|
|
|
|
wp.element.createElement('label', null,
|
|
wp.element.createElement('input', {
|
|
type: 'checkbox',
|
|
checked: selectedImages.includes(image.agent_image_id),
|
|
onChange: (e) => {
|
|
if (e.target.checked) {
|
|
setSelectedImages(prev => [...prev, image.agent_image_id]);
|
|
} else {
|
|
setSelectedImages(prev => prev.filter(id => id !== image.agent_image_id));
|
|
}
|
|
},
|
|
}),
|
|
' Generate this image'
|
|
)
|
|
)
|
|
),
|
|
|
|
wp.element.createElement('div', {
|
|
style: {
|
|
marginTop: '20px',
|
|
display: 'flex',
|
|
gap: '10px',
|
|
justifyContent: 'flex-end',
|
|
}
|
|
},
|
|
wp.element.createElement(Button, {
|
|
variant: 'secondary',
|
|
onClick: onClose,
|
|
}, 'Skip Images'),
|
|
|
|
wp.element.createElement(Button, {
|
|
variant: 'primary',
|
|
onClick: handleGenerateSelected,
|
|
disabled: selectedImages.length === 0 || isGenerating,
|
|
}, `Generate ${calculateTotalVariants()} Image(s) (~$${calculateTotalCost()})`)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
if (step === 'generating') {
|
|
return wp.element.createElement(Modal, {
|
|
title: 'Generating Images',
|
|
onRequestClose: () => {},
|
|
},
|
|
wp.element.createElement('div', { style: { padding: '20px', textAlign: 'center' } },
|
|
wp.element.createElement(Spinner),
|
|
wp.element.createElement('p', null,
|
|
`Generating images... This may take a minute.`
|
|
),
|
|
wp.element.createElement('p', { style: { fontSize: '12px', color: '#666' } },
|
|
`Estimated cost: $${calculateTotalCost()}`
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
if (step === 'selecting') {
|
|
const selectableImages = generatedImages.filter(img => img.variants && img.variants.length > 0);
|
|
return wp.element.createElement(Modal, {
|
|
title: 'Select Image Variants',
|
|
onRequestClose: onClose,
|
|
style: { maxWidth: '900px' },
|
|
},
|
|
wp.element.createElement('div', { className: 'wpaw-variant-selection' },
|
|
selectableImages.length === 0 && wp.element.createElement('div', {
|
|
style: { padding: '24px', color: '#666' }
|
|
}, 'No generated variants were returned. Please try generating again.'),
|
|
selectableImages
|
|
.map(image =>
|
|
wp.element.createElement('div', {
|
|
key: image.agent_image_id,
|
|
style: { marginBottom: '30px' },
|
|
},
|
|
wp.element.createElement('h3', null, image.section_title),
|
|
wp.element.createElement('div', {
|
|
style: {
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '8px',
|
|
marginBottom: '10px',
|
|
}
|
|
},
|
|
wp.element.createElement('label', { style: { fontSize: '12px', color: '#555' } }, 'Generate More'),
|
|
wp.element.createElement('select', {
|
|
value: extraVariantCounts[image.agent_image_id] || 1,
|
|
onChange: (e) => setExtraVariantCounts(prev => ({
|
|
...prev,
|
|
[image.agent_image_id]: parseInt(e.target.value, 10)
|
|
})),
|
|
disabled: generatingMoreFor !== null || committingVariantId !== null,
|
|
style: {
|
|
padding: '4px 6px',
|
|
borderRadius: '4px',
|
|
border: '1px solid #ddd',
|
|
minWidth: '60px'
|
|
}
|
|
},
|
|
wp.element.createElement('option', { value: '1' }, '+1'),
|
|
wp.element.createElement('option', { value: '2' }, '+2'),
|
|
wp.element.createElement('option', { value: '3' }, '+3')
|
|
),
|
|
wp.element.createElement(Button, {
|
|
variant: 'secondary',
|
|
onClick: () => handleGenerateMore(image.agent_image_id),
|
|
disabled: generatingMoreFor !== null || committingVariantId !== null,
|
|
}, generatingMoreFor === image.agent_image_id ? 'Generating...' : 'Generate More')
|
|
),
|
|
|
|
wp.element.createElement('div', {
|
|
style: {
|
|
display: 'grid',
|
|
gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',
|
|
gap: '15px',
|
|
},
|
|
},
|
|
image.variants.map(variant =>
|
|
wp.element.createElement('div', {
|
|
key: variant.id,
|
|
style: {
|
|
border: '1px solid #ddd',
|
|
borderRadius: '4px',
|
|
overflow: 'hidden',
|
|
},
|
|
},
|
|
wp.element.createElement('img', {
|
|
src: variant.temp_file_url,
|
|
alt: 'Variant',
|
|
style: { width: '100%', display: 'block' },
|
|
}),
|
|
|
|
wp.element.createElement('div', { style: { padding: '10px' } },
|
|
wp.element.createElement('p', { style: { fontSize: '12px', margin: '0 0 10px' } },
|
|
`Cost: $${variant.cost.toFixed(3)} • ${Math.round(Number(variant.generation_time) || 0)}s`
|
|
),
|
|
|
|
wp.element.createElement(Button, {
|
|
variant: 'primary',
|
|
onClick: () => handleSelectVariant(image.agent_image_id, variant.id),
|
|
disabled: committingVariantId !== null,
|
|
style: { width: '100%' },
|
|
}, committingVariantId === variant.id ? 'Selecting...' : 'Select')
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
),
|
|
|
|
wp.element.createElement('div', {
|
|
style: { marginTop: '20px', textAlign: 'right' },
|
|
},
|
|
wp.element.createElement(Button, {
|
|
variant: 'secondary',
|
|
onClick: onComplete,
|
|
}, 'Done')
|
|
)
|
|
)
|
|
);
|
|
}
|
|
};
|
|
|
|
// Initialize modal container and event listeners
|
|
let modalContainer = null;
|
|
let currentModalInstance = null;
|
|
let modalRoot = null;
|
|
|
|
const mountModal = (element) => {
|
|
if (typeof createRoot === 'function') {
|
|
if (!modalRoot) {
|
|
modalRoot = createRoot(modalContainer);
|
|
}
|
|
modalRoot.render(element);
|
|
return;
|
|
}
|
|
render(element, modalContainer);
|
|
};
|
|
|
|
const unmountModal = () => {
|
|
if (modalRoot && typeof modalRoot.unmount === 'function') {
|
|
modalRoot.unmount();
|
|
modalRoot = null;
|
|
} else if (modalContainer) {
|
|
render(null, modalContainer);
|
|
}
|
|
currentModalInstance = null;
|
|
};
|
|
|
|
/**
|
|
* Open image modal for review after article generation
|
|
*/
|
|
window.addEventListener('wpaw:open-image-review-modal', (event) => {
|
|
const { postId, imageCount } = event.detail;
|
|
|
|
if (!modalContainer) {
|
|
modalContainer = document.createElement('div');
|
|
modalContainer.id = 'wpaw-image-modal-root';
|
|
document.body.appendChild(modalContainer);
|
|
}
|
|
|
|
currentModalInstance = wp.element.createElement(window.wpAgenticWriter.ImageReviewModal, {
|
|
postId: postId,
|
|
onClose: () => {
|
|
unmountModal();
|
|
},
|
|
onComplete: () => {
|
|
unmountModal();
|
|
},
|
|
});
|
|
mountModal(currentModalInstance);
|
|
});
|
|
|
|
/**
|
|
* Open image modal for single image from toolbar
|
|
*/
|
|
window.addEventListener('wpaw:open-image-modal', (event) => {
|
|
const { agentImageId, blockId } = event.detail;
|
|
const postId = wp.data.select('core/editor').getCurrentPostId();
|
|
|
|
if (!modalContainer) {
|
|
modalContainer = document.createElement('div');
|
|
modalContainer.id = 'wpaw-image-modal-root';
|
|
document.body.appendChild(modalContainer);
|
|
}
|
|
|
|
currentModalInstance = wp.element.createElement(window.wpAgenticWriter.ImageReviewModal, {
|
|
postId: postId,
|
|
initialImageId: agentImageId,
|
|
initialBlockId: blockId,
|
|
onClose: () => {
|
|
unmountModal();
|
|
},
|
|
onComplete: () => {
|
|
unmountModal();
|
|
},
|
|
});
|
|
mountModal(currentModalInstance);
|
|
});
|
|
})();
|