Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
bfb3822
Merge branch 'develop' into trunk
dkotter Nov 9, 2023
b2f148a
Merge branch 'develop' into trunk
dkotter Dec 13, 2023
0f6b46e
Merge branch 'develop' into trunk
dkotter Jan 11, 2024
d4cafe9
Merge branch 'develop' into trunk
dkotter Feb 29, 2024
adb6739
Merge branch 'develop' into trunk
dkotter Mar 7, 2024
7262972
Merge branch 'develop' into trunk
dkotter Jul 15, 2024
9bbea9a
Merge branch 'develop' into trunk
dkotter Jul 15, 2024
6941711
Merge branch 'develop' into trunk
dkotter Aug 6, 2024
e4c2ff0
Merge branch 'trunk' of github.com:10up/classifai into trunk
Sidsector9 Oct 14, 2024
af8eb54
Merge branch 'develop' of github.com:10up/classifai into develop
Sidsector9 Nov 20, 2024
1e35ee9
Merge branch 'develop' into trunk
dkotter Dec 18, 2024
92046da
Merge branch 'develop' into trunk
dkotter Dec 18, 2024
5a1f945
Merge branch 'develop' of github.com:10up/classifai into develop
Sidsector9 Jan 14, 2025
07510e9
Merge branch 'trunk' of github.com:10up/classifai into trunk
Sidsector9 Jan 14, 2025
4e6dbb9
register Tone rewriting
Sidsector9 Jan 14, 2025
dbe1299
setup webpack and eslintrc
Sidsector9 Jan 14, 2025
a901a54
add the useSelectedBlocks hook
Sidsector9 Jan 14, 2025
749a2c7
register classifai-rewrite-tone-plugin
Sidsector9 Jan 14, 2025
f453c28
add the filterAndFlattenAllowedBlocks util
Sidsector9 Jan 14, 2025
c865d7a
add the useEditorCanvas hook
Sidsector9 Jan 18, 2025
7475d59
add the InjectIframeStyles component
Sidsector9 Jan 18, 2025
b2eb294
add the getClientIdToBlockContentMapping util
Sidsector9 Jan 18, 2025
eac4471
Merge branch 'develop' into trunk
dkotter Feb 20, 2025
217ee9a
add the stripOutermostTag util
Sidsector9 Feb 26, 2025
de829ae
implement the rewriteTone() method
Sidsector9 Feb 26, 2025
bb94e40
add replaceBlocksInSource util
Sidsector9 Feb 28, 2025
30b24b9
add InjectIframeStyles component
Sidsector9 Feb 28, 2025
1551101
:white_check_mark: implement rewrite tone with preview and replacement
Sidsector9 Feb 28, 2025
a4c7e95
Merge branch 'develop' of github.com:10up/classifai into develop
Sidsector9 Feb 28, 2025
06da5dd
Merge branch 'develop' into feat/489v2
Sidsector9 Feb 28, 2025
2876750
add tones
Sidsector9 Mar 2, 2025
2812643
move ClassifAI slot-fills to their separate files
Sidsector9 Mar 2, 2025
234b090
add tone attributes
Sidsector9 Mar 2, 2025
b9f5405
render tone descriptions
Sidsector9 Mar 2, 2025
5ffc7c7
add styling to the previewer controls
Sidsector9 Mar 2, 2025
ac412e1
handle tone attributes server-side
Sidsector9 Mar 2, 2025
465ceee
implement custom popover
Sidsector9 Mar 2, 2025
b6a0ab6
fix previewer controls alignment
Sidsector9 Mar 2, 2025
2a0f02a
fix PHPCS errors
Sidsector9 Mar 3, 2025
2bf93df
fix eslint errors
Sidsector9 Mar 3, 2025
97ff4b1
Merge branch 'develop' into trunk
dkotter Mar 12, 2025
de1e82a
Merge branch 'develop' of github.com:10up/classifai into develop
Sidsector9 Apr 9, 2025
3f99ca5
Merge branch 'develop' into feat/489
Sidsector9 Apr 9, 2025
32b0d8f
move ChatUI to components
Sidsector9 Apr 10, 2025
2903640
clean up ChatUI component
Sidsector9 Apr 10, 2025
e88f361
remove Panel from RewriteTone
Sidsector9 Apr 13, 2025
5986cb7
register and enqueue chat UI assets
Sidsector9 Apr 13, 2025
601e312
working version of Tone Rewriting integrated with ChatUI
Sidsector9 Apr 13, 2025
375b397
remove redundant files under chatUI components
Sidsector9 Apr 13, 2025
da15abb
Merge branch 'trunk' of github.com:10up/classifai into trunk
Sidsector9 Apr 28, 2025
ab7aefe
Merge branch 'trunk' into feat/489
Sidsector9 Apr 28, 2025
d2db973
improve InjectIframeStyles UI and add title props
Sidsector9 Apr 28, 2025
bd77e81
fix blocks replacement util
Sidsector9 Apr 28, 2025
8b5d4f2
Merge branch 'develop' of github.com:10up/classifai into develop
Sidsector9 Jul 5, 2025
5123504
Merge branch 'develop' into feat/489
Sidsector9 Jul 6, 2025
e6ffa2a
update available tone options
Sidsector9 Jul 6, 2025
2f9c37a
Merge branch 'develop' of github.com:10up/classifai into develop
Sidsector9 Sep 9, 2025
1ea94f0
Merge branch 'develop' into feat/489
Sidsector9 Sep 9, 2025
c1e53f8
disable rewrite tone options when in progress
Sidsector9 Sep 9, 2025
9c58726
fix eslint errors
Sidsector9 Sep 9, 2025
bf7dc29
fix action priority
Sidsector9 Sep 9, 2025
9aebe18
fix spelling
Sidsector9 Sep 9, 2025
8567298
fix failing E2E test
Sidsector9 Sep 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"globals": {
"wp": "readonly",
Expand All @@ -24,7 +24,10 @@
"Block": "readonly",
"classifai_term_cleanup_params": "readonly",
"classifAISettings": "readonly",
"classifaiRecommendedContentSettings": "readonly"
"DOMParser": "readonly",
"MutationObserver": "readonly",
"classifaiRecommendedContentSettings": "readonly",
"classifaiRewriteToneTones": "readonly"
},
"rules": {
"react/jsx-no-undef": "off"
Expand Down
2 changes: 2 additions & 0 deletions includes/Classifai/Features/ContentGeneration.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ public function enqueue_editor_assets() {
get_asset_info( 'classifai-plugin-content-generation', 'version' ),
true
);

wp_enqueue_script( 'classifai-chat-ui-js' );
}

