Overview
When using AI chat to update content on the canvas, the Canvas Template Agent creates a list of components it can choose from as an input in decision-making. To see what is available to the agent visit: https://v2025demo.ddev.site/admin/config/ai/explorers/tools_explorer?too...
and select "run Function".
This outputs yaml data like this (example truncated to show "section" component from Mercury)
sdc.mercury.section:
id: sdc.mercury.section
name: Section
description: 'A section for components in a column. This can set in between 1 to 4 columns in different ratios. Note that the main slot is where the columns can be set. Header and footer always is 100% width. ALWAYS PUT THE COMPONENTS IN THE main_slot '
group: Layout
props:
columns:
name: 'Content layout'
description: 'No description available'
type: string
width:
name: 'Section width'
description: 'No description available'
type: string
default: 'hg:lg:max-w-full'
enum:
- 'hg:lg:max-w-full'
- 'hg:lg:max-w-9/10'
- 'hg:lg:max-w-8/10'
- 'hg:lg:max-w-3/4'
- 'hg:lg:max-w-1/2'
margin_block_start:In the Canvas UI, the "section width" property values in the select list are "100%, 90%, 80%, 75%, 50%".
These people-friendly UI labels do not appear to the Agent; They only see the values.
If I write a prompt with things I want to happen with specific properties, I'll use the labels I can see on the screen.
"Resize the width of all the sections currently set to 100% to 75%". Currently, the LLM must interpret what I type and guess which values are the closest.
Proposed resolution
Update the Get Component Context (canvas_ai) function to show both the value and labels for components available to Canvas.
- label: '100%'
value: 'hg:lg:max-w-full'
- label: '90%'
value: 'hg:lg:max-w-9/10'
- label: '80%'
value: 'hg:lg:max-w-8/10'
- label: '75%'
value: 'hg:lg:max-w-3/4'
- label: '50%'
value: 'hg:lg:max-w-1/2'User interface changes
None.
Issue fork canvas-3551315
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #4
scott falconer commentedMR: https://git.drupalcode.org/project/canvas/-/merge_requests/526
Summary
- Add internal enum option resolver + prop metadata normalizer to expose enum_options (label/value pairs) from enum + meta:enum, with x-translation-context support.
- Wire normalizer into Canvas AI component context for SDC + JS components; JS path pulls meta:enum from component metadata because propSources strips it.
- Unit coverage for resolver behavior (map/list meta:enum, fallbacks, cache context when translating).
Evidence
- Example excerpt from getComponentContextForAi output:
image_position:
enum_options: [{ label: 'Image on right', value: 'hg:md:flex-row' }, { label: 'Image on left', value: 'hg:md:flex-row-reverse' }]
- Intent test PASS (AI Explorer): final answer contains "Image on left"; tool payload uses enum value hg:md:flex-row-reverse (stored value).
Tests
1. ddev xb-phpcs (pass; deprecation warning about SlevomatCodingStandard include config)
2. ddev xb-phpunit tests/src/Unit/Component/Schema/PropChoiceOptionsResolverTest.php (pass)
3. ddev xb-phpunit tests/src/Functional/BlockComponentFormTest.php (fails: expected size 0, got 1; known issue 3570699; not addressed here)
4. ddev exec -- drush ev '\Drupal::service("account_switcher")->switchTo(\Drupal\user\Entity\User::load(1)); $h=\Drupal::service("canvas_ai.page_builder_helper"); print $h->getComponentContextForAi();' > /Users/scott/dev/drupal-contrib/canvas-dev/test_outputs/get_component_context.yaml (warns missing astro-hydration assets)
5. python3 /Users/scott/.codex/skills/drupal-intent-testing/scripts/intent_test.py /Users/scott/dev/drupal-contrib/worktrees/canvas/issue-3551315-people-friendly-labels/.intent/issue_3551315.yaml --output-dir /Users/scott/dev/drupal-contrib/canvas-dev/test_outputs/intent_3551315_run4 (verdict PASS)
AI disclosure
Drafted with AI assistance; reviewed and tested by a human.
Comment #5
gábor hojtsyTest looks really comprehensive.
I'm a bit surprised we need the PropChoiceOptionsResolver as new code, is that otherwise being done on the client side in JS currently? I would imagine this kind of resolution of what the prop label is should already exist somewhere to display the proper UI to begin with but I can imagine it exists on the client side?
In terms of the CI results, the phpstan ones are not even in the files touched so definitely look unrelated. The playwright ones there are quite a few concerning labels which may be related.
Comment #6
scott falconer commentedRe: Why PropChoiceOptionsResolver is needed
I checked core to see if we could reuse that logic server‑side, but there’s a gap where core normalizes enum + meta:enum inside ComponentMetadata::parseSchemaInfo() (private, tied to SDC component instantiation) and it isn’t exposed as a reusable "options resolver" for arbitrary schema fragments.
Some server‑side UIs (e.g., IconExtractorSettingsForm) map meta:enum to select options, but that logic is scoped to specific forms. Meanwhile, the Canvas AI context builder runs on the backend and currently just dumps raw enum values, so AI tools can’t access the user‑visible labels.
The resolver bridges that gap (including code components, which aren’t covered by core SDC discovery) and produces a canonical {value, label} list with proper translation context.
Re: PHPStan
Agreed, those look unrelated to the touched files.
Re: Playwright failures
Investigating now.
Comment #7
rakhimandhania commentedComment #8
jibranI'll review it this week.
Comment #9
jibranThere was a minor merge conflict which is addressed but other than that patch looks okay, I think Unit tests are enough and we don't need functional test for this change. However, I think we need a test for following case.
[unable to encode]Comment #10
jibranComment #11
scott falconer commentedPulled in the latest MR code and added the requested unit coverage in PropChoiceOptionsResolver.
Comment #13
scott falconer commentedTest failure seems unrelated so marking this as needs review.
- The trace shows a mixed Playwright flake pattern: 1 failed test (Canary › Install modules) plus 4 flaky tests in unrelated specs like responsiveImage and componentOperations.
Comment #14
jibranThis looks good to me, now. Thank you for making the changes.
Comment #15
rakhimandhania commentedComment #17
akhil babuThanks for working on this.
Comment #19
akhil babuLooks like one more approval is needed. So moving back to RTBC
Comment #22
tim.plunkettMerged, thanks!