cardbuddy 0.11.0 → 0.12.1
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 +46 -5
- package/dist/cli.js +51 -1
- package/dist/config.js +51 -16
- package/dist/core.js +2 -2
- package/dist/init.js +425 -0
- package/dist/preview-editor/assets/index-CHuGGIR3.js +276 -0
- package/dist/preview-editor/assets/{index-B4VZgNSK.css → index-CQScMhyg.css} +1 -1
- package/dist/preview-editor/index.html +2 -2
- package/dist/standalone.js +8 -0
- package/dist/vendored/mcp/client.mjs +537 -0
- package/dist/vendored/mcp/tools.mjs +268 -0
- package/dist/vendored/preview/assets/default-image.png +0 -0
- package/dist/{server-core → vendored/server/core}/ai-chat.js +82 -38
- package/dist/{server-core → vendored/server/core}/browser.js +1 -1
- package/dist/{server-core → vendored/server/core}/cardSave.js +8 -3
- package/dist/{server-core → vendored/server/core}/cardSync.js +6 -14
- package/dist/{server-core → vendored/server/core}/cardValidate.js +11 -8
- package/dist/{server-core → vendored/server/core}/index.js +5 -3
- package/dist/vendored/server/core/skillPaths.js +124 -0
- package/dist/{server-core → vendored/server/core}/sliceUpload.js +5 -8
- package/dist/vendored/skills/README.md +93 -0
- package/dist/vendored/skills/card-doctor/SKILL.md +52 -0
- package/dist/vendored/skills/card-skill-trainer/SKILL.md +312 -0
- package/dist/vendored/skills/card-skill-trainer/baselines/v3-r038n.json +43 -0
- package/dist/vendored/skills/card-skill-trainer/baselines/v3-r039n.json +47 -0
- package/dist/vendored/skills/card-skill-trainer/baselines/v3-r050n.json +48 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/build_dataset.py +472 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/build_sample.mjs +162 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/diagnose_case.mjs +241 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/scoring.mjs +525 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/round_diff.mjs +133 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/run_round.mjs +1043 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/score_round.mjs +195 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/test_missing_slice.py +122 -0
- package/dist/vendored/skills/dsl-card-ai/SKILL.md +191 -0
- package/dist/vendored/skills/dsl-card-generator/SKILL.md +480 -0
- package/dist/vendored/skills/dsl-card-generator/config/render-contract.json +11 -0
- package/dist/vendored/skills/dsl-card-generator/references/amount_formatting.md +138 -0
- package/dist/vendored/skills/dsl-card-generator/references/card_index.json +1597 -0
- package/dist/vendored/skills/dsl-card-generator/references/card_templates.json +384 -0
- package/dist/vendored/skills/dsl-card-generator/references/card_to_summary.md +56 -0
- package/dist/vendored/skills/dsl-card-generator/references/component_rules.md +76 -0
- package/dist/vendored/skills/dsl-card-generator/references/component_specs.md +393 -0
- package/dist/vendored/skills/dsl-card-generator/references/dsl_structure.md +208 -0
- package/dist/vendored/skills/dsl-card-generator/references/event_patterns.md +287 -0
- package/dist/vendored/skills/dsl-card-generator/references/expression_engine.md +121 -0
- package/dist/vendored/skills/dsl-card-generator/references/field_name_dict.json +34 -0
- package/dist/vendored/skills/dsl-card-generator/references/field_naming.md +101 -0
- package/dist/vendored/skills/dsl-card-generator/references/generation_pitfalls.md +928 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/README.md +69 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/examples_index.json +2967 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/patterns.json +2835 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/recipes_v2.json +648 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/recipes_v2_schema.md +295 -0
- package/dist/vendored/skills/dsl-card-generator/references/image_diff.md +48 -0
- package/dist/vendored/skills/dsl-card-generator/references/image_to_card.md +129 -0
- package/dist/vendored/skills/dsl-card-generator/references/ir-format.md +212 -0
- package/dist/vendored/skills/dsl-card-generator/references/message-center-pattern.md +149 -0
- package/dist/vendored/skills/dsl-card-generator/references/platform_jsapi.md +196 -0
- package/dist/vendored/skills/dsl-card-generator/references/previewer_contract.md +54 -0
- package/dist/vendored/skills/dsl-card-generator/references/quickref.md +197 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/absolute-badge.json +84 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/accordion-group-toggle.json +18 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/alertCard-pair.json +12 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/alertDialog-call.json +12 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-countdown.json +15 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-donut.json +15 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-gauge-score.json +11 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-radar.json +10 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-title-head.json +13 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/globalSet.json +12 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/label-col-modes.json +21 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/list-expand-collapse.json +17 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/money-format.json +11 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/opacity-switch.json +130 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/progress-bar.json +25 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/tab-billMode.json +15 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/title-underline-overlay.json +21 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipes.md +797 -0
- package/dist/vendored/skills/dsl-card-generator/references/rules.md +235 -0
- package/dist/vendored/skills/dsl-card-generator/references/slices_convention.md +177 -0
- package/dist/vendored/skills/dsl-card-generator/references/spec_to_schema_patterns.md +609 -0
- package/dist/vendored/skills/dsl-card-generator/references/ui_fidelity_rules.md +131 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/add_component.mjs +118 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/analyze_component_usage.mjs +153 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/apply_opacity_pattern.mjs +343 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/build_card_index.mjs +108 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/build_card_templates.mjs +190 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/check_fidelity.mjs +257 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/check_magic_values.mjs +79 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/crop_slices.mjs +98 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/describe_card.mjs +265 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/diff_bands.mjs +257 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/diff_card.mjs +316 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/expand_ir.mjs +873 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/extract_handcrafted.py +1199 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/extract_subtree.mjs +191 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/generate_cards.mjs +389 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/inventory_schemas.py +160 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/iterate.mjs +700 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/design_image.mjs +56 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/design_sample.mjs +328 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/layout-tokens.mjs +79 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/paths.mjs +103 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/scaffold.mjs +575 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/slicePath.mjs +81 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/slice_dedupe.mjs +147 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lint_autofix.mjs +363 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lint_card.mjs +1399 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/preflight.mjs +249 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/register_card_template.mjs +208 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/render_baseline.mjs +231 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/render_card.mjs +801 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/render_diff.mjs +239 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/resolve_card_template.mjs +73 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/route_input.mjs +447 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/scaffold_card.mjs +81 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/slices_normalize.mjs +316 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_audit.mjs +462 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_to_schema_v3.mjs +7735 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_tree.mjs +355 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/validate_all_schemas.py +71 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/default-image-wzZYQ86K.png +0 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-C9k-07y7.css +1 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-oFRM4f4V.js +55 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/svg-symbols-B2exBrAr.svg +118 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/component-meta.json +2714 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/directive-meta.json +40 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/style-meta.json +1086 -0
- package/dist/vendored/{standalone.html → skills/dsl-card-generator/vendor/preview/standalone.html} +2 -1
- package/dist/vendored/skills/finmall-card-sync/SKILL.md +262 -0
- package/dist/vendored/skills/finmall-session-navigator/SKILL.md +169 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/finmall_login.mjs +882 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/package-lock.json +59 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/package.json +10 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/session-manager.mjs +96 -0
- package/dist/vendored/skills/shared/README.md +55 -0
- package/dist/vendored/skills/shared/auth-model.md +79 -0
- package/dist/vendored/skills/sketch-spec-to-card/SKILL.md +329 -0
- package/dist/vendored/skills/sketch-spec-to-card/references/boundary.json +14 -0
- package/dist/vendored/skills/sketch-spec-to-card/references/card_refs.json +13 -0
- package/dist/vendored/skills/sketch-spec-to-card/references/spec-schema.md +103 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/build_prompt.py +797 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/card_refs.py +94 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/cluster_states.py +706 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/parse_meaxure.py +1781 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/png_utils.py +318 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/vision.py +1118 -0
- package/dist/vendored/templates/helloCard/data.json +10 -0
- package/dist/vendored/templates/helloCard/schema.json +238 -0
- package/dist/vendored/tools/link-skills.mjs +485 -0
- package/package.json +2 -1
- package/dist/preview-editor/assets/index-jl4YJzKv.js +0 -180
- package/dist/vendored/App.vue +0 -2858
- package/dist/vendored/ansi.js +0 -185
- package/dist/vendored/assets/apple-touch-icon.png +0 -0
- package/dist/vendored/assets/default-image.png +0 -0
- package/dist/vendored/assets/favicon-16.png +0 -0
- package/dist/vendored/assets/favicon-32.png +0 -0
- package/dist/vendored/assets/site-icon.png +0 -0
- package/dist/vendored/assets/svgIcons/icons.svg +0 -87
- package/dist/vendored/assets/svgIcons/index.js +0 -39
- package/dist/vendored/components/AiSettings.vue +0 -392
- package/dist/vendored/components/AlertCardInstance.vue +0 -58
- package/dist/vendored/components/AlertCardLayer.vue +0 -59
- package/dist/vendored/components/ApiCallPanel.vue +0 -127
- package/dist/vendored/components/ApiPayloadTree.vue +0 -201
- package/dist/vendored/components/AppToast.vue +0 -37
- package/dist/vendored/components/CardGallery.vue +0 -291
- package/dist/vendored/components/CardPreview.vue +0 -72
- package/dist/vendored/components/CardSelect.vue +0 -206
- package/dist/vendored/components/ChatComposer.vue +0 -413
- package/dist/vendored/components/ChatMessage.vue +0 -383
- package/dist/vendored/components/ChatPanel.vue +0 -937
- package/dist/vendored/components/ComponentPalette.vue +0 -301
- package/dist/vendored/components/ConfirmDialog.vue +0 -54
- package/dist/vendored/components/DatePickerOverlay.vue +0 -345
- package/dist/vendored/components/DebugPanel.vue +0 -1734
- package/dist/vendored/components/DeviceOverlay.vue +0 -70
- package/dist/vendored/components/Dropdown.vue +0 -193
- package/dist/vendored/components/EventEditor.vue +0 -273
- package/dist/vendored/components/HoverGuides.vue +0 -122
- package/dist/vendored/components/JsonTree.vue +0 -547
- package/dist/vendored/components/LogValue.vue +0 -52
- package/dist/vendored/components/MarkdownBody.vue +0 -64
- package/dist/vendored/components/NewCardDialog.vue +0 -132
- package/dist/vendored/components/NodeInspector.vue +0 -295
- package/dist/vendored/components/NumberStepper.vue +0 -125
- package/dist/vendored/components/PickerOverlay.vue +0 -131
- package/dist/vendored/components/PropControl.vue +0 -245
- package/dist/vendored/components/PropTree.vue +0 -128
- package/dist/vendored/components/RemotePreview.vue +0 -173
- package/dist/vendored/components/SelectionOverlay.vue +0 -105
- package/dist/vendored/components/StructureTree.vue +0 -1002
- package/dist/vendored/components/Switch.vue +0 -38
- package/dist/vendored/components/setters/SetBgPosition.vue +0 -64
- package/dist/vendored/components/setters/SetBgSize.vue +0 -90
- package/dist/vendored/components/setters/SetCode.vue +0 -198
- package/dist/vendored/components/setters/SetColor.vue +0 -244
- package/dist/vendored/components/setters/SetCustomSelect.vue +0 -58
- package/dist/vendored/components/setters/SetExpr.vue +0 -107
- package/dist/vendored/components/setters/SetGradient.vue +0 -125
- package/dist/vendored/components/setters/SetGrid9.vue +0 -68
- package/dist/vendored/components/setters/SetIconSelect.vue +0 -67
- package/dist/vendored/components/setters/SetIfForm.vue +0 -61
- package/dist/vendored/components/setters/SetImage.vue +0 -43
- package/dist/vendored/components/setters/SetList.vue +0 -139
- package/dist/vendored/components/setters/SetNumber.vue +0 -118
- package/dist/vendored/components/setters/SetNumberStyle.vue +0 -113
- package/dist/vendored/components/setters/SetPadding.vue +0 -77
- package/dist/vendored/components/setters/SetSegment.vue +0 -160
- package/dist/vendored/components/setters/SetSelect.vue +0 -51
- package/dist/vendored/components/setters/SetSwitch.vue +0 -49
- package/dist/vendored/components/setters/SetText.vue +0 -52
- package/dist/vendored/components/setters/props.js +0 -23
- package/dist/vendored/fidelity/baseline-messageOverview.json +0 -87
- package/dist/vendored/fidelity/calibration.json +0 -29
- package/dist/vendored/fidelity/fidelity-messageOverview-after.html +0 -114
- package/dist/vendored/fidelity/fidelity-messageOverview-after.json +0 -272
- package/dist/vendored/fidelity/fidelity-messageOverview.html +0 -185
- package/dist/vendored/fidelity/fidelity-messageOverview.json +0 -336
- package/dist/vendored/fidelity/fidelity-messageOverview_v2.html +0 -262
- package/dist/vendored/fidelity/fidelity-messageOverview_v2.json +0 -502
- package/dist/vendored/fidelity/fidelity-messageOverview_v3.html +0 -201
- package/dist/vendored/fidelity/fidelity-messageOverview_v3.json +0 -417
- package/dist/vendored/fidelity/fidelity-messageOverview_visual.html +0 -125
- package/dist/vendored/fidelity/fidelity-messageOverview_visual.json +0 -338
- package/dist/vendored/index.html +0 -16
- package/dist/vendored/main.js +0 -37
- package/dist/vendored/runtime/ai-api.js +0 -165
- package/dist/vendored/runtime/alert-card.js +0 -125
- package/dist/vendored/runtime/app-toast.js +0 -33
- package/dist/vendored/runtime/clipboard-node.js +0 -78
- package/dist/vendored/runtime/clipboard.js +0 -29
- package/dist/vendored/runtime/event-model.js +0 -300
- package/dist/vendored/runtime/hoverGuide.js +0 -31
- package/dist/vendored/runtime/jsapi-mock.js +0 -256
- package/dist/vendored/runtime/markdown.js +0 -140
- package/dist/vendored/runtime/node-model.js +0 -217
- package/dist/vendored/skills/.manifest.json +0 -1101
- package/dist/vendored/standalone-main.js +0 -36
- package/dist/vendored/styles/ai-chat.css +0 -1850
- package/dist/vendored/styles/app.css +0 -3648
- package/dist/vendored/styles/card-gallery.css +0 -285
- package/dist/vendored/styles/card-select.css +0 -288
- package/dist/vendored/styles/components.css +0 -383
- package/dist/vendored/styles/dropdown.css +0 -324
- package/dist/vendored/styles/json-tree.css +0 -308
- package/dist/vendored/styles/node-inspector.css +0 -800
- package/dist/vendored/styles/setters.css +0 -1050
- package/dist/vendored/styles/standalone.css +0 -27
- package/dist/vendored/styles/structure-tree.css +0 -460
- /package/dist/vendored/{api.js → preview/api.js} +0 -0
- /package/dist/vendored/{assets → preview/assets}/svg-symbols.svg +0 -0
- /package/dist/vendored/{components → preview/components}/AbsoluteLayout.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Button.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Canvas.vue +0 -0
- /package/dist/vendored/{components → preview/components}/CheckGroup.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Column.vue +0 -0
- /package/dist/vendored/{components → preview/components}/CustomPlaceholder.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Divider.vue +0 -0
- /package/dist/vendored/{components → preview/components}/GridLayout.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Icon.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Image.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Input.vue +0 -0
- /package/dist/vendored/{components → preview/components}/ListLayout.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Map.vue +0 -0
- /package/dist/vendored/{components → preview/components}/MultiColumn.vue +0 -0
- /package/dist/vendored/{components → preview/components}/MultiText.vue +0 -0
- /package/dist/vendored/{components → preview/components}/ProgressSlider.vue +0 -0
- /package/dist/vendored/{components → preview/components}/RadioButton.vue +0 -0
- /package/dist/vendored/{components → preview/components}/RenderTree.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Root.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Row.vue +0 -0
- /package/dist/vendored/{components → preview/components}/SvgCode.vue +0 -0
- /package/dist/vendored/{components → preview/components}/SvgSymbols.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Text.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Video.vue +0 -0
- /package/dist/vendored/{components → preview/components}/ViewContainer.vue +0 -0
- /package/dist/vendored/{components → preview/components}/base.js +0 -0
- /package/dist/vendored/{components → preview/components}/icons.js +0 -0
- /package/dist/vendored/{components → preview/components}/index.js +0 -0
- /package/dist/vendored/{meta → preview/meta}/component-meta.json +0 -0
- /package/dist/vendored/{meta → preview/meta}/directive-meta.json +0 -0
- /package/dist/vendored/{meta → preview/meta}/style-meta.json +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/StyleParser.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/adapter.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/assets.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/canvas-context.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/card-runtime.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/evaluate.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/property.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/selectionOverlay.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/toPreview.js +0 -0
- /package/dist/{server-core → vendored/server/core}/assetProxy.js +0 -0
- /package/dist/{server-core → vendored/server/core}/bbox.js +0 -0
- /package/dist/{server-core → vendored/server/core}/buildDataFromSchema.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardFiles.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardHistory.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardImage.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardImportMeta.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardListCache.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardOpenFolder.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardSidecar.js +0 -0
- /package/dist/{server-core → vendored/server/core}/codeBridge.js +0 -0
- /package/dist/{server-core → vendored/server/core}/logStream.js +0 -0
- /package/dist/{server-core → vendored/server/core}/logger.js +0 -0
- /package/dist/{server-core → vendored/server/core}/nodeModels.js +0 -0
- /package/dist/{server-core → vendored/server/core}/renderStatus.js +0 -0
- /package/dist/{server-core → vendored/server/core}/screenshot.js +0 -0
- /package/dist/{server-core → vendored/server/core}/sliceStatic.js +0 -0
- /package/dist/vendored/{meta → skills/dsl-card-generator/vendor/preview/meta}/action-meta.json +0 -0
- /package/dist/{server-core/_vendor → vendored/tools}/finmall-env.mjs +0 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# 铁律集中陈述(rules.md)
|
|
2
|
+
|
|
3
|
+
> 本文件是项目**唯一权威**的铁律陈述。其他文档(SKILL.md / quickref.md / dsl-card-ai/SKILL.md 等)
|
|
4
|
+
> 只做简短引用,不再重复措辞——避免"同一规则在不同文档说法略不同,AI 需要额外注意力统一理解"。
|
|
5
|
+
>
|
|
6
|
+
> 任何与本文档冲突的描述以本文档为准;发现冲突应直接修源文档而非本文件。
|
|
7
|
+
|
|
8
|
+
## 一、证据分级(Reliability Rule)
|
|
9
|
+
|
|
10
|
+
**绝不臆造**任何未经证实的组件、属性、样式键、指令形状、事件键、动作类型或运行时 API。每一个生成的结构必须是以下之一:
|
|
11
|
+
|
|
12
|
+
1. **Export-proven(导出实证)**:从 `src/cards/*/schema.json`(49+ 张 UAT 导出)复制并改造的同构结构。最可靠。
|
|
13
|
+
2. **Source/meta-proven**:编辑器 bundle 元数据(`vendor/preview/meta/{component,style,directive}-meta.json`)确认的 palette 注册、属性元数据、默认样式。可用,但序列化形状仍以导出为准。
|
|
14
|
+
3. **Renderer-proven**:`vendor/preview/` 下的渲染器源码(`runtime/evaluate.js` / `property.js` / `toPreview.js` / `RenderTree.vue` / `card-runtime.js` / 各 `components/*.vue`)证实的运行时行为。
|
|
15
|
+
4. **Unknown**:停下问平台导出,或标记为草稿;不要默默猜测。
|
|
16
|
+
|
|
17
|
+
- **文档冲突以导出代码为准(JSAPI 契约)**:外部卡片文档(如 docs/动态卡片方案详细说明.md 的 JSAPI 章节)若与 `src/cards/*` 导出卡的实际 `ynet.callJSAPI(...)` 调用在入参 / 出参字段上对不上,**一律以导出卡代码为准**。例:旧文档写 `showDatePicker` 回传 `res.data.value`,导出卡 transferPlan 四个调用点只读 `res.currentDate`。生成时按代码实写,并补 `res.currentDate || (res.data && res.data.value)` 兼容取值。
|
|
18
|
+
|
|
19
|
+
保留编辑器元数据字段:`itemId`、`itemValueFrom`、`itemDefaultValue`、`itemCondition`、`itemChildren`、`itemList`、`slotScope`,以及 customAction 的 `code`/`compiled` 双份。它们是 DSL 契约的一部分,不是装饰。
|
|
20
|
+
|
|
21
|
+
## 二、不污染 DSL(项目最高纪律)
|
|
22
|
+
|
|
23
|
+
- **绝不**把 `$schema`、Generator 产物、任何 `x-*` / 工具前缀字段写进 `schema.json`。保存路径只序列化 currentSchema,工具元数据永不落盘。
|
|
24
|
+
- **孤儿字段保留**:schema 里有、组件 meta 未描述的字段(extraProperties / extraDirectives)必须原样带回。
|
|
25
|
+
- **整体替换 schema 时,未涉及节点/字段与孤儿字段逐字节保留**——一次编辑静默抹掉外部 DSL 字段是不可恢复的。
|
|
26
|
+
- 改完后用 `scripts/diff_card.mjs` 机械核对一遍(默认对比 git HEAD),确认没误删什么。
|
|
27
|
+
|
|
28
|
+
## 三、DSL 契约(data.json / 绑定 / 默认值)
|
|
29
|
+
|
|
30
|
+
- `data.json` 必须**逐字节** = `{ inputParams[i].key: inputParams[i].defaultValue }`。
|
|
31
|
+
- 增删 `inputParams` 字段 → 同步改 `data.json`,或跑 `scripts/derive_data_json.py --overwrite`(autofix 也会修漂移)。
|
|
32
|
+
- 绑定变量(`itemValueFrom:"1"`)必须声明在 `dictionary.inputParams`,否则校验失败。
|
|
33
|
+
- **私有绑定字段 key 必须带 `_` 前缀(外部传参判定铁律)**:`inputParams` 中仅供卡片**内部状态**使用、不允许外部调用方传入的绑定字段,key 必须以 `_` 开头(如 `_expanded`、`_activeTab`);支持外部传参的字段**禁止**带 `_` 前缀。判定规则:`data.json` / `inputParams` 中 key 以 `_` 开头 → 卡片私有属性,**外部不支持传入**;否则 → 支持外部传参。`_` 前缀字段仍须进 `data.json`(逐字节契约不变,defaultValue 即内部状态初值)。注意与作用域插槽内置变量 `_index` 区分——`_index` 是渲染期注入的索引,不是 `inputParams`,不受此规则约束。新增/生成卡片强制遵守;存量卡未带前缀的内部字段不强制回改。
|
|
34
|
+
- `itemValueFrom:"1"` 的属性**必须**带 `itemDefaultValue`(预览器 undefined 兜底,否则渲染空)。
|
|
35
|
+
- **`itemValueFrom` 是每个 style 子项 / component.properties / directives 的必填字段**(取值 `"0"|"1"|"2"`)。缺失或空串会被运行时 `property.js onPreview` 静默丢弃该属性(背景/内边距/gap/对齐全部失效,且旧校验漏报)。`validate_schema.py` 现已升级为 ERROR,`autofix_schema.py` 可一键补 `"0"`。**手写 style 极易漏,写完务必跑校验。**
|
|
36
|
+
- `style` 子项的 `itemId` 必须严格对照 `vendor/preview/meta/style-meta.json`;易错:`flexDirection=006`(不是 002,那是 alignItems)。`validate_schema.py` 现已校验子项 itemId 正确性。生成避坑全集见 `references/generation_pitfalls.md`。
|
|
37
|
+
- **进度条/占比条必须数据驱动 + 父子节点范式(UI 功能实现约定)**:识别到设计稿里按比率填充的进度条 / 占比条 / 完成度条,应新增一个 **≤1 的比率 inputParams**(如 `ratio`),用**父子 Row** 实现——父节点=灰色轨道(`width:100%` + `height:4` + 灰底),子节点=彩色填充(`width=Math.round($ratio * 100) + '%'` + `height:4` + 彩底,`itemValueFrom:"1"` + `itemDefaultValue` 兜底),**禁止**把宽度静态写成固定百分比当装饰条,**禁止用 Divider 组件做进度条**(Divider 是分割线,非进度条语义)。百分比表达式必须 `Math.round()` 防 JS 浮点精度(R23)。完整范式见 `references/recipe_snippets/progress-bar.json`;生成避坑全集见 `references/generation_pitfalls.md §8 + R23`。
|
|
38
|
+
- **文本内嵌「类型+数字」枚举必须结构化绑定(枚举统计行铁律)**:**每个常量文本在落盘前都要过一遍 LLM 语义解析**——凡一行文本由 **≥2 段「短标签 + 数字」**(计数或金额)构成、段间用统一分隔符(`· 、,,|/` 或空格)连接,如「标签A 5 · 标签B 2 · 标签C 1 · 标签D 4」,**禁止**整串绑一个 string 或留常量。必须拆为**结构化数组 inputParams**(`<语义名>Breakdown`,`type:"array"`,每项 `{label, value}`:label=标签原文、value=数字),Text 用聚合表达式还原(`$X.map(i => i.label + ' ' + i.value).join(' · ')`,`itemValueFrom:"1"` + `itemDefaultValue` 原文兜底)。识别信号只看**格式/结构**(标签+数字对、统一分隔符、全行覆盖),不看业务词——不依赖"动账/转账"这类具体类型名,任何「类型→数量/金额」聚合文本都适用。脚本确定性兜底:`detectEnumStatStrip`(v3 渲染链路自动拆解);兜底未覆盖的不规则分隔符/量纲由 LLM 主判提取。范式与命名见 `spec_to_schema_patterns.md §19`、`field_naming.md §3.1`。
|
|
39
|
+
- 6 个编辑器专属类(MultiColumn / CheckGroup / RadioButton / ProgressSlider / Video / Map)预览不渲染,仅在有平台导出时生成。
|
|
40
|
+
|
|
41
|
+
## 四、运行时作用域与 Input 回写
|
|
42
|
+
|
|
43
|
+
- **双作用域彻底分离**:`rt.state` = 运行期克隆(深拷贝 `rt.rootScope` 种子,嵌套也独立);`rt.rootScope` = 初始数据 / 落盘 / "保存到本地"脏标记比对源。
|
|
44
|
+
- **运行态变量一律 `this.state` 读**:事件代码里 `this.state.xxx` 读 rt.state(含 Input 实时写入与 setData 写入的活数据)。**`this.data` 仅允许读 Input 绑定键的实时输入值**(渲染层 onInput 兜底回写 rt.state);禁止用 this.data 读普通运行态变量——平台不保证其对运行态的可见性,预览层别名只是兼容行为,lint `r51` 拦截。
|
|
45
|
+
- **Input 回写只写 rt.state**:Input 的 value 绑定 $key 时,渲染层兜底 `rt.state[key] = v`(**只写 rt.state、绝不写 rt.rootScope**,故打字不触发"保存到本地"脏标记)。
|
|
46
|
+
- **读写约定**:运行态变量(含用户输入/绑定派生值)一律用 this.state 读、写走 this.setData;this.data 仅用于读 Input 绑定键的实时输入值;持久卡片态存 this.state;任何应重渲染的改动后调 this.setData(...)。**调试交互绝不调 setRootData**——否则会污染"保存到本地"基线。
|
|
47
|
+
- **事件代码必须 `var self = this` 起手(指针安全铁律)**:每个 customAction 的 `function.code` / `compiled` **第一行**写 `var self = this;`。**任何嵌套函数(addEventListener 回调、`Array.map/forEach/find` 回调、setTimeout、ynet.callJSAPI 回调等)里访问组件实例一律用 `self`,禁止用 `this`**——`this` 在回调里会被运行时重绑(指向全局 / 事件源 / undefined),直接 `this.setData` 会指向错误对象,导致静默失效或 `this is undefined` 报错。顶层(非嵌套)直接调用可用 `this`,但统一用 `self` 最稳、零歧义。**生成事件代码默认就按此写,不要等出错再改。** export-proven:holdFinancialProducts 根 `onReady` 写 `var self = this; ... this.addEventListener('globalSet', (e) => self.$set(e.data.key, e.data.value))`;recipes.md §4 / §7 同构。
|
|
48
|
+
- **List/Grid item 事件改绑定数据必须 globalSet 透传(铁律)**:ListLayout/GridLayout 的 item(slotScope)事件里禁止直接改绑定数据(this.setData/setRootData/直接 state 赋值)——必须 `this.postMessage('globalSet', patch)`(patch 与 setData 入参逐字一致),并先在根节点 onReady `addEventListener('globalSet')` 注册监听、监听里用闭包 self 代 `setData(patch)`;其他节点事件可直接 setData。lint `r54` ERROR 拦截「item 直改」与「用 globalSet 但根无监听」。固定范式见 recipes.md §4 / recipe_snippets/globalSet.json。
|
|
49
|
+
- **slotScope item 字段在事件脚本里直读(铁律 r55)**:slotScope:true 的 List/Grid item 事件脚本里,当前 item 字段**直接 `this.state.<field>` 读取**(如 `var q = this.state.query;`)——作用域展开是平台保证,**不是**需要兜底判断的不确定行为;禁止写 `if (x === undefined && this.state.$$)` 之类的「$$ 兜底」。事件脚本(customAction)里**禁止出现任何 `$$`**(含 `this.state.$$`、`$$.field`);`$$` 只在**表达式**里引用整个 item 时才允许(R50 另管 `$$.field` 冗余)。lint `r55-event-no-dollar-dollar` ERROR 拦截。
|
|
50
|
+
- **调 JSAPI 直接裸调 ynet(铁律 r56)**:事件脚本里调 JSAPI 一律 `ynet.callJSAPI(name, params, cb)`——ynet 由平台注入为全局;**禁止 `window.ynet` 前缀**,也**禁止 `typeof window !== 'undefined' && window.ynet && window.ynet.callJSAPI` 之类存在性守卫**(把平台保证误当不确定行为的冗余错写;49 张导出卡全量裸调为证)。lint `r56-jsapi-no-window-guard` ERROR 拦截。
|
|
51
|
+
- **生成代码必须严格带 `;`(语句结束符不省略)**:customAction 的 `function.code` / `compiled` 里**每条 JS 语句结尾都要写分号,禁止依赖 ASI(自动分号插入)省略**。`return` / 换行 / 对象字面量 / 数组字面量等场景省略分号会触发 ASI 陷阱(如 `return\n{...}` 被解析成 `return;` 直接返回 undefined);且导出卡(49+ 卡 `compiled` 全量带分号)风格不一致会让 `diff_card.mjs` 更难比对。**生成时默认每条语句结尾补 `;`,不要"省着写"。** export-proven:holdFinancialProducts / creditCardBill 等所有导出卡 `compiled` 均全量带分号。
|
|
52
|
+
|
|
53
|
+
## 五、速查优先(降上下文、降思考)
|
|
54
|
+
|
|
55
|
+
常规建卡/改卡任务**只看 `references/quickref.md`**——它已含最常用组件最小骨架、5 个表达式、4 个事件范式、data.json 铁律、快路径命令、范式片段索引。只有当遇到 quickref 未覆盖的**未知组件、边界样式、复杂表达式或事件语义**时,才按需读 `component_specs.md` / `expression_engine.md` / `event_patterns.md` / `recipes.md`。**不要每次把全部 references 加载进上下文**。
|
|
56
|
+
|
|
57
|
+
## 六、验证三层自洽
|
|
58
|
+
|
|
59
|
+
| 层 | 工具 | 目的 | 性质 |
|
|
60
|
+
|---|---|---|---|
|
|
61
|
+
| **validate** | `validate_schema.py` / `validate_all_schemas.py` | 结构合法、能渲染 | CI 硬卡点(0 ERROR) |
|
|
62
|
+
| **lint** | `lint_card.mjs` | 体验达标、不出现怪东西 | 软提醒 |
|
|
63
|
+
| **diff** | `diff_card.mjs` | 铁律保留、未涉及节点没误删 | 改完后回看 |
|
|
64
|
+
|
|
65
|
+
三者互补不重叠,底层都从同一份 `lib/paths.{mjs,py}` 解析路径。
|
|
66
|
+
|
|
67
|
+
## 七、视觉真相优先
|
|
68
|
+
|
|
69
|
+
- `render_status.ok` 只证明"结构合法 + 绑定可解析 + 渲染不抛错",**不证明画对了**。
|
|
70
|
+
- 视觉正确性以 `card_screenshot`(多模态读图)为准;按 `references/image_diff.md` 七维度逐项核对。
|
|
71
|
+
- 别把 `ok:true` 当成「改对了」。
|
|
72
|
+
|
|
73
|
+
### 七.1 适用范围铁律:非 UI 变更不做视觉验证
|
|
74
|
+
|
|
75
|
+
**视觉验证(`render_card.mjs` / `card_screenshot` / Playwright 截图与 computed style 断言)只在本次变更会改变画面时做。** 判定看「改了哪些字段」,不看「改的是不是 `schema.json`」——同一份 schema 里视觉字段与纯逻辑字段混在一起。
|
|
76
|
+
|
|
77
|
+
| 变更性质 | 典型改动 | 验证口径 |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| **UI 变更** | `style.*`(rect / flex / padding / background / border / radius / shadow / other)、`className`、节点增删与移动、`slots` 结构、图片图标资源、Text 静态文案与 `textStyle`、v-show 显隐条件 | §六 三层 **+ 视觉收口**(本节 + `generation_pitfalls.md §5` / §9-R5) |
|
|
80
|
+
| **非 UI 变更** | `dictionary.inputParams`(增删改 / defaultValue / mocks)、绑定表达式 `itemValue`(`itemValueFrom:"1"`)的**取值逻辑**、`eventactions[].action.*.code` / `compiled`、`data.json`、事件与 JSAPI 调用、skill 文档与脚本 | §六 三层 **+ 针对性单元断言**;**不做视觉验证** |
|
|
81
|
+
|
|
82
|
+
- **单元断言怎么做**(比截图更快、更准、可穷举入参):
|
|
83
|
+
- 表达式 → 用宿主工程的表达式求值器(本项目 `preview/runtime/evaluate.js` 导出 `evalExpr(expr, scope)`)直接比对返回字符串;多组入参(number / 字符串 / 脏值 / 缺省)逐个断言。
|
|
84
|
+
- customAction → 抽出 `code` 与 `compiled` 两份,`new Function(src).call(fakeThis)` 跑;`fakeThis` 只需 `state` / `data` / `setData(s){this.state=s}` / `addEventListener(){}` 四个桩,断言跑完的 `state`。两份都要跑,防止只改了一份。
|
|
85
|
+
- **理由**:拉浏览器、等首帧、截图比对的成本远高于收益。非 UI 变更的画面本就不该变——截图相同不产生新信息,截图不同反而说明改错了。
|
|
86
|
+
- **仍按 UI 变更处理的边界**:改动的**副作用会改变布局几何**时——文案长度变化引起换行 / 挤压 / 溢出、`width:"100%"` 节点内容变长、v-show 条件改动改变可见节点集合从而改变容器高度。注意这与「绑定值算得对不对」是两件事:后者一律用单元断言判定,不靠读像素。
|
|
87
|
+
|
|
88
|
+
## 八、远端同步前置
|
|
89
|
+
|
|
90
|
+
- 远端保存(`card_save`)前确保本地镜像是外部最新(先 `card_sync`);保存后建议回读校验。
|
|
91
|
+
- Chrome 启用 remote-debugging(端口 9222),FinMall 页面保持已登录态。
|
|
92
|
+
- IDC10010 保存只传内层 body + 必带 `sassAppId` / `sassWorkspaceId`;"交易成功" ≠ 真落库,须 IDC10012 读回校验。
|
|
93
|
+
|
|
94
|
+
## 九、组件级硬约束
|
|
95
|
+
|
|
96
|
+
详见 `references/component_rules.md`(单表速查)。最易踩的几条:
|
|
97
|
+
- **根节点(Root/view)不设样式宽度**:根节点宽度由顶层 `view.width`(如 `"375"`)承载;`view.style.properties` 里**不要**放 `rect` 组(含 `rect.width`)——放了是冗余/重复宽度(export-proven:49+ 张导出卡根 style 均无 rect,仅 `view.width` 定宽)。若手写时误加,`check_topology.py` 会以 `Root_style_rect_forbidden`(hard 级)提示删除。
|
|
98
|
+
- **关键文本节点(金额/数字/标签/标题/占比/单位)必须显式 `flexShrink:"0"`**:`.ce_text` 默认 `width:fit-content; word-break:break-all`,父子链上任一层被压缩都会沿断行规则塌陷(字符竖排堆叠);关键文本节点 + 包裹它们的直接 Row/Column 容器都要 `flexShrink:"0"`(flex 组 itemId:"004")。详见 `generation_pitfalls.md §9-R17`。
|
|
99
|
+
- **嵌套需 space-between 的 Row 必须显式 `rect.width:"100%"`**:根节点级 Row 默认撑满,嵌套层不再默认撑满(收缩到内容宽 → space-between 失效);视觉上需要左右分布的行容器都要显式宽度 100%(同时按 R22 给该节点 `flexShrink:"1"`)。**例外**:父容器是纵向流且交叉轴 stretch(`Column`/`Root`/`SingleColumn`,CSS 强制 column + `align-items` 缺省 stretch)时子 Row 本就被拉满,或自身已给定宽(如 100px 胶囊)时 `space-between` 已生效——这两种情况 lint `r19` 不报,存量卡不要为消 WARN 去补 `width:"100%"`(会连带触发 R22/R35 两条 ERROR)。详见 `generation_pitfalls.md §9-R19`。
|
|
100
|
+
- **`rect.width:"100%"` 的节点必须保持可压缩(`flexShrink:"1"`),禁止同节点 `flexShrink:"0"`**:`width:100%` 在 flex 里语义是「占满主轴」,与兄弟节点并排时靠 `flexShrink` 协商让位;100% 节点若同时 `flexShrink:"0"`,协商被锁死,固定宽兄弟(图标/金额/按钮)会被挤出父容器或触发溢出。与 R17 按 `width` 分流:关键 Text(auto/fit-content/未设宽)走 R17 `flexShrink:"0"` 保护;`width:100%` 容器走本条 `flexShrink:"1"` 让位。详见 `generation_pitfalls.md §9-R22`。
|
|
101
|
+
- **透明度禁止表达式(铁律 R57)**:移动端渲染不支持 `style.other.opacity` 走表达式(`itemValueFrom:"1"`),预览构建产物更是完全不消费组件级 opacity。透明度需要随状态变化时,必须走**透明度切换固定范式**:按用到的透明度取值生成等量静态变体节点,统一套一层 Row/Column 容器(**容器宽高 = 被展示节点宽高 + `other.overflow:"hidden"`**),点击等事件挂在容器上,变体用互斥 v-show 显隐切换。lint `r57-opacity-expression` ERROR 拦截;机械改写 `scripts/apply_opacity_pattern.mjs`。详见 `recipes.md §17` / `recipe_snippets/opacity-switch.json`。
|
|
102
|
+
- **特殊节点显隐必须外套 Row/Column(铁律)**:MultiColumn/ListLayout/GridLayout/AbsoluteLayout/Canvas/Map/Video/ViewContainer(自定义节点)需要设置显示条件(v-show 01/02)时,**必须外套一层 Row 或 Column 节点,把 v-show 设在包裹层**控制显隐;直接设在这些节点上行为不可靠。其他节点(Row/Column/Text/Image 等)可直接设置 v-show。lint `r53` ERROR 拦截。
|
|
103
|
+
- **Icon 必须带 `style.rect(width,height)`**:Icon 的 SVG 跟随 `.ce_icon` 外层尺寸渲染,无 rect 时按 sprite 默认 32×32 渲染;设计稿要求的具体尺寸(如 20×20)必须显式写入 rect 组(itemId 003/004)。
|
|
104
|
+
- **卡片根节点样式从 spec 卡片 bg shape 全量提取**:聚焦图 `card-N.png` 是 RGBA,卡片几何(`mask.type="geometry"`,见 spec `cards[].mask`)之外已透明;背景可能是任意非均匀颜色/渐变/图案,**禁止**取截图四角/边缘像素来推断 Root 背景。卡片根容器的**全套视觉样式**唯一合法来源是 spec 中**卡片根背景 shape**(`type=="shape"` 且尺寸 == 卡片 `rect`)的 `style` 字段,必须**逐项提取**:
|
|
105
|
+
- **背景**:`style.background.color` 或 `cards[].cardFill`(卡片容器自身 fill);画布/页面色(如 `#F5F6F8`)只存在于卡片外部,**绝不能**作为卡片 Root 背景,否则圆角外会残留设计稿灰底。`cardFill` 为 null 时 Root 必须透明,不得用 `TOKEN.pageBg/fallbackBg` 兜底。
|
|
106
|
+
- **圆角**:`style.radius.borderRadius` 或 `cards[].mask.radius`。
|
|
107
|
+
- **边框**:`style.border`(isBorder/width/style/color)→ `border(700)` 组。常见 0.5px 白色细边框。
|
|
108
|
+
- **阴影**:`style.shadow`(若有值)→ `shadow(800)` 组。
|
|
109
|
+
- **透明度**:`style.opacity`(若 ≠1)→ 对应 opacity 组。
|
|
110
|
+
- 两种合法形态:①**纯色卡**——Root 背景=`cardFill`(`backgroundType=01`),外圆角放 Root,第一层容器透明且不重复设外圆角(v3 的 `whiteCard` 纯色形态即此);②**分层卡**——Root `backgroundType=00`(透明),圆角与卡片背景放在内部独立卡容器(v3 的 `whiteCard`)。完整字段映射见 `generation_pitfalls.md §9-R33`。
|
|
111
|
+
- Icon 必须 `emits:click`,否则 onClick 双触发。
|
|
112
|
+
- Input `events:[]`(无可绑定事件,渲染层兜底回写)。
|
|
113
|
+
- ListLayout / GridLayout 插槽必须 `slotScope:true`。
|
|
114
|
+
- Canvas 必须有 `onMeasured` 才能画。
|
|
115
|
+
- Button.disabled 字符串 `"false"` 也为真。
|
|
116
|
+
- v-show 把 `showType` 塞进 `itemValue` 是错的(应放 `itemChildren.properties`)。
|
|
117
|
+
|
|
118
|
+
## 十、从脚手架起步、绝不空写
|
|
119
|
+
|
|
120
|
+
- 新卡生成从 `scaffold_card.mjs` 或 MCP `card_scaffold` 起步,绝不从空文件手写。
|
|
121
|
+
- 改已有卡从 `card_read` / `card_node_models` 起步,绝不在没有 currentSchema 的情况下凭印象改。
|
|
122
|
+
|
|
123
|
+
## 十一、标注可信度分层(属性值读标注,几何看实图)
|
|
124
|
+
|
|
125
|
+
设计稿标注(spec.json)两类信息**可信度不同**,禁止混用、禁止互相替代:
|
|
126
|
+
|
|
127
|
+
- **属性值类(直接读标注,禁止抄存量卡)**:圆角 `radius`、阴影 `shadow`、字体 `textStyle`(fontSize/lineHeight/fontWeight/color/textAlign)、边框 `border`(isBorder/width/color)、背景 `fill/background`——这些是设计值,标注即为权威。存量卡只用于确认 meta 的取值合法性,**不作为属性值来源**。
|
|
128
|
+
- **几何类(按实图/容器边界分析,不读标注坐标)**:rect 尺寸(x/y/w/h)、元素间距——标注是**绝对定位**,不遵循节点树尺寸逻辑(矩形带 padding/margin 的盒子实际占位 ≠ 标注坐标差),必须以实图视觉 + 相邻容器元素(shape/group/symbol rect)边界之差为准。
|
|
129
|
+
- 派生规则:元素间距用 `margin` 不用 `padding`(padding 污染 box 高);卡片圆角归属见 §九——纯色卡外圆角设 Root,Root 透明 + 内部卡片时圆角设内卡;**禁止画布灰底进入 Root,也禁止从透明化截图里反推背景色**(背景非均匀/非固定,唯一依据是 `cards[].cardFill`)。
|
|
130
|
+
- **裁切后间距归零(卡片根节点零间距铁律)**:spec 里画板坐标标注的「元素距画板边缘的间距」是**画板上下文**——`card-1.png` 聚焦裁切区域(如 x16,y238)之外是状态栏/导航/AI 输入等系统 chrome,**裁切后卡片本体独立渲染,裁切边界即卡片边界,原「距边缘 16px」→ 0**。因此:
|
|
131
|
+
- **卡片根节点(view 下第一层容器)与 Root(view)均不设任何四周 margin/padding**(含左右 16、上 16、下 16 一律不写);卡片四周留白完全由**宿主容器**(弹层 / 页面容器 / 宿主 CSS)决定,schema 不携带任何画板上下文间距。
|
|
132
|
+
- 卡片**内部**内容与卡片本体的间距(如标题距卡片左缘 16px,来自 x32 - 裁切 x16)**裁切后依然成立**,下沉到卡片内部的 `content` 容器(padding 16)承载——根节点/视图零间距,内层内容容器负责内部留白。
|
|
133
|
+
- 反例(曾犯):把画板的 16px 边距写进 `view.style.padding` 或卡片根节点 `margin/padding`,导致预览出现一圈「莫名其妙的灰边/白边」。
|
|
134
|
+
- 完整反例与验收见 `generation_pitfalls.md §9-R1/R8/R9/R10`。
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 引用方式(其他文档)
|
|
139
|
+
|
|
140
|
+
其他文档不要重复本文件的措辞,只需一句引用:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
> 项目铁律详见 `references/rules.md`;本节只列与本节直接相关的子集。
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
或更短的:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
(铁律见 `references/rules.md` §二、§三)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## 十二、卡片目录命名(仅字母数字驼峰)
|
|
153
|
+
|
|
154
|
+
- 卡片目录名(即 `src/cards/<id>/` 的 `<id>`)**必须仅包含英文字母(a-z, A-Z)和数字(0-9)**,不得出现中文、下划线、连字符、空格或其他特殊字符。
|
|
155
|
+
- 推荐使用 **camelCase(驼峰命名法)**,如 `integralCouponList`、`pointDetailHistory`。
|
|
156
|
+
- 此规则适用于所有生成路径(批量脚本 `generate_cards.mjs`、AI 手动建卡、MCP `card_scaffold`)。
|
|
157
|
+
- 设计稿画板名通常含中文序号和描述(如「21我的优惠券_有优惠券」),生成时必须映射为纯字母数字 ID,不得直接用作目录名。
|
|
158
|
+
|
|
159
|
+
## 十三、画板聚焦与卡片生成单位(多画板必须先选卡)
|
|
160
|
+
|
|
161
|
+
一份设计稿常含**多个画板**(MeaXure 导出实测 37–55 个)。两类产物的生成单位不同,**禁止混用**:
|
|
162
|
+
|
|
163
|
+
- **UI 产物允许全量生成**:`sketch-spec-to-card` 管线产物(`prompt.md` / `spec.md` / `artboard.png` / `card-N.png` / `spec.json` / `slices/` / `manifest.json` / `clusters.json`)可一次跑完整份导出的全部画板——它们是可复用素材,不是交付卡片。
|
|
164
|
+
- **卡片生成必须指定单个画板(或单个 UI 产物)**:`schema.json` / `data.json` 的最小生成单位 = **一个画板 / 一个 cardKey 状态**。多画板输入且用户未指定目标时,**必须先把画板清单抛给用户选、拿到指定后才开始产卡**;**禁止**自行挑「第一个 / 最像主屏的」画板,**禁止**未经用户同意批量产卡,**禁止**「先建一张看看」。例外:用户显式说「全部生成 / 批量建卡」时才走批量(`generate_cards.mjs`),并在交付里说明产了几张卡。
|
|
165
|
+
|
|
166
|
+
### 13.1 标准选择交互(列 `1..n + 全部`;支持弹选项则优先弹)
|
|
167
|
+
|
|
168
|
+
1. **先列全量清单(两种选择方式都要做)**:机械取画板(见 13.2),按 **`1..n` 编号**逐行列出(序号 **1 起**,每行 `序号. 画板名 — 类型 / 状态`),清单末尾追加一项 **`全部`**。清单是用户的完整选项池,**不得只列节选**。**逐行配图(强制)**:每个画板行下面紧跟**它自己那张**预览图绝对路径(`图: <绝对路径>`,`parse_meaxure.py --list` / 门禁 exit-4 输出已机械产出;取值优先 `UI/<稿>/<画板>/artboard.png` → 导出 `preview/<slug>.png`),缺图该行显式写 `图: (预览图缺失)`;**禁止把所有预览图统一堆在清单末尾**——部分画板缺图时堆末尾会让序号与图错位,用户选不准。
|
|
169
|
+
2. **再按环境能力选交互方式(优先级从高到低)**:
|
|
170
|
+
- **A|优先:环境支持弹选项**(存在 `request_user_input` 工具,即 Plan 模式等可交互环境)→ 先输出上面的完整 `1..n + 全部` 清单,**紧接着调用 `request_user_input` 弹选择**。该工具每题最多 3 个选项、客户端会自动追加自由输入 Other,故:选项放**最具代表性的 2 个画板 + `全部`**(代表性优先 `artboardType=card` 且 `status=generated`),问题文案里写明「其余画板请在 Other 里填清单序号或画板名」;返回空答案时**不反复追问**,降级到 B。
|
|
171
|
+
- **B|兜底:环境不支持弹选项**(Default 模式 / CLI 非交互 / 无该工具)→ 输出清单 + **一句纯文本提问**(如「请回复要处理的画板序号,或回复 `全部`」),然后**停止输出、结束本轮,等用户回序号**——不得在同一轮顺手产卡,不得自行挑画板。
|
|
172
|
+
- 两种方式都**必须把清单每行自带的图绝对路径逐条内联成 Markdown 图片**(与序号一一对应)辅助看图选;缺图行保留「预览图缺失」标注,不得省略该行、不得把图堆到清单末尾。系统级选择弹窗在本项目**不可实现**(Codex 无原生弹窗控件;`open_in_codex` 只能打开文件/浏览器标签,不能回收用户选择),A/B 即等价物。
|
|
173
|
+
3. **拿到序号才继续**:
|
|
174
|
+
- 回复单个序号 / 多个序号 → 只处理对应画板;产卡时一轮只写一个 `src/cards/<id>/`。
|
|
175
|
+
- 回复 `全部` → **生成设计稿 UI 产物(sketch-spec-to-card 管线)允许**,整份导出全量跑(不加 `--only/--indices`);**产卡**时视为用户**显式批量授权**,按批量流程逐卡产出,交付里说明产了几张卡、分别对应哪些画板。
|
|
176
|
+
4. **序号换算**:`parse_meaxure.py --list` 与门禁清单**展示统一 1 起**(与 `manifest.canvases[].order` 同口径,直接对齐);用户回的 1 起序号 k 传 `--indices` 时 **-1(0 起)**。
|
|
177
|
+
5. **「全部」只能是用户的回答,不是 Agent 的默认值**:「UI 产物允许全量生成」指用户回复 `全部` 后可整份导出全跑;用户消息里没指定画板时,**包括「生成产物 / 前处理 / 跑管线」在内的任何请求都必须先过第 1–3 步**,Agent 不得替用户选「全部」。
|
|
178
|
+
6. **机械卡点(脚本层兜底)**:`sketch-spec-to-card/scripts/parse_meaxure.py` 不带 `--only/--indices/--sample/--scope-all` 的生成运行会打印 1..n + 全部 清单并 **exit 4**(不生成任何东西);拿到用户回答后序号 k → `--indices <k-1>`、「全部」→ `--scope-all`。无人值守训练(`card-skill-trainer`)属显式批量例外,直接带 `--scope-all`(或 `SKETCH_SPEC_SCOPE_ALL=1`)。
|
|
179
|
+
|
|
180
|
+
### 13.2 清单来源(机械产出,不靠记忆)
|
|
181
|
+
|
|
182
|
+
| 输入阶段 | 命令 / 文件 | 给出什么 |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| 原始 MeaXure 导出(未前处理) | `python3 skills/sketch-spec-to-card/scripts/parse_meaxure.py --src <导出目录> --list` | 全部画板:序号(**展示 1 起**,与门禁清单同口径)+ 名称 + 尺寸 + 末尾「全部」 |
|
|
185
|
+
| 已前处理 | `UI/<稿名>/manifest.json` 的 `canvases[]` | `order`(**1 起**)/ `name` / `artboardType`(card / subcard / overlay_dialog)/ `status`(generated / pending)/ `trainingEligible` |
|
|
186
|
+
| 已聚类 | `UI/<稿名>/clusters.json` 的 `cards[]` + `overlays[]` | `cardKey` / `pageId` / `stateNames`(同卡多状态画板)/ `recommendedBase`(建议基线画板) |
|
|
187
|
+
|
|
188
|
+
- 清单**全量列名称**(几十个也列全,可按业务流分组),并标注哪些**不产卡**:`overlay_dialog` → 不生成独立卡,补到父卡事件(见 `dsl-card-generator/SKILL.md §8 Workflow A.5`)。
|
|
189
|
+
- 同一卡有多状态画板时,选择单位是 **cardKey(卡)**,并说明 `clusters.json.recommendedBase` 建议哪张作基线;用户选定卡但未指定状态时用 recommendedBase 并明确告知。
|
|
190
|
+
- 序号基准要对齐:`--list` 与 `manifest.canvases[].order` **展示都是 1 起**(同口径,可直接对齐);只有 `parse_meaxure.py --indices` **传参是 0 起**,用户回的序号 k → `--indices <k-1>`,避免选错画板。
|
|
191
|
+
|
|
192
|
+
### 13.3 落盘纪律
|
|
193
|
+
|
|
194
|
+
- 一轮生成只写**一个卡片目录** `src/cards/<id>/`;批量仅在用户显式要求时进行。
|
|
195
|
+
- **显式批量例外**:用户点名跑无人值守训练(`card-skill-trainer`)或明确说「全部生成 / 批量建卡」时按批量执行——训练产物落隔离 run 目录、不写 `src/cards/`,交付时说明产了几张卡。
|
|
196
|
+
- 选定画板后走 `dsl-card-generator` Workflow A 六步闭环;`route_input.mjs` / MCP `card_route_input` 的形态判定仍是第一步。
|
|
197
|
+
|
|
198
|
+
## 十四、数字语义变量提取(中文+数字混排串 = 静态文案 + 数字变量 + 表达式)
|
|
199
|
+
|
|
200
|
+
生成 / 编辑卡片时,**每个落盘的文本字符串都要先过一遍 LLM 语义解析**(与 §三 枚举统计行铁律同口径):字符串里含**运行期数字信息**时,关键数字必须提取成 `inputParams` 变量,原中文+数字混排串用**表达式拼接**还原;**禁止**整串写死成静态文案,也**禁止**整串绑一个 string 变量。
|
|
201
|
+
|
|
202
|
+
### 14.1 判定:看「数字是否随运行期数据变化」,不看「是否含数字」(AI 语义判定,禁止正则粗暴判定)
|
|
203
|
+
|
|
204
|
+
- **要提取(运行期数字信息)**:金额(含带千分号 / 两位小数 / ¥)、笔数、次数、利率、期限、日期、卡号尾号、进度值等——数值由上游数据决定的部分。如「可用额度¥12,300.00」「本月动账 3 笔」「额度提升至 50,000 元」「还有 5 天还款」。
|
|
205
|
+
- **不提取(数字是固定话术的一部分)**:版本号 / 型号 / 固定营销话术 / 专有名词——「V3 客户」「7 天免费体验」「100% 中奖」「3D 安全验证」「第二期」。数字不随数据变化 → 整串保留字面量。
|
|
206
|
+
- **歧义时看设计稿 / spec 上下文**:同一文案在多个画板 / 状态里数字不同 → 运行期数字信息(提取);所有画板恒定 → 固定话术(不提取)。这是最强的机械信号,优先于单画板猜测。
|
|
207
|
+
|
|
208
|
+
### 14.2 提取与命名
|
|
209
|
+
|
|
210
|
+
- **金额**:`type:"number"`,`defaultValue` 给**原始数字**(`12300` / `12300.00`),**禁止**把格式化串(`"12,300.00"`)或带货币符的串写进字典;命名按 `field_naming.md` 语义名(`availableAmount` / `remainRepayAmount`),禁止序号名。
|
|
211
|
+
- **笔数 / 期限 / 利率等**:默认 `type:"number"`(上游契约为字符串时跟随上游,但显示表达式仍要数值化);命名如 `tradeCount` / `remainDays` / `yearRate`。
|
|
212
|
+
- **私有 vs 外部**:沿用 §三 `_` 前缀铁律——调用方可传的数字不带 `_`;仅卡内中间态的数字带 `_`。
|
|
213
|
+
- **枚举统计行**(一行 ≥2 段「标签+数字」)走 §三 结构化数组铁律(`<语义名>Breakdown`),本节不重复。
|
|
214
|
+
|
|
215
|
+
### 14.3 展示:表达式拼接(静态文案作字面量,数字走表达式)
|
|
216
|
+
|
|
217
|
+
- 混排串 → `'静态文案' + <数字表达式> + '静态文案'`,`itemValueFrom:"1"` + `itemDefaultValue` 兜设计稿原文(预览器 undefined 兜底,§三)。
|
|
218
|
+
- **格式化后带千分号的金额照惯例 = 原始数字变量 + 格式化表达式**:用 `amount_formatting.md` §3 的 canonical 表达式库(默认健壮版 3.4b),中文前缀 / 后缀拼在表达式**外面**;**禁止**把静态中文塞进格式化表达式内部,**禁止**在 data 里预拼格式化串。
|
|
219
|
+
- 「可用额度¥12,300.00元」→ `'可用额度' + '¥' + (((($availableAmount || 0) + '').replace(/,/g, '') * 1) || 0).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',') + '元'`(落盘 `itemValue` 时反斜杠再转义一层,见 `amount_formatting.md` §3)。
|
|
220
|
+
- 纯计数:「本月动账 3 笔」→ `$tradeCount + ' 笔'`(计数默认不格式化;确需千分号复用 §3.2)。
|
|
221
|
+
- 同串多个数字 → 按阅读顺序拼多个变量;静态分隔符(、,空格)作字面量。
|
|
222
|
+
- **可计算性**:提取后的数字变量必须能直接参与 `customAction` / Canvas / 条件比较(取原始值);不要从展示串反解(确需反解用 `amount_formatting.md` §3.5)。
|
|
223
|
+
|
|
224
|
+
### 14.4 禁止清单
|
|
225
|
+
|
|
226
|
+
- 禁止把格式化数字(`12,300.00` / `1,234`)或带货币符的串写进 `defaultValue` / 静态 Text;
|
|
227
|
+
- 禁止把整条中文+数字混排串绑成一个 string 变量(失去可计算性与格式一致性);
|
|
228
|
+
- 禁止用正则「凡含数字就拆」做机械判定——语义判定(14.1)是唯一标准,正则只作 lint 提醒;
|
|
229
|
+
- 禁止只把中文部分抽变量、数字留字面量(反向违规)。
|
|
230
|
+
|
|
231
|
+
### 14.5 机械兜底(提醒级,不阻塞)
|
|
232
|
+
|
|
233
|
+
lint `r61-text-numeric-literal`(WARN):静态 Text(非 `itemValueFrom:"1"`)的 value、或 `inputParams[].defaultValue` 字符串命中「千分号数字 / `¥`+数字 / 数字+单位词(元|笔|次|个|条)」模式 → 提醒按 14.1 做 AI 语义复核是否需要提取(固定话术会误报,WARN 不阻塞、不影响 ERROR 门禁)。
|
|
234
|
+
|
|
235
|
+
**存量卡不强制回改**(与 §三 `_` 前缀同款口径):`r61` 会把存量反范式列出来(如整串绑 string 的 `repayHint`、格式化串进字典的 `foreignAmount`),供编辑该卡时顺手修正;新生成 / 新编辑的卡片强制遵守本节。
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# 切图资产约定(issue-slices 系列 · 2026-08-15 固化)
|
|
2
|
+
|
|
3
|
+
> 适用范围:dsl-card-generator 生成任意卡片时,卡片引用到的切图(slice PNG)的**命名 / 落点 / schema 引用 / 本地预览**规则。
|
|
4
|
+
> 由用户明确固化,覆盖三类问题:
|
|
5
|
+
> - **扁平落点**:`src/slices/` 下只放图片本体,不得保留多级目录。
|
|
6
|
+
> - **仅引用才拷贝**:被 schema 引用到的切图才汇聚到 `src/slices/`,未使用的切图不复制。
|
|
7
|
+
> - **schema 引用格式**:统一用 `/slices/***.**`(绝对路径前缀),`/slices/*` 直接预览(含 `render_card.mjs` 临时 HTTP 服务)。
|
|
8
|
+
|
|
9
|
+
## 1. 三段式路径约定
|
|
10
|
+
|
|
11
|
+
| 阶段 | 路径 | 说明 |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| ① 上游导出(sketch-spec-to-card 中间产物) | `UI/<artboard>/slices/<category>/<name>@3x.png` | 多级目录,保留导出原始结构,仅作为来源 |
|
|
14
|
+
| ② 生成汇聚 + 引用(dsl-card-generator) | `src/slices/<flat>@3x.png` + schema `value="/slices/<flat>@3x.png"` | **扁平落点**,无子目录;`<flat>` = 导出相对路径的 `/` 替换为 `_` |
|
|
15
|
+
| ③ 切图上传(`slice_upload.mjs` / 顶栏「远端 → 切图上传」) | `@filestore/<ns>/<ts>_<name>@3x.png` | 上传 `src/slices/` 真源到 FinMall 图片库(`CC0000.do`),schema 引用改写为 `@filestore/...`,并在 `src/slices/manifest.json` 记「本地文件 ↔ 线上地址」映射;运行时 `adapter.doReplaceUrl`:`@filestore/x` → `/minio/x` → assetProxy 拉远端 |
|
|
16
|
+
|
|
17
|
+
**扁平化示例**(用户给定基准):
|
|
18
|
+
```
|
|
19
|
+
导出路径:UI/08…/slices/8.icon/通用/金额隐藏@3x.png
|
|
20
|
+
扁平文件名:src/slices/8.icon_通用_金额隐藏@3x.png (/ → _)
|
|
21
|
+
schema 引用:/slices/8.icon_通用_金额隐藏@3x.png
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**铁律**:
|
|
25
|
+
- 凡 schema 里 Image / backgroundImage 引用的切图,本地文件必须落在 `src/slices/`,且为扁平文件(无子目录)。
|
|
26
|
+
- 绝不允许散落在 `src/cards/<id>/` 或停留在 `UI/<artboard>/slices/`。
|
|
27
|
+
- 多卡共享的切图(如「箭头右」「顶部-弥散」)**按内容 sha256 去重只存一份**(不是按文件名去重),且以**语义名**落地(见 §1.1 / §3.1)。
|
|
28
|
+
- 落地名 = **按实际功能与作用取的语义名**;`<flat>`(导出路径 `/`→`_`)只是机械产线的过渡形态,交付前必须重命名(§1.1)。
|
|
29
|
+
|
|
30
|
+
## 1.1 语义化命名(落地即重命名,强制 · 2026-09-17 固化)
|
|
31
|
+
|
|
32
|
+
设计稿素材的原始命名**不可信**:MeaXure 导出常见 `8.icon/通用/金额隐藏@3x.png`(导出目录前缀)、`编组 4.png` / `矩形 4.png`(Sketch 默认图层名)、`icon (2).png`(副本)、`展开.png` / `icon_编辑.png`(中文)。扁平化后这些名字直接进 `src/slices/`,**从名字看不出用途**,也无法判断能否复用,同一张图还会以多个名字并存。
|
|
33
|
+
|
|
34
|
+
**规则**:切图落到 `src/slices/` 时**必须按实际功能与作用重命名**——名字由「这张图在卡里干什么」决定,不由素材原名决定。
|
|
35
|
+
|
|
36
|
+
- **命名形态**:纯 ASCII,`<角色><业务对象>[@Nx].<ext>`,camelCase(推荐)或 snake_case;保留来源倍率后缀(`@3x`),扩展名小写。
|
|
37
|
+
- **角色词(按作用取,不限于)**:`icon`(功能图标)/ `bg`(背景、弥散、渐变底)/ `btn`(按钮底图)/ `badge`(角标、标签)/ `logo`(品牌标识)/ `banner`(运营位)/ `avatar`(头像)/ `chart`(图表底图)/ `illustration`(插画)/ `mask`(遮罩)/ `tab`(页签底图)。
|
|
38
|
+
- **业务对象**:取卡片语义(`ArrowRight`、`AmountHidden`、`CardHeader`、`EquityActivity`),不要用画板名、图层序号或导出目录段。
|
|
39
|
+
- **示例**:
|
|
40
|
+
|
|
41
|
+
| 素材原名(不可信) | 落地名(语义化) |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `8.icon/通用/箭头右@3x.png` | `iconArrowRight@3x.png` |
|
|
44
|
+
| `编组 4.png` | `iconArrowRightGray@3x.png` |
|
|
45
|
+
| `99.切图/位图/卡片/详情/顶部-弥散@3x.png` | `bgCardHeaderDiffuse@3x.png` |
|
|
46
|
+
| `展开.png` / `icon (2).png` | `iconCollapse@3x.png` |
|
|
47
|
+
| `成都农商银行logo-26` | `logoBank@3x.png` |
|
|
48
|
+
|
|
49
|
+
- **禁用名**:中文/非 ASCII、空格、`编组 N` / `矩形 N` / `形状` / `位图`、导出目录前缀(`8.icon_…`、`99.切图_…`、`4.信息展示_…`)、副本后缀(`xxx (2)`、`xxx_alt2`、`xxx_副本`)、裸通用名(`icon.png`、`image1.png`、`bg.png`)、哈希/纯数字名。lint `r59-slice-semantic-name` 告警(含空格另按 R31 报 ERROR)。
|
|
50
|
+
- **同一张图全项目只有一个名字**:多卡共用时引用同一语义名,**不允许一卡一名**(`assetAllocRadar_arrow_right.png` 与 `chevron_right.png` 同图两名 = 违规,见 §3.1 存量证据)。
|
|
51
|
+
- **改名要成套做**:改 `src/slices/` 文件名 + 用 `lib/slice_dedupe.mjs → rewriteSliceRefs` 改写**所有**卡片 schema/data 里的引用 + 同步 `src/slices/manifest.json`(已上传过的图改名后按内容 sha256 仍复用原线上地址,见 §3.2)。**禁止**新旧两份并存。
|
|
52
|
+
- **上游保留原名作证据**:`UI/<稿>/<画板>/slices/` 与 `spec.slices` 保持导出原名不动(来源可追溯),语义化只发生在 ② 汇聚落点这一层;元素名 ↔ 导出名匹配仍走 `resolveSliceExport`(§5)。
|
|
53
|
+
- **机械产线现状**:`sliceRefPath()` / v3 产线产出的仍是导出路径扁平名(过渡形态,lint 会告警)。**Agent 生成时优先在 IR 里直接写语义名**(`pi.value: "/slices/iconArrowRight@3x.png"`,并把该名字的切图放进要素包 `slices/`),或落地后按上一条成套改名;交付前 `r59` 告警应清零。
|
|
54
|
+
- **存量清理**:历史落地的非语义名 / 同图多名用 `scripts/slices_normalize.mjs` 成套归一化(默认 dry-run 出报告,`--write` 落盘;改名 + 合并副本 + 改写全部卡片引用 + 同步 manifest 一次做完)。脚本会同时报告**悬空引用**(卡片引用了 `src/slices/` 里不存在的文件)——这类问题改名救不了,需回设计稿重新导出或换图。
|
|
55
|
+
|
|
56
|
+
## 2. schema 里的引用格式:`/slices/***.**`
|
|
57
|
+
|
|
58
|
+
- 一律用 **绝对路径前缀 `/slices/`**,后接扁平文件名(含 `@3x` 与扩展名)。
|
|
59
|
+
- 正确:`/slices/8.icon_通用_金额隐藏@3x.png`
|
|
60
|
+
- 错误:`src/slices/...`、`slices/...`、`UI/.../...@3x.png`、`@filestore/...`(同步前不允许)
|
|
61
|
+
- 生成阶段(上传前)只能出现 `/slices/...`;`@filestore/` 仅由「切图上传」(`skills/finmall-card-sync/scripts/slice_upload.mjs`)在上传成功后改写产生——**没有自动上传**,只有用户点顶栏「远端 → 切图上传」/ 调 MCP `card_slice_upload` / 跑 CLI 时才会发生。
|
|
62
|
+
- 运行时解析:浏览器以页面 origin 为基解析 `/slices/...` → 命中 Vite 的 `/slices/*` 静态中间件(见 §4)。
|
|
63
|
+
|
|
64
|
+
## 2.1 排除「导出目录」切图层(`99.切图/*`)—— 不作为卡片内容
|
|
65
|
+
|
|
66
|
+
**根因(2026-08-15 修复 `切换语音` 误引用)**:MeaXure 导出会在图层列表里塞入一个 `99.切图/...` 组,把**全部已导出切图作为图层**列出(如 `99.切图/icon/24/切换语音`、`99.切图/icon/20/箭头-向右`、`99.切图/位图/卡片/详情/顶部-弥散`)。这些是**导出清单 / 占位引用**,并非卡片视觉内容;它们几乎出现在每一个画板里。若把它们当卡片元素渲染,会凭空在每张卡注入切换语音/箭头等图标,且因其 basename(去前缀后)与真实内容切图撞名,造成误引用——典型表现就是 `切换语音@3x.png` 被 200+ 次错误引用并落盘到 `src/slices/`。
|
|
67
|
+
|
|
68
|
+
**规则**:
|
|
69
|
+
- 渲染阶段(`buildLeaf` 对 `type==='slice'`、`imageFromSlice`)**跳过图层名以 `99.切图` 开头的 slice**,直接 `return null`,不生成 Image、不产生 `/slices/` 引用。
|
|
70
|
+
- 过滤只看「**图层名**前缀」,不看导出路径——导出路径本身可能也是 `99.切图/...`(如顶部-弥散图),那是合法的;真正的内容切图用语义化图层名(如 `顶部返回_icon`、`金额隐藏`、`箭头右_icon`)。
|
|
71
|
+
- 效果:`切换语音`、`箭头-向右` 这类**仅在 `99.切图` 目录里出现**的切图,全量回归后引用数归零、不再落盘;`顶部-弥散` 因另有语义化内容图层引用,仍正常保留。
|
|
72
|
+
|
|
73
|
+
> 配套:`generate_cards.mjs` 在全量运行(`--ui` 不带 `--only`/`--popups`)开始时**清空 `src/slices`**,再仅重建被引用者,从根上杜绝历史残留的未引用切图。
|
|
74
|
+
|
|
75
|
+
## 3. 仅拷贝「被引用」的切图
|
|
76
|
+
|
|
77
|
+
`generate_cards.mjs → collectSlicesToSrcSlices(artboardDir, schema)`:
|
|
78
|
+
1. 遍历生成后的 `schema.view`,收集所有 `properties[].itemKey ∈ {value, src}` 且值匹配 `/^\/slice\/(.+)$/` 的扁平文件名 → `referenced` 集合。
|
|
79
|
+
2. 遍历 `UI/<artboard>/slices/`(递归),将每个图片的「相对路径 `/`→`_` 扁平名」与 `referenced` 比对;命中则 `copyFileSync` 到 `src/slices/<flat>`。
|
|
80
|
+
3. 未命中的切图**不复制**(即使上游导出了也不落地)。
|
|
81
|
+
|
|
82
|
+
> 效果:每个画板只把卡片真正用到的切图汇聚到 `src/slices/`,避免把 chrome 切图(顶部返回 / 切换语音背景 / 顶部-弥散背景等)或冗余素材带进来。
|
|
83
|
+
|
|
84
|
+
## 3.1 落地前判重:已存在则直接引用,不重复导入(强制 · 2026-09-17 固化)
|
|
85
|
+
|
|
86
|
+
汇聚切图到 `src/slices/` **之前必须先判重**。判定口径按权威度排序:
|
|
87
|
+
|
|
88
|
+
1. **内容 sha256(唯一权威判据)**:一致即同一张图 → **复用既有文件**,把 schema/data 里的引用改写到既有文件名,**不新增副本**。
|
|
89
|
+
2. **尺寸(w×h)+ 字节数**:快速预筛与人工核对用(`manifest.json` 的 `dimensions` / `size`),**不单独作为判据**(同尺寸同字节数不等于同图)。
|
|
90
|
+
3. **来源(导出稿 / 画板 / 图层名,若可知)**:只作辅助证据与**命名依据**(同一来源同一图层基本可判定同图),不参与去重判定。
|
|
91
|
+
|
|
92
|
+
- **机械执行**:`expand_ir.mjs → syncReferencedSlices` 与 `generate_cards.mjs → collectSlicesToSrcSlices` 在拷贝前都会算 sha256 比对 `src/slices/`,命中同内容即**跳过拷贝 + 改写引用 + 回写 schema/data**,并打印 `[slices] 内容已存在 → 复用 <既有名>(引用改写 … → …,不重复导入)`。共享实现:`scripts/lib/slice_dedupe.mjs`(`buildContentIndex` / `sha256File` / `duplicatePeers` / `rewriteSliceRefs` / `collectSliceRefStrings`)。
|
|
93
|
+
- **兜底告警**:lint `r60-slice-duplicate-content` 报「本卡引用的切图与 X、Y 内容完全相同」;`node scripts/lint_card.mjs --all | grep r60` 一次列出全仓重复组。
|
|
94
|
+
- **存量证据(2026-09-17)**:`src/slices/` 60 个文件只有 50 份唯一内容、7 组重复(`chevron_right.png` ≡ `assetAllocRadar_arrow_right.png` ≡ `编组 4.png`;`icon_edit.png` ≡ `icon_edit_pen.png` ≡ `icon_编辑.png` 等)——历史遗留,**新增切图不得再制造重复**。
|
|
95
|
+
- **想换个更好的名字**:走 §1.1 的成套改名(改名 + 全量引用改写 + 删旧文件),**不要**再拷一份新名字的副本。
|
|
96
|
+
|
|
97
|
+
## 3.2 上传去重:共用切图只上传一次(强制)
|
|
98
|
+
|
|
99
|
+
③ 阶段 `skills/finmall-card-sync/scripts/slice_upload.mjs` 按**内容 sha256** 判重,且**不认文件名**:
|
|
100
|
+
|
|
101
|
+
- 同一张图被多张卡引用 → **只上传一次**,其余卡的 `/slices/<name>` 引用改写为**同一个** `@filestore/...` 地址。
|
|
102
|
+
- `src/slices/` 里内容相同但命名不同的副本、以及**同一批次内**的同内容文件 → 复用已上传/待上传那一份的地址,`manifest.json` 记 `reusedFrom: <源文件名>`;**绝不为同一张图产生第二个线上地址**。
|
|
103
|
+
- 单卡上传(`--cardId <id>`)同样命中复用;`--dry-run` 会先报出复用对(形如 `编组 4.png≡assetAllocRadar_arrow_right.png`);只有 `--force` 才强制重传。
|
|
104
|
+
- 上传阶段的复用是**兜底**:真正的去重应在 §3.1 落地阶段完成(同内容压根不进 `src/slices/`)。看到 `reusedFrom` 就说明落地阶段漏了,应回头合并成一份语义名文件。
|
|
105
|
+
|
|
106
|
+
## 4. 本地预览可访问(`/slices/*`)
|
|
107
|
+
|
|
108
|
+
本 skill 自包含渲染链路里,`scripts/render_card.mjs` 在自己的临时 HTTP 服务中直接拦截 `/slices/` 请求:
|
|
109
|
+
- 拦截 `req.url` 以 `/slices/` 开头的请求;
|
|
110
|
+
- 映射到宿主 `src/slices/<扁平名>`(`DSL_CARD_SLICES_DIR`,默认 `<cwd>/src/slices`),设置对应 `Content-Type`(`image/png` 等)后流式返回;
|
|
111
|
+
- 带目录穿越防护(`file` 必须仍在 `src/slices` 内);
|
|
112
|
+
- 文件不存在则返回 404。
|
|
113
|
+
|
|
114
|
+
→ 离线 `render_card.mjs` 可直接加载卡片引用的 `/slices/*.png`,无需先同步远端。宿主若另有 dev server(`npm run dev` / `vite preview`,默认 5180;或 cardbuddy:8787),也只需在自己的 Vite 配置中挂载同款 `/slices/*` 静态中间件即可。
|
|
115
|
+
|
|
116
|
+
## 5. 元素名 ↔ 导出文件名 匹配(resolveSliceExport)
|
|
117
|
+
|
|
118
|
+
sketch-spec-to-card 产出的 spec 中,**slice 元素的 `name`(图层路径)≠ 导出文件名(spec.slices 中的相对路径)**,仅 basename(去 `@Nx` 与扩展名后)一致,且常带如下差异,需用归一化匹配:
|
|
119
|
+
|
|
120
|
+
| 元素名(spec.elements) | 导出文件名(spec.slices) | 差异 |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `顶部返回_icon` | `8.icon/标题栏/顶部返回@3x.png` | 元素名尾随 `_icon`、前缀被改为导出目录 |
|
|
123
|
+
| `99.切图/icon/24/切换语音` | `99.切图/icon/28/切换语音@3x.png` | 数字目录段(24↔28)不同 |
|
|
124
|
+
| `4.信息展示/z#/详情/顶部-弥散` | `99.切图/位图/卡片/详情/顶部-弥散@3x.png` | 整段前缀不同 |
|
|
125
|
+
|
|
126
|
+
`lib/slicePath.mjs → resolveSliceExport(specSlices, sliceElName)` 归一化规则(`stripScale`):
|
|
127
|
+
1. 去缩放后缀 `@2x`/`@3x`(任意位置);
|
|
128
|
+
2. 去尾随 `_icon`;
|
|
129
|
+
3. 去扩展名(`.png`/`.jpg`/…);
|
|
130
|
+
4. 再按 basename 比对。
|
|
131
|
+
|
|
132
|
+
未在 spec.slices 中找到对应导出文件的 slice 元素(如 `成都农商银行logo-26`、`成功1`)→ 退回占位图(PLACEHOLDER_IMG),不进入 `/slices/` 引用。
|
|
133
|
+
|
|
134
|
+
## 6. 关联代码位置(保持一致)
|
|
135
|
+
|
|
136
|
+
| 职责 | 文件 / 函数 |
|
|
137
|
+
|---|---|
|
|
138
|
+
| 归一化 + 元素名↔导出匹配 + `/slices/` 路径生成 | `scripts/lib/slicePath.mjs`(`stripScale` / `resolveSliceExport` / `sliceFlatName` / `sliceRefPath`) |
|
|
139
|
+
| ~~geo 变体(已删除 2026-08-31)~~ | `spec_to_schema_geo.mjs` 已随 geo 整体删除;Image 生成现仅由 v3 承担(见上一行)。 |
|
|
140
|
+
| v3 变体(CLI 直组装)生成 Image 时引用 `/slices/` | `scripts/spec_to_schema_v3.mjs`(`imageFromSlice(sliceEl, sem.slicesExports)`,由 `extractSemantics` 透传 `spec.slices`) |
|
|
141
|
+
| 拷贝被引用切图到 `src/slices` | `scripts/generate_cards.mjs`(`collectSlicesToSrcSlices`,仅 referenced) |
|
|
142
|
+
| 本地 `/slices/*` 静态服务(自包含) | `scripts/render_card.mjs`(临时 HTTP 中间件,直接读 `slicesDir`) |
|
|
143
|
+
| ③ 阶段:上传切图 + 写映射 + 改写引用 | `skills/finmall-card-sync/scripts/slice_upload.mjs`(`uploadSlices`);HTTP 端点 `server/core/sliceUpload.js`(`POST /__slice-upload`);编辑器入口 `preview/App.vue`(`uploadCardSlices`);MCP `card_slice_upload` |
|
|
144
|
+
| 语义名判定 + 内容 sha256 去重 + 引用改写(共享实现) | `scripts/lib/slice_dedupe.mjs`(`checkSliceName` / `buildContentIndex` / `sha256File` / `duplicatePeers` / `rewriteSliceRefs` / `collectSliceRefStrings`) |
|
|
145
|
+
| 落地时同内容去重(不重复导入 + 改写引用回写) | `scripts/expand_ir.mjs`(`syncReferencedSlices`,已 export)、`scripts/iterate.mjs`(一条龙闭环内调用同一函数,支持 `--slices-dir`)、`scripts/generate_cards.mjs`(`collectSlicesToSrcSlices`) |
|
|
146
|
+
| 命名 / 重复告警(lint,WARN) | `scripts/lint_card.mjs`(`r59-slice-semantic-name` / `r60-slice-duplicate-content`) |
|
|
147
|
+
| 存量切图归一化(语义化改名 + 同内容合并 + 引用/manifest 成套改写;**默认 dry-run**) | `scripts/slices_normalize.mjs`(`--write` 落盘;`--dedupe` / `--prune-unreferenced` / `--rename old=new` / `--map file.json` / `--no-legacy` / `--json`;内置 `LEGACY_RENAMES` = 2026-09-17 逐张核图确认的存量映射) |
|
|
148
|
+
| 上传阶段同内容复用线上地址(`reusedFrom`) | `skills/finmall-card-sync/scripts/slice_upload.mjs`(`shaToUploaded` 索引 + 批次内延后复用) |
|
|
149
|
+
|
|
150
|
+
> 所有变体(geo / v3)引用切片必须走 `sliceRefPath()`(产出 `/slices/<flat>`),与其 `collectSlicesToSrcSlices` 拷贝目标(扁平 `<flat>`)严格对应,禁止各自拼字符串。
|
|
151
|
+
|
|
152
|
+
## 7. 常见错误
|
|
153
|
+
|
|
154
|
+
| 错误 | 后果 | 正确 |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| 切图落在 `src/slices/<category>/<name>@3x.png`(多级目录) | 与 schema `/slices/<flat>` 对不上,预览 404 | 扁平:`src/slices/<flat>@3x.png` |
|
|
157
|
+
| schema 引用写 `src/slices/...` 或 `slices/...` | Vite 不识别,预览拉不到 | 一律 `/slices/<flat>@3x.png` |
|
|
158
|
+
| 无差别拷贝 `UI/<artboard>/slices/` 下全部切图 | 带进 chrome / 冗余素材 | `collectSlicesToSrcSlices` 仅拷 referenced |
|
|
159
|
+
| 元素名直接当文件名(未归一化) | `顶部返回_icon` ≠ `顶部返回` 匹配失败 → 退化占位图 | 走 `resolveSliceExport` 归一化匹配 |
|
|
160
|
+
| 推送远端时仍残留 `/slices/` 引用 | 线上取不到图(远端没有 `/slices/` 静态服务) | 推送前先跑「切图上传」:`node skills/finmall-card-sync/scripts/slice_upload.mjs --cardId <id>`,改写为 `@filestore/<ns>/<ts>_<name>@3x.png` |
|
|
161
|
+
| 沿用素材原名落地(`编组 4.png` / `8.icon_通用_箭头右@3x.png` / `展开.png`) | 名字看不出用途,无法判断能否复用,后期改名要翻全部卡片 | 按功能/作用语义化命名(§1.1);lint `r59` 告警 |
|
|
162
|
+
| 同图多名各存一份(`chevron_right.png` + `assetAllocRadar_arrow_right.png`) | `src/slices` 堆重复副本,上传后同一张图多个线上地址 | 落地前按 sha256 判重、命中即复用既有名(§3.1);lint `r60` 告警 |
|
|
163
|
+
| 改名只改文件名、不改引用 | schema 指向不存在的文件 → 预览 404 | `rewriteSliceRefs` 成套改写全部卡片 schema/data + 同步 `manifest.json` |
|
|
164
|
+
| 为「共用图」在每张卡各上传一次 | 同一张图产生多个 `@filestore` 地址,图片库膨胀 | 上传按内容 sha256 复用(§3.2),`manifest.json` 记 `reusedFrom` |
|
|
165
|
+
| **箭头/方向类用 Icon 矢量字形而非设计稿切片**(2026-08-17 强化) | 设计稿给了 `slices/8.icon/通用/箭头右@3x.png` 却用 Icon → 视觉风格不统一、颜色难对齐、字形风格可能与设计稿偏差 | 设计稿有切片时 → `<Image value="/slices/<flat>.png" />`;仅当设计稿无切片时才退回 Icon glyph。详见 `generation_pitfalls.md §4.11` |
|
|
166
|
+
|
|
167
|
+
## 8. 校验清单
|
|
168
|
+
|
|
169
|
+
生成卡片后确认:
|
|
170
|
+
- [ ] `src/slices/` 下只有扁平图片文件(无子目录)。
|
|
171
|
+
- [ ] 每张卡片 schema 引用的 `/slices/*.png` 在 `src/slices/` 均有同名扁平文件。
|
|
172
|
+
- [ ] `src/slices/` 文件数 = 所有卡片引用并集去重,未引用的切图未落地。
|
|
173
|
+
- [ ] 卡片预览或 `node scripts/render_card.mjs --id <id> --out render.png` 能直接加载 `/slices/*.png`(Network 面板 200 image/png)。
|
|
174
|
+
- [ ] 跑过「切图上传」后,schema 引用变为 `@filestore/...`,本地 `src/slices/` 真源仍在,且 `src/slices/manifest.json` 有对应记录(`url` / `sha256` / `cards`)。
|
|
175
|
+
- [ ] `src/slices/` 每个文件名都能看出功能与作用(无中文名、无 `编组 N` / 导出目录前缀 / 副本后缀);`node scripts/lint_card.mjs --id <id> | grep r59` 无新增告警。
|
|
176
|
+
- [ ] `src/slices/` 无内容重复文件:`node scripts/lint_card.mjs --all | grep r60` 为空(或按 sha256 分组核对唯一内容数 = 文件数)。
|
|
177
|
+
- [ ] 跑过切图上传后,同一张图在 `manifest.json` 里只有一个 `url`(同内容副本记 `reusedFrom`,没有第二个 `@filestore` 地址)。
|