PluginProbe
Extendify / trunk
Extendify vtrunk
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
← All changes | src/Agent/lib/block-patch.js +51 -9 3.1.5 → trunk View file →
@@ -1,4 +1,5 @@
1 +import { RICH_TEXT_ATTRIBUTES } from '@agent/lib/block-schema';
1 2 import { getBlockType, parse, serialize } from '@wordpress/blocks';
2 3 import { colord } from 'colord';
3 4
4 5 const isMergeable = (value) =>
@@ -67,9 +68,21 @@
67 68 ];
68 69
69 70 // A known slug maps to a theme preset class; anything else routes to inline
70 71 // style.color.* — so "yellow" (no matching token) never lands on a wrong slug.
71 -const routeColors = (attributes, patch, colorSlugs) => {
72 +// Kept inline, a token's own hex stops the block following the palette.
73 +const slugForValue = (value, colorValues) => {
74 + const wanted = colord(value);
75 + if (!wanted.isValid()) return null;
76 + const hex = wanted.toHex();
77 + const match = Object.entries(colorValues ?? {}).find(([, preset]) => {
78 + const parsed = colord(String(preset));
79 + return parsed.isValid() && parsed.toHex() === hex;
80 + });
81 + return match?.[0] ?? null;
82 +};
83 +
84 +const routeColors = (attributes, patch, colorSlugs, colorValues) => {
72 85 const slugs = new Set(colorSlugs ?? []);
73 86 const stripped = {};
74 87 let out = attributes;
75 88 for (const [named, key] of COLOR_CHANNELS) {
@@ -74,9 +87,13 @@
74 87 let out = attributes;
75 88 for (const [named, key] of COLOR_CHANNELS) {
76 89 const value = patch?.[named];
77 90 if (value == null) continue;
78 - if (slugs.has(value)) {
91 + const named_slug = slugs.has(value)
92 + ? value
93 + : slugForValue(value, colorValues);
94 + if (named_slug) {
95 + if (named_slug !== value) out = setPath(out, named, named_slug);
79 96 out = unsetPath(out, `style.color.${key}`);
80 97 continue;
81 98 }
82 99 const parsed = colord(value);
@@ -162,19 +179,36 @@
162 179 }
163 180 return out;
164 181 };
165 182
166 -// The model always says `text`; blocks whose rich text is `content` need the remap.
183 +// The model always says `text`; blocks that keep theirs elsewhere need the remap.
167 184 const remapText = (block, patch) => {
168 185 if (patch?.text == null) return patch;
169 186 const attributes = getBlockType(block.name)?.attributes ?? {};
170 - if (attributes.text || !attributes.content) return patch;
187 + const real = RICH_TEXT_ATTRIBUTES.find((name) => attributes[name]);
188 + if (!real || real === 'text') return patch;
171 189 const { text, ...rest } = patch;
172 - return { ...rest, content: text };
190 + return { ...rest, [real]: text };
173 191 };
174 192
175 193 // Re-serializing runs the block's own save(), which core/button needs to render
176 194 // the color/border styles it marks __experimentalSkipSerialization.
195 +// Merging an empty value leaves a dead key and keeps the preset class.
196 +const emptyLeafPaths = (patch, prefix = '') =>
197 + Object.entries(patch ?? {}).flatMap(([key, value]) => {
198 + const path = prefix ? `${prefix}.${key}` : key;
199 + if (isMergeable(value)) return emptyLeafPaths(value, path);
200 + return value === '' ? [path] : [];
201 + });
202 +
203 +const withoutEmptyLeaves = (patch) =>
204 + Object.fromEntries(
205 + Object.entries(patch ?? {}).flatMap(([key, value]) => {
206 + if (isMergeable(value)) return [[key, withoutEmptyLeaves(value)]];
207 + return value === '' ? [] : [[key, value]];
208 + }),
209 + );
210 +
177 211 export const applyBlockPatch = (
178 212 serializedBlock,
179 213 patch,
180 214 clear = [],
@@ -181,14 +215,22 @@
181 215 presetSlugs = {},
182 216 ) => {
183 217 const blocks = parse(serializedBlock).map((block) => {
184 218 if (!block.name) return block;
185 - const merged = deepMerge(block.attributes, remapText(block, patch));
219 + const remapped = remapText(block, patch);
220 + const filled = withoutEmptyLeaves(remapped);
221 + const merged = deepMerge(block.attributes, filled);
186 222 const routed = routeNamedPresets(
187 - routeColors(merged, patch, presetSlugs.color),
188 - patch,
223 + routeColors(merged, filled, presetSlugs.color, presetSlugs.colorValues),
224 + filled,
189 225 presetSlugs,
190 226 );
191 - return { ...block, attributes: applyClears(routed, clear ?? []) };
227 + return {
228 + ...block,
229 + attributes: applyClears(routed, [
230 + ...(clear ?? []),
231 + ...emptyLeafPaths(remapped),
232 + ]),
233 + };
192 234 });
193 235 return serialize(blocks);
194 236 };