diff --git a/includes/Experiments/Summarization/Summarization.php b/includes/Experiments/Summarization/Summarization.php index 615f324f9..063537c3c 100644 --- a/includes/Experiments/Summarization/Summarization.php +++ b/includes/Experiments/Summarization/Summarization.php @@ -101,11 +101,22 @@ public function enqueue_assets( string $hook_suffix ): void { } Asset_Loader::enqueue_script( 'summarization', 'experiments/summarization' ); + + /** + * Filters the minimum content length required to enable summarization. + * + * @since x.x.x + * + * @param int $min_content_length The minimum number of characters required. Default 100. + */ + $min_content_length = (int) apply_filters( 'wpai_summarization_min_content_length', 100 ); + Asset_Loader::localize_script( 'summarization', 'SummarizationData', array( - 'enabled' => $this->is_enabled(), + 'enabled' => $this->is_enabled(), + 'minContentLength' => $min_content_length, ) ); } diff --git a/src/experiments/summarization/components/SummarizationBlockControls.tsx b/src/experiments/summarization/components/SummarizationBlockControls.tsx index c22cc2e6b..24d7dc3c7 100644 --- a/src/experiments/summarization/components/SummarizationBlockControls.tsx +++ b/src/experiments/summarization/components/SummarizationBlockControls.tsx @@ -23,7 +23,7 @@ const { aiSummarizationData } = window as any; * Block controls component. */ const Controls = () => { - const { isSummarizing, hasSummary, handleSummarize } = + const { isSummarizing, hasSummary, handleSummarize, isContentTooShort } = useSummaryGeneration(); let buttonLabel: string = __( 'Generate Summary', 'ai' ); @@ -46,7 +46,7 @@ const Controls = () => { icon={ update } className="ai-summarization-block-controls-button" onClick={ handleSummarize } - disabled={ isSummarizing } + disabled={ isSummarizing || isContentTooShort } isBusy={ isSummarizing } /> diff --git a/src/experiments/summarization/components/SummarizationPlugin.tsx b/src/experiments/summarization/components/SummarizationPlugin.tsx index dce4651d3..b5d8d1148 100644 --- a/src/experiments/summarization/components/SummarizationPlugin.tsx +++ b/src/experiments/summarization/components/SummarizationPlugin.tsx @@ -7,7 +7,7 @@ */ import { Button, Flex, FlexItem } from '@wordpress/components'; import { PluginPostStatusInfo } from '@wordpress/editor'; -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { update } from '@wordpress/icons'; /** @@ -21,8 +21,13 @@ const { aiSummarizationData } = window as any; * Summarization plugin component. */ export default function SummarizationPlugin() { - const { isSummarizing, hasSummary, handleSummarize } = - useSummaryGeneration(); + const { + isSummarizing, + hasSummary, + handleSummarize, + isContentTooShort, + minContentLength, + } = useSummaryGeneration(); let buttonLabel: string = __( 'Generate Summary', 'ai' ); @@ -31,15 +36,31 @@ export default function SummarizationPlugin() { } else if ( hasSummary ) { buttonLabel = __( 'Regenerate Summary', 'ai' ); } - const buttonDescription = hasSummary - ? __( - 'This will update the generated summary block with a new summary of the content of this post.', - 'ai' - ) - : __( - 'This will create a block that is a summary of the content of this post.', + + const isDisabled = isSummarizing || isContentTooShort; + + let buttonDescription: string; + + if ( isContentTooShort ) { + buttonDescription = sprintf( + /* translators: %d: minimum number of characters required */ + __( + 'Summarization will be available when the post content has at least %d characters.', 'ai' - ); + ), + minContentLength + ); + } else if ( hasSummary ) { + buttonDescription = __( + 'This will update the generated summary block with a new summary of the content of this post.', + 'ai' + ); + } else { + buttonDescription = __( + 'This will create a block that is a summary of the content of this post.', + 'ai' + ); + } // Don't render if disabled. if ( ! aiSummarizationData?.enabled ) { @@ -60,7 +81,7 @@ export default function SummarizationPlugin() { label={ buttonLabel } icon={ update } onClick={ handleSummarize } - disabled={ isSummarizing } + disabled={ isDisabled } isBusy={ isSummarizing } __next40pxDefaultSize > diff --git a/src/experiments/summarization/functions/useSummaryGeneration.ts b/src/experiments/summarization/functions/useSummaryGeneration.ts index eb18c901e..c7bd4287d 100644 --- a/src/experiments/summarization/functions/useSummaryGeneration.ts +++ b/src/experiments/summarization/functions/useSummaryGeneration.ts @@ -16,6 +16,9 @@ import { store as noticesStore } from '@wordpress/notices'; * Internal dependencies */ import { generateSummary } from './generate-summary'; +import { count } from '@wordpress/wordcount'; + +const { aiSummarizationData } = window as any; /** * Summary generation hook. @@ -117,10 +120,18 @@ export function useSummaryGeneration() { } }; + // Minimum content length required for summarization. + const minContentLength: number = + aiSummarizationData?.minContentLength ?? 100; + const isContentTooShort = + count( content, 'characters_including_spaces' ) < minContentLength; + return { isSummarizing, hasSummary: summary && summary.trim().length > 0, summary, handleSummarize, + isContentTooShort, + minContentLength, }; } diff --git a/tests/e2e/specs/experiments/content-summarization.spec.js b/tests/e2e/specs/experiments/content-summarization.spec.js index 1f8d44033..51c466055 100644 --- a/tests/e2e/specs/experiments/content-summarization.spec.js +++ b/tests/e2e/specs/experiments/content-summarization.spec.js @@ -36,12 +36,12 @@ test.describe( 'Content Summarization Experiment', () => { // Enable the Content Summarization Experiment. await enableExperiment( admin, page, 'Content Summarization' ); - // Create a new post. + // Create a new post with content that meets the minimum length requirement (>= 100 chars). await admin.createNewPost( { postType: 'post', title: 'Test Content Summarization Experiment', content: - 'This is some test content for the Content Summarization Experiment.', + 'This is some test content for the Content Summarization Experiment. It needs to be at least one hundred characters long.', } ); // Save the post. @@ -121,6 +121,83 @@ test.describe( 'Content Summarization Experiment', () => { ).not.toBeVisible(); } ); + test( 'Summarize button is disabled when content is shorter than the minimum length', async ( { + admin, + editor, + page, + } ) => { + // Globally turn on Experiments. + await enableExperiments( admin, page ); + + // Enable the Content Summarization Experiment. + await enableExperiment( admin, page, 'Content Summarization' ); + + // Create a new post with content shorter than 100 characters. + await admin.createNewPost( { + postType: 'post', + title: 'Test Short Content', + content: 'Too short.', + } ); + + // Save the post. + await editor.saveDraft(); + + // Ensure the sidebar is visible. + await editor.openDocumentSettingsSidebar(); + + const generateButton = page.locator( + '.ai-summarization-plugin-container button' + ); + + // Button should be visible but disabled. + await expect( generateButton ).toBeVisible(); + await expect( generateButton ).toBeDisabled(); + + // The descriptive text should explain when the button will be enabled. + await expect( + page.locator( '.ai-summarization-plugin-container .description' ) + ).toContainText( '100 characters' ); + } ); + + test( 'Summarize button is enabled when content meets the minimum length', async ( { + admin, + editor, + page, + } ) => { + // Globally turn on Experiments. + await enableExperiments( admin, page ); + + // Enable the Content Summarization Experiment. + await enableExperiment( admin, page, 'Content Summarization' ); + + // Create a new post with content that is at least 100 characters. + await admin.createNewPost( { + postType: 'post', + title: 'Test Sufficient Content', + content: + 'This post has enough content to meet the minimum character requirement for the summarization feature to be enabled.', + } ); + + // Save the post. + await editor.saveDraft(); + + // Ensure the sidebar is visible. + await editor.openDocumentSettingsSidebar(); + + const generateButton = page.locator( + '.ai-summarization-plugin-container button' + ); + + // Button should be visible and enabled. + await expect( generateButton ).toBeVisible(); + await expect( generateButton ).toBeEnabled(); + + // The descriptive text should NOT mention the minimum character requirement. + await expect( + page.locator( '.ai-summarization-plugin-container .description' ) + ).not.toContainText( 'characters' ); + } ); + test( 'Ensure the Content Summarization Experiment UI is not visible when the experiment is disabled', async ( { admin, editor,