Skip to content

Commit

Permalink
AI Assistant: Add partial heading inline extension (#37087)
Browse files Browse the repository at this point in the history
* export ExtensionAIControl

* add types to dropdown content file

* remove heading transformative extension when inline extensions are enabled

* add with-ai-extension on heading

* changelog

* bump version
  • Loading branch information
dhasilva authored Apr 26, 2024
1 parent ce93734 commit 952b3b8
Show file tree
Hide file tree
Showing 16 changed files with 589 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

AI Client: Export ExtensionAIControl
2 changes: 1 addition & 1 deletion projects/js-packages/ai-client/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": false,
"name": "@automattic/jetpack-ai-client",
"version": "0.12.3",
"version": "0.12.4-alpha",
"description": "A JS client for consuming Jetpack AI services",
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/ai-client/#readme",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion projects/js-packages/ai-client/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { AIControl, BlockAIControl } from './ai-control/index.js';
export { AIControl, BlockAIControl, ExtensionAIControl } from './ai-control/index.js';
export { default as AiStatusIndicator } from './ai-status-indicator/index.js';
export { default as AudioDurationDisplay } from './audio-duration-display/index.js';
export {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: other

AI Assistant: Add partial heading inline extension
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,15 @@ export const QUICK_EDIT_KEY_MAKE_LONGER = 'make-longer' as const;
// Ask AI Assistant option
export const KEY_ASK_AI_ASSISTANT = 'ask-ai-assistant' as const;

const quickActionsList = {
const quickActionsList: {
[ key: string ]: {
name: string;
key: string;
aiSuggestion: PromptTypeProp;
icon: ReactElement;
options?: AiAssistantDropdownOnChangeOptionsArgProps;
}[];
} = {
default: [
{
name: __( 'Correct spelling and grammar', 'jetpack' ),
Expand Down Expand Up @@ -102,14 +110,16 @@ export type AiAssistantDropdownOnChangeOptionsArgProps = {
userPrompt?: string;
};

export type OnRequestSuggestion = (
promptType: PromptTypeProp,
options?: AiAssistantDropdownOnChangeOptionsArgProps
) => void;

type AiAssistantToolbarDropdownContentProps = {
blockType: ExtendedBlockProp;
disabled?: boolean;
onAskAiAssistant: () => void;
onRequestSuggestion: (
promptType: PromptTypeProp,
options?: AiAssistantDropdownOnChangeOptionsArgProps
) => void;
onRequestSuggestion: OnRequestSuggestion;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import './editor.scss';
import './supports';
import './extensions/ai-assistant';
import './extensions/jetpack-contact-form';
import './inline-extensions/with-ai-extension';

registerJetpackBlockFromMetadata( metadata, {
edit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,38 @@ import { addFilter } from '@wordpress/hooks';
*/
import metadata from '../../block.json';
import { isUserConnected } from '../../lib/connection';
import { getFeatureAvailability } from '../../lib/utils/get-feature-availability';

// We have two types of block extensions for now, transformative and inline.
// The transformative blocks are transformed into an AI Assistant block when a request is made.
// The inline blocks are updated in place.
// Once all transformative blocks are converted to inline blocks, we can remove the distinction, but for now, we need to keep it.

/*
* Types and Constants
*/
export const AI_ASSISTANT_SUPPORT_NAME = 'ai-assistant-support';
export const AI_ASSISTANT_EXTENSIONS_SUPPORT_NAME = 'ai-assistant-extensions-support';

// List of blocks that can be extended.
export const EXTENDED_BLOCKS = [ 'core/paragraph', 'core/heading', 'core/list' ] as const;
// Check if the AI Assistant support is enabled.
export const isAiAssistantSupportEnabled = getFeatureAvailability( AI_ASSISTANT_SUPPORT_NAME );
// Check if the AI Assistant inline extensions support is enabled.
export const isAiAssistantExtensionsSupportEnabled = getFeatureAvailability(
AI_ASSISTANT_EXTENSIONS_SUPPORT_NAME
);

export type ExtendedBlockProp = ( typeof EXTENDED_BLOCKS )[ number ];
// The blocks will be converted one by one to inline blocks, so we update the lists accordingly, under the feature flag.
export let EXTENDED_TRANSFORMATIVE_BLOCKS: string[];
export let EXTENDED_INLINE_BLOCKS: string[];

if ( isAiAssistantExtensionsSupportEnabled ) {
EXTENDED_TRANSFORMATIVE_BLOCKS = [ 'core/paragraph', 'core/list' ];
EXTENDED_INLINE_BLOCKS = [ 'core/heading' ];
} else {
EXTENDED_TRANSFORMATIVE_BLOCKS = [ 'core/paragraph', 'core/list', 'core/heading' ];
EXTENDED_INLINE_BLOCKS = [];
}

// Since the lists depend on the feature flag, we need to define the types manually.
export type ExtendedBlockProp = 'core/paragraph' | 'core/list' | 'core/heading';
export type ExtendedInlineBlockProp = 'core/heading';

type BlockSettingsProps = {
supports: {
Expand All @@ -28,9 +50,6 @@ type BlockSettingsProps = {
};
};

export const isAiAssistantSupportExtensionEnabled =
window?.Jetpack_Editor_Initial_State?.available_blocks?.[ AI_ASSISTANT_SUPPORT_NAME ];

/**
* Check if it is possible to extend the block.
*
Expand All @@ -43,7 +62,7 @@ export function isPossibleToExtendBlock(): boolean {
}

// Check Jetpack extension is enabled.
if ( ! isAiAssistantSupportExtensionEnabled ) {
if ( ! isAiAssistantSupportEnabled ) {
return false;
}

Expand Down Expand Up @@ -85,7 +104,7 @@ function addJetpackAISupport(
name: ExtendedBlockProp
): BlockSettingsProps {
// Only extend the blocks in the list.
if ( ! EXTENDED_BLOCKS.includes( name ) ) {
if ( ! EXTENDED_TRANSFORMATIVE_BLOCKS.includes( name ) ) {
return settings;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Internal dependencies
*/
import { HeadingHandler } from './heading';
/**
* Types
*/
import type { IBlockHandler } from './types';
import type { ExtendedInlineBlockProp } from '../extensions/ai-assistant';

const handlers = {
'core/heading': HeadingHandler,
};

/**
* Gets the block handler based on the block type.
* The block handler is used to handle the request suggestions.
* @param {ExtendedInlineBlockProp} blockType - The block type.
* @param {string} clientId - The block client ID.
* @returns {IBlockHandler} The block handler.
*/
export function blockHandler(
blockType: ExtendedInlineBlockProp,
clientId: string
): IBlockHandler {
const HandlerClass = handlers[ blockType ];

if ( ! HandlerClass ) {
throw new Error( `No handler found for block type: ${ blockType }` );
}

const handler = new HandlerClass( clientId );

return {
onSuggestion: handler.onSuggestion.bind( handler ),
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* External dependencies
*/
import { ExtensionAIControl } from '@automattic/jetpack-ai-client';
import { useState } from '@wordpress/element';
import React from 'react';
/*
* Types
*/
import type { RequestingErrorProps, RequestingStateProp } from '@automattic/jetpack-ai-client';
import type { ReactElement } from 'react';

export default function AiAssistantInput( {
requestingState,
request,
stopSuggestion,
close,
undo,
}: {
clientId?: string;
postId?: number;
requestingState: RequestingStateProp;
requestingError?: RequestingErrorProps;
suggestion?: string;
request: ( question: string ) => void;
stopSuggestion?: () => void;
close?: () => void;
undo?: () => void;
} ): ReactElement {
const [ value, setValue ] = useState( '' );
const disabled = [ 'requesting', 'suggesting' ].includes( requestingState );

function handleSend(): void {
request?.( value );
}

function handleStopSuggestion(): void {
stopSuggestion?.();
}

function handleClose(): void {
close?.();
}

function handleUndo(): void {
undo?.();
}

function handleUpgrade(): void {
throw new Error( 'Function not implemented.' );
}

return (
<>
<ExtensionAIControl
disabled={ disabled }
value={ value }
state={ requestingState }
onChange={ setValue }
onSend={ handleSend }
onStop={ handleStopSuggestion }
onClose={ handleClose }
onUndo={ handleUndo }
onUpgrade={ handleUpgrade }
/>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* External dependencies
*/
import { aiAssistantIcon } from '@automattic/jetpack-ai-client';
import { useAnalytics } from '@automattic/jetpack-shared-extension-utils';
import { ToolbarButton, Dropdown } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import React from 'react';
/*
* Internal dependencies
*/
import AiAssistantToolbarDropdownContent from '../../../components/ai-assistant-toolbar-dropdown/dropdown-content';
/*
* Types
*/
import type { OnRequestSuggestion } from '../../../components/ai-assistant-toolbar-dropdown/dropdown-content';
import type { ExtendedInlineBlockProp } from '../../../extensions/ai-assistant';
import type { ReactElement } from 'react';

type AiAssistantExtensionToolbarDropdownContentProps = {
blockType: ExtendedInlineBlockProp;
onClose: () => void;
onAskAiAssistant: () => void;
onRequestSuggestion: OnRequestSuggestion;
};

/**
* The dropdown component with logic for the AI Assistant block.
* @param {AiAssistantExtensionToolbarDropdownContentProps} props - The props.
* @returns {ReactElement} The React content of the dropdown.
*/
function AiAssistantExtensionToolbarDropdownContent( {
blockType,
onClose,
onAskAiAssistant,
onRequestSuggestion,
}: AiAssistantExtensionToolbarDropdownContentProps ) {
const handleRequestSuggestion: OnRequestSuggestion = ( promptType, options ) => {
onRequestSuggestion?.( promptType, options );
onClose?.();
};

const handleAskAiAssistant = () => {
onAskAiAssistant?.();
onClose?.();
};

return (
<AiAssistantToolbarDropdownContent
blockType={ blockType }
onRequestSuggestion={ handleRequestSuggestion }
onAskAiAssistant={ handleAskAiAssistant }
disabled={ false }
/>
);
}

type AiAssistantExtensionToolbarDropdownProps = {
blockType: ExtendedInlineBlockProp;
label?: string;
onAskAiAssistant: () => void;
onRequestSuggestion: OnRequestSuggestion;
};

export default function AiAssistantExtensionToolbarDropdown( {
blockType,
label = __( 'AI Assistant', 'jetpack' ),
onAskAiAssistant,
onRequestSuggestion,
}: AiAssistantExtensionToolbarDropdownProps ): ReactElement {
const { tracks } = useAnalytics();

const toggleHandler = ( isOpen: boolean ) => {
if ( isOpen ) {
tracks.recordEvent( 'jetpack_ai_assistant_extension_toolbar_menu_show', {
block_type: blockType,
} );
}
};

return (
<Dropdown
popoverProps={ {
variant: 'toolbar',
} }
renderToggle={ ( { isOpen, onToggle } ) => {
return (
<ToolbarButton
className="jetpack-ai-assistant__button"
showTooltip
onClick={ onToggle }
aria-haspopup="true"
aria-expanded={ isOpen }
label={ label }
icon={ aiAssistantIcon }
/>
);
} }
onToggle={ toggleHandler }
renderContent={ ( { onClose: onClose } ) => (
<AiAssistantExtensionToolbarDropdownContent
onClose={ onClose }
blockType={ blockType }
onAskAiAssistant={ onAskAiAssistant }
onRequestSuggestion={ onRequestSuggestion }
/>
) }
/>
);
}
Loading

0 comments on commit 952b3b8

Please sign in to comment.