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

Command icon 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

afoster created an issue. See original summary.

scott falconer made their first commit to this issue’s fork.

scott falconer’s picture

Status: Active » Needs review

MR: 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.

gábor hojtsy’s picture

Test 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.

scott falconer’s picture

Re: 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.

rakhimandhania’s picture

jibran’s picture

Assigned: Unassigned » jibran

I'll review it this week.

jibran’s picture

Status: Needs review » Needs work

There 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]

jibran’s picture

Assigned: jibran » Unassigned
scott falconer’s picture

Assigned: Unassigned » scott falconer

Pulled in the latest MR code and added the requested unit coverage in PropChoiceOptionsResolver.

scott falconer changed the visibility of the branch 3551315-ai-agents-cant to hidden.

scott falconer’s picture

Assigned: scott falconer » Unassigned
Status: Needs work » Needs review

Test 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.

jibran’s picture

Status: Needs review » Reviewed & tested by the community

This looks good to me, now. Thank you for making the changes.

rakhimandhania’s picture

Issue tags: +AI Page Generation

akhil babu made their first commit to this issue’s fork.

akhil babu’s picture

Status: Reviewed & tested by the community » Fixed

Thanks for working on this.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

akhil babu’s picture

Status: Fixed » Reviewed & tested by the community

Looks like one more approval is needed. So moving back to RTBC

tim.plunkett made their first commit to this issue’s fork.

tim.plunkett’s picture

Status: Reviewed & tested by the community » Fixed

Merged, thanks!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.