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,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design_image.mjs — 设计稿 card-N.png 归档到卡片目录。
|
|
3
|
+
*
|
|
4
|
+
* 约定:生成卡片时,把对应的 card-*.png 复制到卡片目录并重命名为
|
|
5
|
+
* <cardId>.png,使 schema.json / data.json / 设计稿参考图同目录存放。
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'node:fs'
|
|
8
|
+
import path from 'node:path'
|
|
9
|
+
|
|
10
|
+
const CARD_IMAGE_RE = /^card-\d+\.png$/i
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 判断文件是否为 card-N.png 设计稿聚焦图。
|
|
14
|
+
*
|
|
15
|
+
* @param {string} p 文件路径
|
|
16
|
+
* @returns {boolean}
|
|
17
|
+
*/
|
|
18
|
+
export function isCardDesignImage(p) {
|
|
19
|
+
return CARD_IMAGE_RE.test(path.basename(p || ''))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 把 card-*.png 设计稿复制到卡片目录并重命名为 <cardId>.png。
|
|
24
|
+
*
|
|
25
|
+
* 仅处理 card-N.png 命名的设计稿聚焦图;artboard.png 等其它图片不动。
|
|
26
|
+
* 源文件与目标为同一路径时跳过;复制失败不阻断主流程(返回 null)。
|
|
27
|
+
*
|
|
28
|
+
* @param {{ designPath?: string, outDir: string, cardId: string }} args
|
|
29
|
+
* @returns {string|null} 目标文件路径(未复制时返回 null)
|
|
30
|
+
*/
|
|
31
|
+
export function copyCardDesignImage({ designPath, outDir, cardId }) {
|
|
32
|
+
if (!designPath || !isCardDesignImage(designPath) || !outDir || !cardId) return null
|
|
33
|
+
const source = path.resolve(designPath)
|
|
34
|
+
if (!fs.existsSync(source) || !fs.statSync(source).isFile()) return null
|
|
35
|
+
const target = path.join(path.resolve(outDir), `${cardId}.png`)
|
|
36
|
+
if (source === target) return null
|
|
37
|
+
try {
|
|
38
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
39
|
+
fs.copyFileSync(source, target)
|
|
40
|
+
return target
|
|
41
|
+
} catch {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 定位画板目录中第 N 张卡的 card-N.png(N 从 1 开始)。
|
|
48
|
+
*
|
|
49
|
+
* @param {string} canvasDir 画板目录(如 UI/10贷款查询)
|
|
50
|
+
* @param {number} cardNumber 卡片序号(1-based)
|
|
51
|
+
* @returns {string|null} 存在则返回绝对路径,否则 null
|
|
52
|
+
*/
|
|
53
|
+
export function findCardDesignImage(canvasDir, cardNumber = 1) {
|
|
54
|
+
const p = path.join(canvasDir, `card-${cardNumber}.png`)
|
|
55
|
+
return fs.existsSync(p) ? p : null
|
|
56
|
+
}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design_sample.mjs — 从设计稿聚焦图(card-N.png)采样顶部色带。
|
|
3
|
+
*
|
|
4
|
+
* 用途:弥散背景/浅色顶带没有切片素材时,按「缺素材用纯色」约束,
|
|
5
|
+
* 从设计稿截图采样带高与纯色近似值(不裁切图片,只取颜色)。
|
|
6
|
+
*
|
|
7
|
+
* 算法(纯几何 + 统计,无任何业务/样本硬编码):
|
|
8
|
+
* 1. 取中央竖条(30%-70% 宽)避开圆角与侧边装饰;
|
|
9
|
+
* 2. 每行取中位色(对文字等前景像素稳健);
|
|
10
|
+
* 3. 卡体基色 = 底部 8% 行中位色的中位数;
|
|
11
|
+
* 4. 从顶部扫描:行色与基色平均通道差 > 6 视为色带行,
|
|
12
|
+
* 允许开头 ≤8 CSS px 的基色行(圆角行),允许 1 行例外(抗锯齿);
|
|
13
|
+
* 5. 带高 ≥10 且 ≤60% 卡高才采纳。
|
|
14
|
+
*/
|
|
15
|
+
import fs from 'node:fs'
|
|
16
|
+
import { PNG } from 'pngjs'
|
|
17
|
+
|
|
18
|
+
function median(nums) {
|
|
19
|
+
if (!nums.length) return 0
|
|
20
|
+
const sorted = [...nums].sort((a, b) => a - b)
|
|
21
|
+
return sorted[Math.floor(sorted.length / 2)]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function toHex(r, g, b) {
|
|
25
|
+
const c = (v) => Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, '0').toUpperCase()
|
|
26
|
+
return `#${c(r)}${c(g)}${c(b)}`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} designPath 设计稿聚焦图路径
|
|
31
|
+
* @param {number} cardWidth 卡片宽(CSS px)
|
|
32
|
+
* @param {number} cardHeight 卡片高(CSS px)
|
|
33
|
+
* @returns {{ height: number, color: string } | null}
|
|
34
|
+
*/
|
|
35
|
+
export function sampleTopBand(designPath, cardWidth, cardHeight) {
|
|
36
|
+
try {
|
|
37
|
+
if (!designPath || !fs.existsSync(designPath)) return null
|
|
38
|
+
const png = PNG.sync.read(fs.readFileSync(designPath))
|
|
39
|
+
const scale = png.width / Math.max(1, cardWidth)
|
|
40
|
+
if (!(scale > 0.5 && scale < 5)) return null
|
|
41
|
+
const { width: w, height: h, data } = png
|
|
42
|
+
const x0 = Math.floor(w * 0.3)
|
|
43
|
+
const x1 = Math.max(x0 + 2, Math.ceil(w * 0.7))
|
|
44
|
+
|
|
45
|
+
const rowMedian = (y) => {
|
|
46
|
+
const rs = [], gs = [], bs = []
|
|
47
|
+
for (let x = x0; x < x1; x += 2) {
|
|
48
|
+
const i = (y * w + x) * 4
|
|
49
|
+
rs.push(data[i]); gs.push(data[i + 1]); bs.push(data[i + 2])
|
|
50
|
+
}
|
|
51
|
+
return [median(rs), median(gs), median(bs)]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 卡体基色:底部 8% 的行
|
|
55
|
+
const bodyRows = []
|
|
56
|
+
for (let y = Math.floor(h * 0.92); y < h; y++) bodyRows.push(rowMedian(y))
|
|
57
|
+
const body = [0, 1, 2].map((i) => median(bodyRows.map((c) => c[i])))
|
|
58
|
+
const dist = (a, b) => (Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]) + Math.abs(a[2] - b[2])) / 3
|
|
59
|
+
|
|
60
|
+
const maxLeadRows = Math.round(8 * scale) // 圆角保护
|
|
61
|
+
const bandColors = []
|
|
62
|
+
let bandEndRow = -1
|
|
63
|
+
let started = false
|
|
64
|
+
let exceptions = 0
|
|
65
|
+
for (let y = 0; y < h; y++) {
|
|
66
|
+
const c = rowMedian(y)
|
|
67
|
+
if (dist(c, body) > 6) {
|
|
68
|
+
started = true
|
|
69
|
+
bandColors.push(c)
|
|
70
|
+
bandEndRow = y
|
|
71
|
+
exceptions = 0
|
|
72
|
+
} else {
|
|
73
|
+
if (!started) {
|
|
74
|
+
if (y > maxLeadRows) return null // 顶部 8px 内没有出现色带 → 无顶带
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
exceptions += 1
|
|
78
|
+
if (exceptions > 1) break // 容忍 1 行例外(分隔线/抗锯齿)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!started || bandEndRow < 0) return null
|
|
82
|
+
const heightCss = (bandEndRow + 1) / scale
|
|
83
|
+
// 上限 80%:整卡同色时底行=带色根本检不出带,能检出的高带(如顶面板+异色底栏)是合法形态
|
|
84
|
+
if (heightCss < 10 || heightCss > cardHeight * 0.8) return null
|
|
85
|
+
const color = toHex(
|
|
86
|
+
median(bandColors.map((c) => c[0])),
|
|
87
|
+
median(bandColors.map((c) => c[1])),
|
|
88
|
+
median(bandColors.map((c) => c[2])),
|
|
89
|
+
)
|
|
90
|
+
return { height: Math.round(heightCss), color }
|
|
91
|
+
} catch {
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 采样设计稿聚焦图中某个卡内区域的主色(区域中央 50% 的中位色,对内部文字稳健)。
|
|
98
|
+
* 用途:无导出素材的大切片(如烘焙按钮图)用纯色块近似(constraint:缺素材用纯色)。
|
|
99
|
+
* @param {string} designPath
|
|
100
|
+
* @param {number} cardWidth CSS px
|
|
101
|
+
* @param {{x:number,y:number,width:number,height:number}} region 卡内相对坐标
|
|
102
|
+
* @returns {string|null} hex 颜色
|
|
103
|
+
*/
|
|
104
|
+
export function sampleRegionColor(designPath, cardWidth, region) {
|
|
105
|
+
try {
|
|
106
|
+
if (!designPath || !fs.existsSync(designPath)) return null
|
|
107
|
+
const png = PNG.sync.read(fs.readFileSync(designPath))
|
|
108
|
+
const scale = png.width / Math.max(1, cardWidth)
|
|
109
|
+
if (!(scale > 0.5 && scale < 5)) return null
|
|
110
|
+
const { width: w, height: h, data } = png
|
|
111
|
+
const x0 = Math.max(0, Math.round((region.x + region.width * 0.25) * scale))
|
|
112
|
+
const x1 = Math.min(w, Math.round((region.x + region.width * 0.75) * scale))
|
|
113
|
+
const y0 = Math.max(0, Math.round((region.y + region.height * 0.25) * scale))
|
|
114
|
+
const y1 = Math.min(h, Math.round((region.y + region.height * 0.75) * scale))
|
|
115
|
+
if (x1 - x0 < 2 || y1 - y0 < 2) return null
|
|
116
|
+
const rs = [], gs = [], bs = []
|
|
117
|
+
for (let y = y0; y < y1; y += 2) {
|
|
118
|
+
for (let x = x0; x < x1; x += 2) {
|
|
119
|
+
const i = (y * w + x) * 4
|
|
120
|
+
rs.push(data[i]); gs.push(data[i + 1]); bs.push(data[i + 2])
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return toHex(median(rs), median(gs), median(bs))
|
|
124
|
+
} catch {
|
|
125
|
+
return null
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 采样区域边缘条带主色(左右两条竖带的中位色,避开中央文字)。
|
|
131
|
+
* 用途:按钮/pill 的标注填充色与设计稿像素交叉验证——中央采样会被按钮文字污染,
|
|
132
|
+
* 边缘条带(圆角半径之外的纯背景区)才是填充色真值。
|
|
133
|
+
* @param {string} designPath
|
|
134
|
+
* @param {number} cardWidth CSS px
|
|
135
|
+
* @param {{x:number,y:number,width:number,height:number}} region 卡内相对坐标
|
|
136
|
+
* @returns {string|null} hex 颜色
|
|
137
|
+
*/
|
|
138
|
+
export function sampleRegionEdgeColor(designPath, cardWidth, region) {
|
|
139
|
+
try {
|
|
140
|
+
if (!designPath || !fs.existsSync(designPath)) return null
|
|
141
|
+
const png = PNG.sync.read(fs.readFileSync(designPath))
|
|
142
|
+
const scale = png.width / Math.max(1, cardWidth)
|
|
143
|
+
if (!(scale > 0.5 && scale < 5)) return null
|
|
144
|
+
const { width: w, height: h, data } = png
|
|
145
|
+
// 条带内缩:横向 8%-22% / 78%-92%(避开圆角与中央文字),纵向 30%-70%
|
|
146
|
+
const y0 = Math.max(0, Math.round((region.y + region.height * 0.3) * scale))
|
|
147
|
+
const y1 = Math.min(h, Math.round((region.y + region.height * 0.7) * scale))
|
|
148
|
+
const strips = [
|
|
149
|
+
[Math.round((region.x + region.width * 0.08) * scale), Math.round((region.x + region.width * 0.22) * scale)],
|
|
150
|
+
[Math.round((region.x + region.width * 0.78) * scale), Math.round((region.x + region.width * 0.92) * scale)],
|
|
151
|
+
]
|
|
152
|
+
const rs = [], gs = [], bs = []
|
|
153
|
+
for (const [sx0, sx1] of strips) {
|
|
154
|
+
const x0 = Math.max(0, sx0), x1 = Math.min(w, sx1)
|
|
155
|
+
if (x1 - x0 < 2 || y1 - y0 < 2) continue
|
|
156
|
+
for (let y = y0; y < y1; y += 2) {
|
|
157
|
+
for (let x = x0; x < x1; x += 2) {
|
|
158
|
+
const i = (y * w + x) * 4
|
|
159
|
+
rs.push(data[i]); gs.push(data[i + 1]); bs.push(data[i + 2])
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (!rs.length) return null
|
|
164
|
+
return toHex(median(rs), median(gs), median(bs))
|
|
165
|
+
} catch {
|
|
166
|
+
return null
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 采样卡体主色(设计稿聚焦图中央竖条底部 8% 行的中位色)。
|
|
172
|
+
* 用于校验/纠正 spec cardFill 与设计稿明显不一致的情况(视觉优先)。
|
|
173
|
+
* @returns {string|null} hex
|
|
174
|
+
*/
|
|
175
|
+
export function sampleCardBodyColor(designPath, cardWidth) {
|
|
176
|
+
try {
|
|
177
|
+
if (!designPath || !fs.existsSync(designPath)) return null
|
|
178
|
+
const png = PNG.sync.read(fs.readFileSync(designPath))
|
|
179
|
+
const scale = png.width / Math.max(1, cardWidth)
|
|
180
|
+
if (!(scale > 0.5 && scale < 5)) return null
|
|
181
|
+
const { width: w, height: h, data } = png
|
|
182
|
+
const x0 = Math.floor(w * 0.3)
|
|
183
|
+
const x1 = Math.max(x0 + 2, Math.ceil(w * 0.7))
|
|
184
|
+
const rs = [], gs = [], bs = []
|
|
185
|
+
for (let y = Math.floor(h * 0.94); y < h; y++) {
|
|
186
|
+
for (let x = x0; x < x1; x += 2) {
|
|
187
|
+
const i = (y * w + x) * 4
|
|
188
|
+
rs.push(data[i]); gs.push(data[i + 1]); bs.push(data[i + 2])
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return toHex(median(rs), median(gs), median(bs))
|
|
192
|
+
} catch {
|
|
193
|
+
return null
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 文本颜色交叉校验(标注缺陷治理,视觉优先):
|
|
199
|
+
* 标注 textStyle.color 与设计稿实际字形颜色明显不一致时(如设计是蓝灰字、标注写深色),
|
|
200
|
+
* 从设计稿聚焦图采样字形真实颜色覆盖标注。
|
|
201
|
+
*
|
|
202
|
+
* 算法(纯几何 + 统计,无业务词):
|
|
203
|
+
* 1. 文本所在小壳(按钮/pill)bbox 内缩 15% 取中央区(避开圆角/壳边抗锯齿);
|
|
204
|
+
* 2. 全区中位色 ≈ 壳底色(文字稀疏);
|
|
205
|
+
* 3. 与底色距离 >40 的像素视为字形;取其中亮度最低 25%(抗锯齿核心)的中位色;
|
|
206
|
+
* 4. 采样色与标注色平均通道差 >28 才覆盖(小差不折腾,保留标注)。
|
|
207
|
+
*
|
|
208
|
+
* @param {string} designPath 设计稿聚焦图路径(card-N.png)
|
|
209
|
+
* @param {number} cardWidth 卡片宽(CSS px)
|
|
210
|
+
* @param {{x:number,y:number,width:number,height:number}} bboxCardRel 壳 bbox(卡内相对坐标)
|
|
211
|
+
* @param {string} specColorHex 标注颜色(#RRGGBB)
|
|
212
|
+
* @returns {string|null} 需覆盖时返回采样色 #RRGGBB,否则 null
|
|
213
|
+
*/
|
|
214
|
+
export function crossCheckTextColor(designPath, cardWidth, bboxCardRel, specColorHex) {
|
|
215
|
+
try {
|
|
216
|
+
if (!designPath || !fs.existsSync(designPath)) return null
|
|
217
|
+
const png = PNG.sync.read(fs.readFileSync(designPath))
|
|
218
|
+
const scale = png.width / Math.max(1, cardWidth)
|
|
219
|
+
if (!(scale > 0.5 && scale < 5)) return null
|
|
220
|
+
const { width: w, height: h, data } = png
|
|
221
|
+
const bx = (bboxCardRel.x || 0) * scale
|
|
222
|
+
const by = (bboxCardRel.y || 0) * scale
|
|
223
|
+
const bw = (bboxCardRel.width || 0) * scale
|
|
224
|
+
const bh = (bboxCardRel.height || 0) * scale
|
|
225
|
+
if (bw < 8 || bh < 6) return null
|
|
226
|
+
const x0 = Math.max(0, Math.round(bx + bw * 0.15))
|
|
227
|
+
const x1 = Math.min(w - 1, Math.round(bx + bw * 0.85))
|
|
228
|
+
const y0 = Math.max(0, Math.round(by + bh * 0.15))
|
|
229
|
+
const y1 = Math.min(h - 1, Math.round(by + bh * 0.85))
|
|
230
|
+
if (x1 - x0 < 4 || y1 - y0 < 4) return null
|
|
231
|
+
const rs = [], gs = [], bs = []
|
|
232
|
+
for (let y = y0; y <= y1; y++) {
|
|
233
|
+
for (let x = x0; x <= x1; x++) {
|
|
234
|
+
const i = (y * w + x) * 4
|
|
235
|
+
rs.push(data[i]); gs.push(data[i + 1]); bs.push(data[i + 2])
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const bg = [median(rs), median(gs), median(bs)]
|
|
239
|
+
const dist = (r, g, b, t) => (Math.abs(r - t[0]) + Math.abs(g - t[1]) + Math.abs(b - t[2])) / 3
|
|
240
|
+
const glyphs = []
|
|
241
|
+
for (let k = 0; k < rs.length; k++) {
|
|
242
|
+
if (dist(rs[k], gs[k], bs[k], bg) > 40) {
|
|
243
|
+
glyphs.push([rs[k], gs[k], bs[k], rs[k] * 0.299 + gs[k] * 0.587 + bs[k] * 0.114])
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (glyphs.length < 30) return null
|
|
247
|
+
glyphs.sort((a, b) => a[3] - b[3])
|
|
248
|
+
const core = glyphs.slice(0, Math.max(10, Math.floor(glyphs.length * 0.25)))
|
|
249
|
+
const sampled = [
|
|
250
|
+
median(core.map((c) => c[0])),
|
|
251
|
+
median(core.map((c) => c[1])),
|
|
252
|
+
median(core.map((c) => c[2])),
|
|
253
|
+
]
|
|
254
|
+
const m = String(specColorHex || '').replace('#', '')
|
|
255
|
+
if (!/^[0-9a-fA-F]{6}$/.test(m)) return toHex(...sampled)
|
|
256
|
+
const spec = [parseInt(m.slice(0, 2), 16), parseInt(m.slice(2, 4), 16), parseInt(m.slice(4, 6), 16)]
|
|
257
|
+
if (dist(sampled[0], sampled[1], sampled[2], spec) > 28) return toHex(...sampled)
|
|
258
|
+
return null
|
|
259
|
+
} catch {
|
|
260
|
+
return null
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* 描边环采样(标注缺陷治理,视觉优先):
|
|
266
|
+
* 壳标注无 fill 无 border("隐形"壳),但设计稿可见描边时(如线框 pill 按钮),
|
|
267
|
+
* 从设计稿采样壳边缘环带颜色,判定是否存在描边并取其颜色。
|
|
268
|
+
*
|
|
269
|
+
* 算法(纯几何 + 统计):
|
|
270
|
+
* 1. 壳 bbox 四边各取中段 30%-70%(避开圆角);
|
|
271
|
+
* 2. 环带 = bbox 边缘向内 max(1, round(1.6×scale)) px 的窄带;
|
|
272
|
+
* 3. 内部参照色 = 中央 30% 区域中位色;
|
|
273
|
+
* 4. 环带亮度最低 40% 像素的中位色与内部参照色距离 >18 → 存在描边,返回其颜色。
|
|
274
|
+
*
|
|
275
|
+
* @param {string} designPath 设计稿聚焦图路径
|
|
276
|
+
* @param {number} cardWidth 卡片宽(CSS px)
|
|
277
|
+
* @param {{x:number,y:number,width:number,height:number}} bboxCardRel 壳 bbox(卡内相对坐标)
|
|
278
|
+
* @returns {string|null} 描边色 #RRGGBB;无描边/采样失败返回 null
|
|
279
|
+
*/
|
|
280
|
+
export function sampleOutlineRing(designPath, cardWidth, bboxCardRel) {
|
|
281
|
+
try {
|
|
282
|
+
if (!designPath || !fs.existsSync(designPath)) return null
|
|
283
|
+
const png = PNG.sync.read(fs.readFileSync(designPath))
|
|
284
|
+
const scale = png.width / Math.max(1, cardWidth)
|
|
285
|
+
if (!(scale > 0.5 && scale < 5)) return null
|
|
286
|
+
const { width: w, height: h, data } = png
|
|
287
|
+
const bx = Math.round((bboxCardRel.x || 0) * scale)
|
|
288
|
+
const by = Math.round((bboxCardRel.y || 0) * scale)
|
|
289
|
+
const bw = Math.round((bboxCardRel.width || 0) * scale)
|
|
290
|
+
const bh = Math.round((bboxCardRel.height || 0) * scale)
|
|
291
|
+
if (bw < 10 || bh < 8) return null
|
|
292
|
+
const t = Math.max(1, Math.round(1.6 * scale))
|
|
293
|
+
const at = (x, y) => {
|
|
294
|
+
const i = (Math.min(h - 1, Math.max(0, y)) * w + Math.min(w - 1, Math.max(0, x))) * 4
|
|
295
|
+
return [data[i], data[i + 1], data[i + 2]]
|
|
296
|
+
}
|
|
297
|
+
const ring = []
|
|
298
|
+
const push = (x, y) => { const c = at(x, y); ring.push([c[0], c[1], c[2], c[0] * 0.299 + c[1] * 0.587 + c[2] * 0.114]) }
|
|
299
|
+
for (let x = bx + Math.round(bw * 0.3); x <= bx + Math.round(bw * 0.7); x++) {
|
|
300
|
+
for (let k = 0; k < t; k++) { push(x, by + k); push(x, by + bh - 1 - k) }
|
|
301
|
+
}
|
|
302
|
+
for (let y = by + Math.round(bh * 0.3); y <= by + Math.round(bh * 0.7); y++) {
|
|
303
|
+
for (let k = 0; k < t; k++) { push(bx + k, y); push(bx + bw - 1 - k, y) }
|
|
304
|
+
}
|
|
305
|
+
if (ring.length < 20) return null
|
|
306
|
+
// 内部参照色(中央 30% 区域中位色)
|
|
307
|
+
const ir = [], ig = [], ib = []
|
|
308
|
+
for (let y = by + Math.round(bh * 0.35); y <= by + Math.round(bh * 0.65); y += 2) {
|
|
309
|
+
for (let x = bx + Math.round(bw * 0.35); x <= bx + Math.round(bw * 0.65); x += 2) {
|
|
310
|
+
const c = at(x, y); ir.push(c[0]); ig.push(c[1]); ib.push(c[2])
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const interior = [median(ir), median(ig), median(ib)]
|
|
314
|
+
ring.sort((a, b) => a[3] - b[3])
|
|
315
|
+
const core = ring.slice(0, Math.max(8, Math.floor(ring.length * 0.4)))
|
|
316
|
+
const ringColor = [
|
|
317
|
+
median(core.map((c) => c[0])),
|
|
318
|
+
median(core.map((c) => c[1])),
|
|
319
|
+
median(core.map((c) => c[2])),
|
|
320
|
+
]
|
|
321
|
+
const dist = (Math.abs(ringColor[0] - interior[0])
|
|
322
|
+
+ Math.abs(ringColor[1] - interior[1])
|
|
323
|
+
+ Math.abs(ringColor[2] - interior[2])) / 3
|
|
324
|
+
return dist > 18 ? toHex(...ringColor) : null
|
|
325
|
+
} catch {
|
|
326
|
+
return null
|
|
327
|
+
}
|
|
328
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 布局 Token 表(消灭魔法值:gap/padding/radius/fontSize/color 一律引用此处)。
|
|
3
|
+
*
|
|
4
|
+
* 纪律(2026-08-17 L1 落地):
|
|
5
|
+
* - v3 生成器(spec_to_schema_v3.mjs)禁止裸数字魔法值,
|
|
6
|
+
* 一律引用 TOKEN.<组>.<语义名>。
|
|
7
|
+
* - L2 几何推导优先:spec.rect 实测值 > TOKEN(兜底)> 历史魔法值(最后兜底)。
|
|
8
|
+
* - 语义命名按"用途"而非"数值":GAP.base 表示组件内标准间距,不叫 GAP.eight。
|
|
9
|
+
* - 改值单点生效,三策略视觉统一。
|
|
10
|
+
*/
|
|
11
|
+
export const TOKEN = {
|
|
12
|
+
/** 间距(flex gap) */
|
|
13
|
+
GAP: {
|
|
14
|
+
tight: 4, // 行内紧密元素(图标+文字、两段小字)
|
|
15
|
+
micro: 2, // 最密(legacy 文字列)
|
|
16
|
+
none: 0, // 无间距
|
|
17
|
+
base: 8, // 组件内标准间距(默认 Row/Column)
|
|
18
|
+
loose: 12, // 大区块内子区块间距
|
|
19
|
+
section: 16, // 段落/分组间距
|
|
20
|
+
huge: 24, // 大区块间距
|
|
21
|
+
},
|
|
22
|
+
/** 内边距(padding) */
|
|
23
|
+
PADDING: {
|
|
24
|
+
card: 16, // 卡片整体内边距
|
|
25
|
+
section: 12, // 区块内边距
|
|
26
|
+
item: 12, // 列表项内边距
|
|
27
|
+
compact: 8, // 紧凑内边距(v3 标签行/操作行)
|
|
28
|
+
mini: 4, // 最小内边距
|
|
29
|
+
micro: 2, // 极小内边距(列表行等紧凑场景兜底)
|
|
30
|
+
buttonH: 12, // 按钮垂直内边距
|
|
31
|
+
buttonW: 24, // 按钮水平内边距
|
|
32
|
+
root: 0, // Root 内边距(默认 0,宽度由 view.width 承载)
|
|
33
|
+
},
|
|
34
|
+
/** 圆角(borderRadius) */
|
|
35
|
+
RADIUS: {
|
|
36
|
+
root: 8, // Root 圆角
|
|
37
|
+
card: 12, // 卡片圆角
|
|
38
|
+
item: 8, // 列表项/小容器圆角
|
|
39
|
+
colorBlock: 6, // 背景色块圆角
|
|
40
|
+
button: 24, // 按钮圆角(胶囊风格)
|
|
41
|
+
pill: 100, // 全圆角胶囊
|
|
42
|
+
},
|
|
43
|
+
/** 字号(fontSize) */
|
|
44
|
+
FONT: {
|
|
45
|
+
body: 14, // 正文
|
|
46
|
+
sub: 13, // 辅助说明
|
|
47
|
+
small: 12, // 时间/列表小字
|
|
48
|
+
title: 15, // 区块标题
|
|
49
|
+
button: 17, // 按钮文字
|
|
50
|
+
buttonLegacy: 16, // legacy 生成器按钮兜底字号
|
|
51
|
+
large: 30, // 数字大标题
|
|
52
|
+
},
|
|
53
|
+
/** 颜色 */
|
|
54
|
+
COLOR: {
|
|
55
|
+
pageBg: '#F5F6F8', // 页面/画布背景
|
|
56
|
+
cardBg: '#FFFFFF', // 卡片背景
|
|
57
|
+
primary: '#00B578', // 品牌主色(绿,geo/v3)
|
|
58
|
+
primaryBlue: '#0079F2', // legacy 生成器历史主色(蓝)
|
|
59
|
+
fallbackBg: '#F5F5F5', // pickBg 兜底色
|
|
60
|
+
danger: '#F6434F', // 危险/未读红点
|
|
61
|
+
textMain: '#111111', // 主文字
|
|
62
|
+
textDefault: '#333333', // 文本兜底色(spec 未给 color 时)
|
|
63
|
+
blockFill: '#EDEDED', // 色块兜底色(spec 未给 fill 时)
|
|
64
|
+
textSub: '#555555', // 次级文字
|
|
65
|
+
divider: '#EEEEEE', // 分隔线
|
|
66
|
+
link: '#1584FF', // 链接/操作文字(蓝)
|
|
67
|
+
btnText: '#FFFFFF', // 按钮文字(主色底)
|
|
68
|
+
arrow: '#CCCCCC', // 次级箭头
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 便捷:直接拿到字符串化的 Token 值(生成器 setStyleChild 入参用) */
|
|
73
|
+
export const T = {
|
|
74
|
+
gap: (k) => String(TOKEN.GAP[k]),
|
|
75
|
+
pad: (k) => String(TOKEN.PADDING[k]),
|
|
76
|
+
radius: (k) => String(TOKEN.RADIUS[k]),
|
|
77
|
+
font: (k) => String(TOKEN.FONT[k]),
|
|
78
|
+
color: (k) => TOKEN.COLOR[k],
|
|
79
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paths.mjs — skill 内部路径与宿主项目路径单一来源(Node 侧)。
|
|
3
|
+
*
|
|
4
|
+
* 自包含约定:
|
|
5
|
+
* - skill 自身的渲染器/元数据资源固定放在 <skill>/vendor/preview;
|
|
6
|
+
* - 宿主项目(卡片与切图所在目录)默认是调用脚本时的 cwd,也可用环境变量覆盖;
|
|
7
|
+
* - 不再向上查找 package.json,因此本 skill 可整体复制到任意项目使用。
|
|
8
|
+
*
|
|
9
|
+
* 环境变量(按优先级覆盖默认值):
|
|
10
|
+
* DSL_CARD_PROJECT_ROOT 宿主项目根目录(默认 process.cwd())
|
|
11
|
+
* DSL_CARD_CARDS_DIR 卡片目录(默认 <project>/src/cards)
|
|
12
|
+
* DSL_CARD_SLICES_DIR 切图目录(默认 <project>/src/slices)
|
|
13
|
+
* DSL_CARD_META_DIR meta 元数据目录(默认 <skill>/vendor/preview/meta)
|
|
14
|
+
* DSL_CARD_PREVIEW_DIR 自包含预览目录(默认 <skill>/vendor/preview)
|
|
15
|
+
* DSL_CARD_BASELINES_DIR 渲染基线目录(默认 <project>/.card-baselines)
|
|
16
|
+
*/
|
|
17
|
+
import fs from 'node:fs'
|
|
18
|
+
import path from 'node:path'
|
|
19
|
+
import { fileURLToPath } from 'node:url'
|
|
20
|
+
|
|
21
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url))
|
|
22
|
+
|
|
23
|
+
/** 当前 skill 根目录:scripts/lib -> scripts -> dsl-card-generator */
|
|
24
|
+
export const skillRoot = path.resolve(HERE, '..', '..')
|
|
25
|
+
|
|
26
|
+
/** skill 自包含资源根目录。 */
|
|
27
|
+
export const vendorDir = path.join(skillRoot, 'vendor')
|
|
28
|
+
|
|
29
|
+
/** 独立卡片预览目录(vendor/preview)。 */
|
|
30
|
+
export const previewDir =
|
|
31
|
+
process.env.DSL_CARD_PREVIEW_DIR || path.join(vendorDir, 'preview')
|
|
32
|
+
|
|
33
|
+
/** meta 元数据目录。 */
|
|
34
|
+
export const metaDir =
|
|
35
|
+
process.env.DSL_CARD_META_DIR || path.join(previewDir, 'meta')
|
|
36
|
+
|
|
37
|
+
/** 宿主项目根目录(默认当前工作目录)。 */
|
|
38
|
+
export const projectRoot = process.env.DSL_CARD_PROJECT_ROOT || process.cwd()
|
|
39
|
+
|
|
40
|
+
/** 卡片源数据目录。 */
|
|
41
|
+
export const cardsDir =
|
|
42
|
+
process.env.DSL_CARD_CARDS_DIR || path.join(projectRoot, 'src', 'cards')
|
|
43
|
+
|
|
44
|
+
/** 切图统一落点目录。 */
|
|
45
|
+
export const slicesDir =
|
|
46
|
+
process.env.DSL_CARD_SLICES_DIR || path.join(projectRoot, 'src', 'slices')
|
|
47
|
+
|
|
48
|
+
/** 渲染回归基线目录。 */
|
|
49
|
+
export const baselinesDir =
|
|
50
|
+
process.env.DSL_CARD_BASELINES_DIR || path.join(projectRoot, '.card-baselines')
|
|
51
|
+
|
|
52
|
+
/** 兼容旧脚本的别名:宿主项目根目录。 */
|
|
53
|
+
export const repoRoot = projectRoot
|
|
54
|
+
|
|
55
|
+
/** skills 父目录(供需要跨 skill 的脚本使用)。 */
|
|
56
|
+
export const skillsDir = path.dirname(skillRoot)
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 解析卡片目录的绝对路径。
|
|
60
|
+
* @param {string} id 卡片 id
|
|
61
|
+
* @returns {string} `<cardsDir>/<id>` 绝对路径
|
|
62
|
+
*/
|
|
63
|
+
export function cardDir(id) {
|
|
64
|
+
return path.join(cardsDir, id)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 解析卡片 schema.json 的绝对路径。
|
|
69
|
+
* @param {string} id 卡片 id
|
|
70
|
+
* @returns {string}
|
|
71
|
+
*/
|
|
72
|
+
export function cardSchemaPath(id) {
|
|
73
|
+
return path.join(cardsDir, id, 'schema.json')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 解析卡片 data.json 的绝对路径。
|
|
78
|
+
* @param {string} id 卡片 id
|
|
79
|
+
* @returns {string}
|
|
80
|
+
*/
|
|
81
|
+
export function cardDataPath(id) {
|
|
82
|
+
return path.join(cardsDir, id, 'data.json')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 安全读取 JSON(不存在或解析失败时返回 fallback)。
|
|
87
|
+
*
|
|
88
|
+
* @param {string} p 路径
|
|
89
|
+
* @param {*} fallback 不存在/解析失败时的返回值(默认抛错)
|
|
90
|
+
* @returns {*} 解析后的 JS 值
|
|
91
|
+
*/
|
|
92
|
+
export function readJson(p, fallback = undefined) {
|
|
93
|
+
if (!fs.existsSync(p)) {
|
|
94
|
+
if (fallback === undefined) throw new Error(`File not found: ${p}`)
|
|
95
|
+
return fallback
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'))
|
|
99
|
+
} catch (e) {
|
|
100
|
+
if (fallback === undefined) throw new Error(`Cannot parse JSON ${p}: ${e.message}`)
|
|
101
|
+
return fallback
|
|
102
|
+
}
|
|
103
|
+
}
|