| @@ -1,7 +1,9 @@ | ||
| 1 | 1 | // Patch schema from the block's real `supports` — the model never hand-authors HTML. |
| 2 | 2 | // Structure only: the backend overlays the model-facing field descriptions. |
| 3 | 3 | |
| 4 | +import { SETTING_TEXT_BLOCKS } from '@agent/lib/setting-text-blocks'; | |
| 5 | + | |
| 4 | 6 | const str = { type: 'string' }; |
| 5 | 7 | const obj = (properties) => ({ |
| 6 | 8 | type: 'object', |
| 7 | 9 | additionalProperties: false, |
| @@ -114,8 +116,12 @@ | ||
| 114 | 116 | verticalAlignment: str, |
| 115 | 117 | }); |
| 116 | 118 | }; |
| 117 | 119 | |
| 120 | +// Where a block keeps its editable text, most-specific first: `text` (button), | |
| 121 | +// `content` (paragraph, heading), `label` (menu item, search). | |
| 122 | +export const RICH_TEXT_ATTRIBUTES = ['text', 'content', 'label']; | |
| 123 | + | |
| 118 | 124 | // Identity attributes (ref, id, className, lock, metadata) stay out — |
| 119 | 125 | // bad values there break the block, not just its look. |
| 120 | 126 | const SIMPLE_ATTRIBUTES = [ |
| 121 | 127 | 'level', |
| @@ -127,10 +133,24 @@ | ||
| 127 | 133 | 'verticalAlignment', |
| 128 | 134 | 'isStackedOnMobile', |
| 129 | 135 | // media-text's image side is an attribute, not child order. |
| 130 | 136 | 'mediaPosition', |
| 137 | + // Without a size field the model writes picture size into style.css. | |
| 138 | + 'mediaWidth', | |
| 139 | + 'minHeight', | |
| 140 | + 'minHeightUnit', | |
| 141 | + // One field per colour; the patcher routes a slug or a hex into WP's pair. | |
| 142 | + 'overlayColor', | |
| 143 | + 'dimRatio', | |
| 144 | + 'iconColor', | |
| 145 | + 'iconBackgroundColor', | |
| 146 | + // The icon follows the network; a new url alone keeps the old icon. | |
| 147 | + 'service', | |
| 131 | 148 | ]; |
| 132 | 149 | |
| 150 | +// core/navigation paints from colour attributes but declares no supports.color. | |
| 151 | +const ATTRIBUTE_COLOR_TYPES = new Set(['core/navigation']); | |
| 152 | + | |
| 133 | 153 | const simpleAttrSchema = (definition) => { |
| 134 | 154 | if (!definition) return null; |
| 135 | 155 | const schema = {}; |
| 136 | 156 | if (definition.type) schema.type = definition.type; |
| @@ -141,8 +161,9 @@ | ||
| 141 | 161 | export const buildBlockSchema = (blockType) => { |
| 142 | 162 | if (!blockType) return null; |
| 143 | 163 | const attributes = blockType.attributes ?? {}; |
| 144 | 164 | const supports = blockType.supports ?? {}; |
| 165 | + const attrColor = ATTRIBUTE_COLOR_TYPES.has(blockType.name); | |
| 145 | 166 | const typography = supports.typography; |
| 146 | 167 | |
| 147 | 168 | const simple = Object.fromEntries( |
| 148 | 169 | SIMPLE_ATTRIBUTES.map((name) => [name, simpleAttrSchema(attributes[name])]), |
| @@ -150,19 +171,30 @@ | ||
| 150 | 171 | |
| 151 | 172 | const properties = prune({ |
| 152 | 173 | ...simple, |
| 153 | 174 | align: attributes.align ? alignSchema(supports.align) : null, |
| 175 | + // Quote and pullquote align text with an attribute, not a typography support. | |
| 176 | + textAlign: attributes.textAlign | |
| 177 | + ? { type: 'string', enum: ['left', 'center', 'right'] } | |
| 178 | + : null, | |
| 154 | 179 | layout: attributes.layout ? layoutSchema(supports) : null, |
| 155 | - // Rich text lives in `text` (button) or `content` (paragraph, heading, …); | |
| 156 | - // the model always sees `text` — the patcher maps it to the real attribute. | |
| 157 | - text: attributes.text || attributes.content ? str : null, | |
| 180 | + // Model-facing name; block-patch maps it back to the real home. | |
| 181 | + text: | |
| 182 | + RICH_TEXT_ATTRIBUTES.some((name) => attributes[name]) || | |
| 183 | + SETTING_TEXT_BLOCKS[blockType.name] | |
| 184 | + ? str | |
| 185 | + : null, | |
| 158 | 186 | url: attributes.url ? str : null, |
| 159 | 187 | backgroundColor: |
| 160 | - attributes.backgroundColor && colorChannel(supports.color, 'background') | |
| 188 | + attributes.backgroundColor && | |
| 189 | + (attrColor || colorChannel(supports.color, 'background')) | |
| 161 | 190 | ? str |
| 162 | 191 | : null, |
| 163 | 192 | textColor: |
| 164 | - attributes.textColor && colorChannel(supports.color, 'text') ? str : null, | |
| 193 | + attributes.textColor && | |
| 194 | + (attrColor || colorChannel(supports.color, 'text')) | |
| 195 | + ? str | |
| 196 | + : null, | |
| 165 | 197 | // Gradients omitted: the theme's preset slugs are too opaque for the model |
| 166 | 198 | // to pick reliably, so gradients go through style.css instead. |
| 167 | 199 | fontSize: attributes.fontSize && feat(typography, 'fontSize') ? str : null, |
| 168 | 200 | fontFamily: |