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.
Files changed (313) hide show
  1. package/README.md +46 -5
  2. package/dist/cli.js +51 -1
  3. package/dist/config.js +51 -16
  4. package/dist/core.js +2 -2
  5. package/dist/init.js +425 -0
  6. package/dist/preview-editor/assets/index-CHuGGIR3.js +276 -0
  7. package/dist/preview-editor/assets/{index-B4VZgNSK.css → index-CQScMhyg.css} +1 -1
  8. package/dist/preview-editor/index.html +2 -2
  9. package/dist/standalone.js +8 -0
  10. package/dist/vendored/mcp/client.mjs +537 -0
  11. package/dist/vendored/mcp/tools.mjs +268 -0
  12. package/dist/vendored/preview/assets/default-image.png +0 -0
  13. package/dist/{server-core → vendored/server/core}/ai-chat.js +82 -38
  14. package/dist/{server-core → vendored/server/core}/browser.js +1 -1
  15. package/dist/{server-core → vendored/server/core}/cardSave.js +8 -3
  16. package/dist/{server-core → vendored/server/core}/cardSync.js +6 -14
  17. package/dist/{server-core → vendored/server/core}/cardValidate.js +11 -8
  18. package/dist/{server-core → vendored/server/core}/index.js +5 -3
  19. package/dist/vendored/server/core/skillPaths.js +124 -0
  20. package/dist/{server-core → vendored/server/core}/sliceUpload.js +5 -8
  21. package/dist/vendored/skills/README.md +93 -0
  22. package/dist/vendored/skills/card-doctor/SKILL.md +52 -0
  23. package/dist/vendored/skills/card-skill-trainer/SKILL.md +312 -0
  24. package/dist/vendored/skills/card-skill-trainer/baselines/v3-r038n.json +43 -0
  25. package/dist/vendored/skills/card-skill-trainer/baselines/v3-r039n.json +47 -0
  26. package/dist/vendored/skills/card-skill-trainer/baselines/v3-r050n.json +48 -0
  27. package/dist/vendored/skills/card-skill-trainer/scripts/build_dataset.py +472 -0
  28. package/dist/vendored/skills/card-skill-trainer/scripts/build_sample.mjs +162 -0
  29. package/dist/vendored/skills/card-skill-trainer/scripts/diagnose_case.mjs +241 -0
  30. package/dist/vendored/skills/card-skill-trainer/scripts/lib/scoring.mjs +525 -0
  31. package/dist/vendored/skills/card-skill-trainer/scripts/round_diff.mjs +133 -0
  32. package/dist/vendored/skills/card-skill-trainer/scripts/run_round.mjs +1043 -0
  33. package/dist/vendored/skills/card-skill-trainer/scripts/score_round.mjs +195 -0
  34. package/dist/vendored/skills/card-skill-trainer/scripts/test_missing_slice.py +122 -0
  35. package/dist/vendored/skills/dsl-card-ai/SKILL.md +191 -0
  36. package/dist/vendored/skills/dsl-card-generator/SKILL.md +480 -0
  37. package/dist/vendored/skills/dsl-card-generator/config/render-contract.json +11 -0
  38. package/dist/vendored/skills/dsl-card-generator/references/amount_formatting.md +138 -0
  39. package/dist/vendored/skills/dsl-card-generator/references/card_index.json +1597 -0
  40. package/dist/vendored/skills/dsl-card-generator/references/card_templates.json +384 -0
  41. package/dist/vendored/skills/dsl-card-generator/references/card_to_summary.md +56 -0
  42. package/dist/vendored/skills/dsl-card-generator/references/component_rules.md +76 -0
  43. package/dist/vendored/skills/dsl-card-generator/references/component_specs.md +393 -0
  44. package/dist/vendored/skills/dsl-card-generator/references/dsl_structure.md +208 -0
  45. package/dist/vendored/skills/dsl-card-generator/references/event_patterns.md +287 -0
  46. package/dist/vendored/skills/dsl-card-generator/references/expression_engine.md +121 -0
  47. package/dist/vendored/skills/dsl-card-generator/references/field_name_dict.json +34 -0
  48. package/dist/vendored/skills/dsl-card-generator/references/field_naming.md +101 -0
  49. package/dist/vendored/skills/dsl-card-generator/references/generation_pitfalls.md +928 -0
  50. package/dist/vendored/skills/dsl-card-generator/references/handcrafted/README.md +69 -0
  51. package/dist/vendored/skills/dsl-card-generator/references/handcrafted/examples_index.json +2967 -0
  52. package/dist/vendored/skills/dsl-card-generator/references/handcrafted/patterns.json +2835 -0
  53. package/dist/vendored/skills/dsl-card-generator/references/handcrafted/recipes_v2.json +648 -0
  54. package/dist/vendored/skills/dsl-card-generator/references/handcrafted/recipes_v2_schema.md +295 -0
  55. package/dist/vendored/skills/dsl-card-generator/references/image_diff.md +48 -0
  56. package/dist/vendored/skills/dsl-card-generator/references/image_to_card.md +129 -0
  57. package/dist/vendored/skills/dsl-card-generator/references/ir-format.md +212 -0
  58. package/dist/vendored/skills/dsl-card-generator/references/message-center-pattern.md +149 -0
  59. package/dist/vendored/skills/dsl-card-generator/references/platform_jsapi.md +196 -0
  60. package/dist/vendored/skills/dsl-card-generator/references/previewer_contract.md +54 -0
  61. package/dist/vendored/skills/dsl-card-generator/references/quickref.md +197 -0
  62. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/absolute-badge.json +84 -0
  63. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/accordion-group-toggle.json +18 -0
  64. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/alertCard-pair.json +12 -0
  65. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/alertDialog-call.json +12 -0
  66. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-countdown.json +15 -0
  67. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-donut.json +15 -0
  68. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-gauge-score.json +11 -0
  69. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-radar.json +10 -0
  70. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-title-head.json +13 -0
  71. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/globalSet.json +12 -0
  72. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/label-col-modes.json +21 -0
  73. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/list-expand-collapse.json +17 -0
  74. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/money-format.json +11 -0
  75. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/opacity-switch.json +130 -0
  76. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/progress-bar.json +25 -0
  77. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/tab-billMode.json +15 -0
  78. package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/title-underline-overlay.json +21 -0
  79. package/dist/vendored/skills/dsl-card-generator/references/recipes.md +797 -0
  80. package/dist/vendored/skills/dsl-card-generator/references/rules.md +235 -0
  81. package/dist/vendored/skills/dsl-card-generator/references/slices_convention.md +177 -0
  82. package/dist/vendored/skills/dsl-card-generator/references/spec_to_schema_patterns.md +609 -0
  83. package/dist/vendored/skills/dsl-card-generator/references/ui_fidelity_rules.md +131 -0
  84. package/dist/vendored/skills/dsl-card-generator/scripts/add_component.mjs +118 -0
  85. package/dist/vendored/skills/dsl-card-generator/scripts/analyze_component_usage.mjs +153 -0
  86. package/dist/vendored/skills/dsl-card-generator/scripts/apply_opacity_pattern.mjs +343 -0
  87. package/dist/vendored/skills/dsl-card-generator/scripts/build_card_index.mjs +108 -0
  88. package/dist/vendored/skills/dsl-card-generator/scripts/build_card_templates.mjs +190 -0
  89. package/dist/vendored/skills/dsl-card-generator/scripts/check_fidelity.mjs +257 -0
  90. package/dist/vendored/skills/dsl-card-generator/scripts/check_magic_values.mjs +79 -0
  91. package/dist/vendored/skills/dsl-card-generator/scripts/crop_slices.mjs +98 -0
  92. package/dist/vendored/skills/dsl-card-generator/scripts/describe_card.mjs +265 -0
  93. package/dist/vendored/skills/dsl-card-generator/scripts/diff_bands.mjs +257 -0
  94. package/dist/vendored/skills/dsl-card-generator/scripts/diff_card.mjs +316 -0
  95. package/dist/vendored/skills/dsl-card-generator/scripts/expand_ir.mjs +873 -0
  96. package/dist/vendored/skills/dsl-card-generator/scripts/extract_handcrafted.py +1199 -0
  97. package/dist/vendored/skills/dsl-card-generator/scripts/extract_subtree.mjs +191 -0
  98. package/dist/vendored/skills/dsl-card-generator/scripts/generate_cards.mjs +389 -0
  99. package/dist/vendored/skills/dsl-card-generator/scripts/inventory_schemas.py +160 -0
  100. package/dist/vendored/skills/dsl-card-generator/scripts/iterate.mjs +700 -0
  101. package/dist/vendored/skills/dsl-card-generator/scripts/lib/design_image.mjs +56 -0
  102. package/dist/vendored/skills/dsl-card-generator/scripts/lib/design_sample.mjs +328 -0
  103. package/dist/vendored/skills/dsl-card-generator/scripts/lib/layout-tokens.mjs +79 -0
  104. package/dist/vendored/skills/dsl-card-generator/scripts/lib/paths.mjs +103 -0
  105. package/dist/vendored/skills/dsl-card-generator/scripts/lib/scaffold.mjs +575 -0
  106. package/dist/vendored/skills/dsl-card-generator/scripts/lib/slicePath.mjs +81 -0
  107. package/dist/vendored/skills/dsl-card-generator/scripts/lib/slice_dedupe.mjs +147 -0
  108. package/dist/vendored/skills/dsl-card-generator/scripts/lint_autofix.mjs +363 -0
  109. package/dist/vendored/skills/dsl-card-generator/scripts/lint_card.mjs +1399 -0
  110. package/dist/vendored/skills/dsl-card-generator/scripts/preflight.mjs +249 -0
  111. package/dist/vendored/skills/dsl-card-generator/scripts/register_card_template.mjs +208 -0
  112. package/dist/vendored/skills/dsl-card-generator/scripts/render_baseline.mjs +231 -0
  113. package/dist/vendored/skills/dsl-card-generator/scripts/render_card.mjs +801 -0
  114. package/dist/vendored/skills/dsl-card-generator/scripts/render_diff.mjs +239 -0
  115. package/dist/vendored/skills/dsl-card-generator/scripts/resolve_card_template.mjs +73 -0
  116. package/dist/vendored/skills/dsl-card-generator/scripts/route_input.mjs +447 -0
  117. package/dist/vendored/skills/dsl-card-generator/scripts/scaffold_card.mjs +81 -0
  118. package/dist/vendored/skills/dsl-card-generator/scripts/slices_normalize.mjs +316 -0
  119. package/dist/vendored/skills/dsl-card-generator/scripts/spec_audit.mjs +462 -0
  120. package/dist/vendored/skills/dsl-card-generator/scripts/spec_to_schema_v3.mjs +7735 -0
  121. package/dist/vendored/skills/dsl-card-generator/scripts/spec_tree.mjs +355 -0
  122. package/dist/vendored/skills/dsl-card-generator/scripts/validate_all_schemas.py +71 -0
  123. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/default-image-wzZYQ86K.png +0 -0
  124. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-C9k-07y7.css +1 -0
  125. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-oFRM4f4V.js +55 -0
  126. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/svg-symbols-B2exBrAr.svg +118 -0
  127. package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/component-meta.json +2714 -0
  128. package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/directive-meta.json +40 -0
  129. package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/style-meta.json +1086 -0
  130. package/dist/vendored/{standalone.html → skills/dsl-card-generator/vendor/preview/standalone.html} +2 -1
  131. package/dist/vendored/skills/finmall-card-sync/SKILL.md +262 -0
  132. package/dist/vendored/skills/finmall-session-navigator/SKILL.md +169 -0
  133. package/dist/vendored/skills/finmall-session-navigator/scripts/finmall_login.mjs +882 -0
  134. package/dist/vendored/skills/finmall-session-navigator/scripts/package-lock.json +59 -0
  135. package/dist/vendored/skills/finmall-session-navigator/scripts/package.json +10 -0
  136. package/dist/vendored/skills/finmall-session-navigator/scripts/session-manager.mjs +96 -0
  137. package/dist/vendored/skills/shared/README.md +55 -0
  138. package/dist/vendored/skills/shared/auth-model.md +79 -0
  139. package/dist/vendored/skills/sketch-spec-to-card/SKILL.md +329 -0
  140. package/dist/vendored/skills/sketch-spec-to-card/references/boundary.json +14 -0
  141. package/dist/vendored/skills/sketch-spec-to-card/references/card_refs.json +13 -0
  142. package/dist/vendored/skills/sketch-spec-to-card/references/spec-schema.md +103 -0
  143. package/dist/vendored/skills/sketch-spec-to-card/scripts/build_prompt.py +797 -0
  144. package/dist/vendored/skills/sketch-spec-to-card/scripts/card_refs.py +94 -0
  145. package/dist/vendored/skills/sketch-spec-to-card/scripts/cluster_states.py +706 -0
  146. package/dist/vendored/skills/sketch-spec-to-card/scripts/parse_meaxure.py +1781 -0
  147. package/dist/vendored/skills/sketch-spec-to-card/scripts/png_utils.py +318 -0
  148. package/dist/vendored/skills/sketch-spec-to-card/scripts/vision.py +1118 -0
  149. package/dist/vendored/templates/helloCard/data.json +10 -0
  150. package/dist/vendored/templates/helloCard/schema.json +238 -0
  151. package/dist/vendored/tools/link-skills.mjs +485 -0
  152. package/package.json +2 -1
  153. package/dist/preview-editor/assets/index-jl4YJzKv.js +0 -180
  154. package/dist/vendored/App.vue +0 -2858
  155. package/dist/vendored/ansi.js +0 -185
  156. package/dist/vendored/assets/apple-touch-icon.png +0 -0
  157. package/dist/vendored/assets/default-image.png +0 -0
  158. package/dist/vendored/assets/favicon-16.png +0 -0
  159. package/dist/vendored/assets/favicon-32.png +0 -0
  160. package/dist/vendored/assets/site-icon.png +0 -0
  161. package/dist/vendored/assets/svgIcons/icons.svg +0 -87
  162. package/dist/vendored/assets/svgIcons/index.js +0 -39
  163. package/dist/vendored/components/AiSettings.vue +0 -392
  164. package/dist/vendored/components/AlertCardInstance.vue +0 -58
  165. package/dist/vendored/components/AlertCardLayer.vue +0 -59
  166. package/dist/vendored/components/ApiCallPanel.vue +0 -127
  167. package/dist/vendored/components/ApiPayloadTree.vue +0 -201
  168. package/dist/vendored/components/AppToast.vue +0 -37
  169. package/dist/vendored/components/CardGallery.vue +0 -291
  170. package/dist/vendored/components/CardPreview.vue +0 -72
  171. package/dist/vendored/components/CardSelect.vue +0 -206
  172. package/dist/vendored/components/ChatComposer.vue +0 -413
  173. package/dist/vendored/components/ChatMessage.vue +0 -383
  174. package/dist/vendored/components/ChatPanel.vue +0 -937
  175. package/dist/vendored/components/ComponentPalette.vue +0 -301
  176. package/dist/vendored/components/ConfirmDialog.vue +0 -54
  177. package/dist/vendored/components/DatePickerOverlay.vue +0 -345
  178. package/dist/vendored/components/DebugPanel.vue +0 -1734
  179. package/dist/vendored/components/DeviceOverlay.vue +0 -70
  180. package/dist/vendored/components/Dropdown.vue +0 -193
  181. package/dist/vendored/components/EventEditor.vue +0 -273
  182. package/dist/vendored/components/HoverGuides.vue +0 -122
  183. package/dist/vendored/components/JsonTree.vue +0 -547
  184. package/dist/vendored/components/LogValue.vue +0 -52
  185. package/dist/vendored/components/MarkdownBody.vue +0 -64
  186. package/dist/vendored/components/NewCardDialog.vue +0 -132
  187. package/dist/vendored/components/NodeInspector.vue +0 -295
  188. package/dist/vendored/components/NumberStepper.vue +0 -125
  189. package/dist/vendored/components/PickerOverlay.vue +0 -131
  190. package/dist/vendored/components/PropControl.vue +0 -245
  191. package/dist/vendored/components/PropTree.vue +0 -128
  192. package/dist/vendored/components/RemotePreview.vue +0 -173
  193. package/dist/vendored/components/SelectionOverlay.vue +0 -105
  194. package/dist/vendored/components/StructureTree.vue +0 -1002
  195. package/dist/vendored/components/Switch.vue +0 -38
  196. package/dist/vendored/components/setters/SetBgPosition.vue +0 -64
  197. package/dist/vendored/components/setters/SetBgSize.vue +0 -90
  198. package/dist/vendored/components/setters/SetCode.vue +0 -198
  199. package/dist/vendored/components/setters/SetColor.vue +0 -244
  200. package/dist/vendored/components/setters/SetCustomSelect.vue +0 -58
  201. package/dist/vendored/components/setters/SetExpr.vue +0 -107
  202. package/dist/vendored/components/setters/SetGradient.vue +0 -125
  203. package/dist/vendored/components/setters/SetGrid9.vue +0 -68
  204. package/dist/vendored/components/setters/SetIconSelect.vue +0 -67
  205. package/dist/vendored/components/setters/SetIfForm.vue +0 -61
  206. package/dist/vendored/components/setters/SetImage.vue +0 -43
  207. package/dist/vendored/components/setters/SetList.vue +0 -139
  208. package/dist/vendored/components/setters/SetNumber.vue +0 -118
  209. package/dist/vendored/components/setters/SetNumberStyle.vue +0 -113
  210. package/dist/vendored/components/setters/SetPadding.vue +0 -77
  211. package/dist/vendored/components/setters/SetSegment.vue +0 -160
  212. package/dist/vendored/components/setters/SetSelect.vue +0 -51
  213. package/dist/vendored/components/setters/SetSwitch.vue +0 -49
  214. package/dist/vendored/components/setters/SetText.vue +0 -52
  215. package/dist/vendored/components/setters/props.js +0 -23
  216. package/dist/vendored/fidelity/baseline-messageOverview.json +0 -87
  217. package/dist/vendored/fidelity/calibration.json +0 -29
  218. package/dist/vendored/fidelity/fidelity-messageOverview-after.html +0 -114
  219. package/dist/vendored/fidelity/fidelity-messageOverview-after.json +0 -272
  220. package/dist/vendored/fidelity/fidelity-messageOverview.html +0 -185
  221. package/dist/vendored/fidelity/fidelity-messageOverview.json +0 -336
  222. package/dist/vendored/fidelity/fidelity-messageOverview_v2.html +0 -262
  223. package/dist/vendored/fidelity/fidelity-messageOverview_v2.json +0 -502
  224. package/dist/vendored/fidelity/fidelity-messageOverview_v3.html +0 -201
  225. package/dist/vendored/fidelity/fidelity-messageOverview_v3.json +0 -417
  226. package/dist/vendored/fidelity/fidelity-messageOverview_visual.html +0 -125
  227. package/dist/vendored/fidelity/fidelity-messageOverview_visual.json +0 -338
  228. package/dist/vendored/index.html +0 -16
  229. package/dist/vendored/main.js +0 -37
  230. package/dist/vendored/runtime/ai-api.js +0 -165
  231. package/dist/vendored/runtime/alert-card.js +0 -125
  232. package/dist/vendored/runtime/app-toast.js +0 -33
  233. package/dist/vendored/runtime/clipboard-node.js +0 -78
  234. package/dist/vendored/runtime/clipboard.js +0 -29
  235. package/dist/vendored/runtime/event-model.js +0 -300
  236. package/dist/vendored/runtime/hoverGuide.js +0 -31
  237. package/dist/vendored/runtime/jsapi-mock.js +0 -256
  238. package/dist/vendored/runtime/markdown.js +0 -140
  239. package/dist/vendored/runtime/node-model.js +0 -217
  240. package/dist/vendored/skills/.manifest.json +0 -1101
  241. package/dist/vendored/standalone-main.js +0 -36
  242. package/dist/vendored/styles/ai-chat.css +0 -1850
  243. package/dist/vendored/styles/app.css +0 -3648
  244. package/dist/vendored/styles/card-gallery.css +0 -285
  245. package/dist/vendored/styles/card-select.css +0 -288
  246. package/dist/vendored/styles/components.css +0 -383
  247. package/dist/vendored/styles/dropdown.css +0 -324
  248. package/dist/vendored/styles/json-tree.css +0 -308
  249. package/dist/vendored/styles/node-inspector.css +0 -800
  250. package/dist/vendored/styles/setters.css +0 -1050
  251. package/dist/vendored/styles/standalone.css +0 -27
  252. package/dist/vendored/styles/structure-tree.css +0 -460
  253. /package/dist/vendored/{api.js → preview/api.js} +0 -0
  254. /package/dist/vendored/{assets → preview/assets}/svg-symbols.svg +0 -0
  255. /package/dist/vendored/{components → preview/components}/AbsoluteLayout.vue +0 -0
  256. /package/dist/vendored/{components → preview/components}/Button.vue +0 -0
  257. /package/dist/vendored/{components → preview/components}/Canvas.vue +0 -0
  258. /package/dist/vendored/{components → preview/components}/CheckGroup.vue +0 -0
  259. /package/dist/vendored/{components → preview/components}/Column.vue +0 -0
  260. /package/dist/vendored/{components → preview/components}/CustomPlaceholder.vue +0 -0
  261. /package/dist/vendored/{components → preview/components}/Divider.vue +0 -0
  262. /package/dist/vendored/{components → preview/components}/GridLayout.vue +0 -0
  263. /package/dist/vendored/{components → preview/components}/Icon.vue +0 -0
  264. /package/dist/vendored/{components → preview/components}/Image.vue +0 -0
  265. /package/dist/vendored/{components → preview/components}/Input.vue +0 -0
  266. /package/dist/vendored/{components → preview/components}/ListLayout.vue +0 -0
  267. /package/dist/vendored/{components → preview/components}/Map.vue +0 -0
  268. /package/dist/vendored/{components → preview/components}/MultiColumn.vue +0 -0
  269. /package/dist/vendored/{components → preview/components}/MultiText.vue +0 -0
  270. /package/dist/vendored/{components → preview/components}/ProgressSlider.vue +0 -0
  271. /package/dist/vendored/{components → preview/components}/RadioButton.vue +0 -0
  272. /package/dist/vendored/{components → preview/components}/RenderTree.vue +0 -0
  273. /package/dist/vendored/{components → preview/components}/Root.vue +0 -0
  274. /package/dist/vendored/{components → preview/components}/Row.vue +0 -0
  275. /package/dist/vendored/{components → preview/components}/SvgCode.vue +0 -0
  276. /package/dist/vendored/{components → preview/components}/SvgSymbols.vue +0 -0
  277. /package/dist/vendored/{components → preview/components}/Text.vue +0 -0
  278. /package/dist/vendored/{components → preview/components}/Video.vue +0 -0
  279. /package/dist/vendored/{components → preview/components}/ViewContainer.vue +0 -0
  280. /package/dist/vendored/{components → preview/components}/base.js +0 -0
  281. /package/dist/vendored/{components → preview/components}/icons.js +0 -0
  282. /package/dist/vendored/{components → preview/components}/index.js +0 -0
  283. /package/dist/vendored/{meta → preview/meta}/component-meta.json +0 -0
  284. /package/dist/vendored/{meta → preview/meta}/directive-meta.json +0 -0
  285. /package/dist/vendored/{meta → preview/meta}/style-meta.json +0 -0
  286. /package/dist/vendored/{runtime → preview/runtime}/StyleParser.js +0 -0
  287. /package/dist/vendored/{runtime → preview/runtime}/adapter.js +0 -0
  288. /package/dist/vendored/{runtime → preview/runtime}/assets.js +0 -0
  289. /package/dist/vendored/{runtime → preview/runtime}/canvas-context.js +0 -0
  290. /package/dist/vendored/{runtime → preview/runtime}/card-runtime.js +0 -0
  291. /package/dist/vendored/{runtime → preview/runtime}/evaluate.js +0 -0
  292. /package/dist/vendored/{runtime → preview/runtime}/property.js +0 -0
  293. /package/dist/vendored/{runtime → preview/runtime}/selectionOverlay.js +0 -0
  294. /package/dist/vendored/{runtime → preview/runtime}/toPreview.js +0 -0
  295. /package/dist/{server-core → vendored/server/core}/assetProxy.js +0 -0
  296. /package/dist/{server-core → vendored/server/core}/bbox.js +0 -0
  297. /package/dist/{server-core → vendored/server/core}/buildDataFromSchema.js +0 -0
  298. /package/dist/{server-core → vendored/server/core}/cardFiles.js +0 -0
  299. /package/dist/{server-core → vendored/server/core}/cardHistory.js +0 -0
  300. /package/dist/{server-core → vendored/server/core}/cardImage.js +0 -0
  301. /package/dist/{server-core → vendored/server/core}/cardImportMeta.js +0 -0
  302. /package/dist/{server-core → vendored/server/core}/cardListCache.js +0 -0
  303. /package/dist/{server-core → vendored/server/core}/cardOpenFolder.js +0 -0
  304. /package/dist/{server-core → vendored/server/core}/cardSidecar.js +0 -0
  305. /package/dist/{server-core → vendored/server/core}/codeBridge.js +0 -0
  306. /package/dist/{server-core → vendored/server/core}/logStream.js +0 -0
  307. /package/dist/{server-core → vendored/server/core}/logger.js +0 -0
  308. /package/dist/{server-core → vendored/server/core}/nodeModels.js +0 -0
  309. /package/dist/{server-core → vendored/server/core}/renderStatus.js +0 -0
  310. /package/dist/{server-core → vendored/server/core}/screenshot.js +0 -0
  311. /package/dist/{server-core → vendored/server/core}/sliceStatic.js +0 -0
  312. /package/dist/vendored/{meta → skills/dsl-card-generator/vendor/preview/meta}/action-meta.json +0 -0
  313. /package/dist/{server-core/_vendor → vendored/tools}/finmall-env.mjs +0 -0
