Add exposed slots: per-entity editable areas in content templates
Overview
Canvas renders structured content through ContentTemplate config entities, but a content creator cannot add free-form components to an individual entity. Per-entity tree editing was deliberately removed for 1.0 pending this feature (#3520487 (closed), #3541000).
Exposed slots are the long-anticipated mechanism (ADR 0006, functional requirement 6): a site builder marks slots in a content template as editable per entity. This gives Canvas the hybrid structured + unstructured model that Paragraphs provides Drupal today, a structured entity with a free-form components area.
The 1.x back end already ships scaffolding (an exposed_slots config property, ValidExposedSlot validation, a render-time subtree merge). The user-facing feature is missing. The community has proven demand and viability with a working fork (AlchemizeCanvas).
Proposed resolution
An exposed slot is a field. Each exposed slot is backed by its own component_tree field on the host bundle. The field machine name is the slot's stable identity, and per-entity slot content lives in that field as an ordinary component tree. This resolves the #3526189 spike ("one Canvas field per exposed slot") with the verdict: adopt. The template's exposed_slots map stores only the attachment point and label, declares config dependencies on the field configs, and detaches automatically when a field is deleted.
Key changes:
- Manage slots from the template editor, non-destructively. An "Expose slot" action either creates a new slot (label plus derived machine name, with copy stating it adds a field to the content type) or re-exposes an existing slot field (restoring content already stored in it, which is also how a slot is moved, per #3528458). Removing a slot is a detach that preserves the field and all entity content. Permanent deletion happens in Field UI behind Drupal's standard field-delete confirmation and batched purge (#3520517 becomes moot).
- Slots may be exposed empty or with content. Template content already in the slot becomes the per-entity-overridable default; the slot-must-be-empty validation is relaxed accordingly.
- Multiple exposed slots per template. The hard
assert(count === 1)inContentTemplate::build()is removed. - Per-entity editing returns. Entities of a templated bundle with exposed slots open in the Canvas editor. The Layout API serves the merged tree (template plus entity slot content) with per-component editability annotations. Template-owned components are locked; only exposed slots accept drops. A slot's default content shows until the editor overrides it (editing materializes an entity-owned copy). Overrides can be kept empty (stored as a shipped render-nothing marker so an empty field still means "inherit"), and reverting to the default is an explicit action. Writes are partitioned into per-slot subtrees and saved to each slot's field; writes touching template-owned components are rejected server-side.
- Rendering merges each referenced slot field into the template tree, an override replacing that slot's default content. Content in detached fields is retained but not rendered (re-exposing restores it).
- Auto-save and preview compose as elsewhere in Canvas: template drafts and entity drafts stay previewable without affecting the live site.
- BREAKING (storage contract, before any UI data exists). Per-entity slot content moves out of a single shared Canvas field into per-slot fields. The shipped test-only shape (
parent_uuid= template-internal component UUID) is abandoned. Entity data references nothing template-internal, so rebuilding a template never orphans entity content. Nothing released stores either shape (test fixtures plus one dev site only), so there is no update hook.
User interface changes
- Template editor. Slots are selectable; selecting a not-yet-exposed slot offers an "Expose slot" action, and an exposed slot shows usage statistics. The expose dialog has two paths: create a new slot or re-use an existing slot field. Removing a slot is a "Detach" confirm.
- Per-entity editor. Template chrome is locked and non-interactive; exposed slots are the editable areas. A locked slot showing its template default offers "Unlock" (materialize an entity copy) or a jump to the template editor. The layers panel surfaces the content template as a shortcut to editing it.
- Contextual panel. The entity form is split: "Page data" is trimmed to page-level metadata, and a new "Content" tab reaches the bundle's content fields (linking out to Drupal's edit form in phase 1, inline widgets in phase 2).
- Navigation. A "Layout" entry point on templated entities launches the editor for that entity. Inside the editor a dedicated "Content" panel (its own left-rail icon) lists templated entities grouped by content type and searchable. The template editor links back to entities using it.
AI assistance was used to draft this issue summary, per Drupal.org's policy on the use of AI when contributing.