openyida 2026.9.21 → 2026.9.22-beta.2
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 +14 -20
- package/bin/yida.js +6 -11
- package/lib/app/app-list.js +3 -7
- package/lib/app/canvas-compile.js +12 -22
- package/lib/app/check-prd-completeness.js +0 -7
- package/lib/app/create-app.js +1 -4
- package/lib/app/create-form/args.js +12 -37
- package/lib/app/create-form/batch.js +11 -86
- package/lib/app/create-form.js +86 -193
- package/lib/app/create-page.js +17 -84
- package/lib/app/custom-theme.js +0 -11
- package/lib/app/form-field-validator.js +41 -26
- package/lib/app/form-open-guard.js +10 -97
- package/lib/app/get-form-config.js +0 -1
- package/lib/app/inline-css-guard.js +31 -1
- package/lib/app/services/form-compiler.js +4 -2
- package/lib/app/theme-from-design.js +41 -144
- package/lib/app/theme-presets.js +1 -1
- package/lib/app/update-app.js +13 -63
- package/lib/asset/ai-image.js +9 -59
- package/lib/asset/asset-cmd.js +7 -18
- package/lib/asset/asset-plan.js +9 -102
- package/lib/asset/asset-resolve.js +10 -55
- package/lib/asset/asset-status.js +4 -5
- package/lib/asset/host-capabilities.js +2 -11
- package/lib/asset/url-verify.js +4 -19
- package/lib/auth/dws-sync.js +148 -0
- package/lib/auth/token-auth.js +73 -9
- package/lib/auth/token-store.js +50 -0
- package/lib/connector/connector-test.js +5 -19
- package/lib/connector/contract.js +1 -3
- package/lib/core/agent-capabilities.js +16 -19
- package/lib/core/command-manifest.js +40 -329
- package/lib/core/locales/en.js +17 -74
- package/lib/core/locales/zh.js +16 -73
- package/lib/core/redact.js +10 -15
- package/lib/core/sample.js +30 -72
- package/lib/core/utils.js +4 -0
- package/lib/design-plan/design-plan.js +4 -51
- package/lib/design-plan/init.js +26 -121
- package/lib/design-plan/materialize.js +348 -451
- package/lib/design-plan/normalize.js +29 -74
- package/lib/design-plan/parallel.js +16 -71
- package/lib/design-plan/patch.js +16 -63
- package/lib/design-plan/preview.js +8 -6
- package/lib/design-plan/validate.js +11 -71
- package/lib/process/create-process.js +50 -84
- package/lib/samples/openyida-scaffold/canvas-form-drawer.canvas.jsx +90 -20
- package/lib/samples/openyida-scaffold/canvas-nav/content.jsx +13 -93
- package/lib/samples/openyida-scaffold/canvas-nav/data.jsx +18 -100
- package/lib/samples/openyida-scaffold/canvas-nav/mixed.jsx +4 -6
- package/lib/samples/openyida-scaffold/canvas-nav/shared.jsx +5 -5
- package/lib/samples/openyida-scaffold/canvas-nav/side.jsx +2 -1
- package/lib/samples/openyida-scaffold/canvas-nav/sidebar.jsx +1 -1
- package/lib/samples/openyida-scaffold/canvas-nav/top.jsx +9 -15
- package/lib/samples/yida-canvas-table-form/table-form-batch-submit.canvas.jsx +83 -9
- package/lib/samples/yida-rechart/trend-combo.canvas.jsx +87 -14
- package/package.json +1 -3
- package/scripts/postinstall.js +3 -3
- package/yida-skills/SKILL.md +16 -19
- package/yida-skills/references/official-example-schema-patterns.md +1 -1
- package/yida-skills/references/yida-api.md +0 -4
- package/yida-skills/skills/yida-app/SKILL.md +23 -46
- package/yida-skills/skills/yida-app/workflow/parallel-work.md +6 -15
- 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 +15 -29
- package/yida-skills/skills/yida-app/workflow/plan/workflow.md +11 -29
- package/yida-skills/skills/yida-app/workflow/step-1-resource-context.md +2 -4
- package/yida-skills/skills/yida-app/workflow/step-2-design.md +9 -23
- package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +4 -6
- package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +4 -15
- package/yida-skills/skills/yida-app/workflow/step-6-main-page.md +4 -6
- package/yida-skills/skills/yida-app/workflow/step-7-page-code.md +9 -15
- package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +11 -25
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +28 -68
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +43 -42
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +44 -60
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-theme-provider.md +50 -72
- package/yida-skills/skills/yida-canvas-custom-page/references/component-library-guide.md +1 -13
- package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +1 -140
- package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +72 -101
- package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +32 -35
- package/yida-skills/skills/yida-canvas-custom-page/scripts/build-canvas-theme.js +3 -29
- package/yida-skills/skills/yida-canvas-custom-page/scripts/canvas-theme-provider.template.jsx +6 -69
- package/yida-skills/skills/yida-canvas-data-binding/SKILL.md +0 -26
- package/yida-skills/skills/yida-create-app/SKILL.md +0 -2
- package/yida-skills/skills/yida-create-form-page/SKILL.md +58 -72
- package/yida-skills/skills/yida-create-form-page/references/advanced-form-modes.md +5 -5
- package/yida-skills/skills/yida-create-form-page/references/association-form-field.md +4 -4
- package/yida-skills/skills/yida-create-form-page/references/batch-forms.md +5 -6
- package/yida-skills/skills/yida-create-form-page/references/field-definition-guide.md +19 -7
- package/yida-skills/skills/yida-create-form-page/references/form-field-properties.md +44 -62
- package/yida-skills/skills/yida-create-page/SKILL.md +6 -14
- package/yida-skills/skills/yida-create-process/SKILL.md +2 -13
- package/yida-skills/skills/yida-custom-page/SKILL.md +0 -2
- package/yida-skills/skills/yida-custom-page/references/design-system.md +1 -1
- package/yida-skills/skills/yida-dashboard/SKILL.md +4 -5
- package/yida-skills/skills/yida-data-management/SKILL.md +2 -4
- package/yida-skills/skills/yida-data-management/references/data-format-guide.md +4 -4
- package/yida-skills/skills/yida-design/SKILL.md +14 -29
- package/yida-skills/skills/yida-design/references/app/blueprint.md +70 -0
- package/yida-skills/skills/yida-design/references/app/navigation-patterns.md +43 -0
- package/yida-skills/skills/yida-design/references/app/role-journey.md +30 -0
- package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +42 -67
- package/yida-skills/skills/yida-design/references/asset-workflow.md +2 -6
- package/yida-skills/skills/yida-design/references/design-mode.md +4 -4
- package/yida-skills/skills/yida-design/references/page-quality-gates.md +38 -26
- package/yida-skills/skills/yida-design/references/style-design-selection.md +137 -0
- package/yida-skills/skills/yida-design/references/style-designs/_design-md-template.md +185 -0
- package/yida-skills/skills/yida-design/references/style-designs/aqua-service-progress-dashboard.md +242 -0
- package/yida-skills/skills/yida-design/references/style-designs/blue-insight-operations-dashboard.md +243 -0
- package/yida-skills/skills/yida-design/references/style-designs/blue-productivity-insight-workbench.md +242 -0
- package/yida-skills/skills/yida-design/references/style-designs/command-filter-card-console.md +304 -0
- package/yida-skills/skills/yida-design/references/style-designs/contrast-command-analytics-workbench.md +261 -0
- package/yida-skills/skills/yida-design/references/style-designs/dark-stage-analytic-dashboard.md +369 -0
- package/yida-skills/skills/yida-design/references/style-designs/filterable-card-catalog.md +354 -0
- package/yida-skills/skills/yida-design/references/style-designs/green-timeline-progress-workbench.md +250 -0
- package/yida-skills/skills/yida-design/references/style-designs/registry.md +117 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-analytic-workbench.md +348 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-blue-grid-analytic-dashboard.md +658 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-bordered-analytic-workbench.md +385 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-curated-filter-gallery.md +300 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-modular-analytic-workbench.md +404 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-progress-analytics-workbench.md +382 -0
- package/yida-skills/skills/yida-design/references/style-designs/soft-timeline-analytics-workbench.md +367 -0
- package/yida-skills/skills/yida-design/references/style-designs/teal-rail-analytics-workbench.md +341 -0
- package/yida-skills/skills/yida-design/references/theme/app-custom-theme-template.css +432 -350
- package/yida-skills/skills/yida-design/references/visual-decision-engine.md +143 -19
- package/yida-skills/skills/yida-design/references/visual-scaffold-recipes.md +27 -15
- package/yida-skills/skills/yida-design/sub_skill/page-design/SKILL.md +0 -4
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/SKILL.md +8 -22
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/assets/README.md +10 -14
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/assets/build-plan-template.html +4 -9
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-compact-schema.md +27 -49
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +154 -33
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-design.md +127 -48
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-theme-selection.md +51 -37
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/render_build_plan.py +48 -43
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/validate_design_themes.py +190 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/README.md +98 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-media-grid.md +339 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-modular-clarity.md +348 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-structured-clarity.md +344 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ambient-halo-layered.md +345 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-focus-layered.md +355 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-luminous-modular.md +377 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/deep-stage-duotone.md +352 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/hairline-runway-clarity.md +389 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/high-contrast-modular.md +364 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/index.json +329 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/interlocked-vivid-modules.md +348 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/matrix-grid-focus.md +345 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/media-rail-inspector.md +379 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mist-layered-signal.md +347 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/modular-rail-signal.md +352 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mono-grid-signal.md +386 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ribbon-ledger-lift.md +384 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/segmented-meter-clarity.md +388 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/split-rail-precision.md +341 -0
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/status-framed-media-grid.md +361 -0
- package/yida-skills/skills/yida-design/workflow/output-design.md +375 -117
- package/yida-skills/skills/yida-design/workflow/output-prd.md +185 -0
- package/yida-skills/skills/yida-design/workflow/step-1-read-brief.md +1 -1
- package/yida-skills/skills/yida-design/workflow/step-2-theme-system.md +29 -33
- package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +1 -3
- package/yida-skills/skills/yida-design/workflow/step-5-visual-states.md +106 -22
- package/yida-skills/skills/yida-design/workflow/step-6-handoff.md +22 -15
- package/yida-skills/skills/yida-form-permission/SKILL.md +0 -6
- package/yida-skills/skills/yida-image-assets/SKILL.md +50 -73
- package/yida-skills/skills/yida-image-assets/references/manifest-contract.md +52 -82
- package/yida-skills/skills/yida-image-assets/references/source-policy.md +15 -14
- package/yida-skills/skills/yida-integration/SKILL.md +0 -1
- package/yida-skills/skills/yida-login/SKILL.md +0 -2
- package/yida-skills/skills/yida-nav-group/SKILL.md +2 -4
- package/yida-skills/skills/yida-nav-shell/SKILL.md +12 -36
- package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +136 -79
- package/yida-skills/skills/yida-page-config/SKILL.md +4 -13
- package/yida-skills/skills/yida-prd/SKILL.md +3 -5
- package/yida-skills/skills/yida-prd/references/app/blueprint.md +4 -4
- package/yida-skills/skills/yida-prd/references/app/navigation-patterns.md +3 -6
- package/yida-skills/skills/yida-prd/references/app/role-journey.md +3 -5
- package/yida-skills/skills/yida-prd/references/plan-content.md +0 -4
- package/yida-skills/skills/yida-prd/workflow/output-prd.md +11 -18
- package/yida-skills/skills/yida-prd/workflow/plan-business.md +4 -4
- package/yida-skills/skills/yida-prd/workflow/step-2-information-architecture.md +8 -11
- package/yida-skills/skills/yida-publish-page/SKILL.md +1 -9
- package/yida-skills/skills/yida-requirement-analysis/SKILL.md +39 -10
- package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +71 -40
- package/yida-skills/skills-index.json +16 -36
- package/lib/app/application-entry-urls.js +0 -31
- package/lib/app/application-style.js +0 -69
- package/lib/app/canvas-icon-exports.json +0 -6160
- package/lib/app/canvas-icon-guard.js +0 -87
- package/lib/app/canvas-navigation-guard.js +0 -84
- package/lib/app/canvas-path-guard.js +0 -78
- package/lib/app/canvas-theme-guard.js +0 -184
- package/lib/app/form-data-response-guard.js +0 -57
- package/lib/asset/asset-execution.js +0 -79
- package/lib/core/css-structure.js +0 -37
- package/lib/design/check-design.js +0 -51
- package/lib/design/document.js +0 -458
- package/lib/design/icon-contrast.js +0 -85
- package/lib/design/page-content.js +0 -11
- package/lib/design-plan/artifact-updates.js +0 -117
- package/lib/design-plan/confirmation.js +0 -13
- package/lib/design-plan/entry-navigation.js +0 -97
- package/lib/design-plan/navigation-policy.js +0 -38
- package/lib/design-plan/rebase.js +0 -60
- package/lib/design-plan/themes.js +0 -101
- package/lib/design-plan/visual-policy.js +0 -42
- package/lib/samples/openyida-scaffold/canvas-admin-entry.jsx +0 -62
- package/lib/samples/openyida-scaffold/canvas-navigation.jsx +0 -56
- package/lib/samples/openyida-scaffold/canvas-view-state.jsx +0 -74
- package/yida-skills/skills/yida-app/references/entry-navigation.md +0 -193
- package/yida-skills/skills/yida-canvas-custom-page/references/view-state-recovery.md +0 -52
- package/yida-skills/skills/yida-canvas-data-binding/references/business-action-permissions.md +0 -31
- package/yida-skills/skills/yida-design/references/application-style-library.md +0 -93
- package/yida-skills/skills/yida-design/references/application-theme-consistency.md +0 -150
- package/yida-skills/skills/yida-design/references/native-form-styles.md +0 -208
- package/yida-skills/skills/yida-design/references/navigation-decision.md +0 -53
- package/yida-skills/skills/yida-design/references/page-continuity.md +0 -42
- package/yida-skills/skills/yida-design/references/theme/application-style-recipes.css +0 -58
- package/yida-skills/skills/yida-design/references/theme-selection.md +0 -67
- package/yida-skills/skills/yida-design/scripts/validate_design_themes.py +0 -302
- package/yida-skills/skills/yida-design/templates/application-styles.json +0 -398
- package/yida-skills/skills/yida-design/templates/design-themes/README.md +0 -73
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/form-layout.json +0 -15
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/app_theme.css +0 -754
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/design.md +0 -279
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/app_theme.css +0 -758
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/design.md +0 -281
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/app_theme.css +0 -754
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/design.md +0 -279
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/app_theme.css +0 -752
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/design.md +0 -278
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/app_theme.css +0 -748
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/design.md +0 -274
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/form-layout.json +0 -14
- package/yida-skills/skills/yida-design/templates/design-themes/basic-tokens.json +0 -118
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/app_theme.css +0 -690
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/design.md +0 -382
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/app_theme.css +0 -691
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/design.md +0 -380
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/app_theme.css +0 -696
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/design.md +0 -378
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/app_theme.css +0 -689
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/design.md +0 -238
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/app_theme.css +0 -692
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/design.md +0 -375
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/app_theme.css +0 -691
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/design.md +0 -362
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/app_theme.css +0 -691
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/design.md +0 -409
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/index.json +0 -464
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/app_theme.css +0 -701
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/design.md +0 -379
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/app_theme.css +0 -691
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/design.md +0 -404
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/app_theme.css +0 -698
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/design.md +0 -389
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/app_theme.css +0 -691
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/design.md +0 -372
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/app_theme.css +0 -690
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/design.md +0 -455
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/app_theme.css +0 -698
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/design.md +0 -394
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/app_theme.css +0 -692
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/design.md +0 -391
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/app_theme.css +0 -711
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/design.md +0 -408
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/app_theme.css +0 -692
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/design.md +0 -383
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/form-layout.json +0 -13
- package/yida-skills/skills/yida-design/templates/navigation-styles.json +0 -769
- package/yida-skills/skills/yida-requirement-analysis/references/experience-groups.md +0 -79
- package/yida-skills/skills/yida-requirement-analysis/references/handoff.md +0 -68
package/lib/design/document.js
DELETED
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const yaml = require('js-yaml');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const { CliError } = require('../core/cli-error');
|
|
7
|
-
const { t } = require('../core/i18n');
|
|
8
|
-
const { concretePageText } = require('./page-content');
|
|
9
|
-
const { validateIconContrast } = require('./icon-contrast');
|
|
10
|
-
const CONTRACT = require('../../yida-skills/skills/yida-design/templates/design-themes/basic-tokens.json');
|
|
11
|
-
|
|
12
|
-
const HEADINGS = ['1. 风格摘要', '2. 页面视觉系统', '3. 基础组件表达', '4. 特色表达配方', '5. 项目应用与调整规则'];
|
|
13
|
-
const PAGE_LABELS = ['页面任务', '首屏焦点', '布局', '表面与组件', '主操作', '状态', '响应式', '验收'];
|
|
14
|
-
const BRAND_TOKENS = [1, 2, 3, 5, 6, 9, 10].map(n => `--color-brand1-${n}`);
|
|
15
|
-
const TOKEN_NAME = /^--[a-zA-Z_][\w-]*$/;
|
|
16
|
-
const ANCHOR = /^#[a-zA-Z][\w.:-]*$/;
|
|
17
|
-
const REFERENCE_KEY = /^[\p{L}\p{N}_-]+$/u;
|
|
18
|
-
const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
19
|
-
const nonempty = value => typeof value === 'string' && value.trim().length > 0;
|
|
20
|
-
|
|
21
|
-
function invalid(field, code, details = {}) {
|
|
22
|
-
throw new CliError(t('design_document.invalid', field, code), {
|
|
23
|
-
code: 'DESIGN_DOCUMENT_INVALID', details: { field, issue: code, ...details },
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function jsonValue(value, field = 'metadata', active = new Set(), visited = new Set(), depth = 0) {
|
|
28
|
-
if (depth > 128) {invalid(field, 'MAX_DEPTH');}
|
|
29
|
-
if (value === null || ['string', 'boolean'].includes(typeof value)) {return;}
|
|
30
|
-
if (typeof value === 'number' && Number.isFinite(value)) {return;}
|
|
31
|
-
if (!object(value) && !Array.isArray(value)) {invalid(field, 'JSON_VALUE_REQUIRED');}
|
|
32
|
-
if (active.has(value)) {invalid(field, 'CYCLIC_ALIAS');}
|
|
33
|
-
if (visited.has(value)) {return;}
|
|
34
|
-
if (!Array.isArray(value) && ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
|
|
35
|
-
invalid(field, 'PLAIN_MAPPING_REQUIRED');
|
|
36
|
-
}
|
|
37
|
-
active.add(value);
|
|
38
|
-
for (const [key, child] of Object.entries(value)) {
|
|
39
|
-
if (['__proto__', 'constructor', 'prototype'].includes(key)) {invalid(`${field}.${key}`, 'UNSAFE_KEY');}
|
|
40
|
-
jsonValue(child, `${field}.${key}`, active, visited, depth + 1);
|
|
41
|
-
}
|
|
42
|
-
active.delete(value);
|
|
43
|
-
visited.add(value);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function parseDesignDocument(markdown, options = {}) {
|
|
47
|
-
if (typeof markdown !== 'string') {invalid('document', 'STRING_REQUIRED');}
|
|
48
|
-
const match = /^\uFEFF?---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(markdown);
|
|
49
|
-
if (!match) {invalid('frontmatter', 'FRONTMATTER_REQUIRED');}
|
|
50
|
-
let metadata;
|
|
51
|
-
try {
|
|
52
|
-
const load = source => yaml.load(source, { schema: yaml.JSON_SCHEMA, json: false });
|
|
53
|
-
metadata = load(match[1]);
|
|
54
|
-
// Historical Fast files allowed bare HEX tokens. Opt in only for consumers
|
|
55
|
-
// reading unversioned files; final document validation always uses real YAML.
|
|
56
|
-
if (options.legacyTokenValues === true && object(metadata)
|
|
57
|
-
&& !Object.prototype.hasOwnProperty.call(metadata, 'schemaVersion')) {
|
|
58
|
-
const compatible = match[1].replace(/^(\s*["']?--[\w-]+["']?\s*:\s*)(#[\da-f]{3,8})([ \t]*(?:#.*)?)$/gmi,
|
|
59
|
-
(line, prefix, color, comment) => `${prefix}${JSON.stringify(color)}${comment}`);
|
|
60
|
-
if (compatible !== match[1]) {metadata = load(compatible);}
|
|
61
|
-
}
|
|
62
|
-
} catch (error) {
|
|
63
|
-
throw new CliError(t('design_document.yaml'), {
|
|
64
|
-
code: 'DESIGN_DOCUMENT_INVALID', details: { field: 'frontmatter', issue: 'INVALID_YAML', reason: error.reason || error.message },
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
if (!object(metadata)) {invalid('frontmatter', 'MAPPING_REQUIRED');}
|
|
68
|
-
jsonValue(metadata);
|
|
69
|
-
return { metadata, body: markdown.slice(match[0].length).replace(/\r\n/g, '\n') };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function serializeDesignDocument(metadata, body) {
|
|
73
|
-
if (body === undefined && object(metadata) && object(metadata.metadata) && typeof metadata.body === 'string') {
|
|
74
|
-
({ metadata, body } = metadata);
|
|
75
|
-
}
|
|
76
|
-
if (!object(metadata) || typeof body !== 'string') {invalid('document', 'METADATA_AND_BODY_REQUIRED');}
|
|
77
|
-
jsonValue(metadata);
|
|
78
|
-
const lines = ['---'];
|
|
79
|
-
const keyText = key => /^[a-zA-Z_][\w-]*$/.test(key) ? key : JSON.stringify(key);
|
|
80
|
-
function tokenLines(node, indent) {
|
|
81
|
-
return Object.entries(node).map(([key, value]) => {
|
|
82
|
-
const prefix = `${' '.repeat(indent)}${keyText(key)}:`;
|
|
83
|
-
return object(value) && Object.keys(value).length
|
|
84
|
-
? `${prefix}\n${tokenLines(value, indent + 2).join('\n')}`
|
|
85
|
-
: `${prefix} ${JSON.stringify(value)}`;
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
for (const [key, value] of Object.entries(metadata)) {
|
|
89
|
-
if (key === 'tokens' && object(value) && Object.keys(value).length) {
|
|
90
|
-
lines.push('tokens:', ...tokenLines(value, 2));
|
|
91
|
-
} else {
|
|
92
|
-
// JSON is a YAML subset and keeps names and arbitrary user text safely quoted.
|
|
93
|
-
// assetStrategy deliberately remains one line for existing stored documents.
|
|
94
|
-
lines.push(`${keyText(key)}: ${JSON.stringify(value)}`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
lines.push('---', '', body.trim(), '');
|
|
98
|
-
return lines.join('\n');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function cssValue(name, raw) {
|
|
102
|
-
const value = typeof raw === 'number' && Number.isFinite(raw) ? String(raw) : raw;
|
|
103
|
-
if (!TOKEN_NAME.test(name) || typeof value !== 'string' || !value.trim()
|
|
104
|
-
|| /[;{}\r\n<>]|\/\*|\*\//.test(value) || /^[|>]/.test(value)
|
|
105
|
-
|| /(?:javascript|data|vbscript|file|blob)\s*:/i.test(value)) {
|
|
106
|
-
throw new CliError(t('design_document.token_value', name), { code: 'DESIGN_THEME_TOKEN_INVALID', details: { token: name } });
|
|
107
|
-
}
|
|
108
|
-
if (['--pod-app-root-bg-color', '--pod-page-bg-color', '--pod-card-bg-color'].includes(name)
|
|
109
|
-
&& /(?:gradient|url)\s*\(/i.test(value)) {
|
|
110
|
-
invalid(name, 'COLOR_CANNOT_BE_IMAGE');
|
|
111
|
-
}
|
|
112
|
-
let quote = '';
|
|
113
|
-
let depth = 0;
|
|
114
|
-
for (let index = 0; index < value.length; index++) {
|
|
115
|
-
const char = value[index];
|
|
116
|
-
if (char === '\\') {index++; continue;}
|
|
117
|
-
if (quote) {if (char === quote) {quote = '';} continue;}
|
|
118
|
-
if (char === '"' || char === "'") {quote = char;}
|
|
119
|
-
else if (char === '(') {depth++;}
|
|
120
|
-
else if (char === ')' && --depth < 0) {invalid(name, 'UNBALANCED_CSS_VALUE');}
|
|
121
|
-
}
|
|
122
|
-
if (quote || depth) {invalid(name, 'UNBALANCED_CSS_VALUE');}
|
|
123
|
-
return value;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function flattenTokens(node, target, field, strict) {
|
|
127
|
-
if (!object(node)) {invalid(field, 'TOKEN_MAPPING_REQUIRED');}
|
|
128
|
-
for (const [key, value] of Object.entries(node)) {
|
|
129
|
-
if (key.startsWith('--')) {
|
|
130
|
-
const scalar = cssValue(key, value);
|
|
131
|
-
if (Object.prototype.hasOwnProperty.call(target, key) && (strict || target[key] !== scalar)) {
|
|
132
|
-
throw new CliError(t('design_document.token_conflict', key), { code: 'DESIGN_THEME_TOKEN_CONFLICT', details: { token: key } });
|
|
133
|
-
}
|
|
134
|
-
if (/^--color-brand1-(4|7|8)$/.test(key)) {invalid(key, 'UNSUPPORTED_BRAND_TOKEN');}
|
|
135
|
-
target[key] = scalar;
|
|
136
|
-
} else {
|
|
137
|
-
flattenTokens(value, target, `${field}.${key}`, strict);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function validateTokenReferences(tokens) {
|
|
143
|
-
const dependencies = new Map(Object.entries(tokens).map(([name, value]) => [name, [...value.matchAll(/var\(\s*(--[\w-]+)/g)].map(match => match[1])]));
|
|
144
|
-
for (const [name, refs] of dependencies) {
|
|
145
|
-
for (const ref of refs) {
|
|
146
|
-
if (!(ref in tokens)) {invalid(name, 'UNDECLARED_TOKEN_REFERENCE', { reference: ref });}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
const active = new Set();
|
|
150
|
-
const visited = new Set();
|
|
151
|
-
function visit(name) {
|
|
152
|
-
if (active.has(name)) {invalid(name, 'TOKEN_REFERENCE_CYCLE');}
|
|
153
|
-
if (visited.has(name)) {return;}
|
|
154
|
-
active.add(name);
|
|
155
|
-
for (const ref of dependencies.get(name)) {visit(ref);}
|
|
156
|
-
active.delete(name);
|
|
157
|
-
visited.add(name);
|
|
158
|
-
}
|
|
159
|
-
for (const name of dependencies.keys()) {visit(name);}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function extractDesignTokens(metadata, options = {}) {
|
|
163
|
-
const strict = options.strict === true;
|
|
164
|
-
if (!object(metadata) || !object(metadata.tokens)) {
|
|
165
|
-
throw new CliError(t('design_document.tokens_required'), { code: 'DESIGN_THEME_TOKENS_REQUIRED' });
|
|
166
|
-
}
|
|
167
|
-
jsonValue(metadata.tokens, 'tokens');
|
|
168
|
-
const tokens = {};
|
|
169
|
-
if (strict) {
|
|
170
|
-
if (Object.keys(metadata.tokens).length !== 2 || !object(metadata.tokens['application-global']) || !object(metadata.tokens['custom-page'])) {
|
|
171
|
-
invalid('tokens', 'GLOBAL_AND_CUSTOM_SCOPES_REQUIRED');
|
|
172
|
-
}
|
|
173
|
-
const globals = metadata.tokens['application-global'];
|
|
174
|
-
if (Object.keys(globals).sort().join('|') !== Object.keys(CONTRACT.groups).sort().join('|')) {
|
|
175
|
-
invalid('tokens.application-global', 'SIX_GLOBAL_GROUPS_REQUIRED');
|
|
176
|
-
}
|
|
177
|
-
for (const [group, expected] of Object.entries(CONTRACT.groups)) {
|
|
178
|
-
const values = {};
|
|
179
|
-
flattenTokens(globals[group], values, `tokens.application-global.${group}`, true);
|
|
180
|
-
const otherGroups = Object.entries(CONTRACT.groups).filter(([name]) => name !== group).flatMap(([, names]) => names);
|
|
181
|
-
if (expected.some(name => !(name in values)) || otherGroups.some(name => name in values)) {
|
|
182
|
-
invalid(`tokens.application-global.${group}`, 'GLOBAL_TOKEN_SET_MISMATCH', { expected });
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
for (const scope of ['application-global', 'custom-page']) {
|
|
186
|
-
flattenTokens(metadata.tokens[scope], tokens, `tokens.${scope}`, true);
|
|
187
|
-
}
|
|
188
|
-
for (const name of ['--color-white', '--pod-table-cell-color', '--color-fill1-10', '--color-text1-5']) {
|
|
189
|
-
if (tokens[name] !== CONTRACT.fixedValues[name]) {invalid(name, 'FIXED_PLATFORM_VALUE', { expected: CONTRACT.fixedValues[name] });}
|
|
190
|
-
}
|
|
191
|
-
// Both metadata groups are emitted into the application's :root. Shared
|
|
192
|
-
// project tokens can back platform roles; only missing/cyclic refs are invalid.
|
|
193
|
-
validateTokenReferences(tokens);
|
|
194
|
-
} else {
|
|
195
|
-
flattenTokens(metadata.tokens, tokens, 'tokens', false);
|
|
196
|
-
}
|
|
197
|
-
const missing = BRAND_TOKENS.filter(name => !tokens[name]);
|
|
198
|
-
if (missing.length) {
|
|
199
|
-
throw new CliError(t('design_document.brand_required', missing.join(', ')), {
|
|
200
|
-
code: 'DESIGN_THEME_TOKENS_REQUIRED', details: { missing },
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
validateIconContrast(metadata.iconSystem, tokens);
|
|
204
|
-
return tokens;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Strip fenced examples before interpreting document headings, anchors or labels.
|
|
208
|
-
function structuralLines(body) {
|
|
209
|
-
const lines = body.split('\n');
|
|
210
|
-
let fence = null;
|
|
211
|
-
return lines.map(line => {
|
|
212
|
-
const open = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line);
|
|
213
|
-
if (!fence && open) {fence = open[1]; return '';}
|
|
214
|
-
if (fence) {
|
|
215
|
-
if (new RegExp(`^ {0,3}${fence[0]}{${fence.length},}\\s*$`).test(line)) {fence = null;}
|
|
216
|
-
return '';
|
|
217
|
-
}
|
|
218
|
-
return line;
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function documentAnchors(body) {
|
|
223
|
-
const text = structuralLines(body).join('\n');
|
|
224
|
-
const anchors = new Map();
|
|
225
|
-
for (const match of text.matchAll(/<a\s+id=(?:"([^"]+)"|'([^']+)')\s*>\s*<\/a>/g)) {
|
|
226
|
-
const id = '#' + (match[1] || match[2]);
|
|
227
|
-
if (!ANCHOR.test(id)) {invalid('body', 'INVALID_ANCHOR', { anchor: id });}
|
|
228
|
-
if (anchors.has(id)) {invalid('body', 'DUPLICATE_ANCHOR', { anchor: id });}
|
|
229
|
-
anchors.set(id, match.index);
|
|
230
|
-
}
|
|
231
|
-
return { text, anchors };
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function inlineValue(value) {
|
|
235
|
-
const text = value.trim();
|
|
236
|
-
const code = /^(`+)([^`]*)\1$/.exec(text);
|
|
237
|
-
return code ? code[2].trim() : text;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function fastPrdPages(prdMarkdown) {
|
|
241
|
-
const lines = structuralLines(prdMarkdown);
|
|
242
|
-
const starts = lines.map((line, index) => /^## 4\. 页面与功能设计\s*$/.test(line) ? index : -1).filter(index => index >= 0);
|
|
243
|
-
if (starts.length !== 1) {invalid('prd', 'ONE_STRUCTURED_HANDOFF_REQUIRED', { count: 0 });}
|
|
244
|
-
const end = lines.findIndex((line, index) => index > starts[0] && /^## /.test(line));
|
|
245
|
-
const section = lines.slice(starts[0] + 1, end < 0 ? undefined : end);
|
|
246
|
-
const headings = section.map((line, index) => /^### /.test(line) ? index : -1).filter(index => index >= 0);
|
|
247
|
-
const pages = [];
|
|
248
|
-
for (let index = 0; index < headings.length; index++) {
|
|
249
|
-
const entries = section.slice(headings[index] + 1, headings[index + 1]).flatMap(line => {
|
|
250
|
-
const match = /^([ \t]*)[-*+]\s+(.+?)[::][ \t]*(.*?)\s*$/.exec(line);
|
|
251
|
-
return match ? [{ indent: match[1].replace(/\t/g, ' ').length, key: inlineValue(match[2]).replace(/^\*\*|\*\*$/g, ''), value: match[3] }] : [];
|
|
252
|
-
});
|
|
253
|
-
if (!entries.length) {continue;}
|
|
254
|
-
const topIndent = Math.min(...entries.map(entry => entry.indent));
|
|
255
|
-
const top = entries.filter(entry => entry.indent === topIndent);
|
|
256
|
-
const field = (records, key) => {
|
|
257
|
-
const matches = records.filter(entry => entry.key === key);
|
|
258
|
-
if (matches.length > 1) {invalid(`prd.${key}`, 'DUPLICATE_PRD_FIELD');}
|
|
259
|
-
return matches[0];
|
|
260
|
-
};
|
|
261
|
-
if (inlineValue(field(top, '页面类型')?.value || '') !== 'display-page') {continue;}
|
|
262
|
-
const pageId = inlineValue(field(top, 'pageId')?.value || '');
|
|
263
|
-
const parent = field(top, 'pageSpecHandoff');
|
|
264
|
-
if (!parent) {invalid(`prd.pages.${pageId}.pageSpecHandoff`, 'PAGE_HANDOFF_REQUIRED');}
|
|
265
|
-
const following = entries.slice(entries.indexOf(parent) + 1);
|
|
266
|
-
const nextTop = following.findIndex(entry => entry.indent <= topIndent);
|
|
267
|
-
const children = following.slice(0, nextTop < 0 ? undefined : nextTop);
|
|
268
|
-
const childIndent = Math.min(...children.map(entry => entry.indent));
|
|
269
|
-
const direct = children.filter(entry => entry.indent === childIndent);
|
|
270
|
-
const designFile = inlineValue(field(direct, 'designFile')?.value || '');
|
|
271
|
-
const rawRefs = inlineValue(field(direct, 'designRefs')?.value || '');
|
|
272
|
-
let designRefs;
|
|
273
|
-
if (rawRefs.startsWith('[')) {
|
|
274
|
-
try {designRefs = JSON.parse(rawRefs);} catch {invalid('prd.designRefs', 'INVALID_DESIGN_REFERENCE');}
|
|
275
|
-
} else {
|
|
276
|
-
designRefs = rawRefs ? rawRefs.split(/[/、,,]/).map(inlineValue) : [];
|
|
277
|
-
}
|
|
278
|
-
pages.push({ pageId, pageSpecHandoff: { designFile, designRefs } });
|
|
279
|
-
}
|
|
280
|
-
return pages;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function prdHandoffPages(prdMarkdown) {
|
|
284
|
-
const candidates = [];
|
|
285
|
-
const lines = prdMarkdown.replace(/\r\n/g, '\n').split('\n');
|
|
286
|
-
const declared = structuralLines(prdMarkdown).some(line => /^#{2,6} (?:\d+\.\s*)?结构化交接\s*$/.test(line));
|
|
287
|
-
let fence;
|
|
288
|
-
let content = [];
|
|
289
|
-
for (const line of lines) {
|
|
290
|
-
const start = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line);
|
|
291
|
-
if (!fence && start) {fence = { marker: start[1], info: start[2].trim() }; content = []; continue;}
|
|
292
|
-
if (!fence) {continue;}
|
|
293
|
-
if (!new RegExp(`^ {0,3}${fence.marker[0]}{${fence.marker.length},}\\s*$`).test(line)) {content.push(line); continue;}
|
|
294
|
-
if (fence.info === 'json') {
|
|
295
|
-
const source = content.join('\n');
|
|
296
|
-
let value;
|
|
297
|
-
try {value = JSON.parse(source);} catch {
|
|
298
|
-
if (declared || /"pages"\s*:/.test(source) && /"(?:appConfig|resourceBlueprint)"\s*:/.test(source)) {
|
|
299
|
-
invalid('prd', 'INVALID_STRUCTURED_HANDOFF');
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if (object(value) && 'pages' in value && ('appConfig' in value || 'resourceBlueprint' in value)) {
|
|
303
|
-
if (!Array.isArray(value.pages) || !(object(value.appConfig) || Array.isArray(value.resourceBlueprint))) {
|
|
304
|
-
invalid('prd', 'INVALID_STRUCTURED_HANDOFF');
|
|
305
|
-
}
|
|
306
|
-
candidates.push(value);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
fence = null;
|
|
310
|
-
}
|
|
311
|
-
if (fence?.info === 'json' && /"pages"\s*:/.test(content.join('\n'))
|
|
312
|
-
&& /"(?:appConfig|resourceBlueprint)"\s*:/.test(content.join('\n'))) {invalid('prd', 'INVALID_STRUCTURED_HANDOFF');}
|
|
313
|
-
if (candidates.length > 1) {invalid('prd', 'ONE_STRUCTURED_HANDOFF_REQUIRED', { count: candidates.length });}
|
|
314
|
-
if (candidates.length === 1) {return candidates[0].pages;}
|
|
315
|
-
if (declared) {invalid('prd', 'INVALID_STRUCTURED_HANDOFF');}
|
|
316
|
-
return fastPrdPages(prdMarkdown);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/** Compare existing document locations through symlinks; retain logical paths before files are written. */
|
|
320
|
-
function documentLocation(file) {
|
|
321
|
-
const resolved = path.resolve(file);
|
|
322
|
-
try {return fs.realpathSync(resolved);} catch (error) {
|
|
323
|
-
if (!['ENOENT', 'ENOTDIR'].includes(error.code)) {throw error;}
|
|
324
|
-
return resolved;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function validatePrd(prdMarkdown, metadata, pageIds, options) {
|
|
329
|
-
const prdPages = new Set();
|
|
330
|
-
for (const page of prdHandoffPages(prdMarkdown)) {
|
|
331
|
-
if (!object(page)) {invalid('prd.pages', 'PAGE_MAPPING_REQUIRED');}
|
|
332
|
-
if (!object(page.pageSpecHandoff)) {continue;}
|
|
333
|
-
const handoff = page.pageSpecHandoff;
|
|
334
|
-
if (!nonempty(page.pageId) || prdPages.has(page.pageId)) {invalid('prd.pages', 'UNIQUE_PAGE_ID_REQUIRED');}
|
|
335
|
-
prdPages.add(page.pageId);
|
|
336
|
-
if (!nonempty(handoff.designFile)) {invalid(`prd.pages.${page.pageId}.designFile`, 'DESIGN_FILE_REQUIRED');}
|
|
337
|
-
// A supplied document location is always checked. Relocated bundles must
|
|
338
|
-
// explicitly provide their project root instead of skipping relative paths.
|
|
339
|
-
if (options.designFile) {
|
|
340
|
-
const expected = documentLocation(options.designFile);
|
|
341
|
-
const resolved = documentLocation(path.resolve(options.baseDir || process.cwd(), handoff.designFile));
|
|
342
|
-
if (resolved !== expected) {
|
|
343
|
-
invalid(`prd.pages.${page.pageId}.designFile`, 'DESIGN_FILE_MISMATCH', { actual: handoff.designFile, resolved, expected });
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
if (!Array.isArray(handoff.designRefs) || !handoff.designRefs.length) {invalid('prd.designRefs', 'NONEMPTY_ARRAY_REQUIRED');}
|
|
347
|
-
for (const ref of handoff.designRefs) {
|
|
348
|
-
if (typeof ref !== 'string' || !/^(themeProfile|(?:sceneRecipes|components|states)\.[\p{L}\p{N}_-]+)$/u.test(ref)) {
|
|
349
|
-
invalid('prd.designRefs', 'INVALID_DESIGN_REFERENCE', { reference: ref });
|
|
350
|
-
}
|
|
351
|
-
const [root, key] = ref.split('.');
|
|
352
|
-
const target = key === undefined ? metadata[root] : metadata[root]?.[key];
|
|
353
|
-
if (!target) {invalid('prd.designRefs', 'MISSING_DESIGN_REFERENCE', { reference: ref });}
|
|
354
|
-
if (root === 'sceneRecipes' && !target.pages.some(item => item.pageId === page.pageId)) {
|
|
355
|
-
invalid('prd.designRefs', 'SCENE_PAGE_MISMATCH', { reference: ref, pageId: page.pageId });
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
if ([...pageIds].sort().join('|') !== [...prdPages].sort().join('|')) {invalid('prd.pages', 'DESIGN_PAGE_COVERAGE_MISMATCH');}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function validateDesignDocument(markdown, options = {}) {
|
|
363
|
-
const { metadata, body } = parseDesignDocument(markdown);
|
|
364
|
-
if (metadata.schemaVersion !== '1.0') {invalid('schemaVersion', 'EXPECTED_1_0');}
|
|
365
|
-
for (const key of ['name', 'description']) {if (!nonempty(metadata[key])) {invalid(key, 'NONEMPTY_STRING_REQUIRED');}}
|
|
366
|
-
if (metadata.buildPlanRevision !== undefined && !nonempty(metadata.buildPlanRevision)) {
|
|
367
|
-
invalid('buildPlanRevision', 'NONEMPTY_STRING_REQUIRED');
|
|
368
|
-
}
|
|
369
|
-
for (const key of ['themeId', 'templatePath']) {
|
|
370
|
-
if (Object.prototype.hasOwnProperty.call(metadata, key)) {invalid(key, 'TEMPLATE_IDENTITY_NOT_ALLOWED');}
|
|
371
|
-
}
|
|
372
|
-
const tokens = extractDesignTokens(metadata, { strict: true });
|
|
373
|
-
const profile = metadata.themeProfile;
|
|
374
|
-
if (!object(profile)) {invalid('themeProfile', 'MAPPING_REQUIRED');}
|
|
375
|
-
for (const key of ['name', 'themeColor', 'themeColorSource']) {if (!nonempty(profile[key])) {invalid(`themeProfile.${key}`, 'NONEMPTY_STRING_REQUIRED');}}
|
|
376
|
-
if (!/^#[\da-f]{6}$/i.test(profile.themeColor) || profile.themeColor.toUpperCase() !== tokens['--color-brand1-6'].toUpperCase()) {
|
|
377
|
-
invalid('themeProfile.themeColor', 'PRIMARY_COLOR_MISMATCH');
|
|
378
|
-
}
|
|
379
|
-
if (!['light', 'dark'].includes(profile.contentTone)) {invalid('themeProfile.contentTone', 'LIGHT_OR_DARK_REQUIRED');}
|
|
380
|
-
if (!['light', 'dark'].includes(profile.navTheme)) {invalid('themeProfile.navTheme', 'LIGHT_OR_DARK_REQUIRED');}
|
|
381
|
-
if (!['app-custom-theme-file', 'current-app-theme'].includes(profile.themeDelivery)) {invalid('themeProfile.themeDelivery', 'INVALID_THEME_DELIVERY');}
|
|
382
|
-
if (typeof profile.themeFile !== 'string') {invalid('themeProfile.themeFile', 'STRING_REQUIRED');}
|
|
383
|
-
if (profile.themeDelivery === 'app-custom-theme-file' && !nonempty(profile.themeFile)) {invalid('themeProfile.themeFile', 'THEME_FILE_REQUIRED');}
|
|
384
|
-
for (const [key, values] of Object.entries({
|
|
385
|
-
navigationType: ['platform-l-shape', 'platform-top', 'platform-side', 'custom'],
|
|
386
|
-
layoutDirection: ['l_shape', 'top', 'side'], hideAppNav: ['y', 'n'],
|
|
387
|
-
})) {
|
|
388
|
-
if (profile[key] !== undefined && !values.includes(profile[key])) {invalid(`themeProfile.${key}`, 'INVALID_NAVIGATION_VALUE');}
|
|
389
|
-
}
|
|
390
|
-
if (!object(metadata.iconSystem) || !['lucide-react', '@ant-design/icons'].includes(metadata.iconSystem.library) || !object(metadata.iconSystem.mappings)) {
|
|
391
|
-
invalid('iconSystem', 'ICON_LIBRARY_AND_MAPPINGS_REQUIRED');
|
|
392
|
-
}
|
|
393
|
-
for (const [key, value] of Object.entries(metadata.iconSystem.mappings)) {
|
|
394
|
-
if (!nonempty(key) || !nonempty(value)) {invalid(`iconSystem.mappings.${key}`, 'ICON_COMPONENT_NAME_REQUIRED');}
|
|
395
|
-
}
|
|
396
|
-
if (!object(metadata.assetStrategy) || !Array.isArray(metadata.assetStrategy.pages)) {invalid('assetStrategy.pages', 'ARRAY_REQUIRED');}
|
|
397
|
-
// Lazy require: the asset reader imports this document parser too.
|
|
398
|
-
require('../asset/asset-plan').prepareAssetPlan([], metadata.assetStrategy);
|
|
399
|
-
const lines = structuralLines(body);
|
|
400
|
-
if (!/^# [^#\s]/.test(lines.find(line => line.trim()) || '') || lines.filter(line => /^# /.test(line)).length !== 1) {
|
|
401
|
-
invalid('body', 'ONE_LEADING_H1_REQUIRED');
|
|
402
|
-
}
|
|
403
|
-
const headings = lines.filter(line => /^## /.test(line)).map(line => line.slice(3).trim());
|
|
404
|
-
if (headings.join('|') !== HEADINGS.join('|')) {invalid('body', 'FIVE_ORDERED_CHAPTERS_REQUIRED', { expected: HEADINGS });}
|
|
405
|
-
if (/\{\{[^}]+\}\}|<[^>]*(?:实际色值|生成期标记)[^>]*>/.test(markdown)) {invalid('document', 'UNRESOLVED_PLACEHOLDER');}
|
|
406
|
-
const { text, anchors } = documentAnchors(body);
|
|
407
|
-
const chapterThree = text.indexOf('## 3. 基础组件表达');
|
|
408
|
-
const chapterFour = text.indexOf('## 4. 特色表达配方');
|
|
409
|
-
function reference(value, field) {
|
|
410
|
-
if (!object(value) || !ANCHOR.test(value.anchor || '') || !anchors.has(value.anchor)) {
|
|
411
|
-
invalid(field, 'EXISTING_ANCHOR_REQUIRED', { anchor: value?.anchor });
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
for (const root of ['components', 'states']) {
|
|
415
|
-
if (!object(metadata[root])) {invalid(root, 'MAPPING_REQUIRED');}
|
|
416
|
-
for (const [key, value] of Object.entries(metadata[root])) {
|
|
417
|
-
if (!REFERENCE_KEY.test(key)) {invalid(`${root}.${key}`, 'INVALID_REFERENCE_KEY');}
|
|
418
|
-
reference(value, `${root}.${key}`);
|
|
419
|
-
if (Object.keys(value).join('|') !== 'anchor') {invalid(`${root}.${key}`, 'ANCHOR_ONLY_REQUIRED');}
|
|
420
|
-
const position = anchors.get(value.anchor);
|
|
421
|
-
if (position < chapterThree || position >= chapterFour) {invalid(`${root}.${key}`, 'ANCHOR_MUST_BE_IN_CHAPTER_THREE');}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
if (!object(metadata.sceneRecipes)) {invalid('sceneRecipes', 'MAPPING_REQUIRED');}
|
|
425
|
-
const pageIds = new Set();
|
|
426
|
-
const pageAnchors = new Set();
|
|
427
|
-
for (const [key, scene] of Object.entries(metadata.sceneRecipes)) {
|
|
428
|
-
if (!REFERENCE_KEY.test(key)) {invalid(`sceneRecipes.${key}`, 'INVALID_REFERENCE_KEY');}
|
|
429
|
-
if (!object(scene) || !Array.isArray(scene.pages) || !scene.pages.length) {invalid(`sceneRecipes.${key}.pages`, 'NONEMPTY_ARRAY_REQUIRED');}
|
|
430
|
-
for (const page of scene.pages) {
|
|
431
|
-
if (!object(page) || !nonempty(page.pageId) || pageIds.has(page.pageId)) {invalid(`sceneRecipes.${key}.pages`, 'UNIQUE_PAGE_ID_REQUIRED');}
|
|
432
|
-
reference(page, `sceneRecipes.${key}.${page.pageId}`);
|
|
433
|
-
if (Object.keys(page).sort().join('|') !== 'anchor|pageId') {invalid(`sceneRecipes.${key}.${page.pageId}`, 'PAGE_ID_AND_ANCHOR_ONLY_REQUIRED');}
|
|
434
|
-
if (!page.anchor.startsWith('#page-') || pageAnchors.has(page.anchor)) {invalid(page.pageId, 'UNIQUE_PAGE_ANCHOR_REQUIRED');}
|
|
435
|
-
pageIds.add(page.pageId);
|
|
436
|
-
pageAnchors.add(page.anchor);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
const positions = [...pageAnchors].map(anchor => [anchor, anchors.get(anchor)]).sort((a, b) => a[1] - b[1]);
|
|
440
|
-
const chapterFive = text.indexOf('## 5. 项目应用与调整规则');
|
|
441
|
-
for (let index = 0; index < positions.length; index++) {
|
|
442
|
-
const [anchor, start] = positions[index];
|
|
443
|
-
if (start < chapterFive) {invalid(anchor, 'PAGE_MUST_BE_IN_CHAPTER_FIVE');}
|
|
444
|
-
const section = text.slice(start, positions[index + 1]?.[1] || text.length);
|
|
445
|
-
for (const label of PAGE_LABELS) {
|
|
446
|
-
const pattern = new RegExp(`^(?:[ \\t]*[-*][ \\t]+)?(?:\\*\\*)?${label}[::](?:\\*\\*)?[ \\t]*(\\S[^\\n]*)$`, 'm');
|
|
447
|
-
const match = pattern.exec(section);
|
|
448
|
-
if (!match || !concretePageText(match[1])) {invalid(anchor, 'PAGE_LABEL_REQUIRED', { label });}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
for (const page of metadata.assetStrategy.pages) {if (!pageIds.has(page.pageId)) {invalid('assetStrategy.pages', 'UNKNOWN_DESIGN_PAGE', { pageId: page.pageId });}}
|
|
452
|
-
for (const match of text.matchAll(/var\(\s*(--[\w-]+)/g)) {if (!(match[1] in tokens)) {invalid('body', 'UNDECLARED_TOKEN_REFERENCE', { reference: match[1] });}}
|
|
453
|
-
if (options.prdMarkdown !== undefined) {validatePrd(options.prdMarkdown, metadata, pageIds, options);}
|
|
454
|
-
return { success: true, schemaVersion: metadata.schemaVersion, name: metadata.name, pages: [...pageIds], tokenCount: Object.keys(tokens).length, anchorCount: anchors.size, prdChecked: options.prdMarkdown !== undefined,
|
|
455
|
-
iconContrast: validateIconContrast(metadata.iconSystem, tokens) };
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
module.exports = { parseDesignDocument, serializeDesignDocument, extractDesignTokens, validateDesignDocument, HEADINGS, PAGE_LABELS };
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { CliError } = require('../core/cli-error');
|
|
4
|
-
const { t } = require('../core/i18n');
|
|
5
|
-
|
|
6
|
-
function invalid(field, issue, details = {}) {
|
|
7
|
-
throw new CliError(t('design_document.invalid', field, issue), {
|
|
8
|
-
code: 'DESIGN_DOCUMENT_INVALID', details: { field, issue, ...details },
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function color(value, tokens = {}, seen = new Set()) {
|
|
13
|
-
if (typeof value !== 'string') {return null;}
|
|
14
|
-
const text = value.trim().toLowerCase();
|
|
15
|
-
const variable = /^var\(\s*(--[\w-]+)\s*(?:,\s*(.+))?\)$/.exec(value.trim());
|
|
16
|
-
if (variable) {
|
|
17
|
-
if (seen.has(variable[1]) || seen.size > 32) {return null;}
|
|
18
|
-
return color(tokens[variable[1]] ?? variable[2], tokens, new Set([...seen, variable[1]]));
|
|
19
|
-
}
|
|
20
|
-
if (text === 'white') {return [255, 255, 255, 1];}
|
|
21
|
-
if (text === 'black') {return [0, 0, 0, 1];}
|
|
22
|
-
if (text === 'transparent') {return [0, 0, 0, 0];}
|
|
23
|
-
if (/^#(?:[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/.test(text)) {
|
|
24
|
-
let hex = text.slice(1);
|
|
25
|
-
if (hex.length < 5) {hex = [...hex].map(c => c + c).join('');}
|
|
26
|
-
return [0, 2, 4].map(i => parseInt(hex.slice(i, i + 2), 16)).concat(hex.length === 8 ? parseInt(hex.slice(6), 16) / 255 : 1);
|
|
27
|
-
}
|
|
28
|
-
const rgb = /^rgba?\(([^()]+)\)$/.exec(text);
|
|
29
|
-
if (!rgb) {return null;}
|
|
30
|
-
let components;
|
|
31
|
-
if (rgb[1].includes(',')) {
|
|
32
|
-
components = rgb[1].split(',').map(part => part.trim());
|
|
33
|
-
} else {
|
|
34
|
-
const parts = rgb[1].trim().split('/');
|
|
35
|
-
if (parts.length > 2) {return null;}
|
|
36
|
-
components = parts[0].trim().split(/\s+/);
|
|
37
|
-
if (components.length !== 3) {return null;}
|
|
38
|
-
if (parts.length === 2) {components.push(parts[1].trim());}
|
|
39
|
-
}
|
|
40
|
-
if (![3, 4].includes(components.length) || components.some(c => !/^(?:\d*\.)?\d+%?$/.test(c))) {return null;}
|
|
41
|
-
const values = components.map((c, i) => parseFloat(c) * (c.endsWith('%') ? (i < 3 ? 255 / 100 : 1 / 100) : 1));
|
|
42
|
-
if (values.some((v, i) => v < 0 || v > (i < 3 ? 255 : 1))) {return null;}
|
|
43
|
-
return values.length === 3 ? [...values, 1] : values;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function composite(front, back) {
|
|
47
|
-
return front.slice(0, 3).map((value, i) => value * front[3] + back[i] * (1 - front[3])).concat(1);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function contrast(foreground, background) {
|
|
51
|
-
const luminance = rgb => rgb.slice(0, 3).reduce((sum, value, index) => {
|
|
52
|
-
const channel = value / 255;
|
|
53
|
-
return sum + [0.2126, 0.7152, 0.0722][index] * (channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4);
|
|
54
|
-
}, 0);
|
|
55
|
-
const a = luminance(foreground), b = luminance(background);
|
|
56
|
-
return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function validateIconContrast(iconSystem, tokens) {
|
|
60
|
-
const pairs = iconSystem?.colorPairs;
|
|
61
|
-
if (pairs === undefined) {return [];}
|
|
62
|
-
if (!Array.isArray(pairs)) {invalid('iconSystem.colorPairs', 'ARRAY_REQUIRED');}
|
|
63
|
-
const names = new Set();
|
|
64
|
-
return pairs.map((pair, index) => {
|
|
65
|
-
const field = `iconSystem.colorPairs[${index}]`;
|
|
66
|
-
if (!pair || typeof pair !== 'object' || Array.isArray(pair) || typeof pair.name !== 'string' || !pair.name.trim()
|
|
67
|
-
|| names.has(pair.name) || Object.keys(pair).some(key => !['name', 'foreground', 'background', 'surface'].includes(key))) {
|
|
68
|
-
invalid(field, 'ICON_COLOR_PAIR_INVALID');
|
|
69
|
-
}
|
|
70
|
-
names.add(pair.name);
|
|
71
|
-
const foreground = color(pair.foreground, tokens);
|
|
72
|
-
let background = color(pair.background, tokens);
|
|
73
|
-
if (!foreground || !background) {invalid(field, 'ICON_COLOR_UNRESOLVED', { name: pair.name });}
|
|
74
|
-
if (pair.surface !== undefined || background[3] < 1) {
|
|
75
|
-
const surface = color(pair.surface, tokens);
|
|
76
|
-
if (!surface || surface[3] !== 1) {invalid(field, 'ICON_OPAQUE_SURFACE_REQUIRED', { name: pair.name });}
|
|
77
|
-
background = composite(background, surface);
|
|
78
|
-
}
|
|
79
|
-
const ratio = contrast(composite(foreground, background), background);
|
|
80
|
-
if (ratio < 3) {invalid(field, 'ICON_CONTRAST_LOW', { name: pair.name, ratio, minimum: 3 });}
|
|
81
|
-
return { name: pair.name, ratio: Math.round(ratio * 100) / 100, minimum: 3 };
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
module.exports = { color, contrast, validateIconContrast };
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Shared by Plan authoring and the final Fast/Plan document check.
|
|
4
|
-
function concretePageText(value) {
|
|
5
|
-
if (typeof value !== 'string' || !value.trim()) {return false;}
|
|
6
|
-
const text = value.trim().replace(/^(?:\*\*|`)|(?:\*\*|`)$/g, '').trim();
|
|
7
|
-
return !/草稿待补充/.test(text)
|
|
8
|
-
&& !/^(?:继承主题|继承全局|遵循主题|遵循全局|同全局|同上|按主题执行|按全局执行|待補充|待补充|待定|TBD|TODO|[-—])[。.!!]*$/i.test(text);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
module.exports = { concretePageText };
|