Skip to content

Commit

Permalink
Merge pull request #843 from 10up/feature/tldr-block
Browse files Browse the repository at this point in the history
Add new Key Takeaways Feature
  • Loading branch information
dkotter authored Feb 11, 2025
2 parents 477da51 + a94dc0a commit 355582b
Show file tree
Hide file tree
Showing 18 changed files with 1,250 additions and 1 deletion.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Tap into leading cloud-based services like [OpenAI](https://openai.com/), [Micro
## Features

* Generate a summary of post content and store it as an excerpt using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
* Generate key takeaways from post content and render at the top of a post using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat) or [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
* Generate titles from post content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
* Expand or condense text content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
* Generate new images on demand to use in-content or as a featured image using [OpenAI's DALL·E 3 API](https://platform.openai.com/docs/guides/images)
Expand All @@ -41,6 +42,10 @@ Tap into leading cloud-based services like [OpenAI](https://openai.com/), [Micro
| :-: | :-: | :-: | :-: |
| ![Screenshot of ClassifAI audio transcript generation](assets/img/screenshot-9.png "Example of automatic audio transcript generation with OpenAI.") | ![Screenshot of ClassifAI title generation](assets/img/screenshot-10.png "Example of automatic title generation with OpenAI.") | ![Screenshot of ClassifAI expand/condense text feature](assets/img/screenshot-12.png "Example of expanding or condensing text with OpenAI.") | ![Screenshot of ClassifAI text to speech generation](assets/img/screenshot-11.png "Example of automatic text to speech generation with Azure.") |

| Key Takeaways | | | |
| :-: | :-: | :-: | :-: |
| ![Screenshot of the ClassifAI Key Takeaways block](assets/img/screenshot-14.png "Example of generating key takeaways using OpenAI.") | | | |

### Image Processing

| Alt Text | Smart Cropping | Tagging | Generate Images |
Expand Down
Binary file added assets/img/screenshot-14.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions includes/Classifai/Blocks/key-takeaways/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"title": "Key Takeaways",
"description": "Generate a list of key takeaways from post content",
"textdomain": "classifai",
"name": "classifai/key-takeaways",
"category": "text",
"keywords": [ "tldr", "summary", "takeaways", "abstract" ],
"attributes": {
"render": {
"type": "string",
"default": "list"
},
"title": {
"type": "string",
"default": "Key Takeaways"
},
"takeaways": {
"type": "array",
"default": []
}
},
"supports": {
"html": false,
"multiple": false
},
"editorScript": "key-takeaways-editor-script",
"style": "file:./style.css",
"render": "file:./render.php"
}
185 changes: 185 additions & 0 deletions includes/Classifai/Blocks/key-takeaways/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/**
* WordPress dependencies
*/
import {
useBlockProps,
BlockControls,
InspectorControls,
RichText,
} from '@wordpress/block-editor';
import { select } from '@wordpress/data';
import {
Placeholder,
ToolbarGroup,
Spinner,
PanelBody,
Button,
} from '@wordpress/components';
import { useEffect, useState } from '@wordpress/element';
import { postList, paragraph } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';

/**
* Internal dependencies
*/
import { ReactComponent as icon } from '../../../../assets/img/block-icon.svg';

const BlockEdit = ( props ) => {
const [ isLoading, setIsLoading ] = useState( false );
const [ run, setRun ] = useState( false );
const { attributes, setAttributes } = props;
const { render, takeaways, title } = attributes;
const blockProps = useBlockProps();

useEffect( () => {
if ( ( ! isLoading && takeaways.length === 0 ) || run ) {
const postId = select( 'core/editor' ).getCurrentPostId();
const postContent =
select( 'core/editor' ).getEditedPostAttribute( 'content' );
const postTitle =
select( 'core/editor' ).getEditedPostAttribute( 'title' );

setRun( false );
setIsLoading( true );

apiFetch( {
path: '/classifai/v1/key-takeaways/',
method: 'POST',
data: {
id: postId,
content: postContent,
title: postTitle,
render,
},
} ).then(
async ( res ) => {
// Ensure takeaways is always an array.
if ( ! Array.isArray( res ) ) {
res = [ res ];
}

setAttributes( { takeaways: res } );
setIsLoading( false );
},
( err ) => {
setAttributes( {
takeaways: [ `Error: ${ err?.message }` ],
} );
setIsLoading( false );
}
);
}
}, [ run ] );

Check warning on line 73 in includes/Classifai/Blocks/key-takeaways/edit.js

View workflow job for this annotation

GitHub Actions / eslint

React Hook useEffect has missing dependencies: 'isLoading', 'render', 'setAttributes', and 'takeaways.length'. Either include them or remove the dependency array

const renderControls = [
{
icon: postList,
title: __( 'List view', 'classifai' ),
onClick: () => setAttributes( { render: 'list' } ),
isActive: render === 'list',
},
{
icon: paragraph,
title: __( 'Paragraph view', 'classifai' ),
onClick: () => setAttributes( { render: 'paragraph' } ),
isActive: render === 'paragraph',
},
];

const editTakeaways = ( index, value ) => {
const newTakeaways = [ ...takeaways ];

if ( ! value ) {
newTakeaways.splice( index, 1 );
} else {
newTakeaways[ index ] = value;
}

setAttributes( {
takeaways: newTakeaways,
} );
};

return (
<>
<BlockControls>
<ToolbarGroup controls={ renderControls } />
</BlockControls>
<InspectorControls>
<PanelBody title={ __( 'Settings', 'classifai' ) }>
<Button
label={ __( 'Re-generate key takeaways', 'classifai' ) }
text={ __( 'Refresh results', 'classifai' ) }
variant={ 'secondary' }
onClick={ () => setRun( true ) }
isBusy={ isLoading }
/>
</PanelBody>
</InspectorControls>

{ isLoading && (
<Placeholder
icon={ icon }
label={ __( 'Generating Key Takeaways', 'classifai' ) }
>
<Spinner
style={ {
height: 'calc(4px * 10)',
width: 'calc(4px * 10)',
} }
/>
</Placeholder>
) }

{ ! isLoading && (
<div { ...blockProps }>
<RichText
tagName="h2"
className="wp-block-heading wp-block-classifai-key-takeaways__title"
value={ title }
onChange={ ( value ) =>
setAttributes( { title: value } )
}
placeholder="Key Takeaways"
/>
<div
className="wp-block-classifai-key-takeways__content"
style={ { fontStyle: 'italic' } }
>
{ render === 'list' && (
<ul>
{ takeaways.map( ( takeaway, index ) => (
<RichText
tagName="li"
value={ takeaway }
key={ index }
onChange={ ( value ) =>
editTakeaways( index, value )
}
/>
) ) }
</ul>
) }
{ render === 'paragraph' && (
<>
{ takeaways.map( ( takeaway, index ) => (
<RichText
tagName="p"
value={ takeaway }
key={ index }
onChange={ ( value ) =>
editTakeaways( index, value )
}
/>
) ) }
</>
) }
</div>
</div>
) }
</>
);
};

export default BlockEdit;
25 changes: 25 additions & 0 deletions includes/Classifai/Blocks/key-takeaways/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Key Takeaways block
*/

/**
* WordPress dependencies
*/
import { registerBlockType } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import edit from './edit';
import save from './save';
import block from './block.json';
import { ReactComponent as icon } from '../../../../assets/img/block-icon.svg';

/**
* Register block
*/
registerBlockType( block, {
edit,
save,
icon,
} );
43 changes: 43 additions & 0 deletions includes/Classifai/Blocks/key-takeaways/render.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
/**
* Render callback for the Key Takeaways block.
*
* @var array $attributes Block attributes.
* @var string $content Block content.
* @var WP_Block $block Block instance.
*/

$block_title = $attributes['title'] ?? '';
$layout = $attributes['render'] ?? 'list';
$takeaways = $attributes['takeaways'] ?? [];
?>

<div <?php echo get_block_wrapper_attributes(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
<?php if ( $block_title ) : ?>
<h2 class="wp-block-heading wp-block-classifai-key-takeaways__title">
<?php echo wp_kses_post( $block_title ); ?>
</h2>
<?php endif; ?>

<div class="wp-block-classifai-key-takeaways__content">
<?php
if ( 'list' === $layout ) {
echo '<ul>';
foreach ( (array) $takeaways as $takeaway ) {
printf(
'<li>%s</li>',
esc_html( $takeaway )
);
}
echo '</ul>';
} else {
foreach ( (array) $takeaways as $takeaway ) {
printf(
'<p>%s</p>',
esc_html( $takeaway )
);
}
}
?>
</div>
</div>
8 changes: 8 additions & 0 deletions includes/Classifai/Blocks/key-takeaways/save.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* See https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#save
*
* @return {null} Dynamic blocks do not save the HTML.
*/
const BlockSave = () => null;

export default BlockSave;
3 changes: 3 additions & 0 deletions includes/Classifai/Blocks/key-takeaways/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.wp-block-classifai-key-takeaways__content {
font-style: italic;
}
Loading

0 comments on commit 355582b

Please sign in to comment.