diff --git a/projects/plugins/jetpack/changelog/update-jetpack-ai-image-image-block-entrypoint b/projects/plugins/jetpack/changelog/update-jetpack-ai-image-image-block-entrypoint new file mode 100644 index 0000000000000..661378aac3046 --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-jetpack-ai-image-image-block-entrypoint @@ -0,0 +1,4 @@ +Significance: minor +Type: other + +Jetpack AI Image: include new entrypoint as a button on the image/gallery/slideshow block. diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/index.ts b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/index.ts index 96dae8e4fb4e2..8eda7aef961f4 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/index.ts +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/index.ts @@ -1,5 +1,10 @@ import FeaturedImage from './featured-image'; import GeneralPurposeImage from './general-purpose-image'; -import { PLACEMENT_MEDIA_SOURCE_DROPDOWN } from './types'; +import { PLACEMENT_MEDIA_SOURCE_DROPDOWN, PLACEMENT_BLOCK_PLACEHOLDER_BUTTON } from './types'; -export { FeaturedImage, PLACEMENT_MEDIA_SOURCE_DROPDOWN, GeneralPurposeImage }; +export { + FeaturedImage, + PLACEMENT_MEDIA_SOURCE_DROPDOWN, + PLACEMENT_BLOCK_PLACEHOLDER_BUTTON, + GeneralPurposeImage, +}; diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/types.ts b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/types.ts index effb62dd27e07..f0c0c5ae3d125 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/types.ts +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/types.ts @@ -3,3 +3,4 @@ export const GENERAL_IMAGE_FEATURE_NAME = 'general-image' as const; export const IMAGE_GENERATION_MODEL_STABLE_DIFFUSION = 'stable-diffusion' as const; export const IMAGE_GENERATION_MODEL_DALL_E_3 = 'dall-e-3' as const; export const PLACEMENT_MEDIA_SOURCE_DROPDOWN = 'media-source-dropdown' as const; +export const PLACEMENT_BLOCK_PLACEHOLDER_BUTTON = 'block-placeholder-button' as const; diff --git a/projects/plugins/jetpack/extensions/shared/external-media/constants.js b/projects/plugins/jetpack/extensions/shared/external-media/constants.js index 731b27676c815..43e68812377e7 100644 --- a/projects/plugins/jetpack/extensions/shared/external-media/constants.js +++ b/projects/plugins/jetpack/extensions/shared/external-media/constants.js @@ -8,7 +8,10 @@ export const SOURCE_OPENVERSE = 'openverse'; export const SOURCE_PEXELS = 'pexels'; export const SOURCE_JETPACK_APP_MEDIA = 'jetpack_app_media'; export const SOURCE_JETPACK_AI_FEATURED_IMAGE = 'jetpack_ai_featured_image'; -export const SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE = 'jetpack_ai_general_purpose_image'; +export const SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE_FOR_MEDIA_SOURCE = + 'jetpack_ai_general_purpose_image_for_media_source'; +export const SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE_FOR_BLOCK = + 'jetpack_ai_general_purpose_image_for_block'; export const PATH_RECENT = 'recent'; export const PATH_ROOT = '/'; diff --git a/projects/plugins/jetpack/extensions/shared/external-media/editor.scss b/projects/plugins/jetpack/extensions/shared/external-media/editor.scss index b66031d31ebb7..deaa53f64996f 100644 --- a/projects/plugins/jetpack/extensions/shared/external-media/editor.scss +++ b/projects/plugins/jetpack/extensions/shared/external-media/editor.scss @@ -558,15 +558,17 @@ $grid-size: 8px; } } +// Set the wrapper as a flex container to allow displaying multiple buttons side by side. +.jetpack-external-media-button-wrapper { + display: flex; +} + // Reset placeholder button margin. .components-placeholder__fieldset, .editor-post-featured-image { .components-dropdown .jetpack-external-media-button-menu { width: auto; - margin-right: 8px; margin-bottom: 1em; - padding-left: 6px; - padding-right: 6px; > svg { display: none; diff --git a/projects/plugins/jetpack/extensions/shared/external-media/media-button/index.js b/projects/plugins/jetpack/extensions/shared/external-media/media-button/index.js index 48018e8a2f521..be15c29bb2a44 100644 --- a/projects/plugins/jetpack/extensions/shared/external-media/media-button/index.js +++ b/projects/plugins/jetpack/extensions/shared/external-media/media-button/index.js @@ -1,5 +1,7 @@ +import { useBlockEditContext } from '@wordpress/block-editor'; import { useState } from '@wordpress/element'; import { getExternalLibrary } from '../sources'; +import MediaAiButton from './media-ai-button'; import MediaButtonMenu from './media-menu'; const isFeaturedImage = props => @@ -7,10 +9,25 @@ const isFeaturedImage = props => ( props.modalClass && props.modalClass.indexOf( 'featured-image' ) !== -1 ); const isReplaceMenu = props => props.multiple === undefined && ! isFeaturedImage( props ); +const blocksWithAiButtonSupport = [ 'core/image', 'core/gallery', 'jetpack/slideshow' ]; + +/** + * Temporary feature flag to control generalPurposeImageExclusiveMediaSources + * visibility. + */ +const GENERAL_PURPOSE_IMAGE_GENERATOR_BETA_FLAG = 'ai-general-purpose-image-generator'; +const isGeneralPurposeImageGeneratorBetaEnabled = + window?.Jetpack_Editor_Initial_State?.available_blocks?.[ + GENERAL_PURPOSE_IMAGE_GENERATOR_BETA_FLAG + ]?.available === true; + function MediaButton( props ) { + const { name } = useBlockEditContext(); const { mediaProps } = props; const [ selectedSource, setSelectedSource ] = useState( null ); const ExternalLibrary = getExternalLibrary( selectedSource ); + const isFeatured = isFeaturedImage( mediaProps ); + const hasAiButtonSupport = blocksWithAiButtonSupport.includes( name ); const closeLibrary = event => { if ( event ) { @@ -29,14 +46,20 @@ function MediaButton( props ) { return ( // No added functionality, just capping event propagation. // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -
event.stopPropagation() }> +
event.stopPropagation() } + className="jetpack-external-media-button-wrapper" + > 0 } /> + { isGeneralPurposeImageGeneratorBetaEnabled && ! isFeatured && hasAiButtonSupport && ( + + ) } { ExternalLibrary && }
diff --git a/projects/plugins/jetpack/extensions/shared/external-media/media-button/media-ai-button.js b/projects/plugins/jetpack/extensions/shared/external-media/media-button/media-ai-button.js new file mode 100644 index 0000000000000..b2421d0587c0b --- /dev/null +++ b/projects/plugins/jetpack/extensions/shared/external-media/media-button/media-ai-button.js @@ -0,0 +1,23 @@ +import { Button } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE_FOR_BLOCK } from '../constants'; + +function MediaAiButton( props ) { + const { setSelectedSource } = props; + return ( + + ); +} + +export default MediaAiButton; diff --git a/projects/plugins/jetpack/extensions/shared/external-media/sources/index.js b/projects/plugins/jetpack/extensions/shared/external-media/sources/index.js index ea3221d0ecaaf..f150a47ed609c 100644 --- a/projects/plugins/jetpack/extensions/shared/external-media/sources/index.js +++ b/projects/plugins/jetpack/extensions/shared/external-media/sources/index.js @@ -8,11 +8,13 @@ import { SOURCE_PEXELS, SOURCE_JETPACK_APP_MEDIA, SOURCE_JETPACK_AI_FEATURED_IMAGE, - SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE, + SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE_FOR_MEDIA_SOURCE, + SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE_FOR_BLOCK, } from '../constants'; import GooglePhotosMedia from './google-photos'; import JetpackAIFeaturedImage from './jetpack-ai-featured-image'; -import JetpackAIGeneralPurposeImage from './jetpack-ai-general-purpose-image'; +import JetpackAIGeneralPurposeImageForBlock from './jetpack-ai-general-purpose-image-for-block'; +import JetpackAIGeneralPurposeImageForMediaSource from './jetpack-ai-general-purpose-image-for-media-source'; import JetpackAppMedia from './jetpack-app-media'; import OpenverseMedia from './openverse'; import PexelsMedia from './pexels'; @@ -43,7 +45,7 @@ export const featuredImageExclusiveMediaSources = [ */ export const generalPurposeImageExclusiveMediaSources = [ { - id: SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE, + id: SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE_FOR_MEDIA_SOURCE, label: __( 'Generate with AI', 'jetpack' ), icon: aiAssistantIcon, keyword: 'jetpack ai', @@ -109,8 +111,10 @@ export function getExternalLibrary( type ) { return JetpackAppMedia; } else if ( type === SOURCE_JETPACK_AI_FEATURED_IMAGE ) { return JetpackAIFeaturedImage; - } else if ( type === SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE ) { - return JetpackAIGeneralPurposeImage; + } else if ( type === SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE_FOR_MEDIA_SOURCE ) { + return JetpackAIGeneralPurposeImageForMediaSource; + } else if ( type === SOURCE_JETPACK_AI_GENERAL_PURPOSE_IMAGE_FOR_BLOCK ) { + return JetpackAIGeneralPurposeImageForBlock; } return null; } diff --git a/projects/plugins/jetpack/extensions/shared/external-media/sources/jetpack-ai-general-purpose-image-for-block.js b/projects/plugins/jetpack/extensions/shared/external-media/sources/jetpack-ai-general-purpose-image-for-block.js new file mode 100644 index 0000000000000..1dcdfc1b76743 --- /dev/null +++ b/projects/plugins/jetpack/extensions/shared/external-media/sources/jetpack-ai-general-purpose-image-for-block.js @@ -0,0 +1,20 @@ +import { + GeneralPurposeImage, + PLACEMENT_BLOCK_PLACEHOLDER_BUTTON, +} from '../../../plugins/ai-assistant-plugin/components/ai-image'; + +function JetpackAIGeneralPurposeImageForBlock( { + onClose = () => {}, + onSelect, + multiple = false, +} ) { + return ( + onSelect( multiple ? [ image ] : image ) } + /> + ); +} + +export default JetpackAIGeneralPurposeImageForBlock; diff --git a/projects/plugins/jetpack/extensions/shared/external-media/sources/jetpack-ai-general-purpose-image.js b/projects/plugins/jetpack/extensions/shared/external-media/sources/jetpack-ai-general-purpose-image-for-media-source.js similarity index 51% rename from projects/plugins/jetpack/extensions/shared/external-media/sources/jetpack-ai-general-purpose-image.js rename to projects/plugins/jetpack/extensions/shared/external-media/sources/jetpack-ai-general-purpose-image-for-media-source.js index e81a88faaf4aa..9cff6275e8518 100644 --- a/projects/plugins/jetpack/extensions/shared/external-media/sources/jetpack-ai-general-purpose-image.js +++ b/projects/plugins/jetpack/extensions/shared/external-media/sources/jetpack-ai-general-purpose-image-for-media-source.js @@ -3,14 +3,18 @@ import { PLACEMENT_MEDIA_SOURCE_DROPDOWN, } from '../../../plugins/ai-assistant-plugin/components/ai-image'; -function JetpackAIGeneralPurposeImage( { onClose = () => {}, onSelect } ) { +function JetpackAIGeneralPurposeImageForMediaSource( { + onClose = () => {}, + onSelect, + multiple = false, +} ) { return ( onSelect( multiple ? [ image ] : image ) } /> ); } -export default JetpackAIGeneralPurposeImage; +export default JetpackAIGeneralPurposeImageForMediaSource;