openyida 2026.9.15 → 2026.9.16
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 +8 -3
- package/lib/app/canvas-compile.js +2 -0
- package/lib/app/canvas-theme-guard.js +117 -0
- package/lib/app/create-form.js +4 -4
- package/lib/app/services/form-compiler.js +4 -4
- package/lib/app/theme-from-design.js +8 -2
- package/lib/app/theme-presets.js +1 -1
- package/lib/asset/ai-image.js +40 -6
- package/lib/asset/asset-cmd.js +8 -2
- package/lib/asset/asset-plan.js +46 -4
- package/lib/asset/asset-resolve.js +7 -3
- package/lib/core/agent-capabilities.js +10 -1
- package/lib/core/command-manifest.js +113 -16
- package/lib/core/locales/en.js +10 -2
- package/lib/core/locales/zh.js +10 -2
- package/lib/core/redact.js +15 -10
- package/lib/core/sample.js +6 -10
- package/lib/design-plan/design-plan.js +7 -2
- package/lib/design-plan/entry-navigation.js +83 -0
- package/lib/design-plan/init.js +73 -10
- package/lib/design-plan/materialize.js +103 -67
- package/lib/design-plan/normalize.js +23 -1
- package/lib/design-plan/patch.js +24 -4
- package/lib/design-plan/validate.js +30 -5
- package/lib/samples/openyida-scaffold/canvas-form-drawer.canvas.jsx +14 -85
- package/lib/samples/openyida-scaffold/canvas-nav/data.jsx +84 -17
- package/lib/samples/yida-canvas-table-form/table-form-batch-submit.canvas.jsx +9 -83
- package/lib/samples/yida-rechart/trend-combo.canvas.jsx +14 -87
- package/package.json +2 -1
- package/scripts/postinstall.js +3 -3
- package/yida-skills/SKILL.md +3 -3
- package/yida-skills/skills/yida-app/SKILL.md +9 -9
- package/yida-skills/skills/yida-app/references/entry-navigation.md +150 -0
- package/yida-skills/skills/yida-app/workflow/parallel-work.md +10 -1
- package/yida-skills/skills/yida-app/workflow/plan/step-1-understand.md +1 -1
- package/yida-skills/skills/yida-app/workflow/plan/step-2-confirm.md +1 -1
- package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +4 -4
- package/yida-skills/skills/yida-app/workflow/plan/workflow.md +6 -6
- package/yida-skills/skills/yida-app/workflow/step-1-resource-context.md +4 -2
- package/yida-skills/skills/yida-app/workflow/step-2-design.md +15 -5
- package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +4 -2
- package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +9 -1
- package/yida-skills/skills/yida-app/workflow/step-6-main-page.md +6 -4
- package/yida-skills/skills/yida-app/workflow/step-7-page-code.md +6 -4
- package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +13 -4
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +18 -12
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +32 -42
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +5 -5
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-theme-provider.md +45 -50
- package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +2 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +13 -12
- package/yida-skills/skills/yida-canvas-custom-page/scripts/build-canvas-theme.js +19 -2
- package/yida-skills/skills/yida-create-form-page/SKILL.md +23 -15
- package/yida-skills/skills/yida-design/SKILL.md +6 -2
- package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +43 -25
- package/yida-skills/skills/yida-design/references/asset-workflow.md +4 -2
- package/yida-skills/skills/yida-design/references/design-mode.md +2 -2
- package/yida-skills/skills/yida-design/references/navigation-decision.md +42 -0
- package/yida-skills/skills/yida-design/references/theme/app-custom-theme-template.css +2 -2
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/SKILL.md +4 -4
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-compact-schema.md +16 -11
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +24 -150
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-design.md +6 -2
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-theme-selection.md +17 -13
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/render_build_plan.py +25 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/validate_design_themes.py +29 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/README.md +13 -1
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-media-grid.md +86 -87
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-modular-clarity.md +84 -102
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-structured-clarity.md +73 -97
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ambient-halo-layered.md +91 -95
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/basic-tokens.json +101 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-focus-layered.md +82 -110
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-luminous-modular.md +86 -94
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/deep-stage-duotone.md +91 -98
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/hairline-runway-clarity.md +88 -99
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/high-contrast-modular.md +84 -109
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/interlocked-vivid-modules.md +83 -88
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/matrix-grid-focus.md +83 -91
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/media-rail-inspector.md +87 -85
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mist-layered-signal.md +88 -95
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/modular-rail-signal.md +90 -101
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mono-grid-signal.md +87 -99
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ribbon-ledger-lift.md +85 -91
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/segmented-meter-clarity.md +84 -92
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/split-rail-precision.md +85 -87
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/status-framed-media-grid.md +82 -85
- package/yida-skills/skills/yida-design/workflow/output-design.md +1 -1
- package/yida-skills/skills/yida-design/workflow/step-1-read-brief.md +1 -1
- package/yida-skills/skills/yida-image-assets/SKILL.md +48 -51
- package/yida-skills/skills/yida-image-assets/references/manifest-contract.md +50 -54
- package/yida-skills/skills/yida-image-assets/references/source-policy.md +14 -15
- package/yida-skills/skills/yida-nav-group/SKILL.md +4 -2
- package/yida-skills/skills/yida-nav-shell/SKILL.md +23 -6
- package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +6 -6
- package/yida-skills/skills/yida-page-config/SKILL.md +3 -3
- package/yida-skills/skills/yida-prd/SKILL.md +2 -1
- package/yida-skills/skills/yida-prd/references/app/blueprint.md +1 -1
- package/yida-skills/skills/yida-prd/references/app/navigation-patterns.md +6 -3
- package/yida-skills/skills/yida-prd/references/plan-content.md +4 -0
- package/yida-skills/skills/yida-prd/workflow/output-prd.md +13 -7
- package/yida-skills/skills/yida-prd/workflow/plan-business.md +3 -3
- package/yida-skills/skills/yida-prd/workflow/step-2-information-architecture.md +7 -5
- package/yida-skills/skills/yida-publish-page/SKILL.md +1 -0
- package/yida-skills/skills/yida-requirement-analysis/SKILL.md +10 -39
- package/yida-skills/skills/yida-requirement-analysis/references/experience-groups.md +23 -0
- package/yida-skills/skills/yida-requirement-analysis/references/handoff.md +64 -0
- package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +32 -75
- package/yida-skills/skills-index.json +6 -6
- package/yida-skills/skills/yida-design/references/app/blueprint.md +0 -70
- package/yida-skills/skills/yida-design/references/app/navigation-patterns.md +0 -43
- package/yida-skills/skills/yida-design/references/app/role-journey.md +0 -30
- package/yida-skills/skills/yida-design/workflow/output-prd.md +0 -185
package/README.md
CHANGED
|
@@ -396,11 +396,12 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
396
396
|
| `openyida app-list [--type managed\|created] [--page N] [--size N]` | Page through apps I manage or created |
|
|
397
397
|
| `openyida corp-efficiency [overview\|details\|detail\|groups\|notify] [options] [--open\|--no-open]` | Query enterprise efficiency overview and detail reports |
|
|
398
398
|
| `openyida create-app "<name>"\|--name <name> [options] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a Yida app |
|
|
399
|
+
| `openyida design-plan catalog [--json]` | List available planning themes and page patterns |
|
|
399
400
|
| `openyida design-plan init <requirement-brief.json> [--theme-id <id>] [--output-dir <dir>] [--json]` | Initialize a plan draft from confirmed requirements |
|
|
400
401
|
| `openyida design-plan preview <build-plan.json> --part-file <module.json> [--json]` | Update plan drafts by module |
|
|
401
402
|
| `openyida design-plan materialize <build-plan.json> [--from-preview \| --business-file <json> --visual-file <json>] [--output-dir <dir>] [--check] [--json]` | Generate and validate design-plan artifacts from build-plan.json |
|
|
402
|
-
| `openyida design-plan patch <build-plan.json> --set <path=value> [--set <path=value> ...] [--materialize] [--output-dir <dir>] [--json]` | Patch a design plan
|
|
403
|
-
| `openyida update-app <appType> [--name "..."] [--theme-file <css>] [--nav-theme light\|dark\|white\|gray] [--logo-source appIcon\|customImage] [--layout side\|top\|l_shape] [--hide-app-nav\|--show-app-nav]` | Update app info |
|
|
403
|
+
| `openyida design-plan patch <build-plan.json> --set <path=value> [--set <path=value> ...] [--materialize] [--output-dir <dir>] [--json]` | Patch a design plan; asset progress preserves existing approval |
|
|
404
|
+
| `openyida update-app <appType> [--name "..."] [--desc "..."] [--icon <name>] [--icon-color <color>] [--colour <key>] [--theme-color <color>] [--theme-file <css>] [--nav-theme light\|dark\|white\|gray] [--logo-source appIcon\|customImage] [--layout side\|top\|l_shape] [--hide-app-nav\|--show-app-nav]` | Update app info |
|
|
404
405
|
| `openyida app-online <appType> [--to-ding-app-center] [--show-app-center]` | Enable a Yida app |
|
|
405
406
|
| `openyida app-offline <appType> [--to-ding-app-center] [--show-app-center]` | Disable a Yida app |
|
|
406
407
|
| `openyida nav-group <list\|create\|rename\|delete\|move\|order\|auto-order\|hide\|show> <appType> ...` | Manage app sidebar navigation groups |
|
|
@@ -649,10 +650,14 @@ npx clawhub@latest install nicky1108/yida-app
|
|
|
649
650
|
```bash
|
|
650
651
|
git clone https://github.com/openyida/openyida.git
|
|
651
652
|
cd openyida
|
|
652
|
-
|
|
653
|
+
nvm install
|
|
654
|
+
nvm use
|
|
655
|
+
npm ci --ignore-scripts
|
|
653
656
|
npm run check:ci
|
|
654
657
|
```
|
|
655
658
|
|
|
659
|
+
Use the Node.js version in `.nvmrc` to match CI validation. If you do not use nvm, install that version directly.
|
|
660
|
+
|
|
656
661
|
Useful checks:
|
|
657
662
|
|
|
658
663
|
| Command | Purpose |
|
|
@@ -50,6 +50,7 @@ const {
|
|
|
50
50
|
} = require('./form-detail-link-guard');
|
|
51
51
|
const { t } = require('../core/i18n');
|
|
52
52
|
const { assertInlineCssStructure } = require('./inline-css-guard');
|
|
53
|
+
const { assertCanvasThemeStructure } = require('./canvas-theme-guard');
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
56
|
* 依赖 → window 别名白名单,用于把 import 改写到运行时全局对象。
|
|
@@ -967,6 +968,7 @@ function compileCanvasLocal(source, options = {}) {
|
|
|
967
968
|
assertNoBareDependencyGlobals(source, options);
|
|
968
969
|
assertNoManualDependencyGlobals(source, options);
|
|
969
970
|
assertInlineCssStructure(source, options);
|
|
971
|
+
assertCanvasThemeStructure(source, options);
|
|
970
972
|
|
|
971
973
|
const importedModules = extractImportedModules(source);
|
|
972
974
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Babel = require('@babel/standalone');
|
|
4
|
+
const { CliError } = require('../core/cli-error');
|
|
5
|
+
const { t } = require('../core/i18n');
|
|
6
|
+
|
|
7
|
+
// Check the maintained theme contract without executing business code.
|
|
8
|
+
// Dynamic component factories and runtime branches still need browser verification.
|
|
9
|
+
function assertCanvasThemeStructure(source, options = {}) {
|
|
10
|
+
if (!/CanvasThemeProvider|useCanvasThemeContext|CanvasThemeContext/.test(source)) { return; }
|
|
11
|
+
let ast;
|
|
12
|
+
try {
|
|
13
|
+
ast = Babel.packages.parser.parse(source, { sourceType: 'module', plugins: ['jsx', 'typescript'] });
|
|
14
|
+
} catch { return; } // Let the compiler report syntax errors.
|
|
15
|
+
const traverse = Babel.packages.traverse.default || Babel.packages.traverse;
|
|
16
|
+
const functions = new Map();
|
|
17
|
+
let program;
|
|
18
|
+
let defaultExport;
|
|
19
|
+
const fail = (kind, node) => {
|
|
20
|
+
const line = node?.loc?.start.line || 1;
|
|
21
|
+
throw new CliError(t(`publish.canvas_theme_${kind}`, line), {
|
|
22
|
+
code: 'OPENYIDA_CANVAS_THEME_PROVIDER_INVALID',
|
|
23
|
+
details: { stage: 'canvas_compile', sourcePath: options.sourcePath || '', line, issueType: kind },
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
traverse(ast, {
|
|
28
|
+
Program(p) { program = p; },
|
|
29
|
+
ExportDefaultDeclaration(p) { defaultExport = p.get('declaration'); },
|
|
30
|
+
Function(p) {
|
|
31
|
+
const name = p.node.id?.name || (p.parentPath.isVariableDeclarator() ? p.parentPath.node.id.name : '');
|
|
32
|
+
functions.set(p.node, { name, calls: [], renders: [], hooks: [] });
|
|
33
|
+
},
|
|
34
|
+
VariableDeclarator(p) {
|
|
35
|
+
if (p.node.id.name === 'CanvasThemeContext' && p.getFunctionParent()) {
|
|
36
|
+
fail('context_scope', p.node);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
function resolveFunction(p, seen = new Set()) {
|
|
42
|
+
if (!p?.node || seen.has(p.node)) { return null; }
|
|
43
|
+
seen.add(p.node);
|
|
44
|
+
if (p.isFunction()) { return p.node; }
|
|
45
|
+
if (p.isVariableDeclarator()) { return resolveFunction(p.get('init'), seen); }
|
|
46
|
+
if (p.isIdentifier() || p.isJSXIdentifier()) {
|
|
47
|
+
return resolveFunction(p.scope.getBinding(p.node.name)?.path, seen);
|
|
48
|
+
}
|
|
49
|
+
// React.memo/forwardRef retain the wrapped component's theme requirements.
|
|
50
|
+
if (p.isCallExpression()) {
|
|
51
|
+
const callee = p.node.callee;
|
|
52
|
+
const name = callee.type === 'Identifier' ? callee.name : callee.property?.name;
|
|
53
|
+
if (['memo', 'forwardRef'].includes(name)) { return resolveFunction(p.get('arguments.0'), seen); }
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
traverse(ast, {
|
|
59
|
+
CallExpression(p) {
|
|
60
|
+
const owner = functions.get(p.getFunctionParent()?.node);
|
|
61
|
+
const target = resolveFunction(p.get('callee'));
|
|
62
|
+
const targetName = functions.get(target)?.name || p.node.callee.name;
|
|
63
|
+
if (targetName === 'useCanvasThemeContext') {
|
|
64
|
+
if (!owner) { fail('root_hook', p.node); }
|
|
65
|
+
owner.hooks.push(p.node);
|
|
66
|
+
}
|
|
67
|
+
if (!owner) { return; }
|
|
68
|
+
if (target) { owner.calls.push(target); }
|
|
69
|
+
const callee = p.node.callee;
|
|
70
|
+
if (callee.type === 'MemberExpression' && callee.object.name === 'React' && callee.property.name === 'createElement') {
|
|
71
|
+
const component = resolveFunction(p.get('arguments.0'));
|
|
72
|
+
if (component) { owner.renders.push(component); }
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
JSXOpeningElement(p) {
|
|
76
|
+
const owner = functions.get(p.getFunctionParent()?.node);
|
|
77
|
+
const component = resolveFunction(p.get('name'));
|
|
78
|
+
if (owner && component) { owner.renders.push(component); }
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const entry = resolveFunction(program.scope.getBinding('YidaComp')?.path) || resolveFunction(defaultExport);
|
|
83
|
+
if (!entry) { return; }
|
|
84
|
+
|
|
85
|
+
// Calling a hook before returning a Provider cannot read that Provider.
|
|
86
|
+
const directSeen = new Set();
|
|
87
|
+
function checkEntryCalls(node) {
|
|
88
|
+
if (directSeen.has(node)) { return; }
|
|
89
|
+
directSeen.add(node);
|
|
90
|
+
const info = functions.get(node);
|
|
91
|
+
if (info.hooks.length) { fail('root_hook', info.hooks[0]); }
|
|
92
|
+
info.calls.forEach(checkEntryCalls);
|
|
93
|
+
}
|
|
94
|
+
checkEntryCalls(entry);
|
|
95
|
+
|
|
96
|
+
const seen = new Set();
|
|
97
|
+
let providerMounted = false;
|
|
98
|
+
let firstHook;
|
|
99
|
+
function visit(node) {
|
|
100
|
+
if (seen.has(node)) { return; }
|
|
101
|
+
seen.add(node);
|
|
102
|
+
const info = functions.get(node);
|
|
103
|
+
firstHook = firstHook || info.hooks[0];
|
|
104
|
+
info.renders.forEach(child => {
|
|
105
|
+
if (functions.get(child).name === 'CanvasThemeProvider') { providerMounted = true; }
|
|
106
|
+
visit(child);
|
|
107
|
+
});
|
|
108
|
+
info.calls.forEach(visit);
|
|
109
|
+
}
|
|
110
|
+
visit(entry);
|
|
111
|
+
const hasProvider = [...functions.values()].some(info => info.name === 'CanvasThemeProvider');
|
|
112
|
+
if (!providerMounted && (hasProvider || firstHook)) {
|
|
113
|
+
fail('provider_missing', firstHook || entry);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = { assertCanvasThemeStructure };
|
package/lib/app/create-form.js
CHANGED
|
@@ -1719,7 +1719,7 @@ function buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, t
|
|
|
1719
1719
|
id: nextNodeId(),
|
|
1720
1720
|
props: {
|
|
1721
1721
|
contentBgColor: 'white',
|
|
1722
|
-
pageStyle: { backgroundColor: '
|
|
1722
|
+
pageStyle: { backgroundColor: 'transparent' },
|
|
1723
1723
|
contentMargin: '20',
|
|
1724
1724
|
contentPadding: '20',
|
|
1725
1725
|
showTitle: false,
|
|
@@ -1742,7 +1742,7 @@ function buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, t
|
|
|
1742
1742
|
labelWeightMobile: 'normal',
|
|
1743
1743
|
},
|
|
1744
1744
|
condition: true,
|
|
1745
|
-
css: 'body{background-color
|
|
1745
|
+
css: 'body{background-color:transparent}',
|
|
1746
1746
|
methods: {
|
|
1747
1747
|
__initMethods__: {
|
|
1748
1748
|
type: 'js',
|
|
@@ -1941,7 +1941,7 @@ function buildEmptyFormSchema() {
|
|
|
1941
1941
|
id: nextNodeId(),
|
|
1942
1942
|
props: {
|
|
1943
1943
|
contentBgColor: 'white',
|
|
1944
|
-
pageStyle: { backgroundColor: '
|
|
1944
|
+
pageStyle: { backgroundColor: 'transparent' },
|
|
1945
1945
|
contentMargin: '20',
|
|
1946
1946
|
contentPadding: '20',
|
|
1947
1947
|
showTitle: false,
|
|
@@ -1952,7 +1952,7 @@ function buildEmptyFormSchema() {
|
|
|
1952
1952
|
contentBgColorMobile: 'white',
|
|
1953
1953
|
},
|
|
1954
1954
|
condition: true,
|
|
1955
|
-
css: 'body{background-color
|
|
1955
|
+
css: 'body{background-color:transparent}',
|
|
1956
1956
|
methods: {
|
|
1957
1957
|
__initMethods__: {
|
|
1958
1958
|
type: 'js',
|
|
@@ -1615,7 +1615,7 @@ function buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, t
|
|
|
1615
1615
|
id: nextNodeId(),
|
|
1616
1616
|
props: {
|
|
1617
1617
|
contentBgColor: 'white',
|
|
1618
|
-
pageStyle: { backgroundColor: '
|
|
1618
|
+
pageStyle: { backgroundColor: 'transparent' },
|
|
1619
1619
|
contentMargin: '20',
|
|
1620
1620
|
contentPadding: '20',
|
|
1621
1621
|
showTitle: false,
|
|
@@ -1638,7 +1638,7 @@ function buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, t
|
|
|
1638
1638
|
labelWeightMobile: 'normal',
|
|
1639
1639
|
},
|
|
1640
1640
|
condition: true,
|
|
1641
|
-
css: 'body{background-color
|
|
1641
|
+
css: 'body{background-color:transparent}',
|
|
1642
1642
|
methods: {
|
|
1643
1643
|
__initMethods__: {
|
|
1644
1644
|
type: 'js',
|
|
@@ -1833,7 +1833,7 @@ function buildEmptyFormSchema() {
|
|
|
1833
1833
|
id: nextNodeId(),
|
|
1834
1834
|
props: {
|
|
1835
1835
|
contentBgColor: 'white',
|
|
1836
|
-
pageStyle: { backgroundColor: '
|
|
1836
|
+
pageStyle: { backgroundColor: 'transparent' },
|
|
1837
1837
|
contentMargin: '20',
|
|
1838
1838
|
contentPadding: '20',
|
|
1839
1839
|
showTitle: false,
|
|
@@ -1844,7 +1844,7 @@ function buildEmptyFormSchema() {
|
|
|
1844
1844
|
contentBgColorMobile: 'white',
|
|
1845
1845
|
},
|
|
1846
1846
|
condition: true,
|
|
1847
|
-
css: 'body{background-color
|
|
1847
|
+
css: 'body{background-color:transparent}',
|
|
1848
1848
|
methods: {
|
|
1849
1849
|
__initMethods__: {
|
|
1850
1850
|
type: 'js',
|
|
@@ -76,9 +76,15 @@ function applyDesignTokens(template, markdown, previousMarkdown) {
|
|
|
76
76
|
let body = root[1].replace(declarations, (line, name, value) => line.replace(value, () => changed[name] || replaceBrand(value)));
|
|
77
77
|
const extra = Object.entries(changed).filter(([name]) => !(name in original));
|
|
78
78
|
body += extra.map(([name, value]) => ` ${name}: ${value};\n`).join('');
|
|
79
|
-
|
|
80
|
-
const tail = template.slice(root.index + root[0].length).replace(declarations,
|
|
79
|
+
let tail = template.slice(root.index + root[0].length).replace(declarations,
|
|
81
80
|
(line, name, value) => line.replace(value, () => replaceBrand(value)));
|
|
81
|
+
// Platform navigation modes bind independently; the root shell remains the custom-page default.
|
|
82
|
+
if (changed['--pod-shell-theme-bg-color']) {
|
|
83
|
+
tail = tail.replace(/(\.pod-premium\.nav-(light|dark)\s*\{)([^}]*)(\})/g,
|
|
84
|
+
(block, selector, tone, body, close) => selector + body.replace(declarations,
|
|
85
|
+
(line, name, value) => ['--pod-shell-theme-bg-color', '--pod-page-header-bg-color'].includes(name)
|
|
86
|
+
? line.replace(value, `var(--color-brand1-${tone === 'dark' ? 5 : 3})`) : line) + close);
|
|
87
|
+
}
|
|
82
88
|
const css = `${template.slice(0, root.index)}:root {${body}}${tail}`;
|
|
83
89
|
validateThemeCssContent(css);
|
|
84
90
|
extractThemeColor(css);
|
package/lib/app/theme-presets.js
CHANGED
|
@@ -122,7 +122,7 @@ function assertPresetThemeKey(themeKey) {
|
|
|
122
122
|
throw new Error(
|
|
123
123
|
`Unsupported theme: ${themeKey}. Use one of: ${formatPresetThemeKeys()}. ` +
|
|
124
124
|
'For custom colors or application theme token profiles that are not platform --theme keys, ' +
|
|
125
|
-
'upload an application theme CSS file with update-app --theme-file
|
|
125
|
+
'upload an application theme CSS file with update-app --theme-file.'
|
|
126
126
|
);
|
|
127
127
|
}
|
|
128
128
|
|
package/lib/asset/ai-image.js
CHANGED
|
@@ -73,18 +73,52 @@ function getFreeStockLibraries() {
|
|
|
73
73
|
function getMaterialSourcingGuidance() {
|
|
74
74
|
return {
|
|
75
75
|
steps: [
|
|
76
|
-
'1.
|
|
77
|
-
'2.
|
|
78
|
-
'3.
|
|
79
|
-
'4.
|
|
80
|
-
'5.
|
|
76
|
+
'1. Plan 确认后、Fast 设计就绪后启动素材任务,同时推进应用、表单和无图页面;先复用用户图片和已完成位置。',
|
|
77
|
+
'2. 同一页面的图片位置也并发搜索,最多同时四个;每个位置选一张来源完整的图片并查看。',
|
|
78
|
+
'3. 第二轮只补第一轮失败的必需位置,换一张图片或使用设计允许的生成图。',
|
|
79
|
+
'4. 两轮结束后,可选位置采用设计允许的无图布局,必需位置保留缺口;已就绪页面直接继续。',
|
|
80
|
+
'5. 每页使用 asset resolve --page-id 并发下载上传,写入独立清单并交接该页。',
|
|
81
81
|
],
|
|
82
|
+
schedulingPolicy: {
|
|
83
|
+
owner: 'host_agent',
|
|
84
|
+
searchConcurrency: 4,
|
|
85
|
+
concurrencyScope: 'collection_run',
|
|
86
|
+
searchUnit: 'slot',
|
|
87
|
+
resumePolicy: 'reuse_final_slots_and_remaining_round_budget',
|
|
88
|
+
runAlongside: ['app_creation', 'form_creation', 'pages_without_images'],
|
|
89
|
+
waitAt: 'own_page_image_binding_and_acceptance',
|
|
90
|
+
resultWriter: 'one_per_page',
|
|
91
|
+
},
|
|
92
|
+
collectionPolicy: {
|
|
93
|
+
maxRoundsPerPage: 2,
|
|
94
|
+
imagesPerSlot: 1,
|
|
95
|
+
candidatesPerSlotPerRound: 1,
|
|
96
|
+
secondRound: 'failed_required_slots_only',
|
|
97
|
+
concurrency: 'independent_pages_and_assets',
|
|
98
|
+
roundOwner: 'host_agent',
|
|
99
|
+
pageSelector: '--page-id',
|
|
100
|
+
},
|
|
101
|
+
completionPolicy: {
|
|
102
|
+
resultSource: 'asset-manifests/<pageId>.json',
|
|
103
|
+
planApproval: 'reuse_existing_approval',
|
|
104
|
+
progressFields: ['visualStyle.forUser.assetStrategy.materialStatus', 'visualStyle.forUser.assetStrategy.missingAssets'],
|
|
105
|
+
nextStep: 'continue_ready_pages',
|
|
106
|
+
},
|
|
107
|
+
failurePolicy: {
|
|
108
|
+
attemptsPerCandidate: 1,
|
|
109
|
+
sourceUnavailable: 'switch_source_for_remaining_slots',
|
|
110
|
+
candidateFailed: 'replace_input',
|
|
111
|
+
exhausted: 'planned_optional_layout_or_required_gap',
|
|
112
|
+
retryCondition: 'within_two_rounds_or_explicit_request',
|
|
113
|
+
},
|
|
82
114
|
libraries: getFreeStockLibraries(),
|
|
83
115
|
rules: [
|
|
84
116
|
'不编造图片 URL;按 manifest 的页面状态继续,只使用当前页 materialStatus=final 的图片。',
|
|
85
117
|
'默认下载并上传宜搭图片附件;超过 20 MiB 保留原链接;不额外探测 URL,下载失败跳过并记录缺口。',
|
|
86
118
|
'联网搜图仅使用 Unsplash / Pexels。',
|
|
87
|
-
'
|
|
119
|
+
'401、403、429、反爬挑战或超时后,将该来源标记为本次采集不可用,其他页面和后续轮次使用其他来源。',
|
|
120
|
+
'每页最多两轮;每个位置每轮选一张,同一候选尝试一次,成功即结束。第二轮只补失败的必需位置;换来源、换工具或恢复上传仍计入本轮预算。',
|
|
121
|
+
'来源信息获取失败时换用信息齐全的候选;真实房源、商品和人员保持真实素材要求。',
|
|
88
122
|
],
|
|
89
123
|
};
|
|
90
124
|
}
|
package/lib/asset/asset-cmd.js
CHANGED
|
@@ -8,13 +8,13 @@ const { resolveAssets, ASSET_SOURCES } = require('./asset-resolve');
|
|
|
8
8
|
const { detectImageGenerator, getMaterialSourcingGuidance } = require('./ai-image');
|
|
9
9
|
const { throwCommandError, throwStatus, throwUsage } = require('../core/command-errors');
|
|
10
10
|
|
|
11
|
-
const RESOLVE_USAGE = '用法: openyida asset resolve (--input <manifest-draft.json> | --slot <slotId>=<路径或URL> ...) [--manifest <asset-manifest.json>] [--design <design.md>] [--source search|user|generated] [--app-type <appType>] [--upload-assets] [--offline] [--json]';
|
|
11
|
+
const RESOLVE_USAGE = '用法: openyida asset resolve (--input <manifest-draft.json> | --slot <slotId>=<路径或URL> ...) [--manifest <asset-manifest.json>] [--design <design.md>] [--page-id <pageId>] [--source search|user|generated] [--app-type <appType>] [--upload-assets] [--offline] [--json]';
|
|
12
12
|
|
|
13
13
|
function printHelp() {
|
|
14
14
|
console.log(`
|
|
15
15
|
用法:
|
|
16
16
|
openyida asset status [--offline] [--json]
|
|
17
|
-
openyida asset resolve (--input <manifest-draft.json> | --slot <slotId>=<路径或URL> ...) [--manifest <asset-manifest.json>] [--design <design.md>] [--source search|user|generated] [--app-type <appType>] [--upload-assets] [--offline] [--json]
|
|
17
|
+
openyida asset resolve (--input <manifest-draft.json> | --slot <slotId>=<路径或URL> ...) [--manifest <asset-manifest.json>] [--design <design.md>] [--page-id <pageId>] [--source search|user|generated] [--app-type <appType>] [--upload-assets] [--offline] [--json]
|
|
18
18
|
openyida asset sources [--json]
|
|
19
19
|
|
|
20
20
|
子命令:
|
|
@@ -23,6 +23,7 @@ function printHelp() {
|
|
|
23
23
|
|
|
24
24
|
图片不超过 20 MiB 时上传;超过限制的外链保留原地址。
|
|
25
25
|
--app-type 指定目标应用,省略时读取项目 config.json。
|
|
26
|
+
--page-id 只处理该页槽位;按页并发时,每页使用独立的 --manifest 文件。
|
|
26
27
|
--upload-assets 仅兼容旧命令,默认已上传;--offline 不联网、不上传。
|
|
27
28
|
sources 输出 Unsplash/Pexels 使用规则
|
|
28
29
|
`);
|
|
@@ -85,6 +86,10 @@ function parseArgs(args) {
|
|
|
85
86
|
parsed.usedOptions.push('--design');
|
|
86
87
|
parsed.design = args[++i] || '';
|
|
87
88
|
if (!parsed.design || parsed.design.startsWith('--')) {parsed.errors.push('ASSET_DESIGN_REQUIRED');}
|
|
89
|
+
} else if (arg === '--page-id') {
|
|
90
|
+
parsed.usedOptions.push('--page-id');
|
|
91
|
+
parsed.pageId = args[++i] || '';
|
|
92
|
+
if (!parsed.pageId.trim() || parsed.pageId.startsWith('--')) {parsed.errors.push('ASSET_PAGE_ID_REQUIRED');}
|
|
88
93
|
} else if (arg === '--manifest') {
|
|
89
94
|
parsed.usedOptions.push('--manifest');
|
|
90
95
|
parsed.manifest = args[++i] || '';
|
|
@@ -158,6 +163,7 @@ async function runResolve(options) {
|
|
|
158
163
|
|
|
159
164
|
const result = await resolveAssets(assets, {
|
|
160
165
|
assetStrategy,
|
|
166
|
+
pageId: options.pageId,
|
|
161
167
|
online: !options.offline,
|
|
162
168
|
appType: options.appType,
|
|
163
169
|
assetSource: options.source,
|
package/lib/asset/asset-plan.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
4
5
|
const { CliError } = require('../core/cli-error');
|
|
5
6
|
|
|
6
7
|
function invalid(code, details = {}) {
|
|
@@ -18,7 +19,7 @@ function nonempty(value) {
|
|
|
18
19
|
function minimumSize(asset) {
|
|
19
20
|
const match = typeof asset.minSize === 'string' && asset.minSize.match(/^(\d+)x(\d+)$/i);
|
|
20
21
|
if (asset.minSize !== undefined && !match) {
|
|
21
|
-
invalid('ASSET_INVALID_SIZE', { slotId: asset.slotId, minSize: asset.minSize });
|
|
22
|
+
invalid('ASSET_INVALID_SIZE', { slotId: asset.slotId, field: 'minSize', minSize: asset.minSize, expected: 'widthxheight string, e.g. 1600x900' });
|
|
22
23
|
}
|
|
23
24
|
const values = [asset.minWidth ?? 0, asset.minHeight ?? 0];
|
|
24
25
|
const declared = match ? [Number(match[1]), Number(match[2])] : [0, 0];
|
|
@@ -42,7 +43,7 @@ function readDesignAssetStrategy(file) {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
// The design owns page membership, required slots and minimum dimensions.
|
|
45
|
-
function prepareAssetPlan(assets, assetStrategy) {
|
|
46
|
+
function prepareAssetPlan(assets, assetStrategy, pageId) {
|
|
46
47
|
if (!Array.isArray(assets)) {invalid('ASSET_INPUT_INVALID', { field: 'assets' });}
|
|
47
48
|
const seen = new Set();
|
|
48
49
|
const entries = assets.map(asset => {
|
|
@@ -53,10 +54,15 @@ function prepareAssetPlan(assets, assetStrategy) {
|
|
|
53
54
|
minimumSize(asset);
|
|
54
55
|
return { ...asset, slotId };
|
|
55
56
|
});
|
|
56
|
-
if (assetStrategy === undefined) {
|
|
57
|
+
if (assetStrategy === undefined) {
|
|
58
|
+
if (pageId !== undefined) {invalid('ASSET_PAGE_REQUIRES_DESIGN', { pageId });}
|
|
59
|
+
return { entries, pages: [], assetStrategy: undefined };
|
|
60
|
+
}
|
|
57
61
|
if (!object(assetStrategy) || !Array.isArray(assetStrategy.pages)) {
|
|
58
62
|
invalid('ASSET_DESIGN_INVALID', { field: 'assetStrategy.pages' });
|
|
59
63
|
}
|
|
64
|
+
assetStrategy = { ...assetStrategy, pages: assetStrategy.pages.map(page =>
|
|
65
|
+
object(page) && page.imageNeed === 'none' && page.slots === undefined ? { ...page, slots: [] } : page) };
|
|
60
66
|
const pageIds = new Set();
|
|
61
67
|
const slots = new Map();
|
|
62
68
|
const pages = assetStrategy.pages.map(page => {
|
|
@@ -99,7 +105,43 @@ function prepareAssetPlan(assets, assetStrategy) {
|
|
|
99
105
|
minHeight: Math.max(designSize.minHeight, inputSize.minHeight),
|
|
100
106
|
};
|
|
101
107
|
});
|
|
108
|
+
if (pageId !== undefined) {
|
|
109
|
+
if (!nonempty(pageId) || !pageIds.has(pageId.trim())) {invalid('ASSET_PAGE_NOT_FOUND', { pageId });}
|
|
110
|
+
const selectedId = pageId.trim();
|
|
111
|
+
return {
|
|
112
|
+
entries: planned.filter(entry => entry.pageId === selectedId),
|
|
113
|
+
pages: pages.filter(page => page.pageId === selectedId),
|
|
114
|
+
assetStrategy: { ...assetStrategy, pages: assetStrategy.pages.filter(page => page.pageId.trim() === selectedId) },
|
|
115
|
+
};
|
|
116
|
+
}
|
|
102
117
|
return { entries: planned, pages, assetStrategy };
|
|
103
118
|
}
|
|
104
119
|
|
|
105
|
-
|
|
120
|
+
// The host executes search tasks; the CLI provides concrete inputs and independent output paths.
|
|
121
|
+
function buildAssetTasks(assetStrategy, outputDir) {
|
|
122
|
+
const plan = prepareAssetPlan([], assetStrategy);
|
|
123
|
+
const guidance = require('./ai-image').getMaterialSourcingGuidance();
|
|
124
|
+
return plan.pages.filter(page => page.imageNeed !== 'none' && page.slotIds.length).map(page => {
|
|
125
|
+
const basename = path.join(outputDir, 'asset-manifests', encodeURIComponent(page.pageId));
|
|
126
|
+
const draft = `${basename}.draft.json`;
|
|
127
|
+
const manifest = `${basename}.json`;
|
|
128
|
+
return {
|
|
129
|
+
pageId: page.pageId, skillId: 'yida-image-assets', startWhen: 'plan_confirmed',
|
|
130
|
+
...guidance.schedulingPolicy,
|
|
131
|
+
collectionPolicy: guidance.collectionPolicy,
|
|
132
|
+
failurePolicy: guidance.failurePolicy,
|
|
133
|
+
draft, manifest,
|
|
134
|
+
searches: plan.entries.filter(slot => slot.pageId === page.pageId).map(slot => ({
|
|
135
|
+
slotId: slot.slotId, usage: slot.usage, ...minimumSize(slot),
|
|
136
|
+
required: slot.required, generationAllowed: slot.generationAllowed === true, dependsOn: [],
|
|
137
|
+
})),
|
|
138
|
+
resolve: {
|
|
139
|
+
startWhen: 'page_draft_and_app_type_ready', appTypeRequired: true,
|
|
140
|
+
argv: ['asset', 'resolve', '--input', draft, '--manifest', manifest,
|
|
141
|
+
'--design', path.join(outputDir, 'design.md'), '--page-id', page.pageId, '--app-type', '<appType>', '--json'],
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = { minimumSize, prepareAssetPlan, readDesignAssetStrategy, buildAssetTasks };
|
|
@@ -10,6 +10,7 @@ const { minimumSize, prepareAssetPlan } = require('./asset-plan');
|
|
|
10
10
|
const { readImageMetadata, parseImageMetadata } = require('./image-metadata');
|
|
11
11
|
const { MAX_IMAGE_BYTES, uploadAttachment } = require('./attachment-upload');
|
|
12
12
|
const { getAssetStatus } = require('./asset-status');
|
|
13
|
+
const { t } = require('../core/i18n');
|
|
13
14
|
|
|
14
15
|
const ALLOWED_STOCK_IMAGE_PROVIDERS = Object.freeze({
|
|
15
16
|
unsplash: Object.freeze(['images.unsplash.com']),
|
|
@@ -258,7 +259,9 @@ async function resolveOne(candidate, ctx = {}) {
|
|
|
258
259
|
}
|
|
259
260
|
}
|
|
260
261
|
|
|
261
|
-
return { resolved: false, url: raw, source: 'unknown', reason: 'NOT_FOUND'
|
|
262
|
+
return { resolved: false, url: raw, source: 'unknown', reason: 'NOT_FOUND',
|
|
263
|
+
message: t('asset.localFileUnavailable', path.resolve(raw)),
|
|
264
|
+
details: { input: raw, resolvedPath: path.resolve(raw), baseDir: process.cwd(), pathBase: 'cwd' } };
|
|
262
265
|
}
|
|
263
266
|
|
|
264
267
|
function normalizeSource(source, fallback) {
|
|
@@ -284,7 +287,7 @@ function metadataGaps(asset) {
|
|
|
284
287
|
}
|
|
285
288
|
|
|
286
289
|
async function resolveAssets(assets = [], options = {}) {
|
|
287
|
-
const plan = prepareAssetPlan(assets, options.assetStrategy);
|
|
290
|
+
const plan = prepareAssetPlan(assets, options.assetStrategy, options.pageId);
|
|
288
291
|
const entries = plan.entries;
|
|
289
292
|
const online = options.online !== false;
|
|
290
293
|
const status = options.status || getAssetStatus({ online, env: options.env, runtime: options.runtime });
|
|
@@ -363,7 +366,8 @@ async function resolveAssets(assets = [], options = {}) {
|
|
|
363
366
|
slotId: slotId || `slot-${index + 1}`,
|
|
364
367
|
pageId: output.pageId,
|
|
365
368
|
code: result.reason,
|
|
366
|
-
message: `图片未落地:${result.reason}`,
|
|
369
|
+
message: result.message || `图片未落地:${result.reason}`,
|
|
370
|
+
...(result.details ? { details: result.details } : {}),
|
|
367
371
|
});
|
|
368
372
|
gaps.push(...output.gaps.map(gap => ({ ...gap })));
|
|
369
373
|
return;
|
|
@@ -146,6 +146,7 @@ const BUILDER_CORE_COMMAND_IDS = Object.freeze([
|
|
|
146
146
|
'get-permission',
|
|
147
147
|
'save-permission',
|
|
148
148
|
'create-app',
|
|
149
|
+
'design-plan.catalog',
|
|
149
150
|
'design-plan.init',
|
|
150
151
|
'design-plan.materialize',
|
|
151
152
|
'design-plan.preview',
|
|
@@ -176,6 +177,7 @@ function commandBriefs(manifest, commandIds) {
|
|
|
176
177
|
examples: entry.examples || [],
|
|
177
178
|
args: entry.args || [],
|
|
178
179
|
canonical: entry.canonical || null,
|
|
180
|
+
notes: entry.notes || [],
|
|
179
181
|
}));
|
|
180
182
|
}
|
|
181
183
|
|
|
@@ -466,9 +468,15 @@ function buildFullAppArtifactRoute(manifest) {
|
|
|
466
468
|
orchestrator_skill_id: workflow.orchestrator_skill_id,
|
|
467
469
|
requirement_analysis_skill_id: workflow.requirement_analysis_skill_id,
|
|
468
470
|
requirement_brief_path: workflow.requirement_brief_path,
|
|
471
|
+
user_visible_expression_policy: workflow.user_visible_expression_policy,
|
|
469
472
|
optional_asset_branch: workflow.optional_asset_branch,
|
|
470
473
|
design_mode_policy: workflow.design_mode_policy,
|
|
471
474
|
navigation_policy: workflow.navigation_policy,
|
|
475
|
+
entry_navigation_contract: workflow.entry_navigation_contract,
|
|
476
|
+
application_entry_policy: workflow.application_entry_policy,
|
|
477
|
+
default_nav_order_policy: workflow.default_nav_order_policy,
|
|
478
|
+
final_link_policy: workflow.final_link_policy,
|
|
479
|
+
product_design_policy: workflow.product_design_policy,
|
|
472
480
|
plan_command_ids: workflow.plan_command_ids,
|
|
473
481
|
theme_command_ids: workflow.theme_command_ids,
|
|
474
482
|
navigation_command_ids: workflow.navigation_command_ids,
|
|
@@ -489,7 +497,7 @@ function buildApplicationEntryPolicy(builderPath, env = process.env) {
|
|
|
489
497
|
resource_delivery: 'summary_only',
|
|
490
498
|
internal_artifact_delivery: 'never',
|
|
491
499
|
entries: {
|
|
492
|
-
workbench: '
|
|
500
|
+
workbench: 'when_workspace_in_scope',
|
|
493
501
|
custom: 'when_entry_mode_standalone_and_is_render_nav_false_readback',
|
|
494
502
|
admin: injectedAuthUsable ? 'omit' : 'include',
|
|
495
503
|
},
|
|
@@ -539,6 +547,7 @@ function buildCommandManifestDigest(manifest) {
|
|
|
539
547
|
permission_effect: entry.permission && entry.permission.effect,
|
|
540
548
|
args: entry.args || [],
|
|
541
549
|
canonical: entry.canonical || null,
|
|
550
|
+
notes: entry.notes || [],
|
|
542
551
|
})),
|
|
543
552
|
};
|
|
544
553
|
|