openyida 2026.9.19 → 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.
Files changed (195) hide show
  1. package/README.md +4 -4
  2. package/lib/app/application-style.js +69 -0
  3. package/lib/app/create-form/args.js +37 -12
  4. package/lib/app/create-form/batch.js +32 -5
  5. package/lib/app/create-form.js +94 -57
  6. package/lib/app/form-field-validator.js +0 -36
  7. package/lib/app/services/form-compiler.js +0 -2
  8. package/lib/app/theme-from-design.js +40 -14
  9. package/lib/app/update-app.js +63 -13
  10. package/lib/core/command-manifest.js +81 -25
  11. package/lib/core/locales/en.js +20 -3
  12. package/lib/core/locales/zh.js +20 -3
  13. package/lib/core/sample.js +36 -14
  14. package/lib/design/document.js +1 -0
  15. package/lib/design-plan/confirmation.js +13 -0
  16. package/lib/design-plan/design-plan.js +44 -2
  17. package/lib/design-plan/entry-navigation.js +10 -2
  18. package/lib/design-plan/init.js +16 -9
  19. package/lib/design-plan/materialize.js +85 -45
  20. package/lib/design-plan/normalize.js +13 -14
  21. package/lib/design-plan/parallel.js +62 -13
  22. package/lib/design-plan/patch.js +21 -7
  23. package/lib/design-plan/rebase.js +60 -0
  24. package/lib/design-plan/themes.js +16 -1
  25. package/lib/design-plan/validate.js +16 -1
  26. package/lib/design-plan/visual-policy.js +29 -4
  27. package/lib/samples/openyida-scaffold/canvas-form-drawer.canvas.jsx +3 -3
  28. package/lib/samples/openyida-scaffold/canvas-nav/mixed.jsx +6 -4
  29. package/lib/samples/openyida-scaffold/canvas-nav/shared.jsx +5 -5
  30. package/lib/samples/openyida-scaffold/canvas-nav/side.jsx +1 -2
  31. package/lib/samples/openyida-scaffold/canvas-nav/sidebar.jsx +1 -1
  32. package/lib/samples/openyida-scaffold/canvas-nav/top.jsx +5 -4
  33. package/package.json +1 -1
  34. package/yida-skills/SKILL.md +2 -2
  35. package/yida-skills/references/official-example-schema-patterns.md +1 -1
  36. package/yida-skills/references/yida-api.md +4 -0
  37. package/yida-skills/skills/yida-app/SKILL.md +15 -4
  38. package/yida-skills/skills/yida-app/references/entry-navigation.md +11 -9
  39. package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +1 -1
  40. package/yida-skills/skills/yida-app/workflow/plan/workflow.md +11 -4
  41. package/yida-skills/skills/yida-app/workflow/step-2-design.md +3 -3
  42. package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +2 -2
  43. package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +3 -3
  44. package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +3 -2
  45. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +2 -2
  46. package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +1 -1
  47. package/yida-skills/skills/yida-canvas-custom-page/references/canvas-theme-provider.md +1 -1
  48. package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +140 -1
  49. package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +1 -1
  50. package/yida-skills/skills/yida-canvas-custom-page/scripts/canvas-theme-provider.template.jsx +1 -1
  51. package/yida-skills/skills/yida-canvas-data-binding/SKILL.md +7 -0
  52. package/yida-skills/skills/yida-create-form-page/SKILL.md +34 -43
  53. package/yida-skills/skills/yida-create-form-page/references/advanced-form-modes.md +5 -5
  54. package/yida-skills/skills/yida-create-form-page/references/association-form-field.md +4 -4
  55. package/yida-skills/skills/yida-create-form-page/references/batch-forms.md +2 -2
  56. package/yida-skills/skills/yida-create-form-page/references/field-definition-guide.md +4 -16
  57. package/yida-skills/skills/yida-create-form-page/references/form-field-properties.md +14 -37
  58. package/yida-skills/skills/yida-design/SKILL.md +7 -5
  59. package/yida-skills/skills/yida-design/references/application-style-library.md +93 -0
  60. package/yida-skills/skills/yida-design/references/application-theme-consistency.md +48 -7
  61. package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +1 -1
  62. package/yida-skills/skills/yida-design/references/native-form-styles.md +208 -0
  63. package/yida-skills/skills/yida-design/references/navigation-decision.md +6 -4
  64. package/yida-skills/skills/yida-design/references/theme/app-custom-theme-template.css +110 -1
  65. package/yida-skills/skills/yida-design/references/theme/application-style-recipes.css +58 -0
  66. package/yida-skills/skills/yida-design/references/theme-selection.md +12 -11
  67. package/yida-skills/skills/yida-design/references/visual-decision-engine.md +1 -1
  68. package/yida-skills/skills/yida-design/scripts/validate_design_themes.py +49 -6
  69. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/SKILL.md +13 -3
  70. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-compact-schema.md +6 -2
  71. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +5 -3
  72. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-design.md +3 -1
  73. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-theme-selection.md +22 -23
  74. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/render_build_plan.py +2 -0
  75. package/yida-skills/skills/yida-design/templates/application-styles.json +398 -0
  76. package/yida-skills/skills/yida-design/templates/design-themes/README.md +15 -10
  77. package/yida-skills/skills/yida-design/templates/design-themes/app-amber/app_theme.css +748 -0
  78. package/yida-skills/skills/yida-design/templates/design-themes/app-amber/design.md +274 -0
  79. package/yida-skills/skills/yida-design/templates/design-themes/app-amber/form-layout.json +15 -0
  80. package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/app_theme.css +748 -0
  81. package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/design.md +274 -0
  82. package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/form-layout.json +14 -0
  83. package/yida-skills/skills/yida-design/templates/design-themes/app-executive/app_theme.css +748 -0
  84. package/yida-skills/skills/yida-design/templates/design-themes/app-executive/design.md +274 -0
  85. package/yida-skills/skills/yida-design/templates/design-themes/app-executive/form-layout.json +13 -0
  86. package/yida-skills/skills/yida-design/templates/design-themes/app-finance/app_theme.css +748 -0
  87. package/yida-skills/skills/yida-design/templates/design-themes/app-finance/design.md +274 -0
  88. package/yida-skills/skills/yida-design/templates/design-themes/app-finance/form-layout.json +14 -0
  89. package/yida-skills/skills/yida-design/templates/design-themes/app-glass/app_theme.css +748 -0
  90. package/yida-skills/skills/yida-design/templates/design-themes/app-glass/design.md +274 -0
  91. package/yida-skills/skills/yida-design/templates/design-themes/app-glass/form-layout.json +14 -0
  92. package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/app_theme.css +748 -0
  93. package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/design.md +274 -0
  94. package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/form-layout.json +14 -0
  95. package/yida-skills/skills/yida-design/templates/design-themes/app-line/app_theme.css +748 -0
  96. package/yida-skills/skills/yida-design/templates/design-themes/app-line/design.md +274 -0
  97. package/yida-skills/skills/yida-design/templates/design-themes/app-line/form-layout.json +13 -0
  98. package/yida-skills/skills/yida-design/templates/design-themes/app-neon/app_theme.css +748 -0
  99. package/yida-skills/skills/yida-design/templates/design-themes/app-neon/design.md +274 -0
  100. package/yida-skills/skills/yida-design/templates/design-themes/app-neon/form-layout.json +14 -0
  101. package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/app_theme.css +754 -0
  102. package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/design.md +279 -0
  103. package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/form-layout.json +13 -0
  104. package/yida-skills/skills/yida-design/templates/design-themes/app-paper/app_theme.css +748 -0
  105. package/yida-skills/skills/yida-design/templates/design-themes/app-paper/design.md +274 -0
  106. package/yida-skills/skills/yida-design/templates/design-themes/app-paper/form-layout.json +13 -0
  107. package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/app_theme.css +748 -0
  108. package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/design.md +274 -0
  109. package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/form-layout.json +14 -0
  110. package/yida-skills/skills/yida-design/templates/design-themes/app-plum/app_theme.css +748 -0
  111. package/yida-skills/skills/yida-design/templates/design-themes/app-plum/design.md +274 -0
  112. package/yida-skills/skills/yida-design/templates/design-themes/app-plum/form-layout.json +14 -0
  113. package/yida-skills/skills/yida-design/templates/design-themes/app-pop/app_theme.css +758 -0
  114. package/yida-skills/skills/yida-design/templates/design-themes/app-pop/design.md +281 -0
  115. package/yida-skills/skills/yida-design/templates/design-themes/app-pop/form-layout.json +14 -0
  116. package/yida-skills/skills/yida-design/templates/design-themes/app-sage/app_theme.css +748 -0
  117. package/yida-skills/skills/yida-design/templates/design-themes/app-sage/design.md +274 -0
  118. package/yida-skills/skills/yida-design/templates/design-themes/app-sage/form-layout.json +14 -0
  119. package/yida-skills/skills/yida-design/templates/design-themes/app-teal/app_theme.css +748 -0
  120. package/yida-skills/skills/yida-design/templates/design-themes/app-teal/design.md +274 -0
  121. package/yida-skills/skills/yida-design/templates/design-themes/app-teal/form-layout.json +14 -0
  122. package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/app_theme.css +754 -0
  123. package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/design.md +279 -0
  124. package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/form-layout.json +14 -0
  125. package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/app_theme.css +752 -0
  126. package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/design.md +278 -0
  127. package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/form-layout.json +13 -0
  128. package/yida-skills/skills/yida-design/templates/design-themes/app-wire/app_theme.css +748 -0
  129. package/yida-skills/skills/yida-design/templates/design-themes/app-wire/design.md +274 -0
  130. package/yida-skills/skills/yida-design/templates/design-themes/app-wire/form-layout.json +14 -0
  131. package/yida-skills/skills/yida-design/templates/design-themes/basic-tokens.json +2 -1
  132. package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/app_theme.css +690 -0
  133. package/yida-skills/skills/yida-design/templates/design-themes/{dark-inset-hairline.md → dark-inset-hairline/design.md} +107 -58
  134. package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/form-layout.json +13 -0
  135. package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/app_theme.css +691 -0
  136. package/yida-skills/skills/yida-design/templates/design-themes/{dark-rail-fine-lines.md → dark-rail-fine-lines/design.md} +106 -50
  137. package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/form-layout.json +13 -0
  138. package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/app_theme.css +696 -0
  139. package/yida-skills/skills/yida-design/templates/design-themes/{dark-rail-signal-panels.md → dark-rail-signal-panels/design.md} +66 -12
  140. package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/form-layout.json +13 -0
  141. package/yida-skills/skills/yida-design/templates/design-themes/free-creative/app_theme.css +689 -0
  142. package/yida-skills/skills/yida-design/templates/design-themes/free-creative/design.md +238 -0
  143. package/yida-skills/skills/yida-design/templates/design-themes/free-creative/form-layout.json +13 -0
  144. package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/app_theme.css +692 -0
  145. package/yida-skills/skills/yida-design/templates/design-themes/{graphite-bevel-grid.md → graphite-bevel-grid/design.md} +64 -10
  146. package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/form-layout.json +13 -0
  147. package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/app_theme.css +691 -0
  148. package/yida-skills/skills/yida-design/templates/design-themes/{hairline-shared-bands.md → hairline-shared-bands/design.md} +66 -12
  149. package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/form-layout.json +13 -0
  150. package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/app_theme.css +691 -0
  151. package/yida-skills/skills/yida-design/templates/design-themes/{hairline-soft-blocks.md → hairline-soft-blocks/design.md} +61 -9
  152. package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/form-layout.json +13 -0
  153. package/yida-skills/skills/yida-design/templates/design-themes/index.json +399 -31
  154. package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/app_theme.css +701 -0
  155. package/yida-skills/skills/yida-design/templates/design-themes/{ink-glow-soft-panels.md → ink-glow-soft-panels/design.md} +62 -8
  156. package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/form-layout.json +13 -0
  157. package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/app_theme.css +691 -0
  158. package/yida-skills/skills/yida-design/templates/design-themes/{inset-frame-pixel-rhythm.md → inset-frame-pixel-rhythm/design.md} +63 -9
  159. package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/form-layout.json +13 -0
  160. package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/app_theme.css +698 -0
  161. package/yida-skills/skills/yida-design/templates/design-themes/{outlined-texture-duotone.md → outlined-texture-duotone/design.md} +66 -12
  162. package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/form-layout.json +13 -0
  163. package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/app_theme.css +691 -0
  164. package/yida-skills/skills/yida-design/templates/design-themes/{soft-inset-surfaces.md → soft-inset-surfaces/design.md} +63 -11
  165. package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/form-layout.json +13 -0
  166. package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/app_theme.css +690 -0
  167. package/yida-skills/skills/yida-design/templates/design-themes/{soft-outline-rhythm.md → soft-outline-rhythm/design.md} +64 -26
  168. package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/form-layout.json +13 -0
  169. package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/app_theme.css +698 -0
  170. package/yida-skills/skills/yida-design/templates/design-themes/{soft-rail-bold-band.md → soft-rail-bold-band/design.md} +103 -49
  171. package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/form-layout.json +13 -0
  172. package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/app_theme.css +692 -0
  173. package/yida-skills/skills/yida-design/templates/design-themes/{soft-spectral-panels.md → soft-spectral-panels/design.md} +67 -13
  174. package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/form-layout.json +13 -0
  175. package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/app_theme.css +711 -0
  176. package/yida-skills/skills/yida-design/templates/design-themes/{warm-canvas-contrast-panels.md → warm-canvas-contrast-panels/design.md} +64 -10
  177. package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/form-layout.json +13 -0
  178. package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/app_theme.css +692 -0
  179. package/yida-skills/skills/yida-design/templates/design-themes/{warm-rail-muted-panels.md → warm-rail-muted-panels/design.md} +102 -50
  180. package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/form-layout.json +13 -0
  181. package/yida-skills/skills/yida-design/templates/navigation-styles.json +769 -0
  182. package/yida-skills/skills/yida-design/workflow/output-design.md +9 -6
  183. package/yida-skills/skills/yida-design/workflow/step-2-theme-system.md +12 -4
  184. package/yida-skills/skills/yida-design/workflow/step-6-handoff.md +1 -1
  185. package/yida-skills/skills/yida-form-permission/SKILL.md +6 -0
  186. package/yida-skills/skills/yida-nav-shell/SKILL.md +1 -1
  187. package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +6 -0
  188. package/yida-skills/skills/yida-page-config/SKILL.md +9 -0
  189. package/yida-skills/skills/yida-prd/SKILL.md +1 -0
  190. package/yida-skills/skills/yida-prd/workflow/output-prd.md +2 -2
  191. package/yida-skills/skills/yida-prd/workflow/step-2-information-architecture.md +1 -1
  192. package/yida-skills/skills/yida-requirement-analysis/references/experience-groups.md +45 -9
  193. package/yida-skills/skills/yida-requirement-analysis/references/handoff.md +5 -5
  194. package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +17 -4
  195. package/yida-skills/skills-index.json +2 -2