/**
Expand Down
14 changes: 14 additions & 0 deletions includes/Classifai/Features/Feature.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public function setup() {
}

add_action( 'admin_enqueue_scripts', [ $this, 'register_plugin_area_script' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'register_chat_ui_script' ], 99 );

if ( $this->is_feature_enabled() ) {
$this->feature_setup();
Expand Down Expand Up @@ -157,6 +158,19 @@ public function register_plugin_area_script() {
);
}

/**
* Enqueues the JS asset required for the ChatUI plugin.
*/
public function register_chat_ui_script() {
wp_register_script(
'classifai-chat-ui-js',
CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-chat-ui.js',
get_asset_info( 'classifai-plugin-chat-ui', 'dependencies' ),
get_asset_info( 'classifai-plugin-chat-ui', 'version' ),
true
);
}

/**
* Set up the fields for each section.
*
Expand Down
337 changes: 337 additions & 0 deletions includes/Classifai/Features/RewriteTone.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,337 @@
<?php

namespace Classifai\Features;

use Classifai\Providers\OpenAI\ChatGPT;
use Classifai\Services\LanguageProcessing;
use WP_REST_Server;
use WP_REST_Request;
use WP_Error;

use function Classifai\get_asset_info;
use function Classifai\sanitize_prompts;

/**
* Class RewriteTone
*/
class RewriteTone extends Feature {
/**
* ID of the current feature.
*
* @var string
*/
const ID = 'feature_rewrite_tone';

/**
* Constructor.
*/
public function __construct() {
$this->label = __( 'Rewrite Tone', 'classifai' );

// Contains all providers that are registered to the service.
$this->provider_instances = $this->get_provider_instances( LanguageProcessing::get_service_providers() );

// Contains just the providers this feature supports.
$this->supported_providers = [
ChatGPT::ID => __( 'OpenAI ChatGPT', 'classifai' ),
];
}

/**
* Set up necessary hooks.
*
* We utilize this so we can register the REST route.
*/
public function setup() {
parent::setup();
add_action( 'rest_api_init', [ $this, 'register_endpoints' ] );
}

/**
* Set up necessary hooks.
*/
public function feature_setup() {
add_action( 'enqueue_block_assets', [ $this, 'enqueue_editor_assets' ] );
}

/**
* Register any needed endpoints.
*/
public function register_endpoints() {
register_rest_route(
'classifai/v1',
'rewrite-tone',
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'rest_endpoint_callback' ],
'permission_callback' => '__return_true',
'args' => [
'id' => [
'required' => true,
'type' => 'integer',
'sanitize_callback' => 'absint',
'description' => esc_html__( 'Post ID to resize the content for.', 'classifai' ),
],
'content' => [
'type' => 'array',
'sanitize_callback' => function ( $content_array ) {
if ( is_array( $content_array ) ) {
return array_map(
function ( $item ) {
$item['clientId'] = sanitize_text_field( $item['clientId'] );
$item['content'] = wp_kses_post( $item['content'] );
return $item;
},
$content_array
);
}

return [];
},
'validate_callback' => function ( $content_array ) {
if ( is_array( $content_array ) ) {
foreach ( $content_array as $item ) {
if ( ! isset( $item['clientId'] ) || ! is_string( $item['clientId'] ) ) {
return new WP_Error( 'rewrite_tone_invalid_client_id', __( 'Each item must have a valid clientId string.', 'classifai' ), [ 'status' => 400 ] );
}

if ( ! isset( $item['content'] ) || ! is_string( $item['content'] ) ) {
return new WP_Error( 'rewrite_tone_invalid_content', __( 'Each item must have valid content as a string.', 'classifai' ), [ 'status' => 400 ] );
}
}
return true;
}
return new WP_Error( 'rewrite_tone_invalid_data_format', __( 'Content must be an array of objects.', 'classifai' ), [ 'status' => 400 ] );
},
'description' => esc_html__( 'The content to resize.', 'classifai' ),
],
],
]
);
}

