openyida 2026.7.12 → 2026.7.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,7 +28,7 @@ function loadRoutingContext(skillMdPath = DEFAULT_SKILL_MD) {
28
28
 
29
29
  /**
30
30
  * 加载 scenarios 目录下的 *.json golden 用例。
31
- * @returns {Array<{id, prompt, expectedSkill, expectedStages?, rubric?}>}
31
+ * @returns {Array<{id, prompt, expectedSkill, expectedMode?, forbiddenDefaultSkills?, expectedStages?, rubric?}>}
32
32
  */
33
33
  function loadScenarios(dir) {
34
34
  if (!dir || !fs.existsSync(dir)) {return [];}
@@ -58,7 +58,7 @@ function normalizeSkill(name) {
58
58
  function buildRoutingPrompt({ request, routingContext, skillNames }) {
59
59
  return [
60
60
  '下面是宜搭 AI 应用开发技能(openyida)的路由说明文档。请严格依据它,',
61
- '判断针对用户请求应当选择哪一个**子技能**来处理。',
61
+ '判断针对用户请求应当选择哪一个**子技能**来处理。如果是完整应用搭建,还要判断 yida-app 的模式。',
62
62
  '',
63
63
  '=== 路由说明文档开始 ===',
64
64
  routingContext,
@@ -69,10 +69,22 @@ function buildRoutingPrompt({ request, routingContext, skillNames }) {
69
69
  `用户请求:「${request}」`,
70
70
  '',
71
71
  '只输出一个 JSON 对象,不要任何其它文字,形如:',
72
- '{"skill": "yida-dashboard", "reason": "一句话理由"}',
72
+ '{"skill": "yida-app", "mode": "fast_build", "defaultLoadSkills": ["yida-app"], "reason": "一句话理由"}',
73
+ '',
74
+ '要求:',
75
+ '- skill 必须是上面可选子技能名之一。',
76
+ '- 只有完整应用搭建才填写 mode;默认方案 / 不要追问 / 直接创建 必须是 fast_build。',
77
+ '- defaultLoadSkills 只列默认会加载的技能;optionalAfterDone 不要列入默认加载。',
73
78
  ].join('\n');
74
79
  }
75
80
 
81
+ function normalizeSkillList(value) {
82
+ if (!Array.isArray(value)) {
83
+ return [];
84
+ }
85
+ return value.map(normalizeSkill).filter(Boolean);
86
+ }
87
+
76
88
  /**
77
89
  * 评测单条 scenario。
78
90
  */
@@ -111,13 +123,28 @@ function evaluateScenario(options = {}) {
111
123
  }
112
124
 
113
125
  const actual = res.json && res.json.skill ? res.json.skill : null;
114
- const hit = actual !== null
126
+ const skillHit = actual !== null
115
127
  && normalizeSkill(actual) === normalizeSkill(scenario.expectedSkill);
128
+ const actualMode = res.json && res.json.mode ? String(res.json.mode).trim() : null;
129
+ const modeHit = scenario.expectedMode
130
+ ? normalizeSkill(actualMode) === normalizeSkill(scenario.expectedMode)
131
+ : null;
132
+ const defaultLoadSkills = normalizeSkillList(res.json && res.json.defaultLoadSkills);
133
+ const forbiddenDefaultSkillHits = normalizeSkillList(scenario.forbiddenDefaultSkills)
134
+ .filter(skillName => defaultLoadSkills.includes(skillName));
135
+ const hit = skillHit
136
+ && (modeHit !== false)
137
+ && forbiddenDefaultSkillHits.length === 0;
116
138
 
117
139
  return {
118
140
  id: scenario.id,
119
141
  expected: scenario.expectedSkill,
120
142
  actual,
143
+ expectedMode: scenario.expectedMode || null,
144
+ actualMode,
145
+ modeHit,
146
+ defaultLoadSkills,
147
+ forbiddenDefaultSkillHits,
121
148
  reason: res.json ? res.json.reason : null,
122
149
  hit,
123
150
  status: actual === null ? 'unparsed' : 'ok',
@@ -65,6 +65,21 @@
65
65
  "expectedSkill": "yida-app",
66
66
  "note": "从零多步骤全流程 → yida-app 编排"
67
67
  },
68
+ {
69
+ "id": "full-app-visitor-fast-build",
70
+ "prompt": "帮我搭建一个访客系统,按默认方案搭建,不要追问,直接创建",
71
+ "expectedSkill": "yida-app",
72
+ "expectedMode": "fast_build",
73
+ "forbiddenDefaultSkills": [
74
+ "yida-page-uiux",
75
+ "yida-canvas-custom-page",
76
+ "yida-data-source-connectors",
77
+ "yida-data-management",
78
+ "yida-nav-group",
79
+ "yida-dashboard"
80
+ ],
81
+ "note": "默认方案/不要追问/直接创建 → yida-app fast_build;Canvas、UIUX、数据源、示例数据、导航整理均为 optionalAfterDone"
82
+ },
68
83
  {
69
84
  "id": "table-form",
70
85
  "prompt": "我要一个像 Excel 那样可以多行批量录入再一次提交的页面",
@@ -251,13 +251,23 @@ openyida login --browser
251
251
  openyida copy
252
252
  \`\`\`
253
253
 
254
+ ## 完整应用默认链路
255
+
256
+ 用户说“按默认方案 / 不要追问 / 直接创建 / 尽快搭建”时,加载 \`yida-app\` 并选择 \`fast_build\`。
257
+
258
+ \`fast_build\` 只做:创建应用 → 核心表单 → 主页面 → 编写主页面源码 → 发布 → 返回访问链接。发布主页面成功并输出 URL 后即完成。
259
+
260
+ fast_build 页面源码默认不得使用 \`this.dataSourceMap.*\`,除非本轮已经明确创建并绑定设计器数据源;默认使用入口型页面或 \`this.utils.yida.*\` 查询已创建表单。
261
+
262
+ 不要默认加载 \`yida-page-uiux\`、\`yida-data-source-connectors\`、\`yida-data-management\`、\`yida-nav-group\`、\`yida-dashboard\`,也不要默认做示例数据、导航整理、截图验收、公开访问、长 PRD 或深读 references;这些只在用户明确要求或 \`full_demo\` / \`deep_design\` 时执行。
263
+
254
264
  ## 子技能索引
255
265
 
256
- 根据用户意图选择最匹配的子技能。支持 \`use_skill\` / \`search_skills\` 的宿主中,必须调用 \`use_skill("<技能名>", "<本次目的>")\` 加载子技能;不要用 Read / read_file / cat 读取 SKILL.md 路径。\`skills-index.json\` 仅供 yida-agent 或同构宿主机器发现,不支持该索引的宿主忽略它。完全没有 \`use_skill\` 的本地工具,才允许按根技能路由表选定技能,并按 \`skills/<技能名>/SKILL.md\` 定位当前阶段唯一必要的 SKILL.md,禁止并发批量读取多个 SKILL.md。
266
+ 根据用户意图选择最匹配的子技能。支持 \`use_skill\` / \`search_skills\` 的宿主中,必须调用 \`use_skill("<技能名>", "<本次目的>")\` 加载子技能;不要用 Read / read_file / cat 读取 SKILL.md 路径,也不要猜测 .skills、插件缓存或 workspace/project/.skills。\`skills-index.json\` 仅供 yida-agent 或同构宿主机器发现,不支持该索引的宿主忽略它。完全没有 \`use_skill\` 的本地工具,才允许按根技能路由表选定技能,并按 \`skills/<技能名>/SKILL.md\` 定位当前阶段唯一必要的 SKILL.md,禁止并发批量读取多个 SKILL.md。
257
267
 
258
268
  | 意图 | 子技能 |
259
269
  | --- | --- |
260
- | 完整应用开发编排 | \`yida-app\` |
270
+ | 完整应用开发编排(默认 \`fast_build\`:创建应用、核心表单、主页面、发布并返回 URL) | \`yida-app\` |
261
271
  | 登录态管理 | \`yida-login\` |
262
272
  | 退出登录 / 切换账号 | \`yida-logout\` |
263
273
  | 创建应用 | \`yida-create-app\` |
@@ -7,6 +7,7 @@ const path = require('path');
7
7
  const {
8
8
  COMMAND_GROUPS,
9
9
  flattenCommandManifest,
10
+ listCommandPermissionIds,
10
11
  listCommandSideEffectIds,
11
12
  } = require('../lib/core/command-manifest');
12
13
 
@@ -61,6 +62,22 @@ function validateUniqueIds(commands) {
61
62
  }
62
63
  }
63
64
 
65
+ function commandPathKey(entry) {
66
+ return (entry.path || []).join(' ');
67
+ }
68
+
69
+ function validateUniquePaths(commands) {
70
+ const seen = new Map();
71
+ for (const entry of commands) {
72
+ const key = commandPathKey(entry);
73
+ if (seen.has(key)) {
74
+ errors.push(`Duplicate command manifest path "${key}" for ids "${seen.get(key)}" and "${entry.id}"`);
75
+ } else {
76
+ seen.set(key, entry.id);
77
+ }
78
+ }
79
+ }
80
+
64
81
  function validateRouterCoverage(commands) {
65
82
  const routerCases = collectRouterCases();
66
83
  const manifestRoots = collectManifestRoots(commands);
@@ -140,14 +157,160 @@ function validateSideEffects(commands) {
140
157
  }
141
158
  }
142
159
 
160
+ function validatePermissions(commands) {
161
+ const allowedModes = new Set(['allow', 'ask', 'deny']);
162
+ const allowedEffects = new Set(['read', 'write', 'external', 'destructive', 'unknown']);
163
+ const allowedUnknownActionModes = new Set(['ask']);
164
+ const allowedPatternTypes = new Set([
165
+ 'argv_contains_any',
166
+ 'option_value_excludes_any',
167
+ ]);
168
+ const commandIds = new Set(commands.map(entry => entry.id));
169
+
170
+ for (const permissionId of listCommandPermissionIds()) {
171
+ if (!commandIds.has(permissionId)) {
172
+ errors.push(`Permission metadata references unknown command id: ${permissionId}`);
173
+ }
174
+ }
175
+
176
+ for (const entry of commands) {
177
+ const permission = entry.permission;
178
+ if (!permission || typeof permission !== 'object') {
179
+ errors.push(`Command manifest id "${entry.id}" is missing permission metadata`);
180
+ continue;
181
+ }
182
+ if (!allowedModes.has(permission.mode)) {
183
+ errors.push(`Command manifest id "${entry.id}" has invalid permission.mode "${permission.mode}"`);
184
+ }
185
+ if (!allowedEffects.has(permission.effect)) {
186
+ errors.push(`Command manifest id "${entry.id}" has invalid permission.effect "${permission.effect}"`);
187
+ }
188
+ if (permission.mode === 'ask' && permission.effect !== 'destructive') {
189
+ errors.push(`Command manifest id "${entry.id}" permission.mode ask must use permission.effect destructive`);
190
+ }
191
+ if (permission.effect === 'destructive' && permission.mode !== 'ask') {
192
+ errors.push(`Command manifest id "${entry.id}" permission.effect destructive must use permission.mode ask`);
193
+ }
194
+ if (permission.mode === 'allow' && permission.effect === 'unknown' && permission.action_dependent !== true) {
195
+ errors.push(`Command manifest id "${entry.id}" permission.mode allow with unknown effect must be action-dependent`);
196
+ }
197
+ if (entry.sideEffect && entry.sideEffect.kind === 'mixed') {
198
+ if (permission.action_dependent !== true) {
199
+ errors.push(`Command manifest id "${entry.id}" mixed permission.action_dependent must be true`);
200
+ }
201
+ if (!Array.isArray(permission.read_actions)) {
202
+ errors.push(`Command manifest id "${entry.id}" mixed permission.read_actions must be an array`);
203
+ }
204
+ if (!Array.isArray(permission.preauthorized_actions)) {
205
+ errors.push(`Command manifest id "${entry.id}" mixed permission.preauthorized_actions must be an array`);
206
+ }
207
+ if (!Array.isArray(permission.preauthorized_patterns)) {
208
+ errors.push(`Command manifest id "${entry.id}" mixed permission.preauthorized_patterns must be an array`);
209
+ }
210
+ if (!Array.isArray(permission.ask_actions)) {
211
+ errors.push(`Command manifest id "${entry.id}" mixed permission.ask_actions must be an array`);
212
+ }
213
+ if (!Array.isArray(permission.ask_patterns)) {
214
+ errors.push(`Command manifest id "${entry.id}" mixed permission.ask_patterns must be an array`);
215
+ }
216
+ if (!allowedUnknownActionModes.has(permission.unknown_action_mode)) {
217
+ errors.push(`Command manifest id "${entry.id}" mixed permission.unknown_action_mode must be ask`);
218
+ }
219
+ if (Array.isArray(permission.preauthorized_actions) && Array.isArray(permission.ask_actions)) {
220
+ const coveredActions = new Set([
221
+ ...permission.preauthorized_actions,
222
+ ...permission.ask_actions,
223
+ ...collectPatternCovers(permission.preauthorized_patterns),
224
+ ...collectPatternCovers(permission.ask_patterns),
225
+ ]);
226
+ for (const action of entry.sideEffect.mutating_actions || []) {
227
+ if (typeof action === 'string' && action.startsWith('depends on ')) {
228
+ continue;
229
+ }
230
+ if (!coveredActions.has(action)) {
231
+ errors.push(`Command manifest id "${entry.id}" mutating action "${action}" must appear in permission.preauthorized_actions or permission.ask_actions`);
232
+ }
233
+ if (/\\b(delete|remove)\\b/i.test(action) && !permission.ask_actions.includes(action)) {
234
+ errors.push(`Command manifest id "${entry.id}" delete/remove action "${action}" must appear in permission.ask_actions`);
235
+ }
236
+ }
237
+ }
238
+ }
239
+ if (Array.isArray(permission.preauthorized_actions)) {
240
+ for (const action of permission.preauthorized_actions) {
241
+ if (typeof action !== 'string' || !action.trim()) {
242
+ errors.push(`Command manifest id "${entry.id}" permission.preauthorized_actions entries must be non-empty strings`);
243
+ }
244
+ }
245
+ }
246
+ validatePatternList(entry.id, 'preauthorized_patterns', permission.preauthorized_patterns, allowedPatternTypes);
247
+ if (Array.isArray(permission.ask_actions)) {
248
+ for (const action of permission.ask_actions) {
249
+ if (typeof action !== 'string' || !action.trim()) {
250
+ errors.push(`Command manifest id "${entry.id}" permission.ask_actions entries must be non-empty strings`);
251
+ }
252
+ }
253
+ }
254
+ validatePatternList(entry.id, 'ask_patterns', permission.ask_patterns, allowedPatternTypes);
255
+ }
256
+ }
257
+
258
+ function collectPatternCovers(patterns) {
259
+ if (!Array.isArray(patterns)) {
260
+ return [];
261
+ }
262
+ return patterns.flatMap(pattern => {
263
+ if (!pattern || typeof pattern !== 'object') {
264
+ return [];
265
+ }
266
+ if (Array.isArray(pattern.covers)) {
267
+ return pattern.covers;
268
+ }
269
+ return pattern.covers ? [pattern.covers] : [];
270
+ });
271
+ }
272
+
273
+ function validatePatternList(commandId, fieldName, patterns, allowedPatternTypes) {
274
+ if (!Array.isArray(patterns)) {
275
+ return;
276
+ }
277
+ for (const pattern of patterns) {
278
+ if (!pattern || typeof pattern !== 'object' || Array.isArray(pattern)) {
279
+ errors.push(`Command manifest id "${commandId}" permission.${fieldName} entries must be objects`);
280
+ continue;
281
+ }
282
+ if (!allowedPatternTypes.has(pattern.type)) {
283
+ errors.push(`Command manifest id "${commandId}" permission.${fieldName} has invalid pattern type "${pattern.type}"`);
284
+ }
285
+ if (typeof pattern.description !== 'string' || !pattern.description.trim()) {
286
+ errors.push(`Command manifest id "${commandId}" permission.${fieldName} entries must include description`);
287
+ }
288
+ if (pattern.type === 'argv_contains_any') {
289
+ if (!Array.isArray(pattern.values) || pattern.values.length === 0) {
290
+ errors.push(`Command manifest id "${commandId}" permission.${fieldName} argv_contains_any.values must be a non-empty array`);
291
+ }
292
+ }
293
+ if (pattern.type === 'option_value_excludes_any') {
294
+ if (typeof pattern.option !== 'string' || !pattern.option.trim()) {
295
+ errors.push(`Command manifest id "${commandId}" permission.${fieldName} option_value_excludes_any.option must be a non-empty string`);
296
+ }
297
+ if (!Array.isArray(pattern.values) || pattern.values.length === 0) {
298
+ errors.push(`Command manifest id "${commandId}" permission.${fieldName} option_value_excludes_any.values must be a non-empty array`);
299
+ }
300
+ }
301
+ }
302
+ }
303
+
143
304
  function run() {
144
305
  const commands = flattenCommandManifest();
145
306
 
146
307
  validateUniqueIds(commands);
308
+ validateUniquePaths(commands);
147
309
  validateGroupReferences();
148
310
  validateRouterCoverage(commands);
149
311
  validateReadmeCoverage(commands);
150
312
  validateSideEffects(commands);
313
+ validatePermissions(commands);
151
314
 
152
315
  if (errors.length > 0) {
153
316
  console.error('Command manifest validation failed:');
@@ -16,8 +16,9 @@ description: >
16
16
 
17
17
  - 如果当前宿主提供 `use_skill` / `search_skills`:必须通过 `use_skill("<技能名>", "<本阶段目的>")` 加载主技能和子技能,禁止用 `Read` / `read_file` / `cat` 读取 `SKILL.md` 路径;`use_skill` 会稳定返回技能内容和可读取的辅助文件列表。
18
18
  - `skills-index.json` 仅供 yida-agent 或同构宿主做机器可读发现;不支持该索引的宿主忽略它,不要把它当作运行前置条件。
19
+ - 支持 `use_skill` / `search_skills` 的宿主只能读取该工具返回的辅助文件;禁止猜测 `.skills`、`skills`、`yida-skills`、插件缓存、workspace/project/.skills 等安装路径。
19
20
  - 如果当前宿主没有 `use_skill` / `search_skills`:按本文的技能路由表选定技能名,按 `skills/<技能名>/SKILL.md` 定位当前阶段唯一必要的子技能文档;禁止并发批量读取多个 `SKILL.md`;禁止预读未来阶段技能。
20
- - `references/`、`scripts/`、`assets/` 等辅助文件只能在已加载对应技能后按需读取。
21
+ - `references/`、`scripts/`、`assets/` 等辅助文件只能在已加载对应技能后,读取该技能正文明确列出的相对路径;不要把 yida-agent 的 sandbox 路径当作通用路径。
21
22
 
22
23
  ---
23
24
 
@@ -25,7 +26,7 @@ description: >
25
26
 
26
27
  > ⚡ **前置门槛**:确认 openyida 已安装、Node/npm 依赖达标、登录态就绪。**未通过只读验证前,禁止创建应用/页面/表单或发布等任何真实资源操作。**
27
28
 
28
- **怎么做**:优先跑一次 `openyida agent-capabilities --json`。该命令一次返回 version、cwd、AI 工具环境、推荐工作目录、登录态摘要、commands sideEffects,避免反复 `which openyida`、`openyida --version`、`openyida --help`、`openyida env`、`login --check-only`。
29
+ **怎么做**:优先跑一次 `openyida agent-capabilities --json`。该命令一次返回 version、cwd、AI 工具环境、推荐工作目录、登录态摘要、commands、command count、sideEffects 和默认 `fast_build` 契约,避免反复 `which openyida`、`openyida --version`、`openyida --help`、`openyida env`、`login --check-only`。
29
30
 
30
31
  若当前 OpenYida 版本还没有 `agent-capabilities`,退回跑 `openyida env --json` 和 `openyida login --check-only --json`。旧版本地 agent 不需要认识 `skills-index.json`,也不需要支持 `agent-capabilities` 才能继续执行。
31
32
 
@@ -36,7 +37,7 @@ description: >
36
37
  | `login.status` 不是 `ok` 且 `login.can_auto_use` 不是 true | 未登录 → `openyida login`(指定入口带 URL 或 flag) |
37
38
  | `active.projectRootExists` 为 false | 无工作目录 → `openyida copy` 初始化 |
38
39
 
39
- **👉 完整命令解读、悟空降级、Codex handoff 等特殊分支 → [references/setup-and-env.md](references/setup-and-env.md)(必读)。**
40
+ **👉 环境异常、登录失败、悟空降级、Codex handoff 等特殊分支 → [references/setup-and-env.md](references/setup-and-env.md)。正常 `agent-capabilities` 通过时不要默认读取该 reference。**
40
41
 
41
42
  ---
42
43
 
@@ -57,9 +58,13 @@ description: >
57
58
  > 加载子技能 `yida-app`,由它负责完整应用 workflow、阶段子技能加载、关键 ID 流转、PRD 与 schema cache 约束。
58
59
  > 用户说“按默认方案 / 不要追问 / 直接创建 / 尽快搭建”时,`yida-app` 选择 `fast_build`:创建应用、必要表单、主页面、发布并输出链接。
59
60
 
61
+ **默认链路**:`fast_build` 必须只做 `创建应用 → 核心表单 → 主页面 → 编写主页面源码 → 发布 → 返回访问链接`。不要因为应用名里有“看板 / 系统 / 管理”就升级到 `deep_design` 或 `full_demo`。
62
+
63
+ **fast_build 默认加载边界**:只加载 `yida-app` 和当前阶段必需的子技能:`yida-create-app`、`yida-create-form-page`、`yida-create-page`、`yida-custom-page`、`yida-publish-page`。Code Canvas 尚未全量,只有用户明确要求、已有页面为 `YidaCodeCanvas`,或已确认当前组织/页面支持时才加载 `yida-canvas-custom-page`。不要默认加载 `yida-page-uiux`、`yida-data-source-connectors`、`yida-data-management`、`yida-nav-group`、`yida-dashboard`,也不要默认深读 `references/`。
64
+
60
65
  **doneWhen**:`yida-app` 发布主页面成功并输出可访问 URL。到这里默认完成;不要发布后继续 TaskCreate、重复读技能或继续规划。
61
66
 
62
- **optionalAfterDone**:导航整理、示例数据、公开访问、截图验证、深度视觉方向、报表/大屏,只在用户明确要求或 `yida-app` 模式为 `full_demo` / `deep_design` 时执行。
67
+ **optionalAfterDone**:导航整理、示例数据、公开访问、截图验证、深度视觉方向、数据源/连接器深度接入、报表/大屏,只在用户明确要求或 `yida-app` 模式为 `full_demo` / `deep_design` 时执行。
63
68
 
64
69
  ---
65
70
 
@@ -73,12 +78,12 @@ description: >
73
78
 
74
79
  > ⚠️ **同类易错先分清**:改字段结构→`create-form-page`|只读 Schema→`get-schema`|改数据记录→`data-management`|详情页美化→`form-detail`;自定义页视觉方向/去AI味→`page-uiux`(定方向)|token/组件实现→`custom-page`(design-system);加导航先分清→平台左侧菜单分组/排序→`nav-group`|页面隐藏应用导航后页面内自绘导航壳→`nav-shell`(必须隐藏原导航,并让导航项 URL 带 `isRenderNav=false` 等参数);字段实时校验→`formula`|提交后编排→`integration`|跨表高级函数→`business-rule`;从零建流程→`create-process`|改已有流程→`process-rule`;权限按层级:组织→`corp-manager`/应用→`app-permission`/表单→`form-permission`/页面分享→`page-config`;**自定义页面选路见下方专表**。
75
80
 
76
- > 🧭 **自定义页面选路(默认 Code Canvas,按顺序命中即停)**:
77
- > 1. **默认 → Code Canvas** `yida-canvas-custom-page`:现代 React 交互 / hooks 状态 / 可视化 / AI 生成 / 需崩溃隔离,**以及只需通过开放 API(HTTP)读取宜搭数据的页面**(Canvas 自写 fetch 即可拿数据);
78
- > 2. 仅当页面强依赖**原生实例数据桥**——表单内字段双向绑定 `this.$(fieldId)`、`this.utils.yida.*`、`dataSourceMap`、提交流程 / 设计器数据源深度耦合,且用开放 API 重写代价过高 回退 **native** `yida-custom-page`;
79
- > 3. 已有普通 `.oyd.jsx` 要迁到 Canvas → `yida-canvas-upgrade`。
81
+ > 🧭 **自定义页面选路(兼容优先,按顺序命中即停)**:
82
+ > 1. **默认 → native** `yida-custom-page`:平台全量兼容的 `.oyd.jsx` 链路,适合完整应用 `fast_build` 和未确认 Canvas 能力的组织;
83
+ > 2. 仅当用户明确要求 Code Canvas / 代码画布,已有页面 Schema `YidaCodeCanvas`,或已确认当前组织/页面支持 Canvas `yida-canvas-custom-page`;
84
+ > 3. 已有普通 `.oyd.jsx` 要迁到 Canvas,且目标组织支持 Canvas → `yida-canvas-upgrade`。
80
85
  >
81
- > 依据(源码核实):Canvas 代码在宿主页真实 `window` 中 `new Function` 执行,但物料只透传 `code/runtimeCode/importedModules/pageType`,**无 `this` 上下文、无 `dataSourceMap`**,`this.utils.yida.*` 不可用;宿主 window 全局(`__yida_plugin_runtime__` 插件系统、`__VcDeepYidaUtils__`)均非表单数据桥,也无 `window.Deep` 字段组件。Canvas 读宜搭数据 = 在 `YidaComp` 内自写 HTTP 调开放 API。只有需要免费 `this` 实例桥的页才留 native。
86
+ > 依据:Code Canvas 组件在宜搭平台侧尚未全量。native `.oyd.jsx` 是默认兼容链路;Canvas 代码在宿主页真实 `window` 中 `new Function` 执行,但物料只透传 `code/runtimeCode/importedModules/pageType`,无 `this` 上下文、无 `dataSourceMap`,`this.utils.yida.*` 不可用。需要实例桥或未确认 Canvas 支持时留 native。
82
87
 
83
88
  | 分组 | 加载目标 | 何时选择(关键区别已内联) |
84
89
  |------|------|--------------------------|
@@ -89,10 +94,10 @@ description: >
89
94
  | **页面与表单** | 加载子技能 `yida-create-page` | 创建空白自定义页面拿 formUuid,后续写 JSX |
90
95
  | | 加载子技能 `yida-create-form-page` | 创建/更新表单、增删改**字段结构**(普通表单,无审批) |
91
96
  | | 加载子技能 `yida-create-process` | 从零建**带审批**流程表单(表单还不存在,一步到位) |
92
- | | 加载子技能 `yida-page-uiux` | 写自定义页面 UI 前先定视觉方向、去 AI 味(工作台/看板/列表/详情/官网落地页;产出决策块,不写代码;canvas / native 两链路通用) |
93
- | | 加载子技能 `yida-canvas-custom-page` | **自定义页面默认链路**:现代 React 交互 / hooks / 可视化 / AI 页,含只需开放 API HTTP 读数据的页(Code Canvas,`runtimeCode` + `importedModules`,真 React18 + 崩溃隔离) |
94
- | | 加载子技能 `yida-custom-page` | 回退项:仅当强依赖原生实例数据桥(`this.$(fieldId)` 双向绑定 / `this.utils.yida.*` / `dataSourceMap` / 提交流程深度耦合)时才用(native `.oyd.jsx` 链路) |
95
- | | 加载子技能 `yida-canvas-upgrade` | 将已有普通 `.oyd.jsx` / `Jsx` 页面升级迁移到 Code Canvas / `YidaCodeCanvas` 链路 |
97
+ | | 加载子技能 `yida-page-uiux` | 单点页面美化、用户明确要求视觉方向/去 AI 味,或 `yida-app` 进入 `deep_design` 时使用;`fast_build` 不默认加载 |
98
+ | | 加载子技能 `yida-custom-page` | **自定义页面默认兼容链路**:native `.oyd.jsx`,适合完整应用 `fast_build` 和未确认 Canvas 能力的组织 |
99
+ | | 加载子技能 `yida-canvas-custom-page` | Code Canvas 可选链路:用户明确要求代码画布、已有页面为 `YidaCodeCanvas`,或已确认当前组织/页面支持 Canvas 时使用 |
100
+ | | 加载子技能 `yida-canvas-upgrade` | 将已有普通 `.oyd.jsx` / `Jsx` 页面升级迁移到 Code Canvas / `YidaCodeCanvas` 链路;仅在目标组织支持 Canvas 时执行 |
96
101
  | | 加载子技能 `yida-nav-shell` | 自定义页**隐藏应用导航**(`isRenderNav=false`,沉浸/门户/大屏/分享)后,页面内用 JSX 自绘侧边/顶部/浮动/标签导航壳;发布后要配置隐藏原导航,跨页导航项要拼完整 URL 并合并 `isRenderNav=false` / `corpid` / 业务参数(**区别** `yida-nav-group` 平台左侧菜单分组:那是真实导航树,本项是页面内自建导航) |
97
102
  | | 加载子技能 `yida-publish-page` | JSX 写完后编译并发布 |
98
103
  | | 加载子技能 `yida-openyida-publish-guard` | 发布已有自定义页面前检查线上设计器状态,避免本地旧源码覆盖用户在线改动 |
@@ -104,7 +109,7 @@ description: >
104
109
  | | 加载子技能 `yida-dashboard` | 完整看板 / 驾驶舱产品化交付 |
105
110
  | **连接器** | 加载子技能 `yida-connector` | 创建/管理连接器、配鉴权 |
106
111
  | | 加载子技能 `yida-connector-safe-actions` | 连接器已有,从 API 代码生成执行动作 |
107
- | | 加载子技能 `yida-data-source-connectors` | 自定义页面中通过数据源调用连接器 |
112
+ | | 加载子技能 `yida-data-source-connectors` | 用户明确要求通过设计器数据源/连接器调用外部 API 时使用;完整应用 `fast_build` 不默认加载 |
108
113
  | **数据与公式** | 加载子技能 `yida-data-management` | 增删改查**数据记录**,不动字段结构 |
109
114
  | | 加载子技能 `yida-get-schema` | **只读**查 Schema / 字段 ID,不改结构 |
110
115
  | | 加载子技能 `yida-formula` | 配在**字段属性**上的实时计算/默认值/校验 |
@@ -157,7 +162,7 @@ description: >
157
162
  9. **报表美化先问方案**:用户说"优化/美化报表"时先问选原生报表(`yida-report`)还是 ECharts(`yida-chart`)。
158
163
  10. **按 schema 证据选技能**:先看 `formType`、组件树、`dataSource.online`;`receipt/process/report` 分别落到表单/流程/报表技能。
159
164
  11. **官方示例范式优先**:蒸馏官方示例时先理解脱敏 schema 承载方式,不凭截图/标题/视觉判断。
160
- 12. **默认完成即停止**:完整应用默认以发布成功并输出 URL 为 doneWhen;示例数据、导航、截图、TaskCreate 和深度设计都是 optionalAfterDone。
165
+ 12. **默认完成即停止**:完整应用默认以发布成功并输出 URL 为 doneWhen;UIUX、数据源深读、示例数据、导航、截图、TaskCreate 和深度设计都是 optionalAfterDone。
161
166
 
162
167
  > 📖 每条规则的完整说明、PRD 质量门槛、临时文件路径规范、报表美化话术 → [references/development-rules.md](references/development-rules.md)
163
168
 
@@ -13,6 +13,8 @@ description: 宜搭完整应用开发编排技能。从零搭建应用时使用
13
13
 
14
14
  用户说“按默认方案”“不要追问”“直接创建”“尽快搭建”等,必须选择 `fast_build`,用合理 MVP 假设直接执行,不展开深度 PRD 讨论。
15
15
 
16
+ **默认判定**:从零搭建完整应用时,只要用户表达“默认方案 / 不要追问 / 直接创建 / 尽快搭建”等快速交付信号,就必须命中 `fast_build`。默认完成链路固定为 `创建应用 → 核心表单 → 主页面 → 编写主页面源码 → 发布 → 返回访问链接`。
17
+
16
18
  ## 模式
17
19
 
18
20
  | 模式 | 何时使用 | 目标 |
@@ -21,7 +23,7 @@ description: 宜搭完整应用开发编排技能。从零搭建应用时使用
21
23
  | `full_demo` | 用户明确要演示完整、示例数据、导航整理、可点验收 | 在 `fast_build` 后补导航、示例数据、公开访问或截图 |
22
24
  | `deep_design` | 用户明确要深度产品设计、视觉方向、多角色、多页面、复杂流程 | 先做详细 PRD/视觉决策,再执行多阶段搭建 |
23
25
 
24
- 默认不要把 `full_demo` / `deep_design` 的动作塞进 `fast_build`。
26
+ 默认不要把 `full_demo` / `deep_design` 的动作塞进 `fast_build`。不要因为用户说“看板”“系统”“管理”就自动加载视觉决策、数据源、示例数据、导航整理或截图验收。
25
27
 
26
28
  ## 预检
27
29
 
@@ -32,14 +34,21 @@ description: 宜搭完整应用开发编排技能。从零搭建应用时使用
32
34
  | 阶段 | 子技能 | 必做动作 | doneWhen |
33
35
  |------|--------|----------|----------|
34
36
  | 1. 创建应用 | `yida-create-app` | `openyida create-app`,提取 `appType` | 拿到真实 `appType` |
35
- | 2. 记录最小需求 | 无 | 写 `prd/<项目名>.md`:MVP、角色、核心表单/页面、完成标准;写 `.cache/<项目名>-schema.json` 初始骨架 | 业务语义和 ID 存储位置明确 |
37
+ | 2. 记录最小需求 | 无 | 写 `prd/<项目名>.md`:只记录 MVP 假设、核心表单/页面、完成标准;写 `.cache/<项目名>-schema.json` 初始骨架;不要写长 PRD | 业务语义和 ID 存储位置明确 |
36
38
  | 3. 创建必要表单 | `yida-create-form-page` | 只创建支撑 MVP 的核心表单;字段配置文件写入 `.cache/openyida/<项目名>/` | 拿到表单 `formUuid` 和真实 `fieldId` |
37
39
  | 4. 创建主页面 | `yida-create-page` | 创建一个用户主入口页面,通常是工作台/看板/列表入口 | 拿到页面 `formUuid` |
38
- | 5. 编写页面 | 默认 `yida-canvas-custom-page`;强依赖原生实例桥才用 `yida-custom-page` | 生成主页面源码;只实现 MVP 首屏和核心操作,不加载视觉/密度/报表等额外技能 | 本地源码通过对应页面技能的基础校验 |
39
- | 6. 发布页面 | `yida-publish-page` | 按页面链路校验后发布:Canvas `.canvas.jsx` `openyida publish` Canvas 编译阶段或 `compileCanvasLocal` 快检;native `.oyd.jsx` / `.jsx` 才跑 `check-page` / `compile`;再发布主页面 | 发布成功并获得可访问 URL |
40
+ | 5. 编写页面 | 默认 `yida-custom-page`;用户明确要求或已确认支持 Canvas 时才用 `yida-canvas-custom-page` | 生成主页面源码;只实现 MVP 首屏和核心操作。可用已创建表单链接、轻量统计占位和基础列表/入口布局完成主页面;不要加载视觉/密度/报表/数据源等额外技能 | 本地源码通过对应页面技能的基础校验 |
41
+ | 6. 发布页面 | `yida-publish-page` | 按页面链路校验后发布:默认 native `.oyd.jsx` / `.jsx` `check-page` / `compile` 后发布;Canvas `.canvas.jsx` 仅在已选择 Canvas 链路时用 `openyida publish` Canvas 编译阶段或 `compileCanvasLocal` 快检;再发布主页面 | 发布成功并获得可访问 URL |
40
42
  | 7. 输出结果 | 无 | 返回应用链接、主页面链接、已创建资源摘要、后续可选项 | 用户拿到 URL |
41
43
 
42
- `fast_build` 不默认执行:导航重排、示例数据、截图验收、公开访问配置、深度 UI 设计、TaskCreate / 继续规划任务。
44
+ `fast_build` 不默认执行:`yida-page-uiux`、`yida-canvas-custom-page`(除非已确认 Canvas 支持或用户明确要求)、`yida-data-source-connectors`、`yida-data-management`、`yida-nav-group`、`yida-dashboard`、导航重排、示例数据、截图验收、公开访问配置、深度 UI 设计、长 PRD、TaskCreate / 继续规划任务,也不默认读取 `references/app-build-contract.md`。
45
+
46
+ ### fast_build 页面数据契约
47
+
48
+ - 默认页面源码不得使用 `this.dataSourceMap.*`,除非本轮已经明确创建并绑定对应设计器数据源。
49
+ - 默认页面只走两类可闭环方案:入口型页面(表单入口、资源链接、轻量统计占位)或内置数据 API 页面(`this.utils.yida.searchFormDatas` / `saveFormData` 等查询本轮已创建表单)。
50
+ - 如果页面源码确实需要 `this.dataSourceMap.*`,必须先把模式升级为可选数据源链路:加载 `yida-data-source-connectors`,创建/绑定数据源,并在发布后确认页面 Schema 中存在对应数据源;否则 `fast_build` 未完成。
51
+ - 发布输出出现 `No custom page data sources to preserve` 时,只有源码不依赖 `this.dataSourceMap.*` 才能视为正常;若源码依赖 dataSourceMap,必须改源码或补数据源后重新发布。
43
52
 
44
53
  ## full_demo 可选后置
45
54
 
@@ -55,7 +64,7 @@ description: 宜搭完整应用开发编排技能。从零搭建应用时使用
55
64
 
56
65
  ## deep_design 附加要求
57
66
 
58
- 进入 `deep_design` 时,可以读取 [详细编排参考](references/app-build-contract.md),并按需加载 `yida-page-uiux`、`yida-density`、`yida-dashboard` 等设计/看板技能。不要在 `fast_build` 中默认读取这些参考或技能。
67
+ 进入 `deep_design` 时,可以读取 [详细编排参考](references/app-build-contract.md),并按需加载 `yida-page-uiux`、`yida-density`、`yida-dashboard`、`yida-data-source-connectors` 等设计/看板/数据源技能。不要在 `fast_build` 中默认读取这些参考或技能。
59
68
 
60
69
  ## 完成条件
61
70
 
@@ -84,4 +93,4 @@ description: 宜搭完整应用开发编排技能。从零搭建应用时使用
84
93
 
85
94
  ## 参考
86
95
 
87
- - [详细编排参考](references/app-build-contract.md)PRD 模板、字段文件示例、URL 规则、典型场景、删除应用确认、故障处理。
96
+ - [详细编排参考](references/app-build-contract.md):仅 `full_demo` / `deep_design` / 排障时读取;包含 PRD 模板、字段文件示例、URL 规则、典型场景、删除应用确认、故障处理。
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: yida-canvas-custom-page
3
- description: 宜搭 Code Canvas / 代码画布自定义页面开发规范,是**自定义页面的默认链路**——现代 React 交互、hooks 状态、可视化、AI 生成或需要崩溃隔离的页面都用它(真 React18 + `runtimeCode` + `importedModules`)。只需通过开放 API(HTTP fetch)读写宜搭数据的页面也用它——Canvas this 数据桥,在 YidaComp 内自写 HTTP 桥即可。也覆盖用户明确提到 code canvas、代码画布、YidaCodeCanvas、runtimeCode、importedModules,或在 Code Canvas 中使用 EmployeeField、SelectField、DepartmentSelectField 等原生组件的场景。仅当页面强依赖原生实例数据桥(this.$(fieldId) 双向绑定、this.utils.yida.*、dataSourceMap、提交流程深度耦合)时才回退 yida-custom-page。
3
+ description: 宜搭 Code Canvas / 代码画布自定义页面开发规范。Code Canvas 尚未在宜搭平台全量,只有用户明确要求代码画布、已有页面为 YidaCodeCanvas,或已确认当前组织/页面支持 Canvas 时使用;默认兼容链路仍是 yida-custom-page。
4
4
  ---
5
5
 
6
6
  # 宜搭 Code Canvas 自定义页面开发
@@ -9,6 +9,8 @@ description: 宜搭 Code Canvas / 代码画布自定义页面开发规范,是*
9
9
 
10
10
  Code Canvas 是宜搭的**代码画布自定义页面链路**:以 `YidaCodeCanvas` 物料为承载,用户写标准 React18 函数组件源码,编译为 `runtimeCode` + 依赖清单(OpenYida CLI 本地用 Babel 完成),运行时按依赖白名单加载资源并 `new Function` 执行,渲染出真正的 React 组件。相较宜搭原生 `.oyd.jsx` 页面,它提供现代 hooks 写法、组件级崩溃隔离(ErrorBoundary)、Tailwind 运行时样式,适合现代交互 / 可视化 / AI 生成页面。
11
11
 
12
+ > **可用性边界**:Code Canvas 组件在宜搭平台侧尚未全量。完整应用 `fast_build` 和未确认 Canvas 能力的组织默认使用 `yida-custom-page` native 链路;本技能只在用户明确要求、已有页面是 `YidaCodeCanvas`,或已确认当前组织/页面支持 Canvas 时使用。
13
+
12
14
  运行链路(源码核实自 `vc-deep-yida` 物料):
13
15
 
14
16
  1. 编译阶段把用户 React 源码转译为 `runtimeCode` + 依赖清单 `importedModules`(OpenYida CLI **本地用 Babel 完成**:JSX/TS → ES5、`import` 改写成 `window.<别名>` 引用、`export default` 收敛为 `YidaComp`,依赖名正则抽取)。
@@ -21,12 +23,13 @@ Code Canvas 是宜搭的**代码画布自定义页面链路**:以 `YidaCodeCan
21
23
 
22
24
  | 证据 | 判断 |
23
25
  | --- | --- |
24
- | 现代 React 交互 / hooks 状态 / 可视化 / AI 生成 / 需崩溃隔离的自定义页 | **默认使用本技能** |
26
+ | 用户明确要求 Code Canvas / 代码画布 / runtimeCode / importedModules | 使用本技能 |
27
+ | 已确认当前组织/页面支持 `YidaCodeCanvas`,且需要现代 React 交互 / hooks 状态 / 可视化 / AI 生成 / 崩溃隔离 | 使用本技能 |
25
28
  | 页面 Schema 或组件树里有 `componentName: "YidaCodeCanvas"` | 使用本技能 |
26
- | 需求明确是“代码画布 / Code Canvas / runtimeCode / importedModules” | 使用本技能 |
27
29
  | 需求明确是“从 OpenYida 原链路升级/迁移到 Canvas” | 切到 `yida-canvas-upgrade` |
28
- | 只需通过开放 API(HTTP)读写宜搭数据的页面 | **仍用本技能**:在 `YidaComp` 内自写 fetch 调开放 API / 连接器代理(Canvas 无 `this` 桥,需自建 HTTP 数据桥) |
29
- | 强依赖原生实例数据桥:表单内字段双向绑定 `this.$(fieldId)`、`this.utils.yida.*`、`dataSourceMap`、提交流程深度耦合 | 回退 `yida-custom-page`(该实例桥仅 native 免费提供) |
30
+ | 未确认 Canvas 能力,或只是完整应用 `fast_build` 的主入口页 | 使用 `yida-custom-page` |
31
+ | 只需通过开放 API(HTTP)读写宜搭数据,但未确认 Canvas 能力 | 使用 `yida-custom-page`;不要为了 HTTP 数据读取默认切 Canvas |
32
+ | 强依赖原生实例数据桥:表单内字段双向绑定 `this.$(fieldId)`、`this.utils.yida.*`、`dataSourceMap`、提交流程深度耦合 | 使用 `yida-custom-page`(该实例桥仅 native 免费提供) |
30
33
  | 需要字段结构、公式、联动、权限、报表、流程 | 切到对应配置型技能,不用 Code Canvas 承载 |
31
34
 
32
35
  OpenYida CLI 已支持发布 `YidaCodeCanvas` 页面:把 Code Canvas 源码写成 `.canvas.jsx`(推荐 `project/pages/src/<页面名>.canvas.jsx`),执行 `openyida publish <源文件> <appType> <formUuid>` 即自动走 Canvas 链路——`.canvas.jsx` 扩展名会被识别,CLI **本地用 Babel 编译**出 `runtimeCode` + `importedModules`(无需在线编译服务、无需登录态即可完成编译),构造 `YidaCodeCanvas` 组件的 Schema 并保存。扩展名不规范但确为 Canvas 源码时,加 `--canvas` 显式指定。**不要**把 Canvas 源码用普通 `.oyd.jsx`/`.jsx` 走 native 链路发布(会被 Babel 兼容编译器当 `renderJsx` 处理而失败)。
@@ -45,9 +48,9 @@ OpenYida CLI 已支持发布 `YidaCodeCanvas` 页面:把 Code Canvas 源码写
45
48
  > 🔌 在 `YidaComp` 内自写 HTTP 桥读写宜搭数据(连接器代理 / 同源 fetch / 可复用 hook)→ [references/data-bridge-guide.md](references/data-bridge-guide.md)
46
49
  > 🎨 主色跟随 App 品牌:antd `ConfigProvider.colorPrimary` / Tailwind CSS 变量 / 图表配色的落地写法 → [references/canvas-design-system.md](references/canvas-design-system.md)
47
50
 
48
- ## 视觉方向:先定方向,再写代码
51
+ ## 视觉方向:按需定方向
49
52
 
50
- 写页面前先定视觉方向,避免首次就生成「默认蓝 + 大圆角 + emoji」的 AI 味套版。视觉决策是**栈无关**的,与 native 链路共用决策层技能 `yida-page-uiux`:需要视觉方向时调用 `use_skill("yida-page-uiux", "确定 Code Canvas 页面的视觉方向")`,按页面类型(工作台/仪表盘/列表/详情)做 5 维差异化、走去 AI 味清单、严禁 emoji
53
+ 单点页面美化、用户明确要求好看/去 AI 味,或 `yida-app` 进入 `deep_design` 时,再调用 `use_skill("yida-page-uiux", "确定 Code Canvas 页面的视觉方向")`。`yida-app fast_build` 默认不加载 `yida-page-uiux`:直接使用克制的 MVP 工作台/列表/入口布局,禁 emoji、少装饰、主色跟随 App 品牌即可。
51
54
 
52
55
  Canvas 这套栈(React18 + antd + Tailwind)怎么把「主色跟随 App 品牌」落地由 [references/canvas-design-system.md](references/canvas-design-system.md) 负责。一句话记忆:**CSS 变量 `var(--color-brand1-*)` 对普通 DOM / Tailwind 直接可用(Canvas 跑在真 window、节点在宿主树内会级联);antd token 和图表颜色是 JS 消费,需用 `getComputedStyle` 把品牌色解析成真实色值再喂进去。** 语义色(成功/警告/错误)保持固定,不随主题变。
53
56
 
@@ -100,7 +103,7 @@ openyida get-schema <appType> <formUuid>
100
103
  | 发布 Canvas 页(`.canvas.jsx` → `openyida publish` 自动走 Canvas 链路) | 本技能开发流程 step 4 |
101
104
  | 编译并发布 native `.oyd.jsx`/`.jsx` 页面 | `yida-publish-page` |
102
105
  | 创建空白自定义页面 | `yida-create-page` |
103
- | 通过数据源调用连接器 | `yida-data-source-connectors` |
106
+ | 用户明确要求通过设计器数据源调用连接器 | `yida-data-source-connectors`;`fast_build` 不默认加载 |
104
107
  | 创建或管理 HTTP 连接器 | `yida-connector` |
105
108
  | 配表单字段结构,如真正的 EmployeeField 字段 | `yida-create-form-page` |
106
109
 
@@ -8,7 +8,7 @@ description: 创建宜搭应用,返回 appType。搭建应用的第一步。
8
8
  ## 严格禁止 (NEVER DO)
9
9
  - 不要编造 appType,必须从命令返回的 JSON 中提取
10
10
  - 不要在未确认 corpId 的情况下创建应用(先运行 `openyida env` 确认登录态)
11
- - 不要重复创建同名应用,先询问用户是否已有应用
11
+ - 不要在同一轮已成功创建应用后重复创建。若接口明确返回名称冲突,单点任务先询问用户;`yida-app fast_build` 可追加短后缀重试一次,不要为了查重额外探测。
12
12
 
13
13
  ## 严格要求 (MUST DO)
14
14
 
@@ -20,7 +20,7 @@ description: 创建宜搭应用,返回 appType。搭建应用的第一步。
20
20
 
21
21
  用户说"创建应用"、"新建系统"、"搭建平台"时使用此技能。
22
22
  创建应用后,通常需要继续执行:创建表单(`yida-create-form-page`)→ 创建页面(`yida-create-page`)→ 发布页面(`yida-publish-page`)。
23
- 后续如果需要自定义页面,默认走 OpenYida 页面源码链路:源码写到 `project/pages/src/<页面名>.oyd.jsx`,先 `openyida check-page` / `openyida compile`,再 `openyida publish`。不要回到旧的普通 `.jsx` 直发链路。
23
+ 后续如果需要自定义页面,默认走 native 兼容链路:源码写到 `project/pages/src/<页面名>.oyd.jsx`,先 `openyida check-page` / `openyida compile`,再 `openyida publish`。Code Canvas 尚未全量,只有用户明确要求或已确认当前组织/页面支持时才走 `.canvas.jsx` 链路。
24
24
 
25
25
  ---
26
26
 
@@ -11,9 +11,9 @@ description: 在宜搭应用中创建自定义展示页面(display 类型)
11
11
 
12
12
  ## 严格要求 (MUST DO)
13
13
 
14
- - **创建前必须确认**:执行创建命令前,必须向用户确认页面名称和目标应用,获得用户明确同意后再执行
14
+ - **创建前必须确认**:单点创建页面时,执行创建命令前必须向用户确认页面名称和目标应用。由 `yida-app fast_build` 编排且用户已说“默认方案 / 不要追问 / 直接创建”时,合理命名并直接创建,不再二次追问。
15
15
  - 创建成功后,将 formUuid 记录到 `.cache/<项目名>-schema.json`
16
- - 创建页面后,必须继续执行 `yida-custom-page` 编写 JSX 代码,再用 `yida-publish-page` 发布
16
+ - 创建页面后,必须继续执行 `yida-custom-page`(默认兼容链路)或 `yida-canvas-custom-page`(用户明确要求或已确认支持 Canvas 时)编写 JSX 代码,再用 `yida-publish-page` 发布
17
17
  - **本技能不读写 memory**:formUuid 等信息输出到 stdout,通过 `.cache/<项目名>-schema.json` 持久化,不依赖跨会话的 memory 状态
18
18
 
19
19
  ## 适用场景
@@ -52,7 +52,7 @@ openyida create-page <appType> <pageName> [--mode dashboard]
52
52
  {"success":true,"pageId":"FORM-XXX","pageName":"驾驶舱","appType":"APP_XXX","mode":"dashboard","chromeless":true,"url":"{base_url}/APP_XXX/custom/FORM-XXX?isRenderNav=false","workbenchUrl":"{base_url}/APP_XXX/workbench/FORM-XXX"}
53
53
  ```
54
54
 
55
- > 创建后使用 `yida-custom-page` 编写 JSX 代码,再用 `openyida publish` 发布。
55
+ > 创建后默认使用 `yida-custom-page` 编写 `.oyd.jsx` 代码,并通过 `openyida check-page` / `openyida compile` / `openyida publish` 发布;Code Canvas 仅在用户明确要求或已确认支持时使用。
56
56
  > 如需创建表单页面(带字段的数据收集页),请使用 `yida-create-form-page`。
57
57
 
58
58
  ## 异常处理