namespace Elementor\Modules\Ai;
use Elementor\Controls_Manager;
use Elementor\Core\Base\Module as BaseModule;
use Elementor\Core\Common\Modules\Connect\Module as ConnectModule;
use Elementor\Element_Base;
use Elementor\Modules\Ai\Feature_Intro\Product_Image_Unification_Intro;
use Elementor\Core\Utils\Collection;
use Elementor\Modules\Ai\Connect\Ai;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
class Module extends BaseModule {
const HISTORY_TYPE_ALL = 'all';
const HISTORY_TYPE_TEXT = 'text';
const HISTORY_TYPE_CODE = 'code';
const HISTORY_TYPE_IMAGE = 'images';
const HISTORY_TYPE_BLOCK = 'blocks';
const VALID_HISTORY_TYPES = [
self::HISTORY_TYPE_IMAGE,
self::HISTORY_TYPE_BLOCK,
const MIN_PAGES_FOR_CREATE_WITH_AI_BANNER = 10;
public function get_name() {
public function __construct() {
( new SitePlannerConnect\Module() );
( new Preferences() )->register();
add_action( 'elementor/import-export/import-kit/runner/after-run', [ $this, 'handle_kit_install' ] );
if ( ! $this->is_ai_enabled() ) {
add_filter( 'elementor/core/admin/homescreen', [ $this, 'add_create_with_ai_banner_to_homescreen' ] );
add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) {
$connect_module->register_app( 'ai', Ai::get_class_name() );
add_action( 'elementor/ajax/register_actions', function( $ajax ) {
'ai_get_user_information' => [ $this, 'ajax_ai_get_user_information' ],
'ai_get_remote_config' => [ $this, 'ajax_ai_get_remote_config' ],
'ai_get_remote_frontend_config' => [ $this, 'ajax_ai_get_remote_frontend_config' ],
'ai_get_completion_text' => [ $this, 'ajax_ai_get_completion_text' ],
'ai_get_excerpt' => [ $this, 'ajax_ai_get_excerpt' ],
'ai_get_featured_image' => [ $this, 'ajax_ai_get_featured_image' ],
'ai_get_edit_text' => [ $this, 'ajax_ai_get_edit_text' ],
'ai_get_custom_code' => [ $this, 'ajax_ai_get_custom_code' ],
'ai_get_custom_css' => [ $this, 'ajax_ai_get_custom_css' ],
'ai_set_get_started' => [ $this, 'ajax_ai_set_get_started' ],
'ai_set_status_feedback' => [ $this, 'ajax_ai_set_status_feedback' ],
'ai_get_image_prompt_enhancer' => [ $this, 'ajax_ai_get_image_prompt_enhancer' ],
'ai_get_text_to_image' => [ $this, 'ajax_ai_get_text_to_image' ],
'ai_get_image_to_image' => [ $this, 'ajax_ai_get_image_to_image' ],
'ai_get_image_to_image_mask' => [ $this, 'ajax_ai_get_image_to_image_mask' ],
'ai_get_image_to_image_mask_cleanup' => [ $this, 'ajax_ai_get_image_to_image_mask_cleanup' ],
'ai_get_image_to_image_outpainting' => [ $this, 'ajax_ai_get_image_to_image_outpainting' ],
'ai_get_image_to_image_upscale' => [ $this, 'ajax_ai_get_image_to_image_upscale' ],
'ai_get_image_to_image_remove_background' => [ $this, 'ajax_ai_get_image_to_image_remove_background' ],
'ai_get_image_to_image_replace_background' => [ $this, 'ajax_ai_get_image_to_image_replace_background' ],
'ai_upload_image' => [ $this, 'ajax_ai_upload_image' ],
'ai_generate_layout' => [ $this, 'ajax_ai_generate_layout' ],
'ai_get_layout_prompt_enhancer' => [ $this, 'ajax_ai_get_layout_prompt_enhancer' ],
'ai_get_history' => [ $this, 'ajax_ai_get_history' ],
'ai_delete_history_item' => [ $this, 'ajax_ai_delete_history_item' ],
'ai_toggle_favorite_history_item' => [ $this, 'ajax_ai_toggle_favorite_history_item' ],
'ai_get_product_image_unification' => [ $this, 'ajax_ai_get_product_image_unification' ],
'ai_get_animation' => [ $this, 'ajax_ai_get_animation' ],
'ai_get_image_to_image_isolate_objects' => [ $this, 'ajax_ai_get_product_image_unification' ],
foreach ( $handlers as $tag => $callback ) {
$ajax->register_ajax_action( $tag, $callback );
add_action( 'elementor/editor/before_enqueue_scripts', function() {
$this->enqueue_main_script();
$this->enqueue_layout_script();
add_action( 'elementor/editor/after_enqueue_styles', function() {
$this->get_css_assets_url( 'modules/ai/editor' ),
add_action( 'elementor/preview/enqueue_styles', function() {
'elementor-ai-layout-preview',
$this->get_css_assets_url( 'modules/ai/layout-preview' ),
add_action( 'wp_enqueue_media', [ $this, 'enqueue_ai_media_library' ] );
add_action( 'admin_head', [ $this, 'enqueue_ai_media_library_upload_screen' ] );
if ( current_user_can( 'edit_products' ) || current_user_can( 'publish_products' ) ) {
add_action( 'admin_init', [ $this, 'enqueue_ai_products_page_scripts' ] );
add_action( 'current_screen', [ $this, 'enqueue_ai_single_product_page_scripts' ] );
add_action( 'wp_ajax_elementor-ai-get-product-images', [ $this, 'get_product_images_ajax' ] );
add_action( 'wp_ajax_elementor-ai-set-product-images', [ $this, 'set_product_images_ajax' ] );
Product_Image_Unification_Intro::add_hooks();
add_action( 'enqueue_block_editor_assets', function() {
wp_enqueue_script( 'elementor-ai-gutenberg',
$this->get_js_assets_url( 'ai-gutenberg' ),
'elementor-ai-media-library',
ELEMENTOR_VERSION, true );
'elementor-ai-gutenberg',
'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
'connect_url' => $this->get_ai_connect_url(),
wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' );
add_filter( 'elementor/document/save/data', function ( $data ) {
return $this->remove_temporary_containers( $data );
add_action( 'elementor/element/common/section_effects/after_section_start', [ $this, 'register_ai_motion_effect_control' ], 10, 1 );
add_action( 'elementor/element/container/section_effects/after_section_start', [ $this, 'register_ai_motion_effect_control' ], 10, 1 );
add_action( 'elementor/element/common/_section_transform/after_section_end', [ $this, 'register_ai_hover_effect_control' ], 10, 1 );
add_action( 'elementor/element/container/_section_transform/after_section_end', [ $this, 'register_ai_hover_effect_control' ], 10, 1 );
public function is_ai_enabled() {
if ( ! Plugin::$instance->experiments->is_feature_active( 'container' ) ) {
return Preferences::is_ai_enabled( get_current_user_id() );
public function handle_kit_install( $imported_data ) {
if ( ! $this->is_ai_enabled() ) {
if ( ! isset( $imported_data['status'] ) || 'success' !== $imported_data['status'] ) {
if ( ! isset( $imported_data['runner'] ) || 'site-settings' !== $imported_data['runner'] ) {
if ( ! isset( $imported_data['configData']['lastImportedSession']['instance_data']['site_settings']['settings']['ai'] ) ) {
$is_connected = $this->get_ai_app()->is_connected() && User::get_introduction_meta( 'ai_get_started' );
$last_imported_session = $imported_data['configData']['lastImportedSession'];
$imported_ai_data = $last_imported_session['instance_data']['site_settings']['settings']['ai'];
$this->get_ai_app()->send_event( [
'name' => 'kit_installed',
'data' => $imported_ai_data,
'version' => ELEMENTOR_VERSION,
'session_id' => $last_imported_session['session_id'],
public function register_ai_hover_effect_control( Element_Base $element ) {
if ( ! $element->get_controls( 'ai_hover_animation' ) ) {
'tabs_wrapper' => '_tabs_positioning',
'inner_tab' => '_tab_positioning_hover',
'label' => esc_html__( 'Animate With AI', 'elementor' ),
'type' => Controls_Manager::RAW_HTML,
.elementor-control-ai_hover_animation .elementor-control-content {
justify-content: space-between;
.elementor-control-ai_hover_animation .elementor-control-raw-html {
'type' => 'hover_animation',
'of' => '_transform_rotate_popover_hover',
public function register_ai_motion_effect_control( $element ) {
if ( Utils::has_pro() && ! $element->get_controls( 'ai_animation' ) ) {
'label' => esc_html__( 'Animate With AI', 'elementor' ),
'type' => Controls_Manager::RAW_HTML,
.elementor-control-ai_animation .elementor-control-content {
justify-content: space-between;
.elementor-control-ai_animation .elementor-control-raw-html {
private function get_current_screen() {
$is_wc = class_exists( 'WooCommerce' ) && post_type_exists( 'product' );
$is_products_page = isset( $_GET['post_type'] ) && 'product' === $_GET['post_type'];
if ( $is_products_page ) {
$screen = get_current_screen();
$is_single_product_page = isset( $screen->post_type ) && ( 'product' === $screen->post_type && 'post' === $screen->base );
if ( $is_single_product_page ) {
return 'wc-single-product';
public function enqueue_ai_products_page_scripts() {
if ( 'wc-products' !== $this->get_current_screen() ) {
public function enqueue_ai_single_product_page_scripts() {
if ( 'wc-single-product' !== $this->get_current_screen() ) {
private function add_products_bulk_action( $bulk_actions ) {
$bulk_actions['elementor-ai-unify-product-images'] = __( 'Unify with Elementor AI', 'elementor' );
public function get_product_images_ajax() {
check_ajax_referer( 'elementor-ai-unify-product-images_nonce', 'nonce' );
$post_ids = isset( $_POST['post_ids'] ) ? array_map( 'intval', $_POST['post_ids'] ) : [];
$is_galley_only = isset( $_POST['is_galley_only'] ) && sanitize_text_field( wp_unslash( $_POST['is_galley_only'] ) );
foreach ( $post_ids as $post_id ) {
$product = wc_get_product( $post_id );
$gallery_image_ids = $product->get_gallery_image_ids();
foreach ( $gallery_image_ids as $image_id ) {
'image_url' => wp_get_attachment_url( $image_id ),
$image_id = get_post_thumbnail_id( $post_id );
$product = wc_get_product( $post_id );
$gallery_image_ids = $product->get_gallery_image_ids();
if ( ! empty( $gallery_image_ids ) ) {
$image_id = $gallery_image_ids[0];
'id' => $image_id ? $image_id : 'No Image',
'image_url' => $image_id ? wp_get_attachment_url( $image_id ) : 'No Image',
wp_send_json_success( [ 'product_images' => array_slice( $image_ids, 0, 10 ) ] );
private function get_attachment_id_by_url( $url ) {
$attachments = get_posts( [
'post_type' => 'attachment',
'key' => '_wp_attached_file',
'value' => basename( $url ),
return ! empty( $attachments ) ? $attachments[0] : null;
public function set_product_images_ajax() {
check_ajax_referer( 'elementor-ai-unify-product-images_nonce', 'nonce' );
$product_id = isset( $_POST['productId'] ) ? sanitize_text_field( wp_unslash( $_POST['productId'] ) ) : '';
$image_url = isset( $_POST['image_url'] ) ? sanitize_text_field( wp_unslash( $_POST['image_url'] ) ) : '';
$image_to_add = isset( $_POST['image_to_add'] ) ? intval( wp_unslash( $_POST['image_to_add'] ) ) : null;
$image_to_remove = isset( $_POST['image_to_remove'] ) ? intval( wp_unslash( $_POST['image_to_remove'] ) ) : null;
$is_product_gallery = isset( $_POST['is_product_gallery'] ) && sanitize_text_field( wp_unslash( $_POST['is_product_gallery'] ) ) === 'true';
if ( ! $product_id || ! $image_url ) {
throw new \Exception( 'Product ID and Image URL are required' );
$product = wc_get_product( $product_id );
throw new \Exception( 'Product not found' );
$attachment_id = $this->get_attachment_id_by_url( $image_url );
if ( is_wp_error( $attachment_id ) ) {
throw new \Exception( 'Image upload failed' );
if ( $is_product_gallery ) {
$this->update_product_gallery( $product, $image_to_remove, $image_to_add );
$product->set_image_id( $attachment_id );
'message' => __( 'Image added successfully', 'elementor' ),
public function enqueue_ai_media_library_upload_screen() {
$screen = get_current_screen();
if ( ! $screen || 'upload' !== $screen->id ) {
$this->enqueue_ai_media_library();
public function enqueue_ai_media_library() {
wp_enqueue_script( 'elementor-ai-media-library',
$this->get_js_assets_url( 'ai-media-library' ),
'elementor-ai-media-library',
'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
'connect_url' => $this->get_ai_connect_url(),
wp_set_script_translations( 'elementor-ai-media-library', 'elementor' );
private function enqueue_main_script() {
$this->get_js_assets_url( 'ai' ),
'elementor-editor-modules',
'elementor-editor-document',
'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
'connect_url' => $this->get_ai_connect_url(),
wp_set_script_translations( 'elementor-ai', 'elementor' );
private function enqueue_layout_script() {
$this->get_js_assets_url( 'ai-layout' ),
'elementor-editor-modules',