关于 canvas-content-templates
此技能指导在 Drupal Canvas 项目中创建和修改 JSON 内容模板,将实体、视图模式映射到 Canvas 组件及字段。适用于构建 Drupal 内容展示布局的开发者,并提供字段来源、视图模式检查和验证流程。
主要功能
- 01按实体类型、内容类型和视图模式定义模板
- 02将 Drupal 字段映射到组件属性
- 03支持静态值、实体字段和实体 URL
- 04使用字段来源与视图模式上下文
- 05通过命令验证模板
适用场景
- 01为文章全文或摘要视图创建模板
- 02将实体标题、正文或图像绑定到组件
- 03修改模板并保留现有元素 ID
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ drupal-canvas/ skills --skill canvas-content-templates安装后在 Claude Code 中输入/canvas-content-templates确认已加载。
详细介绍
项目类型
应用此技能之前,请检查 package.json 中是否有名为
@drupal-canvas/headless 或以 @drupal-canvas/headless- 开头的依赖。如果有,
这是一个 Canvas Headless 代码库——请先阅读
canvas-headless。无头代码库支持内容模板,但只有在
canvas.config.json 中启用 sync.contentTemplates 时才能创建;使用 Canvas Create
(@drupal-canvas/create) 生成的项目默认会禁用此功能,并且不会包含
content-templates/ 目录。如果没有此类依赖,则这是一个 Canvas 渲染的 React 代码库:组件使用 React(index.jsx/.tsx),并且本技能中的所有内容均适用。这是仅有的两种项目类型。
规范定义
内容模板是一个 JSON 文件,它将 Drupal 内容实体(由实体类型、bundle 和视图模式标识)映射到 Canvas 组件元素树。内容模板定义如何使用 Canvas 组件渲染 Drupal 内容,包括哪些实体字段映射到哪些组件属性。
位置和命名
在已配置的内容模板目录(canvas.config.json 中的 contentTemplatesDir)中编写内容模板。如果未设置 contentTemplatesDir,则默认使用顶层的 content-templates/ 目录。
命名约定: {entityType}.{bundle}.{viewMode}.json
示例:
<content-templates-dir>/node.article.full.json<content-templates-dir>/node.article.teaser.json
内容模板格式
每个内容模板都是一个具有以下顶层键的 JSON 对象:
label(必填):模板的人类可读描述(例如,"文章 — 完整内容")entityType(必填):Drupal 实体类型(例如,"node")bundle(必填):内容类型 / bundle(例如,"article")viewMode(必填):视图模式(例如,"full"、"teaser")elements(必填):以元素 ID 为键的组件元素对象
文件名必须与 {entityType}.{bundle}.{viewMode}.json 匹配,并且与
文件中的值相对应。
元素
elements 的结构与 Canvas 页面规范相同——示例请参见
canvas-page-definition 技能。唯一的区别是属性值类型:内容模板还支持从宿主 Drupal 实体获取值的属性来源(如下所述)。
elements 中的每个条目都以元素 ID 为键,并包含:
type(必填):Canvas 组件类型,前缀为js.(例如,"js.hero"、"js.card"、"js.section")props(可选):组件属性——可以是静态值或实体字段引用slots(可选):将插槽名称映射到同一模板中的元素 ID 数组
元素 ID
除非有意保留其他来源(例如导出的 Canvas 内容模板)中的 ID,否则请使用稳定且描述明确的元素 ID。
属性值类型
属性可以包含三种类型的值:
1. 静态值
普通 JSON 值——字符串、数字、布尔值,或包含 value 和
format 的富文本对象。
{
"buttonLabel": "Build your hybrid team",
"darkVariant": true,
"description": {
"value": "Some <em>HTML</em> content here.",
"format": "canvas_html_block"
}
}
2. 实体字段引用
从 Drupal 实体动态获取的值。使用 sourceType: "entity-field" 和
expression 字符串。
{
"title": {
"sourceType": "entity-field",
"expression": "ℹ︎␜entity:node:article␝title␞␟value"
}
}
3. 宿主实体 URL
指向实体规范 URL 的链接。使用 sourceType: "host-entity-url"。
{
"link": {
"sourceType": "host-entity-url",
"absolute": false
}
}
absolute(可选,默认为 true)用于控制 URL 格式:false 会生成
相对路径(/node/123),true 会生成完整 URL
(https://example.com/node/123)。页面内链接请设置 absolute: false;规范链接、信息流或任何供站外使用的内容,请省略此项或设置为 true。
实体字段表达式语法
表达式使用特殊的 Unicode 控制字符作为分隔符:
ℹ︎␜entity:{entityType}:{bundle}␝{fieldName}␞␟{property}
| 字符 | 名称 | 用途 |
|---|---|---|
ℹ︎ | 信息 | 表达式起始 |
␜ | 文件分隔符 | 段分隔符 |
␝ | 组分隔符 | 实体/字段边界 |
␞ | 记录分隔符 | 字段/属性边界 |
␟ | 单元分隔符 | 属性起始 |
简单字段表达式
获取字段中的单个属性:
ℹ︎␜entity:node:article␝title␞␟value
ℹ︎␜entity:node:article␝body␞␟processed
图像字段表达式
对于需要多个属性的图像字段,请使用多属性语法,并用 ↠(向右箭头)映射源属性:
ℹ︎␜entity:node:article␝field_image␞␟{src↠src_with_alternate_widths,alt↠alt,width↠width,height↠height}
{prop↠source,...} 语法将组件 prop 键映射到实体字段属性。
实体引用遍历
要遍历实体引用(例如作者的个人资料图片),请使用 ␜␜ 链接实体段:
ℹ︎␜entity:node:article␝uid␞␟entity␜␜entity:user␝user_picture␞␟{src↠src_with_alternate_widths,alt↠alt,width↠width,height↠height}
此表达式依次访问:节点 → uid 字段 → 引用的用户实体 → user_picture 字段。
可用的 prop 源
prop 源描述了哪些 Drupal 实体字段可以映射到哪些组件 prop。可用时,它们是构建实体字段映射的权威参考——务必优先使用,而不要手动构造表达式。
prop 源存储在 .agents/drupal-canvas/prop-sources.json 中,按 {entityType} → {bundle} → {js.component} → {propName} → 源选项数组组织。每个源选项包含:
label:字段的易读描述(例如"Title"、"Image"、"Body")source:要在内容模板中直接使用的 prop 值对象—— 可以是{ "sourceType": "entity-field", "expression": "..." },也可以是{ "sourceType": "host-entity-url", "absolute": false }
如何使用 prop 源:
- 确定可用的 bundle: 顶层键是实体类型,其下嵌套了 bundle(例如
node.article、node.page) - 选择组件: 每个 bundle 下的第二层键(例如
js.card、js.hero)指明哪些组件具有可映射到该 bundle 的 prop - 选择 prop 源: 对于每个组件 prop,查看可用源数组并选择最合适的源。将
source对象直接复制到内容模板的 prop 值中——如果存在匹配的源,请勿手动构造实体字段表达式 - 根据 label 选择最佳源: 如果某个 prop 有多个可用源,请根据
label选择语义上正确的源(例如,对于卡片的headingprop,选择"Title"而不是"Alternative text")
如果没有可用的 prop 源,请按照工作流部分中的新鲜度检查来刷新 Agent 上下文。仅当不存在匹配的源时,才回退到手动构造实体字段表达式。
可用的视图模式
视图模式定义了连接的 Drupal 站点上有效的实体类型、bundle 和视图模式组合。使用它们来确定可以创建哪些内容模板,以及哪些模板已经存在。
视图模式存储在 .agents/drupal-canvas/view-modes.json 中,按 {entityType} → {bundle} → {viewMode} → 标签字符串组织(例如 "Full content"、"Teaser")。
如何使用视图模式:
- 发现有效组合: 仅为此处列出的实体类型 / bundle / 视图模式组合创建内容模板
- 检查现有覆盖情况: 列出内容模板目录,查看哪些组合已有模板——避免创建重复模板
- 选择视图模式: 如果用户未指定视图模式,请向他们展示可用选项以及哪些选项仍需创建模板(根据本地文件判断),并在继续操作前请他们确认
如果没有可用的视图模式,请按照工作流部分中的新鲜度检查来刷新 Agent 上下文。如果仍不可用,请询问用户要使用哪种视图模式。
工作流
创建新的内容模板
- 确保 Agent 上下文是最新的: 检查
.agents/drupal-canvas/prop-sources.json和.agents/drupal-canvas/view-modes.json是否存在。如果任一文件缺失,或上次修改时间超过 1 小时,请运行npx canvas agents-context content-templates以刷新。如果文件的修改时间在 1 小时以内,则跳过刷新 - 确定实体映射: 确定模板的实体类型、捆绑包和视图模式。查阅可用的视图模式,确认组合有效。如果用户未指定视图模式,请向他们展示可用选项,并在继续之前请他们确认要使用哪个视图模式
- 检查现有模板: 列出已配置的内容模板目录,查看其中已有的模板——除非用户提出要求,否则不要覆盖
- 检查属性来源: 阅读目标捆绑包的可用属性来源,了解哪些组件具有可映射的属性
- 选择组件: 查看
src/components/中可用于组成布局的组件。使用component.yml文件了解每个组件的属性和插槽。与可用属性来源交叉核对,确认哪些属性可以映射到实体字段 - 选择元素 ID: 为每个元素使用稳定且描述性强的 ID
- 构建模板: 按照上述格式编写 JSON 文件
- 映射实体字段: 尽可能使用可用属性来源中的来源对象。对于固定内容,使用静态值。仅在没有匹配的来源时,才手动构建实体字段表达式
# Check freshness of agents context files and refresh if missing or older than 1 hour.
# Use `date -r <file>` to check file modification time (works on macOS and Linux).
# If stale or missing:
npx canvas agents-context content-templates
# Check available view modes
cat .agents/drupal-canvas/view-modes.json
# Check existing templates (using configured contentTemplatesDir, default: content-templates/)
ls content-templates/
# Read prop sources for the target bundle (prefer jq when available)
jq '.node.article' .agents/drupal-canvas/prop-sources.json
# Check available components
ls src/components/
# Read a component's props to know what to map
cat src/components/hero/component.yml
修改现有内容模板
- 确保 Agent 上下文是最新的: 按照创建工作流中的相同新鲜度检查进行操作——文件缺失或超过 1 小时时刷新,不满 1 小时则跳过
- 阅读现有模板,了解其当前结构
- 保留未更改元素的现有元素 ID
- 为新添加的元素选择描述性强的 ID
- 验证插槽引用——插槽中的所有 ID 都必须存在于
elements中
验证
完成前验证每个编写的内容模板。
npx canvas validate
面向用户的沟通
.agents/drupal-canvas/*.json 文件属于内部 Agent 上下文。不要在摘要、提交消息或其他面向用户的输出中提及这些文件、文件路径或文件内容——描述映射了什么,而不是映射来源于何处。