| 1 |
import { CreditCounter } from '@draft/components/image-generation/CreditCounter'; |
| 2 |
import { useImageGenerationStore } from '@shared/state/generate-images'; |
| 3 |
import { |
| 4 |
Button, |
| 5 |
CheckboxControl, |
| 6 |
TextareaControl, |
| 7 |
__experimentalToggleGroupControl as ToggleGroupControl, |
| 8 |
__experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon, |
| 9 |
} from '@wordpress/components'; |
| 10 |
import { useEffect, useState } from '@wordpress/element'; |
| 11 |
import { __ } from '@wordpress/i18n'; |
| 12 |
|
| 13 |
export const GenerateForm = ({ |
| 14 |
isGenerating, |
| 15 |
errorMessage, |
| 16 |
disclose, |
| 17 |
setDisclose, |
| 18 |
}) => { |
| 19 |
const { imageCredits, resetImageCredits, aiImageOptions, setAiImageOption } = |
| 20 |
useImageGenerationStore(); |
| 21 |
const usedCredits = imageCredits.total - imageCredits.remaining; |
| 22 |
const [refreshCheck, setRefreshCheck] = useState(0); |
| 23 |
const { size, prompt } = aiImageOptions; |
| 24 |
|
| 25 |
useEffect(() => { |
| 26 |
const handle = () => { |
| 27 |
setRefreshCheck((prev) => prev + 1); |
| 28 |
if (!imageCredits.refresh) return; |
| 29 |
if (new Date(Number(imageCredits.refresh)) > new Date()) return; |
| 30 |
resetImageCredits(); |
| 31 |
}; |
| 32 |
if (refreshCheck === 0) handle(); // First run |
| 33 |
const id = setTimeout(handle, 1000); |
| 34 |
return () => clearTimeout(id); |
| 35 |
}, [imageCredits, resetImageCredits, refreshCheck]); |
| 36 |
|
| 37 |
return ( |
| 38 |
<> |
| 39 |
{isGenerating ? null : ( |
| 40 |
<div className="flex flex-col gap-4"> |
| 41 |
<TextareaControl |
| 42 |
id="draft-ai-image-textarea" |
| 43 |
autoFocus |
| 44 |
placeholder={__( |
| 45 |
'Tell AI about the image you would like to create', |
| 46 |
'extendify-local', |
| 47 |
)} |
| 48 |
label={__('Image Prompt', 'extendify-local')} |
| 49 |
hideLabelFromVision |
| 50 |
rows="7" |
| 51 |
value={prompt} |
| 52 |
onChange={(prompt) => setAiImageOption('prompt', prompt)} |
| 53 |
/> |
| 54 |
|
| 55 |
<ToggleGroupControl |
| 56 |
isBlock |
| 57 |
label={__('Aspect Ratio', 'extendify-local')} |
| 58 |
onChange={(size) => setAiImageOption('size', size)} |
| 59 |
value={size} |
| 60 |
> |
| 61 |
<ToggleGroupControlOptionIcon |
| 62 |
className="m-auto" |
| 63 |
type="button" |
| 64 |
icon={AspectRatioSquare} |
| 65 |
label={__('Square: 1:1', 'extendify-local')} |
| 66 |
value="1024x1024" |
| 67 |
/> |
| 68 |
<ToggleGroupControlOptionIcon |
| 69 |
className="m-auto" |
| 70 |
type="button" |
| 71 |
icon={AspectRatioLandscape} |
| 72 |
label={__('Landscape: 4:3', 'extendify-local')} |
| 73 |
value="1536x1024" |
| 74 |
/> |
| 75 |
<ToggleGroupControlOptionIcon |
| 76 |
className="m-auto" |
| 77 |
type="button" |
| 78 |
icon={AspectRatioPortrait} |
| 79 |
label={__('Portrait: 3:4', 'extendify-local')} |
| 80 |
value="1024x1536" |
| 81 |
/> |
| 82 |
</ToggleGroupControl> |
| 83 |
|
| 84 |
<CheckboxControl |
| 85 |
__nextHasNoMarginBottom |
| 86 |
// translators: Checkbox that adds a visible "AI Generated" mark onto the image. |
| 87 |
label={__('Label image as AI-generated', 'extendify-local')} |
| 88 |
checked={disclose} |
| 89 |
onChange={setDisclose} |
| 90 |
/> |
| 91 |
</div> |
| 92 |
)} |
| 93 |
{errorMessage.length > 0 && ( |
| 94 |
<p className="mb-0 text-red-500">{errorMessage}</p> |
| 95 |
)} |
| 96 |
<Button |
| 97 |
type="submit" |
| 98 |
className="w-full justify-center" |
| 99 |
variant="primary" |
| 100 |
__next40pxDefaultSize |
| 101 |
disabled={isGenerating || !prompt || usedCredits >= imageCredits.total} |
| 102 |
> |
| 103 |
{isGenerating |
| 104 |
? __('Generating image...', 'extendify-local') |
| 105 |
: __('Generate image', 'extendify-local')} |
| 106 |
</Button> |
| 107 |
{isGenerating ? null : ( |
| 108 |
<CreditCounter usedCredits={usedCredits} total={imageCredits.total} /> |
| 109 |
)} |
| 110 |
</> |
| 111 |
); |
| 112 |
}; |
| 113 |
|
| 114 |
const AspectRatioLandscape = ( |
| 115 |
<svg xmlns="http://www.w3.org/2000/svg" style={{ padding: '7px 4px' }}> |
| 116 |
<title>{__('Landscape: 4:3', 'extendify-local')}</title> |
| 117 |
<path |
| 118 |
fillRule="evenodd" |
| 119 |
d="M0 1c0-.552285.447715-1 1-1h14c.5523 0 1 .447715 1 1v8c0 .55228-.4477 1-1 1H1c-.552285 0-1-.44772-1-1V1Z" |
| 120 |
clipRule="evenodd" |
| 121 |
/> |
| 122 |
</svg> |
| 123 |
); |
| 124 |
|
| 125 |
const AspectRatioPortrait = ( |
| 126 |
<svg xmlns="http://www.w3.org/2000/svg" style={{ padding: '4px 6px' }}> |
| 127 |
<title>{__('Portrait: 3:4', 'extendify-local')}</title> |
| 128 |
<path |
| 129 |
fillRule="evenodd" |
| 130 |
d="M9.66669 3.5e-7C10.219 3.7e-7 10.6667.447716 10.6667 1v14c0 .5523-.4477 1-1.00001 1h-8c-.55229 0-1.000003-.4477-1.000003-1L.666688 1C.666688.447715 1.1144-2e-8 1.66669 0l8 3.5e-7Z" |
| 131 |
clipRule="evenodd" |
| 132 |
/> |
| 133 |
</svg> |
| 134 |
); |
| 135 |
const AspectRatioSquare = ( |
| 136 |
<svg xmlns="http://www.w3.org/2000/svg" style={{ padding: '6px' }}> |
| 137 |
<title>{__('Square: 1:1', 'extendify-local')}</title> |
| 138 |
<path |
| 139 |
fillRule="evenodd" |
| 140 |
d="M11.3333-4e-8c.5523 2e-8 1 .44771504 1 1.00000004v10c0 .5523-.4477 1-1 1H1.33333c-.552283 0-.999998-.4477-.999998-1V.999999C.333332.447715.781047-5e-7 1.33333-4.8e-7L11.3333-4e-8Z" |
| 141 |
clipRule="evenodd" |
| 142 |
/> |
| 143 |
</svg> |
| 144 |
); |
| 145 |
|