@@ -0,0 +1,343 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * apply_opacity_pattern.mjs — 把「透明度表达式节点」机械改写成透明度切换固定范式(R57)。
4
+ *
5
+ * 背景:移动端渲染不支持 style.other.opacity 走表达式(itemValueFrom:"1"),只有预览环境可能生效。
6
+ * 范式:按该节点用到的透明度取值生成等量「静态透明度变体」,统一套进一层 Row/Column 容器;
7
+ * 容器宽高 = 被展示节点宽高 + other.overflow:"hidden"(隐藏变体若仍占位则被裁掉);
8
+ * 点击等事件全部移到容器上;变体之间用互斥 v-show 显隐切换,视觉上等价于透明度变化。
9
+ * 完整说明见 references/recipes.md §17 + references/recipe_snippets/opacity-switch.json。
10
+ *
11
+ * 用法:
12
+ * node scripts/apply_opacity_pattern.mjs --id <cardId> # 改写全部透明度表达式节点
13
+ * node scripts/apply_opacity_pattern.mjs --id <cardId> --check # 只报告,不写盘(有则 exit 1)
14
+ * node scripts/apply_opacity_pattern.mjs --id <cardId> --dry --json # 预览改写结果(JSON)
15
+ * node scripts/apply_opacity_pattern.mjs --id <cardId> --node comp_8 --container Column
16
+ *
17
+ * 可解析的透明度表达式:单三元 `COND ? A : B`、右嵌套三元 `C1 ? A : C2 ? B : C`。
18
+ * 其他形态(函数调用/查表)无法机械拆解 → exit 2,按范式手工改写。
19
+ */
20
+ import fs from 'node:fs'
21
+ import path from 'node:path'
22
+ import { cardSchemaPath, metaDir, readJson } from './lib/paths.mjs'
23
+
24
+ const VSHOW_WRAP_REQUIRED = new Set([
25
+ 'MultiColumn', 'ListLayout', 'GridLayout', 'AbsoluteLayout', 'Canvas', 'Map', 'Video', 'ViewContainer',
26
+ ])
27
+
28
+ function parseArgs(argv) {
29
+ const o = { id: '', node: [], container: 'Row', check: false, dry: false, json: false }
30
+ for (let i = 0; i < argv.length; i++) {
31
+ const a = argv[i]
32
+ if (a === '--id') o.id = argv[++i]
33
+ else if (a === '--node') o.node.push(argv[++i])
34
+ else if (a === '--container') o.container = argv[++i]
35
+ else if (a === '--check') o.check = true
36
+ else if (a === '--dry') o.dry = true
37
+ else if (a === '--json') o.json = true
38
+ }
39
+ return o
40
+ }
41
+
42
+ /* ---------- style-meta:itemId 权威来源 ---------- */
43
+ const styleMeta = readJson(path.join(metaDir, 'style-meta.json'), {})
44
+ const GROUP = {}
45
+ for (const g of styleMeta.properties || []) {
46
+ if (!g.itemKey) continue
47
+ GROUP[g.itemKey] = {
48
+ itemId: g.itemId,
49
+ children: Object.fromEntries((g.itemChildren || []).filter((c) => c.itemKey).map((c) => [c.itemKey, c.itemId])),
50
+ }
51
+ }
52
+ function gitemId(groupKey) {
53
+ const g = GROUP[groupKey]
54
+ if (!g) throw new Error(`style-meta 里没有样式组 ${groupKey}`)
55
+ return g.itemId
56
+ }
57
+ function citemId(groupKey, childKey) {
58
+ const id = (GROUP[groupKey] || {}).children?.[childKey]
59
+ if (!id) throw new Error(`style-meta 里 ${groupKey} 组没有子项 ${childKey}`)
60
+ return id
61
+ }
62
+
63
+ const leaf = (groupKey, key, value) => ({
64
+ itemId: citemId(groupKey, key), itemKey: key, itemName: key, itemType: '',
65
+ itemValueFrom: '0', itemValue: value, itemDefaultValue: value,
66
+ })
67
+ const group = (key, children) => ({
68
+ itemId: gitemId(key), itemKey: key, itemName: key, itemType: 'group',
69
+ itemValueFrom: '0', itemValue: {}, itemDefaultValue: {},
70
+ itemChildren: { properties: children },
71
+ })
72
+
73
+ /* ---------- 读节点样式 ---------- */
74
+ function styleProps(node) { return ((node.style || {}).properties) || [] }
75
+ function groupOf(node, key) { return styleProps(node).find((p) => p.itemKey === key) }
76
+ function childOf(node, key, childKey) {
77
+ return (((groupOf(node, key) || {}).itemChildren || {}).properties || []).find((c) => c.itemKey === childKey)
78
+ }
79
+ function valOf(node, key, childKey) {
80
+ const c = childOf(node, key, childKey)
81
+ return c ? c.itemValue : undefined
82
+ }
83
+ function opacityProp(node) { return childOf(node, 'other', 'opacity') }
84
+
85
+ function isExprOpacity(node) {
86
+ const c = opacityProp(node)
87
+ if (!c) return false
88
+ return c.itemValueFrom === '1' || (typeof c.itemValue === 'string' && c.itemValue.includes('$'))
89
+ }
90
+
91
+ /* ---------- 透明度表达式 → 变体表 ---------- */
92
+ function parseOpacityExpr(expr) {
93
+ const src = String(expr).trim()
94
+ const variants = []
95
+ let rest = src
96
+ for (;;) {
97
+ const m = rest.match(/^(.+?)\s*\?\s*(-?[\d.]+)\s*:\s*([\s\S]+)$/)
98
+ if (!m) break
99
+ variants.push({ cond: m[1].trim(), opacity: m[2] })
100
+ rest = m[3].trim()
101
+ // 右嵌套三元:剥掉外层括号继续
102
+ if (rest.startsWith('(') && rest.endsWith(')')) rest = rest.slice(1, -1).trim()
103
+ }
104
+ if (!variants.length) return null
105
+ if (!/^-?[\d.]+$/.test(rest)) return null
106
+ variants.push({ cond: null, opacity: rest })
107
+ return variants
108
+ }
109
+
110
+ function negate(conds) {
111
+ return conds.map((c) => `!(${c})`).join(' && ')
112
+ }
113
+
114
+ function condName(expr, opacity) {
115
+ if (expr.length < 15) return expr
116
+ return `透明度${opacity}:${expr.replace(/\$/g, '').replace(/\s+/g, '')}`
117
+ }
118
+
119
+ /* ---------- v-show 指令 ---------- */
120
+ function vshow(expr, name) {
121
+ const showType = {
122
+ itemId: '001', itemKey: 'showType', itemName: 'showType', itemType: '',
123
+ itemValueFrom: '0', itemValue: '01', itemDefaultValue: '01',
124
+ }
125
+ const condition = {
126
+ itemId: '002', itemKey: 'condition', itemName: 'condition', itemType: '',
127
+ itemValueFrom: '0', itemValue: { expression: expr, name },
128
+ itemDefaultValue: { expression: expr, name },
129
+ }
130
+ return {
131
+ itemId: '001', itemKey: 'v-show', itemName: 'v-show', itemType: '',
132
+ itemValueFrom: '0', itemValue: {}, itemDefaultValue: {},
133
+ itemChildren: { properties: [showType, condition] },
134
+ }
135
+ }
136
+
137
+ const clone = (x) => JSON.parse(JSON.stringify(x))
138
+
139
+ /* ---------- 构造变体节点 ---------- */
140
+ function buildVariant(node, { id, cond, opacity, needsVshowWrapper }) {
141
+ const variant = clone(node)
142
+ variant.componentId = id
143
+ variant.eventactions = [] // 事件一律上移到容器
144
+ variant.directives = []
145
+ // margin 归零:外边距由容器承载,避免变体各带一份外边距
146
+ const op = opacityProp(variant)
147
+ if (op) {
148
+ op.itemValueFrom = '0'
149
+ op.itemValue = String(opacity)
150
+ op.itemDefaultValue = String(opacity)
151
+ }
152
+ const marginGroup = groupOf(variant, 'margin')
153
+ if (marginGroup) {
154
+ for (const c of (marginGroup.itemChildren || {}).properties || []) {
155
+ const zero = c.itemKey === 'marginType' ? '' : '0'
156
+ c.itemValue = zero
157
+ c.itemDefaultValue = zero
158
+ }
159
+ }
160
+ if (!needsVshowWrapper) {
161
+ variant.directives = [vshow(cond.expression, cond.name)]
162
+ return { outer: variant, id }
163
+ }
164
+ // 特殊节点(List/Grid/Canvas…)不能直接挂 v-show(R53):每个变体再套一层承载显隐的 Row
165
+ const w = valOf(node, 'rect', 'width')
166
+ const h = valOf(node, 'rect', 'height')
167
+ const wrapId = `${id}_v`
168
+ const wrapper = {
169
+ componentId: wrapId,
170
+ className: 'Row',
171
+ name: 'Row',
172
+ style: {
173
+ properties: [
174
+ group('rect', [
175
+ leaf('rect', 'width', w === undefined ? 'auto' : String(w)),
176
+ leaf('rect', 'height', h === undefined ? 'auto' : String(h)),
177
+ ]),
178
+ group('flex', [
179
+ leaf('flex', 'justifyContent', 'center'),
180
+ leaf('flex', 'alignItems', 'center'),
181
+ leaf('flex', 'gap', '0'),
182
+ leaf('flex', 'flexShrink', '0'),
183
+ ]),
184
+ group('padding', [leaf('padding', 'paddingType', '0'), leaf('padding', 'padding', '0')]),
185
+ group('other', [leaf('other', 'overflow', 'hidden')]),
186
+ ],
187
+ },
188
+ properties: [],
189
+ directives: [vshow(cond.expression, cond.name)],
190
+ eventactions: [],
191
+ slots: [{ slotKey: 'default', slotName: '默认插槽', slotSwitch: '1', components: [variant] }],
192
+ }
193
+ return { outer: wrapper, id: wrapId }
194
+ }
195
+
196
+ /* ---------- 构造容器 ---------- */
197
+ function buildContainer(node, variants, containerClass) {
198
+ const width = valOf(node, 'rect', 'width')
199
+ const height = valOf(node, 'rect', 'height')
200
+ const shrink = valOf(node, 'flex', 'flexShrink')
201
+ const props = [
202
+ // 容器宽高必须与被展示节点一致(范式硬约束)
203
+ group('rect', [
204
+ leaf('rect', 'width', width === undefined ? 'auto' : String(width)),
205
+ leaf('rect', 'height', height === undefined ? 'auto' : String(height)),
206
+ ]),
207
+ group('flex', [
208
+ leaf('flex', 'justifyContent', 'center'),
209
+ leaf('flex', 'alignItems', 'center'),
210
+ leaf('flex', 'gap', '0'),
211
+ leaf('flex', 'flexShrink', shrink === undefined ? '0' : String(shrink)),
212
+ ]),
213
+ group('background', [leaf('background', 'backgroundType', '00')]),
214
+ group('padding', [leaf('padding', 'paddingType', '0'), leaf('padding', 'padding', '0')]),
215
+ ]
216
+ // 原节点的外边距搬到容器(保持在父布局里的位置)
217
+ const marginGroup = groupOf(node, 'margin')
218
+ if (marginGroup) props.push(clone(marginGroup))
219
+ // 隐藏溢出:v-show 在部分渲染实现里只隐藏不脱流,靠裁切保证外部布局不被撑开
220
+ props.push(group('other', [leaf('other', 'overflow', 'hidden')]))
221
+
222
+ return {
223
+ componentId: `${node.componentId}_box`,
224
+ className: containerClass,
225
+ name: containerClass,
226
+ style: { properties: props },
227
+ properties: [],
228
+ directives: clone(node.directives || []), // 原节点自身的显隐条件留在容器上
229
+ eventactions: clone(node.eventactions || []), // 点击等事件挂在容器上
230
+ slots: [{ slotKey: 'default', slotName: '默认插槽', slotSwitch: '1', components: variants }],
231
+ }
232
+ }
233
+
234
+ /* ---------- 遍历改写 ---------- */
235
+ function childArrays(node) {
236
+ const arrs = []
237
+ if (Array.isArray(node.components)) arrs.push(node.components)
238
+ for (const s of node.slots || []) if (Array.isArray(s.components)) arrs.push(s.components)
239
+ return arrs
240
+ }
241
+
242
+ function transform(view, opts) {
243
+ const changed = []
244
+ const seen = new Set()
245
+ const walkNode = (node) => {
246
+ for (const arr of childArrays(node)) {
247
+ for (let i = 0; i < arr.length; i++) {
248
+ const child = arr[i]
249
+ if (isExprOpacity(child) && (!opts.node.length || opts.node.includes(child.componentId))) {
250
+ arr[i] = rewrite(child, opts, changed, seen)
251
+ }
252
+ walkNode(arr[i])
253
+ }
254
+ }
255
+ }
256
+ walkNode(view)
257
+ return changed
258
+ }
259
+
260
+ function rewrite(node, opts, changed, seen) {
261
+ if (seen.has(node.componentId)) return node
262
+ seen.add(node.componentId)
263
+ const expr = String(opacityProp(node).itemValue)
264
+ const parsed = parseOpacityExpr(expr)
265
+ if (!parsed) {
266
+ throw Object.assign(new Error(
267
+ `${node.className} "${node.componentId}" 的透明度表达式无法机械拆解:${expr}\n` +
268
+ '请按 references/recipes.md §17 手工改写(枚举用到的透明度取值 → 等量静态变体 + 容器 + 互斥 v-show)。'
269
+ ), { code: 'unparsable-opacity-expr' })
270
+ }
271
+ const opacities = [...new Set(parsed.map((v) => v.opacity))]
272
+ const needsVshowWrapper = VSHOW_WRAP_REQUIRED.has(node.className)
273
+ const conds = []
274
+ const variants = parsed.map((v, idx) => {
275
+ const expression = v.cond ? v.cond : negate(conds.slice())
276
+ if (v.cond) conds.push(v.cond)
277
+ const cond = { expression, name: condName(expression, v.opacity) }
278
+ const id = idx === 0 ? node.componentId : `${node.componentId}_op${idx}`
279
+ return buildVariant(node, { id, cond, opacity: v.opacity, needsVshowWrapper }).outer
280
+ })
281
+ const container = buildContainer(node, variants, opts.container)
282
+ changed.push({
283
+ componentId: node.componentId,
284
+ className: node.className,
285
+ containerId: container.componentId,
286
+ containerClass: opts.container,
287
+ expression: expr,
288
+ opacities,
289
+ variants: variants.map((v) => v.componentId),
290
+ conditions: variants.map((v, idx) => {
291
+ const src = idx === 0 && !needsVshowWrapper ? v : v
292
+ const d = (src.directives || [])[0]
293
+ const c = (((d || {}).itemChildren || {}).properties || []).find((p) => p.itemKey === 'condition')
294
+ return (c || {}).itemValue
295
+ }),
296
+ eventsMoved: (node.eventactions || []).length,
297
+ })
298
+ return container
299
+ }
300
+
301
+ /* ---------- main ---------- */
302
+ const opts = parseArgs(process.argv.slice(2))
303
+ if (!opts.id) {
304
+ console.error('用法: node scripts/apply_opacity_pattern.mjs --id <cardId> [--check|--dry] [--json] [--node <cid>] [--container Row|Column]')
305
+ process.exit(1)
306
+ }
307
+ const schemaPath = cardSchemaPath(opts.id)
308
+ if (!fs.existsSync(schemaPath)) {
309
+ console.error(`[apply_opacity_pattern] 找不到卡片 schema: ${schemaPath}`)
310
+ process.exit(1)
311
+ }
312
+ const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'))
313
+
314
+ // --check:只报告存在透明度表达式的节点
315
+ if (opts.check) {
316
+ const found = []
317
+ const walkNode = (n) => {
318
+ if (isExprOpacity(n)) found.push({ componentId: n.componentId, className: n.className, expression: String(opacityProp(n).itemValue) })
319
+ for (const arr of childArrays(n)) for (const c of arr) walkNode(c)
320
+ }
321
+ walkNode(schema.view)
322
+ if (opts.json) console.log(JSON.stringify({ id: opts.id, ok: found.length === 0, found }, null, 2))
323
+ else if (found.length) {
324
+ console.log(`[apply_opacity_pattern] ${opts.id}: ${found.length} 个节点用了透明度表达式(R57 禁止)`)
325
+ for (const f of found) console.log(` - ${f.className} ${f.componentId}: opacity="${f.expression}"`)
326
+ } else console.log(`[apply_opacity_pattern] ${opts.id}: 无透明度表达式节点`)
327
+ process.exit(found.length ? 1 : 0)
328
+ }
329
+
330
+ let changed
331
+ try {
332
+ changed = transform(schema.view, opts)
333
+ } catch (e) {
334
+ console.error(`[apply_opacity_pattern] ${e.message}`)
335
+ process.exit(e.code === 'unparsable-opacity-expr' ? 2 : 1)
336
+ }
337
+
338
+ if (!changed.length) {
339
+ console.log(JSON.stringify({ id: opts.id, changed: 0, note: '没有需要改写的透明度表达式节点' }, null, 2))
340
+ process.exit(0)
341
+ }
342
+ if (!opts.dry) fs.writeFileSync(schemaPath, JSON.stringify(schema, null, 2) + '\n', 'utf8')
343
+ console.log(JSON.stringify({ id: opts.id, written: !opts.dry, changed: changed.length, nodes: changed }, null, 2))
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * build_card_index.mjs — 预计算「卡片索引」,让智能体选近邻模板时查表而非扫源文件。
4
+ *
5
+ * 扫描宿主项目卡片目录(默认 <cwd>/src/cards)下每张卡的 schema.json,对每张卡提取:
6
+ * - components:用到的组件类集合
7
+ * - recipes:用到的范式(alertCard / globalSet / money-format / list-expand / canvas-head / tab-billMode)
8
+ * - tags:结构信号 + 通用 UI 意图词(list / detail / form / dialog / result / canvas / amount / tab / interactive)
9
+ *
10
+ * 输出 <skill>/references/card_index.json,供 SKILL.md 的「意图路由表」直接查。
11
+ *
12
+ * 用法:node scripts/build_card_index.mjs
13
+ */
14
+ import fs from 'node:fs'
15
+ import path from 'node:path'
16
+ import { fileURLToPath } from 'node:url'
17
+
18
+ import { cardsDir, skillRoot } from './lib/paths.mjs'
19
+
20
+ const CARDS_DIR = cardsDir
21
+ const OUT = path.join(skillRoot, 'references', 'card_index.json')
22
+
23
+ /* ---- recipe 探测(对整卡文本做子串/特征匹配) ---- */
24
+ const RECIPE_PROBES = [
25
+ { key: 'alertCard', test: (t) => /alertCard/.test(t) },
26
+ { key: 'globalSet', test: (t) => /globalSet/.test(t) },
27
+ { key: 'money-format', test: (t) => /toFixed\(/.test(t) && /replace\(/.test(t) || /formatMoney/.test(t) },
28
+ { key: 'list-expand', test: (t) => /showAll/.test(t) },
29
+ { key: 'canvas-head', test: (t) => /onMeasured/.test(t) },
30
+ { key: 'tab-billMode', test: (t) => /billMode/.test(t) || /activeTab/.test(t) },
31
+ ]
32
+
33
+ /* ---- 标签启发(结构信号优先,卡名仅用通用 UI 意图词,不含具体业务实体名) ---- */
34
+ function inferTags(name, components, recipes) {
35
+ const tags = new Set()
36
+ const has = (re) => re.test(name || '')
37
+ // 结构信号(优先,无业务词依赖)
38
+ if (components.includes('ListLayout') || components.includes('GridLayout')) tags.add('list')
39
+ if (components.includes('Input')) tags.add('form')
40
+ if (components.includes('Canvas')) tags.add('canvas')
41
+ if (recipes.includes('alertCard')) tags.add('dialog')
42
+ if (recipes.includes('tab-billMode')) tags.add('tab')
43
+ if (recipes.includes('money-format')) tags.add('amount')
44
+ if (recipes.includes('list-expand')) tags.add('list')
45
+ if (recipes.includes('globalSet')) tags.add('interactive')
46
+ // 通用 UI 意图词兜底(非业务实体名;如"列表/表单/弹窗/结果/详情"等)
47
+ if (has(/列表|清单|list|记录/)) tags.add('list')
48
+ if (has(/弹窗|dialog|对话框/)) tags.add('dialog')
49
+ if (has(/表单|输入|密码|验证|form/)) tags.add('form')
50
+ if (has(/成功|失败|结果|result|success|failed/)) tags.add('result')
51
+ if (has(/详情|detail/)) tags.add('detail')
52
+ return [...tags]
53
+ }
54
+
55
+ // 子节点双通道:既可能在 node.components(如 Root 直连),也可能在 node.slots[].components
56
+ // (如 Row/Column 的插槽)——历史上只走 slots 通道,导致 Root 直连子树整棵丢失、
57
+ // card_index 的 components 字段退化为只剩 ["Root"]。
58
+ function collectComponents(node, out) {
59
+ if (!node || typeof node !== 'object') return
60
+ if (node.className) out.add(node.className)
61
+ for (const c of node.components || []) collectComponents(c, out)
62
+ for (const s of node.slots || []) for (const c of s.components || []) collectComponents(c, out)
63
+ }
64
+
65
+ export { RECIPE_PROBES, inferTags, collectComponents }
66
+
67
+ function main() {
68
+ if (!fs.existsSync(CARDS_DIR)) { console.error('[error] 未找到 src/cards:' + CARDS_DIR); process.exit(1) }
69
+ const cards = []
70
+ for (const id of fs.readdirSync(CARDS_DIR).sort()) {
71
+ const dir = path.join(CARDS_DIR, id)
72
+ const schemaPath = path.join(dir, 'schema.json')
73
+ if (!fs.statSync(dir).isDirectory() || !fs.existsSync(schemaPath)) continue
74
+ let schema
75
+ try { schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8')) } catch { continue }
76
+ const text = JSON.stringify(schema)
77
+ const compSet = new Set()
78
+ collectComponents(schema.view, compSet)
79
+ const recipes = RECIPE_PROBES.filter((p) => p.test(text)).map((p) => p.key)
80
+ const name = schema.name || id
81
+ const tags = inferTags(name, [...compSet], recipes)
82
+ cards.push({
83
+ id,
84
+ name,
85
+ pageId: schema.pageId || '',
86
+ components: [...compSet].sort(),
87
+ recipes,
88
+ tags,
89
+ })
90
+ }
91
+ const index = {
92
+ generatedAt: new Date().toISOString(),
93
+ cardCount: cards.length,
94
+ cards,
95
+ }
96
+ fs.mkdirSync(path.dirname(OUT), { recursive: true })
97
+ fs.writeFileSync(OUT, JSON.stringify(index, null, 2) + '\n')
98
+ console.log(`[ok] 已生成卡片索引:${OUT}(${cards.length} 张)`)
99
+ // 打印意图覆盖概览
100
+ const byTag = {}
101
+ for (const c of cards) for (const t of c.tags) byTag[t] = (byTag[t] || 0) + 1
102
+ console.log('标签覆盖:' + JSON.stringify(byTag))
103
+ }
104
+
105
+ // 仅当作为 CLI 直接运行时才执行(被 import 复用探测/遍历函数时不执行)
106
+ const _thisScript = fs.realpathSync(fileURLToPath(import.meta.url))
107
+ const _calledAs = process.argv[1] ? fs.realpathSync(path.resolve(process.argv[1])) : ''
108
+ if (_thisScript === _calledAs) main()
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * build_card_templates.mjs — 从存量卡提炼「整卡模板库」(全量重建)。
4
+ *
5
+ * 与空骨架(scaffold_card --intent)的边界:
6
+ * - 空骨架出「最小合法结构」,快但版式纪律要从零重建;
7
+ * - 整卡模板出「克隆最近邻真实卡 + 改写」,天然继承存量卡已验证的版式/绑定/范式。
8
+ *
9
+ * 聚类:按卡片结构标签(inferTags 产出的通用 UI 意图词)归簇——纯结构信号,
10
+ * 不含业务词,设计稿风格变化不影响聚类逻辑。
11
+ *
12
+ * 代表卡选择(确定性打分,同分取字典序小的保证可复现):
13
+ * score = recipe 命中数 × 10 + 组件种类数 × 2 − 节点数 × 0.01
14
+ *
15
+ * 与 register_card_template.mjs 的协作(沉淀闭环):
16
+ * - 本工具是全量重建;register 是单卡登记(沉淀时刻)。
17
+ * - 重建时**保留** register 写入的人工资产:pinned 代表卡(不被打分顶替)、
18
+ * keywords(意图路由词)、custom 自定义簇;仅当 pinned 源卡被删除时才回退打分。
19
+ *
20
+ * 输出 references/card_templates.json,供 scaffold_card.mjs --from / MCP card_scaffold
21
+ * 的 from 参数、resolve_card_template.mjs 意图路由使用。
22
+ *
23
+ * 用法:node skills/dsl-card-generator/scripts/build_card_templates.mjs [--json]
24
+ * 退出码:0 = 正常。
25
+ */
26
+ import fs from 'node:fs'
27
+ import path from 'node:path'
28
+ import { fileURLToPath } from 'node:url'
29
+ import { cardsDir, skillRoot } from './lib/paths.mjs'
30
+ import { RECIPE_PROBES, inferTags, collectComponents } from './build_card_index.mjs'
31
+
32
+ const OUT = path.join(skillRoot, 'references', 'card_templates.json')
33
+
34
+ /** 簇优先级:一张卡可能同时命中多个标签,按此顺序归入最特异的一个簇 */
35
+ const CLUSTER_ORDER = ['dialog', 'form', 'result', 'tab', 'canvas', 'list', 'amount', 'detail', 'interactive', 'other']
36
+
37
+ /** 内置簇的种子意图关键词(通用 UI 意图词,非业务词;register 登记的自定义词会合入) */
38
+ const SEED_KEYWORDS = {
39
+ dialog: ['弹窗', '弹框', '对话框', 'dialog'],
40
+ form: ['表单', '输入', 'form'],
41
+ result: ['结果', '成功', '失败', 'result'],
42
+ tab: ['多tab', '页签', '切换', 'tab'],
43
+ canvas: ['图表', '仪表盘', '画布', '环形图', 'canvas'],
44
+ list: ['列表', '清单', '记录', 'list'],
45
+ amount: ['金额', '账单', '数值', 'amount'],
46
+ detail: ['详情', 'detail'],
47
+ interactive: ['交互', '联动', 'interactive'],
48
+ other: ['其他'],
49
+ }
50
+
51
+ /** 测试夹具卡(脚手架回归/全组件自测)不是生产版式,不进入模板候选 */
52
+ const TEST_FIXTURE_RE = /(^dsl_test|[-_]test)/i
53
+
54
+ function countNodes(node) {
55
+ if (!node || typeof node !== 'object') return 0
56
+ let n = 1
57
+ for (const c of node.components || []) n += countNodes(c)
58
+ for (const s of node.slots || []) for (const c of s.components || []) n += countNodes(c)
59
+ return n
60
+ }
61
+
62
+ function clusterOf(tags) {
63
+ for (const key of CLUSTER_ORDER) if (tags.includes(key)) return key
64
+ return 'other'
65
+ }
66
+
67
+ /** 提取单卡模板特征(签名)。供本工具与 register/resolve 复用(同一份定义)。 */
68
+ export function extractCardFeatures(id) {
69
+ const schemaPath = path.join(cardsDir, id, 'schema.json')
70
+ if (!fs.existsSync(schemaPath)) return null
71
+ let schema
72
+ try {
73
+ schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'))
74
+ } catch {
75
+ return null
76
+ }
77
+ const text = JSON.stringify(schema)
78
+ const compSet = new Set()
79
+ collectComponents(schema.view, compSet)
80
+ const recipes = RECIPE_PROBES.filter((p) => p.test(text)).map((p) => p.key)
81
+ const tags = inferTags(schema.name || id, [...compSet], recipes)
82
+ const nodeCount = countNodes(schema.view)
83
+ return {
84
+ id,
85
+ name: schema.name || id,
86
+ tags,
87
+ recipes,
88
+ components: [...compSet].sort(),
89
+ nodeCount,
90
+ cluster: clusterOf(tags),
91
+ score: recipes.length * 10 + compSet.size * 2 - nodeCount * 0.01,
92
+ }
93
+ }
94
+
95
+ /** 读取现有注册表(不存在返回空壳) */
96
+ export function loadRegistry() {
97
+ try {
98
+ return JSON.parse(fs.readFileSync(OUT, 'utf8'))
99
+ } catch {
100
+ return { clusters: [] }
101
+ }
102
+ }
103
+
104
+ function main() {
105
+ const asJson = process.argv.includes('--json')
106
+ const cards = []
107
+ for (const id of fs.readdirSync(cardsDir).sort()) {
108
+ if (TEST_FIXTURE_RE.test(id)) continue
109
+ const dir = path.join(cardsDir, id)
110
+ if (!fs.statSync(dir).isDirectory()) continue
111
+ const f = extractCardFeatures(id)
112
+ if (f) cards.push(f)
113
+ }
114
+
115
+ const prev = loadRegistry()
116
+ const prevByKey = new Map((prev.clusters || []).map((c) => [c.key, c]))
117
+ const aliveIds = new Set(cards.map((c) => c.id))
118
+ const preservedNotes = []
119
+
120
+ const clusters = []
121
+ for (const key of CLUSTER_ORDER) {
122
+ const members = cards.filter((c) => c.cluster === key)
123
+ if (!members.length) continue
124
+ members.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
125
+ const old = prevByKey.get(key)
126
+ const pinnedAlive = old && old.template && old.template.pinned && aliveIds.has(old.template.sourceCard)
127
+ // pin 是人工意图,优先于自动聚类:pinned 源卡即使自动簇判定落在别的簇
128
+ // (不在本簇 members 里),也必须保住代表位,按其真实特征刷新模板信息。
129
+ const t = pinnedAlive ? extractCardFeatures(old.template.sourceCard) || members[0] : members[0]
130
+ if (old && old.template && old.template.pinned && !pinnedAlive) {
131
+ preservedNotes.push(`簇 ${key}:pinned 模板 ${old.template.sourceCard} 已不存在,回退为打分选代表`)
132
+ }
133
+ const keywords = [...new Set([...(SEED_KEYWORDS[key] || []), ...((old && old.keywords) || [])])]
134
+ clusters.push({
135
+ key,
136
+ keywords,
137
+ cardCount: members.length,
138
+ template: {
139
+ sourceCard: t.id,
140
+ name: t.name,
141
+ tags: t.tags,
142
+ recipes: t.recipes,
143
+ components: t.components,
144
+ nodeCount: t.nodeCount,
145
+ ...(pinnedAlive ? { pinned: true } : {}),
146
+ },
147
+ members: members.map((m) => m.id),
148
+ })
149
+ }
150
+
151
+ // 保留 register 登记的自定义簇(源卡存活才保留)
152
+ for (const old of prevByKey.values()) {
153
+ if (!old.custom) continue
154
+ if (!old.template || !aliveIds.has(old.template.sourceCard)) {
155
+ preservedNotes.push(`自定义簇 ${old.key}:模板卡 ${old.template ? old.template.sourceCard : '?'} 已不存在,簇被移除`)
156
+ continue
157
+ }
158
+ const f = extractCardFeatures(old.template.sourceCard)
159
+ clusters.push({
160
+ ...old,
161
+ template: { ...old.template, ...(f ? { tags: f.tags, recipes: f.recipes, components: f.components, nodeCount: f.nodeCount, name: f.name } : {}) },
162
+ })
163
+ }
164
+
165
+ const registry = {
166
+ generatedAt: new Date().toISOString(),
167
+ cardCount: cards.length,
168
+ hint: '整卡模板库:克隆模板卡改写 = scaffold_card.mjs --from <sourceCard> 或 MCP card_scaffold(from=...);意图路由查 keywords(resolve_card_template.mjs);新模式登记用 register_card_template.mjs。',
169
+ clusters,
170
+ }
171
+ fs.mkdirSync(path.dirname(OUT), { recursive: true })
172
+ fs.writeFileSync(OUT, JSON.stringify(registry, null, 2) + '\n')
173
+
174
+ if (asJson) {
175
+ console.log(JSON.stringify(registry, null, 2))
176
+ } else {
177
+ console.log(`[ok] 已生成整卡模板库:${OUT}(${cards.length} 张卡 → ${clusters.length} 簇)`)
178
+ for (const c of clusters) {
179
+ const pin = c.template.pinned ? '📌' : ' '
180
+ console.log(
181
+ ` ${pin}${c.key.padEnd(12)} ${String(c.cardCount).padStart(2)} 卡 模板=${c.template.sourceCard}(${c.template.name},${c.template.nodeCount} 节点)`,
182
+ )
183
+ }
184
+ for (const n of preservedNotes) console.log(` ⚠️ ${n}`)
185
+ }
186
+ }
187
+
188
+ const _thisScript = fs.realpathSync(fileURLToPath(import.meta.url))
189
+ const _calledAs = process.argv[1] ? fs.realpathSync(path.resolve(process.argv[1])) : ''
190
+ if (_thisScript === _calledAs) main()