/** * External dependencies */ import { renderMarkdownFromHTML, renderHTMLFromMarkdown } from '@automattic/jetpack-ai-client'; import { rawHandler, getBlockContent } from '@wordpress/blocks'; import { select, dispatch } from '@wordpress/data'; /** * Types */ import type { BlockBehavior, BlockEditorDispatch, BlockEditorSelect } from './types'; import type { Block, RenderHTMLRules } from '@automattic/jetpack-ai-client'; export function getMarkdown( html: string ) { return renderMarkdownFromHTML( { content: html } ); } export function renderHTMLContent( markdown: string, rules: RenderHTMLRules = [] ) { return renderHTMLFromMarkdown( { content: markdown, rules, extension: true } ); } export class BlockHandler { public clientId: string; public renderRules: RenderHTMLRules = []; public firstUpdate: boolean = true; public behavior: BlockBehavior = 'dropdown' as const; public isChildBlock: boolean = false; constructor( clientId: string, renderRules: RenderHTMLRules = [], behavior: BlockBehavior = 'dropdown', isChildBlock: boolean = false ) { this.clientId = clientId; this.renderRules = renderRules; this.behavior = behavior; this.isChildBlock = isChildBlock; } public getBlock(): Block { const { getBlock } = select( 'core/block-editor' ) as BlockEditorSelect; return getBlock( this.clientId ); } public getContent() { const block = this.getBlock(); return getMarkdown( getBlockContent( block ) ); } public renderContent( markdown: string ) { return renderHTMLContent( markdown, this.renderRules ); } public onSuggestion( suggestion: string ): void { // Ignore an empty suggestion if ( ! suggestion ) { return; } const HTML = this.renderContent( suggestion ); this.replaceBlockContent( HTML ); } // eslint-disable-next-line @typescript-eslint/no-unused-vars public onDone( suggestion: string ): void { this.firstUpdate = true; } public replaceBlockContent( newContent: string ): void { // Create a new block with the raw HTML content. const [ newBlock ] = rawHandler( { HTML: newContent } ); if ( ! newBlock ) { return; } const { updateBlockAttributes, replaceInnerBlocks, __unstableMarkNextChangeAsNotPersistent } = dispatch( 'core/block-editor' ) as BlockEditorDispatch; // Do not mark the very first change as not persistent. if ( this.firstUpdate ) { this.firstUpdate = false; } else { // Mark all other changes as not persistent so we can undo all the changes in one step. __unstableMarkNextChangeAsNotPersistent(); } // Replace the original block attributes with the new block attributes. updateBlockAttributes( this.clientId, newBlock.attributes ); // Replace the original block inner blocks with the new block inner blocks. __unstableMarkNextChangeAsNotPersistent(); replaceInnerBlocks( this.clientId, newBlock.innerBlocks ); } }