Skip to content

Commit

Permalink
feat: add editable action toolbar
Browse files Browse the repository at this point in the history
  • Loading branch information
Soare-Robert-Daniel committed Jul 30, 2024
1 parent 66ecaea commit abc0d0f
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 105 deletions.
58 changes: 58 additions & 0 deletions inc/plugins/class-options-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,64 @@ function ( $item ) {
'default' => true,
)
);

// Define an entry for a prompt actions list of toolbar. It should a list with a structure of {tile, prompt}.
register_setting(
'themeisle_blocks_settings',
'themeisle_blocks_settings_prompt_actions',
array(
'type' => 'array',
'description' => __( 'The prompt actions list of toolbar.', 'otter-blocks' ),
'sanitize_callback' => function( $array ) {
return array_map(
function( $item ) {
if ( isset( $item['title'] ) ) {
$item['title'] = sanitize_text_field( $item['title'] );
}
if ( isset( $item['prompt'] ) ) {
$item['prompt'] = sanitize_text_field( $item['prompt'] );
}
return $item;
},
$array
);
},
'default' => array(
array(
'title' => 'Fix Grammar',
'prompt' => 'Fix any grammatical errors in the following: {text_input}',
),
array(
'title' => 'Rephrase',
'prompt' => 'Rephrase the following following: {text_input}',
),
array(
'title' => 'Make Shorter',
'prompt' => 'Summarize or shorten the following: {text_input}',
),
array(
'title' => 'Make Longer',
'prompt' => 'Expand or elaborate on the following: {text_input}',
),
),
'show_in_rest' => array(
'schema' => array(
'type' => 'array',
'items' => array(
'type' => 'object',
'properties' => array(
'title' => array(
'type' => 'string',
),
'prompt' => array(
'type' => 'string',
),
),
),
),
),
)
);
}

/**
Expand Down
48 changes: 48 additions & 0 deletions src/blocks/helpers/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,51 @@ export function injectConversationIntoPrompt( embeddedPrompt: PromptData, conver
]
};
}

/**
* Injects content into a template. If no match is found, adds the content at the end.
*
* @param template The template to inject into.
* @param content The content to inject.
* @returns The template with the content injected or appended.
*/
export function tryInjectIntoTemplate( template: string, content: string ): string {
if ( ! template ) {
return content;
}

const injected = template.replace( /{text_input}/gi, () => content || '{text_input}' );

if ( injected === template && content ) {
return template + ' ' + content;
}

return injected;
}

