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,43 @@
1
+ {
2
+ "baselineId": "v3-r038n",
3
+ "date": "2026-08-26",
4
+ "strategy": "v3",
5
+ "dataset": "training/datasets/v3/train-set.jsonl",
6
+ "datasetSha256": "33e0dddc26db72f4471c8d8f08b6983afe04ecaf01467ff97c8738ddb687e316",
7
+ "sampleCount": 48,
8
+ "metrics": {
9
+ "passRate": 1,
10
+ "passed": 48,
11
+ "validateErrors": 0,
12
+ "lintErrors": 0,
13
+ "avgPixelDiff": 0.11110358659080005,
14
+ "avgFidelity": 0.7756660799198527,
15
+ "avgFidelityComponents": 0.8986764532981638,
16
+ "avgColorFidelity": 0.9667358398437497,
17
+ "avgStructureFidelity": 0.6551878934800744,
18
+ "avgDimensionFidelity": 0.9717187946952559,
19
+ "renderFailures": 0,
20
+ "avgAnnotationCompleteness": 1,
21
+ "avgTextRetention": 1,
22
+ "upstreamFailures": 0
23
+ },
24
+ "gates": {
25
+ "passRateMinimum": 0.95,
26
+ "validateErrorsMaximum": 0,
27
+ "lintErrorsMaximum": 0,
28
+ "renderFailuresMaximum": 0,
29
+ "upstreamFailuresMaximum": 0,
30
+ "srcPollutionRequired": false
31
+ },
32
+ "artifacts": {
33
+ "runDir": "training/runs/r038n",
34
+ "summary": "training/runs/r038n/reports/summary.md"
35
+ },
36
+ "promotedLessons": [
37
+ "LESSON-20260826-039",
38
+ "LESSON-20260826-040",
39
+ "LESSON-20260826-041",
40
+ "LESSON-20260826-042",
41
+ "LESSON-20260826-043"
42
+ ]
43
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "baselineId": "v3-r039n",
3
+ "date": "2026-08-26",
4
+ "strategy": "v3",
5
+ "dataset": "training/datasets/v3/train-set.jsonl",
6
+ "datasetSha256": "33e0dddc26db72f4471c8d8f08b6983afe04ecaf01467ff97c8738ddb687e316",
7
+ "sampleCount": 48,
8
+ "metrics": {
9
+ "passRate": 1,
10
+ "passed": 48,
11
+ "validateErrors": 0,
12
+ "lintErrors": 0,
13
+ "avgPixelDiff": 0.1178415490216939,
14
+ "avgFidelity": 0.7813162775989081,
15
+ "avgFidelityComponents": 0.9605243097951431,
16
+ "avgColorFidelity": 0.9669010416666665,
17
+ "avgStructureFidelity": 0.6553877132475057,
18
+ "avgDimensionFidelity": 0.9515361106243735,
19
+ "renderFailures": 0,
20
+ "avgAnnotationCompleteness": 1,
21
+ "avgTextRetention": 1,
22
+ "upstreamFailures": 0
23
+ },
24
+ "comparison": {
25
+ "previousBaseline": "v3-r038n",
26
+ "avgFidelityDelta": 0.0056501976790554,
27
+ "avgFidelityComponentsDelta": 0.0618478564969793
28
+ },
29
+ "gates": {
30
+ "passRateMinimum": 0.95,
31
+ "validateErrorsMaximum": 0,
32
+ "lintErrorsMaximum": 0,
33
+ "renderFailuresMaximum": 0,
34
+ "upstreamFailuresMaximum": 0,
35
+ "srcPollutionRequired": false
36
+ },
37
+ "artifacts": {
38
+ "runDir": "training/runs/r039n",
39
+ "summary": "training/runs/r039n/reports/summary.md"
40
+ },
41
+ "promotedLessons": [
42
+ "LESSON-20260826-044",
43
+ "LESSON-20260826-045",
44
+ "LESSON-20260826-046",
45
+ "LESSON-20260826-047"
46
+ ]
47
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "baselineId": "v3-r050n",
3
+ "date": "2026-08-26",
4
+ "strategy": "v3",
5
+ "dataset": "training/datasets/v3/train-set.jsonl",
6
+ "datasetSha256": "33e0dddc26db72f4471c8d8f08b6983afe04ecaf01467ff97c8738ddb687e316",
7
+ "sampleCount": 48,
8
+ "metrics": {
9
+ "passRate": 1,
10
+ "passed": 48,
11
+ "validateErrors": 0,
12
+ "lintErrors": 0,
13
+ "avgPixelDiff": 0.11277390158817568,
14
+ "avgFidelity": 0.7952778246566923,
15
+ "avgFidelityComponents": 0.9559238061321395,
16
+ "avgColorFidelity": 0.9831754557291665,
17
+ "avgStructureFidelity": 0.7040960215045314,
18
+ "avgDimensionFidelity": 0.9620245001437465,
19
+ "renderFailures": 0,
20
+ "avgAnnotationCompleteness": 1,
21
+ "avgTextRetention": 1,
22
+ "upstreamFailures": 0,
23
+ "semanticReviewPending": 0
24
+ },
25
+ "comparison": {
26
+ "previousBaseline": "v3-r039n",
27
+ "avgFidelityDelta": 0.0139615470577862,
28
+ "avgStructureFidelityDelta": 0.0487083082574945,
29
+ "avgColorFidelityDelta": 0.0162744140625
30
+ },
31
+ "gates": {
32
+ "passRateMinimum": 0.95,
33
+ "validateErrorsMaximum": 0,
34
+ "lintErrorsMaximum": 0,
35
+ "renderFailuresMaximum": 0,
36
+ "upstreamFailuresMaximum": 0,
37
+ "srcPollutionRequired": false
38
+ },
39
+ "artifacts": {
40
+ "runDir": "training/runs/r050n",
41
+ "summary": "training/runs/r050n/reports/summary.json"
42
+ },
43
+ "promotedLessons": [
44
+ "LESSON-20260826-048",
45
+ "LESSON-20260826-049",
46
+ "LESSON-20260826-050"
47
+ ]
48
+ }
@@ -0,0 +1,472 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import hashlib
4
+ import json
5
+ import re
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ from datetime import datetime, timezone
10
+ from pathlib import Path
11
+ from typing import Optional
12
+
13
+ from PIL import Image
14
+
15
+
16
+ REPO_ROOT = Path(__file__).resolve().parents[3]
17
+ PARSER = REPO_ROOT / "skills" / "sketch-spec-to-card" / "scripts" / "parse_meaxure.py"
18
+ SPLITS = ("train", "dev", "eval", "holdout")
19
+ POPUP_TYPES = {"subcard", "overlay_dialog", "alert_dialog"}
20
+
21
+
22
+ def toolchain_rev() -> str:
23
+ """解析器所在的仓库版本:数据集产物不入库,靠它 + sourceRoot 才能判断可否复现。"""
24
+ try:
25
+ done = subprocess.run(
26
+ ["git", "rev-parse", "--short", "HEAD"],
27
+ cwd=REPO_ROOT, capture_output=True, text=True, timeout=10,
28
+ )
29
+ rev = done.stdout.strip()
30
+ dirty = subprocess.run(
31
+ ["git", "status", "--porcelain", "--", "skills"],
32
+ cwd=REPO_ROOT, capture_output=True, text=True, timeout=10,
33
+ ).stdout.strip()
34
+ return f"{rev}-dirty" if dirty else rev
35
+ except Exception:
36
+ return "unknown"
37
+
38
+
39
+ def safe_name(value: str) -> str:
40
+ value = re.sub(r"[^\w\u4e00-\u9fff.-]+", "_", value).strip("._")
41
+ return value or "untitled"
42
+
43
+
44
+ def stable_hash(*parts: str) -> str:
45
+ return hashlib.sha256("\u241f".join(parts).encode("utf-8")).hexdigest()
46
+
47
+
48
+ def split_for(case_id: str) -> str:
49
+ value = int(stable_hash(case_id, "card-skill-trainer-v1")[:8], 16) % 100
50
+ if value < 65:
51
+ return "train"
52
+ if value < 85:
53
+ return "dev"
54
+ if value < 95:
55
+ return "eval"
56
+ return "holdout"
57
+
58
+
59
+ def discover_sources(root: Path):
60
+ for path in sorted(root.iterdir(), key=lambda p: p.name):
61
+ if path.is_dir() and (path / "index.html").exists():
62
+ yield path
63
+
64
+
65
+ def parse_source(source: Path, raw_root: Path, force: bool, sample_size: int = 0) -> bool:
66
+ target = raw_root / safe_name(source.name)
67
+ if (target / "manifest.json").exists() and not force:
68
+ print(f"[skip] {source.name}")
69
+ return True
70
+ target.mkdir(parents=True, exist_ok=True)
71
+ command = [
72
+ sys.executable,
73
+ str(PARSER),
74
+ "--src",
75
+ str(source),
76
+ "--out",
77
+ str(target),
78
+ "--build",
79
+ # 无人值守训练 = 显式批量授权(rules.md §13.3 例外):直接过 parse_meaxure 的多画板选择门禁
80
+ "--scope-all",
81
+ ]
82
+ if sample_size:
83
+ command.extend(["--sample", str(sample_size)])
84
+ print(f"[parse] {source.name}")
85
+ result = subprocess.run(command)
86
+ return result.returncode == 0 and (target / "manifest.json").exists()
87
+
88
+
89
+ def first_image(spec_dir: Path, card: dict) -> Optional[Path]:
90
+ for key in ("image", "cardImage"):
91
+ rel = card.get(key)
92
+ if rel:
93
+ candidate = spec_dir / rel
94
+ if candidate.exists():
95
+ return candidate
96
+ artboard = spec_dir / "artboard.png"
97
+ return artboard if artboard.exists() else None
98
+
99
+
100
+ def expected_from_spec(card: dict):
101
+ texts = []
102
+ components = []
103
+ for element in card.get("elements", []):
104
+ kind = element.get("type") or element.get("class") or ""
105
+ if kind.lower() == "text":
106
+ value = (element.get("value") or element.get("content") or "").strip()
107
+ if value:
108
+ texts.append(value)
109
+ name = (element.get("name") or "").lower()
110
+ if any(word in name for word in ("btn", "button", "input", "tab", "list")):
111
+ components.append({"signal": name})
112
+ return {"requiredTexts": list(dict.fromkeys(texts))[:80], "componentSignals": components[:80]}
113
+
114
+
115
+ def is_popup_case(spec: dict, canvas: dict) -> bool:
116
+ if canvas.get("trainingEligible") is False:
117
+ return True
118
+ if spec.get("focusSkipReason") in {
119
+ "overlay_dialog_geometry", "bottom_sheet_geometry", "conversation_flow_geometry",
120
+ }:
121
+ return True
122
+ artboard_type = str(spec.get("artboardType") or canvas.get("artboardType") or "").lower()
123
+ if artboard_type in POPUP_TYPES:
124
+ return True
125
+ name = str(canvas.get("name") or "")
126
+ return any(keyword in name for keyword in ("弹窗", "弹框", "键盘弹窗", "二次确认"))
127
+
128
+
129
+ def difference_hash(image_path: Path, hash_size: int = 16) -> int:
130
+ """生成尺寸无关的横向梯度指纹(dHash)。"""
131
+ with Image.open(image_path) as image:
132
+ gray = image.convert("L").resize(
133
+ (hash_size + 1, hash_size), Image.Resampling.LANCZOS
134
+ )
135
+ pixels = list(gray.getdata())
136
+ value = 0
137
+ for row in range(hash_size):
138
+ for column in range(hash_size):
139
+ left = pixels[row * (hash_size + 1) + column]
140
+ right = pixels[row * (hash_size + 1) + column + 1]
141
+ value = (value << 1) | (1 if left > right else 0)
142
+ return value
143
+
144
+
145
+ def hamming_distance(left: int, right: int) -> int:
146
+ return bin(left ^ right).count("1")
147
+
148
+
149
+ def artboard_richness(spec: dict) -> tuple:
150
+ return (
151
+ len(spec.get("cards") or []),
152
+ len(spec.get("elements") or []),
153
+ len(spec.get("slices") or []),
154
+ )
155
+
156
+
157
+ def fingerprint_artboard(record: dict) -> dict:
158
+ preview = record["specDir"] / "artboard.png"
159
+ digest = hashlib.sha256(preview.read_bytes()).hexdigest() if preview.exists() else ""
160
+ perceptual = difference_hash(preview) if preview.exists() else 0
161
+ return {"sha256": digest, "dhash": perceptual, "layout": layout_signature(record["spec"])}
162
+
163
+
164
+ def layout_signature(spec: dict) -> set:
165
+ """提取尺寸无关的元素布局签名,避免把不同状态误判为同一画板。"""
166
+ size = spec.get("size") or {}
167
+ canvas_width = float(size.get("width") or 0)
168
+ canvas_height = float(size.get("height") or 0)
169
+ if canvas_width <= 0 or canvas_height <= 0:
170
+ return set()
171
+
172
+ signature = set()
173
+ type_counts = {}
174
+ text_lengths = []
175
+ for element in spec.get("elements") or []:
176
+ rect = element.get("rect") or {}
177
+ width = float(rect.get("width") or 0)
178
+ height = float(rect.get("height") or 0)
179
+ x = float(rect.get("x") or 0)
180
+ y = float(rect.get("y") or 0)
181
+ if width <= 0 or height <= 0:
182
+ continue
183
+
184
+ kind = str(element.get("type") or "unknown").lower()
185
+ type_counts[kind] = type_counts.get(kind, 0) + 1
186
+ center_x = min(15, max(0, int((x + width / 2) / canvas_width * 16)))
187
+ center_y = min(15, max(0, int((y + height / 2) / canvas_height * 16)))
188
+ width_bucket = min(7, int(max(0.0, width / canvas_width) * 8))
189
+ height_bucket = min(7, int(max(0.0, height / canvas_height) * 8))
190
+ signature.add((kind, center_x, center_y, width_bucket, height_bucket))
191
+
192
+ text = element.get("text")
193
+ if kind == "text" and text:
194
+ value = str(text).strip()
195
+ if value:
196
+ text_lengths.append(len(value))
197
+
198
+ for kind, count in type_counts.items():
199
+ signature.add(("count", kind, min(20, count)))
200
+ for index, length in enumerate(sorted(text_lengths)[:40]):
201
+ signature.add(("text-length", index, min(80, length // 4)))
202
+ return signature
203
+
204
+
205
+ def signature_similarity(left: set, right: set) -> float:
206
+ if not left or not right:
207
+ return 0.0
208
+ return len(left & right) / len(left | right)
209
+
210
+
211
+ def deduplicate_artboards(records: list, threshold: int = 4):
212
+ """按整板视觉指纹抽样,富信息画板优先做簇代表。"""
213
+ for record in records:
214
+ record["fingerprint"] = fingerprint_artboard(record)
215
+ record["richness"] = artboard_richness(record["spec"])
216
+
217
+ ordered = sorted(
218
+ records,
219
+ key=lambda item: (
220
+ tuple(-value for value in item["richness"]),
221
+ item["source"],
222
+ item["name"],
223
+ ),
224
+ )
225
+ clusters = []
226
+ for record in ordered:
227
+ target = None
228
+ for cluster in clusters:
229
+ representative = cluster[0]
230
+ same_ratio = abs(
231
+ record["aspectRatio"] - representative["aspectRatio"]
232
+ ) <= max(0.02, 0.02 * representative["aspectRatio"])
233
+ exact = bool(
234
+ record["fingerprint"]["sha256"] and
235
+ record["fingerprint"]["sha256"] == representative["fingerprint"]["sha256"]
236
+ )
237
+ near = hamming_distance(
238
+ record["fingerprint"]["dhash"],
239
+ representative["fingerprint"]["dhash"],
240
+ ) <= threshold
241
+ layout_similarity = signature_similarity(
242
+ record["fingerprint"]["layout"],
243
+ representative["fingerprint"]["layout"],
244
+ )
245
+ structurally_same = (
246
+ record["richness"] == representative["richness"] and
247
+ layout_similarity >= 0.94
248
+ )
249
+ if same_ratio and (exact or (near and structurally_same)):
250
+ target = cluster
251
+ break
252
+ if target is None:
253
+ clusters.append([record])
254
+ else:
255
+ target.append(record)
256
+
257
+ representatives = {cluster[0]["key"] for cluster in clusters}
258
+ duplicate_clusters = [
259
+ {
260
+ "representative": cluster[0]["key"],
261
+ "duplicates": [item["key"] for item in cluster[1:]],
262
+ "hammingDistances": [
263
+ hamming_distance(
264
+ item["fingerprint"]["dhash"],
265
+ cluster[0]["fingerprint"]["dhash"],
266
+ )
267
+ for item in cluster[1:]
268
+ ],
269
+ "layoutSimilarities": [
270
+ round(signature_similarity(
271
+ item["fingerprint"]["layout"],
272
+ cluster[0]["fingerprint"]["layout"],
273
+ ), 4)
274
+ for item in cluster[1:]
275
+ ],
276
+ }
277
+ for cluster in clusters
278
+ if len(cluster) > 1
279
+ ]
280
+ return representatives, duplicate_clusters
281
+
282
+
283
+ def main():
284
+ parser = argparse.ArgumentParser(description="Build an unattended dsl-card training set")
285
+ parser.add_argument("--src", required=True)
286
+ parser.add_argument("--out", required=True)
287
+ parser.add_argument("--max-cases", type=int, default=160)
288
+ parser.add_argument("--max-sources", type=int, default=0)
289
+ parser.add_argument("--artboards-per-source", type=int, default=0)
290
+ parser.add_argument("--force", action="store_true")
291
+ parser.add_argument("--duplicate-threshold", type=int, default=4)
292
+ parser.add_argument("--no-artboard-dedup", action="store_true")
293
+ args = parser.parse_args()
294
+
295
+ source_root = Path(args.src).expanduser().resolve()
296
+ out_root = Path(args.out).resolve()
297
+ raw_root = out_root / "raw"
298
+ raw_root.mkdir(parents=True, exist_ok=True)
299
+
300
+ sources = list(discover_sources(source_root))
301
+ if args.max_sources:
302
+ sources = sources[: args.max_sources]
303
+ if not sources:
304
+ raise SystemExit(f"No Sketch exports found under {source_root}")
305
+
306
+ cases = []
307
+ failures = []
308
+ skippedPopups = []
309
+ skippedNoCards = []
310
+ focusAudit = []
311
+ eligibleRecords = []
312
+ generatedArtboards = 0
313
+ for source in sources:
314
+ ok = parse_source(source, raw_root, args.force, args.artboards_per_source)
315
+ if not ok:
316
+ failures.append({"source": source.name, "error": "parse-failed"})
317
+ continue
318
+ parsed_dir = raw_root / safe_name(source.name)
319
+ manifest = json.loads((parsed_dir / "manifest.json").read_text(encoding="utf-8"))
320
+ for canvas in manifest.get("canvases", []):
321
+ name = canvas.get("name", "")
322
+ status = canvas.get("status")
323
+ spec_dir = parsed_dir / name
324
+ spec_path = spec_dir / "spec.json"
325
+ if status == "skipped" or not spec_path.exists():
326
+ continue
327
+ generatedArtboards += 1
328
+ if canvas.get("trainingEligible") is False:
329
+ skippedPopups.append({"source": source.name, "canvas": name, "reason": "manifest-training-ineligible"})
330
+ continue
331
+ try:
332
+ spec = json.loads(spec_path.read_text(encoding="utf-8"))
333
+ except Exception as exc:
334
+ failures.append({"source": source.name, "canvas": name, "error": f"bad-spec:{exc}"})
335
+ continue
336
+ if is_popup_case(spec, canvas):
337
+ skippedPopups.append({"source": source.name, "canvas": name, "reason": "popup-card"})
338
+ continue
339
+ slug = canvas.get("slug") or name
340
+ usableCards = []
341
+ for card_index, card in enumerate(spec.get("cards", [])):
342
+ if card.get("trainingEligible") is False:
343
+ continue
344
+ design = first_image(spec_dir, card)
345
+ if not design:
346
+ continue
347
+ usableCards.append((card_index, card, design))
348
+ if not usableCards:
349
+ skippedNoCards.append({
350
+ "source": source.name,
351
+ "canvas": name,
352
+ "reason": spec.get("focusSkipReason") or "no-usable-card-image",
353
+ })
354
+ continue
355
+ key = f"{source.name}\u241f{name}"
356
+ eligibleRecords.append({
357
+ "key": key,
358
+ "source": source.name,
359
+ "sourceDir": source,
360
+ "name": name,
361
+ "slug": slug,
362
+ "spec": spec,
363
+ "specDir": spec_dir,
364
+ "specPath": spec_path,
365
+ "canvas": canvas,
366
+ "cards": usableCards,
367
+ "aspectRatio": spec["size"]["width"] / max(spec["size"]["height"], 1),
368
+ })
369
+ focusAudit.append({
370
+ "source": source.name,
371
+ "canvas": name,
372
+ "focusSkipReason": spec.get("focusSkipReason") or "",
373
+ "acceptedCards": len(usableCards),
374
+ "rejectedCards": spec.get("rejectedCards") or [],
375
+ })
376
+
377
+ if args.no_artboard_dedup:
378
+ representatives = {record["key"] for record in eligibleRecords}
379
+ duplicateClusters = []
380
+ else:
381
+ representatives, duplicateClusters = deduplicate_artboards(
382
+ eligibleRecords, args.duplicate_threshold
383
+ )
384
+
385
+ for record in eligibleRecords:
386
+ if record["key"] not in representatives:
387
+ continue
388
+ source = record["sourceDir"]
389
+ name = record["name"]
390
+ spec = record["spec"]
391
+ spec_path = record["specPath"]
392
+ spec_dir = record["specDir"]
393
+ canvas = record["canvas"]
394
+ for card_index, card, design in record["cards"]:
395
+ case_id = stable_hash(source.name, name, str(card_index))[:16]
396
+ cases.append(
397
+ {
398
+ "caseId": case_id,
399
+ "source": source.name,
400
+ "artboardName": name,
401
+ "cardIndex": card_index,
402
+ "cardImage": design.name,
403
+ "taskType": "spec_to_card",
404
+ "difficulty": "medium",
405
+ "tags": [spec.get("artboardType", "card"), *(canvas.get("tags") or [])],
406
+ "input": {
407
+ "specJson": str(spec_path),
408
+ "designImage": str(design),
409
+ "slicesDir": str(spec_dir / "slices"),
410
+ },
411
+ "expected": expected_from_spec(card),
412
+ "split": split_for(case_id),
413
+ }
414
+ )
415
+
416
+ # 同一源目录被重复发现(staging 里符号链接 + 真实目录同名)会产出逐字节相同的 case:
417
+ # 训练轮重复渲染同一张卡、passRate 分母失真(v4 实测 221 行 / 218 唯一)。按 caseId 去重。
418
+ seen_case_ids = set()
419
+ duplicate_case_ids = []
420
+ unique_cases = []
421
+ for case in cases:
422
+ if case["caseId"] in seen_case_ids:
423
+ duplicate_case_ids.append(case["caseId"])
424
+ continue
425
+ seen_case_ids.add(case["caseId"])
426
+ unique_cases.append(case)
427
+ cases = unique_cases
428
+
429
+ selected = cases[: args.max_cases]
430
+ dataset_path = out_root / "train-set.jsonl"
431
+ with dataset_path.open("w", encoding="utf-8") as handle:
432
+ for case in selected:
433
+ handle.write(json.dumps(case, ensure_ascii=False) + "\n")
434
+
435
+ counts = {split: sum(1 for case in selected if case["split"] == split) for split in SPLITS}
436
+ summary = {
437
+ "generatedAt": datetime.now(timezone.utc).isoformat(timespec="seconds"),
438
+ "provenance": {
439
+ "sourceRoot": str(source_root),
440
+ "sourceNames": [source.name for source in sources],
441
+ "toolchainRev": toolchain_rev(),
442
+ "parser": str(PARSER.relative_to(REPO_ROOT)),
443
+ "duplicateThreshold": args.duplicate_threshold,
444
+ "artboardsPerSource": args.artboards_per_source,
445
+ "maxCases": args.max_cases,
446
+ "noArtboardDedup": bool(args.no_artboard_dedup),
447
+ },
448
+ "sources": len(sources),
449
+ "generatedArtboards": generatedArtboards,
450
+ "eligibleArtboards": len(eligibleRecords),
451
+ "selectedArtboards": len(representatives),
452
+ "duplicateArtboards": len(eligibleRecords) - len(representatives),
453
+ "candidates": len(cases),
454
+ "selected": len(selected),
455
+ "duplicateCases": sorted(set(duplicate_case_ids)),
456
+ "splits": counts,
457
+ "duplicateClusters": duplicateClusters,
458
+ "skippedPopups": skippedPopups,
459
+ "skippedNoCards": skippedNoCards,
460
+ "failures": failures,
461
+ }
462
+ (out_root / "dataset-summary.json").write_text(
463
+ json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8"
464
+ )
465
+ (out_root / "focus-audit.json").write_text(
466
+ json.dumps(focusAudit, ensure_ascii=False, indent=2), encoding="utf-8"
467
+ )
468
+ print(json.dumps(summary, ensure_ascii=False, indent=2))
469
+
470
+
471
+ if __name__ == "__main__":
472
+ main()