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,343 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* apply_opacity_pattern.mjs — 把「透明度表达式节点」机械改写成透明度切换固定范式(R57)。
|
|
4
|
+
*
|
|
5
|
+
* 背景:移动端渲染不支持 style.other.opacity 走表达式(itemValueFrom:"1"),只有预览环境可能生效。
|
|
6
|
+
* 范式:按该节点用到的透明度取值生成等量「静态透明度变体」,统一套进一层 Row/Column 容器;
|
|
7
|
+
* 容器宽高 = 被展示节点宽高 + other.overflow:"hidden"(隐藏变体若仍占位则被裁掉);
|
|
8
|
+
* 点击等事件全部移到容器上;变体之间用互斥 v-show 显隐切换,视觉上等价于透明度变化。
|
|
9
|
+
* 完整说明见 references/recipes.md §17 + references/recipe_snippets/opacity-switch.json。
|
|
10
|
+
*
|
|
11
|
+
* 用法:
|
|
12
|
+
* node scripts/apply_opacity_pattern.mjs --id <cardId> # 改写全部透明度表达式节点
|
|
13
|
+
* node scripts/apply_opacity_pattern.mjs --id <cardId> --check # 只报告,不写盘(有则 exit 1)
|
|
14
|
+
* node scripts/apply_opacity_pattern.mjs --id <cardId> --dry --json # 预览改写结果(JSON)
|
|
15
|
+
* node scripts/apply_opacity_pattern.mjs --id <cardId> --node comp_8 --container Column
|
|
16
|
+
*
|
|
17
|
+
* 可解析的透明度表达式:单三元 `COND ? A : B`、右嵌套三元 `C1 ? A : C2 ? B : C`。
|
|
18
|
+
* 其他形态(函数调用/查表)无法机械拆解 → exit 2,按范式手工改写。
|
|
19
|
+
*/
|
|
20
|
+
import fs from 'node:fs'
|
|
21
|
+
import path from 'node:path'
|
|
22
|
+
import { cardSchemaPath, metaDir, readJson } from './lib/paths.mjs'
|
|
23
|
+
|
|
24
|
+
const VSHOW_WRAP_REQUIRED = new Set([
|
|
25
|
+
'MultiColumn', 'ListLayout', 'GridLayout', 'AbsoluteLayout', 'Canvas', 'Map', 'Video', 'ViewContainer',
|
|
26
|
+
])
|
|
27
|
+
|
|
28
|
+
function parseArgs(argv) {
|
|
29
|
+
const o = { id: '', node: [], container: 'Row', check: false, dry: false, json: false }
|
|
30
|
+
for (let i = 0; i < argv.length; i++) {
|
|
31
|
+
const a = argv[i]
|
|
32
|
+
if (a === '--id') o.id = argv[++i]
|
|
33
|
+
else if (a === '--node') o.node.push(argv[++i])
|
|
34
|
+
else if (a === '--container') o.container = argv[++i]
|
|
35
|
+
else if (a === '--check') o.check = true
|
|
36
|
+
else if (a === '--dry') o.dry = true
|
|
37
|
+
else if (a === '--json') o.json = true
|
|
38
|
+
}
|
|
39
|
+
return o
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ---------- style-meta:itemId 权威来源 ---------- */
|
|
43
|
+
const styleMeta = readJson(path.join(metaDir, 'style-meta.json'), {})
|
|
44
|
+
const GROUP = {}
|
|
45
|
+
for (const g of styleMeta.properties || []) {
|
|
46
|
+
if (!g.itemKey) continue
|
|
47
|
+
GROUP[g.itemKey] = {
|
|
48
|
+
itemId: g.itemId,
|
|
49
|
+
children: Object.fromEntries((g.itemChildren || []).filter((c) => c.itemKey).map((c) => [c.itemKey, c.itemId])),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function gitemId(groupKey) {
|
|
53
|
+
const g = GROUP[groupKey]
|
|
54
|
+
if (!g) throw new Error(`style-meta 里没有样式组 ${groupKey}`)
|
|
55
|
+
return g.itemId
|
|
56
|
+
}
|
|
57
|
+
function citemId(groupKey, childKey) {
|
|
58
|
+
const id = (GROUP[groupKey] || {}).children?.[childKey]
|
|
59
|
+
if (!id) throw new Error(`style-meta 里 ${groupKey} 组没有子项 ${childKey}`)
|
|
60
|
+
return id
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const leaf = (groupKey, key, value) => ({
|
|
64
|
+
itemId: citemId(groupKey, key), itemKey: key, itemName: key, itemType: '',
|
|
65
|
+
itemValueFrom: '0', itemValue: value, itemDefaultValue: value,
|
|
66
|
+
})
|
|
67
|
+
const group = (key, children) => ({
|
|
68
|
+
itemId: gitemId(key), itemKey: key, itemName: key, itemType: 'group',
|
|
69
|
+
itemValueFrom: '0', itemValue: {}, itemDefaultValue: {},
|
|
70
|
+
itemChildren: { properties: children },
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
/* ---------- 读节点样式 ---------- */
|
|
74
|
+
function styleProps(node) { return ((node.style || {}).properties) || [] }
|
|
75
|
+
function groupOf(node, key) { return styleProps(node).find((p) => p.itemKey === key) }
|
|
76
|
+
function childOf(node, key, childKey) {
|
|
77
|
+
return (((groupOf(node, key) || {}).itemChildren || {}).properties || []).find((c) => c.itemKey === childKey)
|
|
78
|
+
}
|
|
79
|
+
function valOf(node, key, childKey) {
|
|
80
|
+
const c = childOf(node, key, childKey)
|
|
81
|
+
return c ? c.itemValue : undefined
|
|
82
|
+
}
|
|
83
|
+
function opacityProp(node) { return childOf(node, 'other', 'opacity') }
|
|
84
|
+
|
|
85
|
+
function isExprOpacity(node) {
|
|
86
|
+
const c = opacityProp(node)
|
|
87
|
+
if (!c) return false
|
|
88
|
+
return c.itemValueFrom === '1' || (typeof c.itemValue === 'string' && c.itemValue.includes('$'))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* ---------- 透明度表达式 → 变体表 ---------- */
|
|
92
|
+
function parseOpacityExpr(expr) {
|
|
93
|
+
const src = String(expr).trim()
|
|
94
|
+
const variants = []
|
|
95
|
+
let rest = src
|
|
96
|
+
for (;;) {
|
|
97
|
+
const m = rest.match(/^(.+?)\s*\?\s*(-?[\d.]+)\s*:\s*([\s\S]+)$/)
|
|
98
|
+
if (!m) break
|
|
99
|
+
variants.push({ cond: m[1].trim(), opacity: m[2] })
|
|
100
|
+
rest = m[3].trim()
|
|
101
|
+
// 右嵌套三元:剥掉外层括号继续
|
|
102
|
+
if (rest.startsWith('(') && rest.endsWith(')')) rest = rest.slice(1, -1).trim()
|
|
103
|
+
}
|
|
104
|
+
if (!variants.length) return null
|
|
105
|
+
if (!/^-?[\d.]+$/.test(rest)) return null
|
|
106
|
+
variants.push({ cond: null, opacity: rest })
|
|
107
|
+
return variants
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function negate(conds) {
|
|
111
|
+
return conds.map((c) => `!(${c})`).join(' && ')
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function condName(expr, opacity) {
|
|
115
|
+
if (expr.length < 15) return expr
|
|
116
|
+
return `透明度${opacity}:${expr.replace(/\$/g, '').replace(/\s+/g, '')}`
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ---------- v-show 指令 ---------- */
|
|
120
|
+
function vshow(expr, name) {
|
|
121
|
+
const showType = {
|
|
122
|
+
itemId: '001', itemKey: 'showType', itemName: 'showType', itemType: '',
|
|
123
|
+
itemValueFrom: '0', itemValue: '01', itemDefaultValue: '01',
|
|
124
|
+
}
|
|
125
|
+
const condition = {
|
|
126
|
+
itemId: '002', itemKey: 'condition', itemName: 'condition', itemType: '',
|
|
127
|
+
itemValueFrom: '0', itemValue: { expression: expr, name },
|
|
128
|
+
itemDefaultValue: { expression: expr, name },
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
itemId: '001', itemKey: 'v-show', itemName: 'v-show', itemType: '',
|
|
132
|
+
itemValueFrom: '0', itemValue: {}, itemDefaultValue: {},
|
|
133
|
+
itemChildren: { properties: [showType, condition] },
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const clone = (x) => JSON.parse(JSON.stringify(x))
|
|
138
|
+
|
|
139
|
+
/* ---------- 构造变体节点 ---------- */
|
|
140
|
+
function buildVariant(node, { id, cond, opacity, needsVshowWrapper }) {
|
|
141
|
+
const variant = clone(node)
|
|
142
|
+
variant.componentId = id
|
|
143
|
+
variant.eventactions = [] // 事件一律上移到容器
|
|
144
|
+
variant.directives = []
|
|
145
|
+
// margin 归零:外边距由容器承载,避免变体各带一份外边距
|
|
146
|
+
const op = opacityProp(variant)
|
|
147
|
+
if (op) {
|
|
148
|
+
op.itemValueFrom = '0'
|
|
149
|
+
op.itemValue = String(opacity)
|
|
150
|
+
op.itemDefaultValue = String(opacity)
|
|
151
|
+
}
|
|
152
|
+
const marginGroup = groupOf(variant, 'margin')
|
|
153
|
+
if (marginGroup) {
|
|
154
|
+
for (const c of (marginGroup.itemChildren || {}).properties || []) {
|
|
155
|
+
const zero = c.itemKey === 'marginType' ? '' : '0'
|
|
156
|
+
c.itemValue = zero
|
|
157
|
+
c.itemDefaultValue = zero
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!needsVshowWrapper) {
|
|
161
|
+
variant.directives = [vshow(cond.expression, cond.name)]
|
|
162
|
+
return { outer: variant, id }
|
|
163
|
+
}
|
|
164
|
+
// 特殊节点(List/Grid/Canvas…)不能直接挂 v-show(R53):每个变体再套一层承载显隐的 Row
|
|
165
|
+
const w = valOf(node, 'rect', 'width')
|
|
166
|
+
const h = valOf(node, 'rect', 'height')
|
|
167
|
+
const wrapId = `${id}_v`
|
|
168
|
+
const wrapper = {
|
|
169
|
+
componentId: wrapId,
|
|
170
|
+
className: 'Row',
|
|
171
|
+
name: 'Row',
|
|
172
|
+
style: {
|
|
173
|
+
properties: [
|
|
174
|
+
group('rect', [
|
|
175
|
+
leaf('rect', 'width', w === undefined ? 'auto' : String(w)),
|
|
176
|
+
leaf('rect', 'height', h === undefined ? 'auto' : String(h)),
|
|
177
|
+
]),
|
|
178
|
+
group('flex', [
|
|
179
|
+
leaf('flex', 'justifyContent', 'center'),
|
|
180
|
+
leaf('flex', 'alignItems', 'center'),
|
|
181
|
+
leaf('flex', 'gap', '0'),
|
|
182
|
+
leaf('flex', 'flexShrink', '0'),
|
|
183
|
+
]),
|
|
184
|
+
group('padding', [leaf('padding', 'paddingType', '0'), leaf('padding', 'padding', '0')]),
|
|
185
|
+
group('other', [leaf('other', 'overflow', 'hidden')]),
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
properties: [],
|
|
189
|
+
directives: [vshow(cond.expression, cond.name)],
|
|
190
|
+
eventactions: [],
|
|
191
|
+
slots: [{ slotKey: 'default', slotName: '默认插槽', slotSwitch: '1', components: [variant] }],
|
|
192
|
+
}
|
|
193
|
+
return { outer: wrapper, id: wrapId }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* ---------- 构造容器 ---------- */
|
|
197
|
+
function buildContainer(node, variants, containerClass) {
|
|
198
|
+
const width = valOf(node, 'rect', 'width')
|
|
199
|
+
const height = valOf(node, 'rect', 'height')
|
|
200
|
+
const shrink = valOf(node, 'flex', 'flexShrink')
|
|
201
|
+
const props = [
|
|
202
|
+
// 容器宽高必须与被展示节点一致(范式硬约束)
|
|
203
|
+
group('rect', [
|
|
204
|
+
leaf('rect', 'width', width === undefined ? 'auto' : String(width)),
|
|
205
|
+
leaf('rect', 'height', height === undefined ? 'auto' : String(height)),
|
|
206
|
+
]),
|
|
207
|
+
group('flex', [
|
|
208
|
+
leaf('flex', 'justifyContent', 'center'),
|
|
209
|
+
leaf('flex', 'alignItems', 'center'),
|
|
210
|
+
leaf('flex', 'gap', '0'),
|
|
211
|
+
leaf('flex', 'flexShrink', shrink === undefined ? '0' : String(shrink)),
|
|
212
|
+
]),
|
|
213
|
+
group('background', [leaf('background', 'backgroundType', '00')]),
|
|
214
|
+
group('padding', [leaf('padding', 'paddingType', '0'), leaf('padding', 'padding', '0')]),
|
|
215
|
+
]
|
|
216
|
+
// 原节点的外边距搬到容器(保持在父布局里的位置)
|
|
217
|
+
const marginGroup = groupOf(node, 'margin')
|
|
218
|
+
if (marginGroup) props.push(clone(marginGroup))
|
|
219
|
+
// 隐藏溢出:v-show 在部分渲染实现里只隐藏不脱流,靠裁切保证外部布局不被撑开
|
|
220
|
+
props.push(group('other', [leaf('other', 'overflow', 'hidden')]))
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
componentId: `${node.componentId}_box`,
|
|
224
|
+
className: containerClass,
|
|
225
|
+
name: containerClass,
|
|
226
|
+
style: { properties: props },
|
|
227
|
+
properties: [],
|
|
228
|
+
directives: clone(node.directives || []), // 原节点自身的显隐条件留在容器上
|
|
229
|
+
eventactions: clone(node.eventactions || []), // 点击等事件挂在容器上
|
|
230
|
+
slots: [{ slotKey: 'default', slotName: '默认插槽', slotSwitch: '1', components: variants }],
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* ---------- 遍历改写 ---------- */
|
|
235
|
+
function childArrays(node) {
|
|
236
|
+
const arrs = []
|
|
237
|
+
if (Array.isArray(node.components)) arrs.push(node.components)
|
|
238
|
+
for (const s of node.slots || []) if (Array.isArray(s.components)) arrs.push(s.components)
|
|
239
|
+
return arrs
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function transform(view, opts) {
|
|
243
|
+
const changed = []
|
|
244
|
+
const seen = new Set()
|
|
245
|
+
const walkNode = (node) => {
|
|
246
|
+
for (const arr of childArrays(node)) {
|
|
247
|
+
for (let i = 0; i < arr.length; i++) {
|
|
248
|
+
const child = arr[i]
|
|
249
|
+
if (isExprOpacity(child) && (!opts.node.length || opts.node.includes(child.componentId))) {
|
|
250
|
+
arr[i] = rewrite(child, opts, changed, seen)
|
|
251
|
+
}
|
|
252
|
+
walkNode(arr[i])
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
walkNode(view)
|
|
257
|
+
return changed
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function rewrite(node, opts, changed, seen) {
|
|
261
|
+
if (seen.has(node.componentId)) return node
|
|
262
|
+
seen.add(node.componentId)
|
|
263
|
+
const expr = String(opacityProp(node).itemValue)
|
|
264
|
+
const parsed = parseOpacityExpr(expr)
|
|
265
|
+
if (!parsed) {
|
|
266
|
+
throw Object.assign(new Error(
|
|
267
|
+
`${node.className} "${node.componentId}" 的透明度表达式无法机械拆解:${expr}\n` +
|
|
268
|
+
'请按 references/recipes.md §17 手工改写(枚举用到的透明度取值 → 等量静态变体 + 容器 + 互斥 v-show)。'
|
|
269
|
+
), { code: 'unparsable-opacity-expr' })
|
|
270
|
+
}
|
|
271
|
+
const opacities = [...new Set(parsed.map((v) => v.opacity))]
|
|
272
|
+
const needsVshowWrapper = VSHOW_WRAP_REQUIRED.has(node.className)
|
|
273
|
+
const conds = []
|
|
274
|
+
const variants = parsed.map((v, idx) => {
|
|
275
|
+
const expression = v.cond ? v.cond : negate(conds.slice())
|
|
276
|
+
if (v.cond) conds.push(v.cond)
|
|
277
|
+
const cond = { expression, name: condName(expression, v.opacity) }
|
|
278
|
+
const id = idx === 0 ? node.componentId : `${node.componentId}_op${idx}`
|
|
279
|
+
return buildVariant(node, { id, cond, opacity: v.opacity, needsVshowWrapper }).outer
|
|
280
|
+
})
|
|
281
|
+
const container = buildContainer(node, variants, opts.container)
|
|
282
|
+
changed.push({
|
|
283
|
+
componentId: node.componentId,
|
|
284
|
+
className: node.className,
|
|
285
|
+
containerId: container.componentId,
|
|
286
|
+
containerClass: opts.container,
|
|
287
|
+
expression: expr,
|
|
288
|
+
opacities,
|
|
289
|
+
variants: variants.map((v) => v.componentId),
|
|
290
|
+
conditions: variants.map((v, idx) => {
|
|
291
|
+
const src = idx === 0 && !needsVshowWrapper ? v : v
|
|
292
|
+
const d = (src.directives || [])[0]
|
|
293
|
+
const c = (((d || {}).itemChildren || {}).properties || []).find((p) => p.itemKey === 'condition')
|
|
294
|
+
return (c || {}).itemValue
|
|
295
|
+
}),
|
|
296
|
+
eventsMoved: (node.eventactions || []).length,
|
|
297
|
+
})
|
|
298
|
+
return container
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* ---------- main ---------- */
|
|
302
|
+
const opts = parseArgs(process.argv.slice(2))
|
|
303
|
+
if (!opts.id) {
|
|
304
|
+
console.error('用法: node scripts/apply_opacity_pattern.mjs --id <cardId> [--check|--dry] [--json] [--node <cid>] [--container Row|Column]')
|
|
305
|
+
process.exit(1)
|
|
306
|
+
}
|
|
307
|
+
const schemaPath = cardSchemaPath(opts.id)
|
|
308
|
+
if (!fs.existsSync(schemaPath)) {
|
|
309
|
+
console.error(`[apply_opacity_pattern] 找不到卡片 schema: ${schemaPath}`)
|
|
310
|
+
process.exit(1)
|
|
311
|
+
}
|
|
312
|
+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'))
|
|
313
|
+
|
|
314
|
+
// --check:只报告存在透明度表达式的节点
|
|
315
|
+
if (opts.check) {
|
|
316
|
+
const found = []
|
|
317
|
+
const walkNode = (n) => {
|
|
318
|
+
if (isExprOpacity(n)) found.push({ componentId: n.componentId, className: n.className, expression: String(opacityProp(n).itemValue) })
|
|
319
|
+
for (const arr of childArrays(n)) for (const c of arr) walkNode(c)
|
|
320
|
+
}
|
|
321
|
+
walkNode(schema.view)
|
|
322
|
+
if (opts.json) console.log(JSON.stringify({ id: opts.id, ok: found.length === 0, found }, null, 2))
|
|
323
|
+
else if (found.length) {
|
|
324
|
+
console.log(`[apply_opacity_pattern] ${opts.id}: ${found.length} 个节点用了透明度表达式(R57 禁止)`)
|
|
325
|
+
for (const f of found) console.log(` - ${f.className} ${f.componentId}: opacity="${f.expression}"`)
|
|
326
|
+
} else console.log(`[apply_opacity_pattern] ${opts.id}: 无透明度表达式节点`)
|
|
327
|
+
process.exit(found.length ? 1 : 0)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
let changed
|
|
331
|
+
try {
|
|
332
|
+
changed = transform(schema.view, opts)
|
|
333
|
+
} catch (e) {
|
|
334
|
+
console.error(`[apply_opacity_pattern] ${e.message}`)
|
|
335
|
+
process.exit(e.code === 'unparsable-opacity-expr' ? 2 : 1)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (!changed.length) {
|
|
339
|
+
console.log(JSON.stringify({ id: opts.id, changed: 0, note: '没有需要改写的透明度表达式节点' }, null, 2))
|
|
340
|
+
process.exit(0)
|
|
341
|
+
}
|
|
342
|
+
if (!opts.dry) fs.writeFileSync(schemaPath, JSON.stringify(schema, null, 2) + '\n', 'utf8')
|
|
343
|
+
console.log(JSON.stringify({ id: opts.id, written: !opts.dry, changed: changed.length, nodes: changed }, null, 2))
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* build_card_index.mjs — 预计算「卡片索引」,让智能体选近邻模板时查表而非扫源文件。
|
|
4
|
+
*
|
|
5
|
+
* 扫描宿主项目卡片目录(默认 <cwd>/src/cards)下每张卡的 schema.json,对每张卡提取:
|
|
6
|
+
* - components:用到的组件类集合
|
|
7
|
+
* - recipes:用到的范式(alertCard / globalSet / money-format / list-expand / canvas-head / tab-billMode)
|
|
8
|
+
* - tags:结构信号 + 通用 UI 意图词(list / detail / form / dialog / result / canvas / amount / tab / interactive)
|
|
9
|
+
*
|
|
10
|
+
* 输出 <skill>/references/card_index.json,供 SKILL.md 的「意图路由表」直接查。
|
|
11
|
+
*
|
|
12
|
+
* 用法:node scripts/build_card_index.mjs
|
|
13
|
+
*/
|
|
14
|
+
import fs from 'node:fs'
|
|
15
|
+
import path from 'node:path'
|
|
16
|
+
import { fileURLToPath } from 'node:url'
|
|
17
|
+
|
|
18
|
+
import { cardsDir, skillRoot } from './lib/paths.mjs'
|
|
19
|
+
|
|
20
|
+
const CARDS_DIR = cardsDir
|
|
21
|
+
const OUT = path.join(skillRoot, 'references', 'card_index.json')
|
|
22
|
+
|
|
23
|
+
/* ---- recipe 探测(对整卡文本做子串/特征匹配) ---- */
|
|
24
|
+
const RECIPE_PROBES = [
|
|
25
|
+
{ key: 'alertCard', test: (t) => /alertCard/.test(t) },
|
|
26
|
+
{ key: 'globalSet', test: (t) => /globalSet/.test(t) },
|
|
27
|
+
{ key: 'money-format', test: (t) => /toFixed\(/.test(t) && /replace\(/.test(t) || /formatMoney/.test(t) },
|
|
28
|
+
{ key: 'list-expand', test: (t) => /showAll/.test(t) },
|
|
29
|
+
{ key: 'canvas-head', test: (t) => /onMeasured/.test(t) },
|
|
30
|
+
{ key: 'tab-billMode', test: (t) => /billMode/.test(t) || /activeTab/.test(t) },
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
/* ---- 标签启发(结构信号优先,卡名仅用通用 UI 意图词,不含具体业务实体名) ---- */
|
|
34
|
+
function inferTags(name, components, recipes) {
|
|
35
|
+
const tags = new Set()
|
|
36
|
+
const has = (re) => re.test(name || '')
|
|
37
|
+
// 结构信号(优先,无业务词依赖)
|
|
38
|
+
if (components.includes('ListLayout') || components.includes('GridLayout')) tags.add('list')
|
|
39
|
+
if (components.includes('Input')) tags.add('form')
|
|
40
|
+
if (components.includes('Canvas')) tags.add('canvas')
|
|
41
|
+
if (recipes.includes('alertCard')) tags.add('dialog')
|
|
42
|
+
if (recipes.includes('tab-billMode')) tags.add('tab')
|
|
43
|
+
if (recipes.includes('money-format')) tags.add('amount')
|
|
44
|
+
if (recipes.includes('list-expand')) tags.add('list')
|
|
45
|
+
if (recipes.includes('globalSet')) tags.add('interactive')
|
|
46
|
+
// 通用 UI 意图词兜底(非业务实体名;如"列表/表单/弹窗/结果/详情"等)
|
|
47
|
+
if (has(/列表|清单|list|记录/)) tags.add('list')
|
|
48
|
+
if (has(/弹窗|dialog|对话框/)) tags.add('dialog')
|
|
49
|
+
if (has(/表单|输入|密码|验证|form/)) tags.add('form')
|
|
50
|
+
if (has(/成功|失败|结果|result|success|failed/)) tags.add('result')
|
|
51
|
+
if (has(/详情|detail/)) tags.add('detail')
|
|
52
|
+
return [...tags]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 子节点双通道:既可能在 node.components(如 Root 直连),也可能在 node.slots[].components
|
|
56
|
+
// (如 Row/Column 的插槽)——历史上只走 slots 通道,导致 Root 直连子树整棵丢失、
|
|
57
|
+
// card_index 的 components 字段退化为只剩 ["Root"]。
|
|
58
|
+
function collectComponents(node, out) {
|
|
59
|
+
if (!node || typeof node !== 'object') return
|
|
60
|
+
if (node.className) out.add(node.className)
|
|
61
|
+
for (const c of node.components || []) collectComponents(c, out)
|
|
62
|
+
for (const s of node.slots || []) for (const c of s.components || []) collectComponents(c, out)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { RECIPE_PROBES, inferTags, collectComponents }
|
|
66
|
+
|
|
67
|
+
function main() {
|
|
68
|
+
if (!fs.existsSync(CARDS_DIR)) { console.error('[error] 未找到 src/cards:' + CARDS_DIR); process.exit(1) }
|
|
69
|
+
const cards = []
|
|
70
|
+
for (const id of fs.readdirSync(CARDS_DIR).sort()) {
|
|
71
|
+
const dir = path.join(CARDS_DIR, id)
|
|
72
|
+
const schemaPath = path.join(dir, 'schema.json')
|
|
73
|
+
if (!fs.statSync(dir).isDirectory() || !fs.existsSync(schemaPath)) continue
|
|
74
|
+
let schema
|
|
75
|
+
try { schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8')) } catch { continue }
|
|
76
|
+
const text = JSON.stringify(schema)
|
|
77
|
+
const compSet = new Set()
|
|
78
|
+
collectComponents(schema.view, compSet)
|
|
79
|
+
const recipes = RECIPE_PROBES.filter((p) => p.test(text)).map((p) => p.key)
|
|
80
|
+
const name = schema.name || id
|
|
81
|
+
const tags = inferTags(name, [...compSet], recipes)
|
|
82
|
+
cards.push({
|
|
83
|
+
id,
|
|
84
|
+
name,
|
|
85
|
+
pageId: schema.pageId || '',
|
|
86
|
+
components: [...compSet].sort(),
|
|
87
|
+
recipes,
|
|
88
|
+
tags,
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
const index = {
|
|
92
|
+
generatedAt: new Date().toISOString(),
|
|
93
|
+
cardCount: cards.length,
|
|
94
|
+
cards,
|
|
95
|
+
}
|
|
96
|
+
fs.mkdirSync(path.dirname(OUT), { recursive: true })
|
|
97
|
+
fs.writeFileSync(OUT, JSON.stringify(index, null, 2) + '\n')
|
|
98
|
+
console.log(`[ok] 已生成卡片索引:${OUT}(${cards.length} 张)`)
|
|
99
|
+
// 打印意图覆盖概览
|
|
100
|
+
const byTag = {}
|
|
101
|
+
for (const c of cards) for (const t of c.tags) byTag[t] = (byTag[t] || 0) + 1
|
|
102
|
+
console.log('标签覆盖:' + JSON.stringify(byTag))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 仅当作为 CLI 直接运行时才执行(被 import 复用探测/遍历函数时不执行)
|
|
106
|
+
const _thisScript = fs.realpathSync(fileURLToPath(import.meta.url))
|
|
107
|
+
const _calledAs = process.argv[1] ? fs.realpathSync(path.resolve(process.argv[1])) : ''
|
|
108
|
+
if (_thisScript === _calledAs) main()
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* build_card_templates.mjs — 从存量卡提炼「整卡模板库」(全量重建)。
|
|
4
|
+
*
|
|
5
|
+
* 与空骨架(scaffold_card --intent)的边界:
|
|
6
|
+
* - 空骨架出「最小合法结构」,快但版式纪律要从零重建;
|
|
7
|
+
* - 整卡模板出「克隆最近邻真实卡 + 改写」,天然继承存量卡已验证的版式/绑定/范式。
|
|
8
|
+
*
|
|
9
|
+
* 聚类:按卡片结构标签(inferTags 产出的通用 UI 意图词)归簇——纯结构信号,
|
|
10
|
+
* 不含业务词,设计稿风格变化不影响聚类逻辑。
|
|
11
|
+
*
|
|
12
|
+
* 代表卡选择(确定性打分,同分取字典序小的保证可复现):
|
|
13
|
+
* score = recipe 命中数 × 10 + 组件种类数 × 2 − 节点数 × 0.01
|
|
14
|
+
*
|
|
15
|
+
* 与 register_card_template.mjs 的协作(沉淀闭环):
|
|
16
|
+
* - 本工具是全量重建;register 是单卡登记(沉淀时刻)。
|
|
17
|
+
* - 重建时**保留** register 写入的人工资产:pinned 代表卡(不被打分顶替)、
|
|
18
|
+
* keywords(意图路由词)、custom 自定义簇;仅当 pinned 源卡被删除时才回退打分。
|
|
19
|
+
*
|
|
20
|
+
* 输出 references/card_templates.json,供 scaffold_card.mjs --from / MCP card_scaffold
|
|
21
|
+
* 的 from 参数、resolve_card_template.mjs 意图路由使用。
|
|
22
|
+
*
|
|
23
|
+
* 用法:node skills/dsl-card-generator/scripts/build_card_templates.mjs [--json]
|
|
24
|
+
* 退出码:0 = 正常。
|
|
25
|
+
*/
|
|
26
|
+
import fs from 'node:fs'
|
|
27
|
+
import path from 'node:path'
|
|
28
|
+
import { fileURLToPath } from 'node:url'
|
|
29
|
+
import { cardsDir, skillRoot } from './lib/paths.mjs'
|
|
30
|
+
import { RECIPE_PROBES, inferTags, collectComponents } from './build_card_index.mjs'
|
|
31
|
+
|
|
32
|
+
const OUT = path.join(skillRoot, 'references', 'card_templates.json')
|
|
33
|
+
|
|
34
|
+
/** 簇优先级:一张卡可能同时命中多个标签,按此顺序归入最特异的一个簇 */
|
|
35
|
+
const CLUSTER_ORDER = ['dialog', 'form', 'result', 'tab', 'canvas', 'list', 'amount', 'detail', 'interactive', 'other']
|
|
36
|
+
|
|
37
|
+
/** 内置簇的种子意图关键词(通用 UI 意图词,非业务词;register 登记的自定义词会合入) */
|
|
38
|
+
const SEED_KEYWORDS = {
|
|
39
|
+
dialog: ['弹窗', '弹框', '对话框', 'dialog'],
|
|
40
|
+
form: ['表单', '输入', 'form'],
|
|
41
|
+
result: ['结果', '成功', '失败', 'result'],
|
|
42
|
+
tab: ['多tab', '页签', '切换', 'tab'],
|
|
43
|
+
canvas: ['图表', '仪表盘', '画布', '环形图', 'canvas'],
|
|
44
|
+
list: ['列表', '清单', '记录', 'list'],
|
|
45
|
+
amount: ['金额', '账单', '数值', 'amount'],
|
|
46
|
+
detail: ['详情', 'detail'],
|
|
47
|
+
interactive: ['交互', '联动', 'interactive'],
|
|
48
|
+
other: ['其他'],
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 测试夹具卡(脚手架回归/全组件自测)不是生产版式,不进入模板候选 */
|
|
52
|
+
const TEST_FIXTURE_RE = /(^dsl_test|[-_]test)/i
|
|
53
|
+
|
|
54
|
+
function countNodes(node) {
|
|
55
|
+
if (!node || typeof node !== 'object') return 0
|
|
56
|
+
let n = 1
|
|
57
|
+
for (const c of node.components || []) n += countNodes(c)
|
|
58
|
+
for (const s of node.slots || []) for (const c of s.components || []) n += countNodes(c)
|
|
59
|
+
return n
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function clusterOf(tags) {
|
|
63
|
+
for (const key of CLUSTER_ORDER) if (tags.includes(key)) return key
|
|
64
|
+
return 'other'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** 提取单卡模板特征(签名)。供本工具与 register/resolve 复用(同一份定义)。 */
|
|
68
|
+
export function extractCardFeatures(id) {
|
|
69
|
+
const schemaPath = path.join(cardsDir, id, 'schema.json')
|
|
70
|
+
if (!fs.existsSync(schemaPath)) return null
|
|
71
|
+
let schema
|
|
72
|
+
try {
|
|
73
|
+
schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'))
|
|
74
|
+
} catch {
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
const text = JSON.stringify(schema)
|
|
78
|
+
const compSet = new Set()
|
|
79
|
+
collectComponents(schema.view, compSet)
|
|
80
|
+
const recipes = RECIPE_PROBES.filter((p) => p.test(text)).map((p) => p.key)
|
|
81
|
+
const tags = inferTags(schema.name || id, [...compSet], recipes)
|
|
82
|
+
const nodeCount = countNodes(schema.view)
|
|
83
|
+
return {
|
|
84
|
+
id,
|
|
85
|
+
name: schema.name || id,
|
|
86
|
+
tags,
|
|
87
|
+
recipes,
|
|
88
|
+
components: [...compSet].sort(),
|
|
89
|
+
nodeCount,
|
|
90
|
+
cluster: clusterOf(tags),
|
|
91
|
+
score: recipes.length * 10 + compSet.size * 2 - nodeCount * 0.01,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** 读取现有注册表(不存在返回空壳) */
|
|
96
|
+
export function loadRegistry() {
|
|
97
|
+
try {
|
|
98
|
+
return JSON.parse(fs.readFileSync(OUT, 'utf8'))
|
|
99
|
+
} catch {
|
|
100
|
+
return { clusters: [] }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function main() {
|
|
105
|
+
const asJson = process.argv.includes('--json')
|
|
106
|
+
const cards = []
|
|
107
|
+
for (const id of fs.readdirSync(cardsDir).sort()) {
|
|
108
|
+
if (TEST_FIXTURE_RE.test(id)) continue
|
|
109
|
+
const dir = path.join(cardsDir, id)
|
|
110
|
+
if (!fs.statSync(dir).isDirectory()) continue
|
|
111
|
+
const f = extractCardFeatures(id)
|
|
112
|
+
if (f) cards.push(f)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const prev = loadRegistry()
|
|
116
|
+
const prevByKey = new Map((prev.clusters || []).map((c) => [c.key, c]))
|
|
117
|
+
const aliveIds = new Set(cards.map((c) => c.id))
|
|
118
|
+
const preservedNotes = []
|
|
119
|
+
|
|
120
|
+
const clusters = []
|
|
121
|
+
for (const key of CLUSTER_ORDER) {
|
|
122
|
+
const members = cards.filter((c) => c.cluster === key)
|
|
123
|
+
if (!members.length) continue
|
|
124
|
+
members.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
|
125
|
+
const old = prevByKey.get(key)
|
|
126
|
+
const pinnedAlive = old && old.template && old.template.pinned && aliveIds.has(old.template.sourceCard)
|
|
127
|
+
// pin 是人工意图,优先于自动聚类:pinned 源卡即使自动簇判定落在别的簇
|
|
128
|
+
// (不在本簇 members 里),也必须保住代表位,按其真实特征刷新模板信息。
|
|
129
|
+
const t = pinnedAlive ? extractCardFeatures(old.template.sourceCard) || members[0] : members[0]
|
|
130
|
+
if (old && old.template && old.template.pinned && !pinnedAlive) {
|
|
131
|
+
preservedNotes.push(`簇 ${key}:pinned 模板 ${old.template.sourceCard} 已不存在,回退为打分选代表`)
|
|
132
|
+
}
|
|
133
|
+
const keywords = [...new Set([...(SEED_KEYWORDS[key] || []), ...((old && old.keywords) || [])])]
|
|
134
|
+
clusters.push({
|
|
135
|
+
key,
|
|
136
|
+
keywords,
|
|
137
|
+
cardCount: members.length,
|
|
138
|
+
template: {
|
|
139
|
+
sourceCard: t.id,
|
|
140
|
+
name: t.name,
|
|
141
|
+
tags: t.tags,
|
|
142
|
+
recipes: t.recipes,
|
|
143
|
+
components: t.components,
|
|
144
|
+
nodeCount: t.nodeCount,
|
|
145
|
+
...(pinnedAlive ? { pinned: true } : {}),
|
|
146
|
+
},
|
|
147
|
+
members: members.map((m) => m.id),
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 保留 register 登记的自定义簇(源卡存活才保留)
|
|
152
|
+
for (const old of prevByKey.values()) {
|
|
153
|
+
if (!old.custom) continue
|
|
154
|
+
if (!old.template || !aliveIds.has(old.template.sourceCard)) {
|
|
155
|
+
preservedNotes.push(`自定义簇 ${old.key}:模板卡 ${old.template ? old.template.sourceCard : '?'} 已不存在,簇被移除`)
|
|
156
|
+
continue
|
|
157
|
+
}
|
|
158
|
+
const f = extractCardFeatures(old.template.sourceCard)
|
|
159
|
+
clusters.push({
|
|
160
|
+
...old,
|
|
161
|
+
template: { ...old.template, ...(f ? { tags: f.tags, recipes: f.recipes, components: f.components, nodeCount: f.nodeCount, name: f.name } : {}) },
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const registry = {
|
|
166
|
+
generatedAt: new Date().toISOString(),
|
|
167
|
+
cardCount: cards.length,
|
|
168
|
+
hint: '整卡模板库:克隆模板卡改写 = scaffold_card.mjs --from <sourceCard> 或 MCP card_scaffold(from=...);意图路由查 keywords(resolve_card_template.mjs);新模式登记用 register_card_template.mjs。',
|
|
169
|
+
clusters,
|
|
170
|
+
}
|
|
171
|
+
fs.mkdirSync(path.dirname(OUT), { recursive: true })
|
|
172
|
+
fs.writeFileSync(OUT, JSON.stringify(registry, null, 2) + '\n')
|
|
173
|
+
|
|
174
|
+
if (asJson) {
|
|
175
|
+
console.log(JSON.stringify(registry, null, 2))
|
|
176
|
+
} else {
|
|
177
|
+
console.log(`[ok] 已生成整卡模板库:${OUT}(${cards.length} 张卡 → ${clusters.length} 簇)`)
|
|
178
|
+
for (const c of clusters) {
|
|
179
|
+
const pin = c.template.pinned ? '📌' : ' '
|
|
180
|
+
console.log(
|
|
181
|
+
` ${pin}${c.key.padEnd(12)} ${String(c.cardCount).padStart(2)} 卡 模板=${c.template.sourceCard}(${c.template.name},${c.template.nodeCount} 节点)`,
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
for (const n of preservedNotes) console.log(` ⚠️ ${n}`)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const _thisScript = fs.realpathSync(fileURLToPath(import.meta.url))
|
|
189
|
+
const _calledAs = process.argv[1] ? fs.realpathSync(path.resolve(process.argv[1])) : ''
|
|
190
|
+
if (_thisScript === _calledAs) main()
|