← All changes
|
src/Agent/workflows/block-selector/tools/find-blocks.js
+33
-5
3.2.0
→
3.2.2
View file →
| @@ -74,18 +74,40 @@ | ||
| 74 | 74 | } |
| 75 | 75 | return [...found.values()]; |
| 76 | 76 | }; |
| 77 | 77 | |
| 78 | +const bareAddress = (value) => | |
| 79 | + (value ?? '') | |
| 80 | + .toLowerCase() | |
| 81 | + .replace(/^https?:\/\//, '') | |
| 82 | + .replace(/^www\./, '') | |
| 83 | + .replace(/\/$/, ''); | |
| 84 | + | |
| 85 | +// A link asked for by address carries only its label as text. | |
| 86 | +const matchesAddress = ({ blockId, type }, text) => { | |
| 87 | + if (type !== 'core/navigation-link') return false; | |
| 88 | + const wanted = bareAddress(text); | |
| 89 | + const href = resolveScopedId(blockId) | |
| 90 | + ?.querySelector('a') | |
| 91 | + ?.getAttribute('href'); | |
| 92 | + return Boolean(wanted && href) && bareAddress(href).includes(wanted); | |
| 93 | +}; | |
| 94 | + | |
| 78 | 95 | // A search for "footer" arrives as text and would otherwise match nothing. |
| 79 | -const matchesText = ({ text: blockText, part }, text) => { | |
| 96 | +const matchesText = (entry, text) => { | |
| 80 | 97 | if (!text) return true; |
| 98 | + const { text: blockText, part } = entry; | |
| 81 | 99 | const needle = text.toLowerCase(); |
| 82 | 100 | if ((blockText ?? '').toLowerCase().includes(needle)) return true; |
| 101 | + if (matchesAddress(entry, text)) return true; | |
| 83 | 102 | return Boolean(part) && needle.includes(part.toLowerCase()); |
| 84 | 103 | }; |
| 85 | 104 | |
| 105 | +// core/navigation never resolves to a type, so a menu search matches its links. | |
| 86 | 106 | const matchesType = (type, blockTypes) => |
| 87 | - !blockTypes?.length || blockTypes.includes(type); | |
| 107 | + !blockTypes?.length || | |
| 108 | + blockTypes.includes(type) || | |
| 109 | + (type === 'core/navigation-link' && blockTypes.includes('core/navigation')); | |
| 88 | 110 | |
| 89 | 111 | const matchesPart = (entry, part) => |
| 90 | 112 | !part || (entry.part ?? '').toLowerCase() === part.toLowerCase(); |
| 91 | 113 | |
| @@ -100,13 +122,19 @@ | ||
| 100 | 122 | return true; |
| 101 | 123 | }); |
| 102 | 124 | }; |
| 103 | 125 | |
| 126 | +// Page blocks carry no part, so an invented name like "post-content" empties the search. | |
| 127 | +const knownPart = (entries, part) => | |
| 128 | + entries.some((entry) => matchesPart(entry, part) && entry.part) ? part : null; | |
| 129 | + | |
| 104 | 130 | export default ({ blockTypes, text, part } = {}) => { |
| 105 | 131 | const root = document.querySelector('.wp-site-blocks') ?? document.body; |
| 106 | - const all = scopeRoots(root) | |
| 107 | - .flatMap(manifestFor) | |
| 108 | - .filter((entry) => addressable(entry.type) && matchesPart(entry, part)); | |
| 132 | + const manifest = scopeRoots(root).flatMap(manifestFor); | |
| 133 | + const scope = knownPart(manifest, part); | |
| 134 | + const all = manifest.filter( | |
| 135 | + (entry) => addressable(entry.type) && matchesPart(entry, scope), | |
| 136 | + ); | |
| 109 | 137 | const byId = new Map(all.map((entry) => [entry.blockId, entry])); |
| 110 | 138 | const byType = all.filter(({ type }) => matchesType(type, blockTypes)); |
| 111 | 139 | const matches = byType.filter((entry) => matchesText(entry, text)); |
| 112 | 140 | // "hero" is a position, not text: AND-ing it would match nothing. |