# extendify/3.2.1/src/Agent/lib/insertable-blocks.js

Extendify, version 3.2.1. 106 lines.

- Page: https://pluginprobe.com/plugins/extendify/3.2.1/code/src/Agent/lib/insertable-blocks.js
- Raw: https://pluginprobe.com/plugins/extendify/3.2.1/raw/src/Agent/lib/insertable-blocks.js
- Modified: 2026-08-12T16:23:58+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/extendify/3.2.1/code/src/Agent/lib/insertable-blocks.js#L10-L20`.

```javascript
import { applyBlockPatch } from '@agent/lib/block-patch';
import { createBlock, getBlockType, parse, serialize } from '@wordpress/blocks';

// The add catalog the backend prompt offers — the plugin constrains it, not
// the backend, so old fielded builds never see types they can't build.
export const INSERTABLE_BLOCK_TYPES = [
	'core/button',
	'core/heading',
	'core/paragraph',
	'core/list',
	'core/quote',
	'core/pullquote',
	'core/code',
	'core/preformatted',
	'core/verse',
	'core/group',
	'core/column',
	'core/spacer',
	'core/separator',
];

// One level of explicit children, on containers only.
const CHILD_BEARING_TYPES = ['core/group', 'core/column'];

// The model authors `text`; each type stores it under its own attribute.
const RICH_TEXT_ATTR = {
	'core/button': 'text',
	'core/heading': 'content',
	'core/paragraph': 'content',
	'core/pullquote': 'value',
	'core/code': 'content',
	'core/preformatted': 'content',
	'core/verse': 'content',
};

const paragraphChildren = (text) =>
	text ? [createBlock('core/paragraph', { content: text })] : [];

// A bare column is only valid inside core/columns, so it can't be a child.
const childBlocks = (children) =>
	(children ?? [])
		.filter(
			({ blockType }) =>
				blockType !== 'core/column' &&
				INSERTABLE_BLOCK_TYPES.includes(blockType) &&
				getBlockType(blockType),
		)
		.map(({ blockType, text }) =>
			blockType === 'core/button'
				? createBlock('core/buttons', {}, [freshBlock('core/button', text)])
				: freshBlock(blockType, text),
		);

const freshBlock = (blockType, text, children = []) => {
	if (blockType === 'core/list') {
		// The model sometimes authors <li> markup despite the plain-text
		// instruction; splitting on it beats an empty leading item.
		const items = String(text ?? '')
			.replaceAll(/<\/?li[^>]*>/gi, '\n')
			.split('\n')
			.map((line) => line.trim())
			.filter(Boolean);
		return createBlock(
			'core/list',
			{},
			items.map((content) => createBlock('core/list-item', { content })),
		);
	}
	if (CHILD_BEARING_TYPES.includes(blockType)) {
		const composed = childBlocks(children);
		return createBlock(
			blockType,
			{},
			composed.length ? composed : paragraphChildren(text),
		);
	}
	// Quote holds its text as a paragraph child, not an attribute.
	if (blockType === 'core/quote')
		return createBlock(blockType, {}, paragraphChildren(text));
	const attribute = RICH_TEXT_ATTR[blockType];
	return createBlock(
		blockType,
		attribute && text != null ? { [attribute]: text } : {},
	);
};

export const buildNewBlock = (
	blockType,
	patch = {},
	clear = [],
	presetSlugs = {},
) => {
	if (!INSERTABLE_BLOCK_TYPES.includes(blockType)) return null;
	if (!getBlockType(blockType)) return null;
	const { text, children, ...attributePatch } = patch ?? {};
	const markup = applyBlockPatch(
		serialize([freshBlock(blockType, text, children ?? [])]),
		attributePatch,
		clear ?? [],
		presetSlugs,
	);
	if (blockType !== 'core/button') return markup;
	// A lone button never exists in the editor; ship it inside its wrapper.
	return serialize([createBlock('core/buttons', {}, parse(markup))]);
};

```
