# extendify/3.1.6/src/Agent/workflows/block-selector/tools/find-blocks.js

Extendify, version 3.1.6. 84 lines.

- Page: https://pluginprobe.com/plugins/extendify/3.1.6/code/src/Agent/workflows/block-selector/tools/find-blocks.js
- Raw: https://pluginprobe.com/plugins/extendify/3.1.6/raw/src/Agent/workflows/block-selector/tools/find-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.1.6/code/src/Agent/workflows/block-selector/tools/find-blocks.js#L10-L20`.

```javascript
import { IGNORED_BLOCKS } from '@agent/lib/classify-block-edit';
import { buildSubtreeManifest } from '@agent/lib/subtree-manifest';

// Mirrors TagBlocks::$ignored — a patch here would rewrite every loop item.
const LOOP_BLOCKS = new Set([
	'core/query',
	'core/post-template',
	'core/post-content',
	'core/comments',
	'core/comment-template',
	'woocommerce/product-collection',
	'woocommerce/product-template',
]);

const MAX_CANDIDATES = 25;

const BLOCK_ID_ATTR = 'data-extendify-agent-block-id';

// An item can't add a sibling, nor stand in for the whole set.
export const CONTAINER_TYPES = new Set([
	'core/columns',
	'core/list',
	'core/group',
]);

const addressable = (type) =>
	!LOOP_BLOCKS.has(type) && !IGNORED_BLOCKS.has(type);

const containersFor = (matches, byId) => {
	const found = new Map();
	for (const { blockId } of matches) {
		let node = document.querySelector(
			`[${BLOCK_ID_ATTR}="${CSS.escape(String(blockId))}"]`,
		)?.parentElement;
		while (node) {
			const id = node.getAttribute?.(BLOCK_ID_ATTR);
			const entry = id ? byId.get(id) : null;
			if (entry && CONTAINER_TYPES.has(entry.type)) {
				found.set(id, entry);
				break;
			}
			node = node.parentElement;
		}
	}
	return [...found.values()];
};

const matchesText = (blockText, text) =>
	!text || (blockText ?? '').toLowerCase().includes(text.toLowerCase());

const matchesType = (type, blockTypes) =>
	!blockTypes?.length || blockTypes.includes(type);

export default ({ blockTypes, text } = {}) => {
	const root = document.querySelector('.wp-site-blocks') ?? document.body;
	const all = buildSubtreeManifest(root).filter(({ type }) =>
		addressable(type),
	);
	const byId = new Map(all.map((entry) => [entry.blockId, entry]));
	const byType = all.filter(({ type }) => matchesType(type, blockTypes));
	const matches = byType.filter(({ text: blockText }) =>
		matchesText(blockText, text),
	);
	// "hero" is a position, not text: AND-ing it would match nothing.
	const textIgnored =
		Boolean(text) &&
		Boolean(blockTypes?.length) &&
		!matches.length &&
		byType.length > 0;
	const found = textIgnored ? byType : matches;
	// Cap first, or a long match list truncates the containers away.
	const shown = found.slice(0, MAX_CANDIDATES);
	const containers = containersFor(shown, byId).filter(
		(entry) => !shown.some(({ blockId }) => blockId === entry.blockId),
	);
	return {
		blockSearch: {
			total: found.length,
			...(textIgnored && { ignoredText: text }),
			candidates: [...shown, ...containers],
		},
	};
};

```
