Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use WordPress\AI\Experiments\Experiment_Category;
use WordPress\AI\Settings\Settings_Registration;

use function WordPress\AI\get_min_content_length;

if ( ! defined( 'ABSPATH' ) ) {
exit;
}
Expand Down Expand Up @@ -151,9 +153,10 @@ public function enqueue_assets( string $hook_suffix ): void {
'content_classification',
'ContentClassificationData',
array(
'enabled' => $this->is_enabled(),
'strategy' => $this->get_strategy(),
'maxSuggestions' => $this->get_max_suggestions(),
'enabled' => $this->is_enabled(),
'strategy' => $this->get_strategy(),
'maxSuggestions' => $this->get_max_suggestions(),
'minContentLength' => get_min_content_length( 'content-classification', 150 ),
)
);
}
Expand Down
5 changes: 4 additions & 1 deletion includes/Experiments/Content_Resizing/Content_Resizing.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use WordPress\AI\Asset_Loader;
use WordPress\AI\Experiments\Experiment_Category;

use function WordPress\AI\get_min_content_length;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
Expand Down Expand Up @@ -87,7 +89,8 @@ public function enqueue_assets( string $hook_suffix ): void {
'content_resizing',
'ContentResizingData',
array(
'enabled' => $this->is_enabled(),
'enabled' => $this->is_enabled(),
'minContentLength' => get_min_content_length( 'content-resizing', 5 ),
)
);
}
Expand Down
5 changes: 4 additions & 1 deletion includes/Experiments/Summarization/Summarization.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use WordPress\AI\Asset_Loader;
use WordPress\AI\Experiments\Experiment_Category;

use function WordPress\AI\get_min_content_length;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
Expand Down Expand Up @@ -106,10 +108,11 @@ public function enqueue_assets( string $hook_suffix ): void {
* Filters the minimum content length required to enable summarization.
*
* @since 1.0.0
* @deprecated x.x.x Use {@see 'wpai_min_content_length'} instead.
*
* @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 );
$min_content_length = (int) apply_filters( 'wpai_summarization_min_content_length', get_min_content_length( 'summarization', 100 ) );