/**
* Check if a given request has access to resize content.
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|bool
*/
public function resize_content_permissions_check( WP_REST_Request $request ) {
$post_id = $request->get_param( 'id' );

// Ensure we have a logged in user that can edit the item.
if ( empty( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) {
return false;
}

$post_type = get_post_type( $post_id );
$post_type_obj = get_post_type_object( $post_type );

// Ensure the post type is allowed in REST endpoints.
if ( ! $post_type || empty( $post_type_obj ) || empty( $post_type_obj->show_in_rest ) ) {
return false;
}

// Ensure the feature is enabled. Also runs a user check.
if ( ! $this->is_feature_enabled() ) {
return new WP_Error( 'not_enabled', esc_html__( 'Rewrite Tone is not currently enabled.', 'classifai' ) );
}

return true;
}

/**
* Generic request handler for all our custom routes.
*
* @param WP_REST_Request $request The full request object.
* @return \WP_REST_Response
*/
public function rest_endpoint_callback( WP_REST_Request $request ) {
$route = $request->get_route();

if ( strpos( $route, '/classifai/v1/rewrite-tone' ) === 0 ) {
return rest_ensure_response(
$this->run(
$request->get_param( 'id' ),
'rewrite_tone',
[
'content' => $request->get_param( 'content' ),
'tone' => $request->get_param( 'tone' ),
'tones' => $this->get_tones(),
]
)
);
}

return parent::rest_endpoint_callback( $request );
}

/**
* Enqueue the editor scripts.
*/
public function enqueue_editor_assets() {
global $post;

if ( empty( $post ) || ! is_admin() ) {
return;
}

wp_enqueue_script(
'classifai-plugin-rewrite-tone-js',
CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-rewrite-tone.js',
array_merge(
get_asset_info( 'classifai-plugin-rewrite-tone', 'dependencies' ),
array( Feature::PLUGIN_AREA_SCRIPT )
),
get_asset_info( 'classifai-plugin-rewrite-tone', 'version' ),
true
);

wp_enqueue_script( 'classifai-chat-ui-js' );

wp_add_inline_script(
'classifai-plugin-rewrite-tone-js',
sprintf(
'let classifaiRewriteToneTones = %s',
wp_json_encode(
array_map(
fn( $x ) => array(
'label' => $x['label'],
'value' => $x['value'],
'info' => $x['info'],
),
$this->get_tones()
)
)
)
);
}

/**
* Returns an array of tones available.
*
* @since x.x.x
*
* @return array
*/
private function get_tones() {
/**
* Filter the tones available for the Rewrite Tone feature.
*
* @since x.x.x
* @hook classifai_feature_rewrite_tone_get_tones
*
* @param {array} $tones Array of tones.
*
* @return {array} Filtered array of tones.
*/
return apply_filters(
'classifai_' . self::ID . 'get_tones',
array(
array(
'label' => __( '🙂 Friendly', 'classifai' ),
'value' => 'friendly',
'info' => __( 'Conversational and warm, like talking to a friend.', 'classifai' ),
'sys_prompt' => __(
'You are a friendly and approachable writer. Rewrite the given content using a conversational and welcoming tone. Use inclusive language and keep the reader engaged with warmth and clarity, as if you\'re talking to a friend.',
'classifai'
),
),
array(
'label' => __( '💼 Professional', 'classifai' ),
'value' => 'professional',
'info' => __( 'Formal and polished, suitable for business settings.', 'classifai' ),
'sys_prompt' => __(
'You are a professional copywriter. Rewrite the given content with a formal and polished tone suitable for a business or corporate audience. Maintain clarity and authority, avoid contractions, and use industry-appropriate language.',
'classifai'
),
),
array(
'label' => __( '🗣️ Persuasive', 'classifai' ),
'value' => 'persuasive',
'info' => __( 'Compelling and action-driven to influence readers.', 'classifai' ),
'sys_prompt' => __(
'You are a persuasive marketing expert. Rewrite the content to convince the reader to take action. Use compelling language, clear value propositions, and emotional appeal. Emphasize benefits over features, and guide the reader confidently toward the intended goal.',
'classifai'
),
),
array(
'label' => __( '📘 Educational', 'classifai' ),
'value' => 'educational',
'info' => __( 'Clear and instructive, ideal for teaching.', 'classifai' ),
'sys_prompt' => __(
'You are an educational content specialist. Rewrite the content in a tone that is instructive, clear, and easy to follow. Use simple, precise language to teach or explain, ensuring the reader understands the topic step by step.',
'classifai'
),
),
array(
'label' => __( '📖 Storytelling', 'classifai' ),
'value' => 'storytelling',
'info' => __( 'Narrative and engaging, turning facts into stories.', 'classifai' ),
'sys_prompt' => __(
'You are a skilled storyteller. Rewrite the content as a narrative that draws the reader in. Use vivid descriptions, emotional hooks, and a clear arc to transform information into an engaging story that resonates with the audience.',
'classifai'
),
),
)
);
}

/**
* Get the description for the enable field.
*
* @return string
*/
public function get_enable_description(): string {
return esc_html__( '"Condense this text" and "Expand this text" menu items will be added to the paragraph block\'s toolbar menu.', 'classifai' );
}

/**
* Add any needed custom fields.
*/
public function add_custom_settings_fields() {
$settings = $this->get_settings();

add_settings_field(
'rewrite_tone_prompt',
esc_html__( 'Prompt', 'classifai' ),
[ $this, 'render_prompt_repeater_field' ],
$this->get_option_name(),
$this->get_option_name() . '_section',
[
'label_for' => 'rewrite_tone_prompt',
'default_value' => $settings['rewrite_tone_prompt'],
'description' => esc_html__( 'Add a custom prompt, if desired.', 'classifai' ),
]
);
}

/**
* Returns the default settings for the feature.
*
* @return array
*/
public function get_feature_default_settings(): array {
return [
'rewrite_tone_prompt' => [
[
'title' => esc_html__( 'ClassifAI default', 'classifai' ),
'original' => 1,
],
],
'provider' => ChatGPT::ID,
];
}

/**
* Sanitizes the default feature settings.
*
* @param array $new_settings Settings being saved.
* @return array
*/
public function sanitize_default_feature_settings( array $new_settings ): array {
$new_settings['rewrite_tone_prompt'] = sanitize_prompts( 'rewrite_tone_prompt', $new_settings );

return $new_settings;
}
}
Loading