Creating my first Gutenberg Plugin

As part of my journey to learn more about Gutenberg, today I created my first Gutenberg Plugin, which helps us to write better text with the help of ChatGPT.

Why create a plugin?

My big milestone with these Gutenberg studies is to build knowledge enough to fix this undo/redo issue. And after I learned how we store data with @wordpress/data API and tried to understand the issue, I felt I’m missing the basic blocks of how Gutenberg works.

So now, I’m taking a moment to learn:

  • How to extend the Gutenberg code?
  • How does Gutenberg load and uses our code?
  • How can we store/retrieve data from Gutenberg APIs?
  • How data is stored inside Gutenberg?
  • How the undo/redo system will interact with my recently created plugin?

“Improve my writing” plugin with ChatGPT

I took this opportunity to also learn a basic usage of ChatGPT API, so I created the Improve my writing plugin, which is available on GitHub.

Here we can see the usage of the plugin in action:

Improve my writing Gutenberg Plugin in action

How it works?

The code is super simple as you can see in the edit.js file. We basically render some BlockControls adding a ToolbarGroup with our custom DropdownMenu. Once the user clicks on play button, we send a request to ChatGPT containing the command textTool value and the text.

This is just an initial version and super draft code. In order to use it, you will want to add your YOUR_CHAT_GPT_API_KEY which you can get here. For more instructions, follow the README How to play with it?

import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import axios from 'axios';

import { 
	useBlockProps, 
	RichText, 
	BlockControls, 
	AlignmentToolbar, 
} from '@wordpress/block-editor';

import { ToolbarGroup, ToolbarButton, DropdownMenu, Icon } from '@wordpress/components';
import './editor.scss';

const API_KEY = 'YOUR_CHAT_GPT_API_KEY';

export default function Edit({ attributes, setAttributes }) {

	const textTools = [
		{ title: 'Fix wrong words', value:'Fix wrong words: ', onClick: function() { setTextTool(this.value) }},
		{ title: 'Write it better', value:'Write it better: ', onClick: function() { setTextTool(this.value) }},
	];

	const deafultTextTool = textTools[0];
	const [blockText, setBlockText] = useState(attributes.content);
	const [textTool, setTextTool] = useState(deafultTextTool.value);

	const onChangeContent = ( newContent ) => {
		console.log('newContent: ', newContent)
		setBlockText(newContent)
		setAttributes( { content: newContent } );
		console.log('attributes.content: ', attributes.content)
	};

	const handleButtonClick = () => {
		axios.post(' https://api.openai.com/v1/chat/completions', {
			model: "gpt-3.5-turbo",
			messages: [{ role: 'user', content: textTool + blockText }],
		}, {
			headers: {
			'Authorization': `Bearer ${API_KEY}`,
			'Content-Type': 'application/json'
			}
		})
		.then(response => {
			const suggestion = response.data.choices[0].message.content;
			setBlockText(suggestion);
		})
		.catch(error => {
			console.error(error);
		});
	  };

	const onChangeAlignment = ( newAlignment ) => {
		console.log('newAlignment ', newAlignment)
		setAttributes( {
			alignment: newAlignment === undefined ? 'none' : newAlignment,
		} );
	};

	const MyDropdownMenu = () => (
		<DropdownMenu
			icon={ <Icon icon="welcome-write-blog" /> }
			label="Select a tool to improve your text"
			controls={ textTools }
		/>
	);

	return (
		<div { ...useBlockProps() }>
			{
				<BlockControls>
					<ToolbarGroup>
						<MyDropdownMenu/>
						<ToolbarButton
							icon="controls-play"
							label="Click me to improve your writing"
							onClick={handleButtonClick}
						/>
						<AlignmentToolbar
							value={ attributes.alignment }
							onChange={ onChangeAlignment }
						/>
					</ToolbarGroup>
				</BlockControls>
			}
			<RichText
				className={ attributes.className }
				style={ { textAlign: attributes.alignment } }
				tagName="p"
				onChange={ onChangeContent }
				value={ blockText }
			/>

		</div>
	);
}

Conclusion

I’m super happy to have dedicated a time to create my first Gutenberg plugin. Although it’s a pretty basic step, I already learned more about its code and now, my next steps of understanding how the save and undo/redo systems works are closer!

Leave a Reply