@@ -4,6 +4,13 @@
4
4
  'use strict';
5
5
 
6
6
  module.exports = {
7
+ design_plan: {
8
+ rebase_required: 'Before first materialization, repair the matching facts in business.json/visual.json and preserve the source baseline. If the source changed, add --rebase-parts to the original materialize command to reconcile against the saved baseline. Do not invent a digest, delete drafts, or run init again.',
9
+ baseline_missing: 'No trusted baseline matches these parts. Preserve the plan and facts; restore .build-plan-base.json from the original workspace. If unavailable, report the blocker instead of inventing a digest or rebuilding.',
10
+ rebase_stage_invalid: 'Rebasing is limited to drafts before first materialization. For presented or materialized plans use patch --materialize on the current build-plan.json, without merging initial parts again.',
11
+ rebase_conflict: 'The source and part changed the same field differently. Review conflicts and explicitly select the current or baseline value in the part before retrying; preserve other facts. No files were saved.',
12
+ repair_page_bindings: 'Repair pageApplications in visual.json to match customPageDetails in business.json one to one; exclude native forms. Each visualMemoryApplications must be an array; use [] when none apply. Keep the existing draft, fix the listed fields and retry the original command. Do not delete the directory or run init again.',
13
+ },
7
14
  design_document: {
8
15
  update_conflict: 'Cannot update {0} ({1}): the existing content differs from the last generated version. Read this section, reconcile the local edit with the plan source, and retry. No files were saved.',
9
16
  theme_css_invalid: 'Theme CSS near line {0} has an unclosed or mismatched delimiter, string, or comment. Fix it and retry.',
@@ -174,6 +181,8 @@ module.exports = {
174
181
  },
175
182
 
176
183
  cli: {
184
+ design_plan_entry_mode_required: 'Choose an entry mode: shared business pages (unified), separate frontend and business backend (service-management), frontend only (frontend-only), or business backend only (backend-only).',
185
+ design_plan_backend_only_roles: 'For backend-only, every entry must use role management.',
177
186
  design_plan_local_menu_binding: 'Local menu {0} requires a host page and viewKey: entry {1} sceneKey must match the page sceneKey, menu resource must equal the page name, and viewKey must be nonempty. This applies to every entry role.',
178
187
  design_plan_visual_object_required: '{0} must be an object, not a string or array. Preserve the structure generated by init and fill in its values. Example: {1}.',
179
188
  help: `
@@ -190,7 +199,7 @@ Commands:
190
199
  create-app --name "<name>" Create an app and output appType; configure theme resources and Logo source with update-app
191
200
  create-page <appType> "<pageName>" [--mode dashboard] [--hide-nav] Create a custom page, output pageId
192
201
  update-app <appType> [--name "..."] [--hide-app-nav|--show-app-nav] Update app info
193
- create-form create <appType> "<formName>" <fieldsJSON> [--layout <layout>] [--theme <theme>] [--label-align <align>] Create a form page
202
+ create-form create <appType> "<formName>" <fieldsJSON> [--layout single|double|card|section] [--theme default|compact|comfortable] [--label-align top|left|right] Create a form page
194
203
  create-form update <appType> <formUuid> <changesJSON> Update a form page
195
204
  list-forms <appType> [--keyword <text>] List forms/pages in an app
196
205
  get-schema <appType> <formUuid|--all> Get one form Schema or all form Schemas
@@ -848,12 +857,12 @@ Examples:
848
857
  changes_applied: ' Changes applied: {0}',
849
858
  create_post_failure_retry_advice: 'Do not repeat create directly. First run openyida list-forms {0} --keyword "{1}" to check for an existing same-title form; if this run already created a blank/existing form, prefer create-form update or a future --resume-form-uuid flow.',
850
859
  error: '\n❌ Error: {0}',
851
- usage_create: 'Usage: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
860
+ usage_create: 'Usage: openyida create-form create <appType> <formTitle> <fieldsJsonFile> [--layout single|double|card|section] [--theme default|compact|comfortable] [--label-align top|left|right] [--icon auto|<iconName>] [--locale zh_CN|en_US|ja_JP] [--open|--no-open]',
852
861
  example_create: 'Example: openyida create-form create "APP_XXX" "Employee Info" .cache/openyida/forms/employee-fields.json',
853
862
  usage_update: 'Usage: openyida create-form update <appType> <formUuid> <changesJsonOrFile>',
854
863
  example_update: 'Example: openyida create-form update "APP_XXX" "FORM-YYY" \'[{"action":"add","field":{"type":"TextField","label":"Note"}}]\'',
855
864
  usage_label: 'Usage:',
856
- usage_create_short: ' create: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
865
+ usage_create_short: ' create: openyida create-form create <appType> <formTitle> <fieldsJsonFile> [--layout single|double|card|section] [--theme default|compact|comfortable] [--label-align top|left|right] [--icon auto|<iconName>] [--locale zh_CN|en_US|ja_JP] [--open|--no-open]',
857
866
  usage_update_short: ' update: openyida create-form update <appType> <formUuid> <changesJsonOrFile> or --data-file <changesJsonOrFile>',
858
867
  resume_readback_failed: 'Unable to read the target form before resume.',
859
868
  resume_ownership_unverified: 'The target form does not belong to the requested app; resume stopped.',
@@ -1152,6 +1161,7 @@ Examples:
1152
1161
  theme_preset_conflict: 'A preset colour cannot be combined with custom CSS or themeColor. Use --colour custom or omit --colour.',
1153
1162
  custom_theme_color_required: 'colour=custom requires a theme file or a valid themeColor. Pass --theme-file or --theme-color.',
1154
1163
  theme_not_persisted: 'App theme readback failed or did not match the saved settings. The CSS resource binding is unverified. Inspect themeVerification and retry update-app <appType> --theme-file <css>; do not refresh the app.',
1164
+ navigation_not_persisted: 'App navigation readback failed or did not match the requested settings. Navigation changes remain unverified. Inspect the expected values, actual values and readback error in navigationVerification, then check the current app settings.',
1155
1165
  usage: 'openyida update-app <appType> [--name "New Name"] [--theme-file ./theme.css] [--nav-theme light|dark|white|gray] [--logo-source appIcon|customImage] [--layout side|top|l_shape]',
1156
1166
  example: 'openyida update-app APP_XXX --theme-file ./app-theme.css --nav-theme light --logo-source appIcon --layout side',
1157
1167
  options: 'Options:\n --name, -n App name (specify at least one update field)\n --desc, -d App description\n --icon Icon name (e.g. xian-yingyong)\n --icon-color Icon color (default: #0089FF)\n --colour, --theme podBlue | podGreen | podOrange | deepBlue | teal | black | custom ...\n --theme-color Update the app primary color; the CSS primary color takes precedence when a theme file is supplied\n --theme-file Custom app theme CSS file (up to 20 MB; must declare --color-brand1-6)\n --nav-theme Navigation theme: light, dark, white, or gray\n --logo-source Navigation logo source: appIcon or customImage\n --layout Modern navigation layout: side, top, or l_shape\n --hide-app-nav Hide app navigation in visit mode, only for custom pages with self-rendered top/side navigation\n --show-app-nav Restore app navigation in visit mode',
@@ -2123,3 +2133,10 @@ module.exports.connector_e2e = {
2123
2133
  Object.assign(module.exports.process_errors || (module.exports.process_errors = {}), {
2124
2134
  action_config_invalid: 'Invalid approval action configuration for node {0}: {1}',
2125
2135
  });
2136
+
2137
+ module.exports.cli_argument = { invalid: 'Invalid or missing argument value: {0}. Follow the command help.' };
2138
+
2139
+ module.exports.sample_options = {
2140
+ 'theme_help': 'app-theme: --output is a CSS file; --design-file reads a completed design. Omitting it resets CSS. Set theme values in the design.',
2141
+ 'style_help': 'application-style: --style-id is required and accepts every catalog theme; --output is a directory for three files. Existing bundle files cause an error.'
2142
+ };
@@ -4,6 +4,13 @@
4
4
  'use strict';
5
5
 
6
6
  module.exports = {
7
+ design_plan: {
8
+ rebase_required: '首次物化前请修正 business.json/visual.json 中的对应 facts,保留主计划基线。如果主计划已改变,在原 materialize 命令加 --rebase-parts,CLI 会根据保存的基线核对差异;不要手填 digest、删除草稿或重新 init。',
9
+ baseline_missing: '未找到与片段匹配的可信原始基线。保留当前计划与 facts;从原工作区恢复 .build-plan-base.json 后重试。无法恢复时明确告知用户阻塞,不伪造摘要或自动重建。',
10
+ rebase_stage_invalid: '基线恢复仅用于首次物化前的草稿。已展示或已物化方案请沿用当前 build-plan.json,通过 patch --materialize 调整,不再合并初始化片段。',
11
+ rebase_conflict: '主计划和片段对同一字段有不同修改。按 conflicts 核对并在片段中明确采用当前值或原基线值后重试;其他 facts 保留。本次未保存文件。',
12
+ repair_page_bindings: '按 business.json 的 customPageDetails 逐一修复 visual.json 的 pageApplications;原生表单不加入绑定。每项 visualMemoryApplications 必须为数组,无适用项用 []。保留现有草稿,按列出的字段修复后重试原命令;不要删除目录或重新 init。',
13
+ },
7
14
  design_document: {
8
15
  update_conflict: '无法更新 {0}({1}):现有内容与上次生成结果冲突。请读取这一部分,将本地修改与计划源对齐后重试;本次未保存任何文件。',
9
16
  theme_css_invalid: '主题 CSS 第 {0} 行附近存在未闭合或不匹配的括号、字符串或注释,请修复后重试。',
@@ -174,6 +181,8 @@ module.exports = {
174
181
  },
175
182
 
176
183
  cli: {
184
+ design_plan_entry_mode_required: '请明确入口方式:不分前后台(unified)、分前后台(service-management)、只有访问前台(frontend-only)或只有访问后台(backend-only)。',
185
+ design_plan_backend_only_roles: '只有访问后台(backend-only)时,每个入口的 role 必须为 management。',
177
186
  design_plan_local_menu_binding: '本地菜单 {0} 必须关联承载页面和 viewKey:入口 {1} 的 sceneKey 对应页面 sceneKey,菜单 resource 等于页面 name,并填写非空 viewKey。此规则适用于所有入口身份。',
178
187
  design_plan_visual_object_required: '{0} 必须为对象,不能写成字符串或数组。沿用 init 生成的字段结构,只补齐值;示例:{1}。',
179
188
  help: `
@@ -190,7 +199,7 @@ openyida - 宜搭命令行工具
190
199
  create-app --name "<名称>" 创建应用并输出 appType;主题资源与 Logo 来源使用 update-app 配置
191
200
  create-page <appType> "<页面名>" [--mode dashboard] [--hide-nav] 创建自定义页面,输出 pageId
192
201
  update-app <appType> [--name "..."] [--hide-app-nav|--show-app-nav] 更新应用信息
193
- create-form create <appType> "<表单名>" <字段JSON> [--layout <布局>] [--theme <主题>] [--label-align <对齐>] 创建表单页面
202
+ create-form create <appType> "<表单名>" <字段JSON> [--layout single|double|card|section] [--theme default|compact|comfortable] [--label-align top|left|right] 创建表单页面
194
203
  create-form update <appType> <formUuid> <修改JSON> 更新表单页面
195
204
  list-forms <appType> [--keyword <关键词>] 列出应用下的表单/页面
196
205
  get-schema <appType> <formUuid|--all> 获取单个或全部表单 Schema
@@ -819,12 +828,12 @@ openyida - 宜搭命令行工具
819
828
  changes_applied: ' 已应用变更: {0}',
820
829
  create_post_failure_retry_advice: '不要直接重复 create。先运行 openyida list-forms {0} --keyword "{1}" 确认同名表单;若是本轮创建的空白/已有表单,优先使用 create-form update 或后续 --resume-form-uuid 复用。',
821
830
  error: '\n❌ 错误: {0}',
822
- usage_create: '用法: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
831
+ usage_create: '用法: openyida create-form create <appType> <formTitle> <fieldsJsonFile> [--layout single|double|card|section] [--theme default|compact|comfortable] [--label-align top|left|right] [--icon auto|<iconName>] [--locale zh_CN|en_US|ja_JP] [--open|--no-open]',
823
832
  example_create: '示例:openyida create-form create "APP_XXX" "员工信息登记" .cache/openyida/forms/employee-fields.json',
824
833
  usage_update: '用法: openyida create-form update <appType> <formUuid> <changesJsonOrFile>',
825
834
  example_update: '示例:openyida create-form update "APP_XXX" "FORM-YYY" \'[{"action":"add","field":{"type":"TextField","label":"备注"}}]\'',
826
835
  usage_label: '用法:',
827
- usage_create_short: ' 创建: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
836
+ usage_create_short: ' 创建: openyida create-form create <appType> <formTitle> <fieldsJsonFile> [--layout single|double|card|section] [--theme default|compact|comfortable] [--label-align top|left|right] [--icon auto|<iconName>] [--locale zh_CN|en_US|ja_JP] [--open|--no-open]',
828
837
  usage_update_short: ' 更新: openyida create-form update <appType> <formUuid> <changesJsonOrFile> 或 --data-file <changesJsonOrFile>',
829
838
  resume_readback_failed: '恢复前无法回读目标表单。',
830
839
  resume_ownership_unverified: '目标表单不属于指定应用,已停止恢复。',
@@ -1082,6 +1091,7 @@ openyida - 宜搭命令行工具
1082
1091
  theme_preset_conflict: '平台预置 colour 不能与自定义 CSS 或 themeColor 同传;请使用 colour=custom 或省略 --colour。',
1083
1092
  custom_theme_color_required: 'colour=custom 需要主题文件或有效 themeColor;请传 --theme-file 或 --theme-color。',
1084
1093
  theme_not_persisted: '应用主题保存后回读不一致或查询失败,CSS 资源尚未确认绑定。请查看 themeVerification,并使用 update-app <appType> --theme-file <css> 重试;不要重复创建应用。',
1094
+ navigation_not_persisted: '应用导航保存后回读不一致或查询失败,导航设置尚未确认生效。请查看 navigationVerification 中的期望值、实际值和查询错误,核实应用当前设置。',
1085
1095
  usage: 'openyida update-app <appType> [--name "新名称"] [--theme-file ./theme.css] [--nav-theme light|dark|white|gray] [--logo-source appIcon|customImage] [--layout side|top|l_shape]',
1086
1096
  example: 'openyida update-app APP_XXX --theme-file ./app-theme.css --nav-theme light --logo-source appIcon --layout side',
1087
1097
  options: '选项:\n --name, -n 应用名称(至少指定一个更新字段)\n --desc, -d 应用描述\n --icon 图标名称(如: xian-yingyong)\n --icon-color 图标颜色(默认: #0089FF)\n --colour, --theme podBlue | podGreen | podOrange | deepBlue | teal | black | custom ...\n --theme-color 更新应用主色;与主题文件同传时以 CSS 主色为准\n --theme-file 自定义应用主题 CSS 文件(最大 20 MB,必须声明 --color-brand1-6)\n --nav-theme 导航主题 light、dark、white 或 gray\n --logo-source 导航 Logo 来源 appIcon 或 customImage\n --layout 新版导航布局 side、top 或 l_shape\n --hide-app-nav 隐藏访问态应用导航,仅在自定义页自绘顶部/侧边导航壳时使用\n --show-app-nav 恢复访问态应用导航',
@@ -2077,3 +2087,10 @@ module.exports.connector_e2e = {
2077
2087
  Object.assign(module.exports.process_errors || (module.exports.process_errors = {}), {
2078
2088
  action_config_invalid: '节点 {0} 的审批动作配置无效:{1}',
2079
2089
  });
2090
+
2091
+ module.exports.cli_argument = { invalid: '参数无效或缺少值:{0}。请按命令帮助填写。' };
2092
+
2093
+ module.exports.sample_options = {
2094
+ 'theme_help': 'app-theme:--output 指向 CSS 文件;--design-file 读取已完成的设计。省略设计文件会重置 CSS,主题值通过设计填写。',
2095
+ 'style_help': 'application-style:--style-id 必填,支持目录中全部主题;--output 指向目录,输出三文件,有同名文件时报错。'
2096
+ };
@@ -18,6 +18,7 @@
18
18
  const fs = require('fs');
19
19
  const path = require('path');
20
20
  const { CliError } = require('./cli-error');
21
+ const { t } = require('./i18n');
21
22
 
22
23
  // ── Sample 索引表 ─────────────────────────────────────────────────────
23
24
 
@@ -111,7 +112,7 @@ const SAMPLE_HINTS = {
111
112
  'openyida-page-template/canvas-nav-top': '顶部默认贴顶通栏;长页先合并 canvas-nav-content,CanvasNav 传 headerOnly,有首屏背景时传 overlay;悬浮外观仅显式 floating=true。',
112
113
  'openyida-page-template/canvas-nav-content': '合并 CanvasNavigationContent;长页用 layout=document,CanvasNav 传 headerOnly/overlay;默认 sticky,受宿主限制时用 navigationPosition=fixed。工作区用 workspace,宿主已分配高度时传 height。',
113
114
  'openyida-page-template/canvas-nav-data': '页内展示用 mode=local(key/viewKey/targetType=local),不请求平台菜单;platform 需真实资源 ID;independent 或声明 access 时必须接入真实 resolveAccess。保留 PRD 顺序与分组。',
114
- 'openyida-page-template/form-open-container': '将 import、CanvasDrawer、FormOpenContainer、useYidaFormOpen 和辅助函数合并到现有 Canvas 页面;表单按钮调用 openForm(request),页面必须渲染返回的 formOpenContainer;保留 iframe、标题栏操作、调宽和关闭刷新。普通业务内容使用 CanvasDrawer;抽屉背景为 --pod-shell-theme-bg-color。',
115
+ 'openyida-page-template/form-open-container': '将 import、CanvasDrawer、FormOpenContainer、useYidaFormOpen 和辅助函数合并到现有 Canvas 页面;表单按钮调用 openForm(request),页面必须渲染返回的 formOpenContainer;保留 iframe、标题栏操作、调宽和关闭刷新。普通业务内容使用 CanvasDrawer;抽屉背景使用 --pod-page-bg-color,文字使用 --color-text1-4,background 可按设计覆盖。',
115
116
  'yida-design/app-theme': '已复制应用主题模板;请由 yida-design 按 design.md 定点修改需要变化的 token。',
116
117
  };
117
118
 
@@ -148,6 +149,9 @@ function printSampleList() {
148
149
  }
149
150
  console.log('');
150
151
  console.log(' 应用主题:openyida sample yida-design app-theme --output <app-theme.css> --design-file <design.md>');
152
+ console.log(' Application style: openyida sample yida-design application-style --style-id <themeId|free-creative> --output <directory>');
153
+ console.log(` ${t('sample_options.theme_help')}`);
154
+ console.log(` ${t('sample_options.style_help')}`);
151
155
  }
152
156
 
153
157
  /**
@@ -162,23 +166,29 @@ function ensureDirectoryExists(filePath) {
162
166
  }
163
167
 
164
168
  /**
165
- * 解析 --output 与 --var KEY=VALUE 参数。
169
+ * 解析 sample 输出路径、设计文件、主题 ID 与模板变量;拒绝未声明参数。
166
170
  * @param {string[]} rest
167
171
  * @param {string} defaultOutputPath
168
- * @returns {{ outputPath: string, variables: Object }}
172
+ * @returns {{ outputPath: string, variables: Object, designFile?: string, styleId?: string }}
169
173
  */
170
174
  function parseOptions(rest, defaultOutputPath) {
171
175
  let outputPath = defaultOutputPath;
172
176
  let designFile;
177
+ let styleId;
173
178
  const variables = {};
174
179
 
175
180
  for (let i = 0; i < rest.length; i++) {
176
181
  const arg = rest[i];
182
+ const invalid = () => {throw new CliError(t('cli_argument.invalid', arg), { code: 'SAMPLE_ARGUMENT_INVALID' });};
183
+ if (!['--output', '--var', '--design-file', '--style-id'].includes(arg)) {invalid();}
184
+ if (!rest[i + 1] || rest[i + 1].startsWith('--')) {invalid();}
185
+
186
+ if (arg === '--style-id') {
187
+ styleId = rest[++i];
188
+ continue;
189
+ }
177
190
 
178
191
  if (arg === '--design-file') {
179
- if (!rest[i + 1] || rest[i + 1].startsWith('--')) {
180
- throw new CliError('--design-file 缺少 design.md 路径', { code: 'DESIGN_THEME_FILE_REQUIRED' });
181
- }
182
192
  designFile = rest[++i];
183
193
  continue;
184
194
  }
@@ -192,19 +202,15 @@ function parseOptions(rest, defaultOutputPath) {
192
202
  if (arg === '--var' && rest[i + 1]) {
193
203
  const pair = rest[i + 1];
194
204
  const eqIndex = pair.indexOf('=');
195
- if (eqIndex > 0) {
196
- const key = pair.slice(0, eqIndex).trim();
197
- const value = pair.slice(eqIndex + 1);
198
- if (key) {
199
- variables[key] = value;
200
- }
201
- }
205
+ if (eqIndex <= 0 || !pair.slice(0, eqIndex).trim()) {invalid();}
206
+ const key = pair.slice(0, eqIndex).trim();
207
+ variables[key] = pair.slice(eqIndex + 1);
202
208
  i++;
203
209
  continue;
204
210
  }
205
211
  }
206
212
 
207
- return { outputPath, variables, designFile };
213
+ return { outputPath, variables, designFile, styleId };
208
214
  }
209
215
 
210
216
  /**
@@ -234,6 +240,19 @@ async function run(args) {
234
240
  }
235
241
 
236
242
  const [skill, name, ...rest] = args;
243
+ if (skill === 'yida-design' && name === 'application-style') {
244
+ const options = parseOptions(rest, path.join(process.cwd(), '.cache/samples/application-style'));
245
+ if (options.designFile || Object.keys(options.variables).length) {
246
+ throw new CliError(t('design_document.invalid', 'application-style', 'APPLICATION_STYLE_SAMPLE_INVALID'), { code: 'APPLICATION_STYLE_SAMPLE_INVALID' });
247
+ }
248
+ if (!options.styleId) {
249
+ throw new CliError(t('cli_argument.invalid', '--style-id'), { code: 'APPLICATION_STYLE_REQUIRED' });
250
+ }
251
+ const result = require('../app/application-style').exportApplicationStyle(options.styleId, options.outputPath);
252
+ console.log(JSON.stringify(result, null, 2));
253
+ return result;
254
+ }
255
+ if (rest.includes('--style-id')) {throw new CliError(t('design_document.invalid', '--style-id', 'APPLICATION_STYLE_SAMPLE_INVALID'), { code: 'APPLICATION_STYLE_SAMPLE_INVALID' });}
237
256
 
238
257
  const { c, error: chalkError, success: chalkSuccess, hint: chalkHint } = require('./chalk');
239
258
 
@@ -275,6 +294,9 @@ async function run(args) {
275
294
  throw new CliError('--design-file 仅用于 yida-design app-theme', { code: 'DESIGN_THEME_SAMPLE_INVALID' });
276
295
  }
277
296
  if (sampleKey === 'yida-design/app-theme') {
297
+ if (Object.keys(variables).length) {
298
+ throw new CliError(t('cli_argument.invalid', '--var (yida-design app-theme: --design-file)'), { code: 'SAMPLE_ARGUMENT_INVALID' });
299
+ }
278
300
  const snapshotPath = `${outputPath}.tokens.md`;
279
301
  if (designFile && path.resolve(snapshotPath) === path.resolve(designFile)) {
280
302
  throw new CliError('输出路径不能覆盖模板或 design.md', { code: 'SAMPLE_OUTPUT_CONFLICT' });
@@ -376,6 +376,7 @@ function validateDesignDocument(markdown, options = {}) {
376
376
  if (!/^#[\da-f]{6}$/i.test(profile.themeColor) || profile.themeColor.toUpperCase() !== tokens['--color-brand1-6'].toUpperCase()) {
377
377
  invalid('themeProfile.themeColor', 'PRIMARY_COLOR_MISMATCH');
378
378
  }
379
+ if (!['light', 'dark'].includes(profile.contentTone)) {invalid('themeProfile.contentTone', 'LIGHT_OR_DARK_REQUIRED');}
379
380
  if (!['light', 'dark'].includes(profile.navTheme)) {invalid('themeProfile.navTheme', 'LIGHT_OR_DARK_REQUIRED');}
380
381
  if (!['app-custom-theme-file', 'current-app-theme'].includes(profile.themeDelivery)) {invalid('themeProfile.themeDelivery', 'INVALID_THEME_DELIVERY');}
381
382
  if (typeof profile.themeFile !== 'string') {invalid('themeProfile.themeFile', 'STRING_REQUIRED');}
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ function confirmationPayload(outputs, revision) {
4
+ return {
5
+ question: '请确认当前搭建方案',
6
+ options: [{ label: '确认并开始搭建', value: 'confirm_build' }, { label: '继续调整', value: 'continue_editing' }],
7
+ submitLabel: '提交选择',
8
+ attachments: [{ name: '搭建方案.html', path: outputs.html }],
9
+ revision,
10
+ };
11
+ }
12
+
13
+ module.exports = { confirmationPayload };
@@ -12,7 +12,7 @@ function usage() {
12
12
  ' openyida design-plan catalog [--json]',
13
13
  ' openyida design-plan init <requirement-brief.json> [--theme-id <id>] [--output-dir <dir>] [--json]',
14
14
  ' openyida design-plan preview <build-plan.json> --part-file <module.json> [--json]',
15
- ' openyida design-plan materialize <build-plan.json> [--from-preview | --business-file <json> --visual-file <json>] [--output-dir <dir>] [--check] [--json]',
15
+ ' openyida design-plan materialize <build-plan.json> [--from-preview | --business-file <json> --visual-file <json>] [--output-dir <dir>] [--rebase-parts] [--check] [--json]',
16
16
  ' openyida design-plan patch <build-plan.json> --set <path=value> [--set <path=value> ...] [--materialize] [--output-dir <dir>] [--json]',
17
17
  ].join('\n');
18
18
  }
@@ -50,6 +50,46 @@ function setValues(args) {
50
50
  return values;
51
51
  }
52
52
 
53
+ const OPTION_SCHEMA = Object.freeze({
54
+ catalog: { flags: ['--json'], values: [] },
55
+ init: { flags: ['--json'], values: ['--theme-id', '--output-dir'] },
56
+ preview: { flags: ['--json'], values: ['--part-file'] },
57
+ materialize: { flags: ['--from-preview', '--rebase-parts', '--check', '--json'], values: ['--business-file', '--visual-file', '--output-dir'] },
58
+ patch: { flags: ['--materialize', '--json'], values: ['--set', '--output-dir'], inlineValues: ['--set'] },
59
+ });
60
+
61
+ function assertKnownOptions(subcommand, args) {
62
+ const schema = OPTION_SCHEMA[subcommand];
63
+ const flags = new Set(schema.flags);
64
+ const values = new Set(schema.values);
65
+ const inlineValues = new Set(schema.inlineValues || []);
66
+ for (let index = 0; index < args.length; index += 1) {
67
+ const token = args[index];
68
+ const equalsAt = token.indexOf('=');
69
+ const option = equalsAt === -1 ? token : token.slice(0, equalsAt);
70
+ if (equalsAt !== -1 && inlineValues.has(option)) {
71
+ continue;
72
+ }
73
+ if (flags.has(token)) {
74
+ continue;
75
+ }
76
+ if (values.has(token)) {
77
+ if (!args[index + 1] || args[index + 1].startsWith('--')) {
78
+ throw new CliError(`${token} 缺少参数值`, {
79
+ code: 'DESIGN_PLAN_INVALID_ARGUMENT',
80
+ usage: usage(),
81
+ });
82
+ }
83
+ index += 1;
84
+ continue;
85
+ }
86
+ throw new CliError(`未知 ${subcommand} 参数:${token}`, {
87
+ code: 'DESIGN_PLAN_INVALID_ARGUMENT',
88
+ usage: usage(),
89
+ });
90
+ }
91
+ }
92
+
53
93
  async function run(args) {
54
94
  const subcommand = args[0];
55
95
  if (!subcommand || args.includes('--help') || args.includes('-h')) {
@@ -63,6 +103,7 @@ async function run(args) {
63
103
  });
64
104
  }
65
105
  if (subcommand === 'catalog') {
106
+ assertKnownOptions(subcommand, args.slice(1));
66
107
  console.log(JSON.stringify(catalog(), null, 2));
67
108
  return;
68
109
  }
@@ -74,13 +115,14 @@ async function run(args) {
74
115
  });
75
116
  }
76
117
  const options = args.slice(2);
118
+ assertKnownOptions(subcommand, options);
77
119
  const outputDir = optionValue(options, '--output-dir');
78
120
  const result = subcommand === 'init'
79
121
  ? initialize(input, { outputDir, themeId: optionValue(options, '--theme-id') })
80
122
  : subcommand === 'preview'
81
123
  ? preview(input, { partFile: optionValue(options, '--part-file') })
82
124
  : subcommand === 'materialize'
83
- ? materialize(input, { outputDir, check: options.includes('--check'), fromPreview: options.includes('--from-preview'), businessFile: optionValue(options, '--business-file'), visualFile: optionValue(options, '--visual-file') })
125
+ ? materialize(input, { outputDir, check: options.includes('--check'), rebaseParts: options.includes('--rebase-parts'), fromPreview: options.includes('--from-preview'), businessFile: optionValue(options, '--business-file'), visualFile: optionValue(options, '--visual-file') })
84
126
  : patchPlan(input, setValues(options), { outputDir, materialize: options.includes('--materialize') });
85
127
 
86
128
  if (options.includes('--json') || subcommand === 'preview') {
@@ -2,6 +2,13 @@
2
2
 
3
3
  const { CliError } = require('../core/cli-error');
4
4
  const { t } = require('../core/i18n');
5
+ const ENTRY_MODES = Object.freeze(['unified', 'service-management', 'frontend-only', 'backend-only']);
6
+ const ENTRY_MODE_SUMMARIES = Object.freeze({
7
+ unified: '不分前后台:使用同一套业务页面,按职责显示菜单、数据和操作。',
8
+ 'service-management': '分前后台:办事的人使用前台,处理业务的人使用后台,两边共享业务数据。',
9
+ 'frontend-only': '只有访问前台:本次提供办理、查询或展示页面;需要处理的业务由已有系统、人员入口或自动流程承接。',
10
+ 'backend-only': '只有访问后台:工作人员录入、受理、处理或维护业务,办事入口在别处或本次不需要。',
11
+ });
5
12
  const filled = value => typeof value === 'string' && value.trim().length > 0;
6
13
  const object = value => value && typeof value === 'object' && !Array.isArray(value);
7
14
  const assert = (condition, message, details) => {
@@ -12,7 +19,7 @@ const assert = (condition, message, details) => {
12
19
  function buildEntryNavigation(plan, resources, pages) {
13
20
  const recommendation = plan.execution?.entryRecommendation;
14
21
  if (recommendation === undefined) {return null;}
15
- assert(object(recommendation) && ['unified', 'service-management', 'frontend-only'].includes(recommendation.mode), '入口方案需要明确 unified/service-management/frontend-only');
22
+ assert(object(recommendation) && ENTRY_MODES.includes(recommendation.mode), t('cli.design_plan_entry_mode_required'));
16
23
  assert(Array.isArray(recommendation.entries) && recommendation.entries.length > 0, '入口方案缺少 entries');
17
24
  const names = new Set(resources.map(resource => resource.name));
18
25
  const entryKeys = new Set();
@@ -83,7 +90,8 @@ function buildEntryNavigation(plan, resources, pages) {
83
90
  const roles = recommendation.entries.map(entry => entry.role);
84
91
  assert(recommendation.mode !== 'service-management' || (roles.includes('service') && roles.includes('management')), '前后台方案必须同时包含访客端和业务管理端');
85
92
  assert(recommendation.mode !== 'frontend-only' || roles.every(role => role === 'service'), '仅前台方案不能包含管理入口');
93
+ assert(recommendation.mode !== 'backend-only' || roles.every(role => role === 'management'), t('cli.design_plan_backend_only_roles'));
86
94
  return { recommendation: JSON.parse(JSON.stringify(recommendation)), platformOrder: [...new Set(platformOrder)] };
87
95
  }
88
96
 
89
- module.exports = { buildEntryNavigation };
97
+ module.exports = { buildEntryNavigation, ENTRY_MODES, ENTRY_MODE_SUMMARIES };
@@ -9,7 +9,7 @@ const { planBase } = require('./parallel');
9
9
  const { collectIssues } = require('./validate');
10
10
  const { assertVisualFieldObjects } = require('./normalize');
11
11
 
12
- const { PLAN_SKILL_ROOT: ROOT, loadThemeIndex, themeTemplatePath } = require('./themes');
12
+ const { PLAN_SKILL_ROOT: ROOT, loadThemeIndex, themeTemplatePath, themeNavTheme } = require('./themes');
13
13
 
14
14
  function list(value) {
15
15
  return Array.isArray(value) ? value : [];
@@ -57,7 +57,8 @@ function catalog() {
57
57
  const { themes, pagePatterns } = loadCatalog();
58
58
  return {
59
59
  success: true,
60
- themes: themes.map(({ themeId, label, templatePath, styleSummary }) => ({ themeId, label, templatePath, styleSummary })),
60
+ themes: themes.filter(theme => theme.mode !== 'creative').map(theme => ({ ...theme })),
61
+ creativeOption: themes.find(theme => theme.mode === 'creative'),
61
62
  pagePatterns: pagePatterns.map(({ id, label, mustKeep }) => ({ id, label, mustKeep })),
62
63
  };
63
64
  }
@@ -138,13 +139,14 @@ function initialize(inputPath, options = {}) {
138
139
  code: 'DESIGN_PLAN_THEME_UNKNOWN', details: { themes: themes.map(({ themeId, label }) => ({ themeId, label })), catalogCommand: 'openyida design-plan catalog --json' },
139
140
  });
140
141
  }
142
+ const navTheme = theme.mode === 'creative' ? brief.visualSelection?.navigationStyle?.tone || '' : themeNavTheme(theme);
141
143
  const outputDir = path.resolve(options.outputDir || path.join('prd', project));
142
144
  const output = path.join(outputDir, 'build-plan.json');
143
145
  const guide = path.join(outputDir, 'authoring-context.md');
144
146
  const business = path.join(outputDir, 'business.json');
145
147
  const visual = path.join(outputDir, 'visual.json');
146
148
  const materializeCommand = `openyida design-plan materialize ${quoteArgument(output)} --business-file ${quoteArgument(business)} --visual-file ${quoteArgument(visual)} --json`;
147
- if ([output, guide, business, visual].some(file => fs.existsSync(file))) {
149
+ if ([output, guide, business, visual, path.join(outputDir, require('./rebase').BASE_FILE)].some(file => fs.existsSync(file))) {
148
150
  throw new CliError('计划或编写上下文已存在,请继续补充现有文件', { code: 'DESIGN_PLAN_ALREADY_EXISTS' });
149
151
  }
150
152
  const scopedPages = list(brief.explicitScope?.pages);
@@ -211,9 +213,10 @@ function initialize(inputPath, options = {}) {
211
213
  ...(choice.tokens ? { tokens: choice.tokens } : {}),
212
214
  ...(choice.evidence ? { evidence: choice.evidence } : {}),
213
215
  ...(choice.constraints ? { constraints: choice.constraints } : {}),
216
+ ...(choice.creativeDirection ? { creativeDirection: choice.creativeDirection } : {}),
214
217
  forUser: { visualDirection: { ...(choice.visualDirection || { label: theme.label, description: theme.styleSummary }), source: visualSource },
215
218
  colorStrategy: { primaryColor: '', primaryColorName: '', source: '', usage: '', ...normalizeColorStrategy(choice.colorStrategy) },
216
- navigationStyle: choice.navigationStyle || { structure: '', tone: '', source: '', selectionReason: '' },
219
+ navigationStyle: { structure: '', source: '', selectionReason: '', ...(choice.navigationStyle || {}), tone: navTheme, toneSource: theme.mode === 'creative' ? 'project_defined' : 'theme_derived' },
217
220
  pageApplications: pages.map(page => ({ pageId: page.pageId, firstScreenFocus: '', layout: '', responsive: '', primaryAction: '', acceptanceChecks: [],
218
221
  visualMemoryApplications: [], ...list(choice.pageApplications).find(item => item?.pageId === page.pageId) })),
219
222
  ...(choice.assetStrategy ? { assetStrategy: choice.assetStrategy } : {}),
@@ -231,9 +234,11 @@ function initialize(inputPath, options = {}) {
231
234
  `读取本上下文、编写契约和 business.json;按 authoring.pendingFields 补齐事实,保留 base 与已有需求。business facts 填写 overview、dataModels、businessFlows、pages 和可选 execution;视觉填写 visual.json。复核业务覆盖后设置片段 ready=true,再执行:${materializeCommand}。使用成功结果中的 HTML 路径展示“当前方案”,revision 仅放在内部确认字段,不写入用户可见标题、摘要或问题。视觉片段按每页实际任务补齐焦点、布局、主操作、响应式与验收;已完成的选择直接复用。`, '',
232
235
  '## 本轮编写重点', '设计功能范围、数据与规则、页面组织、关键交互和业务验收。页面 blocks 按优先顺序填写 {name,purpose};CLI 派生默认内容优先级与内容层次,保留明确填写的独立设计。通用状态和验收检查由 CLI 补齐,interactionStates 与 acceptanceCriteria 填写项目差异。', '',
233
236
  '首页按使用者的主要任务选择:集中办事可用门户式工作台(workbench),单项任务可直达原生业务页;只有已确认的指标分析或投屏监控任务才规划 dashboard/screen。工作台按需求组织待办、操作队列、业务列表和常用入口,辅助统计不改变页面主任务。不得从后台身份、系统名称或暗色风格自动增加驾驶舱。完整规则见 yida-requirement-analysis/references/experience-groups.md 的“首页按任务选择”。', '',
234
- '前台、后台及共用工作区都要确定默认页面、菜单及分组顺序、打开后的首屏任务。menu 数组记录展示顺序,defaultMenuKey 记录默认任务;按角色需求决定,不按资源创建先后排列。平台导航通过排序命令落实,自定义导航通过页面菜单落实,frontend-only 也必须完成入口排序与首页验收。', '',
235
- '入口方案沿用 execution.entryRecommendation;逐入口补齐 role、menu 和 defaultMenuKey,访客端关联独立页面 sceneKey。任何身份的入口含 local 菜单时,entry.sceneKey 必须关联承载页面,menu.resource 等于页面 name,viewKey 非空。管理端可直接落到原生业务视图,不强制创建首页。所有叶子菜单按资源、视图和操作记录 access 权限依赖,平台导航排序从管理菜单派生。详见 yida-app/references/entry-navigation.md。', '',
236
- '保留 visual.json 中 facts.visualStyle.forUser.visualDirection 和 navigationStyle 的对象结构,只补齐内部字段;前者包含 label/description,后者包含 structure=top|side 和 tone=light|dark,不改写为文字字符串。', '',
237
+ '无论是否分前后台,每个业务入口都要确定默认页面、菜单及分组顺序、打开后的首屏任务。menu 数组记录展示顺序,defaultMenuKey 记录默认任务;按角色需求决定,不按资源创建先后排列。平台导航通过排序命令落实,自定义导航通过页面菜单落实,frontend-only 也必须完成入口排序与首页验收。', '',
238
+ '入口方案沿用 execution.entryRecommendation:unified 表示不分前后台,service-management 表示分前后台,frontend-only 表示只有访问前台,backend-only 表示只有访问后台。向用户说明谁使用、看到什么、能做什么;只有前台且提交后需要处理时,在 reason 和业务流程中说明承接方式;逐入口补齐 role、menu 和 defaultMenuKey,前台关联独立页面 sceneKey。任何身份的入口含 local 菜单时,entry.sceneKey 必须关联承载页面,menu.resource 等于页面 name,viewKey 非空。管理端可直接落到原生业务视图,不强制创建首页。所有叶子菜单按资源、视图和操作记录 access 权限依赖,平台导航排序从管理菜单派生。详见 yida-app/references/entry-navigation.md。', '',
239
+ theme.mode === 'creative'
240
+ ? '保留 visual.json 中 facts.visualStyle.forUser.visualDirection 和 navigationStyle 的对象结构;自由创意需明确填写 structure=top|side 和 tone=light|dark,并配套导航 Token。'
241
+ : `保留 visual.json 中 facts.visualStyle.forUser.visualDirection 和 navigationStyle 的对象结构,只补齐内部字段;前者包含 label/description,后者只需维护 structure=top|side。tone=${navTheme} 由已选主题模板派生,不要修改。`, '',
237
242
  '## 页面模式', ...patterns.map(item => `- ${item.id}(${item.label}):${item.mustKeep.join(';')}`), '',
238
243
  `## 已选风格:${theme.label}`, overview.trim(), '',
239
244
  '平台变量清单是基础契约,不是主题能力上限。visualStyle.tokens 可新增项目命名空间的材质、布局、字体、动效和组件状态变量,并在设计正文说明消费位置;平台变量可引用同一主题中已声明且无循环的扩展变量。不要为一次性页面样式强制新增 token。', '',
@@ -257,11 +262,13 @@ function initialize(inputPath, options = {}) {
257
262
  && ['top', 'side'].includes(selectedVisual.navigationStyle.structure)
258
263
  && ['light', 'dark'].includes(selectedVisual.navigationStyle.tone)
259
264
  && ['label', 'description'].every(key => typeof selectedVisual.visualDirection[key] === 'string' && selectedVisual.visualDirection[key].trim())
260
- && !collectIssues(plan).some(issue => issue.path.startsWith('visualStyle.forUser.pageApplications['));
265
+ && !collectIssues(plan).some(issue => issue.path.startsWith('visualStyle.forUser.pageApplications[')
266
+ || issue.path.startsWith('visualStyle.creativeDirection.') || issue.path.startsWith('visualStyle.tokens.'));
261
267
  const visualPart = `${JSON.stringify({ base, ready: visualReady, facts: { visualStyle: plan.visualStyle } }, null, 2)}\n`;
262
268
  const authoring = authoringGuidance(plan, business, visual, businessReady, visualReady);
263
269
  const formatContext = ['## 输入格式示例', '示例仅说明格式,按当前业务填写。', '```json', JSON.stringify(authoring.examples, null, 2), '```', '', authoring.completion, ''].join('\n');
264
- writeFiles([[output, `${JSON.stringify(plan, null, 2)}\n`], [guide, context + formatContext], [business, businessPart], [visual, visualPart]]);
270
+ const baseline = path.join(outputDir, require('./rebase').BASE_FILE);
271
+ writeFiles([[output, `${JSON.stringify(plan, null, 2)}\n`], [baseline, `${JSON.stringify({ schemaVersion: 1, base, plan }, null, 2)}\n`], [guide, context + formatContext], [business, businessPart], [visual, visualPart]]);
265
272
  return { success: true, input, output, context: guide, contract: path.join(ROOT, 'references/build-plan-compact-schema.md'), draft: true,
266
273
  materialize: { mode: 'complete_files_once', maxCalls: 1,
267
274
  shell: process.platform === 'win32' ? 'powershell' : 'posix',