/**
* Edits the last conversation in the prompt data.
*
* @param embeddedPrompt The existing prompt data.
* @param content The content to be injected.
* @returns The updated prompt data with the last conversation edited.
*/
export function editLastConversation( embeddedPrompt: PromptData, callback: ( currentContent?: string ) => string ): PromptData {
const { messages } = embeddedPrompt;
const lastUserMessageIndex = messages.map( ( message ) => message.role ).lastIndexOf( 'user' );

if ( -1 === lastUserMessageIndex ) {
return embeddedPrompt;
}

return {
...embeddedPrompt,
messages: [
...messages.slice( 0, lastUserMessageIndex ),
{
role: 'user',
content: callback( messages?.[ lastUserMessageIndex ]?.content )
}
]
};
}
85 changes: 33 additions & 52 deletions src/blocks/plugins/ai-content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { BlockControls } from '@wordpress/block-editor';
*/
import { aiGeneration } from '../../helpers/icons';
import './editor.scss';
import { PromptsData, injectActionIntoPrompt, retrieveEmbeddedPrompt, sendPromptToOpenAI } from '../../helpers/prompt';
import { PromptsData, editLastConversation, injectActionIntoPrompt, retrieveEmbeddedPrompt, sendPromptToOpenAI, tryInjectIntoTemplate } from '../../helpers/prompt';
import useSettings from '../../helpers/use-settings';
import { openAiAPIKeyName } from '../../components/prompt';
import { insertBlockBelow } from '../../helpers/block-utility';
Expand Down Expand Up @@ -79,6 +79,7 @@ const AIToolbar = ({
const [ hasAPIKey, setHasAPIKey ] = useState<boolean>( false );
const [ isProcessing, setIsProcessing ] = useState<Record<string, boolean>>({});
const [ displayError, setDisplayError ] = useState<string|undefined>( undefined );
const [ customActions, setCustomActions ] = useState<{title: string, prompt: string}[]>([]);

// Get the create notice function from the hooks api.
const { createNotice } = useDispatch( 'core/notices' );
Expand All @@ -103,6 +104,7 @@ const AIToolbar = ({
if ( 'loaded' === status && ! hasAPIKey ) {
const key = getOption( openAiAPIKeyName ) as string;
setHasAPIKey( Boolean( key ) && 0 < key.length );
setCustomActions( getOption( 'themeisle_blocks_settings_prompt_actions' ) as {title: string, prompt: string}[]);
}
}, [ status, getOption ]);

Expand All @@ -123,7 +125,7 @@ const AIToolbar = ({
setDisplayError( undefined );
}, [ displayError ]);

const generateContent = async( content: string, actionKey: string, callback: Function = () =>{}) => {
const generateContent = async( content: string, actionKey: string, callback: Function = () =>{}, actionIndex: number = -1 ) => {

if ( ! content ) {
setDisplayError( __( 'No content detected in selected block.', 'otter-blocks' ) );
Expand All @@ -135,7 +137,7 @@ const AIToolbar = ({
embeddedPromptsCache = response?.prompts ?? [];
}

const embeddedPrompt = embeddedPromptsCache?.find( ( prompt ) => 'textTransformation' === prompt.otter_name );
let embeddedPrompt = embeddedPromptsCache?.find( ( prompt ) => 'textTransformation' === prompt.otter_name );

if ( ! embeddedPrompt ) {
setDisplayError( __( 'Something when wrong retrieving the prompts.', 'otter-blocks' ) );
Expand All @@ -158,6 +160,18 @@ const AIToolbar = ({

window.oTrk?.add({ feature: 'ai-generation', featureComponent: 'ai-toolbar', featureValue: actionKey }, { consent: true });

embeddedPrompt = injectActionIntoPrompt(
embeddedPrompt,
action
);

if ( -1 !== actionIndex && customActions?.[ actionIndex ]?.prompt ) {

// Overwrite the prompt with the custom action.
embeddedPrompt = editLastConversation( embeddedPrompt, ( _ ) => tryInjectIntoTemplate( customActions![ actionIndex ]!.prompt, content ) );
window.oTrk?.add({ feature: 'ai-generation', featureComponent: 'ai-toolbar-custom-action', featureValue: customActions?.[ actionIndex ]?.title }, { consent: true });
}

sendPromptToOpenAI(
content,
injectActionIntoPrompt(
Expand Down Expand Up @@ -213,12 +227,12 @@ const AIToolbar = ({
});
};

const ActionMenuItem = ( args: { actionKey: string, children: React.ReactNode, callback: Function }) => {
const ActionMenuItem = ( args: { actionKey: string, children: React.ReactNode, callback: Function, actionIndex?: number }) => {
return (
<Disabled isDisabled={Object.values( isProcessing ).some( x => x )}>
<MenuItem
onClick={ () => {
generateContent( extractContent( isMultipleSelection ? selectedBlocks : props ), args.actionKey, () => args.callback?.( args.actionKey ) );
generateContent( extractContent( isMultipleSelection ? selectedBlocks : props ), args.actionKey, () => args.callback?.( args.actionKey ), args?.actionIndex );
}}
>
{ args.children }
Expand All @@ -245,59 +259,26 @@ const AIToolbar = ({
)
}
<MenuGroup>
<ExternalLink className='o-menu-item-alignment' href="https://docs.themeisle.com/collection/1563-otter---page-builder-blocks-extensions" target="_blank" rel="noopener noreferrer">
{
__( 'Edit Prompts', 'otter-blocks' )
}
</ExternalLink>
</MenuGroup>
<MenuGroup>
<span className="o-menu-item-header o-menu-item-alignment">{__( 'Writing', 'otter-blocks' )}</span>
<ActionMenuItem actionKey='otter_action_generate_title' callback={onClose}>
{ __( 'Generate a heading', 'otter-blocks' ) }
</ActionMenuItem>
<ActionMenuItem actionKey='otter_action_continue_writing' callback={onClose}>
{ __( 'Continue writing', 'otter-blocks' ) }
</ActionMenuItem>
<ActionMenuItem actionKey='otter_action_summarize' callback={onClose}>
{ __( 'Summarize it', 'otter-blocks' ) }
</ActionMenuItem>
<ActionMenuItem actionKey='otter_action_make_shorter' callback={onClose}>
{ __( 'Make it shorter', 'otter-blocks' ) }
</ActionMenuItem>
<ActionMenuItem actionKey='otter_action_make_longer' callback={onClose}>
{ __( 'Make it longer', 'otter-blocks' ) }
</ActionMenuItem>
<ActionMenuItem actionKey='otter_action_make_descriptive' callback={onClose}>
{ __( 'Make it more descriptive', 'otter-blocks' ) }
</ActionMenuItem>
</MenuGroup>
<MenuGroup>
<span className="o-menu-item-header o-menu-item-alignment">{__( 'Tone', 'otter-blocks' )}</span>
<ActionMenuItem actionKey='otter_action_tone_professional' callback={onClose}>
{ __( 'Professional', 'otter-blocks' ) }
</ActionMenuItem>
<ActionMenuItem actionKey='otter_action_tone_friendly' callback={onClose}>
{ __( 'Friendly', 'otter-blocks' ) }
</ActionMenuItem>
<ActionMenuItem actionKey='otter_action_tone_humorous' callback={onClose}>
{ __( 'Humorous', 'otter-blocks' ) }
</ActionMenuItem>
<ActionMenuItem actionKey='otter_action_tone_confident' callback={onClose}>
{ __( 'Confident', 'otter-blocks' ) }
</ActionMenuItem>
<ActionMenuItem actionKey='otter_action_tone_persuasive' callback={onClose}>
{ __( 'Persuasive', 'otter-blocks' ) }
</ActionMenuItem>
<ActionMenuItem actionKey='otter_action_tone_casual' callback={onClose}>
{ __( 'Casual', 'otter-blocks' ) }
</ActionMenuItem>
{
customActions.map( ( action, index ) => (
<ActionMenuItem key={index} actionIndex={index} actionKey={'otter_action_prompt'} callback={onClose}>
{ action.title }
</ActionMenuItem>
) )
}
</MenuGroup>
<MenuGroup>
<ActionMenuItem actionKey='otter_action_prompt' callback={onClose}>
{ __( 'Use as prompt', 'otter-blocks' ) }
</ActionMenuItem>
</MenuGroup>
<MenuGroup>
<ExternalLink className='o-menu-item-alignment' href="/wp-admin/admin.php?page=otter#integrations" target="_blank" rel="noopener noreferrer">
{
__( 'Edit Custom Prompts', 'otter-blocks' )
}
</ExternalLink>
</MenuGroup>
<MenuGroup>
<ExternalLink className='o-menu-item-alignment' href="https://docs.themeisle.com/collection/1563-otter---page-builder-blocks-extensions" target="_blank" rel="noopener noreferrer">
{
Expand Down
Loading

0 comments on commit abc0d0f

Please sign in to comment.