| @@ -1,6 +1,8 @@ | ||
| 1 | 1 | import { AI_HOST } from '@constants'; |
| 2 | +import { renderAiLabelPill, stampAiLabel } from '@shared/lib/ai-label'; | |
| 2 | 3 | import apiFetch from '@wordpress/api-fetch'; |
| 4 | +import { sprintf } from '@wordpress/i18n'; | |
| 3 | 5 | import { addQueryArgs } from '@wordpress/url'; |
| 4 | 6 | |
| 5 | 7 | export const getPlugin = async (slug) => { |
| 6 | 8 | const response = await apiFetch({ |
| @@ -71,8 +73,17 @@ | ||
| 71 | 73 | img.onerror = (e) => reject(e); |
| 72 | 74 | }); |
| 73 | 75 | }; |
| 74 | 76 | |
| 77 | +const shouldDisclose = (metadata) => | |
| 78 | + Boolean(metadata.aiGenerated) && Boolean(metadata.disclose); | |
| 79 | + | |
| 80 | +const altText = (metadata) => { | |
| 81 | + if (!shouldDisclose(metadata)) return metadata.alt ?? ''; | |
| 82 | + const pattern = window.extSharedData?.aiImageAltPattern ?? 'AI Generated: %s'; | |
| 83 | + return sprintf(pattern, metadata.alt ?? '').trim(); | |
| 84 | +}; | |
| 85 | + | |
| 75 | 86 | export const importImage = async (imageUrl, metadata = {}) => { |
| 76 | 87 | const image = new Image(); |
| 77 | 88 | image.src = imageUrl; |
| 78 | 89 | image.crossOrigin = 'anonymous'; |
| @@ -84,8 +95,11 @@ | ||
| 84 | 95 | |
| 85 | 96 | const ctx = canvas.getContext('2d'); |
| 86 | 97 | if (!ctx) return; |
| 87 | 98 | ctx.drawImage(image, 0, 0); |
| 99 | + if (shouldDisclose(metadata)) { | |
| 100 | + stampAiLabel(ctx, canvas.width, canvas.height); | |
| 101 | + } | |
| 88 | 102 | |
| 89 | 103 | const blob = await new Promise((resolve) => { |
| 90 | 104 | canvas.toBlob((blob) => { |
| 91 | 105 | blob && resolve(blob); |
| @@ -93,11 +107,14 @@ | ||
| 93 | 107 | }); |
| 94 | 108 | |
| 95 | 109 | const formData = new FormData(); |
| 96 | 110 | formData.append('file', new File([blob], metadata.filename)); |
| 97 | - formData.append('alt_text', metadata.alt ?? ''); | |
| 111 | + formData.append('alt_text', altText(metadata)); | |
| 98 | 112 | formData.append('caption', metadata.caption ?? ''); |
| 99 | 113 | formData.append('status', 'publish'); |
| 114 | + if (metadata.aiGenerated) { | |
| 115 | + formData.append('meta[extendify_ai_generated]', '1'); | |
| 116 | + } | |
| 100 | 117 | |
| 101 | 118 | return await apiFetch({ |
| 102 | 119 | path: 'wp/v2/media', |
| 103 | 120 | method: 'POST', |
| @@ -108,10 +125,22 @@ | ||
| 108 | 125 | export const importImageServer = async (src, metadata = {}) => { |
| 109 | 126 | const formData = new FormData(); |
| 110 | 127 | formData.append('source', src); |
| 111 | 128 | // Fallback doesn't support custom file_name |
| 112 | - formData.append('alt_text', metadata.alt ?? ''); | |
| 129 | + formData.append('alt_text', altText(metadata)); | |
| 113 | 130 | formData.append('caption', metadata.caption ?? ''); |
| 131 | + if (metadata.aiGenerated) { | |
| 132 | + formData.append('ai_generated', '1'); | |
| 133 | + if (shouldDisclose(metadata)) { | |
| 134 | + const pill = await renderAiLabelPill(); | |
| 135 | + if (pill) { | |
| 136 | + formData.append( | |
| 137 | + 'disclosure_label', | |
| 138 | + new File([pill], 'disclosure-label.png', { type: 'image/png' }), | |
| 139 | + ); | |
| 140 | + } | |
| 141 | + } | |
| 142 | + } | |
| 114 | 143 | |
| 115 | 144 | return await apiFetch({ |
| 116 | 145 | path: '/extendify/v1/draft/upload-image', |
| 117 | 146 | method: 'POST', |
| @@ -126,22 +155,28 @@ | ||
| 126 | 155 | unsplashId, |
| 127 | 156 | metadata = { alt: '', caption: '' }, |
| 128 | 157 | ) => { |
| 129 | 158 | let image; |
| 159 | + const aiGenerated = source === 'ai-generated'; | |
| 130 | 160 | if (unsplashId) { |
| 131 | 161 | await downloadPing(id, source, { unsplashId }); |
| 162 | + } else if (aiGenerated && id) { | |
| 163 | + await downloadPing(id, source, { disclose: metadata.disclose }); | |
| 132 | 164 | } |
| 133 | 165 | try { |
| 134 | 166 | image = await importImage(src, { |
| 135 | 167 | alt: metadata.alt, |
| 136 | - filename: 'image.jpg', | |
| 168 | + filename: metadata.filename ?? 'image.jpg', | |
| 137 | 169 | caption: metadata.caption, |
| 170 | + aiGenerated, | |
| 171 | + disclose: metadata.disclose, | |
| 138 | 172 | }); |
| 139 | 173 | } catch (_e) { |
| 140 | 174 | image = await importImageServer(src, { |
| 141 | 175 | alt: metadata.alt, |
| 142 | - filename: 'image.jpg', | |
| 143 | 176 | caption: metadata.caption, |
| 177 | + aiGenerated, | |
| 178 | + disclose: metadata.disclose, | |
| 144 | 179 | }); |
| 145 | 180 | } |
| 146 | 181 | |
| 147 | 182 | return image; |