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,447 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* route_input.mjs — 建卡输入形态路由(SKILL.md §8 Workflow 0 的机械判定器)。
|
|
4
|
+
*
|
|
5
|
+
* 收到建卡请求先把用户给的输入路径喂给本工具,由它判定形态并给出下一步命令,
|
|
6
|
+
* 避免 AI 凭感觉决定「要不要先跑 sketch-spec-to-card」:
|
|
7
|
+
* node scripts/route_input.mjs ~/Downloads/转账ui设计稿0723
|
|
8
|
+
* node scripts/route_input.mjs UI/01基金_基金推荐 --json
|
|
9
|
+
*
|
|
10
|
+
* 判定结果(kind):
|
|
11
|
+
* meaxure-export Sketch MeaXure 标注导出目录(index.html 内嵌 artboards + preview/ + assets/)
|
|
12
|
+
* → 必须先经 sketch-spec-to-card 前处理,禁止直接拿 preview/ 整图建卡;
|
|
13
|
+
* 若 UI/ 下已有 export 匹配的 manifest.json 则跳过前处理,直接消费存量产物。
|
|
14
|
+
* ui-bundle sketch-spec-to-card 的产物(manifest.json / spec.json + card-N.png)→ Workflow A 按用户指定的单画板生成
|
|
15
|
+
* card-dir 已存在的卡片目录(schema.json)→ Workflow B 编辑
|
|
16
|
+
* image 单张截图/设计稿 PNG → Workflow A(无 spec,全靠视觉推断)
|
|
17
|
+
* unknown 判不出形态,需人工确认输入
|
|
18
|
+
*
|
|
19
|
+
* 退出码:0 = 已路由;3 = unknown(需人工确认);1 = 用法错误 / 路径不存在。
|
|
20
|
+
*/
|
|
21
|
+
import fs from 'node:fs'
|
|
22
|
+
import path from 'node:path'
|
|
23
|
+
import { spawnSync } from 'node:child_process'
|
|
24
|
+
import { fileURLToPath } from 'node:url'
|
|
25
|
+
import { skillsDir } from './lib/paths.mjs'
|
|
26
|
+
|
|
27
|
+
const IMAGE_EXT = new Set(['.png', '.jpg', '.jpeg', '.webp'])
|
|
28
|
+
/** 已知入口文件:判定交给它所在目录(index.html=MeaXure 导出;manifest/clusters/spec/prompt=要素包;schema=卡片目录)。 */
|
|
29
|
+
const ENTRY_FILES = new Set(['index.html', 'manifest.json', 'clusters.json', 'spec.json', 'spec.md', 'prompt.md', 'schema.json'])
|
|
30
|
+
const SKETCH_SKILL = path.join(skillsDir, 'sketch-spec-to-card')
|
|
31
|
+
/**
|
|
32
|
+
* 多画板选择门禁(铁律见 references/rules.md §十三):UI 产物允许全量生成,
|
|
33
|
+
* 但卡片生成必须以用户指定的单个画板 / 单个 cardKey 为单位。
|
|
34
|
+
* 作为提示文本随 next/note 下发,让判定器输出即带门禁,不依赖模型记住文档。
|
|
35
|
+
*/
|
|
36
|
+
const SELECTION_GATE =
|
|
37
|
+
'# 多画板门禁(rules.md §十三):把全量画板按 1..n 列出(序号 1 起)+ 末尾加「全部」;当前环境支持弹选项(request_user_input)就优先弹选择,否则一句提问后停止输出、等用户回序号再继续——UI 产物可回「全部」全量生成,产卡默认单画板 / 单 cardKey(回「全部」才算显式批量授权);禁止自行挑画板、禁止擅自批量产卡;清单逐行配图:每个画板行下紧跟它自己的预览图绝对路径(--list / 门禁输出的 `图:` 行),与序号一一对应,缺图显式标缺失,禁止把图堆在清单末尾。'
|
|
38
|
+
/**
|
|
39
|
+
* 要素包根目录 UI/:与上游 `sketch-spec-to-card/scripts/parse_meaxure.py` 的 `REPO_ROOT/UI`
|
|
40
|
+
* (按脚本自身路径推算,非 cwd)保持同一锚点,避免「建议命令」与「实际落点」分叉。
|
|
41
|
+
* 可用 DSL_CARD_UI_DIR 直接指定,或用 DSL_CARD_PROJECT_ROOT 指定宿主工程根。
|
|
42
|
+
*/
|
|
43
|
+
const REPO_ROOT = path.resolve(skillsDir, '..')
|
|
44
|
+
const UI_ROOT =
|
|
45
|
+
process.env.DSL_CARD_UI_DIR || path.join(process.env.DSL_CARD_PROJECT_ROOT || REPO_ROOT, 'UI')
|
|
46
|
+
|
|
47
|
+
function exists(target) {
|
|
48
|
+
try {
|
|
49
|
+
fs.accessSync(target)
|
|
50
|
+
return true
|
|
51
|
+
} catch {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isDir(target) {
|
|
57
|
+
try {
|
|
58
|
+
return fs.statSync(target).isDirectory()
|
|
59
|
+
} catch {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function entries(target) {
|
|
65
|
+
try {
|
|
66
|
+
return fs.readdirSync(target, { withFileTypes: true })
|
|
67
|
+
} catch {
|
|
68
|
+
return []
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 大文件安全读取:超过 cap 时只取头尾各一半(MeaXure 的 data 通常在 index.html 尾部)。 */
|
|
73
|
+
function readTextCapped(file, cap = 64 * 1024 * 1024) {
|
|
74
|
+
const size = fs.statSync(file).size
|
|
75
|
+
if (size <= cap) return fs.readFileSync(file, 'utf8')
|
|
76
|
+
const half = Math.floor(cap / 2)
|
|
77
|
+
const fd = fs.openSync(file, 'r')
|
|
78
|
+
const head = Buffer.alloc(half)
|
|
79
|
+
const tail = Buffer.alloc(half)
|
|
80
|
+
fs.readSync(fd, head, 0, half, 0)
|
|
81
|
+
fs.readSync(fd, tail, 0, half, Math.max(0, size - half))
|
|
82
|
+
fs.closeSync(fd)
|
|
83
|
+
return head.toString('utf8') + tail.toString('utf8')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** 命令里输出相对 cwd 的短路径,跨盘/越界时退回绝对路径。 */
|
|
87
|
+
function shortPath(abs) {
|
|
88
|
+
const rel = path.relative(process.cwd(), abs)
|
|
89
|
+
return rel && !rel.startsWith('..') && !path.isAbsolute(rel) ? rel : abs
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function pythonBin() {
|
|
93
|
+
if (process.env.DSL_CARD_PYTHON) return process.env.DSL_CARD_PYTHON
|
|
94
|
+
for (const bin of ['python3', 'python']) {
|
|
95
|
+
const probe = spawnSync(bin, ['--version'], { stdio: 'ignore' })
|
|
96
|
+
if (probe.status === 0) return bin
|
|
97
|
+
}
|
|
98
|
+
return 'python3'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Sketch MeaXure 标注导出判定:index.html 的 data 签名是硬证据,preview/ assets/ links/ 是佐证。 */
|
|
102
|
+
function detectMeaxure(dir) {
|
|
103
|
+
const evidence = []
|
|
104
|
+
const indexHtml = path.join(dir, 'index.html')
|
|
105
|
+
let signature = false
|
|
106
|
+
if (exists(indexHtml)) {
|
|
107
|
+
try {
|
|
108
|
+
const text = readTextCapped(indexHtml)
|
|
109
|
+
signature = /let\s+data\s*=/.test(text) && /"artboards"\s*:/.test(text)
|
|
110
|
+
if (signature) evidence.push('index.html 内嵌 let data = {"artboards":[...]} 标注 JSON')
|
|
111
|
+
else evidence.push('index.html 存在但未命中 MeaXure data 签名')
|
|
112
|
+
} catch {
|
|
113
|
+
evidence.push('index.html 不可读')
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const previewDir = path.join(dir, 'preview')
|
|
117
|
+
const assetsDir = path.join(dir, 'assets')
|
|
118
|
+
const linksDir = path.join(dir, 'links')
|
|
119
|
+
const previewPngs = isDir(previewDir)
|
|
120
|
+
? entries(previewDir).filter((e) => e.isFile() && IMAGE_EXT.has(path.extname(e.name).toLowerCase())).length
|
|
121
|
+
: 0
|
|
122
|
+
if (previewPngs) evidence.push(`preview/ 画板整图 ${previewPngs} 张`)
|
|
123
|
+
if (isDir(assetsDir)) evidence.push('assets/ 切图素材目录')
|
|
124
|
+
if (isDir(linksDir)) evidence.push('links/ 重定向页(MeaXure 附带)')
|
|
125
|
+
|
|
126
|
+
const matched = signature || (exists(indexHtml) && (previewPngs > 0 || isDir(assetsDir)))
|
|
127
|
+
return { matched, confidence: signature ? 'high' : 'medium', evidence, previewPngs }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** sketch-spec-to-card 产物判定:支持平铺(UI/<画板>/)与按稿分片(UI/<稿名>/<画板>/)两种布局。 */
|
|
131
|
+
function detectUiBundle(dir) {
|
|
132
|
+
const evidence = []
|
|
133
|
+
const manifest = path.join(dir, 'manifest.json')
|
|
134
|
+
const clusters = path.join(dir, 'clusters.json')
|
|
135
|
+
const ownSpec = path.join(dir, 'spec.json')
|
|
136
|
+
const ownCards = entries(dir).filter((e) => e.isFile() && /^card-\d+\.png$/i.test(e.name)).length
|
|
137
|
+
|
|
138
|
+
if (exists(manifest)) evidence.push('manifest.json 画板清单')
|
|
139
|
+
if (exists(clusters)) evidence.push('clusters.json 聚类/状态模型')
|
|
140
|
+
if (exists(ownSpec)) evidence.push('spec.json 视觉要素标注')
|
|
141
|
+
if (ownCards) evidence.push(`card-N.png 聚焦图 ${ownCards} 张`)
|
|
142
|
+
|
|
143
|
+
const subBoards = entries(dir)
|
|
144
|
+
.filter((e) => e.isDirectory())
|
|
145
|
+
.map((e) => path.join(dir, e.name))
|
|
146
|
+
.filter((sub) => exists(path.join(sub, 'spec.json')))
|
|
147
|
+
if (subBoards.length) evidence.push(`${subBoards.length} 个画板子目录含 spec.json`)
|
|
148
|
+
|
|
149
|
+
const isArtboardBundle = exists(ownSpec) && ownCards > 0
|
|
150
|
+
const matched = exists(manifest) || isArtboardBundle || subBoards.length > 0
|
|
151
|
+
return {
|
|
152
|
+
matched,
|
|
153
|
+
confidence: exists(manifest) || isArtboardBundle ? 'high' : 'medium',
|
|
154
|
+
evidence,
|
|
155
|
+
manifest: exists(manifest) ? manifest : null,
|
|
156
|
+
clusters: exists(clusters) ? clusters : null,
|
|
157
|
+
artboardBundle: isArtboardBundle,
|
|
158
|
+
specJson: exists(ownSpec) ? ownSpec : null,
|
|
159
|
+
cardImages: ownCards,
|
|
160
|
+
boards: subBoards,
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** 统计一份 manifest 里「已产出要素包」的画板数:同一导出有多套产物时用它挑主产物。 */
|
|
165
|
+
function countGenerated(manifest) {
|
|
166
|
+
const canvases = Array.isArray(manifest?.canvases) ? manifest.canvases : []
|
|
167
|
+
return {
|
|
168
|
+
generated: canvases.filter((c) => c && c.status === 'generated').length,
|
|
169
|
+
total: canvases.length,
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* 在 UI/ 下找已前处理过的产物:比对 manifest.export 与输入目录,其次按稿名兜底。
|
|
175
|
+
*
|
|
176
|
+
* 同一份导出可能同时存在两套产物(历史上先平铺跑过 UI/<画板>/,后来改用按稿分片 UI/<稿名>/<画板>/):
|
|
177
|
+
* 只返回第一份会让下游误判「目标画板还没前处理」而重跑管线,故这里收集全部匹配,
|
|
178
|
+
* 以「已生成画板最多」的为主产物,其余进 alsoFound 一并报告。
|
|
179
|
+
*/
|
|
180
|
+
function findPreprocessed(srcAbs) {
|
|
181
|
+
const norm = (p) => path.resolve(String(p || '')).replace(/[\\/]+$/, '')
|
|
182
|
+
const target = norm(srcAbs)
|
|
183
|
+
const candidates = [UI_ROOT, ...entries(UI_ROOT).filter((e) => e.isDirectory()).map((e) => path.join(UI_ROOT, e.name))]
|
|
184
|
+
const matches = []
|
|
185
|
+
for (const root of candidates) {
|
|
186
|
+
const manifestPath = path.join(root, 'manifest.json')
|
|
187
|
+
if (!exists(manifestPath)) continue
|
|
188
|
+
try {
|
|
189
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
|
|
190
|
+
if (manifest.export && norm(manifest.export) === target) {
|
|
191
|
+
matches.push({
|
|
192
|
+
uiDir: root,
|
|
193
|
+
manifest: manifestPath,
|
|
194
|
+
draft: manifest.draft || null,
|
|
195
|
+
via: 'manifest.export 匹配',
|
|
196
|
+
...countGenerated(manifest),
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
} catch {
|
|
200
|
+
// 坏 manifest 不作为证据,继续找下一个
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (!matches.length) {
|
|
204
|
+
const byName = path.join(UI_ROOT, path.basename(target), 'manifest.json')
|
|
205
|
+
if (exists(byName)) {
|
|
206
|
+
let counts = { generated: 0, total: 0 }
|
|
207
|
+
try {
|
|
208
|
+
counts = countGenerated(JSON.parse(fs.readFileSync(byName, 'utf8')))
|
|
209
|
+
} catch {
|
|
210
|
+
/* 坏 manifest 仍按目录名匹配,只是不带统计 */
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
uiDir: path.dirname(byName),
|
|
214
|
+
manifest: byName,
|
|
215
|
+
draft: path.basename(target),
|
|
216
|
+
via: '稿名同名目录兜底匹配',
|
|
217
|
+
...counts,
|
|
218
|
+
alsoFound: [],
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return null
|
|
222
|
+
}
|
|
223
|
+
matches.sort((a, b) => b.generated - a.generated || a.uiDir.length - b.uiDir.length)
|
|
224
|
+
const [best, ...rest] = matches
|
|
225
|
+
return {
|
|
226
|
+
...best,
|
|
227
|
+
alsoFound: rest.map((m) => ({ uiDir: shortPath(m.uiDir), manifest: shortPath(m.manifest), generated: m.generated, total: m.total })),
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function detectCardDir(dir) {
|
|
232
|
+
const schema = path.join(dir, 'schema.json')
|
|
233
|
+
if (!exists(schema)) return { matched: false }
|
|
234
|
+
return {
|
|
235
|
+
matched: true,
|
|
236
|
+
confidence: 'high',
|
|
237
|
+
evidence: [`schema.json(已存在卡片 ${path.basename(dir)})`, exists(path.join(dir, 'data.json')) ? 'data.json' : 'data.json 缺失'],
|
|
238
|
+
schema,
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function route(inputAbs) {
|
|
243
|
+
if (isDir(inputAbs)) {
|
|
244
|
+
const card = detectCardDir(inputAbs)
|
|
245
|
+
if (card.matched) {
|
|
246
|
+
return {
|
|
247
|
+
kind: 'card-dir',
|
|
248
|
+
confidence: card.confidence,
|
|
249
|
+
evidence: card.evidence,
|
|
250
|
+
skillChain: ['dsl-card-generator'],
|
|
251
|
+
workflow: 'Workflow B — 编辑已有卡片',
|
|
252
|
+
next: [
|
|
253
|
+
`node ${shortPath(path.join(skillsDir, 'dsl-card-generator', 'scripts', 'describe_card.mjs'))} --id ${path.basename(inputAbs)}`,
|
|
254
|
+
`node ${shortPath(path.join(skillsDir, 'dsl-card-generator', 'scripts', 'diff_card.mjs'))} --id ${path.basename(inputAbs)}`,
|
|
255
|
+
],
|
|
256
|
+
note: '输入已是卡片目录,走编辑链路(card_node_models 定位 + add_component 增改 + diff_card 兜底),不需要设计稿前处理。',
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const meaxure = detectMeaxure(inputAbs)
|
|
261
|
+
if (meaxure.matched) {
|
|
262
|
+
const done = findPreprocessed(inputAbs)
|
|
263
|
+
const draft = (done && done.draft) || path.basename(inputAbs)
|
|
264
|
+
const uiDir = done ? done.uiDir : path.join(UI_ROOT, draft)
|
|
265
|
+
const parse = path.join(SKETCH_SKILL, 'scripts', 'parse_meaxure.py')
|
|
266
|
+
const cluster = path.join(SKETCH_SKILL, 'scripts', 'cluster_states.py')
|
|
267
|
+
const py = pythonBin()
|
|
268
|
+
const uiRel = shortPath(uiDir)
|
|
269
|
+
return {
|
|
270
|
+
kind: 'meaxure-export',
|
|
271
|
+
confidence: meaxure.confidence,
|
|
272
|
+
evidence: meaxure.evidence,
|
|
273
|
+
skillChain: done ? ['dsl-card-generator'] : ['sketch-spec-to-card', 'dsl-card-generator'],
|
|
274
|
+
workflow: done ? 'Workflow 0 入口② → 选卡后 Workflow A(单画板)' : 'Workflow 0 入口① → 先 sketch-spec-to-card 前处理',
|
|
275
|
+
preprocess: {
|
|
276
|
+
needed: !done,
|
|
277
|
+
draft,
|
|
278
|
+
uiDir: uiRel,
|
|
279
|
+
uiRoot: shortPath(UI_ROOT),
|
|
280
|
+
manifest: done ? shortPath(done.manifest) : null,
|
|
281
|
+
matchedVia: done ? done.via : null,
|
|
282
|
+
// 主产物里已生成 / 总画板数,以及同一导出的其它产物目录(平铺与按稿分片并存时)
|
|
283
|
+
generated: done ? done.generated : 0,
|
|
284
|
+
total: done ? done.total : 0,
|
|
285
|
+
alsoFound: done && done.alsoFound.length ? done.alsoFound : undefined,
|
|
286
|
+
},
|
|
287
|
+
next: done
|
|
288
|
+
? [
|
|
289
|
+
`# 多画板门禁(rules.md §十三):产物已存在≠已选卡。先跑下面这条只读 --list 取全量画板清单(1..n + 全部),交给用户并停止输出等回复,再消费产物`,
|
|
290
|
+
`${py} ${shortPath(parse)} --src "${inputAbs}" --draft "${draft}" --list`,
|
|
291
|
+
SELECTION_GATE,
|
|
292
|
+
`# 拿到回复后(产物已存在,${done.via},已生成 ${done.generated}/${done.total} 画板),跳过前处理,只消费用户指定的那个画板:`,
|
|
293
|
+
`cat ${shortPath(done.manifest)}`,
|
|
294
|
+
`node ${shortPath(path.join(skillsDir, 'dsl-card-generator', 'scripts', 'spec_tree.mjs'))} --spec ${shortPath(path.join(uiDir, '<画板>', 'spec.json'))}`,
|
|
295
|
+
...(done.alsoFound && done.alsoFound.length
|
|
296
|
+
? [`# 同一导出另有产物目录(目标画板在主 manifest 里是 pending 时先去这些目录找,找到就直接用,别重跑管线):${done.alsoFound.map((m) => `${m.uiDir}(已生成 ${m.generated}/${m.total})`).join('、')}`]
|
|
297
|
+
: []),
|
|
298
|
+
`# 用户回「全部」= 显式批量授权:可重跑管线补齐缺失画板(${py} ${shortPath(parse)} --src "${inputAbs}" --out "${shortPath(UI_ROOT)}" --draft "${draft}" --scope-all --build)`,
|
|
299
|
+
]
|
|
300
|
+
: [
|
|
301
|
+
`# 多画板门禁(rules.md §十三):先 --list 列 1..n + 全部 并停下等用户回复;下面 parse 不带范围直接跑会 exit 4`,
|
|
302
|
+
`${py} ${shortPath(parse)} --src "${inputAbs}" --out "${shortPath(UI_ROOT)}" --draft "${draft}" --build`,
|
|
303
|
+
`# 拿到回复后给上一条 parse 命令补范围:序号 k → --indices <k-1>;「全部」→ --scope-all`,
|
|
304
|
+
`${py} ${shortPath(cluster)} --out "${uiRel}"`,
|
|
305
|
+
SELECTION_GATE,
|
|
306
|
+
],
|
|
307
|
+
note: done
|
|
308
|
+
? 'UI/ 下已有该导出的产物,不重复跑管线;按 manifest.json / clusters.json 选卡后走 Workflow A(每轮只产用户指定的那一张卡),overlay_dialog 走 Workflow A.5(不产卡,补到父卡事件)。多画板未指定目标时先列清单请用户选,禁止自行挑画板或擅自批量(rules.md §十三)。'
|
|
309
|
+
: 'MeaXure 导出是多画板批量输入:禁止直接读 preview/ 整图建卡,必须先由 sketch-spec-to-card 做卡片聚焦裁切、多卡分离、弹窗识别与切图收集,产出 UI/<稿名>/ 要素包后回本 skill 生成。UI 产物可全量生成,卡片生成以用户指定的单画板 / 单 cardKey 为单位(rules.md §十三)。',
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const bundle = detectUiBundle(inputAbs)
|
|
314
|
+
if (bundle.matched) {
|
|
315
|
+
const specHint = bundle.specJson
|
|
316
|
+
? `node ${shortPath(path.join(skillsDir, 'dsl-card-generator', 'scripts', 'spec_tree.mjs'))} --spec ${shortPath(bundle.specJson)}`
|
|
317
|
+
: `cat ${shortPath(bundle.manifest || inputAbs)}`
|
|
318
|
+
return {
|
|
319
|
+
kind: 'ui-bundle',
|
|
320
|
+
confidence: bundle.confidence,
|
|
321
|
+
evidence: bundle.evidence,
|
|
322
|
+
skillChain: ['dsl-card-generator'],
|
|
323
|
+
workflow: bundle.artboardBundle ? 'Workflow A(单画板要素包)' : 'Workflow 0 入口② → 选卡后 Workflow A(单画板)',
|
|
324
|
+
bundle: {
|
|
325
|
+
manifest: bundle.manifest ? shortPath(bundle.manifest) : null,
|
|
326
|
+
clusters: bundle.clusters ? shortPath(bundle.clusters) : null,
|
|
327
|
+
specJson: bundle.specJson ? shortPath(bundle.specJson) : null,
|
|
328
|
+
cardImages: bundle.cardImages,
|
|
329
|
+
boards: bundle.boards.length,
|
|
330
|
+
},
|
|
331
|
+
next: [
|
|
332
|
+
specHint,
|
|
333
|
+
`# 再按 SKILL.md §8 Workflow A 六步闭环生成 schema(scaffold_card → add_component/expand_ir → validate+lint → render_diff)`,
|
|
334
|
+
...(bundle.artboardBundle ? [] : [SELECTION_GATE]),
|
|
335
|
+
],
|
|
336
|
+
note: bundle.artboardBundle
|
|
337
|
+
? '输入已是单画板要素包,无需前处理,直接走 Workflow A 生成本卡。'
|
|
338
|
+
: '输入已是 sketch-spec-to-card 的产物,无需前处理;多画板/多卡时先让用户指定单个画板或 cardKey(禁止自行挑、禁止擅自批量,rules.md §十三),再走 Workflow A;subcard 与 overlay_dialog 按 Workflow A.5 分流。',
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const images = entries(inputAbs).filter((e) => e.isFile() && IMAGE_EXT.has(path.extname(e.name).toLowerCase()))
|
|
343
|
+
if (images.length === 1) {
|
|
344
|
+
return route(path.join(inputAbs, images[0].name))
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
kind: 'unknown',
|
|
348
|
+
confidence: 'low',
|
|
349
|
+
evidence: [
|
|
350
|
+
`目录内 ${entries(inputAbs).length} 项,未命中 MeaXure 签名(index.html + artboards)`,
|
|
351
|
+
'未命中 UI 要素包签名(manifest.json / spec.json + card-N.png)',
|
|
352
|
+
images.length ? `含 ${images.length} 张图片(多张,无法自动判定哪张是设计稿)` : '无图片文件',
|
|
353
|
+
],
|
|
354
|
+
skillChain: [],
|
|
355
|
+
workflow: '需人工确认',
|
|
356
|
+
next: ['# 请指明输入形态:MeaXure 导出目录 / UI 要素包 / 单张设计稿 PNG / 已有卡片目录'],
|
|
357
|
+
note: '判不出形态时不要猜:向用户确认输入,或显式指定 UI/<画板>/spec.json 与 card-N.png。',
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const ext = path.extname(inputAbs).toLowerCase()
|
|
362
|
+
if (IMAGE_EXT.has(ext)) {
|
|
363
|
+
const siblingSpec = path.join(path.dirname(inputAbs), 'spec.json')
|
|
364
|
+
if (exists(siblingSpec)) return route(path.dirname(inputAbs))
|
|
365
|
+
return {
|
|
366
|
+
kind: 'image',
|
|
367
|
+
confidence: 'high',
|
|
368
|
+
evidence: [`单张图片 ${path.basename(inputAbs)}(同目录无 spec.json)`],
|
|
369
|
+
skillChain: ['dsl-card-generator'],
|
|
370
|
+
workflow: 'Workflow 0 入口③ → Workflow A(无标注,视觉推断)',
|
|
371
|
+
next: [`# 直接多模态读图 → scaffold_card 出骨架 → validate+lint → render_diff 视觉闭环`],
|
|
372
|
+
note: '无 MeaXure 标注时全靠 AI 视觉推断,间距/圆角从图上目视估算;若这张图其实来自某份标注导出,请改传导出目录以走入口①。',
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (ENTRY_FILES.has(path.basename(inputAbs).toLowerCase())) {
|
|
377
|
+
return route(path.dirname(inputAbs))
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return {
|
|
381
|
+
kind: 'unknown',
|
|
382
|
+
confidence: 'low',
|
|
383
|
+
evidence: [`文件 ${path.basename(inputAbs)}(${ext || '无扩展名'})不是图片,也不是目录入口`],
|
|
384
|
+
skillChain: [],
|
|
385
|
+
workflow: '需人工确认',
|
|
386
|
+
next: ['# 传目录(MeaXure 导出 / UI 要素包 / 卡片目录)或设计稿 PNG'],
|
|
387
|
+
note: '判不出形态时不要猜:向用户确认输入。',
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function printHuman(result, inputAbs) {
|
|
392
|
+
const icon = { 'meaxure-export': '🧭', 'ui-bundle': '📦', 'card-dir': '✏️', image: '🖼️', unknown: '❓' }[result.kind] || '•'
|
|
393
|
+
const label = {
|
|
394
|
+
'meaxure-export': 'Sketch MeaXure 标注导出目录',
|
|
395
|
+
'ui-bundle': 'sketch-spec-to-card 产物(UI 要素包)',
|
|
396
|
+
'card-dir': '已存在卡片目录',
|
|
397
|
+
image: '单张设计稿图片',
|
|
398
|
+
unknown: '未识别形态',
|
|
399
|
+
}[result.kind]
|
|
400
|
+
console.log(`${icon} 输入路由:${result.kind} — ${label}(置信度 ${result.confidence})`)
|
|
401
|
+
console.log(` 输入:${shortPath(inputAbs)}`)
|
|
402
|
+
console.log(` 证据:${result.evidence.join(';')}`)
|
|
403
|
+
console.log(` 链路:${result.skillChain.length ? result.skillChain.join(' → ') : '(待定)'}|${result.workflow}`)
|
|
404
|
+
if (result.preprocess) {
|
|
405
|
+
const p = result.preprocess
|
|
406
|
+
console.log(` 前处理:${p.needed ? `需要(稿名建议 "${p.draft}" → ${p.uiDir})` : `已存在,跳过(${p.matchedVia};manifest=${p.manifest})`}`)
|
|
407
|
+
}
|
|
408
|
+
console.log(' 下一步:')
|
|
409
|
+
for (const line of result.next) console.log(` ${line}`)
|
|
410
|
+
console.log(` 说明:${result.note}`)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* 判定入口(CLI 与 MCP `card_route_input` 共用同一份实现,避免漂移)。
|
|
415
|
+
* 不打印、不 process.exit,只返回结构化结果;路径不存在时返回 `error` 字段。
|
|
416
|
+
* @param {string} input 输入路径(相对路径按 cwd 解析)
|
|
417
|
+
* @returns {object} `{ input, inputAbs, kind, confidence, evidence, skillChain, workflow, next, note, ... }`
|
|
418
|
+
*/
|
|
419
|
+
export function routeInput(input) {
|
|
420
|
+
const raw = String(input == null ? '' : input)
|
|
421
|
+
const inputAbs = path.resolve(raw)
|
|
422
|
+
if (!raw.trim() || !exists(inputAbs)) {
|
|
423
|
+
return { input: raw, inputAbs, error: 'input-not-found', message: `输入路径不存在:${inputAbs}` }
|
|
424
|
+
}
|
|
425
|
+
return { input: shortPath(inputAbs), inputAbs, ...route(inputAbs) }
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function main() {
|
|
429
|
+
const argv = process.argv.slice(2)
|
|
430
|
+
const asJson = argv.includes('--json')
|
|
431
|
+
const rest = argv.filter((a) => a !== '--json')
|
|
432
|
+
if (!rest.length) {
|
|
433
|
+
console.error('[usage] node route_input.mjs <输入路径(MeaXure 导出目录 / UI 要素包 / 卡片目录 / 设计稿 PNG)> [--json]')
|
|
434
|
+
process.exit(1)
|
|
435
|
+
}
|
|
436
|
+
const result = routeInput(rest[0])
|
|
437
|
+
if (result.error) {
|
|
438
|
+
console.error(`[error] ${result.message}`)
|
|
439
|
+
process.exit(1)
|
|
440
|
+
}
|
|
441
|
+
if (asJson) console.log(JSON.stringify(result, null, 2))
|
|
442
|
+
else printHuman(result, result.inputAbs)
|
|
443
|
+
process.exit(result.kind === 'unknown' ? 3 : 0)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// 只有被直接执行时才跑 CLI;被 MCP client 动态 import 时只取 routeInput,不触发 process.exit
|
|
447
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scaffold_card.mjs — 秒出合法卡片骨架,避免手写组件样板。
|
|
4
|
+
*
|
|
5
|
+
* 用法:
|
|
6
|
+
* node scaffold_card.mjs --intent list --id myCard --name "我的卡"
|
|
7
|
+
* node scaffold_card.mjs --intent form --id f1 --name "表单" --out /tmp/cards --force
|
|
8
|
+
* node scaffold_card.mjs --from creditCardBill --id myNewCard --name "新卡" # 整卡模板克隆
|
|
9
|
+
*
|
|
10
|
+
* 意图:blank | list | detail | form | dialog | tab | canvas-head
|
|
11
|
+
* 输出:<outBase>/<id>/{schema.json, data.json}(data.json 由 inputParams 反推,自洽)
|
|
12
|
+
*
|
|
13
|
+
* 设计:形状来自 lib/scaffold.mjs(与 gen-all-components-test 同源),保证合法。
|
|
14
|
+
* --from 模式:克隆存量整卡模板(references/card_templates.json 提供簇→代表卡路由),
|
|
15
|
+
* 只重写 pageId/name,版式纪律与范式原样继承;封面不复制(需定稿后重新渲染)。
|
|
16
|
+
*/
|
|
17
|
+
import path from 'node:path'
|
|
18
|
+
import fs from 'node:fs'
|
|
19
|
+
import { writeSkeleton, cloneCardFrom, SCAFFOLD_INTENTS } from './lib/scaffold.mjs'
|
|
20
|
+
|
|
21
|
+
function parseArgs(argv) {
|
|
22
|
+
const out = {}
|
|
23
|
+
for (let i = 0; i < argv.length; i++) {
|
|
24
|
+
const a = argv[i]
|
|
25
|
+
if (!a.startsWith('--')) continue
|
|
26
|
+
const key = a.slice(2)
|
|
27
|
+
const next = argv[i + 1]
|
|
28
|
+
if (next && !next.startsWith('--')) { out[key] = next; i++ } else out[key] = true
|
|
29
|
+
}
|
|
30
|
+
return out
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const args = parseArgs(process.argv.slice(2))
|
|
34
|
+
const intent = args.intent || args.i
|
|
35
|
+
const from = args.from
|
|
36
|
+
const id = args.id
|
|
37
|
+
const name = args.name || args.n
|
|
38
|
+
const outBase = args.out ? path.resolve(args.out) : undefined
|
|
39
|
+
const force = !!args.force
|
|
40
|
+
|
|
41
|
+
if (!from && (!intent || !SCAFFOLD_INTENTS.includes(intent))) {
|
|
42
|
+
console.error(`[usage] --intent <${SCAFFOLD_INTENTS.join('|')}> --id <cardId> [--name <名称>] [--out <dir>] [--force]`)
|
|
43
|
+
console.error(` 或 --from <模板卡id> --id <cardId> [--name <名称>] [--force](整卡模板克隆,模板见 references/card_templates.json)`)
|
|
44
|
+
process.exit(1)
|
|
45
|
+
}
|
|
46
|
+
if (!id) {
|
|
47
|
+
console.error('[error] 缺少 --id <cardId>')
|
|
48
|
+
process.exit(1)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (force && outBase) {
|
|
52
|
+
const dir = path.join(outBase, id)
|
|
53
|
+
if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
if (from) {
|
|
58
|
+
const dir = cloneCardFrom(from, id, { name, outBase })
|
|
59
|
+
console.log(`[ok] 已克隆模板卡:${from} → ${dir}`)
|
|
60
|
+
console.log(` 注意:封面未复制(封面必须代表卡片自身外观),结构定稿后重新渲染生成`)
|
|
61
|
+
} else {
|
|
62
|
+
const dir = writeSkeleton(id, intent, { name, outBase, force })
|
|
63
|
+
console.log(`[ok] 已生成骨架:${dir}`)
|
|
64
|
+
console.log(` 意图=${intent} 组件树=/Root>${describe(intent)}`)
|
|
65
|
+
}
|
|
66
|
+
} catch (e) {
|
|
67
|
+
console.error('[error] ' + e.message)
|
|
68
|
+
process.exit(1)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function describe(intent) {
|
|
72
|
+
return {
|
|
73
|
+
blank: '(空容器)',
|
|
74
|
+
list: 'Column > Text($title) + ListLayout($list)',
|
|
75
|
+
detail: 'Column > Text($title) + ListLayout($detailList) + Button',
|
|
76
|
+
form: 'Column > Text($title) + Input×2 + Button',
|
|
77
|
+
dialog: 'Column > Text($title) + Text($desc) + Button',
|
|
78
|
+
tab: 'Column > Row(tabs) + Column(panel)',
|
|
79
|
+
'canvas-head': 'Column > Row(Canvas + Text)',
|
|
80
|
+
}[intent] || ''
|
|
81
|
+
}
|