From 186bec0194810d52f0129fc10d47e654271d20b7 Mon Sep 17 00:00:00 2001 From: Todd Wright Date: Thu, 28 May 2026 15:40:13 +1000 Subject: [PATCH 1/5] AI Sidebar: Hide legacy toolbar when replacement is active Mark the legacy AI Assistant toolbar extensions unavailable when Jetpack AI Sidebar block transformations are active. Prevents duplicate AI toolbar buttons while preserving the legacy controls whenever the sidebar entrypoint is gated off. --- .../hide-legacy-ai-toolbar-with-sidebar | 4 + .../image/with-ai-image-extension.tsx | 27 ++-- .../lib/can-ai-assistant-be-enabled.ts | 4 + .../text-blocks/with-ai-text-extension.tsx | 19 +-- .../plugins/jetpack/extensions/index.json | 1 + .../ai-sidebar/class-jetpack-ai-sidebar.php | 45 ++++++- .../ai-sidebar/Jetpack_AI_Sidebar_Test.php | 124 ++++++++++++++++++ 7 files changed, 198 insertions(+), 26 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/hide-legacy-ai-toolbar-with-sidebar diff --git a/projects/plugins/jetpack/changelog/hide-legacy-ai-toolbar-with-sidebar b/projects/plugins/jetpack/changelog/hide-legacy-ai-toolbar-with-sidebar new file mode 100644 index 000000000000..01074d49ea25 --- /dev/null +++ b/projects/plugins/jetpack/changelog/hide-legacy-ai-toolbar-with-sidebar @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +AI Assistant: Hide legacy block toolbar controls when Jetpack AI Sidebar content editing is enabled. diff --git a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx index 06605b84aa7c..e26df2c698bb 100644 --- a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx +++ b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx @@ -21,7 +21,10 @@ import debugFactory from 'debug'; import { store as seoStore } from '../../../../plugins/ai-assistant-plugin/components/seo-enhancer/store'; import useBlockModuleStatus from '../../hooks/use-block-module-status'; import { getFeatureAvailability } from '../../lib/utils/get-feature-availability'; -import { canAIAssistantBeEnabled } from '../lib/can-ai-assistant-be-enabled'; +import { + canAIAssistantBeEnabled, + isJetpackAiSidebarBlockToolbarEnabled, +} from '../lib/can-ai-assistant-be-enabled'; import { preprocessImageContent } from '../lib/preprocess-image-content'; import { TYPE_ALT_TEXT, TYPE_CAPTION } from '../types'; import AiAssistantImageExtensionToolbarDropdown from './components/image-toolbar-dropdown'; @@ -231,16 +234,18 @@ const blockEditWithAiComponents = createHigherOrderComponent( BlockEdit => { return ( <> - - request( TYPE_ALT_TEXT ) } - onRequestCaption={ () => request( TYPE_CAPTION ) } - loadingAltText={ loadingAltText } - loadingCaption={ loadingCaption } - disabled={ ! hasImage } - wrapperRef={ wrapperRef } - /> - + { ! isJetpackAiSidebarBlockToolbarEnabled && ( + + request( TYPE_ALT_TEXT ) } + onRequestCaption={ () => request( TYPE_CAPTION ) } + loadingAltText={ loadingAltText } + loadingCaption={ loadingCaption } + disabled={ ! hasImage } + wrapperRef={ wrapperRef } + /> + + ) } ); } diff --git a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts index 2c2d2ec72628..7050c4fb468d 100644 --- a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts +++ b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts @@ -10,9 +10,13 @@ import { select } from '@wordpress/data'; import { getFeatureAvailability } from '../../lib/utils/get-feature-availability'; export const AI_ASSISTANT_SUPPORT_NAME = 'ai-assistant-support'; +export const JETPACK_AI_SIDEBAR_BLOCK_TOOLBAR = 'ai-sidebar-block-toolbar'; // Check if the AI Assistant support is enabled. export const isAiAssistantSupportEnabled = getFeatureAvailability( AI_ASSISTANT_SUPPORT_NAME ); +export const isJetpackAiSidebarBlockToolbarEnabled = getFeatureAvailability( + JETPACK_AI_SIDEBAR_BLOCK_TOOLBAR +); /** * Check if it is possible to enable the AI Assistant block and its features. diff --git a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx index dec87d5785c9..d32900ec9a1e 100644 --- a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx +++ b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx @@ -22,6 +22,7 @@ import debugFactory from 'debug'; import useAutoScroll from '../../hooks/use-auto-scroll'; import useBlockModuleStatus from '../../hooks/use-block-module-status'; import { mapInternalPromptTypeToBackendPromptType } from '../../lib/prompt/backend-prompt'; +import { isJetpackAiSidebarBlockToolbarEnabled } from '../lib/can-ai-assistant-be-enabled'; import AiAssistantInput from './components/ai-assistant-input'; import AiAssistantExtensionToolbarDropdown from './components/ai-assistant-toolbar-dropdown'; import { getBlockHandler, InlineExtensionsContext } from './get-block-handler'; @@ -561,14 +562,16 @@ const blockEditWithAiComponents = createHigherOrderComponent( BlockEdit => { /> ) } - - - + { ! isJetpackAiSidebarBlockToolbarEnabled && ( + + + + ) } ); diff --git a/projects/plugins/jetpack/extensions/index.json b/projects/plugins/jetpack/extensions/index.json index 4d8417cd26c2..5a62f880de09 100644 --- a/projects/plugins/jetpack/extensions/index.json +++ b/projects/plugins/jetpack/extensions/index.json @@ -74,6 +74,7 @@ "ai-title-optimization-keywords-support", "ai-response-feedback", "ai-assistant-image-extension", + "ai-sidebar-block-toolbar", "ai-seo-enhancer", "ai-correct-spelling", "paypal-payment-buttons", diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php index 019c4bacb747..26fa884152ba 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php @@ -17,13 +17,14 @@ use Automattic\Jetpack\Status; use Automattic\Jetpack\Status\Host; -const AM_ASSET_BASE_PATH = 'widgets.wp.com/agents-manager/'; -const AI_SIDEBAR_ASSET_TRANSIENT = 'jetpack_ai_sidebar_asset'; -const AI_SIDEBAR_JS_URL = 'https://' . AM_ASSET_BASE_PATH . 'jetpack-ai-sidebar.min.js'; -const AI_SIDEBAR_CSS_URL = 'https://' . AM_ASSET_BASE_PATH . 'jetpack-ai-sidebar.css'; -const AI_SIDEBAR_RTL_CSS_URL = 'https://' . AM_ASSET_BASE_PATH . 'jetpack-ai-sidebar.rtl.css'; -const AI_SIDEBAR_PROVIDER_URL = 'https://' . AM_ASSET_BASE_PATH . 'jetpack-ai-sidebar.provider.mjs'; -const AI_SIDEBAR_AGENT_ID = 'wp-orchestrator'; +const AM_ASSET_BASE_PATH = 'widgets.wp.com/agents-manager/'; +const AI_SIDEBAR_ASSET_TRANSIENT = 'jetpack_ai_sidebar_asset'; +const AI_SIDEBAR_JS_URL = 'https://' . AM_ASSET_BASE_PATH . 'jetpack-ai-sidebar.min.js'; +const AI_SIDEBAR_CSS_URL = 'https://' . AM_ASSET_BASE_PATH . 'jetpack-ai-sidebar.css'; +const AI_SIDEBAR_RTL_CSS_URL = 'https://' . AM_ASSET_BASE_PATH . 'jetpack-ai-sidebar.rtl.css'; +const AI_SIDEBAR_PROVIDER_URL = 'https://' . AM_ASSET_BASE_PATH . 'jetpack-ai-sidebar.provider.mjs'; +const AI_SIDEBAR_AGENT_ID = 'wp-orchestrator'; +const AI_SIDEBAR_BLOCK_TOOLBAR_EXTENSION = 'ai-sidebar-block-toolbar'; /** * Initializes the Agents Manager package and registers the Jetpack AI @@ -71,6 +72,9 @@ public static function init(): void { // never fired. Priority 250 runs after both jetpack-mu-wpcom and the // Agents Manager package enqueue (priority 101). add_action( 'admin_enqueue_scripts', array( __CLASS__, 'maybe_patch_jetpack_ai_sidebar_preview_data' ), 250 ); + + // Let editor JS know when the Jetpack AI Sidebar toolbar replacement is active. + add_action( 'jetpack_register_gutenberg_extensions', array( __CLASS__, 'register_block_toolbar_extension' ), 99 ); } // ────────────────────────────────────────────────── @@ -385,6 +389,33 @@ private static function get_jetpack_ai_sidebar_preview_config(): array { ); } + /** + * Whether Jetpack AI Sidebar content-editing block toolbar controls are active. + * + * @return bool + */ + public static function has_block_transformations_enabled(): bool { + $preview_config = self::get_jetpack_ai_sidebar_preview_config(); + + return self::is_post_editor() + && self::has_ai_features() + && true === $preview_config['enabled'] + && true === ( $preview_config['features']['blockTransformations'] ?? false ); + } + + /** + * Register the Jetpack AI Sidebar block toolbar replacement feature. + * + * @return void + */ + public static function register_block_toolbar_extension(): void { + if ( ! self::has_block_transformations_enabled() ) { + return; + } + + \Jetpack_Gutenberg::set_extension_available( AI_SIDEBAR_BLOCK_TOOLBAR_EXTENSION ); + } + /** * Add Jetpack AI Sidebar-specific data to externally emitted Agents Manager payloads. * diff --git a/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php b/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php index 73454c6c5539..c79202dd42f8 100644 --- a/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php +++ b/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php @@ -52,6 +52,7 @@ class Jetpack_AI_Sidebar_Test extends WP_UnitTestCase { public function set_up() { parent::set_up(); $this->reset_sidebar_hooks(); + \Jetpack_Gutenberg::reset(); add_filter( 'jetpack_offline_mode', '__return_false' ); update_option( 'jetpack_offline_mode', '0' ); Status_Cache::clear(); @@ -93,6 +94,7 @@ public function tear_down() { $GLOBALS['current_screen'] = $this->saved_screen; $GLOBALS['wp_scripts'] = $this->saved_wp_scripts; $GLOBALS['wp_styles'] = $this->saved_wp_styles; + \Jetpack_Gutenberg::reset(); parent::tear_down(); } @@ -109,6 +111,7 @@ private function reset_sidebar_hooks() { remove_all_filters( 'jetpack_ai_sidebar_agents_manager_data' ); remove_action( 'admin_enqueue_scripts', array( Jetpack_AI_Sidebar::class, 'maybe_enqueue_abilities_script' ), 201 ); remove_action( 'admin_enqueue_scripts', array( Jetpack_AI_Sidebar::class, 'maybe_patch_jetpack_ai_sidebar_preview_data' ), 250 ); + remove_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_toolbar_extension' ), 99 ); } /** @@ -192,6 +195,14 @@ private function simulate_self_hosted() { Status_Cache::clear(); } + /** + * Mark legacy Jetpack AI block toolbar extensions as available. + */ + private function make_legacy_block_toolbar_extensions_available() { + \Jetpack_Gutenberg::set_extension_available( 'ai-assistant-support' ); + \Jetpack_Gutenberg::set_extension_available( 'ai-assistant-image-extension' ); + } + /** * Simulate the Big_Sky class existing, as it does when the Big Sky plugin is present. */ @@ -285,6 +296,10 @@ public function test_init_registers_hooks_by_default() { has_action( 'admin_enqueue_scripts', array( Jetpack_AI_Sidebar::class, 'maybe_patch_jetpack_ai_sidebar_preview_data' ) ), 'maybe_patch_jetpack_ai_sidebar_preview_data should be hooked by default.' ); + $this->assertNotFalse( + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_toolbar_extension' ) ), + 'register_block_toolbar_extension should be hooked by default.' + ); } /** @@ -302,6 +317,10 @@ public function test_init_does_nothing_when_filter_is_false() { has_filter( 'agents_manager_enabled_in_block_editor', array( Jetpack_AI_Sidebar::class, 'enable_agents_manager_in_post_editor' ) ), 'enable_agents_manager_in_post_editor should not be hooked when filter is false.' ); + $this->assertFalse( + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_toolbar_extension' ) ), + 'register_block_toolbar_extension should not be hooked when filter is false.' + ); } /** @@ -320,6 +339,10 @@ public function test_init_does_nothing_on_self_hosted() { has_filter( 'agents_manager_enabled_in_block_editor', array( Jetpack_AI_Sidebar::class, 'enable_agents_manager_in_post_editor' ) ), 'enable_agents_manager_in_post_editor should not be hooked on a self-hosted site.' ); + $this->assertFalse( + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_toolbar_extension' ) ), + 'register_block_toolbar_extension should not be hooked when the preview gate is false.' + ); } /** @@ -366,6 +389,10 @@ public function test_init_registers_hooks_when_enabled() { has_action( 'admin_enqueue_scripts', array( Jetpack_AI_Sidebar::class, 'maybe_patch_jetpack_ai_sidebar_preview_data' ) ), 'maybe_patch_jetpack_ai_sidebar_preview_data should be hooked when filter is true.' ); + $this->assertNotFalse( + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_toolbar_extension' ) ), + 'register_block_toolbar_extension should be hooked when filter is true.' + ); } // ────────────────────────────────────────────────── @@ -456,6 +483,103 @@ public function test_preview_filter_overrides_gate() { $this->assertFalse( $this->gate_open() ); } + // ────────────────────────────────────────────────── + // Legacy toolbar replacement tests + // ────────────────────────────────────────────────── + + /** + * Test that block transformations are active in the post editor by default. + */ + public function test_block_transformations_enabled_in_post_editor() { + $this->set_block_editor_screen(); + + $this->assertTrue( Jetpack_AI_Sidebar::has_block_transformations_enabled() ); + } + + /** + * Test that the sidebar kill switch disables block transformations. + */ + public function test_block_transformations_respect_sidebar_kill_switch() { + $this->set_block_editor_screen(); + add_filter( 'jetpack_ai_sidebar_enabled', '__return_false' ); + + $this->assertFalse( Jetpack_AI_Sidebar::has_block_transformations_enabled() ); + } + + /** + * Test that the preview feature flag disables block transformations. + */ + public function test_block_transformations_respect_preview_feature_flag() { + $this->set_block_editor_screen(); + add_filter( + 'jetpack_ai_sidebar_preview_features', + static function ( $features ) { + $features['blockTransformations'] = false; + return $features; + } + ); + + $this->assertFalse( Jetpack_AI_Sidebar::has_block_transformations_enabled() ); + } + + /** + * Test that the active sidebar registers the replacement toolbar extension. + */ + public function test_register_block_toolbar_extension_marks_replacement_available() { + $this->set_block_editor_screen(); + $this->make_legacy_block_toolbar_extensions_available(); + add_filter( 'jetpack_ai_sidebar_enabled', '__return_true' ); + add_filter( 'jetpack_ai_sidebar_preview_enabled', '__return_true' ); + add_filter( + 'jetpack_ai_sidebar_preview_features', + static function ( $features ) { + $features['blockTransformations'] = true; + return $features; + } + ); + + Jetpack_AI_Sidebar::register_block_toolbar_extension(); + + $this->assertTrue( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TOOLBAR_EXTENSION ) ); + $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) ); + $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) ); + } + + /** + * Test that the replacement toolbar extension stays unavailable when block transformations are off. + */ + public function test_register_block_toolbar_extension_skips_when_transformations_disabled() { + $this->set_block_editor_screen(); + $this->make_legacy_block_toolbar_extensions_available(); + add_filter( + 'jetpack_ai_sidebar_preview_features', + static function ( $features ) { + $features['blockTransformations'] = false; + return $features; + } + ); + + Jetpack_AI_Sidebar::register_block_toolbar_extension(); + + $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TOOLBAR_EXTENSION ) ); + $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) ); + $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) ); + } + + /** + * Test that the replacement toolbar extension stays unavailable outside the post editor. + */ + public function test_register_block_toolbar_extension_skips_page_editor() { + $this->set_page_block_editor_screen(); + $this->make_legacy_block_toolbar_extensions_available(); + + Jetpack_AI_Sidebar::register_block_toolbar_extension(); + + $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TOOLBAR_EXTENSION ) ); + $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) ); + $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) ); + } + // ────────────────────────────────────────────────── // agents_manager_enabled_in_block_editor() tests // ────────────────────────────────────────────────── From 9adb07f3c352bb26d93bf4fd807a73f188d26ae8 Mon Sep 17 00:00:00 2001 From: Todd Wright Date: Fri, 29 May 2026 10:31:12 +1000 Subject: [PATCH 2/5] AI Sidebar: Rename block transformations flag Use the block transformations feature name for the sidebar availability flag so Jetpack and Calypso refer to the same capability consistently. --- .../image/with-ai-image-extension.tsx | 4 +- .../lib/can-ai-assistant-be-enabled.ts | 6 +-- .../text-blocks/with-ai-text-extension.tsx | 4 +- .../plugins/jetpack/extensions/index.json | 2 +- .../ai-sidebar/class-jetpack-ai-sidebar.php | 28 ++++++------ .../ai-sidebar/Jetpack_AI_Sidebar_Test.php | 44 +++++++++---------- 6 files changed, 44 insertions(+), 44 deletions(-) diff --git a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx index e26df2c698bb..51c52e57efa7 100644 --- a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx +++ b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx @@ -23,7 +23,7 @@ import useBlockModuleStatus from '../../hooks/use-block-module-status'; import { getFeatureAvailability } from '../../lib/utils/get-feature-availability'; import { canAIAssistantBeEnabled, - isJetpackAiSidebarBlockToolbarEnabled, + isJetpackAiSidebarBlockTransformationsEnabled, } from '../lib/can-ai-assistant-be-enabled'; import { preprocessImageContent } from '../lib/preprocess-image-content'; import { TYPE_ALT_TEXT, TYPE_CAPTION } from '../types'; @@ -234,7 +234,7 @@ const blockEditWithAiComponents = createHigherOrderComponent( BlockEdit => { return ( <> - { ! isJetpackAiSidebarBlockToolbarEnabled && ( + { ! isJetpackAiSidebarBlockTransformationsEnabled && ( request( TYPE_ALT_TEXT ) } diff --git a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts index 7050c4fb468d..cd06f729c891 100644 --- a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts +++ b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts @@ -10,12 +10,12 @@ import { select } from '@wordpress/data'; import { getFeatureAvailability } from '../../lib/utils/get-feature-availability'; export const AI_ASSISTANT_SUPPORT_NAME = 'ai-assistant-support'; -export const JETPACK_AI_SIDEBAR_BLOCK_TOOLBAR = 'ai-sidebar-block-toolbar'; +export const JETPACK_AI_SIDEBAR_BLOCK_TRANSFORMATIONS = 'ai-sidebar-block-transformations'; // Check if the AI Assistant support is enabled. export const isAiAssistantSupportEnabled = getFeatureAvailability( AI_ASSISTANT_SUPPORT_NAME ); -export const isJetpackAiSidebarBlockToolbarEnabled = getFeatureAvailability( - JETPACK_AI_SIDEBAR_BLOCK_TOOLBAR +export const isJetpackAiSidebarBlockTransformationsEnabled = getFeatureAvailability( + JETPACK_AI_SIDEBAR_BLOCK_TRANSFORMATIONS ); /** diff --git a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx index d32900ec9a1e..4ac3d21740db 100644 --- a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx +++ b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx @@ -22,7 +22,7 @@ import debugFactory from 'debug'; import useAutoScroll from '../../hooks/use-auto-scroll'; import useBlockModuleStatus from '../../hooks/use-block-module-status'; import { mapInternalPromptTypeToBackendPromptType } from '../../lib/prompt/backend-prompt'; -import { isJetpackAiSidebarBlockToolbarEnabled } from '../lib/can-ai-assistant-be-enabled'; +import { isJetpackAiSidebarBlockTransformationsEnabled } from '../lib/can-ai-assistant-be-enabled'; import AiAssistantInput from './components/ai-assistant-input'; import AiAssistantExtensionToolbarDropdown from './components/ai-assistant-toolbar-dropdown'; import { getBlockHandler, InlineExtensionsContext } from './get-block-handler'; @@ -562,7 +562,7 @@ const blockEditWithAiComponents = createHigherOrderComponent( BlockEdit => { /> ) } - { ! isJetpackAiSidebarBlockToolbarEnabled && ( + { ! isJetpackAiSidebarBlockTransformationsEnabled && ( assertNotFalse( - has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_toolbar_extension' ) ), - 'register_block_toolbar_extension should be hooked by default.' + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_transformations_extension' ) ), + 'register_block_transformations_extension should be hooked by default.' ); } @@ -318,8 +318,8 @@ public function test_init_does_nothing_when_filter_is_false() { 'enable_agents_manager_in_post_editor should not be hooked when filter is false.' ); $this->assertFalse( - has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_toolbar_extension' ) ), - 'register_block_toolbar_extension should not be hooked when filter is false.' + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_transformations_extension' ) ), + 'register_block_transformations_extension should not be hooked when filter is false.' ); } @@ -340,8 +340,8 @@ public function test_init_does_nothing_on_self_hosted() { 'enable_agents_manager_in_post_editor should not be hooked on a self-hosted site.' ); $this->assertFalse( - has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_toolbar_extension' ) ), - 'register_block_toolbar_extension should not be hooked when the preview gate is false.' + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_transformations_extension' ) ), + 'register_block_transformations_extension should not be hooked when the preview gate is false.' ); } @@ -390,8 +390,8 @@ public function test_init_registers_hooks_when_enabled() { 'maybe_patch_jetpack_ai_sidebar_preview_data should be hooked when filter is true.' ); $this->assertNotFalse( - has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_toolbar_extension' ) ), - 'register_block_toolbar_extension should be hooked when filter is true.' + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_transformations_extension' ) ), + 'register_block_transformations_extension should be hooked when filter is true.' ); } @@ -484,7 +484,7 @@ public function test_preview_filter_overrides_gate() { } // ────────────────────────────────────────────────── - // Legacy toolbar replacement tests + // Sidebar block transformations tests // ────────────────────────────────────────────────── /** @@ -523,9 +523,9 @@ static function ( $features ) { } /** - * Test that the active sidebar registers the replacement toolbar extension. + * Test that the active sidebar registers the block transformations feature. */ - public function test_register_block_toolbar_extension_marks_replacement_available() { + public function test_register_block_transformations_extension_marks_feature_available() { $this->set_block_editor_screen(); $this->make_legacy_block_toolbar_extensions_available(); add_filter( 'jetpack_ai_sidebar_enabled', '__return_true' ); @@ -538,17 +538,17 @@ static function ( $features ) { } ); - Jetpack_AI_Sidebar::register_block_toolbar_extension(); + Jetpack_AI_Sidebar::register_block_transformations_extension(); - $this->assertTrue( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TOOLBAR_EXTENSION ) ); + $this->assertTrue( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) ); } /** - * Test that the replacement toolbar extension stays unavailable when block transformations are off. + * Test that the block transformations feature stays unavailable when transformations are off. */ - public function test_register_block_toolbar_extension_skips_when_transformations_disabled() { + public function test_register_block_transformations_extension_skips_when_transformations_disabled() { $this->set_block_editor_screen(); $this->make_legacy_block_toolbar_extensions_available(); add_filter( @@ -559,23 +559,23 @@ static function ( $features ) { } ); - Jetpack_AI_Sidebar::register_block_toolbar_extension(); + Jetpack_AI_Sidebar::register_block_transformations_extension(); - $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TOOLBAR_EXTENSION ) ); + $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) ); } /** - * Test that the replacement toolbar extension stays unavailable outside the post editor. + * Test that the block transformations feature stays unavailable outside the post editor. */ - public function test_register_block_toolbar_extension_skips_page_editor() { + public function test_register_block_transformations_extension_skips_page_editor() { $this->set_page_block_editor_screen(); $this->make_legacy_block_toolbar_extensions_available(); - Jetpack_AI_Sidebar::register_block_toolbar_extension(); + Jetpack_AI_Sidebar::register_block_transformations_extension(); - $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TOOLBAR_EXTENSION ) ); + $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) ); } From 685eaf46e9feea615f99f6e48f8de3b009d359e3 Mon Sep 17 00:00:00 2001 From: Todd Wright Date: Fri, 29 May 2026 15:40:42 +1000 Subject: [PATCH 3/5] AI Sidebar: Gate legacy toolbar replacement Require the blockToolbarButton preview feature before exposing the sidebar replacement flag so preview/editorial review can run without hiding the legacy toolbar controls. --- .../ai-sidebar/class-jetpack-ai-sidebar.php | 8 ++++++- .../ai-sidebar/Jetpack_AI_Sidebar_Test.php | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php index de2a1b7d9f34..ffb7bc37bbfd 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php @@ -365,6 +365,7 @@ private static function get_jetpack_ai_sidebar_preview_config(): array { 'aiEditorialReview' => self::is_ai_editorial_review_enabled(), 'generateFeedback' => self::is_generate_feedback_enabled(), 'blockTransformations' => true, + 'blockToolbarButton' => false, 'optimizeTitleSuggestion' => self::is_optimize_title_suggestion_enabled(), 'chatHistory' => false, 'supportGuides' => false, @@ -409,7 +410,12 @@ public static function has_block_transformations_enabled(): bool { * @return void */ public static function register_block_transformations_extension(): void { - if ( ! self::has_block_transformations_enabled() ) { + $preview_config = self::get_jetpack_ai_sidebar_preview_config(); + + if ( + ! self::has_block_transformations_enabled() + || true !== ( $preview_config['features']['blockToolbarButton'] ?? false ) + ) { return; } diff --git a/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php b/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php index 516493e9a290..f54309630a21 100644 --- a/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php +++ b/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php @@ -534,6 +534,7 @@ public function test_register_block_transformations_extension_marks_feature_avai 'jetpack_ai_sidebar_preview_features', static function ( $features ) { $features['blockTransformations'] = true; + $features['blockToolbarButton'] = true; return $features; } ); @@ -545,6 +546,26 @@ static function ( $features ) { $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) ); } + /** + * Test that the block transformations feature stays unavailable until the toolbar button is released. + */ + public function test_register_block_transformations_extension_skips_when_toolbar_button_disabled() { + $this->set_block_editor_screen(); + $this->make_legacy_block_toolbar_extensions_available(); + add_filter( + 'jetpack_ai_sidebar_preview_features', + static function ( $features ) { + $features['blockTransformations'] = true; + $features['blockToolbarButton'] = false; + return $features; + } + ); + + Jetpack_AI_Sidebar::register_block_transformations_extension(); + + $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ) ); + } + /** * Test that the block transformations feature stays unavailable when transformations are off. */ @@ -555,6 +576,7 @@ public function test_register_block_transformations_extension_skips_when_transfo 'jetpack_ai_sidebar_preview_features', static function ( $features ) { $features['blockTransformations'] = false; + $features['blockToolbarButton'] = true; return $features; } ); @@ -653,6 +675,7 @@ public function test_add_agents_manager_data_exposes_ai_editorial_review_enabled $this->assertSame( true, $data['jetpackAiSidebar']['enabled'] ); $this->assertSame( true, $data['jetpackAiSidebar']['features']['aiEditorialReview'] ); $this->assertSame( true, $data['jetpackAiSidebar']['features']['blockTransformations'] ); + $this->assertSame( false, $data['jetpackAiSidebar']['features']['blockToolbarButton'] ); // generateFeedback and optimizeTitleSuggestion are in development: off outside testing environments. $this->assertSame( false, $data['jetpackAiSidebar']['features']['generateFeedback'] ); $this->assertSame( false, $data['jetpackAiSidebar']['features']['optimizeTitleSuggestion'] ); @@ -752,6 +775,7 @@ public function test_add_agents_manager_data_allows_preview_without_ai_editorial $this->assertSame( true, $data['jetpackAiSidebar']['enabled'] ); $this->assertSame( false, $data['jetpackAiSidebar']['features']['aiEditorialReview'] ); $this->assertSame( true, $data['jetpackAiSidebar']['features']['blockTransformations'] ); + $this->assertSame( false, $data['jetpackAiSidebar']['features']['blockToolbarButton'] ); } /** From 89eb28a3b4d3659aacbc86ffc7748bdc4ef1500e Mon Sep 17 00:00:00 2001 From: Todd Wright Date: Fri, 29 May 2026 16:50:29 +1000 Subject: [PATCH 4/5] AI Sidebar: Gate toolbar replacement availability Explicitly marks the sidebar block transformations extension unavailable until its release flags are active. Prevents Jetpack from hiding the legacy toolbar before the replacement button is available. --- .../ai-sidebar/class-jetpack-ai-sidebar.php | 4 +++ .../ai-sidebar/Jetpack_AI_Sidebar_Test.php | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php index ffb7bc37bbfd..07659e73ce14 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php @@ -416,6 +416,10 @@ public static function register_block_transformations_extension(): void { ! self::has_block_transformations_enabled() || true !== ( $preview_config['features']['blockToolbarButton'] ?? false ) ) { + \Jetpack_Gutenberg::set_extension_unavailable( + AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION, + 'jetpack_ai_sidebar_feature_disabled' + ); return; } diff --git a/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php b/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php index f54309630a21..5d81009308ee 100644 --- a/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php +++ b/projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php @@ -109,11 +109,33 @@ private function reset_sidebar_hooks() { remove_all_filters( 'jetpack_ai_sidebar_preview_enabled' ); remove_all_filters( 'jetpack_ai_sidebar_preview_features' ); remove_all_filters( 'jetpack_ai_sidebar_agents_manager_data' ); + remove_filter( 'jetpack_is_connection_ready', '__return_true', 1000 ); + remove_filter( 'jetpack_gutenberg', '__return_true' ); + remove_filter( 'jetpack_set_available_extensions', array( __CLASS__, 'get_sidebar_extension_allowlist' ) ); remove_action( 'admin_enqueue_scripts', array( Jetpack_AI_Sidebar::class, 'maybe_enqueue_abilities_script' ), 201 ); remove_action( 'admin_enqueue_scripts', array( Jetpack_AI_Sidebar::class, 'maybe_patch_jetpack_ai_sidebar_preview_data' ), 250 ); remove_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_transformations_extension' ), 99 ); } + /** + * Limit Jetpack Gutenberg availability checks to the sidebar extension under test. + * + * @return array + */ + public static function get_sidebar_extension_allowlist() { + return array( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ); + } + + /** + * Enable Jetpack Gutenberg availability checks for the sidebar extension under test. + */ + private function enable_sidebar_extension_availability_checks() { + add_filter( 'jetpack_is_connection_ready', '__return_true', 1000 ); + add_filter( 'jetpack_gutenberg', '__return_true' ); + add_filter( 'jetpack_set_available_extensions', array( __CLASS__, 'get_sidebar_extension_allowlist' ) ); + Status_Cache::clear(); + } + /** * Simulate a connected Jetpack owner. */ @@ -528,6 +550,7 @@ static function ( $features ) { public function test_register_block_transformations_extension_marks_feature_available() { $this->set_block_editor_screen(); $this->make_legacy_block_toolbar_extensions_available(); + $this->enable_sidebar_extension_availability_checks(); add_filter( 'jetpack_ai_sidebar_enabled', '__return_true' ); add_filter( 'jetpack_ai_sidebar_preview_enabled', '__return_true' ); add_filter( @@ -544,6 +567,7 @@ static function ( $features ) { $this->assertTrue( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) ); + $this->assertTrue( \Jetpack_Gutenberg::get_availability()[ AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ]['available'] ); } /** @@ -552,6 +576,7 @@ static function ( $features ) { public function test_register_block_transformations_extension_skips_when_toolbar_button_disabled() { $this->set_block_editor_screen(); $this->make_legacy_block_toolbar_extensions_available(); + $this->enable_sidebar_extension_availability_checks(); add_filter( 'jetpack_ai_sidebar_preview_features', static function ( $features ) { @@ -564,6 +589,9 @@ static function ( $features ) { Jetpack_AI_Sidebar::register_block_transformations_extension(); $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ) ); + $availability = \Jetpack_Gutenberg::get_availability()[ AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ]; + $this->assertFalse( $availability['available'] ); + $this->assertSame( 'jetpack_ai_sidebar_feature_disabled', $availability['unavailable_reason'] ); } /** From ab2c455b3ad3f0a954d215ee0e692385cd5748de Mon Sep 17 00:00:00 2001 From: Todd Wright Date: Wed, 24 Jun 2026 13:27:34 +1000 Subject: [PATCH 5/5] AI Sidebar: Gate legacy toolbar on the toolbar button alone Replaced the ai-sidebar-block-transformations extension with ai-sidebar-toolbar-button, so hiding the legacy AI toolbar is driven solely by whether its replacement button is live. Block transformations is becoming inherent agent behaviour with no real off state, so coupling the toolbar's visibility to it was a fiction that locked the two features in lockstep. Also deduplicated the post-editor/AI-features/preview checks through should_expose_sidebar(). --- .../image/with-ai-image-extension.tsx | 4 +- .../lib/can-ai-assistant-be-enabled.ts | 6 +- .../text-blocks/with-ai-text-extension.tsx | 4 +- .../plugins/jetpack/extensions/index.json | 2 +- .../ai-sidebar/class-jetpack-ai-sidebar.php | 45 ++++---- .../ai-sidebar/Jetpack_AI_Sidebar_Test.php | 104 ++++++++---------- 6 files changed, 73 insertions(+), 92 deletions(-) diff --git a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx index 51c52e57efa7..53b8aa3ef10f 100644 --- a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx +++ b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/image/with-ai-image-extension.tsx @@ -23,7 +23,7 @@ import useBlockModuleStatus from '../../hooks/use-block-module-status'; import { getFeatureAvailability } from '../../lib/utils/get-feature-availability'; import { canAIAssistantBeEnabled, - isJetpackAiSidebarBlockTransformationsEnabled, + isAiSidebarToolbarButtonEnabled, } from '../lib/can-ai-assistant-be-enabled'; import { preprocessImageContent } from '../lib/preprocess-image-content'; import { TYPE_ALT_TEXT, TYPE_CAPTION } from '../types'; @@ -234,7 +234,7 @@ const blockEditWithAiComponents = createHigherOrderComponent( BlockEdit => { return ( <> - { ! isJetpackAiSidebarBlockTransformationsEnabled && ( + { ! isAiSidebarToolbarButtonEnabled && ( request( TYPE_ALT_TEXT ) } diff --git a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts index cd06f729c891..effd03a11041 100644 --- a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts +++ b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/lib/can-ai-assistant-be-enabled.ts @@ -10,13 +10,11 @@ import { select } from '@wordpress/data'; import { getFeatureAvailability } from '../../lib/utils/get-feature-availability'; export const AI_ASSISTANT_SUPPORT_NAME = 'ai-assistant-support'; -export const JETPACK_AI_SIDEBAR_BLOCK_TRANSFORMATIONS = 'ai-sidebar-block-transformations'; +export const AI_SIDEBAR_TOOLBAR_BUTTON = 'ai-sidebar-toolbar-button'; // Check if the AI Assistant support is enabled. export const isAiAssistantSupportEnabled = getFeatureAvailability( AI_ASSISTANT_SUPPORT_NAME ); -export const isJetpackAiSidebarBlockTransformationsEnabled = getFeatureAvailability( - JETPACK_AI_SIDEBAR_BLOCK_TRANSFORMATIONS -); +export const isAiSidebarToolbarButtonEnabled = getFeatureAvailability( AI_SIDEBAR_TOOLBAR_BUTTON ); /** * Check if it is possible to enable the AI Assistant block and its features. diff --git a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx index 4ac3d21740db..d30d9455d21a 100644 --- a/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx +++ b/projects/plugins/jetpack/extensions/blocks/ai-assistant/extensions/text-blocks/with-ai-text-extension.tsx @@ -22,7 +22,7 @@ import debugFactory from 'debug'; import useAutoScroll from '../../hooks/use-auto-scroll'; import useBlockModuleStatus from '../../hooks/use-block-module-status'; import { mapInternalPromptTypeToBackendPromptType } from '../../lib/prompt/backend-prompt'; -import { isJetpackAiSidebarBlockTransformationsEnabled } from '../lib/can-ai-assistant-be-enabled'; +import { isAiSidebarToolbarButtonEnabled } from '../lib/can-ai-assistant-be-enabled'; import AiAssistantInput from './components/ai-assistant-input'; import AiAssistantExtensionToolbarDropdown from './components/ai-assistant-toolbar-dropdown'; import { getBlockHandler, InlineExtensionsContext } from './get-block-handler'; @@ -562,7 +562,7 @@ const blockEditWithAiComponents = createHigherOrderComponent( BlockEdit => { /> ) } - { ! isJetpackAiSidebarBlockTransformationsEnabled && ( + { ! isAiSidebarToolbarButtonEnabled && ( assertNotFalse( - has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_transformations_extension' ) ), - 'register_block_transformations_extension should be hooked by default.' + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_toolbar_button_extension' ) ), + 'register_toolbar_button_extension should be hooked by default.' ); } @@ -340,8 +340,8 @@ public function test_init_does_nothing_when_filter_is_false() { 'enable_agents_manager_in_post_editor should not be hooked when filter is false.' ); $this->assertFalse( - has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_transformations_extension' ) ), - 'register_block_transformations_extension should not be hooked when filter is false.' + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_toolbar_button_extension' ) ), + 'register_toolbar_button_extension should not be hooked when filter is false.' ); } @@ -362,8 +362,8 @@ public function test_init_does_nothing_on_self_hosted() { 'enable_agents_manager_in_post_editor should not be hooked on a self-hosted site.' ); $this->assertFalse( - has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_transformations_extension' ) ), - 'register_block_transformations_extension should not be hooked when the preview gate is false.' + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_toolbar_button_extension' ) ), + 'register_toolbar_button_extension should not be hooked when the preview gate is false.' ); } @@ -412,8 +412,8 @@ public function test_init_registers_hooks_when_enabled() { 'maybe_patch_jetpack_ai_sidebar_preview_data should be hooked when filter is true.' ); $this->assertNotFalse( - has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_block_transformations_extension' ) ), - 'register_block_transformations_extension should be hooked when filter is true.' + has_action( 'jetpack_register_gutenberg_extensions', array( Jetpack_AI_Sidebar::class, 'register_toolbar_button_extension' ) ), + 'register_toolbar_button_extension should be hooked when filter is true.' ); } @@ -506,48 +506,55 @@ public function test_preview_filter_overrides_gate() { } // ────────────────────────────────────────────────── - // Sidebar block transformations tests + // Sidebar toolbar button tests // ────────────────────────────────────────────────── /** - * Test that block transformations are active in the post editor by default. + * Test that the toolbar button stays disabled until its preview feature is released. */ - public function test_block_transformations_enabled_in_post_editor() { + public function test_toolbar_button_disabled_by_default() { $this->set_block_editor_screen(); - $this->assertTrue( Jetpack_AI_Sidebar::has_block_transformations_enabled() ); + $this->assertFalse( Jetpack_AI_Sidebar::is_toolbar_button_enabled() ); } /** - * Test that the sidebar kill switch disables block transformations. + * Test that the preview feature flag activates the toolbar button. */ - public function test_block_transformations_respect_sidebar_kill_switch() { + public function test_toolbar_button_enabled_by_preview_feature_flag() { $this->set_block_editor_screen(); - add_filter( 'jetpack_ai_sidebar_enabled', '__return_false' ); + add_filter( + 'jetpack_ai_sidebar_preview_features', + static function ( $features ) { + $features['blockToolbarButton'] = true; + return $features; + } + ); - $this->assertFalse( Jetpack_AI_Sidebar::has_block_transformations_enabled() ); + $this->assertTrue( Jetpack_AI_Sidebar::is_toolbar_button_enabled() ); } /** - * Test that the preview feature flag disables block transformations. + * Test that the sidebar kill switch disables the toolbar button. */ - public function test_block_transformations_respect_preview_feature_flag() { + public function test_toolbar_button_respects_sidebar_kill_switch() { $this->set_block_editor_screen(); add_filter( 'jetpack_ai_sidebar_preview_features', static function ( $features ) { - $features['blockTransformations'] = false; + $features['blockToolbarButton'] = true; return $features; } ); + add_filter( 'jetpack_ai_sidebar_enabled', '__return_false' ); - $this->assertFalse( Jetpack_AI_Sidebar::has_block_transformations_enabled() ); + $this->assertFalse( Jetpack_AI_Sidebar::is_toolbar_button_enabled() ); } /** - * Test that the active sidebar registers the block transformations feature. + * Test that the active sidebar registers the toolbar button feature. */ - public function test_register_block_transformations_extension_marks_feature_available() { + public function test_register_toolbar_button_extension_marks_feature_available() { $this->set_block_editor_screen(); $this->make_legacy_block_toolbar_extensions_available(); $this->enable_sidebar_extension_availability_checks(); @@ -556,76 +563,59 @@ public function test_register_block_transformations_extension_marks_feature_avai add_filter( 'jetpack_ai_sidebar_preview_features', static function ( $features ) { - $features['blockTransformations'] = true; - $features['blockToolbarButton'] = true; + $features['blockToolbarButton'] = true; return $features; } ); - Jetpack_AI_Sidebar::register_block_transformations_extension(); + Jetpack_AI_Sidebar::register_toolbar_button_extension(); - $this->assertTrue( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ) ); + $this->assertTrue( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_TOOLBAR_BUTTON_EXTENSION ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) ); - $this->assertTrue( \Jetpack_Gutenberg::get_availability()[ AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ]['available'] ); + $this->assertTrue( \Jetpack_Gutenberg::get_availability()[ AiAssistantPlugin\AI_SIDEBAR_TOOLBAR_BUTTON_EXTENSION ]['available'] ); } /** - * Test that the block transformations feature stays unavailable until the toolbar button is released. + * Test that the toolbar button feature stays unavailable until the preview feature is released. */ - public function test_register_block_transformations_extension_skips_when_toolbar_button_disabled() { + public function test_register_toolbar_button_extension_skips_when_toolbar_button_disabled() { $this->set_block_editor_screen(); $this->make_legacy_block_toolbar_extensions_available(); $this->enable_sidebar_extension_availability_checks(); add_filter( 'jetpack_ai_sidebar_preview_features', static function ( $features ) { - $features['blockTransformations'] = true; - $features['blockToolbarButton'] = false; + $features['blockToolbarButton'] = false; return $features; } ); - Jetpack_AI_Sidebar::register_block_transformations_extension(); + Jetpack_AI_Sidebar::register_toolbar_button_extension(); - $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ) ); - $availability = \Jetpack_Gutenberg::get_availability()[ AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ]; + $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_TOOLBAR_BUTTON_EXTENSION ) ); + $availability = \Jetpack_Gutenberg::get_availability()[ AiAssistantPlugin\AI_SIDEBAR_TOOLBAR_BUTTON_EXTENSION ]; $this->assertFalse( $availability['available'] ); $this->assertSame( 'jetpack_ai_sidebar_feature_disabled', $availability['unavailable_reason'] ); } /** - * Test that the block transformations feature stays unavailable when transformations are off. + * Test that the toolbar button feature stays unavailable outside the post editor. */ - public function test_register_block_transformations_extension_skips_when_transformations_disabled() { - $this->set_block_editor_screen(); + public function test_register_toolbar_button_extension_skips_page_editor() { + $this->set_page_block_editor_screen(); $this->make_legacy_block_toolbar_extensions_available(); add_filter( 'jetpack_ai_sidebar_preview_features', static function ( $features ) { - $features['blockTransformations'] = false; - $features['blockToolbarButton'] = true; + $features['blockToolbarButton'] = true; return $features; } ); - Jetpack_AI_Sidebar::register_block_transformations_extension(); - - $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ) ); - $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) ); - $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) ); - } - - /** - * Test that the block transformations feature stays unavailable outside the post editor. - */ - public function test_register_block_transformations_extension_skips_page_editor() { - $this->set_page_block_editor_screen(); - $this->make_legacy_block_toolbar_extensions_available(); - - Jetpack_AI_Sidebar::register_block_transformations_extension(); + Jetpack_AI_Sidebar::register_toolbar_button_extension(); - $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_BLOCK_TRANSFORMATIONS_EXTENSION ) ); + $this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_TOOLBAR_BUTTON_EXTENSION ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) ); $this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) ); }