diff --git a/.eslintrc.json b/.eslintrc.json
index 58bbb406e..85f09bd41 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -24,5 +24,5 @@
"Block": "readonly"
},
"extends": ["plugin:@wordpress/eslint-plugin/recommended"],
- "ignorePatterns": ["*.json"]
+ "ignorePatterns": ["*.json", "webpack.config.js"]
}
diff --git a/assets/img/block-icon.svg b/assets/img/block-icon.svg
index e74bacb9d..01da9f386 100644
--- a/assets/img/block-icon.svg
+++ b/assets/img/block-icon.svg
@@ -1,3 +1,3 @@
-
+
diff --git a/includes/Classifai/Features/AudioTranscriptsGeneration.php b/includes/Classifai/Features/AudioTranscriptsGeneration.php
index 13a4b6d54..bc9c4ba99 100644
--- a/includes/Classifai/Features/AudioTranscriptsGeneration.php
+++ b/includes/Classifai/Features/AudioTranscriptsGeneration.php
@@ -140,10 +140,10 @@ public function rest_endpoint_callback( WP_REST_Request $request ) {
*/
public function enqueue_admin_assets() {
wp_enqueue_script(
- 'classifai-media-script',
- CLASSIFAI_PLUGIN_URL . 'dist/media.js',
- array_merge( get_asset_info( 'media', 'dependencies' ), array( 'jquery', 'media-editor', 'lodash' ) ),
- get_asset_info( 'media', 'version' ),
+ 'classifai-plugin-media-processing-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-media-processing.js',
+ array_merge( get_asset_info( 'classifai-plugin-media-processing', 'dependencies' ), array( 'jquery', 'media-editor', 'lodash' ) ),
+ get_asset_info( 'classifai-plugin-media-processing', 'version' ),
true
);
}
diff --git a/includes/Classifai/Features/Classification.php b/includes/Classifai/Features/Classification.php
index f3b8427d7..72ad266b2 100644
--- a/includes/Classifai/Features/Classification.php
+++ b/includes/Classifai/Features/Classification.php
@@ -303,18 +303,18 @@ public function rest_after_insert( \WP_Post $post ) {
*/
public function enqueue_admin_assets() {
wp_enqueue_script(
- 'classifai-language-processing-script',
- CLASSIFAI_PLUGIN_URL . 'dist/language-processing.js',
- get_asset_info( 'language-processing', 'dependencies' ),
- get_asset_info( 'language-processing', 'version' ),
+ 'classifai-plugin-classification-previewer-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-classification-previewer.js',
+ get_asset_info( 'classifai-plugin-classification-previewer', 'dependencies' ),
+ get_asset_info( 'classifai-plugin-classification-previewer', 'version' ),
true
);
wp_enqueue_style(
- 'classifai-language-processing-style',
- CLASSIFAI_PLUGIN_URL . 'dist/language-processing.css',
+ 'classifai-plugin-classification-previewer-css',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-classification-previewer.css',
array(),
- get_asset_info( 'language-processing', 'version' ),
+ get_asset_info( 'classifai-plugin-classification-previewer', 'version' ),
'all'
);
}
@@ -326,10 +326,10 @@ public function enqueue_editor_assets() {
global $post;
wp_enqueue_script(
- 'classifai-editor',
- CLASSIFAI_PLUGIN_URL . 'dist/editor.js',
- get_asset_info( 'editor', 'dependencies' ),
- get_asset_info( 'editor', 'version' ),
+ 'classifai-plugin-classification-ibm-watson-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-classification-ibm-watson.js',
+ get_asset_info( 'classifai-plugin-classification-ibm-watson', 'dependencies' ),
+ get_asset_info( 'classifai-plugin-classification-ibm-watson', 'version' ),
true
);
@@ -338,15 +338,15 @@ public function enqueue_editor_assets() {
}
wp_enqueue_script(
- 'classifai-gutenberg-plugin',
- CLASSIFAI_PLUGIN_URL . 'dist/gutenberg-plugin.js',
- array_merge( get_asset_info( 'gutenberg-plugin', 'dependencies' ), array( 'lodash' ) ),
- get_asset_info( 'gutenberg-plugin', 'version' ),
+ 'classifai-plugin-classification-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-classification.js',
+ array_merge( get_asset_info( 'classifai-plugin-classification', 'dependencies' ), array( 'lodash' ), array( Feature::PLUGIN_AREA_SCRIPT ) ),
+ get_asset_info( 'classifai-plugin-classification', 'version' ),
true
);
wp_add_inline_script(
- 'classifai-gutenberg-plugin',
+ 'classifai-plugin-classification-js',
sprintf(
'var classifaiPostData = %s;',
wp_json_encode(
diff --git a/includes/Classifai/Features/ContentResizing.php b/includes/Classifai/Features/ContentResizing.php
index 34836ade0..431005e89 100644
--- a/includes/Classifai/Features/ContentResizing.php
+++ b/includes/Classifai/Features/ContentResizing.php
@@ -85,7 +85,6 @@ static function () {
*/
public function feature_setup() {
add_action( 'enqueue_block_assets', [ $this, 'enqueue_editor_assets' ] );
- add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] );
}
/**
@@ -189,39 +188,22 @@ public function enqueue_editor_assets() {
}
wp_enqueue_script(
- 'classifai-content-resizing-plugin-js',
- CLASSIFAI_PLUGIN_URL . 'dist/content-resizing-plugin.js',
- get_asset_info( 'content-resizing-plugin', 'dependencies' ),
- get_asset_info( 'content-resizing-plugin', 'version' ),
+ 'classifai-plugin-content-resizing-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-content-resizing.js',
+ get_asset_info( 'classifai-plugin-content-resizing', 'dependencies' ),
+ get_asset_info( 'classifai-plugin-content-resizing', 'version' ),
true
);
wp_enqueue_style(
- 'classifai-content-resizing-plugin-css',
- CLASSIFAI_PLUGIN_URL . 'dist/content-resizing-plugin.css',
+ 'classifai-plugin-content-resizing-css',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-content-resizing.css',
[],
- get_asset_info( 'content-resizing-plugin', 'version' ),
+ get_asset_info( 'classifai-plugin-content-resizing', 'version' ),
'all'
);
}
- /**
- * Enqueue the admin scripts.
- *
- * @param string $hook_suffix The current admin page.
- */
- public function enqueue_admin_assets( string $hook_suffix ) {
- // Load asset in new post and edit post screens.
- if ( 'post.php' === $hook_suffix || 'post-new.php' === $hook_suffix ) {
- wp_enqueue_style(
- 'classifai-language-processing-style',
- CLASSIFAI_PLUGIN_URL . 'dist/language-processing.css',
- [],
- get_asset_info( 'language-processing', 'version' ),
- );
- }
- }
-
/**
* Get the description for the enable field.
*
diff --git a/includes/Classifai/Features/ExcerptGeneration.php b/includes/Classifai/Features/ExcerptGeneration.php
index 431ec22cb..7ab110922 100644
--- a/includes/Classifai/Features/ExcerptGeneration.php
+++ b/includes/Classifai/Features/ExcerptGeneration.php
@@ -198,10 +198,10 @@ public function enqueue_editor_assets() {
// This script removes the core excerpt panel and replaces it with our own.
wp_enqueue_script(
- 'classifai-post-excerpt',
- CLASSIFAI_PLUGIN_URL . 'dist/post-excerpt.js',
- array_merge( get_asset_info( 'post-excerpt', 'dependencies' ), [ 'lodash' ] ),
- get_asset_info( 'post-excerpt', 'version' ),
+ 'classifai-plugin-excerpt-generation-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-excerpt-generation.js',
+ array_merge( get_asset_info( 'classifai-plugin-excerpt-generation', 'dependencies' ), [ 'lodash' ] ),
+ get_asset_info( 'classifai-plugin-excerpt-generation', 'version' ),
true
);
}
@@ -220,23 +220,23 @@ public function enqueue_admin_assets( string $hook_suffix ) {
if ( $screen && ! $screen->is_block_editor() ) {
if ( post_type_supports( $screen->post_type, 'excerpt' ) ) {
wp_enqueue_style(
- 'classifai-generate-title-classic-css',
- CLASSIFAI_PLUGIN_URL . 'dist/generate-title-classic.css',
+ 'classifai-plugin-classic-excerpt-generation-css',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-classic-excerpt-generation.css',
[],
- get_asset_info( 'generate-title-classic', 'version' ),
+ get_asset_info( 'classifai-plugin-classic-excerpt-generation', 'version' ),
'all'
);
wp_enqueue_script(
- 'classifai-generate-excerpt-classic-js',
- CLASSIFAI_PLUGIN_URL . 'dist/generate-excerpt-classic.js',
- array_merge( get_asset_info( 'generate-excerpt-classic', 'dependencies' ), array( 'wp-api' ) ),
- get_asset_info( 'generate-excerpt-classic', 'version' ),
+ 'classifai-plugin-classic-excerpt-generation-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-classic-excerpt-generation.js',
+ array_merge( get_asset_info( 'classifai-plugin-classic-excerpt-generation', 'dependencies' ), array( 'wp-api' ) ),
+ get_asset_info( 'classifai-plugin-classic-excerpt-generation', 'version' ),
true
);
wp_add_inline_script(
- 'classifai-generate-excerpt-classic-js',
+ 'classifai-plugin-classic-excerpt-generation-js',
sprintf(
'var classifaiGenerateExcerpt = %s;',
wp_json_encode(
@@ -251,13 +251,6 @@ public function enqueue_admin_assets( string $hook_suffix ) {
);
}
}
-
- wp_enqueue_style(
- 'classifai-language-processing-style',
- CLASSIFAI_PLUGIN_URL . 'dist/language-processing.css',
- [],
- get_asset_info( 'language-processing', 'version' ),
- );
}
}
diff --git a/includes/Classifai/Features/Feature.php b/includes/Classifai/Features/Feature.php
index cd1e6a636..f4e468035 100644
--- a/includes/Classifai/Features/Feature.php
+++ b/includes/Classifai/Features/Feature.php
@@ -5,6 +5,7 @@
use WP_REST_Request;
use WP_Error;
use function Classifai\find_provider_class;
+use function Classifai\get_asset_info;
abstract class Feature {
/**
@@ -16,6 +17,16 @@ abstract class Feature {
*/
const ID = '';
+ /**
+ * Plugin area script handle.
+ *
+ * Every feature that injects content into the plugin area
+ * should add this script as a dependency.
+ *
+ * @var string
+ */
+ const PLUGIN_AREA_SCRIPT = 'classifai-plugin-fill-js';
+
/**
* Feature label.
*
@@ -53,6 +64,7 @@ public function setup() {
add_action( 'admin_init', [ $this, 'setup_roles' ] );
add_action( 'admin_init', [ $this, 'register_setting' ] );
add_action( 'admin_init', [ $this, 'setup_fields_sections' ] );
+ add_action( 'admin_enqueue_scripts', [ $this, 'register_plugin_area_script' ] );
if ( $this->is_feature_enabled() ) {
$this->feature_setup();
@@ -114,6 +126,19 @@ public function get_label(): string {
);
}
+ /**
+ * Registers the plugin area script.
+ */
+ public function register_plugin_area_script() {
+ wp_register_script(
+ self::PLUGIN_AREA_SCRIPT,
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-fill.js',
+ get_asset_info( 'classifai-plugin-fill', 'dependencies' ),
+ get_asset_info( 'classifai-plugin-fill', 'version' ),
+ true
+ );
+ }
+
/**
* Set up the fields for each section.
*
diff --git a/includes/Classifai/Features/ImageGeneration.php b/includes/Classifai/Features/ImageGeneration.php
index 56df86aef..0db56fe52 100644
--- a/includes/Classifai/Features/ImageGeneration.php
+++ b/includes/Classifai/Features/ImageGeneration.php
@@ -186,34 +186,26 @@ public function enqueue_admin_scripts( string $hook_suffix = '' ) {
wp_enqueue_media();
wp_enqueue_style(
- 'classifai-image-processing-style',
- CLASSIFAI_PLUGIN_URL . 'dist/media-modal.css',
+ 'classifai-plugin-image-generation-media-modal-css',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-image-generation-media-modal.css',
[],
- get_asset_info( 'media-modal', 'version' ),
+ get_asset_info( 'classifai-plugin-image-generation-media-modal', 'version' ),
'all'
);
wp_enqueue_script(
- 'classifai-generate-images',
- CLASSIFAI_PLUGIN_URL . 'dist/media-modal.js',
- array_merge( get_asset_info( 'media-modal', 'dependencies' ), array( 'jquery', 'wp-api' ) ),
- get_asset_info( 'media-modal', 'version' ),
+ 'classifai-plugin-image-generation-media-modal-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-image-generation-media-modal.js',
+ array_merge( get_asset_info( 'classifai-plugin-image-generation-media-modal', 'dependencies' ), array( 'jquery', 'wp-api' ) ),
+ get_asset_info( 'classifai-plugin-image-generation-media-modal', 'version' ),
true
);
wp_enqueue_script(
- 'classifai-inserter-media-category',
- CLASSIFAI_PLUGIN_URL . 'dist/inserter-media-category.js',
- get_asset_info( 'inserter-media-category', 'dependencies' ),
- get_asset_info( 'inserter-media-category', 'version' ),
- true
- );
-
- wp_enqueue_script(
- 'classifai-extend-image-blocks',
- CLASSIFAI_PLUGIN_URL . 'dist/extend-image-blocks.js',
- get_asset_info( 'extend-image-blocks', 'dependencies' ),
- get_asset_info( 'extend-image-blocks', 'version' ),
+ 'classifai-plugin-inserter-media-category-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-inserter-media-category.js',
+ get_asset_info( 'classifai-plugin-inserter-media-category', 'dependencies' ),
+ get_asset_info( 'classifai-plugin-inserter-media-category', 'version' ),
true
);
@@ -237,7 +229,7 @@ public function enqueue_admin_scripts( string $hook_suffix = '' ) {
);
wp_localize_script(
- 'classifai-generate-images',
+ 'classifai-plugin-image-generation-media-modal-js',
'classifaiDalleData',
[
'endpoint' => 'classifai/v1/generate-image',
@@ -253,15 +245,15 @@ public function enqueue_admin_scripts( string $hook_suffix = '' ) {
if ( 'classifai-generate-image' === $action ) {
wp_enqueue_script(
- 'classifai-generate-images-media-upload',
- CLASSIFAI_PLUGIN_URL . 'dist/generate-image-media-upload.js',
- array_merge( get_asset_info( 'generate-image-media-upload', 'dependencies' ), array( 'jquery' ) ),
- get_asset_info( 'classifai-generate-images-media-upload', 'version' ),
+ 'classifai-plugin-image-generation-generate-image-media-upload-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-image-generation-generate-image-media-upload.js',
+ array_merge( get_asset_info( 'classifai-plugin-image-generation-generate-image-media-upload', 'dependencies' ), array( 'jquery' ) ),
+ get_asset_info( 'classifai-plugin-image-generation-generate-image-media-upload', 'version' ),
true
);
wp_localize_script(
- 'classifai-generate-images-media-upload',
+ 'classifai-plugin-image-generation-generate-image-media-upload-js',
'classifaiGenerateImages',
[
'upload_url' => esc_url( admin_url( 'upload.php' ) ),
diff --git a/includes/Classifai/Features/ImageTextExtraction.php b/includes/Classifai/Features/ImageTextExtraction.php
index 7e0a40673..c896b7379 100644
--- a/includes/Classifai/Features/ImageTextExtraction.php
+++ b/includes/Classifai/Features/ImageTextExtraction.php
@@ -216,10 +216,10 @@ public function add_ocr_data_to_api_response() {
*/
public function enqueue_editor_assets() {
wp_enqueue_script(
- 'editor-ocr',
- CLASSIFAI_PLUGIN_URL . 'dist/editor-ocr.js',
- array_merge( get_asset_info( 'editor-ocr', 'dependencies' ), array( 'lodash' ) ),
- get_asset_info( 'editor-ocr', 'version' ),
+ 'classifai-plugin-editor-ocr-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-editor-ocr.js',
+ array_merge( get_asset_info( 'classifai-plugin-editor-ocr', 'dependencies' ), array( 'lodash' ) ),
+ get_asset_info( 'classifai-plugin-editor-ocr', 'version' ),
true
);
}
diff --git a/includes/Classifai/Features/TextToSpeech.php b/includes/Classifai/Features/TextToSpeech.php
index 9ac3250a1..1f177fe0a 100644
--- a/includes/Classifai/Features/TextToSpeech.php
+++ b/includes/Classifai/Features/TextToSpeech.php
@@ -118,20 +118,15 @@ public function enqueue_editor_assets() {
}
wp_enqueue_script(
- 'classifai-gutenberg-plugin',
- CLASSIFAI_PLUGIN_URL . 'dist/gutenberg-plugin.js',
- array_merge( get_asset_info( 'gutenberg-plugin', 'dependencies' ), array( 'lodash' ) ),
- get_asset_info( 'gutenberg-plugin', 'version' ),
- true
- );
-
- wp_add_inline_script(
- 'classifai-gutenberg-plugin',
- sprintf(
- 'var classifaiTTSEnabled = %d;',
- true
+ 'classifai-plugin-text-to-speech',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-text-to-speech.js',
+ array_merge(
+ get_asset_info( 'classifai-plugin-text-to-speech', 'dependencies' ),
+ array( 'lodash' ),
+ array( Feature::PLUGIN_AREA_SCRIPT )
),
- 'before'
+ get_asset_info( 'classifai-plugin-text-to-speech', 'version' ),
+ true
);
}
@@ -662,18 +657,18 @@ public function render_post_audio_controls( string $content ): string {
}
wp_enqueue_script(
- 'classifai-post-audio-player-js',
- CLASSIFAI_PLUGIN_URL . 'dist/post-audio-controls.js',
- get_asset_info( 'post-audio-controls', 'dependencies' ),
- get_asset_info( 'post-audio-controls', 'version' ),
+ 'classifai-plugin-text-to-speech-frontend-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-text-to-speech-frontend.js',
+ get_asset_info( 'classifai-plugin-text-to-speech-frontend', 'dependencies' ),
+ get_asset_info( 'classifai-plugin-text-to-speech-frontend', 'version' ),
true
);
wp_enqueue_style(
- 'classifai-post-audio-player-css',
- CLASSIFAI_PLUGIN_URL . 'dist/post-audio-controls.css',
+ 'classifai-plugin-text-to-speech-frontend-css',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-text-to-speech-frontend.css',
array( 'dashicons' ),
- get_asset_info( 'post-audio-controls', 'version' ),
+ get_asset_info( 'classifai-plugin-text-to-speech-frontend', 'version' ),
'all'
);
diff --git a/includes/Classifai/Features/TitleGeneration.php b/includes/Classifai/Features/TitleGeneration.php
index be6646471..a86d74388 100644
--- a/includes/Classifai/Features/TitleGeneration.php
+++ b/includes/Classifai/Features/TitleGeneration.php
@@ -200,15 +200,15 @@ public function enqueue_editor_assets() {
}
wp_enqueue_script(
- 'classifai-post-status-info',
- CLASSIFAI_PLUGIN_URL . 'dist/post-status-info.js',
- get_asset_info( 'post-status-info', 'dependencies' ),
- get_asset_info( 'post-status-info', 'version' ),
+ 'classifai-plugin-title-generation-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-title-generation.js',
+ get_asset_info( 'classifai-plugin-title-generation', 'dependencies' ),
+ get_asset_info( 'classifai-plugin-title-generation', 'version' ),
true
);
wp_add_inline_script(
- 'classifai-post-status-info',
+ 'classifai-plugin-title-generation-js',
sprintf(
'var classifaiChatGPTData = %s;',
wp_json_encode( $this->get_localised_vars() )
@@ -231,23 +231,23 @@ public function enqueue_admin_assets( string $hook_suffix ) {
if ( $screen && ! $screen->is_block_editor() ) {
if ( post_type_supports( $screen->post_type, 'title' ) ) {
wp_enqueue_style(
- 'classifai-generate-title-classic-css',
- CLASSIFAI_PLUGIN_URL . 'dist/generate-title-classic.css',
+ 'classifai-plugin-classic-title-generation-css',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-classic-title-generation.css',
[],
- get_asset_info( 'generate-title-classic', 'version' ),
+ get_asset_info( 'classifai-plugin-classic-title-generation', 'version' ),
'all'
);
wp_enqueue_script(
- 'classifai-generate-title-classic-js',
- CLASSIFAI_PLUGIN_URL . 'dist/generate-title-classic.js',
- array_merge( get_asset_info( 'generate-title-classic', 'dependencies' ), array( 'wp-api' ) ),
+ 'classifai-plugin-classic-title-generation-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-classic-title-generation.js',
+ array_merge( get_asset_info( 'classifai-plugin-classic-title-generation', 'dependencies' ), array( 'wp-api' ) ),
get_asset_info( 'generate-title-classic', 'version' ),
true
);
wp_add_inline_script(
- 'classifai-generate-title-classic-js',
+ 'classifai-plugin-classic-title-generation-js',
sprintf(
'var classifaiChatGPTData = %s;',
wp_json_encode( $this->get_localised_vars() )
@@ -256,13 +256,6 @@ public function enqueue_admin_assets( string $hook_suffix ) {
);
}
}
-
- wp_enqueue_style(
- 'classifai-language-processing-style',
- CLASSIFAI_PLUGIN_URL . 'dist/language-processing.css',
- [],
- get_asset_info( 'language-processing', 'version' ),
- );
}
}
@@ -271,12 +264,12 @@ public function enqueue_admin_assets( string $hook_suffix ) {
*/
public function register_generated_titles_template() {
?>
-
-
-
-
-
-
+
diff --git a/includes/Classifai/Plugin.php b/includes/Classifai/Plugin.php
index 3f3313746..fc44e3683 100644
--- a/includes/Classifai/Plugin.php
+++ b/includes/Classifai/Plugin.php
@@ -198,10 +198,10 @@ public function enqueue_admin_assets() {
if ( wp_script_is( 'wp-commands', 'registered' ) ) {
wp_enqueue_script(
- 'classifai-commands',
- CLASSIFAI_PLUGIN_URL . 'dist/commands.js',
- get_asset_info( 'commands', 'dependencies' ),
- get_asset_info( 'commands', 'version' ),
+ 'classifai-plugin-commands-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-commands.js',
+ get_asset_info( 'classifai-plugin-commands', 'dependencies' ),
+ get_asset_info( 'classifai-plugin-commands', 'version' ),
true
);
}
diff --git a/includes/Classifai/Services/ImageProcessing.php b/includes/Classifai/Services/ImageProcessing.php
index b83e9f88d..981f22f19 100644
--- a/includes/Classifai/Services/ImageProcessing.php
+++ b/includes/Classifai/Services/ImageProcessing.php
@@ -67,16 +67,16 @@ public static function get_service_providers(): array {
*/
public function enqueue_media_scripts() {
wp_enqueue_script(
- 'classifai-media-script',
- CLASSIFAI_PLUGIN_URL . 'dist/media.js',
- array_merge( get_asset_info( 'media', 'dependencies' ), array( 'jquery', 'media-editor', 'lodash' ) ),
- get_asset_info( 'media', 'version' ),
+ 'classifai-plugin-media-processing-js',
+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-media-processing.js',
+ array_merge( get_asset_info( 'classifai-plugin-media-processing', 'dependencies' ), array( 'jquery', 'media-editor', 'lodash' ) ),
+ get_asset_info( 'classifai-plugin-media-processing', 'version' ),
true
);
$feature = new DescriptiveTextGenerator();
wp_add_inline_script(
- 'classifai-media-script',
+ 'classifai-plugin-media-processing-js',
'const classifaiMediaVars = ' . wp_json_encode(
array(
'enabledAltTextFields' => $feature->get_alt_text_settings() ? $feature->get_alt_text_settings() : array(),
diff --git a/package.json b/package.json
index b6b73c857..2c491d611 100644
--- a/package.json
+++ b/package.json
@@ -7,8 +7,8 @@
"scripts": {
"start": "wp-scripts start",
"build": "wp-scripts build",
- "lint:js": "wp-scripts lint-js",
- "lint:js-fix": "wp-scripts lint-js --fix",
+ "lint:js": "wp-scripts lint-js ./src ./includes/Classifai/Blocks",
+ "lint:js-fix": "wp-scripts lint-js ./src ./includes/Classifai/Blocks --fix",
"install_tests": "./bin/install-wp-tests.sh classifai_unit_tests root password 127.0.0.1",
"test": "./vendor/bin/phpunit",
"tests:e2e": "./vendor/bin/wpacceptance run",
diff --git a/src/js/features/classification/classification-button.js b/src/js/features/classification/classification-button.js
new file mode 100644
index 000000000..82dc164b8
--- /dev/null
+++ b/src/js/features/classification/classification-button.js
@@ -0,0 +1,324 @@
+/**
+ * External dependencies.
+ */
+import { dispatch, useSelect } from '@wordpress/data';
+import { useState } from '@wordpress/element';
+import { Button, Modal } from '@wordpress/components';
+import { __, sprintf } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies.
+ */
+import TaxonomyControls from './taxonomy-controls';
+import PrePubClassifyPost from './pre-publish-classify-post';
+import { DisableFeatureButton } from '../../components';
+import { handleClick } from '../../../js/helpers';
+
+/**
+ * Classify button.
+ *
+ * Used to manually classify the content.
+ */
+export const ClassificationButton = () => {
+ const processContent = useSelect( ( select ) =>
+ select( 'core/editor' ).getEditedPostAttribute(
+ 'classifai_process_content'
+ )
+ );
+
+ const postId = wp.data.select( 'core/editor' ).getCurrentPostId();
+ const postType = wp.data.select( 'core/editor' ).getCurrentPostType();
+ const postTypeLabel =
+ wp.data.select( 'core/editor' ).getPostTypeLabel() ||
+ __( 'Post', 'classifai' );
+
+ const [ isLoading, setLoading ] = useState( false );
+ const [ resultReceived, setResultReceived ] = useState( false );
+ const [ isOpen, setOpen ] = useState( false );
+ const [ popupOpened, setPopupOpened ] = useState( false );
+ const openModal = () => setOpen( true );
+ const closeModal = () => setOpen( false );
+
+ const [ taxQuery, setTaxQuery ] = useState( [] );
+ const [ featureTaxonomies, setFeatureTaxonomies ] = useState( [] );
+ let [ taxTermsAI, setTaxTermsAI ] = useState( [] );
+
+ /**
+ * Callback function to handle API response.
+ *
+ * @param {Object} resp Response from the API.
+ * @param {Object} callbackArgs Callback arguments.
+ */
+ const buttonClickCallBack = async ( resp, callbackArgs ) => {
+ if ( resp && resp.terms ) {
+ // set feature taxonomies
+ if ( resp?.feature_taxonomies ) {
+ setFeatureTaxonomies( resp.feature_taxonomies );
+ }
+
+ const taxonomies = resp.terms;
+ const taxTerms = {};
+ const taxTermsExisting = {};
+
+ // get current terms of the post
+ const currentTerms = wp.data
+ .select( 'core' )
+ .getEntityRecord( 'postType', postType, postId );
+
+ Object.keys( taxonomies ).forEach( ( taxonomy ) => {
+ let tax = taxonomy;
+ if ( 'post_tag' === taxonomy ) {
+ tax = 'tags';
+ }
+ if ( 'category' === taxonomy ) {
+ tax = 'categories';
+ }
+
+ const currentTermsOfTaxonomy = currentTerms[ tax ];
+ if ( currentTermsOfTaxonomy ) {
+ taxTermsExisting[ tax ] = currentTermsOfTaxonomy;
+ }
+
+ const newTerms = Object.values( resp.terms[ taxonomy ] );
+ if ( newTerms && Object.keys( newTerms ).length ) {
+ // Loop through each term and add in taxTermsAI if it does not exist in the post.
+ taxTermsAI = taxTermsAI || {};
+ Object( newTerms ).forEach( ( termId ) => {
+ if ( taxTermsExisting[ tax ] ) {
+ const matchedTerm = taxTermsExisting[ tax ].find(
+ ( termID ) => termID === termId
+ );
+ if ( ! matchedTerm ) {
+ taxTermsAI[ tax ] = taxTermsAI[ tax ] || [];
+ // push only if not exist already
+ if ( ! taxTermsAI[ tax ].includes( termId ) ) {
+ taxTermsAI[ tax ].push( termId );
+ }
+ }
+ }
+ } );
+
+ // update the taxTerms
+ taxTerms[ tax ] = newTerms;
+ }
+ } );
+
+ // Merge taxterms with taxTermsExisting and remove duplicates
+ Object.keys( taxTermsExisting ).forEach( ( taxonomy ) => {
+ if ( taxTerms[ taxonomy ] ) {
+ // Merge taxTermsExisting into taxTerms
+ taxTerms[ taxonomy ] = taxTerms[ taxonomy ].concat(
+ taxTermsExisting[ taxonomy ]
+ );
+ } else {
+ // Initialize taxTerms with taxTermsExisting if not already set
+ taxTerms[ taxonomy ] = taxTermsExisting[ taxonomy ];
+ }
+
+ // Remove duplicate items from taxTerms
+ taxTerms[ taxonomy ] = [ ...new Set( taxTerms[ taxonomy ] ) ];
+ } );
+
+ setTaxQuery( taxTerms );
+ setTaxTermsAI( taxTermsAI );
+ }
+ if ( callbackArgs?.openPopup ) {
+ openModal();
+ setPopupOpened( true );
+ }
+ setLoading( false );
+ setResultReceived( true );
+ };
+
+ /**
+ * Save the terms (Modal).
+ *
+ * @param {Object} taxTerms Taxonomy terms.
+ */
+ const saveTerms = async ( taxTerms ) => {
+ // Remove index values from the nested object
+ // Convert the object into an array of key-value pairs
+ const taxTermsArray = Object.entries( taxTerms );
+
+ // Remove index values from the nested objects and convert back to an object
+ const newtaxTerms = Object.fromEntries(
+ taxTermsArray.map( ( [ key, value ] ) => {
+ if ( typeof value === 'object' ) {
+ return [ key, Object.values( value ) ];
+ }
+ return [ key, value ];
+ } )
+ );
+
+ await dispatch( 'core' ).editEntityRecord(
+ 'postType',
+ postType,
+ postId,
+ newtaxTerms
+ );
+
+ // If no edited values in post trigger save.
+ const isDirty = await wp.data
+ .select( 'core/editor' )
+ .isEditedPostDirty();
+ if ( ! isDirty ) {
+ await dispatch( 'core' ).saveEditedEntityRecord(
+ 'postType',
+ postType,
+ postId
+ );
+ }
+
+ // Display success notice.
+ dispatch( 'core/notices' ).createSuccessNotice(
+ sprintf(
+ /** translators: %s is post type label. */
+ __( '%s classified successfully.', 'classifai' ),
+ postTypeLabel
+ ),
+ { type: 'snackbar' }
+ );
+ closeModal();
+ };
+
+ // Display classify post button only when process content on update is disabled.
+ const enabled = 'no' === processContent ? 'no' : 'yes';
+ if ( 'yes' === enabled ) {
+ return null;
+ }
+
+ const buttonText = __( 'Suggest terms & tags', 'classifai' );
+
+ let updatedTaxQuery = Object.entries( taxQuery || {} ).reduce(
+ ( accumulator, [ taxonomySlug, terms ] ) => {
+ accumulator[ taxonomySlug ] = terms;
+
+ return accumulator;
+ },
+ {}
+ );
+
+ if ( updatedTaxQuery.taxQuery ) {
+ updatedTaxQuery = updatedTaxQuery.taxQuery;
+ }
+
+ const modalData = (
+ <>
+
{
+ setTaxQuery( newTaxQuery );
+ } }
+ query={ {
+ contentPostType: postType,
+ featureTaxonomies,
+ taxQuery: updatedTaxQuery,
+ taxTermsAI: taxTermsAI || {},
+ isLoading,
+ } }
+ />
+
+
+ { sprintf(
+ /* translators: %s is post type label */
+ __(
+ 'Note that the lists above include any pre-existing terms from this %s.',
+ 'classifai'
+ ),
+ postTypeLabel
+ ) }
+
+ { __(
+ 'AI recommendations saved to this post will not include the "[AI]" text.',
+ 'classifai'
+ ) }
+
+
saveTerms( updatedTaxQuery ) }
+ >
+ { __( 'Save', 'classifai' ) }
+
+
+
+ >
+ );
+
+ return (
+
+ { isOpen && (
+
+ { modalData }
+
+ ) }
+
{
+ handleClick( {
+ button: e.target,
+ endpoint: '/classifai/v1/classify/',
+ callback: buttonClickCallBack,
+ callbackArgs: {
+ openPopup: true,
+ },
+ buttonText,
+ linkTerms: false,
+ } );
+ } }
+ >
+ { buttonText }
+
+
+
+
+ { ! resultReceived && (
+ <>
+ {
+ handleClick( {
+ button: e.target,
+ endpoint: '/classifai/v1/classify/',
+ callback: buttonClickCallBack,
+ buttonText,
+ linkTerms: false,
+ } );
+ } }
+ >
+ { buttonText }
+
+
+
+ >
+ ) }
+ { resultReceived && modalData }
+
+
+ );
+};
diff --git a/src/js/features/classification/classification-toggle.js b/src/js/features/classification/classification-toggle.js
new file mode 100644
index 000000000..aefd99f5f
--- /dev/null
+++ b/src/js/features/classification/classification-toggle.js
@@ -0,0 +1,34 @@
+/**
+ * Internal dependencies.
+ */
+import { ToggleControl } from '@wordpress/components';
+import { useSelect, useDispatch } from '@wordpress/data';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * ClassificationToggle Component.
+ *
+ * Used to toggle the classification process on or off.
+ */
+export const ClassificationToggle = () => {
+ // Use the datastore to retrieve all the meta for this post.
+ const processContent = useSelect( ( select ) =>
+ select( 'core/editor' ).getEditedPostAttribute(
+ 'classifai_process_content'
+ )
+ );
+
+ // Use the datastore to tell the post to update the meta.
+ const { editPost } = useDispatch( 'core/editor' );
+ const enabled = 'yes' === processContent ? 'yes' : 'no';
+
+ return (
+ {
+ editPost( { classifai_process_content: value ? 'yes' : 'no' } );
+ } }
+ />
+ );
+};
diff --git a/src/js/editor.js b/src/js/features/classification/ibm-watson.js
similarity index 100%
rename from src/js/editor.js
rename to src/js/features/classification/ibm-watson.js
diff --git a/src/js/features/classification/index.js b/src/js/features/classification/index.js
new file mode 100644
index 000000000..abce612e7
--- /dev/null
+++ b/src/js/features/classification/index.js
@@ -0,0 +1,62 @@
+/**
+ * External dependencies.
+ */
+import { useSelect } from '@wordpress/data';
+import { registerPlugin } from '@wordpress/plugins';
+
+/**
+ * Internal dependencies.
+ */
+import { ClassificationToggle } from './classification-toggle';
+import { ClassificationButton } from './classification-button';
+
+const { classifaiPostData, ClassifaiEditorSettingPanel } = window;
+
+const ClassificationPlugin = () => {
+ const postType = useSelect( ( select ) =>
+ select( 'core/editor' ).getCurrentPostType()
+ );
+ const postStatus = useSelect( ( select ) =>
+ select( 'core/editor' ).getCurrentPostAttribute( 'status' )
+ );
+
+ // Ensure we are on a supported post type, checking settings from all features.
+ const isNLUPostTypeSupported =
+ classifaiPostData &&
+ classifaiPostData.supportedPostTypes &&
+ classifaiPostData.supportedPostTypes.includes( postType );
+
+ // Ensure we are on a supported post status, checking settings from all features.
+ const isNLUPostStatusSupported =
+ classifaiPostData &&
+ classifaiPostData.supportedPostStatues &&
+ classifaiPostData.supportedPostStatues.includes( postStatus );
+
+ // Ensure the user has permissions to use the feature.
+ const userHasNLUPermissions =
+ classifaiPostData &&
+ ! (
+ classifaiPostData.noPermissions &&
+ 1 === parseInt( classifaiPostData.noPermissions )
+ );
+
+ const nluPermissionCheck =
+ userHasNLUPermissions &&
+ isNLUPostTypeSupported &&
+ isNLUPostStatusSupported;
+
+ return (
+ <>
+ { nluPermissionCheck && (
+
+
+
+
+ ) }
+ >
+ );
+};
+
+registerPlugin( 'classifai-plugin-classification', {
+ render: ClassificationPlugin,
+} );
diff --git a/src/js/gutenberg-plugins/pre-publish-classify-post.js b/src/js/features/classification/pre-publish-classify-post.js
similarity index 100%
rename from src/js/gutenberg-plugins/pre-publish-classify-post.js
rename to src/js/features/classification/pre-publish-classify-post.js
diff --git a/src/js/language-processing.js b/src/js/features/classification/previewer.js
similarity index 99%
rename from src/js/language-processing.js
rename to src/js/features/classification/previewer.js
index 88a1e9d14..176ab31cc 100644
--- a/src/js/language-processing.js
+++ b/src/js/features/classification/previewer.js
@@ -1,5 +1,5 @@
import Choices from 'choices.js';
-import '../scss/language-processing.scss';
+import './previewer.scss';
( () => {
let featureStatuses = {};
diff --git a/src/scss/language-processing.scss b/src/js/features/classification/previewer.scss
similarity index 87%
rename from src/scss/language-processing.scss
rename to src/js/features/classification/previewer.scss
index c0cf3fcba..1320b9bb2 100644
--- a/src/scss/language-processing.scss
+++ b/src/js/features/classification/previewer.scss
@@ -1,4 +1,4 @@
-@import '../../node_modules/choices.js/public/assets/styles/choices.min.css';
+@import '../../../../node_modules/choices.js/public/assets/styles/choices.min.css';
@keyframes loading-rotation {
0% {
@@ -131,37 +131,6 @@
}
}
-.editor-post-excerpt {
- button {
- margin-top: 10px;
- }
-}
-
-.title-modal {
- .components-modal__content {
- display: flex;
- flex-wrap: wrap;
- align-content: flex-start;
- gap: 20px;
- }
-
- .classifai-title {
- width: 300px;
- max-width: 100%;
- margin: 1px 0 10px;
- padding-bottom: 10px;
-
- textarea {
- width: 100%;
- }
-
- button {
- display: block;
- margin-top: 8px;
- }
- }
-}
-
#classifai-text-to-speech-meta-box {
.description {
display: block;
diff --git a/src/js/taxonomy-controls.js b/src/js/features/classification/taxonomy-controls.js
similarity index 97%
rename from src/js/taxonomy-controls.js
rename to src/js/features/classification/taxonomy-controls.js
index 4e43daa8a..089699b06 100644
--- a/src/js/taxonomy-controls.js
+++ b/src/js/features/classification/taxonomy-controls.js
@@ -10,8 +10,8 @@ import { store as coreStore } from '@wordpress/core-data';
import {
getEntitiesInfo,
useTaxonomies,
-} from '../../includes/Classifai/Blocks/recommended-content-block/utils';
-import { useState } from '@wordpress/element';
+} from '../../../../includes/Classifai/Blocks/recommended-content-block/utils';
+import { useState, Fragment } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
const termsPerPage = -1;
@@ -274,7 +274,7 @@ const TaxonomyControls = ( { onChange, query } ) => {
}
return (
- <>
+
{
>
) }
- >
+
);
} ) }
>
diff --git a/src/js/gutenberg-plugins/commands.js b/src/js/features/commands.js
similarity index 100%
rename from src/js/gutenberg-plugins/commands.js
rename to src/js/features/commands.js
diff --git a/src/js/gutenberg-plugins/content-resizing-plugin.js b/src/js/features/content-resizing/index.js
similarity index 98%
rename from src/js/gutenberg-plugins/content-resizing-plugin.js
rename to src/js/features/content-resizing/index.js
index 31ba78255..66e9cccb3 100644
--- a/src/js/gutenberg-plugins/content-resizing-plugin.js
+++ b/src/js/features/content-resizing/index.js
@@ -23,8 +23,8 @@ import {
} from '@wordpress/wordcount';
import { __, _nx } from '@wordpress/i18n';
-import { DisableFeatureButton } from '../components';
-import '../../scss/content-resizing-plugin.scss';
+import { DisableFeatureButton } from '../../components';
+import './index.scss';
const aiIconSvg = (
'
+ '
'
)
.parent()
.append(
$( ' ', {
- class: 'classifai-openai__excerpt-generate-btn--spinner',
+ class: 'classifai-excerpt-generation__excerpt-generate-btn--spinner',
} )
)
.insertAfter( excerptContainer );
$( '', {
- class: 'classifai-openai__excerpt-generate-error',
+ class: 'classifai-excerpt-generation__excerpt-generate-error',
} ).insertAfter(
- document.getElementById( 'classifai-openai__excerpt-generate-btn' )
+ document.getElementById(
+ 'classifai-excerpt-generation__excerpt-generate-btn'
+ )
);
// Append disable feature link.
@@ -60,12 +62,12 @@ const classifaiExcerptData = window.classifaiGenerateExcerpt || {};
class: 'classifai-disable-feature-link',
} )
.wrap(
- `
`
+ `
`
)
.parent()
.insertAfter(
document.getElementById(
- 'classifai-openai__excerpt-generate-btn'
+ 'classifai-excerpt-generation__excerpt-generate-btn'
)
);
}
@@ -80,12 +82,14 @@ const classifaiExcerptData = window.classifaiGenerateExcerpt || {};
}
const generateTextEl = $(
- '.classifai-openai__excerpt-generate-btn--text'
+ '.classifai-excerpt-generation__excerpt-generate-btn--text'
);
const spinnerEl = $(
- '.classifai-openai__excerpt-generate-btn--spinner'
+ '.classifai-excerpt-generation__excerpt-generate-btn--spinner'
+ );
+ const errorEl = $(
+ '.classifai-excerpt-generation__excerpt-generate-error'
);
- const errorEl = $( '.classifai-openai__excerpt-generate-error' );
generateTextEl.css( 'opacity', '0' );
spinnerEl.show();
@@ -118,7 +122,7 @@ const classifaiExcerptData = window.classifaiGenerateExcerpt || {};
// Event handler registration to generate the excerpt.
$( document ).on(
'click',
- '#classifai-openai__excerpt-generate-btn',
+ '#classifai-excerpt-generation__excerpt-generate-btn',
generateExcerpt
);
}
diff --git a/src/js/features/excerpt-generation/classic/index.scss b/src/js/features/excerpt-generation/classic/index.scss
new file mode 100644
index 000000000..40973d67f
--- /dev/null
+++ b/src/js/features/excerpt-generation/classic/index.scss
@@ -0,0 +1,55 @@
+#postexcerpt {
+ #classifai-excerpt-generation__excerpt-generate-btn {
+ display: block;
+ width: 160px;
+ max-width: 100%;
+ margin: 1em 0 0 auto;
+ text-align: center;
+ position: relative;
+
+ & > .classifai-excerpt-generation__excerpt-generate-btn--spinner {
+ display: none;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate( -50%, -50% );
+ line-height: 29px;
+ }
+ }
+
+ .classifai-excerpt-generation__excerpt-generate-error {
+ display: none;
+ color: #dc3232;
+ text-align: right;
+ }
+
+ .classifai-excerpt-generation__excerpt-generate-disable-link {
+ text-align: right;
+ margin-top: 6px;
+ }
+}
+
+.classifai-excerpt-generation {
+ &__excerpt-generate-btn--spinner {
+ &:before {
+ display: block;
+ animation: rotation 2s infinite linear;
+ content: "\f463";
+ font-family: dashicons;
+ font-size: 1.75rem;
+
+ width: 1.75rem;
+ height: 1.75rem;
+ }
+ }
+}
+
+@keyframes rotation {
+ from {
+ transform: rotate(0deg);
+ }
+
+ to {
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/js/post-excerpt/index.js b/src/js/features/excerpt-generation/index.js
similarity index 100%
rename from src/js/post-excerpt/index.js
rename to src/js/features/excerpt-generation/index.js
diff --git a/src/js/post-excerpt/maybe-excerpt-panel.js b/src/js/features/excerpt-generation/maybe-excerpt-panel.js
similarity index 100%
rename from src/js/post-excerpt/maybe-excerpt-panel.js
rename to src/js/features/excerpt-generation/maybe-excerpt-panel.js
diff --git a/src/js/post-excerpt/panel.js b/src/js/features/excerpt-generation/panel.js
similarity index 95%
rename from src/js/post-excerpt/panel.js
rename to src/js/features/excerpt-generation/panel.js
index e4ba898c3..292caabcb 100644
--- a/src/js/post-excerpt/panel.js
+++ b/src/js/features/excerpt-generation/panel.js
@@ -1,5 +1,5 @@
/**
- * WordPress dependencies
+ * External Dependencies.
*/
import { __ } from '@wordpress/i18n';
import { Button, ExternalLink, TextareaControl } from '@wordpress/components';
@@ -7,7 +7,11 @@ import { withSelect, withDispatch } from '@wordpress/data';
import { compose } from '@wordpress/compose';
import { useState } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
-import { DisableFeatureButton } from '../components';
+
+/**
+ * Internal Dependencies.
+ */
+import { DisableFeatureButton } from '../../components';
/**
* PostExcerpt component.
@@ -82,6 +86,7 @@ function PostExcerpt( { excerpt, onUpdateExcerpt } ) {
variant={ 'secondary' }
disabled={ isLoading }
data-id={ postId }
+ style={ { marginTop: '1rem' } }
onClick={ () =>
buttonClick( '/classifai/v1/generate-excerpt/' )
}
diff --git a/src/js/extend-image-block-generate-image.js b/src/js/features/image-generation/extend-image-block-generate-image.js
similarity index 96%
rename from src/js/extend-image-block-generate-image.js
rename to src/js/features/image-generation/extend-image-block-generate-image.js
index 829e3db45..706ff7e14 100644
--- a/src/js/extend-image-block-generate-image.js
+++ b/src/js/features/image-generation/extend-image-block-generate-image.js
@@ -45,7 +45,7 @@ function addImageGenerationLink( Component ) {
{ ...rest }
mode="generate"
render={ ( { open } ) => (
-
+
{ _nx(
'Generate image',
'Generate images',
diff --git a/src/js/gutenberg-plugins/inserter-media-category.js b/src/js/features/image-generation/inserter-media-category.js
similarity index 100%
rename from src/js/gutenberg-plugins/inserter-media-category.js
rename to src/js/features/image-generation/inserter-media-category.js
diff --git a/src/js/media-modal/collections/images.js b/src/js/features/image-generation/media-modal/collections/images.js
similarity index 100%
rename from src/js/media-modal/collections/images.js
rename to src/js/features/image-generation/media-modal/collections/images.js
diff --git a/src/js/media-modal/index.js b/src/js/features/image-generation/media-modal/index.js
similarity index 98%
rename from src/js/media-modal/index.js
rename to src/js/features/image-generation/media-modal/index.js
index 75dcabb65..26e6fa8e6 100644
--- a/src/js/media-modal/index.js
+++ b/src/js/features/image-generation/media-modal/index.js
@@ -1,7 +1,7 @@
/* eslint object-shorthand: 0 */
import Prompt from './views/prompt';
-import '../../scss/media-modal.scss';
+import './index.scss';
const currentMediaSelectFrame = wp.media.view.MediaFrame.Select;
const currentPostFrame = wp.media.view.MediaFrame.Post;
diff --git a/src/scss/media-modal.scss b/src/js/features/image-generation/media-modal/index.scss
similarity index 100%
rename from src/scss/media-modal.scss
rename to src/js/features/image-generation/media-modal/index.scss
diff --git a/src/js/media-modal/models/image.js b/src/js/features/image-generation/media-modal/models/image.js
similarity index 100%
rename from src/js/media-modal/models/image.js
rename to src/js/features/image-generation/media-modal/models/image.js
diff --git a/src/js/media-modal/views/error-message.js b/src/js/features/image-generation/media-modal/views/error-message.js
similarity index 100%
rename from src/js/media-modal/views/error-message.js
rename to src/js/features/image-generation/media-modal/views/error-message.js
diff --git a/src/js/media-modal/views/generate-image-media-upload.js b/src/js/features/image-generation/media-modal/views/generate-image-media-upload.js
similarity index 100%
rename from src/js/media-modal/views/generate-image-media-upload.js
rename to src/js/features/image-generation/media-modal/views/generate-image-media-upload.js
diff --git a/src/js/media-modal/views/generated-image.js b/src/js/features/image-generation/media-modal/views/generated-image.js
similarity index 100%
rename from src/js/media-modal/views/generated-image.js
rename to src/js/features/image-generation/media-modal/views/generated-image.js
diff --git a/src/js/media-modal/views/generated-images-container.js b/src/js/features/image-generation/media-modal/views/generated-images-container.js
similarity index 100%
rename from src/js/media-modal/views/generated-images-container.js
rename to src/js/features/image-generation/media-modal/views/generated-images-container.js
diff --git a/src/js/media-modal/views/prompt.js b/src/js/features/image-generation/media-modal/views/prompt.js
similarity index 100%
rename from src/js/media-modal/views/prompt.js
rename to src/js/features/image-generation/media-modal/views/prompt.js
diff --git a/src/js/editor-ocr.js b/src/js/features/media-processing/editor-ocr.js
similarity index 100%
rename from src/js/editor-ocr.js
rename to src/js/features/media-processing/editor-ocr.js
diff --git a/src/js/media.js b/src/js/features/media-processing/media-upload.js
similarity index 99%
rename from src/js/media.js
rename to src/js/features/media-processing/media-upload.js
index d45c25733..4a0ec1a6c 100644
--- a/src/js/media.js
+++ b/src/js/features/media-processing/media-upload.js
@@ -1,6 +1,6 @@
/* global ClassifAI */
-import { handleClick } from './helpers';
+import { handleClick } from '../../helpers';
import { __ } from '@wordpress/i18n';
( function( $ ) { // eslint-disable-line
diff --git a/src/js/features/slot-fill/index.js b/src/js/features/slot-fill/index.js
new file mode 100644
index 000000000..cc4819cb7
--- /dev/null
+++ b/src/js/features/slot-fill/index.js
@@ -0,0 +1,41 @@
+/**
+ * External dependencies
+ */
+import { PluginDocumentSettingPanel } from '@wordpress/edit-post';
+import { Icon, SlotFillProvider, Slot, Fill } from '@wordpress/components';
+import { PluginArea, registerPlugin } from '@wordpress/plugins';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { ReactComponent as icon } from '../../../../assets/img/block-icon.svg';
+
+const ClassifAIPluginArea = () => {
+ return (
+
+ }
+ className="classifai-panel"
+ >
+
+
+
+
+
+ );
+};
+
+registerPlugin( 'classifai-plugin-area', {
+ render: ClassifAIPluginArea,
+} );
+
+window.ClassifaiEditorSettingPanel = ( { children } ) => {
+ return { children } ;
+};
diff --git a/src/js/post-audio-controls.js b/src/js/features/text-to-speech/frontend/index.js
similarity index 96%
rename from src/js/post-audio-controls.js
rename to src/js/features/text-to-speech/frontend/index.js
index 69b88df3d..d4df03cd0 100644
--- a/src/js/post-audio-controls.js
+++ b/src/js/features/text-to-speech/frontend/index.js
@@ -1,4 +1,4 @@
-import '../scss/post-audio-controls.scss';
+import './index.scss';
const audioControlEl = document.querySelector( '.class-post-audio-controls' );
const playBtn = document.querySelector( '.dashicons-controls-play' );
diff --git a/src/scss/post-audio-controls.scss b/src/js/features/text-to-speech/frontend/index.scss
similarity index 100%
rename from src/scss/post-audio-controls.scss
rename to src/js/features/text-to-speech/frontend/index.scss
diff --git a/src/js/features/text-to-speech/index.js b/src/js/features/text-to-speech/index.js
new file mode 100644
index 000000000..6e1d807d7
--- /dev/null
+++ b/src/js/features/text-to-speech/index.js
@@ -0,0 +1,262 @@
+/**
+ * External dependencies.
+ */
+import { useState, useEffect, useRef } from '@wordpress/element';
+import {
+ ToggleControl,
+ BaseControl,
+ Button,
+ Icon,
+} from '@wordpress/components';
+import { useSelect, subscribe } from '@wordpress/data';
+import { registerPlugin } from '@wordpress/plugins';
+import { __, sprintf } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies.
+ */
+import { store as postAudioStore } from './store';
+
+const { ClassifaiEditorSettingPanel } = window;
+
+/**
+ * ClassifAI Text to Audio component.
+ */
+const TextToSpeechPlugin = () => {
+ // State of the audio being previewed in PluginDocumentSettingPanel.
+ const [ isPreviewing, setIsPreviewing ] = useState( false );
+
+ const [ timestamp, setTimestamp ] = useState( new Date().getTime() );
+
+ // Indicates whether speech synthesis is enabled for the current post.
+ const isSynthesizeSpeech = useSelect( ( select ) =>
+ select( 'core/editor' ).getEditedPostAttribute(
+ 'classifai_synthesize_speech'
+ )
+ );
+
+ // Indicates whether generated audio should be displayed on the frontend.
+ const displayGeneratedAudio = useSelect( ( select ) =>
+ select( 'core/editor' ).getEditedPostAttribute(
+ 'classifai_display_generated_audio'
+ )
+ );
+
+ // Post type label.
+ const postTypeLabel = useSelect(
+ ( select ) =>
+ ( typeof select( 'core/editor' ).getPostTypeLabel !== 'undefined' &&
+ select( 'core/editor' ).getPostTypeLabel() ) ||
+ __( 'Post', 'classifai' )
+ );
+
+ // Says whether speech synthesis is in progress.
+ const isProcessingAudio = useSelect( ( select ) =>
+ select( postAudioStore ).getIsProcessing()
+ );
+
+ // The audio ID saved in the DB for the current post.
+ const defaultAudioId = useSelect( ( select ) =>
+ select( 'core/editor' ).getEditedPostAttribute(
+ 'classifai_post_audio_id'
+ )
+ );
+
+ // New audio ID in case it is regenerated manually or through publishing/updating the current post.
+ const audioId =
+ useSelect( ( select ) => select( postAudioStore ).getAudioId() ) ||
+ defaultAudioId;
+
+ // Get the attachment data by audio ID.
+ const attachments = useSelect( ( select ) =>
+ select( 'core' ).getEntityRecords( 'postType', 'attachment', {
+ include: [ audioId ],
+ } )
+ );
+
+ // Get URL for the attachment.
+ const sourceUrl =
+ attachments && attachments.length > 0 && attachments[ 0 ].source_url;
+
+ const isProcessingAudioProgress = useRef( false );
+ const isPostSavingInProgress = useRef( false );
+ const { isSavingPost } = useSelect( ( select ) => {
+ return {
+ isSavingPost: select( 'core/editor' ).isSavingPost(),
+ };
+ } );
+ const { isAutosavingPost } = useSelect( ( select ) => {
+ return {
+ isSavingPost: select( 'core/editor' ).isAutosavingPost(),
+ };
+ } );
+
+ // Handles playing/pausing post audio during preview.
+ useEffect( () => {
+ const audioEl = document.getElementById( 'classifai-audio-preview' );
+
+ if ( ! audioEl ) {
+ return;
+ }
+
+ if ( isPreviewing ) {
+ audioEl.play();
+ } else {
+ audioEl.pause();
+ }
+ }, [ isPreviewing ] );
+
+ // Generates a unique timestamp to cache bust audio file.
+ useEffect( () => {
+ if ( isProcessingAudio ) {
+ isProcessingAudioProgress.current = true;
+ }
+
+ if ( isProcessingAudioProgress.current && ! isProcessingAudio ) {
+ setTimestamp( new Date().getTime() );
+ }
+ }, [ isProcessingAudio ] );
+
+ useEffect( () => {
+ // Code to run during post saving is in process.
+ if (
+ isSavingPost &&
+ ! isAutosavingPost &&
+ ! isPostSavingInProgress.current
+ ) {
+ isPostSavingInProgress.current = true;
+ if ( isSynthesizeSpeech ) {
+ wp.data.dispatch( postAudioStore ).setIsProcessing( true );
+ }
+ }
+
+ if (
+ ! isSavingPost &&
+ ! isAutosavingPost &&
+ isPostSavingInProgress.current
+ ) {
+ // Code to run after post is done saving.
+ isPostSavingInProgress.current = false;
+ wp.data.dispatch( postAudioStore ).setIsProcessing( false );
+ }
+ }, [ isSavingPost, isAutosavingPost, isSynthesizeSpeech ] );
+
+ // Fetches the latest audio file to avoid disk cache.
+ const cacheBustingUrl = `${ sourceUrl }?ver=${ timestamp }`;
+
+ let audioIcon = 'controls-play';
+
+ if ( isProcessingAudio ) {
+ audioIcon = 'format-audio';
+ } else if ( isPreviewing ) {
+ audioIcon = 'controls-pause';
+ }
+
+ return (
+
+ {
+ wp.data.dispatch( 'core/editor' ).editPost( {
+ classifai_synthesize_speech: value,
+ } );
+ } }
+ disabled={ isProcessingAudio }
+ isBusy={ isProcessingAudio }
+ />
+ { sourceUrl && (
+ <>
+ {
+ wp.data.dispatch( 'core/editor' ).editPost( {
+ classifai_display_generated_audio: value,
+ } );
+ } }
+ disabled={ isProcessingAudio }
+ isBusy={ isProcessingAudio }
+ />
+
+ }
+ variant="secondary"
+ onClick={ () => setIsPreviewing( ! isPreviewing ) }
+ disabled={ isProcessingAudio }
+ isBusy={ isProcessingAudio }
+ >
+ { isProcessingAudio
+ ? __( 'Generating audio..', 'classifai' )
+ : __( 'Preview', 'classifai' ) }
+
+
+ >
+ ) }
+ { sourceUrl && (
+ setIsPreviewing( false ) }
+ >
+ ) }
+
+ );
+};
+
+registerPlugin( 'classifai-plugin-text-to-speech', {
+ render: TextToSpeechPlugin,
+} );
+
+let saveHappened = false;
+let showingNotice = false;
+
+subscribe( () => {
+ if ( saveHappened === false ) {
+ saveHappened = wp.data.select( 'core/editor' ).isSavingPost() === true;
+ }
+
+ if (
+ saveHappened &&
+ wp.data.select( 'core/editor' ).isSavingPost() === false &&
+ showingNotice === false
+ ) {
+ const meta = wp.data
+ .select( 'core/editor' )
+ .getCurrentPostAttribute( 'meta' );
+ if ( meta && meta._classifai_text_to_speech_error ) {
+ showingNotice = true;
+ const error = JSON.parse( meta._classifai_text_to_speech_error );
+ wp.data
+ .dispatch( 'core/notices' )
+ .createErrorNotice(
+ `Audio generation failed. Error: ${ error.code } - ${ error.message }`
+ );
+ saveHappened = false;
+ showingNotice = false;
+ }
+ }
+} );
diff --git a/src/js/store/register.js b/src/js/features/text-to-speech/store.js
similarity index 100%
rename from src/js/store/register.js
rename to src/js/features/text-to-speech/store.js
diff --git a/src/js/openai/classic-editor-title-generator.js b/src/js/features/title-generation/classic/index.js
similarity index 69%
rename from src/js/openai/classic-editor-title-generator.js
rename to src/js/features/title-generation/classic/index.js
index dcfe7e5ab..647b8156b 100644
--- a/src/js/openai/classic-editor-title-generator.js
+++ b/src/js/features/title-generation/classic/index.js
@@ -1,6 +1,6 @@
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
-import '../../scss/openai/classic-editor-title-generator.scss';
+import './index.scss';
const ClassifAI = window.ClassifAI || {};
const classifaiChatGPTData = window.classifaiChatGPTData || {};
@@ -44,15 +44,15 @@ const scriptData = classifaiChatGPTData.enabledFeatures.reduce(
// Creates and appends the "Generate titles" button.
$( ' ', {
text: scriptData?.title?.buttonText ?? '',
- class: 'classifai-openai__title-generate-btn--text',
+ class: 'classifai-title-generation__title-generate-btn--text',
} )
.wrap(
- '
'
+ '
'
)
.parent()
.append(
$( ' ', {
- class: 'classifai-openai__title-generate-btn--spinner',
+ class: 'classifai-title-generation__title-generate-btn--spinner',
} )
)
.appendTo( '#titlewrap' );
@@ -62,8 +62,8 @@ const scriptData = classifaiChatGPTData.enabledFeatures.reduce(
// Callback to hide the popup.
const hidePopup = () => {
- $( '#classifai-openai__results' )
- .removeClass( 'classifai-openai--fade-in' )
+ $( '#classifai-title-generation__results' )
+ .removeClass( 'classifai-title-generation--fade-in' )
.delay( 300 )
.fadeOut( 0 );
};
@@ -72,7 +72,7 @@ const scriptData = classifaiChatGPTData.enabledFeatures.reduce(
const applyTitle = ( e ) => {
const selectBtnEl = $( e.target );
const textarea = selectBtnEl
- .closest( '.classifai-openai__result-item' )
+ .closest( '.classifai-title-generation__result-item' )
.find( 'textarea' );
const isDirty = isPostChanged();
$( '#title' ).val( textarea.val() ).trigger( 'input' );
@@ -88,12 +88,12 @@ const scriptData = classifaiChatGPTData.enabledFeatures.reduce(
return;
}
- $( '#classifai-openai__results-content' ).html( '' );
+ $( '#classifai-title-generation__results-content' ).html( '' );
const generateTextEl = $(
- '.classifai-openai__title-generate-btn--text'
+ '.classifai-title-generation__title-generate-btn--text'
);
const spinnerEl = $(
- '.classifai-openai__title-generate-btn--spinner'
+ '.classifai-title-generation__title-generate-btn--spinner'
);
generateTextEl.css( 'opacity', '0' );
@@ -115,17 +115,19 @@ const scriptData = classifaiChatGPTData.enabledFeatures.reduce(
text: title,
} )
.wrap(
- `
`
+ `
`
)
.parent()
.append(
$( ' ', {
text: scriptData.title.selectBtnText,
type: 'button',
- class: 'button classifai-openai__select-title',
+ class: 'button classifai-title-generation__select-title',
} )
)
- .appendTo( '#classifai-openai__results-content' );
+ .appendTo(
+ '#classifai-title-generation__results-content'
+ );
} );
// Append disable feature link.
@@ -145,15 +147,15 @@ const scriptData = classifaiChatGPTData.enabledFeatures.reduce(
class: 'classifai-disable-feature-link',
} )
.wrap(
- `
`
+ `
`
)
.parent()
- .appendTo( '#classifai-openai__modal' );
+ .appendTo( '#classifai-title-generation__modal' );
}
- $( '#classifai-openai__results' )
+ $( '#classifai-title-generation__results' )
.show()
- .addClass( 'classifai-openai--fade-in' );
+ .addClass( 'classifai-title-generation--fade-in' );
} )
.catch( ( error ) => {
generateTextEl.css( 'opacity', '1' );
@@ -162,41 +164,49 @@ const scriptData = classifaiChatGPTData.enabledFeatures.reduce(
$( '' )
.text( error?.message )
- .wrap( `
` )
- .appendTo( '#classifai-openai__results-content' );
+ .wrap(
+ `
`
+ )
+ .appendTo(
+ '#classifai-title-generation__results-content'
+ );
- $( '#classifai-openai__results' )
+ $( '#classifai-title-generation__results' )
.show()
- .addClass( 'classifai-openai--fade-in' );
+ .addClass( 'classifai-title-generation--fade-in' );
} );
};
// Event handler registration to generate the title.
$( document ).on(
'click',
- '#classifai-openai__title-generate-btn',
+ '#classifai-title-generation__title-generate-btn',
generateTitle
);
// Event handler registration to hide the popup.
- $( document ).on( 'click', '#classifai-openai__overlay', hidePopup );
$( document ).on(
'click',
- '#classifai-openai__close-modal-button',
+ '#classifai-title-generation__overlay',
+ hidePopup
+ );
+ $( document ).on(
+ 'click',
+ '#classifai-title-generation__close-modal-button',
hidePopup
);
// Event handler registration to apply the selected title to the post title.
$( document ).on(
'click',
- '.classifai-openai__select-title',
+ '.classifai-title-generation__select-title',
applyTitle
);
// Sets the modal title.
- const resultWrapper = $( '#classifai-openai__results' );
+ const resultWrapper = $( '#classifai-title-generation__results' );
resultWrapper
- .find( '#classifai-openai__results-title' )
+ .find( '#classifai-title-generation__results-title' )
.text( scriptData.title.modalTitle );
}
} )( jQuery );
diff --git a/src/scss/openai/classic-editor-title-generator.scss b/src/js/features/title-generation/classic/index.scss
similarity index 74%
rename from src/scss/openai/classic-editor-title-generator.scss
rename to src/js/features/title-generation/classic/index.scss
index 86c7ca0ff..06b786a67 100644
--- a/src/scss/openai/classic-editor-title-generator.scss
+++ b/src/js/features/title-generation/classic/index.scss
@@ -1,13 +1,13 @@
#titlewrap {
display: flex;
- #classifai-openai__title-generate-btn {
+ #classifai-title-generation__title-generate-btn {
position: relative;
margin-left: 0.5rem;
height: 2.348rem;
line-height: 2.348rem;
- & > .classifai-openai__title-generate-btn--spinner {
+ & > .classifai-title-generation__title-generate-btn--spinner {
display: none;
position: absolute;
top: 50%;
@@ -18,38 +18,7 @@
}
}
-#postexcerpt {
- #classifai-openai__excerpt-generate-btn {
- display: block;
- width: 160px;
- max-width: 100%;
- margin: 1em 0 0 auto;
- text-align: center;
- position: relative;
-
- & > .classifai-openai__excerpt-generate-btn--spinner {
- display: none;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate( -50%, -50% );
- line-height: 29px;
- }
- }
-
- .classifai-openai__excerpt-generate-error {
- display: none;
- color: #dc3232;
- text-align: right;
- }
-
- .classifai-openai__excerpt-generate-disable-link {
- text-align: right;
- margin-top: 6px;
- }
-}
-
-#classifai-openai {
+#classifai-title-generation {
&__overlay {
position: fixed;
top: 0;
@@ -114,7 +83,7 @@
}
}
-.classifai-openai {
+.classifai-title-generation {
&__result-item {
flex-grow: 1;
flex-basis: 0;
@@ -132,12 +101,12 @@
}
&--fade-in {
- #classifai-openai__overlay {
+ #classifai-title-generation__overlay {
opacity: 1 !important;
transition: opacity 0.3s ease !important;
}
- #classifai-openai__modal {
+ #classifai-title-generation__modal {
opacity: 1 !important;
transform: translate(-50%, 0) !important;
transition: opacity 0.3s ease, transform 0.3s ease !important;
diff --git a/src/js/gutenberg-plugins/post-status-info.js b/src/js/features/title-generation/index.js
similarity index 70%
rename from src/js/gutenberg-plugins/post-status-info.js
rename to src/js/features/title-generation/index.js
index d386b9cd7..c939b7caa 100644
--- a/src/js/gutenberg-plugins/post-status-info.js
+++ b/src/js/features/title-generation/index.js
@@ -1,12 +1,25 @@
+/**
+ * External Dependencies.
+ */
import { dispatch, select } from '@wordpress/data';
import { PluginPostStatusInfo } from '@wordpress/edit-post';
import { PostTypeSupportCheck } from '@wordpress/editor';
-import { Button, Modal, Spinner } from '@wordpress/components';
+import {
+ Button,
+ Modal,
+ Spinner,
+ TextareaControl,
+ BaseControl,
+} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { registerPlugin } from '@wordpress/plugins';
import { useState } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
-import { DisableFeatureButton } from '../components';
+
+/**
+ * Internal Dependencies.
+ */
+import { DisableFeatureButton } from '../../components';
const { classifaiChatGPTData } = window;
@@ -18,7 +31,7 @@ const RenderError = ( { error } ) => {
return { error }
;
};
-const PostStatusInfo = () => {
+const TitleGenerationPlugin = () => {
const [ isLoading, setIsLoading ] = useState( false );
const [ isOpen, setOpen ] = useState( false );
const [ error, setError ] = useState( false );
@@ -75,39 +88,42 @@ const PostStatusInfo = () => {
{ dataToRender.map( ( item, i ) => {
return (
-
- {
- const isDirty =
- select(
- 'core/editor'
- ).isEditedPostDirty();
- dispatch( 'core/editor' ).editPost( {
- title: data[ i ],
- } );
- closeModal();
- if ( ! isDirty ) {
- await dispatch(
- 'core'
- ).saveEditedEntityRecord(
- 'postType',
- postType,
- postId
- );
- }
- } }
- >
- { __( 'Select', 'classifai' ) }
-
+
+ {
+ dataToRender[ i ] = e.target.value;
+ setData( dataToRender );
+ } }
+ />
+ {
+ const isDirty =
+ select(
+ 'core/editor'
+ ).isEditedPostDirty();
+ dispatch( 'core/editor' ).editPost( {
+ title: data[ i ],
+ } );
+ closeModal();
+ if ( ! isDirty ) {
+ await dispatch(
+ 'core'
+ ).saveEditedEntityRecord(
+ 'postType',
+ postType,
+ postId
+ );
+ }
+ } }
+ >
+ { __( 'Select', 'classifai' ) }
+
+
+
);
} ) }
@@ -153,4 +169,6 @@ const PostStatusInfo = () => {
);
};
-registerPlugin( 'classifai-status-info', { render: PostStatusInfo } );
+registerPlugin( 'classifai-plugin-title-generation', {
+ render: TitleGenerationPlugin,
+} );
diff --git a/src/js/gutenberg-plugin.js b/src/js/gutenberg-plugin.js
deleted file mode 100644
index 42cc12c50..000000000
--- a/src/js/gutenberg-plugin.js
+++ /dev/null
@@ -1,665 +0,0 @@
-/* eslint-disable no-unused-vars */
-import { ReactComponent as icon } from '../../assets/img/block-icon.svg';
-import { handleClick } from './helpers';
-import { useSelect, useDispatch, subscribe } from '@wordpress/data';
-import { PluginDocumentSettingPanel } from '@wordpress/edit-post';
-import {
- Button,
- Icon,
- ToggleControl,
- BaseControl,
- Modal,
-} from '@wordpress/components';
-import { __, sprintf } from '@wordpress/i18n';
-import { registerPlugin } from '@wordpress/plugins';
-import { useState, useEffect, useRef } from '@wordpress/element';
-import { store as postAudioStore } from './store/register';
-import TaxonomyControls from './taxonomy-controls';
-import PrePubClassifyPost from './gutenberg-plugins/pre-publish-classify-post';
-import { DisableFeatureButton } from './components';
-
-const { classifaiPostData, classifaiTTSEnabled } = window;
-
-/**
- * Create the ClassifAI icon
- */
-const ClassifAIIcon = () => (
-
-);
-
-/**
- * ClassificationToggle Component.
- *
- * Used to toggle the classification process on or off.
- */
-const ClassificationToggle = () => {
- // Use the datastore to retrieve all the meta for this post.
- const processContent = useSelect( ( select ) =>
- select( 'core/editor' ).getEditedPostAttribute(
- 'classifai_process_content'
- )
- );
-
- // Use the datastore to tell the post to update the meta.
- const { editPost } = useDispatch( 'core/editor' );
- const enabled = 'yes' === processContent ? 'yes' : 'no';
-
- return (
- {
- editPost( { classifai_process_content: value ? 'yes' : 'no' } );
- } }
- />
- );
-};
-
-/**
- * Classify button.
- *
- * Used to manually classify the content.
- */
-const ClassificationButton = () => {
- const processContent = useSelect( ( select ) =>
- select( 'core/editor' ).getEditedPostAttribute(
- 'classifai_process_content'
- )
- );
-
- const { select, dispatch } = wp.data;
- const postId = select( 'core/editor' ).getCurrentPostId();
- const postType = select( 'core/editor' ).getCurrentPostType();
- const postTypeLabel =
- select( 'core/editor' ).getPostTypeLabel() || __( 'Post', 'classifai' );
-
- const [ isLoading, setLoading ] = useState( false );
- const [ resultReceived, setResultReceived ] = useState( false );
- const [ isOpen, setOpen ] = useState( false );
- const [ popupOpened, setPopupOpened ] = useState( false );
- const openModal = () => setOpen( true );
- const closeModal = () => setOpen( false );
-
- const [ taxQuery, setTaxQuery ] = useState( [] );
- const [ featureTaxonomies, setFeatureTaxonomies ] = useState( [] );
- let [ taxTermsAI, setTaxTermsAI ] = useState( [] );
-
- /**
- * Callback function to handle API response.
- *
- * @param {Object} resp Response from the API.
- * @param {Object} callbackArgs Callback arguments.
- */
- const buttonClickCallBack = async ( resp, callbackArgs ) => {
- if ( resp && resp.terms ) {
- // set feature taxonomies
- if ( resp?.feature_taxonomies ) {
- setFeatureTaxonomies( resp.feature_taxonomies );
- }
-
- const taxonomies = resp.terms;
- const taxTerms = {};
- const taxTermsExisting = {};
-
- // get current terms of the post
- const currentTerms = select( 'core' ).getEntityRecord(
- 'postType',
- postType,
- postId
- );
-
- Object.keys( taxonomies ).forEach( ( taxonomy ) => {
- let tax = taxonomy;
- if ( 'post_tag' === taxonomy ) {
- tax = 'tags';
- }
- if ( 'category' === taxonomy ) {
- tax = 'categories';
- }
-
- const currentTermsOfTaxonomy = currentTerms[ tax ];
- if ( currentTermsOfTaxonomy ) {
- taxTermsExisting[ tax ] = currentTermsOfTaxonomy;
- }
-
- const newTerms = Object.values( resp.terms[ taxonomy ] );
- if ( newTerms && Object.keys( newTerms ).length ) {
- // Loop through each term and add in taxTermsAI if it does not exist in the post.
- taxTermsAI = taxTermsAI || {};
- Object( newTerms ).forEach( ( termId ) => {
- if ( taxTermsExisting[ tax ] ) {
- const matchedTerm = taxTermsExisting[ tax ].find(
- ( termID ) => termID === termId
- );
- if ( ! matchedTerm ) {
- taxTermsAI[ tax ] = taxTermsAI[ tax ] || [];
- // push only if not exist already
- if ( ! taxTermsAI[ tax ].includes( termId ) ) {
- taxTermsAI[ tax ].push( termId );
- }
- }
- }
- } );
-
- // update the taxTerms
- taxTerms[ tax ] = newTerms;
- }
- } );
-
- // Merge taxterms with taxTermsExisting and remove duplicates
- Object.keys( taxTermsExisting ).forEach( ( taxonomy ) => {
- if ( taxTerms[ taxonomy ] ) {
- // Merge taxTermsExisting into taxTerms
- taxTerms[ taxonomy ] = taxTerms[ taxonomy ].concat(
- taxTermsExisting[ taxonomy ]
- );
- } else {
- // Initialize taxTerms with taxTermsExisting if not already set
- taxTerms[ taxonomy ] = taxTermsExisting[ taxonomy ];
- }
-
- // Remove duplicate items from taxTerms
- taxTerms[ taxonomy ] = [ ...new Set( taxTerms[ taxonomy ] ) ];
- } );
-
- setTaxQuery( taxTerms );
- setTaxTermsAI( taxTermsAI );
- }
- if ( callbackArgs?.openPopup ) {
- openModal();
- setPopupOpened( true );
- }
- setLoading( false );
- setResultReceived( true );
- };
-
- /**
- * Save the terms (Modal).
- *
- * @param {Object} taxTerms Taxonomy terms.
- */
- const saveTerms = async ( taxTerms ) => {
- // Remove index values from the nested object
- // Convert the object into an array of key-value pairs
- const taxTermsArray = Object.entries( taxTerms );
-
- // Remove index values from the nested objects and convert back to an object
- const newtaxTerms = Object.fromEntries(
- taxTermsArray.map( ( [ key, value ] ) => {
- if ( typeof value === 'object' ) {
- return [ key, Object.values( value ) ];
- }
- return [ key, value ];
- } )
- );
-
- await dispatch( 'core' ).editEntityRecord(
- 'postType',
- postType,
- postId,
- newtaxTerms
- );
-
- // If no edited values in post trigger save.
- const isDirty = await select( 'core/editor' ).isEditedPostDirty();
- if ( ! isDirty ) {
- await dispatch( 'core' ).saveEditedEntityRecord(
- 'postType',
- postType,
- postId
- );
- }
-
- // Display success notice.
- dispatch( 'core/notices' ).createSuccessNotice(
- sprintf(
- /** translators: %s is post type label. */
- __( '%s classified successfully.', 'classifai' ),
- postTypeLabel
- ),
- { type: 'snackbar' }
- );
- closeModal();
- };
-
- // Display classify post button only when process content on update is disabled.
- const enabled = 'no' === processContent ? 'no' : 'yes';
- if ( 'yes' === enabled ) {
- return null;
- }
-
- const buttonText = __( 'Suggest terms & tags', 'classifai' );
-
- let updatedTaxQuery = Object.entries( taxQuery || {} ).reduce(
- ( accumulator, [ taxonomySlug, terms ] ) => {
- accumulator[ taxonomySlug ] = terms;
-
- return accumulator;
- },
- {}
- );
-
- if ( updatedTaxQuery.taxQuery ) {
- updatedTaxQuery = updatedTaxQuery.taxQuery;
- }
-
- const modalData = (
- <>
- {
- setTaxQuery( newTaxQuery );
- } }
- query={ {
- contentPostType: postType,
- featureTaxonomies,
- taxQuery: updatedTaxQuery,
- taxTermsAI: taxTermsAI || {},
- isLoading,
- } }
- />
-
-
- { sprintf(
- /* translators: %s is post type label */
- __(
- 'Note that the lists above include any pre-existing terms from this %s.',
- 'classifai'
- ),
- postTypeLabel
- ) }
-
- { __(
- 'AI recommendations saved to this post will not include the "[AI]" text.',
- 'classifai'
- ) }
-
-
saveTerms( updatedTaxQuery ) }
- >
- { __( 'Save', 'classifai' ) }
-
-
-
- >
- );
-
- return (
-
- { isOpen && (
-
- { modalData }
-
- ) }
-
{
- handleClick( {
- button: e.target,
- endpoint: '/classifai/v1/classify/',
- callback: buttonClickCallBack,
- callbackArgs: {
- openPopup: true,
- },
- buttonText,
- linkTerms: false,
- } );
- } }
- >
- { buttonText }
-
-
-
-
- { ! resultReceived && (
- <>
- {
- handleClick( {
- button: e.target,
- endpoint: '/classifai/v1/classify/',
- callback: buttonClickCallBack,
- buttonText,
- linkTerms: false,
- } );
- } }
- >
- { buttonText }
-
-
-
- >
- ) }
- { resultReceived && modalData }
-
-
- );
-};
-
-/**
- * ClassifAI Text to Audio component.
- */
-const ClassifAITTS = () => {
- // State of the audio being previewed in PluginDocumentSettingPanel.
- const [ isPreviewing, setIsPreviewing ] = useState( false );
-
- const [ timestamp, setTimestamp ] = useState( new Date().getTime() );
-
- // Indicates whether speech synthesis is enabled for the current post.
- const isSynthesizeSpeech = useSelect( ( select ) =>
- select( 'core/editor' ).getEditedPostAttribute(
- 'classifai_synthesize_speech'
- )
- );
-
- // Indicates whether generated audio should be displayed on the frontend.
- const displayGeneratedAudio = useSelect( ( select ) =>
- select( 'core/editor' ).getEditedPostAttribute(
- 'classifai_display_generated_audio'
- )
- );
-
- // Post type label.
- const postTypeLabel = useSelect(
- ( select ) =>
- ( typeof select( 'core/editor' ).getPostTypeLabel !== 'undefined' &&
- select( 'core/editor' ).getPostTypeLabel() ) ||
- __( 'Post', 'classifai' )
- );
-
- // Says whether speech synthesis is in progress.
- const isProcessingAudio = useSelect( ( select ) =>
- select( postAudioStore ).getIsProcessing()
- );
-
- // The audio ID saved in the DB for the current post.
- const defaultAudioId = useSelect( ( select ) =>
- select( 'core/editor' ).getEditedPostAttribute(
- 'classifai_post_audio_id'
- )
- );
-
- // New audio ID in case it is regenerated manually or through publishing/updating the current post.
- const audioId =
- useSelect( ( select ) => select( postAudioStore ).getAudioId() ) ||
- defaultAudioId;
-
- // Get the attachment data by audio ID.
- const attachments = useSelect( ( select ) =>
- select( 'core' ).getEntityRecords( 'postType', 'attachment', {
- include: [ audioId ],
- } )
- );
-
- // Get URL for the attachment.
- const sourceUrl =
- attachments && attachments.length > 0 && attachments[ 0 ].source_url;
-
- const isProcessingAudioProgress = useRef( false );
- const isPostSavingInProgress = useRef( false );
- const { isSavingPost } = useSelect( ( select ) => {
- return {
- isSavingPost: select( 'core/editor' ).isSavingPost(),
- };
- } );
- const { isAutosavingPost } = useSelect( ( select ) => {
- return {
- isSavingPost: select( 'core/editor' ).isAutosavingPost(),
- };
- } );
-
- // Handles playing/pausing post audio during preview.
- useEffect( () => {
- const audioEl = document.getElementById( 'classifai-audio-preview' );
-
- if ( ! audioEl ) {
- return;
- }
-
- if ( isPreviewing ) {
- audioEl.play();
- } else {
- audioEl.pause();
- }
- }, [ isPreviewing ] );
-
- // Generates a unique timestamp to cache bust audio file.
- useEffect( () => {
- if ( isProcessingAudio ) {
- isProcessingAudioProgress.current = true;
- }
-
- if ( isProcessingAudioProgress.current && ! isProcessingAudio ) {
- setTimestamp( new Date().getTime() );
- }
- }, [ isProcessingAudio ] );
-
- useEffect( () => {
- // Code to run during post saving is in process.
- if (
- isSavingPost &&
- ! isAutosavingPost &&
- ! isPostSavingInProgress.current
- ) {
- isPostSavingInProgress.current = true;
- if ( isSynthesizeSpeech ) {
- wp.data.dispatch( postAudioStore ).setIsProcessing( true );
- }
- }
-
- if (
- ! isSavingPost &&
- ! isAutosavingPost &&
- isPostSavingInProgress.current
- ) {
- // Code to run after post is done saving.
- isPostSavingInProgress.current = false;
- wp.data.dispatch( postAudioStore ).setIsProcessing( false );
- }
- }, [ isSavingPost, isAutosavingPost, isSynthesizeSpeech ] );
-
- // Fetches the latest audio file to avoid disk cache.
- const cacheBustingUrl = `${ sourceUrl }?ver=${ timestamp }`;
-
- let audioIcon = 'controls-play';
-
- if ( isProcessingAudio ) {
- audioIcon = 'format-audio';
- } else if ( isPreviewing ) {
- audioIcon = 'controls-pause';
- }
-
- return (
- <>
- {
- wp.data.dispatch( 'core/editor' ).editPost( {
- classifai_synthesize_speech: value,
- } );
- } }
- disabled={ isProcessingAudio }
- isBusy={ isProcessingAudio }
- />
- { sourceUrl && (
- <>
- {
- wp.data.dispatch( 'core/editor' ).editPost( {
- classifai_display_generated_audio: value,
- } );
- } }
- disabled={ isProcessingAudio }
- isBusy={ isProcessingAudio }
- />
-
- }
- variant="secondary"
- onClick={ () => setIsPreviewing( ! isPreviewing ) }
- disabled={ isProcessingAudio }
- isBusy={ isProcessingAudio }
- >
- { isProcessingAudio
- ? __( 'Generating audio..', 'classifai' )
- : __( 'Preview', 'classifai' ) }
-
-
- >
- ) }
- { sourceUrl && (
- setIsPreviewing( false ) }
- >
- ) }
- >
- );
-};
-
-/**
- * Add the ClassifAI panel to Gutenberg
- */
-const ClassifAIPlugin = () => {
- const postType = useSelect( ( select ) =>
- select( 'core/editor' ).getCurrentPostType()
- );
- const postStatus = useSelect( ( select ) =>
- select( 'core/editor' ).getCurrentPostAttribute( 'status' )
- );
-
- // Ensure that at least one feature is enabled.
- const isNLULanguageProcessingEnabled =
- classifaiPostData && classifaiPostData.NLUEnabled;
-
- // Ensure we are on a supported post type, checking settings from all features.
- const isNLUPostTypeSupported =
- classifaiPostData &&
- classifaiPostData.supportedPostTypes &&
- classifaiPostData.supportedPostTypes.includes( postType );
-
- // Ensure we are on a supported post status, checking settings from all features.
- const isNLUPostStatusSupported =
- classifaiPostData &&
- classifaiPostData.supportedPostStatues &&
- classifaiPostData.supportedPostStatues.includes( postStatus );
-
- // Ensure the user has permissions to use the feature.
- const userHasNLUPermissions =
- classifaiPostData &&
- ! (
- classifaiPostData.noPermissions &&
- 1 === parseInt( classifaiPostData.noPermissions )
- );
-
- const nluPermissionCheck =
- userHasNLUPermissions &&
- isNLULanguageProcessingEnabled &&
- isNLUPostTypeSupported &&
- isNLUPostStatusSupported;
-
- return (
-
- <>
- { nluPermissionCheck && (
- <>
-
- { nluPermissionCheck && }
- >
- ) }
- { classifaiTTSEnabled && }
- >
-
- );
-};
-
-let saveHappened = false;
-let showingNotice = false;
-
-subscribe( () => {
- if ( saveHappened === false ) {
- saveHappened = wp.data.select( 'core/editor' ).isSavingPost() === true;
- }
-
- if (
- saveHappened &&
- wp.data.select( 'core/editor' ).isSavingPost() === false &&
- showingNotice === false
- ) {
- const meta = wp.data
- .select( 'core/editor' )
- .getCurrentPostAttribute( 'meta' );
- if ( meta && meta._classifai_text_to_speech_error ) {
- showingNotice = true;
- const error = JSON.parse( meta._classifai_text_to_speech_error );
- wp.data
- .dispatch( 'core/notices' )
- .createErrorNotice(
- `Audio generation failed. Error: ${ error.code } - ${ error.message }`
- );
- saveHappened = false;
- showingNotice = false;
- }
- }
-} );
-
-registerPlugin( 'classifai-plugin', { render: ClassifAIPlugin } );
diff --git a/tests/cypress/integration/language-processing/excerpt-generation-azure-openai.test.js b/tests/cypress/integration/language-processing/excerpt-generation-azure-openai.test.js
index d01786ea2..813ba3217 100644
--- a/tests/cypress/integration/language-processing/excerpt-generation-azure-openai.test.js
+++ b/tests/cypress/integration/language-processing/excerpt-generation-azure-openai.test.js
@@ -117,10 +117,10 @@ describe( '[Language processing] Excerpt Generation Tests', () => {
cy.get( '#postexcerpt-hide' ).check( { force: true } );
// Verify button exists.
- cy.get( '#classifai-openai__excerpt-generate-btn' ).should( 'exist' );
+ cy.get( '#classifai-excerpt-generation__excerpt-generate-btn' ).should( 'exist' );
// Click on button and verify data loads in.
- cy.get( '#classifai-openai__excerpt-generate-btn' ).click();
+ cy.get( '#classifai-excerpt-generation__excerpt-generate-btn' ).click();
cy.get( '#excerpt' ).should( 'have.value', data );
cy.disableClassicEditor();
diff --git a/tests/cypress/integration/language-processing/excerpt-generation-googleai-gemini-api.test.js b/tests/cypress/integration/language-processing/excerpt-generation-googleai-gemini-api.test.js
index 07555d6ad..9c78624b8 100644
--- a/tests/cypress/integration/language-processing/excerpt-generation-googleai-gemini-api.test.js
+++ b/tests/cypress/integration/language-processing/excerpt-generation-googleai-gemini-api.test.js
@@ -106,10 +106,10 @@ describe( '[Language processing] Excerpt Generation Tests', () => {
cy.get( '#postexcerpt-hide' ).check( { force: true } );
// Verify button exists.
- cy.get( '#classifai-openai__excerpt-generate-btn' ).should( 'exist' );
+ cy.get( '#classifai-excerpt-generation__excerpt-generate-btn' ).should( 'exist' );
// Click on button and verify data loads in.
- cy.get( '#classifai-openai__excerpt-generate-btn' ).click();
+ cy.get( '#classifai-excerpt-generation__excerpt-generate-btn' ).click();
cy.get( '#excerpt' ).should( 'have.value', data );
cy.disableClassicEditor();
diff --git a/tests/cypress/integration/language-processing/excerpt-generation-openai-chatgpt.test.js b/tests/cypress/integration/language-processing/excerpt-generation-openai-chatgpt.test.js
index 335e60353..89bacb92b 100644
--- a/tests/cypress/integration/language-processing/excerpt-generation-openai-chatgpt.test.js
+++ b/tests/cypress/integration/language-processing/excerpt-generation-openai-chatgpt.test.js
@@ -103,10 +103,10 @@ describe( '[Language processing] Excerpt Generation Tests', () => {
cy.get( '#postexcerpt-hide' ).check( { force: true } );
// Verify button exists.
- cy.get( '#classifai-openai__excerpt-generate-btn' ).should( 'exist' );
+ cy.get( '#classifai-excerpt-generation__excerpt-generate-btn' ).should( 'exist' );
// Click on button and verify data loads in.
- cy.get( '#classifai-openai__excerpt-generate-btn' ).click();
+ cy.get( '#classifai-excerpt-generation__excerpt-generate-btn' ).click();
cy.get( '#excerpt' ).should( 'have.value', data );
cy.disableClassicEditor();
diff --git a/tests/cypress/integration/language-processing/title-generation-azure-openai.test.js b/tests/cypress/integration/language-processing/title-generation-azure-openai.test.js
index 598df389d..a0a1b5e8f 100644
--- a/tests/cypress/integration/language-processing/title-generation-azure-openai.test.js
+++ b/tests/cypress/integration/language-processing/title-generation-azure-openai.test.js
@@ -140,15 +140,15 @@ describe( '[Language processing] Title Generation Tests', () => {
cy.visit( '/wp-admin/post-new.php' );
- cy.get( '#classifai-openai__title-generate-btn' ).click();
- cy.get( '#classifai-openai__modal' ).should( 'be.visible' );
- cy.get( '.classifai-openai__result-item' )
+ cy.get( '#classifai-title-generation__title-generate-btn' ).click();
+ cy.get( '#classifai-title-generation__modal' ).should( 'be.visible' );
+ cy.get( '.classifai-title-generation__result-item' )
.first()
.find( 'textarea' )
.should( 'have.value', data );
- cy.get( '.classifai-openai__select-title' ).first().click();
- cy.get( '#classifai-openai__modal' ).should( 'not.be.visible' );
+ cy.get( '.classifai-title-generation__select-title' ).first().click();
+ cy.get( '#classifai-title-generation__modal' ).should( 'not.be.visible' );
cy.get( '#title' ).should( 'have.value', data );
cy.disableClassicEditor();
diff --git a/tests/cypress/integration/language-processing/title-generation-googleai-gemini-api.test.js b/tests/cypress/integration/language-processing/title-generation-googleai-gemini-api.test.js
index c8adb87ca..681dd0bf2 100644
--- a/tests/cypress/integration/language-processing/title-generation-googleai-gemini-api.test.js
+++ b/tests/cypress/integration/language-processing/title-generation-googleai-gemini-api.test.js
@@ -124,15 +124,15 @@ describe( '[Language processing] Title Generation Tests', () => {
cy.visit( '/wp-admin/post-new.php' );
- cy.get( '#classifai-openai__title-generate-btn' ).click();
- cy.get( '#classifai-openai__modal' ).should( 'be.visible' );
- cy.get( '.classifai-openai__result-item' )
+ cy.get( '#classifai-title-generation__title-generate-btn' ).click();
+ cy.get( '#classifai-title-generation__modal' ).should( 'be.visible' );
+ cy.get( '.classifai-title-generation__result-item' )
.first()
.find( 'textarea' )
.should( 'have.value', data );
- cy.get( '.classifai-openai__select-title' ).first().click();
- cy.get( '#classifai-openai__modal' ).should( 'not.be.visible' );
+ cy.get( '.classifai-title-generation__select-title' ).first().click();
+ cy.get( '#classifai-title-generation__modal' ).should( 'not.be.visible' );
cy.get( '#title' ).should( 'have.value', data );
cy.disableClassicEditor();
diff --git a/tests/cypress/integration/language-processing/title-generation-openai-chatgpt.test.js b/tests/cypress/integration/language-processing/title-generation-openai-chatgpt.test.js
index bbaf7b365..33fe9e31f 100644
--- a/tests/cypress/integration/language-processing/title-generation-openai-chatgpt.test.js
+++ b/tests/cypress/integration/language-processing/title-generation-openai-chatgpt.test.js
@@ -121,15 +121,15 @@ describe( '[Language processing] Title Generation Tests', () => {
cy.visit( '/wp-admin/post-new.php' );
- cy.get( '#classifai-openai__title-generate-btn' ).click();
- cy.get( '#classifai-openai__modal' ).should( 'be.visible' );
- cy.get( '.classifai-openai__result-item' )
+ cy.get( '#classifai-title-generation__title-generate-btn' ).click();
+ cy.get( '#classifai-title-generation__modal' ).should( 'be.visible' );
+ cy.get( '.classifai-title-generation__result-item' )
.first()
.find( 'textarea' )
.should( 'have.value', data );
- cy.get( '.classifai-openai__select-title' ).first().click();
- cy.get( '#classifai-openai__modal' ).should( 'not.be.visible' );
+ cy.get( '.classifai-title-generation__select-title' ).first().click();
+ cy.get( '#classifai-title-generation__modal' ).should( 'not.be.visible' );
cy.get( '#title' ).should( 'have.value', data );
cy.disableClassicEditor();
diff --git a/webpack.config.js b/webpack.config.js
index 6acba9b7c..322100481 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -8,41 +8,34 @@ module.exports = {
path: path.resolve( __dirname, 'dist' ),
},
entry: {
- editor: [ './src/js/editor.js' ],
- 'editor-ocr': [ './src/js/editor-ocr.js' ],
- media: [ './src/js/media.js' ],
admin: [ './src/js/admin.js' ],
- 'language-processing': [ './src/js/language-processing.js' ],
- 'gutenberg-plugin': [ './src/js/gutenberg-plugin.js' ],
- 'post-audio-controls': [ './src/js/post-audio-controls.js' ],
- 'post-status-info': [
- './src/js/gutenberg-plugins/post-status-info.js',
- ],
- 'content-resizing-plugin': [
- './src/js/gutenberg-plugins/content-resizing-plugin.js',
- ],
'recommended-content-block': [
'./includes/Classifai/Blocks/recommended-content-block/index.js',
],
'recommended-content-block-frontend': [
'./includes/Classifai/Blocks/recommended-content-block/frontend.js',
],
- 'post-excerpt': [ './src/js/post-excerpt/index.js' ],
- 'media-modal': [ './src/js/media-modal/index.js' ],
- 'inserter-media-category': [
- './src/js/gutenberg-plugins/inserter-media-category.js',
- ],
- 'generate-excerpt-classic': [
- './src/js/openai/classic-editor-excerpt-generator.js',
- ],
- 'generate-title-classic': [
- './src/js/openai/classic-editor-title-generator.js',
- ],
- commands: [ './src/js/gutenberg-plugins/commands.js' ],
- 'generate-image-media-upload': [
- './src/js/media-modal/views/generate-image-media-upload.js',
- ],
- 'extend-image-blocks': './src/js/extend-image-block-generate-image.js',
+
+ 'classifai-plugin-media-processing': './src/js/features/media-processing/media-upload.js',
+ 'classifai-plugin-editor-ocr': './src/js/features/media-processing/editor-ocr.js',
+ 'classifai-plugin-commands': './src/js/features/commands.js',
+ 'classifai-plugin-classification': './src/js/features/classification/index.js',
+ 'classifai-plugin-classification-previewer': './src/js/features/classification/previewer.js',
+ 'classifai-plugin-classification-ibm-watson': './src/js/features/classification/ibm-watson.js',
+ 'classifai-plugin-fill': './src/js/features/slot-fill/index.js',
+ 'classifai-plugin-text-to-speech': './src/js/features/text-to-speech/index.js',
+ 'classifai-plugin-text-to-speech-frontend': './src/js/features/text-to-speech/frontend/index.js',
+ 'classifai-plugin-content-resizing': './src/js/features/content-resizing/index.js',
+ 'classifai-plugin-title-generation': './src/js/features/title-generation/index.js',
+ 'classifai-plugin-classic-title-generation': './src/js/features/title-generation/classic/index.js',
+ 'classifai-plugin-excerpt-generation': './src/js/features/excerpt-generation/index.js',
+ 'classifai-plugin-classic-excerpt-generation': './src/js/features/excerpt-generation/classic/index.js',
+ 'classifai-plugin-inserter-media-category': './src/js/features/image-generation/inserter-media-category.js',
+ 'classifai-plugin-image-generation-media-modal': [
+ './src/js/features/image-generation/media-modal/index.js',
+ './src/js/features/image-generation/extend-image-block-generate-image.js'
+ ],
+ 'classifai-plugin-image-generation-generate-image-media-upload': './src/js/features/image-generation/media-modal/views/generate-image-media-upload.js',
},
module: {
rules: [