关于 editor-creator
用于在 Datex Studio 分支上创建或修改编辑器配置的 skill,适合构建单实体查看与编辑界面。它强调数据源与实体字段绑定、查看/编辑模式,以及通过 CRUD action 保存。
主要功能
- 01创建或修改编辑器配置
- 02为单个实体绑定字段
- 03配置只读与编辑模式切换
- 04通过包装 function 调用 CRUD action
- 05验证配置后写回分支
适用场景
- 01为单条记录创建详情与编辑页面
- 02按实体属性配置编辑器字段
- 03为现有配置调整保存和编辑行为
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ datex/ skills --skill editor-creator安装后在 Claude Code 中输入/editor-creator确认已加载。
附带文件evals/evals.jsonreferences/editors.md
详细介绍
Editor Creator
在分支上创作或修改 Datex Studio 编辑器(configurationTypeId=4)——这是一个单实体查看/编辑屏幕,可加载一条记录,将字段与其属性一一绑定,在只读模式和编辑模式之间切换,并通过调用 CRUD 操作的封装函数保存更改。
参考资料
- ../datex-studio-shared/branch-setup.md — 分支/连接选择(各技能通用)
- references/editors.md — 权威的编辑器创作参考:文件结构、运行时全局变量、调用契约、常见模式、预检清单
- ../datex-studio-conventions/file-format.md —
configurationTypeId表和 TypeScript 表达式编码规则 - ../datex-studio-conventions/naming-conventions.md —
_editor后缀、文件名主体匹配、显示名称规则 - ../datex-studio-runtime/runtime-globals.md — 编辑器代码中可用的平台注入全局变量(
$editor、$flows、$shell、$utils等) - ../datex-studio-runtime/calling-conventions.md — UI 层调用规则(调用函数,绝不调用操作;通过
$apis.<Package>.FootprintApi.extendedActions.<action_name>执行 CRUD) - ../form-creator/references/forms.md — 用于临时收集输入的同类组件(编辑器与表单的选择)
- ../grid-creator/references/grids.md — 编辑器的典型宿主,通过行点击/行操作流调用
- ../datasource-creator/references/flow-datasources.md — 编辑器后端数据源所需的单结果形状
- ../component-wiring-check/references/component-wiring.md — 宿主引用契约、必须声明 vars 的规则、moduleId 规则
依赖项
requirements-gathering技能 — 如果对话上下文中尚无需求简报,则调用此技能生成一份form-creator/grid-creator技能 — 当需求实际涉及临时收集输入(表单)或多记录列表(网格),而非单实体详情屏幕时调用datasource-creator技能 — 当编辑器所依赖的单结果流数据源需要作为独立配置创作时调用(较少见——通常使用嵌入式私有数据源)。要创作嵌入式数据源,请遵循 ../datasource-creator/references/datasources.md → 创建自有数据源 —generate-flow --single --get-flow已会生成编辑器所需的结构,因此只需应用信封差异。component-wiring-check技能 — 推送前调用,以审核编辑器宿主(中心标签页 / 网格行操作 / 表单)上的configParameters↔ 目标inParams契约type-definition-creator技能 — 当编辑器绑定的实体接口或相关类型定义需要创作或扩展时调用(例如,向架构添加字段,而编辑器必须新增相应绑定)tailoring-overlay技能 — 当编辑器的客户专属扩展(添加字段、修改绑定)需要放入定制覆盖层,而非基础配置中时调用
CLI 生命周期
编辑器编写通过 dxs configuration 进行——这是一个适用于所有平台配置类型的通用 CRUD 基元。没有 dxs editor 子命令,也不支持字段级修补;你需要构建(或获取并提取)完整的 JSON 正文,编辑后再将其整体推回。CLI 中的类型标识符是 editor(小写,与 ConfigurationEndpoints.normalize_type 的输出一致),对应 configurationTypeId: 4。
创建新编辑器:
# 1. Build body.json from scratch (see references/editors.md → Minimal Valid Skeleton)
# 2. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate editor -b <branchId> -D body.json
# 3. Create
dxs configuration upsert editor -b <branchId> -D body.json
编辑现有编辑器:
# 1. Fetch — note the envelope wrapper
dxs configuration get editor <configId> -b <branchId> -O envelope.json
# 2. EXTRACT THE INNER BODY (round-trip footgun guard — see "Round-trip rule" below)
jq .json envelope.json > body.json
# 3. Edit body.json
# 4. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate editor -b <branchId> -D body.json
# 5. Push
dxs configuration upsert editor -b <branchId> -D body.json
往返规则(至关重要)
编辑现有配置时,切勿将 envelope.json 直接通过管道传给 dxs configuration upsert——这会在不报错的情况下破坏配置内容。上文修正后的步骤(在编辑前使用 jq 提取内部的 .json)适用于任何往返操作,不可省略。规范的往返流程和相关底层错误,参见 ../datex-studio-shared/configuration-roundtrip.md。
编辑器内容复杂——正文中会包含大量已填充内容,包括 toolbar、fieldsets、flows、datasourceConfig 和 datasources,并且每个嵌入的 flow 都带有 code 字段。在如此庞大的 JSON 中进行精细修改很容易出错;必须严格遵循往返流程(获取 → 使用 jq 提取 → 编辑 → 验证 → 推回),不能妥协。
工作流
[Phase 1: Setup + Requirements]
Follow branch-setup.md for branch/connection selection
|
[requirements brief in context?]
+-----+-----+
| |
YES NO -> invoke `requirements-gathering`
| |
+-----+------+
|
[Phase 2: Editor vs Form vs Grid decision]
Consult references/editors.md → "Purpose & When to Use":
- single hydrated entity, view/edit toggle, persist via CRUD -> editor
- transient input collection, returns outParams, no entity -> form
- multi-record tabular view -> grid
If form -> invoke `form-creator` instead and stop here.
If grid -> invoke `grid-creator` instead and stop here.
Create-only dialogs are usually forms, not editors.
|
[Phase 3: Author editor body]
Build body.json:
- File shape (configurationTypeId=4, *-editor.json suffix,
referenceName ends _editor)
- Embedded single-result private datasource in datasources[]
(configurationTypeId=6, type=flows, accessModifier=private,
getFlow populated; getListFlow/getByKeysFlow null;
resultIsCollection=false; outParams[0].isCollection=false)
- Flow shape: getFlow populated; getListFlow and getByKeysFlow null
(editors call .get() — code left in getListFlow silently breaks hydration)
- Entity shape mirrored: datasources[0].outParams[0].objectTypeDef
== datasourceConfig.configOutParameters.result.objectTypeDef
- onInitFlowConfig (pre-hydration; no $editor.entity reads)
vs onDataLoadedFlowConfig (post-hydration; entity-derived defaults)
- View/edit toggle via $editor.vars.edit_mode (declared in vars[])
- Save branches on $editor.entity.isNew (crud_create vs crud_update)
- onFormValidateFlowConfig gates $editor.toolbar.<save>.control.readOnly
- EditorFields parity: every entity-interface field has a binding
(stub with removed:true when UX isn't ready)
- Invoke `datasource-creator` if a standalone datasource is missing
- Invoke `component-wiring-check` to audit host's configParameters
|
[Phase 4: Validate + push]
dxs configuration validate editor -b <branchId> -D body.json
|
+----+----+
| |
CREATE MODIFY-EXISTING
| |
| use the corrected round-trip
| (get -O envelope -> jq .json -> body)
| |
+----+----+
|
v
dxs configuration upsert editor -b <branchId> -D body.json
(upsert creates or updates by referenceName — one command for both)
|
[Phase 5: Verify in Studio (optional)]
Open the editor as a dialog (from its host hub/grid); confirm
hydration, toggle into edit mode, save commits, cancel restores
|
[invoke `post-edit-verification`; then `component-validator`]
阶段详情
阶段 1:设置与需求
- 按照 ../datex-studio-shared/branch-setup.md 选择分支和连接。切勿假定分支 ID——请让用户确认。
- 检查对话上下文中是否已存在需求简报(由
requirements-gathering或其他调用技能生成)。- 已有简报——使用该简报。简报应说明正在编辑的实体(以及宿主传入哪个键)、哪些字段可见或可编辑、仅查看与编辑模式的行为、保存语义(哪个 CRUD 操作会持久化更改),以及同一个编辑器是否也处理创建模式。
- 没有简报——先调用
requirements-gathering技能。预先明确实体结构和保存语义,可避免后续进行繁琐的往返操作。
阶段 2:选择编辑器、表单或网格
编写前请参阅 references/editors.md → 用途与适用场景。这不是风格上的选择——编辑器、表单和网格承担不同的作用,不能互换。
在以下情况选择编辑器:
- 用户查看或修改由键标识的单个实体。
- 字段级输入与该实体的属性一一对应。
- UX 需要在不同视图模式(只读)和编辑模式(输入已解锁、保存已启用)之间切换。
- 工作流通过 CRUD 操作(
crud_create_entity/crud_update_entity)持久化更改。
在以下情况改用表单:
- 工作是临时收集输入,与已存储的记录无关。
- 对话框将
outParams返回给调用方;不会隐式持久化任何内容。 - 对话框仅用于创建,尚无实体可供填充。
在以下情况改用网格:
- 用户以表格布局查看或操作多条记录。
如果答案是表单,请停止并调用 form-creator。如果答案是网格,请停止并调用 grid-creator。编辑器可以通过 $editor.entity.isNew 处理创建模式,但只有在同一个组件同时处理同一实体类型的创建和编辑时,这种做法才合理——纯创建对话框通常更适合使用表单。
阶段 3:编写编辑器正文
根据 references/editors.md → 最小有效骨架中的骨架构建 body.json。要点如下:
-
文件基础要求。 遵循下方的预检清单和 ../datex-studio-conventions/universal-checklist.md;
-editor.json文件格式参见 references/editors.md → 文件位置与命名。 -
嵌入式单结果数据源。 每个编辑器都在
datasources[]中嵌入且仅嵌入一个私有单结果流程数据源。必需的形状:configurationTypeId: 6、type: "flows"、accessModifier: "private"、已填充的getFlow、getListFlow: null、getByKeysFlow: null、resultIsCollection: false、outParams[0].isCollection: false。返回集合的数据源会破坏编辑器引用——datasourceConfig.get({...})无法从列表中填充$editor.entity。参见 references/editors.md → 嵌入式私有数据源 和 ../datasource-creator/references/flow-datasources.md → 单结果形状。默认由其所有者管理——除非有覆盖规则适用,否则应将数据源编写在嵌入位置;只有当第二个使用方确实需要相同查询时,才提升为独立数据源(完整覆盖规则列表参见 ../datasource-creator/references/datasources.md → 默认由其所有者管理)。如果确实需要独立数据源,请调用datasource-creator来编写。分支的服务端用法门禁会在契约验证时独立执行此规则——如果编辑器的数据源未通过get返回单个非集合结果,就会阻止发布。 -
实体形状镜像。
datasources[0].outParams[0].objectTypeDef(数据源一侧)必须逐字段镜像datasourceConfig.configOutParameters.result.objectTypeDef(编辑器使用方一侧)。任何实体形状变更都必须在同一次编辑中修改这两个位置。datasourceConfig上的datasourceKeyDef必须与嵌入式数据源的keyDef完全一致,并匹配宿主传入的inParams形状。 -
onInitFlowConfig与onDataLoadedFlowConfig。 编辑器中最常见的错误就是混淆这两者。onInit在数据源解析之前触发——此时$editor.entity尚未填充;读取它会得到 undefined。使用onInit进行变量设置、在isNew时为edit_mode设置默认值,或处理独立于实体数据的逻辑。onDataLoaded在实体填充之后触发——用于设置基于实体的默认值、保存编辑前的快照,以及根据实体字段填充仅供 UI 使用的状态。参见 references/editors.md → onInitFlowConfig 与 onDataLoadedFlowConfig。 -
查看/编辑模式切换。 在顶层
vars[]中声明edit_mode(流程代码中写入的每个$editor.vars.<id>都必须声明——参见 ../component-wiring-check/references/component-wiring.md)。当!edit_mode时显示编辑按钮;当edit_mode时显示保存和取消按钮。字段的readOnly绑定到!edit_mode(或按字段计算,以便锁定某些字段)。取消操作会恢复实体的原始值——重新调用数据源,或在onDataLoaded期间将编辑前的值存入$editor.vars。 -
保存操作根据
$editor.entity.isNew分支。 新建时调用crud_create_entity(或由函数封装的等效创建操作);已有实体时调用crud_update_entity。这两个分支都不能硬编码。调用规则适用于 UI 层:编辑器代码通过$flows.<Package>.<fn>调用函数;函数再通过$apis.<Package>.FootprintApi.extendedActions.<action_name>({...})封装对 CRUD 操作的调用。参见 ../datex-studio-runtime/calling-conventions.md。 -
由验证控制的保存按钮。 每当任何字段发生更改时,都会运行
onFormValidateFlowConfig。根据字段值计算is_valid(必填字符串通常使用$utils.isDefinedTrimmed),然后设置$editor.toolbar.<save_button>.control.readOnly = !is_valid。也可以将每个字段的提示信息写入control.validationMessage。请参阅 references/editors.md → 由 onFormValidateFlowConfig 控制的保存按钮。 -
EditorFields 一致性。 平台会根据绑定实体的接口自动生成
EditorFieldsTypeScript 类型。编辑器必须为该接口中的每个字段声明一个带有controlConfig的绑定 — 嵌套路径会以__展平(例如,replenishments.rules→ 字段 IDreplenishments__rules)。缺少绑定会导致导入失败。架构新增字段时,编辑器应在同一次修改中添加相应绑定;如果实际 UX 尚未就绪,可以使用removed: true的存根绑定。对于对象数组子树,请使用按钮 +$editor.vars.<var>+set_config合并模式。请参阅 references/editors.md → EditorFields 要求为每个架构字段提供绑定以及序列化配置 JSON 部分中关于包装实体的说明。 -
每个声明式字符串槽位都要进行 TypeScript 表达式编码。 每个
tooltip、placeholder、value、format以及(绑定到变量时的)按钮label,都会原样内联到生成的 TS 中。将显示文本包裹在反引号中("`Manage rules…`"),将普通字符串字面量包裹在 TS 引号中("'MM/DD/YYYY'"),原始表达式则不加包裹("$editor.vars.foo")。未加包裹的提示文字(如"Manage rules.")会编译为裸 TS 标记并导致构建失败。请参阅 ../datex-studio-conventions/file-format.md → 声明式字符串值是 TypeScript 表达式。 -
vars/inParams/outParams中不得使用自定义枚举 FQN。 编辑器无法解析参数声明中的$types.<Package>.e_<enum>— 请将这些字段声明为基本类型(字符串值枚举使用string,数值枚举使用number),并在流程代码中的用法处进行类型转换。 -
onCustomization*槽位保持为null,除非明确需要定制钩子 — 这些是用于定制覆盖层(tailoring-overlay)的平台扩展点,并非日常编辑器接线用途。 -
审核宿主契约。 以对话框形式打开此编辑器的中心标签页、网格行操作或表单,必须声明完整的
configParameters契约 — 编辑器声明的每个inParam都必须在宿主中有对应条目,包括未使用且value: null的参数。推送前请运行component-wiring-check,审核引用契约。请参阅 ../component-wiring-check/references/component-wiring.md。
阶段 4:验证并推送
# Validate the body locally against the branch. Exit 1 = validation found errors
# (read validation_errors, fix body.json, re-run) — not a broken CLI. Do not push on exit 1.
dxs configuration validate editor -b <branchId> -D body.json
# For a new editor
dxs configuration upsert editor -b <branchId> -D body.json
# For modify-existing (round-trip — never skip the jq extract)
dxs configuration get editor <configId> -b <branchId> -O envelope.json
jq .json envelope.json > body.json
# ... edit body.json ...
dxs configuration upsert editor -b <branchId> -D body.json
验证会在推送前发现缺失的必填字段、格式错误的参数描述符结构、EditorFields 一致性违规以及引用错误。它不会发现 onInit 与 onDataLoaded 之间的切换、未声明的 $editor.vars.<id> 写入,或未加包裹的 TypeScript 表达式槽位 — 这些属于行为问题,只会在运行时暴露。推送前请逐项检查 references/editors.md → 预检清单。
阶段 5:在 Studio 中验证(可选)
通过常规调用路径以对话框形式打开编辑器(例如 hub 标签页工具栏按钮、网格行操作,或会链接到编辑器的表单):
- 数据填充成功 — 运行
onDataLoaded时,$editor.entity已填充;字段显示已加载的值。 - 编辑模式切换正常 — 编辑按钮将
edit_mode = true;保存和取消按钮出现;字段变为可编辑。 - 保存成功提交 — 根据
$editor.entity.isNew正确分支;外层 CRUD 操作成功;对话框关闭或切回只读状态。 - 取消后恢复原始值 — 编辑前快照仍可用(或数据源可重新获取数据且正常)。
- 对于创建模式(
isNew: true),嵌入式数据源会生成空白实体;保存路径进入创建分支。 - 只有
onFormValidateFlowConfig报告is_valid后,保存按钮才会启用。
如果无法运行应用,请重新获取配置(使用修正后的 jq .json 提取模式),并与 body.json 对比,确认推送已生效。
推送前检查清单
推送前,请完整检查 references/editors.md → 推送前检查清单。快速版:
- 文件基本信息。
configurationTypeId: 4,后缀为-editor.json,referenceName以_editor结尾 — 另需通过通用检查(../datex-studio-conventions/universal-checklist.md)。 - 恰好一个嵌入式数据源 —
accessModifier: "private",type: "flows",单结果结构(getFlow已填充;getListFlow+getByKeysFlow为 null;resultIsCollection: false;outParams[0].isCollection: false)。 - 实体结构保持一致 —
datasourceConfig.configOutParameters.result.objectTypeDef的字段与datasources[0].outParams[0].objectTypeDef逐项匹配。datasourceKeyDef与嵌入式数据源的keyDef及宿主的inParams结构匹配。 - 初始化钩子拆分正确 —
onInitFlowConfig不读取$editor.entity;依赖实体的默认值和编辑前快照应放在onDataLoadedFlowConfig中。 - 保存根据
$editor.entity.isNew分支 — 新建时使用crud_create_entity,已有实体时使用crud_update_entity。两个分支都不能硬编码。 onFormValidateFlowConfig根据字段有效性控制$editor.toolbar.<save>.control.readOnly。- 已声明
$editor.vars— 流程代码中写入的每个变量(edit_mode、快照、进行中的数组)都在顶层vars[]中。 - EditorFields 保持一致 — 实体接口中的每个字段都有绑定(如果 UX 尚未就绪,则使用存根
removed: true)。对象数组子树使用按钮 +$editor.vars+set_config合并模式。 - 符合调用层级要求 — 编辑器代码通过
$flows.<Package>.<fn>调用函数;函数通过$apis.<Package>.FootprintApi.extendedActions.<action_name>({...})封装 CRUD 操作;编辑器不得直接调用操作。 - TypeScript 表达式字符串包装正确 — 显示文本使用反引号;原始表达式不加包装;普通字面量加引号。
- 除非有意进行自定义,否则
onCustomization*插槽为null。 vars/inParams/outParams中不使用$types.<Package>.e_<enum>— 参数层仅使用基本类型;在使用处进行类型转换。- 宿主包含完整的
configParameters契约 — 编辑器声明的每个 inParam 都有对应条目;未使用的条目设为value: null。使用component-wiring-check进行审查。
常见错误
| 错误 | 修正方法 |
|---|---|
在 onInitFlowConfig 中读取 $editor.entity | 此时实体尚未完成加载,结果为 undefined。将基于实体的默认值和编辑前快照移至 onDataLoadedFlowConfig。 |
嵌入式数据源返回集合(resultIsCollection: true 或 outParams[0].isCollection: true) | 编辑器无法从列表中加载 $editor.entity。改用单结果形态:填充 getFlow,并将 getListFlow/getByKeysFlow 设为 null。 |
datasources[0].outParams[0].objectTypeDef 和 datasourceConfig.configOutParameters.result.objectTypeDef 不一致 | 编辑器引用会在运行时失效。两者互为镜像,任何实体形态变更都要同步修改两处。 |
保存路径硬编码为 crud_update_entity(或 crud_create_entity) | 另一种模式会失效。根据 $editor.entity.isNew 分支处理。 |
在流程代码中写入 $editor.vars.edit_mode = true,却未在顶层 vars[] 中声明 edit_mode | 变量未声明,写入会静默失败或触发运行时错误。声明每个变量。 |
| 新架构字段缺少字段绑定 | EditorFields 类型错误:Property 'foo__bar' is missing... but required in type 'EditorFields'。在同一次编辑中添加绑定;如果用户体验尚未就绪,可使用 removed: true 占位。 |
实体是序列化配置包装对象时,直接修改 $editor.entity.<field> | 类型错误:entity 提供的是包装对象,而非解析后的配置。在 set_state 中通过 JSON.parse($editor.entity.config) 解析;在 set_config 中重新组装;通过 CRUD 操作提交 $editor.vars.new_config。 |
| 将对象数组子树编写为扁平字段绑定 | id__path 扁平化不适用于数组。使用按钮 + $editor.vars.<array_var> + set_config 合并模式(或使用 codeBox 实现面向高级用户的管理工具)。 |
| 编辑器代码直接调用操作 | UI 层规则:编辑器只能调用函数。函数通过 $apis.<Package>.FootprintApi.extendedActions.<action_name>({...}) 包装操作。 |
声明式字符串槽位未加包装("Manage rules.",而不是 "`Manage rules.`") | 内容会原样内联到生成的 TS 中,未包装的标记会导致构建失败。显示文本使用反引号;普通字面量使用引号;原始表达式不加包装。 |
在 vars / inParams / outParams 中使用 $types.<Package>.e_<enum> | 自定义枚举的 FQN 无法在参数层解析。将其声明为基本类型(string / number);在流程代码中使用时再进行类型转换。 |
将 datasourceConfig.configOutParameters 编写为以参数名为键的对象({"result": {...}}) | 它是一个参数描述符数组,与嵌入式数据源的 outParams 对应:[{"id":"result","type":"object","objectTypeDef":[...],"isCollection":false}]。对象形式会导致验证失败,并输出原始错误 Cannot deserialize ... into type 'IList<VarConfig>',且不会指出字段名。 |
嵌入式数据源的 datasourceConfig 缺少 isOwned: true | isOwned 是唯一用于将引用路由到嵌入式 datasources[],而非跨应用查找的设置。若未设置,平台会根据 configId + moduleId 在其他应用中解析,解析失败后报告 Invalid contract. Referenced configuration <name> does not exist or has been renamed。设置 isOwned: true。移除 moduleId 无法解决此问题——对于自有引用,系统不会读取 moduleId。参见 ../datasource-creator/references/datasources.md → Resolving an Owned Reference。 |
嵌入式数据源缺少 hasKey / hasResult / queryOptionsObjectTypeDef | 三者都必需。queryOptionsObjectTypeDef 重复定义实体形态(与 outParams[0].objectTypeDef 为同一数组),是第五处实体形态类型定义,最容易遗漏。省略该项会导致错误消息含糊且无法诊断:Entity definition is required。 |
将 dxs configuration get -O envelope.json 的输出直接传给 dxs configuration upsert -D envelope.json | 配置内容会被静默破坏。编辑前务必先运行 jq .json envelope.json > body.json。参见上文“往返规则”。 |
description 超过 100 个字符 | SQL 列长度有限,推送时验证会失败。请缩短。 |
referenceName 不以 _editor 结尾,或与文件名主体不匹配 | 导入 / 查找会失败。使用 snake_case,以 _editor 结尾,并确保与文件名主体一致。 |
宿主的引用集合中,moduleId 被设为宿主的包,而不是编辑器的包 | 跨组件引用规则:moduleId 始终指向目标的包。参见 ../component-wiring-check/references/component-wiring.md。 |
编辑完成后,请调用 post-edit-verification,以发现描述、JSON 或架构违规。进行最终审核时,请调用 component-validator。 |