Asset_Loader::localize_script(
'summarization',
Expand Down
21 changes: 21 additions & 0 deletions includes/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -540,3 +540,24 @@ function is_connector_plugin_active( array $connector_data ): bool {

return is_multisite() && function_exists( 'is_plugin_active_for_network' ) && is_plugin_active_for_network( $plugin_file );
}

/**
* Returns the minimum content length required for a given feature.
*
* @since x.x.x
*
* @param string $feature_id The feature identifier (e.g. 'content-resizing', 'content-classification', 'summarization').
* @param int $content_length The default minimum content length.
* @return int The minimum content length.
*/
function get_min_content_length( string $feature_id, int $content_length = 100 ): int {
/**
* Filters the minimum content length required for a feature.
*
* @since x.x.x
*
* @param int $content_length The minimum content length. Default 100.
* @param string $feature_id The feature identifier.
*/
return (int) apply_filters( 'wpai_min_content_length', $content_length, $feature_id );
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { close as closeIcon, update } from '@wordpress/icons';
* Internal dependencies
*/
import { useContentClassification } from './useContentClassification';
import { getWordCountType } from '../../../utils/word-count';
import type { TagSuggestion } from '../types';

interface SuggestionPanelProps {
Expand Down Expand Up @@ -42,6 +43,7 @@ export default function SuggestionPanel( {
handleAccept,
handleDismiss,
handleDismissAll,
minContentLength,
} = useContentClassification( taxonomy );

const taxonomyObject: any = useSelect(
Expand Down Expand Up @@ -76,10 +78,23 @@ export default function SuggestionPanel( {

{ ! hasEnoughContent && ! hasSuggestions && (
<p className="ai-content-classification__hint components-base-control__help">
{ __(
'Add more content to enable AI suggestions (approximately 150 words).',
'ai'
) }
{ getWordCountType() !== 'words'
? sprintf(
/* translators: %d: Minimum content length. */
__(
'Add more content to enable AI suggestions (approximately %d characters).',
'ai'
),
minContentLength
)
: sprintf(
/* translators: %d: Minimum content length. */
__(
'Add more content to enable AI suggestions (approximately %d words).',
'ai'
),
minContentLength
) }
</p>
) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { store as coreStore } from '@wordpress/core-data';
import { store as editorStore } from '@wordpress/editor';
import { useState, useCallback } from '@wordpress/element';
import { store as noticesStore } from '@wordpress/notices';
import { count as wordCount } from '@wordpress/wordcount';
import { addQueryArgs } from '@wordpress/url';
import apiFetch from '@wordpress/api-fetch';

Expand All @@ -19,14 +18,15 @@ import apiFetch from '@wordpress/api-fetch';
*/
import { runAbility } from '../../../utils/run-ability';
import { ensureProvider } from '../../../utils/provider-status';
import { hasMinimumContent } from '../../../utils/word-count';
import type {
ContentClassificationAbilityInput,
ContentClassificationResponse,
TagSuggestion,
ContentClassificationData,
} from '../types';

const MINIMUM_WORD_COUNT = 150;
const MINIMUM_CONTENT_COUNT_DEFAULT = 150;
const NOTICE_ID = 'ai_content_classification_error';
const DEFAULT_MAX_SUGGESTIONS = 5;
const MIN_SUGGESTIONS = 1;
Expand Down Expand Up @@ -55,6 +55,8 @@ const getSettings = (): ContentClassificationData => {
enabled: settings.enabled ?? false,
strategy: settings.strategy ?? 'existing_only',
maxSuggestions: normalizeMaxSuggestions( settings.maxSuggestions ),
minContentLength:
settings.minContentLength ?? MINIMUM_CONTENT_COUNT_DEFAULT,
};
};

Expand Down Expand Up @@ -139,6 +141,7 @@ export function useContentClassification( taxonomy: string ): {
handleAccept: ( suggestion: TagSuggestion ) => void;
handleDismiss: ( suggestion: TagSuggestion ) => void;
handleDismissAll: () => void;
minContentLength: number;
} {
const { postId, content } = useSelect( ( selectFn ) => {
const editor = selectFn( editorStore );
Expand All @@ -153,8 +156,10 @@ export function useContentClassification( taxonomy: string ): {
const { removeNotice, createErrorNotice } = dispatch( noticesStore ) as any;

// Check if content has enough words.
const hasEnoughContent =
wordCount( content || '', 'words' ) >= MINIMUM_WORD_COUNT;
const hasEnoughContent = hasMinimumContent(
content || '',
getSettings().minContentLength
);

const handleGenerate = useCallback( async () => {
if ( ! ensureProvider( NOTICE_ID ) ) {
Expand Down Expand Up @@ -233,6 +238,7 @@ export function useContentClassification( taxonomy: string ): {
handleAccept,
handleDismiss,
handleDismissAll,
minContentLength: getSettings().minContentLength,
};
}

Expand Down
1 change: 1 addition & 0 deletions src/experiments/content-classification/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ export interface ContentClassificationData {
enabled: boolean;
strategy: string;
maxSuggestions: number;
minContentLength: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,31 @@ import { useState, useCallback, useMemo } from '@wordpress/element';
import { __, _n, sprintf } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
import { store as editorStore } from '@wordpress/editor';
import { count } from '@wordpress/wordcount';

/**
* Internal dependencies
*/
import { runAbility } from '../../../utils/run-ability';
import { getBlockText } from '../../../utils/blocks';
import type { ContentResizingAction } from '../types';
import type { ContentResizingAction, ContentResizingData } from '../types';
import { ICON_SHORTEN, ICON_EXPAND, ICON_REPHRASE } from '../icons';
import { ensureProvider } from '../../../utils/provider-status';
import { getContentCount, getWordCountType } from '../../../utils/word-count';
import AIIcon from '../../../../routes/ai-home/ai-icon';

const SHORTEN_MIN_WORDS = 5;
const SHORTEN_MIN_CONTENT_LENGTH = 5;
const NOTICE_ID = 'ai_content_resizing_error';

const getSettings = (): ContentResizingData => {
const settings = ( window as any ).aiContentResizingData ?? {};

return {
enabled: settings.enabled ?? false,
minContentLength:
settings.minContentLength ?? SHORTEN_MIN_CONTENT_LENGTH,
};
};

/**
* Content resizing toolbar component.
*
Expand Down Expand Up @@ -80,9 +90,9 @@ export default function ContentResizingToolbar( {
}

if ( action === 'shorten' ) {
const wordCount = count( blockContent, 'words', {} );
// We need at least 5 words to shorten the content.
if ( wordCount < SHORTEN_MIN_WORDS ) {
const contentCount = getContentCount( blockContent );
// We need at least the minimum content length to shorten.
if ( contentCount < getSettings().minContentLength ) {
noticesDispatch.createErrorNotice(
__( 'Text is too short to shorten further.', 'ai' ),
{
Expand Down Expand Up @@ -158,9 +168,10 @@ export default function ContentResizingToolbar( {
return null;
}

const isCharacterType = getWordCountType() !== 'words';
const delta =
count( suggestedContent, 'words', {} ) -
count( blockContent, 'words', {} );
getContentCount( suggestedContent ) -
getContentCount( blockContent );

if ( delta === 0 ) {
return {
Expand All @@ -173,33 +184,49 @@ export default function ContentResizingToolbar( {
const magnitude = Math.abs( delta );

if ( delta > 0 ) {
const label = isCharacterType
? /* translators: %d: Number of characters added. */
_n( '+%d character', '+%d characters', magnitude, 'ai' )
: /* translators: %d: Number of words added. */
_n( '+%d word', '+%d words', magnitude, 'ai' );
const ariaLabel = isCharacterType
? /* translators: %d: Number of characters added. */
_n(
'%d character added',
'%d characters added',
magnitude,
'ai'
)
: /* translators: %d: Number of words added. */
_n( '%d word added', '%d words added', magnitude, 'ai' );

return {
modifier: 'positive' as const,
label: sprintf(
/* translators: %d: Number of words added. */
_n( '+%d word', '+%d words', magnitude, 'ai' ),
magnitude
),
ariaLabel: sprintf(
/* translators: %d: Number of words added. */
_n( '%d word added', '%d words added', magnitude, 'ai' ),
magnitude
),
label: sprintf( label, magnitude ),
ariaLabel: sprintf( ariaLabel, magnitude ),
};
}

const label = isCharacterType
? /* translators: %d: Number of characters removed. */
_n( '-%d character', '-%d characters', magnitude, 'ai' )
: /* translators: %d: Number of words removed. */
_n( '-%d word', '-%d words', magnitude, 'ai' );
const ariaLabel = isCharacterType
? /* translators: %d: Number of characters removed. */
_n(
'%d character removed',
'%d characters removed',
magnitude,
'ai'
)
: /* translators: %d: Number of words removed. */
_n( '%d word removed', '%d words removed', magnitude, 'ai' );

return {
modifier: 'negative' as const,
label: sprintf(
/* translators: %d: Number of words removed. */
_n( '−%d word', '−%d words', magnitude, 'ai' ),
magnitude
),
ariaLabel: sprintf(
/* translators: %d: Number of words removed. */
_n( '%d word removed', '%d words removed', magnitude, 'ai' ),
magnitude
),
label: sprintf( label, magnitude ),
ariaLabel: sprintf( ariaLabel, magnitude ),
};
}, [ blockContent, suggestedContent ] );

Expand Down
8 changes: 8 additions & 0 deletions src/experiments/content-resizing/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@ export interface ContentResizingAbilityInput {
content: string;
action: ContentResizingAction;
}

/**
* Localized data from the PHP side.
*/
export interface ContentResizingData {
enabled: boolean;
minContentLength: number;
}
27 changes: 19 additions & 8 deletions src/experiments/summarization/components/SummarizationPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { update } from '@wordpress/icons';
/**
* Internal dependencies
*/
import { getWordCountType } from '../../../utils/word-count';
import { useSummaryGeneration } from '../functions/useSummaryGeneration';

const { aiSummarizationData } = window as any;
Expand Down Expand Up @@ -42,14 +43,24 @@ export default function SummarizationPlugin() {
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
);
const isCharacterType = getWordCountType() !== 'words';
buttonDescription = isCharacterType
? sprintf(
/* translators: %d: minimum number of characters required */
__(
'Summarization will be available when the post content has at least %d characters.',
'ai'
),
minContentLength
)
: sprintf(
/* translators: %d: minimum number of words required */
__(
'Summarization will be available when the post content has at least %d words.',
'ai'
),
minContentLength
);
} else if ( hasSummary ) {
buttonDescription = __(
'This will update the generated summary block with a new summary of the content of this post.',
Expand Down
Loading
Loading