关于 platform-widget-generate
用于根据组件名称和数据结构构建 Salesforce WidgetBundle 的技能。它生成 UEM 布局、JSON Schema 和注册元数据,并通过组织操作发现 UI 区块及其架构。
主要功能
- 01创建 UEM 组件树
- 02生成组件 JSON Schema
- 03编写 .uiwidget-meta.xml 注册文件
- 04发现可用 UI 组件
- 05获取所选区块的架构
- 06绑定数据并配置操作
适用场景
- 01构建卡片式富 UI 组件
- 02呈现对象或列表中的字段
- 03添加条件或计算内容
MCP 相关
通过 H360 MCP dispatch 工具调用 Salesforce 可调用操作,以发现 UI 组件并获取其架构;文档还说明了 sf API 请求作为备用方式。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill platform-widget-generate安装后在 Claude Code 中输入/platform-widget-generate确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/conditional.jsonformulas.jsonlist-with-foreach.jsonlocalized-labels.jsonsingle-object.jsonreferences/schema-from-lightning-type.mdwidget-actions.mdwidget-bundle-layout.mdwidget-bundle-validation.mdwidget-formulas.mdwidget-gotchas.mdwidget-labels.mdwidget-meta-directives.mdwidget-styling.md详细介绍
生成 WidgetBundle
编写一个完整的 WidgetBundle:UEM 树(tile/widget)、描述该 widget 输入约定的 JSON Schema,以及用于注册此 bundle 的 .uiwidget-meta.xml。
适用场景
当用户要求创建 widget、mosaic、fragment 或卡片式富 UI 表面时使用。自定义 LWC 渲染器或 Custom Lightning Type bundle 中的 renderer.json 文件不适用此技能;这些应使用 platform-custom-lightning-type-generate。
输入
widgetName(必填)——camelCase标识符;将成为uiWidgets/下的目录名。- shape——widget 要渲染的数据。没有 shape 就无法生成 widget。shape 按以下优先顺序通过两种方式之一提供:
lightningTypeSchema——现有 Apex 支持的 Lightning Type 的{ path, apexClassFqn }。FQN 有两种形式:外部类(<namespace>__<ClassName>),其中外部类是 payload;或内部类(<namespace>__<ClassName>$<InnerClass>),其中指定的内部类是 payload。由platform-lightning-type-widget-coordinate编排器传入。若存在,请按references/schema-from-lightning-type.md推导。- 从用户提示词中提取——未传入
lightningTypeSchema时,直接根据用户提供的内容推断 shape:粘贴的 JSON payload、列出的字段(如“id 为字符串,total 为数字”)或描述性文字。无论通过哪种方式,输出都是相同的有序{ name, type, required }列表。
如果两种来源都无法提供 shape,请停止并先询问用户。
输出
在 <pkgDir>/uiWidgets/<widgetName>/ 中创建三个文件:
| 文件 | 内容 |
|---|---|
<widgetName>.json | Widget envelope——{ "type": "lightning__agentforceWidget", "contentBody": { "widgetBody": { UEM tree rooted at tile/widget } } } |
schema.json | JSON Schema——根节点包含 type: "object" 和 properties.attributes 包装结构,其中包含 lightning:type: "lightning__objectType" 及字段 properties |
<widgetName>.uiwidget-meta.xml | <UiWidgetBundle> 元素,包含 <masterLabel>、<description> 和 <widgetType>JSON</widgetType> |
<pkgDir> 的解析步骤以及 <widgetName>.uiwidget-meta.xml> 的确切结构,请参见 references/widget-bundle-layout.md。
组合结构
Widget 主体是一个 UEM 树,块嵌套在 contentBody.widgetBody 下。根节点为 tile/widget。所有节点——根节点和非根节点——结构相同:没有 type 键;仅包含 definition、可选的 attributes、可选的 meta 和可选的 children。块结构:
interface Block {
definition: string // {namespace}/{blockName} — root is "tile/widget"
attributes?: Record<string, any>
meta?: { // see references/widget-meta-directives.md
forEach?: string
forItem?: string
if?: string
}
children?: Block[]
}
tile/widget.children 的第一个子节点应为一个 tile/column。为使不同表面上的垂直结构保持可预测,widget 内容通常都放在这个首个子节点中。
可用的可调用操作
两者都是目标 org 上的标准可调用操作。
调用约定
使用可用的任一机制调用每个操作:H360 MCP dispatch 工具(向端点路径发送 POST 请求并附带请求正文),或 sf api request rest。使用 v{version} = 此技能的 minApiVersion。切勿使用 curl,也不要提取 org 的访问令牌。
sf api request rest '/services/data/v{version}/actions/standard/<actionName>' --method POST --body '<request body>' --target-org <org>
响应处理
每个响应都是包含一个 envelope 的数组(dispatch 会将其嵌套在 body 下)。请按顺序检查以下三层失败情况:
| 层 | 信号 | 处理方式 |
|---|---|---|
| 传输 / 身份验证 | dispatch 的 status_code 不是 200,或 CLI 以非零状态退出 | 呈现错误 |
| 操作 | [0].isSuccess 为 false,或 [0].errors 非空 | 呈现 errors[].message |
单条结果(仅 getUiComponentSchemas) | 任意 componentSchemas[].success 为 false,即使 [0].isSuccess 为 true | 将该块视为不可用 |
成功时,读取 [0].outputValues。
discoverUiComponents
用途: 发现可用于组合的区块。
端点: POST /services/data/v{version}/actions/standard/discoverUiComponents
请求正文: {"inputs":[{"pageType":"FRAGMENT"}]}
返回: outputValues.componentDefinitions[]——{ definition, description, label } 列表。
getUiComponentSchemas
用途: 获取所选区块的架构(属性类型、必填项与可选项、验证规则)。
端点: POST /services/data/v{version}/actions/standard/getUiComponentSchemas
请求正文: {"inputs":[{"pageType":"FRAGMENT","componentDefinitions":["namespace/definition", ...]}]}
返回: outputValues.componentSchemas[]——{ definition, success, schema } 列表(schema 是 YAML 字符串)。支持部分失败。
切勿将
tile/widget传给getUiComponentSchemas——它是固定包装器,不是可查询的组件。
属性绑定
- 使用
{!$attrs.<attrName>}将区块属性绑定到运行时数据。<attrName>必须与schema.json中的属性名称一致。 - 在
forEach中,应改为引用循环变量,例如"text": "{!$item.name}"。参见references/widget-meta-directives.md。 - 绑定可以是公式表达式,而不只是裸字段引用。支持的函数列表、语法和注意事项,请参见
references/widget-formulas.md。 - **本地化:**仅当用户要求本地化或翻译文本(或提及 Custom Labels /
$Label)时,才将静态的用户可见字符串写为 Custom Label 引用——{!$Label.c.<LabelName>}。参见references/widget-labels.md。
操作
tile/button 支持 actions 属性,可在事件发生时分派操作节点。支持两种操作定义:
| 操作 | 时机 | 必填属性 | 效果 |
|---|---|---|---|
action/openLink | 同步 | url(字符串)。target 可选(_blank|_self,默认为 _blank) | 打开 URL |
action/sendMessage | 异步 | content(字符串) | 向 Agent 发回一条新的用户消息,并开启新一轮交互 |
完整的 sendMessage 和 openLink 按钮 JSON,请参见 references/widget-actions.md。
没有 actions 属性的 tile/button 会呈现为禁用状态——按钮用于触发操作,因此要让按钮可交互,务必为其附加 click 操作。
布局最佳实践
这些约定涵盖组件的结构——区块如何分组和堆叠。
| 基元 | 用途 | 使用场景 |
|---|---|---|
tile/column | 垂直堆叠子项 | 根包装器,以及任何需要堆叠区块的分组 |
tile/row | 水平排列子项 | 两个或更多需要位于同一行的区块 |
tile/spacer | 区块之间的留白 | 内容组之间需要额外间距时 |
- **嵌套:**优先使用扁平布局。只有当子组的视觉方向确实发生变化时,才在
tile/row中嵌套tile/column(反之亦然)。 - **权威区块列表:**上表列出了常见布局基元。务必检查
discoverUiComponents的输出,确认区块存在;切勿仅凭此表假定某个区块名称有效。
样式最佳实践
使用语义化样式(变体,而非具体颜色或像素),每组使用一个主按钮,每个小组件使用一个 h1,且仅使用 Lucide 图标。指定变体、尺寸或图标前,请阅读 references/widget-styling.md。
工作流
-
确定小组件规格——按顺序排列的
{ name, type, required }列表。来源取决于提供的输入(参见 输入):- 如果编排器传入了
lightningTypeSchema→ 按references/schema-from-lightning-type.md推导。 - 否则 → 直接根据用户提示推断列表(粘贴的 JSON 负载、枚举字段列表或描述性文字)。
- 如果编排器传入了
-
发现区块(必需——不要跳过)。 调用
discoverUiComponents可调用操作(参见 Available Invocable Actions),并根据返回的label/description值匹配 widget 规范中的属性类型。如果isSuccess为false、errors非空,或componentDefinitions为空,请停止并原样呈现错误——不要根据记忆、以往运行结果或训练数据臆造区块名称。 -
选择区块。 为 widget 规范中的每个属性选择一个区块,并从 Layout Best Practices 中选择结构基元。
-
获取区块架构(必需——不要跳过)。 调用
getUiComponentSchemas可调用操作,获取所选区块的架构。检查属性元数据,包括每个属性的default——省略任何值等于默认值的属性(参见references/widget-styling.md)。如果isSuccess为false、componentSchemas为空,或任何条目的success: false,请勿使用该区块——从步骤 2 的结果中重新选择其他区块;如果没有可用区块,请停止并呈现错误。切勿根据项目中已有的 widget 臆造。 -
**构建 UEM 树(必须阅读示例——不要跳过)。**首先,确定哪些模式与 widget 规范匹配,并从此技能自己的
examples/目录(<skill-root>/examples/)中读取每个匹配的示例文件:规范中的模式 要读取的示例 单个对象(无迭代) <skill-root>/examples/single-object.json任意列表迭代(根级数组、嵌套列表,或嵌入单对象 widget 的列表) <skill-root>/examples/list-with-foreach.json条件渲染( if绑定到布尔值或公式)<skill-root>/examples/conditional.json计算值、文本/数字转换、本地化格式的数字/货币/日期,或由公式驱动的 if(而非直接使用布尔字段)<skill-root>/examples/formulas.json用户要求使用本地化/翻译文本(Custom Labels) <skill-root>/examples/localized-labels.json规范可能匹配多种模式(例如,包含一些有条件渲染项的项目列表,需要同时读取
list-with-foreach.json和conditional.json)。读取每个匹配的示例,且只读取这些示例——即使模式看起来很熟悉,也不要跳过阅读。编写任何公式之前,请完整阅读
references/widget-formulas.md——这是受支持运算符和函数的权威列表;不要臆造其中未记载的函数名称或运算符。编写任何
$Label引用之前,请完整阅读references/widget-labels.md。然后:
- 将 widget 规范中的每个属性映射到区块属性;保持规范中的顺序。
- **确定根级迭代方式:**单个对象 → 属性直接放在根
tile/column下。集合 → 使用forEach/forItem包裹重复区块。参见references/widget-meta-directives.md。 - 使用
{!$attrs.X}(或在forEach中使用{!$item.X})绑定值。 - 对于条件区块,在
meta上添加"if"——可以是直接的lightning__booleanType属性,也可以是计算结果为布尔值的公式表达式。参见references/widget-formulas.md。 - 当 widget 规范要求计算值(总计、派生标签、格式化字符串)而不是原始字段时,请按照
references/widget-formulas.md,使用{!...}中的公式表达该值——不要为了保存可通过现有属性计算出的值而臆造新的架构属性。
-
编写
schema.json。 根据 widget 规范构建 JSON Schema。字段位于attributes包装器下一层:{ "title": "<Widget Display Name>", "description": "<one line about what the widget shows>", "type": "object", "properties": { "attributes": { "lightning:type": "lightning__objectType", "properties": { "<propertyName>": { "title": "<label>", "description": "<short description>", "lightning:type": "<lightning__textType | lightning__numberType | ...>" } } } } }必需的根键:
title、type: "object"、properties.attributes(包含lightning:type: "lightning__objectType"和嵌套的properties映射)。关于基本类型的完整指导,请参见references/schema-from-lightning-type.md。 -
编写
<widgetName>.uiwidget-meta.xml。 具体结构请参见references/widget-bundle-layout.md。 -
解析
<pkgDir>并写入 bundle。 按照references/widget-bundle-layout.md中的## Resolving <pkgDir>流程操作。一个 widget bundle 是一组三个文件——必须在同一步写入全部三个文件;少于三个文件的 bundle 不完整,无法部署。<pkgDir>/uiWidgets/<widgetName>/<widgetName>.json # widget envelope — UEM tree (primary artifact) <pkgDir>/uiWidgets/<widgetName>/schema.json # attribute contract for the envelope <pkgDir>/uiWidgets/<widgetName>/<widgetName>.uiwidget-meta.xml # UiWidgetBundle registration每个文件都有不同的作用:
<widgetName>.json— 包含tile/widgetUEM 树的 widget 封装。它是主要产物;schema.json是它的配套契约,不能替代它。schema.json— 用于定义封装中{!$attrs.X}绑定所引用属性的 JSON Schema。<widgetName>.uiwidget-meta.xml— 用于注册 bundle 以进行源代码跟踪和部署的UiWidgetBundle元素。
在继续自检之前,先写入全部三个文件。
-
自检。 报告前,运行检查并逐项报告结果(
pass或fail (<reason>))。不要用单独一行“全部通过”来概括——请列出每项检查,方便审核者发现被悄然跳过的检查。 运行references/widget-bundle-validation.md中的所有检查。 -
报告标签。 如果 widget 使用
$Label,请在回复末尾附上references/widget-labels.md中所述的标签报告。
规则 / 约束
| 约束 | 原因 |
|---|---|
Block 定义遵循 {namespace}/{blockName} 格式,并且必须与 discoverUiComponents 的输出一致 | 运行时会根据精确的定义字符串解析 block |
绝不要将 tile/widget 传递给 getUiComponentSchemas | 它是固定封装,不是可查询的组件 |
仅在 references/ 和 examples/ 文件所指定的步骤中读取这些文件 | 提前读取会浪费上下文,并且早于用于确定所需文件的发现结果 |
正文中的每个 {!$attrs.X} 都必须对应 widget schema.json 中的一个属性 | 不得臆造字段 |
每个 tile/button 都必须包含一个使用 action/openLink 或 action/sendMessage 的 actions.click 条目 | 这两种是 tile 唯一支持的操作定义;没有操作的按钮会呈现为禁用状态 |
任何 Bash 工具调用中都不得使用 $(…)、反引号、<(…)、花括号展开 {a,b,c} 或 eval/exec | 即使在 Bypass 模式下,Vibes 的安全 shell 过滤器也会要求对这些模式进行手动批准。请发出单独的命令(mkdir -p a && mkdir -p b),或用各自的命令打印每个值并根据输出进行判断——不要将值捕获到 shell 变量中 |
注意事项
已知故障模式及其修复方法(不完整的 schema、未解析的绑定、错误的输出路径、if 真值判断、失效的按钮、操作选择):参见 references/widget-gotchas.md。
参考文件索引
| 文件 | 何时阅读 |
|---|---|
references/widget-meta-directives.md | 使用 forEach / forItem(迭代)和 if(条件渲染)时,包括嵌套循环 |
references/schema-from-lightning-type.md | 提供 lightningTypeSchema 时;如何从基于 Apex 的 Lightning Type 派生组件 schema.json |
references/widget-bundle-layout.md | 文件夹布局、-meta.xml 结构、<pkgDir> 解析规则 |
references/widget-bundle-validation.md | 第 9 步——报告结果前要运行的检查 |
references/widget-formulas.md | 支持的公式函数和运算符,包括区域格式化函数(FORMATNUMBER、FORMATCURRENCY、FORMATDATE,等等) |
references/widget-labels.md | 本地化——$Label 语法、命名和复用、允许的属性、与数据组合、创建标签以及标签报告 |
references/widget-actions.md | action/sendMessage 和 action/openLink 的完整 tile/button JSON |
references/widget-styling.md | 变体、大小、标题和图标规范 |
references/widget-gotchas.md | 已知故障模式及其修复方法 |
examples/single-object.json | 单对象模式(通过 {!$attrs.X} 进行根绑定,不迭代) |
examples/list-with-foreach.json | 任意列表迭代场景——根级集合、嵌套列表,以及嵌入单对象组件中的列表(例如,迭代外层 Apex 负载中的 List<InnerClass>) |
examples/conditional.json | 条件模式(对 meta 使用 if,包括同时使用 if 和 forEach) |
examples/formulas.json | 公式用法——文本/算术/日期转换、区域格式化函数、使用 IF / AND / NOT 控制 tile/text 和 meta.if |
examples/localized-labels.json | 本地化——标题、说明文字、徽章(由 IF 在标签之间选择)、按钮标签,以及与数据拼接的标签中的 $Label |