openyida 2026.9.20 → 2026.9.21
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.
- package/README.md +1 -1
- package/lib/app/application-style.js +3 -1
- package/lib/app/theme-from-design.js +38 -13
- package/lib/app/update-app.js +63 -13
- package/lib/core/command-manifest.js +32 -20
- package/lib/core/locales/en.js +17 -0
- package/lib/core/locales/zh.js +17 -0
- package/lib/core/sample.js +17 -14
- package/lib/design-plan/confirmation.js +13 -0
- package/lib/design-plan/design-plan.js +44 -2
- package/lib/design-plan/entry-navigation.js +10 -2
- package/lib/design-plan/init.js +11 -7
- package/lib/design-plan/materialize.js +62 -43
- package/lib/design-plan/normalize.js +13 -14
- package/lib/design-plan/parallel.js +62 -13
- package/lib/design-plan/patch.js +21 -7
- package/lib/design-plan/rebase.js +60 -0
- package/lib/design-plan/themes.js +16 -1
- package/lib/design-plan/validate.js +6 -1
- package/lib/design-plan/visual-policy.js +18 -4
- package/lib/samples/openyida-scaffold/canvas-form-drawer.canvas.jsx +3 -3
- package/lib/samples/openyida-scaffold/canvas-nav/mixed.jsx +6 -4
- package/lib/samples/openyida-scaffold/canvas-nav/shared.jsx +5 -5
- package/lib/samples/openyida-scaffold/canvas-nav/side.jsx +1 -2
- package/lib/samples/openyida-scaffold/canvas-nav/sidebar.jsx +1 -1
- package/lib/samples/openyida-scaffold/canvas-nav/top.jsx +5 -4
- package/package.json +1 -1
- package/yida-skills/SKILL.md +2 -2
- package/yida-skills/references/yida-api.md +4 -0
- package/yida-skills/skills/yida-app/SKILL.md +12 -4
- package/yida-skills/skills/yida-app/references/entry-navigation.md +11 -9
- package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +1 -1
- package/yida-skills/skills/yida-app/workflow/plan/workflow.md +11 -4
- package/yida-skills/skills/yida-app/workflow/step-2-design.md +3 -3
- package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +2 -2
- package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +3 -3
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +3 -2
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +2 -2
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +1 -1
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-theme-provider.md +1 -1
- package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +140 -1
- package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +1 -1
- package/yida-skills/skills/yida-canvas-custom-page/scripts/canvas-theme-provider.template.jsx +1 -1
- package/yida-skills/skills/yida-canvas-data-binding/SKILL.md +7 -0
- package/yida-skills/skills/yida-create-form-page/SKILL.md +1 -1
- package/yida-skills/skills/yida-design/SKILL.md +5 -3
- package/yida-skills/skills/yida-design/references/application-style-library.md +22 -7
- package/yida-skills/skills/yida-design/references/application-theme-consistency.md +45 -5
- package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +1 -1
- package/yida-skills/skills/yida-design/references/native-form-styles.md +1 -1
- package/yida-skills/skills/yida-design/references/navigation-decision.md +6 -4
- package/yida-skills/skills/yida-design/references/theme/app-custom-theme-template.css +62 -0
- package/yida-skills/skills/yida-design/references/theme-selection.md +10 -9
- package/yida-skills/skills/yida-design/references/visual-decision-engine.md +1 -1
- package/yida-skills/skills/yida-design/scripts/validate_design_themes.py +35 -9
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/SKILL.md +13 -3
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-compact-schema.md +4 -2
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +4 -2
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-design.md +3 -1
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-theme-selection.md +15 -17
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/render_build_plan.py +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/README.md +15 -10
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/app_theme.css +224 -83
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/design.md +69 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/app_theme.css +224 -83
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/design.md +69 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/app_theme.css +244 -103
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/design.md +68 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/app_theme.css +223 -82
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/design.md +68 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/app_theme.css +225 -84
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/design.md +69 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/app_theme.css +244 -103
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/design.md +68 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/app_theme.css +224 -83
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/design.md +69 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/app_theme.css +244 -103
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/design.md +68 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/app_theme.css +236 -89
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/design.md +74 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/app_theme.css +224 -83
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/design.md +68 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/app_theme.css +223 -82
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/design.md +68 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/app_theme.css +224 -83
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/design.md +68 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/app_theme.css +240 -89
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/design.md +76 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/app_theme.css +224 -83
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/design.md +69 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/app_theme.css +224 -83
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/design.md +68 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/app_theme.css +254 -107
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/design.md +74 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/app_theme.css +233 -88
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/design.md +73 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/app_theme.css +224 -83
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/design.md +69 -14
- package/yida-skills/skills/yida-design/templates/design-themes/basic-tokens.json +2 -1
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/app_theme.css +690 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{dark-inset-hairline.md → dark-inset-hairline/design.md} +103 -58
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/app_theme.css +691 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{dark-rail-fine-lines.md → dark-rail-fine-lines/design.md} +102 -50
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/app_theme.css +696 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{dark-rail-signal-panels.md → dark-rail-signal-panels/design.md} +62 -12
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/app_theme.css +278 -115
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/design.md +57 -10
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/app_theme.css +692 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{graphite-bevel-grid.md → graphite-bevel-grid/design.md} +60 -10
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/app_theme.css +691 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{hairline-shared-bands.md → hairline-shared-bands/design.md} +62 -12
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/app_theme.css +691 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{hairline-soft-blocks.md → hairline-soft-blocks/design.md} +57 -9
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/index.json +141 -48
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/app_theme.css +701 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{ink-glow-soft-panels.md → ink-glow-soft-panels/design.md} +58 -8
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/app_theme.css +691 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{inset-frame-pixel-rhythm.md → inset-frame-pixel-rhythm/design.md} +59 -9
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/app_theme.css +698 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{outlined-texture-duotone.md → outlined-texture-duotone/design.md} +62 -12
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/app_theme.css +691 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{soft-inset-surfaces.md → soft-inset-surfaces/design.md} +59 -11
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/app_theme.css +690 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{soft-outline-rhythm.md → soft-outline-rhythm/design.md} +60 -26
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/app_theme.css +698 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{soft-rail-bold-band.md → soft-rail-bold-band/design.md} +99 -49
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/app_theme.css +692 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{soft-spectral-panels.md → soft-spectral-panels/design.md} +63 -13
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/app_theme.css +711 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{warm-canvas-contrast-panels.md → warm-canvas-contrast-panels/design.md} +60 -10
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/app_theme.css +692 -0
- package/yida-skills/skills/yida-design/templates/design-themes/{warm-rail-muted-panels.md → warm-rail-muted-panels/design.md} +98 -50
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/form-layout.json +13 -0
- package/yida-skills/skills/yida-design/templates/navigation-styles.json +769 -0
- package/yida-skills/skills/yida-design/workflow/output-design.md +5 -5
- package/yida-skills/skills/yida-design/workflow/step-2-theme-system.md +5 -3
- package/yida-skills/skills/yida-design/workflow/step-6-handoff.md +1 -1
- package/yida-skills/skills/yida-form-permission/SKILL.md +6 -0
- package/yida-skills/skills/yida-nav-shell/SKILL.md +1 -1
- package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +6 -0
- package/yida-skills/skills/yida-page-config/SKILL.md +9 -0
- package/yida-skills/skills/yida-prd/SKILL.md +1 -0
- package/yida-skills/skills/yida-prd/workflow/output-prd.md +2 -2
- package/yida-skills/skills/yida-prd/workflow/step-2-information-architecture.md +1 -1
- package/yida-skills/skills/yida-requirement-analysis/references/experience-groups.md +45 -9
- package/yida-skills/skills/yida-requirement-analysis/references/handoff.md +5 -5
- package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +17 -4
- package/yida-skills/skills-index.json +2 -2
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { createHash } = require('crypto');
|
|
4
4
|
const { CliError } = require('../core/cli-error');
|
|
5
|
+
const { t } = require('../core/i18n');
|
|
5
6
|
const { readJson } = require('./files');
|
|
6
7
|
const { assertVisualFieldObjects } = require('./normalize');
|
|
7
8
|
|
|
@@ -11,13 +12,14 @@ function planBase(plan) {
|
|
|
11
12
|
return { revision: plan.meta?.revision, digest: createHash('sha256').update(JSON.stringify(plan)).digest('hex') };
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
function readPart(file, role, base, allowed) {
|
|
15
|
-
const part = readJson(file, `${role} 规划片段`);
|
|
15
|
+
function readPart(file, role, base, allowed, supplied) {
|
|
16
|
+
const part = supplied || readJson(file, `${role} 规划片段`);
|
|
16
17
|
if (!object(part) || part.ready !== true || !object(part.facts)) {
|
|
17
18
|
throw new CliError(`${role} 规划尚未完成,请补齐 facts 并设置 ready=true`, { code: 'DESIGN_PLAN_PART_NOT_READY' });
|
|
18
19
|
}
|
|
19
20
|
if (part.base?.revision !== base.revision || part.base?.digest !== base.digest) {
|
|
20
|
-
throw new CliError(`${role} 规划来自旧版本,请根据当前计划重新整理`, { code: 'DESIGN_PLAN_STALE_PART'
|
|
21
|
+
throw new CliError(`${role} 规划来自旧版本,请根据当前计划重新整理`, { code: 'DESIGN_PLAN_STALE_PART',
|
|
22
|
+
details: { sourcePath: file, expectedBase: base, actualBase: part.base, nextStep: t('design_plan.rebase_required') } });
|
|
21
23
|
}
|
|
22
24
|
const unknown = Object.keys(part.facts).filter(key => !allowed.includes(key));
|
|
23
25
|
if (unknown.length) {
|
|
@@ -26,10 +28,63 @@ function readPart(file, role, base, allowed) {
|
|
|
26
28
|
return part.facts;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
// Report all binding defects at once so agents can repair the existing part.
|
|
32
|
+
function assertPageBindings(pages, applications, visualFile, businessFile) {
|
|
33
|
+
const prefix = 'facts.visualStyle.forUser.pageApplications';
|
|
34
|
+
const issues = [];
|
|
35
|
+
const pageIds = [];
|
|
36
|
+
pages.forEach((page, index) => {
|
|
37
|
+
const field = `facts.pages.customPageDetails[${index}].pageId`;
|
|
38
|
+
if (!object(page) || typeof page.pageId !== 'string' || !page.pageId.trim()) {
|
|
39
|
+
issues.push({ sourcePath: businessFile, path: field, code: 'expected_nonempty_string' });
|
|
40
|
+
} else if (pageIds.includes(page.pageId)) {
|
|
41
|
+
issues.push({ sourcePath: businessFile, path: field, code: 'duplicate_page', pageId: page.pageId });
|
|
42
|
+
} else { pageIds.push(page.pageId); }
|
|
43
|
+
});
|
|
44
|
+
const seen = new Set();
|
|
45
|
+
if (!Array.isArray(applications)) {
|
|
46
|
+
issues.push({ sourcePath: visualFile, path: prefix, code: 'expected_array' });
|
|
47
|
+
} else {
|
|
48
|
+
applications.forEach((item, index) => {
|
|
49
|
+
const field = `${prefix}[${index}]`;
|
|
50
|
+
if (!object(item)) {
|
|
51
|
+
issues.push({ sourcePath: visualFile, path: field, code: 'expected_object' });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (typeof item.pageId !== 'string' || !item.pageId.trim()) {
|
|
55
|
+
issues.push({ sourcePath: visualFile, path: `${field}.pageId`, code: 'expected_nonempty_string' });
|
|
56
|
+
} else {
|
|
57
|
+
if (!pageIds.includes(item.pageId)) {
|
|
58
|
+
issues.push({ sourcePath: visualFile, path: `${field}.pageId`, code: 'unexpected_page', pageId: item.pageId });
|
|
59
|
+
}
|
|
60
|
+
if (seen.has(item.pageId)) {
|
|
61
|
+
issues.push({ sourcePath: visualFile, path: `${field}.pageId`, code: 'duplicate_page', pageId: item.pageId });
|
|
62
|
+
}
|
|
63
|
+
seen.add(item.pageId);
|
|
64
|
+
}
|
|
65
|
+
if (!Array.isArray(item.visualMemoryApplications)) {
|
|
66
|
+
issues.push({ sourcePath: visualFile, path: `${field}.visualMemoryApplications`, code: 'expected_array' });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
pageIds.filter(pageId => !seen.has(pageId)).forEach(pageId => {
|
|
71
|
+
issues.push({ sourcePath: visualFile, path: prefix, code: 'missing_page', pageId });
|
|
72
|
+
});
|
|
73
|
+
if (issues.length) {
|
|
74
|
+
const nextStep = t('design_plan.repair_page_bindings');
|
|
75
|
+
const summary = issues.map(issue => `${issue.path}: ${issue.code}${issue.pageId ? ` (${issue.pageId})` : ''}`).join('; ');
|
|
76
|
+
throw new CliError(`${nextStep} ${summary}`, {
|
|
77
|
+
code: 'DESIGN_PLAN_PAGE_BINDINGS_REQUIRED',
|
|
78
|
+
details: { sourcePath: visualFile, expectedPageIds: pageIds, issues, nextStep },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function mergeParts(source, businessFile, visualFile, options = {}) {
|
|
30
84
|
const base = planBase(source);
|
|
31
|
-
const
|
|
32
|
-
const
|
|
85
|
+
const rebased = options.rebaseParts ? require('./rebase').rebaseParts(source, options.input, businessFile, visualFile) : [];
|
|
86
|
+
const business = readPart(businessFile, 'business', base, ['overview', 'dataModels', 'businessFlows', 'pages', 'execution', 'meta'], rebased[0]);
|
|
87
|
+
const visual = readPart(visualFile, 'visual', base, ['visualStyle'], rebased[1]);
|
|
33
88
|
assertVisualFieldObjects(visual.visualStyle?.forUser, { prefix: 'facts.visualStyle.forUser', sourcePath: visualFile });
|
|
34
89
|
if (!object(business.overview) || !Array.isArray(business.dataModels) || !Array.isArray(business.businessFlows)
|
|
35
90
|
|| !Array.isArray(business.pages?.customPageDetails) || !object(visual.visualStyle)) {
|
|
@@ -39,13 +94,7 @@ function mergeParts(source, businessFile, visualFile) {
|
|
|
39
94
|
|| Object.keys(business.meta).some(key => !['businessDomain', 'experienceTopology'].includes(key)))) {
|
|
40
95
|
throw new CliError('业务 meta 仅填写 businessDomain/experienceTopology,版本由 CLI 维护', { code: 'DESIGN_PLAN_PART_CONFLICT' });
|
|
41
96
|
}
|
|
42
|
-
|
|
43
|
-
const applications = visual.visualStyle.forUser?.pageApplications;
|
|
44
|
-
if (!Array.isArray(applications) || applications.length !== pageIds.length
|
|
45
|
-
|| new Set(applications.map(item => item.pageId)).size !== pageIds.length
|
|
46
|
-
|| applications.some(item => !pageIds.includes(item.pageId) || !Array.isArray(item.visualMemoryApplications))) {
|
|
47
|
-
throw new CliError('视觉规划需按业务页面逐一完成 pageApplications,再合并产物', { code: 'DESIGN_PLAN_PAGE_BINDINGS_REQUIRED' });
|
|
48
|
-
}
|
|
97
|
+
assertPageBindings(business.pages.customPageDetails, visual.visualStyle.forUser?.pageApplications, visualFile, businessFile);
|
|
49
98
|
const plan = JSON.parse(JSON.stringify({ ...source, ...business, ...visual, meta: { ...source.meta, ...business.meta },
|
|
50
99
|
...(source.execution || business.execution ? { execution: { ...source.execution, ...business.execution,
|
|
51
100
|
...(source.execution?.appConfig || business.execution?.appConfig
|
package/lib/design-plan/patch.js
CHANGED
|
@@ -12,10 +12,13 @@ const PROTECTED_PATHS = [
|
|
|
12
12
|
'meta.updatedAt',
|
|
13
13
|
'meta.planState',
|
|
14
14
|
'visualStyle.forUser.themeProfile',
|
|
15
|
+
'visualStyle.forUser.navigationStyle.tone',
|
|
16
|
+
'visualStyle.forUser.navigationStyle.toneSource',
|
|
17
|
+
'execution.appConfig.navTheme',
|
|
15
18
|
];
|
|
16
19
|
const DANGEROUS_SEGMENTS = new Set(['__proto__', 'prototype', 'constructor']);
|
|
17
20
|
|
|
18
|
-
function parsePath(pathExpression) {
|
|
21
|
+
function parsePath(pathExpression, options = {}) {
|
|
19
22
|
const expression = String(pathExpression || '').trim();
|
|
20
23
|
if (!expression || !/^[A-Za-z_$][\w$-]*(?:(?:\.(?:[A-Za-z_$][\w$-]*|--[\w-]+))|(?:\[\d+\]))*$/.test(expression)) {
|
|
21
24
|
throw new CliError(`无效字段路径:${expression || '(empty)'}`, {
|
|
@@ -28,11 +31,16 @@ function parsePath(pathExpression) {
|
|
|
28
31
|
code: 'DESIGN_PLAN_UNSAFE_PATH',
|
|
29
32
|
});
|
|
30
33
|
}
|
|
31
|
-
|
|
34
|
+
const protectedPaths = options.creative ? PROTECTED_PATHS.filter(field => field !== 'visualStyle.forUser.navigationStyle.tone') : PROTECTED_PATHS;
|
|
35
|
+
if (protectedPaths.some(protectedPath => expression === protectedPath
|
|
32
36
|
|| expression.startsWith(`${protectedPath}.`)
|
|
33
37
|
|| expression.startsWith(`${protectedPath}[`)
|
|
34
38
|
|| protectedPath.startsWith(`${expression}.`))) {
|
|
35
|
-
const hint = expression.includes('themeProfile')
|
|
39
|
+
const hint = expression.includes('themeProfile')
|
|
40
|
+
? ';themeProfile 只读,请改 visualStyle.tokens 或 colorStrategy.primaryColor'
|
|
41
|
+
: /(?:navigationStyle\.(?:tone|toneSource)|appConfig\.navTheme)/.test(expression)
|
|
42
|
+
? ';导航明暗由所选主题模板派生,请改选主题'
|
|
43
|
+
: '';
|
|
36
44
|
throw new CliError(`字段 ${expression} 由 design-plan patch 自动维护,不能通过 --set 修改${hint}`, {
|
|
37
45
|
code: 'DESIGN_PLAN_PROTECTED_PATH',
|
|
38
46
|
});
|
|
@@ -49,7 +57,7 @@ function parseValue(rawValue) {
|
|
|
49
57
|
}
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
function parseSetExpression(expression) {
|
|
60
|
+
function parseSetExpression(expression, options = {}) {
|
|
53
61
|
const separator = String(expression || '').indexOf('=');
|
|
54
62
|
if (separator <= 0) {
|
|
55
63
|
throw new CliError(`--set 必须使用 path=value 格式:${expression || '(empty)'}`, {
|
|
@@ -59,7 +67,7 @@ function parseSetExpression(expression) {
|
|
|
59
67
|
const pathExpression = expression.slice(0, separator).trim();
|
|
60
68
|
return {
|
|
61
69
|
path: pathExpression,
|
|
62
|
-
segments: parsePath(pathExpression),
|
|
70
|
+
segments: parsePath(pathExpression, options),
|
|
63
71
|
value: parseValue(expression.slice(separator + 1)),
|
|
64
72
|
};
|
|
65
73
|
}
|
|
@@ -67,7 +75,7 @@ function parseSetExpression(expression) {
|
|
|
67
75
|
const OPTIONAL_PATHS = [
|
|
68
76
|
/^execution$/,
|
|
69
77
|
/^execution\.(resourceBlueprint|resourceCreationOrder|pageImplementationOrder|navigationOrder|navigationFallback|entryRecommendation|sampleDataPlan|interactionStates|acceptanceCriteria|explicitScope|appConfig)$/,
|
|
70
|
-
/^execution\.appConfig\.(appType|corpId|baseUrl|navigationType|hideAppNav|layoutDirection|
|
|
78
|
+
/^execution\.appConfig\.(appType|corpId|baseUrl|navigationType|hideAppNav|layoutDirection|logoSource)$/,
|
|
71
79
|
/^execution\.interactionStates\.(empty|loading|error|formEntry|detail)$/,
|
|
72
80
|
/^visualStyle\.tokens(?:\.--[\w-]+)?$/,
|
|
73
81
|
/^visualStyle\.forUser\.iconSystem(?:\.(library|mappings|colorPairs))?$/,
|
|
@@ -154,12 +162,17 @@ function patchPlan(inputPath, setExpressions, options = {}) {
|
|
|
154
162
|
}
|
|
155
163
|
const original = readJson(resolvedInput);
|
|
156
164
|
const patched = JSON.parse(JSON.stringify(original));
|
|
157
|
-
const
|
|
165
|
+
const selectedTheme = original.visualStyle?.internal?.selectedTheme || original.visualStyle?.forUser?.selectedTheme;
|
|
166
|
+
const creative = selectedTheme?.themeId === 'free-creative';
|
|
167
|
+
const changes = expressions.map(expression => parseSetExpression(expression, { creative })).map(change => ({
|
|
158
168
|
...change,
|
|
159
169
|
previousValue: setExistingPath(patched, change),
|
|
160
170
|
}));
|
|
161
171
|
const effectiveChanges = changes.filter(change => JSON.stringify(change.previousValue) !== JSON.stringify(change.value));
|
|
162
172
|
if (JSON.stringify(original) === JSON.stringify(patched)) {
|
|
173
|
+
if (options.materialize) {
|
|
174
|
+
return { ...require('./materialize').materialize(resolvedInput, { outputDir: options.outputDir }), changed: false, materialized: true, changedPaths: [], confirmationInvalidated: false };
|
|
175
|
+
}
|
|
163
176
|
return {
|
|
164
177
|
success: true,
|
|
165
178
|
changed: false,
|
|
@@ -195,6 +208,7 @@ function patchPlan(inputPath, setExpressions, options = {}) {
|
|
|
195
208
|
confirmationInvalidated: !assetProgressOnly,
|
|
196
209
|
materialized: options.materialize === true,
|
|
197
210
|
outputs: options.materialize ? artifacts.outputs : undefined,
|
|
211
|
+
...(options.materialize ? { confirmation: require('./confirmation').confirmationPayload(artifacts.outputs, artifacts.revision) } : {}),
|
|
198
212
|
updated: updates?.updated || [],
|
|
199
213
|
};
|
|
200
214
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { CliError } = require('../core/cli-error');
|
|
6
|
+
const { readJson } = require('./files');
|
|
7
|
+
const { t } = require('../core/i18n');
|
|
8
|
+
|
|
9
|
+
const BASE_FILE = '.build-plan-base.json';
|
|
10
|
+
const equal = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
11
|
+
const object = value => value && typeof value === 'object' && !Array.isArray(value);
|
|
12
|
+
|
|
13
|
+
// Arrays represent ordered business facts; conflicting edits require a decision.
|
|
14
|
+
function reconcile(base, current, proposed, field, conflicts) {
|
|
15
|
+
if (equal(current, proposed) || equal(proposed, base)) { return current; }
|
|
16
|
+
if (equal(current, base)) { return proposed; }
|
|
17
|
+
if (object(base) && object(current) && object(proposed)) {
|
|
18
|
+
const result = {};
|
|
19
|
+
for (const key of new Set([...Object.keys(base), ...Object.keys(current), ...Object.keys(proposed)])) {
|
|
20
|
+
const value = reconcile(base[key], current[key], proposed[key], field ? `${field}.${key}` : key, conflicts);
|
|
21
|
+
if (value !== undefined) { result[key] = value; }
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
conflicts.push(field);
|
|
26
|
+
return proposed;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function rebaseParts(source, input, businessFile, visualFile) {
|
|
30
|
+
const { planBase } = require('./parallel');
|
|
31
|
+
const baselinePath = path.join(path.dirname(path.resolve(input)), BASE_FILE);
|
|
32
|
+
const fail = (code, details) => { throw new CliError(t('design_plan.rebase_required'), { code, details: { baselinePath, ...details } }); };
|
|
33
|
+
if (!fs.existsSync(baselinePath)) { fail('DESIGN_PLAN_BASELINE_MISSING', { nextStep: t('design_plan.baseline_missing') }); }
|
|
34
|
+
const saved = readJson(baselinePath);
|
|
35
|
+
if (saved.schemaVersion !== 1 || !object(saved.plan) || !equal(saved.base, planBase(saved.plan))) {
|
|
36
|
+
fail('DESIGN_PLAN_BASELINE_INVALID', { nextStep: t('design_plan.baseline_missing') });
|
|
37
|
+
}
|
|
38
|
+
if (source.meta?.status !== 'draft' || source.meta?.planState?.presentedRevision
|
|
39
|
+
|| source.meta?.planState?.planConfirmed || !equal(source.meta, saved.plan.meta)) {
|
|
40
|
+
fail('DESIGN_PLAN_REBASE_STAGE_INVALID', { nextStep: t('design_plan.rebase_stage_invalid') });
|
|
41
|
+
}
|
|
42
|
+
const parts = [businessFile, visualFile].map(file => readJson(file));
|
|
43
|
+
for (const [index, part] of parts.entries()) {
|
|
44
|
+
if (!equal(part.base, saved.base) || !object(part.facts)) {
|
|
45
|
+
fail('DESIGN_PLAN_BASELINE_MISMATCH', { sourcePath: [businessFile, visualFile][index], nextStep: t('design_plan.baseline_missing') });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return parts.map((part, index) => {
|
|
49
|
+
const conflicts = [];
|
|
50
|
+
const facts = Object.fromEntries(Object.keys(part.facts).map(key => [key,
|
|
51
|
+
reconcile(saved.plan[key], source[key], part.facts[key], key, conflicts),
|
|
52
|
+
]));
|
|
53
|
+
if (conflicts.length) {
|
|
54
|
+
fail('DESIGN_PLAN_REBASE_CONFLICT', { sourcePath: [businessFile, visualFile][index], conflicts, nextStep: t('design_plan.rebase_conflict') });
|
|
55
|
+
}
|
|
56
|
+
return { ...part, facts, base: planBase(source) };
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = { BASE_FILE, rebaseParts };
|
|
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { CliError } = require('../core/cli-error');
|
|
6
6
|
const { readJson } = require('./files');
|
|
7
|
+
const { parseDesignDocument } = require('../design/document');
|
|
7
8
|
|
|
8
9
|
const DESIGN_SKILL_ROOT = path.resolve(__dirname, '../../yida-skills/skills/yida-design');
|
|
9
10
|
const PLAN_SKILL_ROOT = path.join(DESIGN_SKILL_ROOT, 'sub_skill/yida-design-plan');
|
|
@@ -21,6 +22,20 @@ function themeTemplatePath(theme) {
|
|
|
21
22
|
return file;
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
function themeNavTheme(theme) {
|
|
26
|
+
if (theme.mode === 'creative') {return undefined;}
|
|
27
|
+
const file = themeTemplatePath(theme);
|
|
28
|
+
const metadata = parseDesignDocument(fs.readFileSync(file, 'utf8')).metadata;
|
|
29
|
+
const navTheme = metadata.themeProfile?.navTheme || metadata.navTheme;
|
|
30
|
+
if (!['light', 'dark'].includes(navTheme)) {
|
|
31
|
+
throw new CliError(`主题模板 navTheme 无效:${theme.themeId}`, {
|
|
32
|
+
code: 'DESIGN_PLAN_THEME_NAV_THEME_INVALID',
|
|
33
|
+
details: { themeId: theme.themeId, navTheme: metadata.navTheme, templatePath: theme.templatePath },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return navTheme;
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
// Color recipes live with the visual theme. Resolve their token dependencies rather
|
|
25
40
|
// than duplicating per-theme palettes in JavaScript.
|
|
26
41
|
function resolveThemeColors(source) {
|
|
@@ -83,4 +98,4 @@ function resolveColorToken(tokens, name, seen = new Set()) {
|
|
|
83
98
|
return alias ? resolveColorToken(tokens, alias[1], seen) || alias[2] || null : null;
|
|
84
99
|
}
|
|
85
100
|
|
|
86
|
-
module.exports = { DESIGN_SKILL_ROOT, PLAN_SKILL_ROOT, loadThemeIndex, themeTemplatePath, resolveThemeColors, resolveColorToken };
|
|
101
|
+
module.exports = { DESIGN_SKILL_ROOT, PLAN_SKILL_ROOT, loadThemeIndex, themeTemplatePath, themeNavTheme, resolveThemeColors, resolveColorToken };
|
|
@@ -6,6 +6,8 @@ const { CliError } = require('../core/cli-error');
|
|
|
6
6
|
const { derivePageContent } = require('./normalize');
|
|
7
7
|
const { PLAN_SKILL_ROOT: ROOT, loadThemeIndex } = require('./themes');
|
|
8
8
|
const { concretePageText } = require('../design/page-content');
|
|
9
|
+
const { ENTRY_MODES } = require('./entry-navigation');
|
|
10
|
+
const { t } = require('../core/i18n');
|
|
9
11
|
|
|
10
12
|
const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
11
13
|
const filled = value => typeof value === 'string' && value.trim().length > 0;
|
|
@@ -74,8 +76,11 @@ function collectIssues(plan) {
|
|
|
74
76
|
const pages = plan.pages?.customPageDetails;
|
|
75
77
|
const recommendation = plan.execution?.entryRecommendation;
|
|
76
78
|
if (recommendation !== undefined && check(object(recommendation), 'execution.entryRecommendation', '填写访问态入口方案')) {
|
|
77
|
-
check(
|
|
79
|
+
check(ENTRY_MODES.includes(recommendation.mode), 'execution.entryRecommendation.mode', t('cli.design_plan_entry_mode_required'));
|
|
78
80
|
if (array(recommendation.entries, 'execution.entryRecommendation.entries', true)) {
|
|
81
|
+
if (recommendation.mode === 'backend-only') {
|
|
82
|
+
check(recommendation.entries.every(entry => entry?.role === 'management'), 'execution.entryRecommendation.entries', t('cli.design_plan_backend_only_roles'));
|
|
83
|
+
}
|
|
79
84
|
recommendation.entries.forEach((entry, index) => {
|
|
80
85
|
const p = `execution.entryRecommendation.entries[${index}]`;
|
|
81
86
|
strings(entry, p, ['key', 'name', 'defaultMenuKey']);
|
|
@@ -4,6 +4,20 @@ function getVisualDecisionPolicy() {
|
|
|
4
4
|
return {
|
|
5
5
|
reference: 'yida-skills/skills/yida-design/references/theme-selection.md#设计方向比较',
|
|
6
6
|
preserve: 'explicit_user_direction_brand_and_existing_app_theme',
|
|
7
|
+
applicationStyle: {
|
|
8
|
+
scope: ['navigation', 'application_shell', 'native_form', 'custom_page', 'record_detail'],
|
|
9
|
+
navigationTokens: 'tokens.application-global.appearance.navigation',
|
|
10
|
+
navigationRoles: ['surface', 'text', 'hover', 'selected', 'disabled', 'logo', 'group', 'search', 'popup', 'action', 'border', 'radius', 'shadow', 'density'],
|
|
11
|
+
navigationShape: {
|
|
12
|
+
reference: 'yida-skills/skills/yida-design/references/application-theme-consistency.md#导航形状与密度的案例经验',
|
|
13
|
+
inputs: ['radius', 'normal_hover_selected_borders', 'selected_shadow', 'item_height', 'padding', 'gap', 'shell_spacing'],
|
|
14
|
+
authoring: { fast: 'design.md tokens.application-global.appearance.navigation', plan: 'visualStyle.tokens' },
|
|
15
|
+
layout: 'preserve_business_navigation_type_independently_of_theme_appearance',
|
|
16
|
+
verification: ['native_data_management', 'custom_page', 'submission', 'record_detail', 'long_labels', 'collapsed_navigation'],
|
|
17
|
+
},
|
|
18
|
+
delivery: 'one_design_document_and_application_theme_css',
|
|
19
|
+
verification: 'read_back_app_settings_and_verify_rendered_navigation_form_custom_page_and_detail',
|
|
20
|
+
},
|
|
7
21
|
nativeFormLayout: {
|
|
8
22
|
model: 'component_based_native_form_layout',
|
|
9
23
|
regions: ['top', 'left', 'main', 'right', 'between_fields'],
|
|
@@ -15,13 +29,13 @@ function getVisualDecisionPolicy() {
|
|
|
15
29
|
creativeOption: { alwaysAvailable: true, themeId: 'free-creative', selectionRequired: false,
|
|
16
30
|
source: 'independent_business_reasoning_not_preset_matching',
|
|
17
31
|
authoringReference: 'yida-skills/skills/yida-design/references/application-style-library.md' },
|
|
18
|
-
comparison: { baseline: 'first_instinct', alternatives: 2, distinctDimensions: 2,
|
|
19
|
-
dimensions: ['composition', 'typography', 'surface_hierarchy', 'color_relationships'] },
|
|
32
|
+
comparison: { candidateCount: 3, baseline: 'first_instinct', alternatives: 2, distinctDimensions: 2,
|
|
33
|
+
dimensions: ['composition', 'typography', 'surface_hierarchy', 'color_relationships', 'navigation_shape_and_density'] },
|
|
20
34
|
selection: 'refine_and_choose_a_supported_alternative_within_business_constraints',
|
|
21
35
|
quality: ['task_efficiency', 'readability_and_contrast', 'consistent_spacing_and_alignment', 'coherent_visual_identity'],
|
|
22
|
-
execution: '
|
|
36
|
+
execution: 'one_shared_three_direction_generation_pass_for_fast_and_plan',
|
|
23
37
|
handoff: ['visualSelection.visualDirection.description', 'visualSelection.colorStrategy.usage'],
|
|
24
|
-
presentation: '
|
|
38
|
+
presentation: 'fast_selects_internally; plan_asks_user_when_no_explicit_visual_direction',
|
|
25
39
|
};
|
|
26
40
|
}
|
|
27
41
|
|
|
@@ -61,7 +61,7 @@ function buildYidaFormUrl(request, currentAppType) {
|
|
|
61
61
|
return '';
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
/**
|
|
64
|
+
/** 抽屉默认使用内容画布与正文文字,background 可为当前抽屉指定颜色、渐变或 CSS token。 */
|
|
65
65
|
function CanvasDrawer({
|
|
66
66
|
open, title, onClose, onOpenInNewWindow, extra, children,
|
|
67
67
|
contentMode = 'content',
|
|
@@ -138,7 +138,7 @@ function CanvasDrawer({
|
|
|
138
138
|
border: 0;
|
|
139
139
|
border-radius: 8px;
|
|
140
140
|
background: transparent;
|
|
141
|
-
color: var(--drawer-close-color, var(--pod-page-header-text-color, var(--color-text1-
|
|
141
|
+
color: var(--drawer-close-color, var(--pod-page-header-text-color, var(--color-text1-4, #1f2329)));
|
|
142
142
|
cursor: pointer;
|
|
143
143
|
}
|
|
144
144
|
.openyida-form-drawer .oy-drawer-action:hover {
|
|
@@ -215,7 +215,7 @@ function CanvasDrawer({
|
|
|
215
215
|
},
|
|
216
216
|
content: {
|
|
217
217
|
background,
|
|
218
|
-
color: 'var(--drawer-content-color, var(--color-text1-4, #1f2329))',
|
|
218
|
+
color: 'var(--drawer-content-color, var(--pod-page-header-text-color, var(--color-text1-4, #1f2329)))',
|
|
219
219
|
borderLeft: 'var(--drawer-border-width, 0px) var(--drawer-border-style, solid) var(--drawer-border-color, transparent)',
|
|
220
220
|
borderRadius: fullScreen ? 0 : 'var(--pod-drawer-border-radius, var(--pod-drawer-radius, var(--drawer-corner, 20px))) 0 0 var(--pod-drawer-border-radius, var(--pod-drawer-radius, var(--drawer-corner, 20px)))',
|
|
221
221
|
overflow: 'hidden',
|
|
@@ -4,12 +4,14 @@ function CanvasNav({ items = [], activeKey, onSelect, title, logo, actions, chil
|
|
|
4
4
|
return <div className="oy-canvas-nav oy-nav-mixed">
|
|
5
5
|
<style>{CANVAS_NAV_CSS + `
|
|
6
6
|
.oy-nav-mixed { display: flex; flex-direction: column; }
|
|
7
|
-
.oy-nav-mixed .oy-nav-header { display: flex; flex-wrap: wrap; align-items: center; gap: var(--pod-nav-l-header-gap, 24px); padding: var(--pod-nav-l-header-padding, 8px 16px); border-bottom: 1px solid var(--pod-nav-sub-divider-color, var(--color-line1-1, #ddd)); }
|
|
8
|
-
.oy-nav-mixed .oy-nav-groups { display: flex; flex: 1; min-width: 0; overflow-x: auto; }
|
|
7
|
+
.oy-nav-mixed .oy-nav-header { box-sizing: border-box; min-height: var(--pod-nav-platform-header-height, 48px); display: flex; flex-wrap: wrap; align-items: center; gap: var(--pod-nav-l-header-gap, 24px); padding: var(--pod-nav-l-header-padding, 8px 16px); border-bottom: 1px solid var(--pod-nav-sub-divider-color, var(--color-line1-1, #ddd)); }
|
|
8
|
+
.oy-nav-mixed .oy-nav-groups { display: flex; flex: 1; min-width: 0; gap: var(--pod-nav-menu-gap, 8px); padding: calc(var(--pod-nav-menu-gap, 8px) / 2); overflow-x: auto; }
|
|
9
|
+
.oy-nav-mixed .oy-nav-groups .oy-nav-item { min-height: var(--pod-nav-top-tab-height, 40px); padding: var(--pod-nav-top-tab-item-padding, 0 12px); max-width: var(--pod-nav-top-tab-item-max-width, 240px); }
|
|
10
|
+
.oy-nav-mixed .oy-nav-groups .oy-nav-item > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
9
11
|
.oy-nav-mixed .oy-nav-body { display: flex; flex: 1; }
|
|
12
|
+
.oy-canvas-nav.oy-nav-mixed .oy-nav-sidebar { padding: var(--pod-nav-l-aside-padding, 8px); }
|
|
10
13
|
.oy-nav-mixed .oy-nav-content { flex: 1; }
|
|
11
|
-
.oy-nav-mixed .oy-nav-menu { display: flex; flex-direction: column; gap: var(--pod-nav-
|
|
12
|
-
.oy-nav-mixed .oy-nav-item[aria-current="page"] { box-shadow: inset 0 -2px var(--pod-nav-tab-line-selected-color, var(--color-brand1-6, #1677ff)); }
|
|
14
|
+
.oy-nav-mixed .oy-nav-menu { display: flex; flex-direction: column; gap: var(--pod-nav-menu-gap, 8px); }
|
|
13
15
|
@media (max-width: 767px) {
|
|
14
16
|
.oy-nav-mixed .oy-nav-groups { flex-basis: 100%; }
|
|
15
17
|
.oy-nav-mixed .oy-nav-body { display: block; }
|
|
@@ -14,12 +14,12 @@ const CANVAS_NAV_CSS = `
|
|
|
14
14
|
.oy-canvas-nav .oy-nav-brand > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
15
15
|
.oy-canvas-nav .oy-nav-item {
|
|
16
16
|
box-sizing: border-box; display: flex; flex-shrink: 0; align-items: center; gap: var(--pod-nav-logo-gap, 8px);
|
|
17
|
-
min-height: var(--pod-nav-menu-item-height, 40px); padding: var(--pod-nav-
|
|
18
|
-
border:
|
|
19
|
-
color: var(--pod-nav-item-text-color, var(--color-text1-4, #1f2329)); font: inherit; font-size: var(--pod-nav-menu-font-size, 14px); text-decoration: none; text-align: start; cursor: pointer;
|
|
17
|
+
min-height: var(--pod-nav-menu-item-height, 40px); padding: var(--pod-nav-menu-item-padding, 8px 12px);
|
|
18
|
+
border: var(--pod-nav-menu-item-border, none); border-radius: var(--pod-nav-menu-item-radius, 8px); background: transparent;
|
|
19
|
+
color: var(--pod-nav-item-text-color, var(--color-text1-4, #1f2329)); font: inherit; font-size: var(--pod-nav-menu-font-size, 14px); line-height: var(--pod-nav-menu-line-height, 20px); text-decoration: none; text-align: start; cursor: pointer;
|
|
20
20
|
}
|
|
21
|
-
.oy-canvas-nav .oy-nav-item:hover:not(:disabled) { background: var(--pod-nav-menu-bg-hover-color, var(--color-fill1-2, #f2f3f5)); color: var(--pod-nav-item-text-hover-color, inherit); }
|
|
22
|
-
.oy-canvas-nav .oy-nav-item[aria-current="page"] { background: var(--pod-nav-menu-bg-selected-color, var(--color-brand1-1, #e8f2ff)); color: var(--pod-nav-item-text-selected-color, var(--color-brand1-6, #1677ff)); font-weight: var(--pod-nav-menu-item-selected-font-weight, 600); }
|
|
21
|
+
.oy-canvas-nav .oy-nav-item:hover:not(:disabled) { border: var(--pod-nav-menu-item-hover-border, var(--pod-nav-menu-item-border, none)); background: var(--pod-nav-menu-bg-hover-color, var(--color-fill1-2, #f2f3f5)); color: var(--pod-nav-item-text-hover-color, inherit); }
|
|
22
|
+
.oy-canvas-nav .oy-nav-item[aria-current="page"], .oy-canvas-nav .oy-nav-item[aria-current="page"]:hover { border: var(--pod-nav-menu-item-selected-border, var(--pod-nav-menu-item-border, none)); box-shadow: var(--pod-nav-menu-item-selected-shadow, none); background: var(--pod-nav-menu-bg-selected-color, var(--color-brand1-1, #e8f2ff)); color: var(--pod-nav-item-text-selected-color, var(--color-brand1-6, #1677ff)); font-weight: var(--pod-nav-menu-item-selected-font-weight, 600); }
|
|
23
23
|
.oy-canvas-nav .oy-nav-item:disabled { color: var(--pod-nav-item-text-disabled-color, var(--color-text1-1, #999)); cursor: not-allowed; }
|
|
24
24
|
.oy-canvas-nav .oy-nav-item:focus-visible { outline: 2px solid var(--pod-nav-tab-line-selected-color, var(--color-brand1-6, #1677ff)); outline-offset: -2px; }
|
|
25
25
|
.oy-canvas-nav .oy-nav-item svg { flex-shrink: 0; width: var(--pod-nav-platform-action-icon-size, 18px); height: var(--pod-nav-platform-action-icon-size, 18px); }
|
|
@@ -5,8 +5,7 @@ function CanvasNav({ items = [], activeKey, onSelect, title, logo, actions, chil
|
|
|
5
5
|
.oy-nav-side { display: flex; }
|
|
6
6
|
.oy-nav-side .oy-nav-content { flex: 1; }
|
|
7
7
|
.oy-nav-side .oy-nav-brand { min-height: var(--pod-nav-platform-header-height, 48px); }
|
|
8
|
-
.oy-nav-side .oy-nav-menu { display: flex; flex-direction: column; gap: var(--pod-nav-
|
|
9
|
-
.oy-nav-side .oy-nav-item[aria-current="page"] { box-shadow: inset 3px 0 var(--pod-nav-tab-line-selected-color, var(--color-brand1-6, #1677ff)); }
|
|
8
|
+
.oy-nav-side .oy-nav-menu { display: flex; flex-direction: column; gap: var(--pod-nav-menu-gap, 8px); }
|
|
10
9
|
@media (max-width: 767px) { .oy-nav-side { display: block; } }
|
|
11
10
|
`}</style>
|
|
12
11
|
<CanvasSidebar title={title} logo={logo} actions={actions} activeKey={activeKey} defaultWidth={sidebarWidth} defaultCollapsed={defaultCollapsed}>
|
|
@@ -41,7 +41,7 @@ function CanvasSidebar({ children, title, logo, actions, activeKey, defaultWidth
|
|
|
41
41
|
onClick={event => { if (mobile && event.target.closest('.oy-nav-menu .oy-nav-item')) setMobileOpen(false); }}
|
|
42
42
|
style={{ '--oy-sidebar-width': width === null ? (typeof defaultWidth === 'number' ? `${defaultWidth}px` : defaultWidth) : `${clamp(width)}px`, '--oy-sidebar-max-width': `${maxWidth}px` }}>
|
|
43
43
|
<style>{`
|
|
44
|
-
.oy-canvas-nav .oy-nav-sidebar { position: relative; box-sizing: border-box; flex: 0 0 auto; min-width: 0; width: clamp(180px, var(--oy-sidebar-width, var(--shell-dark-aside-width, 216px)), var(--oy-sidebar-max-width, 400px)); padding: var(--pod-nav-
|
|
44
|
+
.oy-canvas-nav .oy-nav-sidebar { position: relative; box-sizing: border-box; flex: 0 0 auto; min-width: 0; width: clamp(180px, var(--oy-sidebar-width, var(--shell-dark-aside-width, 216px)), var(--oy-sidebar-max-width, 400px)); padding: var(--pod-nav-slide-aside-padding, 8px); border-right: 1px solid var(--pod-nav-sub-divider-color, var(--color-line1-1, #ddd)); }
|
|
45
45
|
.oy-canvas-nav .oy-sidebar-toggle { margin-left: auto; flex-shrink: 0; justify-content: center; padding: 0 8px; }
|
|
46
46
|
.oy-canvas-nav .oy-sidebar-items { min-width: 0; }
|
|
47
47
|
.oy-canvas-nav .oy-nav-sidebar .oy-nav-item > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
@@ -7,16 +7,17 @@ function CanvasNav({ items = [], activeKey, onSelect, title, logo, actions, chil
|
|
|
7
7
|
React.useEffect(() => setExpanded(false), [activeKey]);
|
|
8
8
|
return <div className={'oy-canvas-nav oy-nav-top' + (floating ? ' is-floating' : '') + (headerOnly ? ' is-header-only' : '') + (overlay ? ' is-overlay' : '')}>
|
|
9
9
|
<style>{CANVAS_NAV_CSS + `
|
|
10
|
-
.oy-nav-top .oy-nav-header { display: flex; flex-wrap: wrap; align-items: center; gap: var(--pod-nav-top-header-gap, 24px); padding: var(--pod-nav-top-header-padding, 8px 16px); border-bottom: 1px solid var(--pod-nav-sub-divider-color, var(--color-line1-1, #ddd)); }
|
|
10
|
+
.oy-nav-top .oy-nav-header { box-sizing: border-box; min-height: var(--pod-nav-platform-header-height, 48px); display: flex; flex-wrap: wrap; align-items: center; gap: var(--pod-nav-top-header-gap, 24px); padding: var(--pod-nav-top-header-padding, 8px 16px); border-bottom: 1px solid var(--pod-nav-sub-divider-color, var(--color-line1-1, #ddd)); }
|
|
11
11
|
.oy-nav-top.is-header-only { min-height: 0; background: transparent; }
|
|
12
12
|
.openyida-nav-layout[data-layout="document"][data-scrolled="false"] .oy-nav-top.is-overlay .oy-nav-header:not(.is-expanded) { background: transparent; color: var(--openyida-navigation-overlay-color, #fff); border-color: transparent; }
|
|
13
13
|
.openyida-nav-layout[data-layout="document"][data-scrolled="false"] .oy-nav-top.is-overlay .oy-nav-header:not(.is-expanded) .oy-nav-brand,
|
|
14
14
|
.openyida-nav-layout[data-layout="document"][data-scrolled="false"] .oy-nav-top.is-overlay .oy-nav-header:not(.is-expanded) .oy-nav-item:not(:disabled) { color: inherit; }
|
|
15
15
|
.openyida-nav-layout[data-layout="document"][data-scrolled="false"] .oy-nav-top.is-overlay .oy-nav-header:not(.is-expanded) .oy-nav-item:is(:hover,[aria-current="page"]):not(:disabled) { background: var(--openyida-navigation-overlay-active-bg, rgba(255,255,255,.16)); }
|
|
16
16
|
.oy-nav-top.is-floating { padding-top: 16px; box-sizing: border-box; }
|
|
17
|
-
.oy-nav-top.is-floating .oy-nav-header { position: sticky; top: 16px; z-index: 10; margin: 0 16px; border: 1px solid var(--pod-nav-sub-divider-color, var(--color-line1-1, #ddd)); border-radius: var(--pod-
|
|
18
|
-
.oy-nav-top .oy-nav-menu { display: flex; flex: 1; min-width: 0; overflow-x: auto; }
|
|
19
|
-
.oy-nav-top .oy-nav-item
|
|
17
|
+
.oy-nav-top.is-floating .oy-nav-header { position: sticky; top: 16px; z-index: 10; margin: 0 16px; border: 1px solid var(--pod-nav-sub-divider-color, var(--color-line1-1, #ddd)); border-radius: var(--pod-shell-lshape-border-radius, 16px); box-shadow: var(--pod-nav-popup-shadow, 0 8px 24px rgba(0,0,0,.12)); }
|
|
18
|
+
.oy-nav-top .oy-nav-menu { display: flex; flex: 1; min-width: 0; gap: var(--pod-nav-menu-gap, 8px); padding: calc(var(--pod-nav-menu-gap, 8px) / 2); overflow-x: auto; }
|
|
19
|
+
.oy-nav-top .oy-nav-menu .oy-nav-item { min-height: var(--pod-nav-top-tab-height, 40px); padding: var(--pod-nav-top-tab-item-padding, 0 12px); max-width: var(--pod-nav-top-tab-item-max-width, 240px); }
|
|
20
|
+
.oy-nav-top .oy-nav-menu .oy-nav-item > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
20
21
|
.oy-nav-top .oy-nav-toggle { display: none; margin-left: auto; }
|
|
21
22
|
@media (max-width: 767px) {
|
|
22
23
|
.oy-nav-top .oy-nav-header { gap: var(--pod-nav-logo-gap, 8px); }
|
package/package.json
CHANGED
package/yida-skills/SKILL.md
CHANGED
|
@@ -195,9 +195,9 @@ description: >
|
|
|
195
195
|
- 不把 OpenYida 业务中间文件写到仓库根目录或系统临时目录。
|
|
196
196
|
|
|
197
197
|
20. **报表和可视化先分流**:标准统计与原生报表用 `yida-report`;定制图表页面默认用 `yida-rechart`;只有明确 ECharts、维护旧 ECharts 页面或复杂 option 超出 Recharts 能力时用 `yida-chart`。
|
|
198
|
-
21. **应用主题只有一份**:涉及应用蓝图、页面视觉、应用主题色、品牌色、全局换肤或 `--color-brand1-*` 时先读 `yida-design
|
|
198
|
+
21. **应用主题只有一份**:涉及应用蓝图、页面视觉、应用主题色、品牌色、全局换肤或 `--color-brand1-*` 时先读 `yida-design`。导航、应用框架、提交/编辑表单、记录详情和自定义页面按[整体主题规则](skills/yida-design/references/application-theme-consistency.md#导航与应用框架)一起设计。`app-theme.css` 只在应用级统一配置,各类页面消费同一份主题 token。严禁在页面级重复写入、同步或向上层注入主题样式;`YidaCodeCanvas` 源码只在 `YidaComp` 内消费现有主题 token。
|
|
199
199
|
22. **默认完成即停止**:完整应用默认以资源发布成功、轻量导航排序完成、示例数据就绪并输出一组有明确名称的应用入口与业务交付总结为 doneWhen;截图、精细导航整理和额外深读属于 optionalAfterDone,除非用户明确要求。
|
|
200
|
-
23. **说清楚做什么**:对话、任务列表、步骤标题、进度和提问面向非技术用户。用日常用语和短句,一句话说一件事,先说结果或动作,再补必要原因。功能写谁能做什么;进度写当前动作和下一步;失败写问题、影响和处理办法;待核实的结果如实说明。文件名、路径、技能名和调用方式留在内部执行。PRD 业务说明、HTML 和消息使用功能与体验描述;接口参数、配置键值及内部 ID 留在 Agent 实施交接,遵循[用户可见表达契约](skills/yida-design/references/ask-human-interaction-contract.md)。交付卡片的 `description` 写 2-3 句业务交付总结、核验结果与剩余事项,卡片包含一组“应用访问入口”;宿主没有交付工具时,在最终回复中给出相同内容。不得把需求信息文件、PRD、视觉设计、build manifest、资源清单、Schema
|
|
200
|
+
23. **说清楚做什么**:对话、任务列表、步骤标题、进度和提问面向非技术用户。用日常用语和短句,一句话说一件事,先说结果或动作,再补必要原因。功能写谁能做什么;进度写当前动作和下一步;失败写问题、影响和处理办法;待核实的结果如实说明。文件名、路径、技能名和调用方式留在内部执行。PRD 业务说明、HTML 和消息使用功能与体验描述;接口参数、配置键值及内部 ID 留在 Agent 实施交接,遵循[用户可见表达契约](skills/yida-design/references/ask-human-interaction-contract.md)。交付卡片的 `description` 写 2-3 句业务交付总结、核验结果与剩余事项,卡片包含一组“应用访问入口”;宿主没有交付工具时,在最终回复中给出相同内容。不得把需求信息文件、PRD、视觉设计、build manifest、资源清单、Schema 或每个表单/流程/报表分别登记成用户可见交付物;不分前后台或包含业务后台时,给出经验证的业务页面入口,前台页面经 PRD 标记为 `standalone` 且导航配置回读通过时给独立业务入口;完整应用默认再提供开发者管理后台 `adminUrl`,不因云端登录方式省略;明确仅业务后台时交付业务后台与开发者管理后台,不追加前台;明确仅前台时不追加业务后台,仍交付前台与开发者管理后台两个地址。有前后台时交付前台、业务后台、开发者管理后台三个地址;不分前后台时交付系统入口与开发者管理后台。用户明确排除的入口和单页任务范围优先。
|
|
201
201
|
24. **任务复盘沉淀**:用户多次纠正、平台接口假成功、页面骨架共性质量问题、线上回读验收方法、一次性脚本可产品化等情况,完成前判断是否需要沉淀到 CLI、测试或 skill。
|
|
202
202
|
|
|
203
203
|
常见问题见 [常见问题解决方案](references/execution-rules.md)。
|
|
@@ -192,6 +192,10 @@ this.utils.yida.saveFormData({
|
|
|
192
192
|
});
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
+
> ⚠️ **匿名态限制**:该 API 与 `window.__OPENYIDA_YIDA_API__.saveFormData` 依赖登录态。页面运行在公开链接 `/o/...`(`FREELOGIN` / `loginUser.userId === 'FREEUSER'`)时,即使桥对象存在,调用 `saveFormData` 仍会返回 `LOGIN FAILED`;必须优先改走 `window.pageConfig.RECEIPT_SAVE_FORM_DATA` 网关,并同时满足显示页 `isOpen === 'y'`、目标表单 `isOpen === 'y'`、目标表单 DEFAULT 权限包含 `FREE_LOGIN` 三项门禁。
|
|
196
|
+
>
|
|
197
|
+
> 匿名网关使用 `POST application/x-www-form-urlencoded` 和 `X-Requested-With: XMLHttpRequest`,CSRF 优先读取 `window.g_config._csrf_token` 并作为 `_csrf_token` 参数传递。`value` 必须是字段数组,每项使用 `{ componentName, fieldId, fieldData: { value } }`;`componentName` 从目标表单 Schema 获取,标准名称见[字段定义 JSON 指南](../skills/yida-create-form-page/references/field-definition-guide.md#支持的字段类型),`fieldData.value` 的值结构见[数据格式指南](../skills/yida-data-management/references/data-format-guide.md#常见字段值格式)。这些通用字段文档不代表对应组件均支持匿名提交;匿名能力边界以公开页面实际限制和实例回读为准。不得把字段 ID 放进 `fieldData`;网关返回 `success: true` 和 `formInstId` 仍可能创建空记录,验收必须按该实例 ID 回读代表性字段值。完整分流和请求契约见 [`yida-canvas-custom-page/references/data-bridge-guide.md`](../skills/yida-canvas-custom-page/references/data-bridge-guide.md) 的"公开访问(匿名态)提交"章节。
|
|
198
|
+
|
|
195
199
|
---
|
|
196
200
|
|
|
197
201
|
### updateFormData
|
|
@@ -7,15 +7,23 @@ description: 创建完整宜搭应用,或补齐已有应用时使用。先确
|
|
|
7
7
|
|
|
8
8
|
完整应用编排技能。它负责把一次“创建/搭建/补齐应用”的需求拆成资源解析、产品设计、资源落地、页面发布和结果输出。全局 CLI、ID、存储、发布和输出规则以主入口 `SKILL.md` 为准;按步骤执行该步骤所需 `use_skill(...)`。
|
|
9
9
|
|
|
10
|
-
完整应用交付默认保留开发者管理后台(CLI `adminUrl
|
|
10
|
+
完整应用交付默认保留开发者管理后台(CLI `adminUrl`),与业务后台区分:有前后台时共三个地址;仅前台、仅业务后台或不分前后台时共两个地址。登录态来源不影响 `/admin` 的交付;单页任务保持单页范围,用户明确排除的入口优先。详见 Step 9。
|
|
11
11
|
|
|
12
12
|
## 模式入口(先按这里路由)
|
|
13
13
|
|
|
14
14
|
首次需求澄清优先进入 Step 2 的 2.0;首问前不执行 Step 1 的环境和资源预检,已有明确资源引用作为需求上下文保留。澄清完成后再按需核验资源、补齐规划输入。
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
已有任务先按 [续做与恢复](#续做与恢复) 定位当前阶段。
|
|
17
17
|
|
|
18
|
-
Plan
|
|
18
|
+
需求确认不能替代当前 Plan revision 的批准或工具权限确认。`intake.designMode` 沿用用户最后一次明确选择,其他问题的回答只更新对应字段。Plan 收到与当前 revision 匹配的 `confirm_build` 后进入资源实施;选择 `continue_editing` 时修改当前计划,生成新 revision 后再确认。素材进度更新不改变已有的需求确认和模式选择。
|
|
19
|
+
|
|
20
|
+
Plan 规划阶段的工作流入口只读取 `workflow/step-1-resource-context.md`、`workflow/step-2-design.md` 和精确路径 `workflow/plan/workflow.md`;后者已经包含完整 Plan 入口。禁止用 Glob 查找 Plan 文件,也不要额外读取 `workflow/plan/step-1-understand.md` 或 `workflow/plan/step-2-confirm.md`。此读取范围不限制确认后的实施、验收和交付,各阶段按需读取对应 workflow。Plan 确认恢复后,若 `explicitScope.allowInferredResources=false`,直接读取 `workflow/step-4-forms-processes.md` 实施范围内资源,不再重读 Step 1、调用 list-forms 或做应用设置预检。
|
|
21
|
+
|
|
22
|
+
## 续做与恢复
|
|
23
|
+
|
|
24
|
+
先判断本轮是否已有有效需求确认:历史回答或同任务 `intake.confirmed=true` 覆盖当前范围且未被撤回时,复用答案、PRD、设计和资源回执,从未完成阶段继续。刷新、恢复、压缩或用户说“继续”不触发重新首问;仅应用已存在也不代表需求已确认。状态不明时先只读核对当前任务材料;范围变更、材料缺失或冲突按 [需求分析的适用阶段与续做](../yida-requirement-analysis/workflow/prepare-brief.md#适用阶段与续做) 处理。
|
|
25
|
+
|
|
26
|
+
已发布但尚未完成本轮导航、验收或交付时,按缺项进入 [Step 8](workflow/step-8-publish-navigation.md) 或 [Step 9](workflow/step-9-output-finish.md),不返回需求首问。发布成功回执不能替代实际验收。
|
|
19
27
|
|
|
20
28
|
## 执行步骤(进行时展示给用户)
|
|
21
29
|
|
|
@@ -86,7 +94,7 @@ Plan 模式只读取 `workflow/step-1-resource-context.md`、`workflow/step-2-de
|
|
|
86
94
|
|
|
87
95
|
1. **资源判断以 Step 1 为准**:已有 app/page/form/process 默认复用;只有用户明确从零创建,或目标缺失且本轮允许创建时,才创建缺失资源。
|
|
88
96
|
2. **显式目标优先**:本轮用户给出的 `appType`、`formUuid`、URL、页面名或流程标识,优先级高于绑定上下文和历史缓存;同级冲突或无法唯一识别时才问用户。
|
|
89
|
-
3. **产品与视觉分工**:`yida-requirement-analysis` 先统一整理用户需求;业务目标、资源蓝图、页面结构、导航顺序和验收标准由 `yida-prd` 写入 `prd.md
|
|
97
|
+
3. **产品与视觉分工**:`yida-requirement-analysis` 先统一整理用户需求;业务目标、资源蓝图、页面结构、导航顺序和验收标准由 `yida-prd` 写入 `prd.md`;`yida-design` 将导航、应用框架、表单、详情和自定义页面的主题 token、布局、材质、形状、密度与状态规则写入 `design.md`。两份文件校验通过前不得创建资源。
|
|
90
98
|
4. **阶段技能按需加载**:进入应用壳、表单、流程、页面、发布、数据写入等阶段时,才执行对应 `use_skill(...)`。
|
|
91
99
|
5. **真实 ID 和真实数据**:不编造 `appType`、`formUuid`、`fieldId`、`processCode`、`reportId`。无显式窄范围的完整应用默认给核心普通表单写入 1-3 条业务化 seed records 并 query 抽查;`explicitScope.allowInferredResources=false` 时不得增加未点名的 seed records 或页面。
|
|
92
100
|
6. **自定义页面开发技能固定**:完整应用页面源码按 Step 7 执行;管理端仅需原生视图时不创建 display 首页。前后台按 [访问态入口契约](references/entry-navigation.md) 分别规划菜单及分组顺序、默认页面、首屏任务和权限;平台导航与自定义导航都要落实排序并验收。
|