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,797 @@
1
+ # Recipes — 通用开发范式库
2
+
3
+ 可复用、export-proven 的组合配方。每条给出结构 + 代码 + 来源卡片。生成新卡时优先套用最接近的范式。
4
+
5
+ ## 1. Canvas 画布标题头
6
+
7
+ 用途:在卡片顶部画一个带浅色背景条的标题(替代 Text,便于精确控制对齐与装饰)。
8
+
9
+ 结构:
10
+ - 外层 `Row`,flex.justifyContent=flex-start(左对齐)或 center(居中)、alignItems=center,slots[default].components 放 Canvas。
11
+ - `Canvas` 的 rect.width 用**表达式**(itemValueFrom:"1")随标题长度变,rect.height 固定数字。
12
+ - Canvas 的 eventactions[0]:eventKey=onMeasured,actionType=customAction,function.code 里用 event.canvas 画。
13
+
14
+ 宽度表达式(4 个中文字 bold 16px):`($title.length || 4) * 16`,约 64;height 30。
15
+
16
+ onMeasured 代码(左对齐,来源 creditCardBill/canvas_title):
17
+
18
+ ```javascript
19
+ const canvas = event.canvas;
20
+ const ctx = canvas.getContext("2d");
21
+ ctx.font = "bold 16px Arial";
22
+ ctx.fillStyle = "#333";
23
+ const text = this.state.billMode === '1' ? '已出账单' : '未出账单';
24
+ const textWidth = ctx.measureText(text).width;
25
+ const singleTextWidth = textWidth / text.length;
26
+ const m = ctx.measureText(text);
27
+ const textHeight = m.actualBoundingBoxAscent + m.actualBoundingBoxDescent;
28
+ // 浅蓝色背景条(文本下方,靠左)
29
+ const startX = 0;
30
+ const startY = canvas.height / 2 + 1;
31
+ ctx.fillStyle = "#CBEEDF";
32
+ ctx.fillRect(startX, startY, singleTextWidth * text.length, textHeight * 0.6);
33
+ // 文本(靠左 + 垂直居中)
34
+ ctx.fillStyle = "#333";
35
+ ctx.textAlign = "left";
36
+ ctx.textBaseline = "middle";
37
+ ctx.fillText(text, 0, canvas.height / 2);
38
+ ```
39
+
40
+ 居中变体(来源 dreamPlan/assetOverview):startX = `canvas.width / 2 - textWidth / 2`,textAlign="center",x = `canvas.width / 2`。
41
+
42
+ 要点:
43
+ - 坐标是**设计稿 px**,event.canvas 是设计稿坐标系包装对象(宽度/2dctx 已按 scale 换算)。不要乘 devicePixelRatio,不要用 canvas.width/2 做左对齐(那是设备 px 假设)。
44
+ - code 与 compiled 两份保持同步(compiled 是 var 形式)。
45
+ - 来源:creditCardBill(左对齐)、dreamPlan、assetOverview、financialProductDetail、debtDetails、myDeposit、querySelector 等 25 张卡都用 onMeasured。
46
+
47
+ ## 2. 弹一张卡片(alertCard)+ 同名 Dialog 表
48
+
49
+ 用途:在当前卡里点击弹出另一张卡(底部/居中弹窗),子卡关闭后回传数据。
50
+
51
+ 打开(父卡 customAction,来源 creditCardBill/monthlyBill/activityLevel):
52
+
53
+ ```javascript
54
+ var that = this;
55
+ ynet.callJSAPI('alertCard', {
56
+ cardCode: 'billListDialog',
57
+ data: {
58
+ title: '账单详情',
59
+ list: that.state.billList,
60
+ parentInstanceId: event.instanceId
61
+ },
62
+ gravity: 'bottom'
63
+ }, function (data) {
64
+ if (data.success) {
65
+ // data.data.event === 'hidden' 表示弹窗被关闭
66
+ // 子卡 closeAlertCard 回传的数据在 data.data
67
+ }
68
+ });
69
+ ```
70
+
71
+ 关闭(子卡 customAction):
72
+
73
+ ```javascript
74
+ // 仅关闭
75
+ ynet.callJSAPI('closeAlertCard', { cardCode: 'billListDialog' }, function () {});
76
+ // 关闭并回传「选中项」——只回传父卡需要的字段,禁止 data: this.state(会把整个子卡作用域甩给父卡)
77
+ ynet.callJSAPI('closeAlertCard', {
78
+ data: {
79
+ selected: {
80
+ acctNum: this.state.acctNum,
81
+ branchNodeName: this.state.branchNodeName,
82
+ balance: this.state.balance
83
+ }
84
+ }
85
+ }, function () {});
86
+ ```
87
+
88
+ 要点:
89
+ - alertCard 是 JSAPI(在 customAction 里调),不是 actionType。
90
+ - cardCode 指向另一张卡(通常 *Dialog 同名卡)。data 透传给子卡作用域(子卡 data.json ∪ 传入 data)。
91
+ - gravity:bottom/center/top/left/right;center 小弹窗可传 width/offsetY。
92
+ - parentInstanceId: event.instanceId 让子卡回信给父实例。
93
+ - 预览器实时从磁盘读目标卡(GET /__card/<cardCode>),保证弹窗卡是最新落盘版。
94
+ - **回传数据显式化(铁律)**:子卡 `closeAlertCard` 只回传父卡需要的字段(如 `selected:{acctNum,branchNodeName,balance}`),**禁止 `data: this.state`**;父卡回调里**按字段提取**再赋值(`that.state.currCard = { acctNum: data.data.selected.acctNum, ... }`),**绝不直接 `that.state.currCard = data.data.currCard` 整体赋值**——子卡 `this.state` 混有父卡注入的 `list`/`title`/`parentInstanceId`,整体赋值会让字段对不上。
95
+ - 重复 openAlertCard 会先关闭已有弹窗(触发其回调 event:'hidden')。
96
+
97
+ 49 卡同名 Dialog 对应表(精选):
98
+
99
+ | Dialog 卡 | 透传字段 | 典型 opener | 关闭目标 |
100
+ | --- | --- | --- | --- |
101
+ | billListDialog | list, title | creditCardBill | billListDialog |
102
+ | transferRecordDialog | tradeList, title | monthlyBill | transferRecordDialog |
103
+ | activityLevelDialog | listData, title | activityLevel | activityLevelDialog |
104
+ | activityRecordDialog | listData, title | (父卡预览按钮) | activityRecordDialog |
105
+ | monthlyBillDialog | listData, title | (宿主编排) | monthlyBillDialog |
106
+ | transitionMonthListDialog | list, title | (宿主编排) | transitionMonthListDialog |
107
+ | multiMonthTransitionListDialog | list, title | transitionListDialog | multiMonthTransitionListDialog |
108
+ | accountDialog | title, account_details, show, otherLangLabels | (宿主编排) | accountDialog |
109
+ | inputPasswordDialog | currentCard, maxLength, list, successQueryParams, validateMode | accountDialog/myAccount | inputPasswordDialog |
110
+ | myCardNumberDialog | cardNumber | inputPasswordDialog | myCardNumberDialog |
111
+
112
+ ## 3. 列表展开/收起
113
+
114
+ 用途:列表默认显示前 N 项,点击"查看更多"显示全部,再点"收起"。
115
+
116
+ 数据模型:inputParams 加 `showAll`(boolean, defaultValue false)。
117
+
118
+ 绑定:ListLayout.value 绑 `$showAll ? $list : $list.slice(0, 3)`(前 3 项)。
119
+
120
+ 按钮文案:Text.value 绑 `$showAll ? '收起' : '查看更多...'`。
121
+
122
+ 点击事件(customAction):
123
+
124
+ ```javascript
125
+ // 简单
126
+ this.setData({ showAll: !this.state.showAll });
127
+ // 或
128
+ var newState = !this.state.showAll;
129
+ this.state.showAll = newState;
130
+ this.setData(this.state);
131
+ ```
132
+
133
+ 变体 A — 整段用 v-show 控制每项显隐(accountCreditedlist):v-show condition.expression = `$_index < 3 || $showAll`,点击按钮 `this.state.showAll = !this.state.showAll; this.setData(this.state)`。
134
+
135
+ 变体 B — 复合条件(accountCreditedlist):`$action === 'onSelect' || ($_index < 3 || $showAll) || $action === 'multi'`(选择模式下全显,否则前 3 或展开)。
136
+
137
+ 来源:account、activityList、depositList、activityRight、accountCreditedlist、holdFinancialProducts。
138
+
139
+ ## 4. globalSet 子→父数据透传
140
+
141
+ 用途:子组件(如列表项内按钮)修改数据后,通知根节点更新 state 并重渲染。子组件无法直接改根 state,只能 postMessage 让根节点监听器代劳。
142
+
143
+ **铁律(lint `r54`)**:ListLayout/GridLayout 的 item 事件里修改绑定数据禁止直接 this.setData/setRootData/直接 state 赋值——必须走本范式透传;用了 globalSet 但根节点没注册监听同样拦截。**payload 即 patch 对象,与 `setData(...)` 入参逐字一致**(浅合并、语义同 setData),存量 setData 代码迁移只需改调用名;深路径写入不内置于范式,确需时在卡内自扩展 $set 监听(activityRight/DepositHolding 等历史实现);监听回调里 this 不可靠,一律用 onReady 闭包捕获的 self。
144
+
145
+ **读 item 字段同理直读(铁律 r55)**:item 事件里 `this.state.<field>` 就是当前项字段(平台保证展开到 state),如 `var q = this.state.query;`;禁止 `$$` / `this.state.$$` / `if (q === undefined && this.state.$$)` 之类兜底判断,lint `r55` ERROR 拦截。
146
+
147
+ 根节点 onReady(customAction)注册监听(固定范式,serviceEvaluation 为规范实现):
148
+
149
+ ```javascript
150
+ var self = this;
151
+ this.addEventListener('globalSet', function (event) {
152
+ var patch = (event && event.data) || {};
153
+ if (patch && typeof patch === 'object' && Object.keys(patch).length) {
154
+ self.setData(patch);
155
+ }
156
+ });
157
+ ```
158
+
159
+ 子节点(列表项按钮)customAction:
160
+
161
+ ```javascript
162
+ // patch 对象与 setData(...) 入参逐字一致——存量 setData 代码迁移只需改调用名
163
+ this.postMessage('globalSet', { overallActiveQuery: q, overallLocked: true });
164
+ ```
165
+
166
+ 配合 syncList 事件触发列表重算(activityRight):
167
+
168
+ ```javascript
169
+ // 根节点
170
+ var initList = function () {
171
+ var curGroup = self.state['listData' + self.state.activeTab];
172
+ self.state.renderListData = curGroup[self.state.activeSubTab].list;
173
+ self.state.showAll = false;
174
+ self.setData(self.state);
175
+ };
176
+ initList();
177
+ this.addEventListener('syncList', function (event) { setTimeout(initList, 10); });
178
+
179
+ // 子节点切 tab 后
180
+ this.postMessage('globalSet', { key: 'activeSubTab', value: this.state._index });
181
+ this.postMessage('syncList');
182
+ ```
183
+
184
+ 相关内部事件名(49 卡实测):updateData、globalSet、globalUpdateItem、syncList、clearVerifyTimer、sendVerfiyClick。updateData 与 globalSet 形态一致(key 可为路径数组),accountCreditedlist 用 updateData。
185
+
186
+ ## 5. 金额格式化(千分号/正负号/货币符号)
187
+
188
+ 用途:金额显示带千分号、2 位小数、可选正负号与货币符号。
189
+
190
+ 内联表达式(来源 creditCardPayment,适合单处使用):
191
+
192
+ ```javascript
193
+ '剩余应还¥' + ($currentPeriodAmount * 1).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')
194
+ ```
195
+
196
+ 健壮版表达式(**默认口径**:显示金额一律在 view 表达式里直出,不在 onReady 预生成 `*_fmt`):
197
+
198
+ ```javascript
199
+ // 恒为正的金额(应还 / 最低应还 / 总额)
200
+ '¥' + (((($remainingAmount || 0) + '').replace(/,/g, '') * 1) || 0).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')
201
+
202
+ // 可能为负的金额:前面加符号位
203
+ (((($paidAmount || 0) + '').replace(/,/g, '') * 1) < 0 ? '-' : '') + '¥'
204
+ + Math.abs((($paidAmount || 0) + '').replace(/,/g, '') * 1).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')
205
+ ```
206
+
207
+ - `($x || 0) + ''` 统一成字符串 → `.replace(/,/g, '')` 去掉上游可能已带的千分号 → `* 1` 数值化。
208
+ - 末尾 `|| 0` 是 `NaN` 兜底,等价旧 helper 里的 `if (isNaN(num)) num = 0`;缺了它,脏值会渲染成 `¥NaN`。
209
+ - 确定恒为正就别套 `Math.abs` + 符号三元,白占长度。
210
+
211
+ > ⚠️ **反范式(已废弃,勿再生成)**:在 onReady 里 `for (key in this.state)` 按 **key 后缀**批量造 `key + '_fmt'`:
212
+ > ```javascript
213
+ > if (key.slice(-6) === 'Amount' && typeof val === 'number') { this.state[key + '_fmt'] = ... }
214
+ > ```
215
+ > 按 key 名猜语义不可靠:命名不规范就整片漏掉(`totalAmt` / `amount` / `balance` 一个都不命中);`_fmt` 又不是声明变量,调试面板里看不见;把关条件一旦写错(如上面的 `typeof val === 'number'`,上游投字符串数字就全军覆没),金额**整块空白且零报错**,极难定位。creditCardBill 已改造为表达式直出。
216
+
217
+ **唯一例外**:`_fmt` 本身是 `inputParams[].children` 里**声明过的字段**(上游数据契约,如 creditCardBill 的 `billList[].amount_fmt`)。这时可以在 onReady 里**指名道姓**赋值——显式取字段,不靠 key 匹配:
218
+
219
+ ```javascript
220
+ this.$formatMoney = function (money, isPrefix, bothSign) {
221
+ var num = parseFloat(money);
222
+ if (isNaN(num)) { num = 0; }
223
+ var sign = num < 0 ? '-' : (num > 0 && bothSign ? '+' : '');
224
+ var parts = Math.abs(num).toFixed(2).split('.');
225
+ parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
226
+ return sign + (isPrefix ? '¥' : '') + parts.join('.');
227
+ };
228
+ // 列表项金额:显式 e.amount,bothSign=true 让正数也带 +
229
+ this.state.billList.forEach(function (e) {
230
+ e.amount_fmt = _this.$formatMoney(e.amount, true, true);
231
+ });
232
+ this.setData(this.state);
233
+ ```
234
+
235
+ 要点:
236
+ - `* 1` / `parseFloat` 把字符串金额转数字;`|| 0` / `isNaN` 兜底 0,绝不让 `NaN` 上屏。
237
+ - 千分号正则 `/\B(?=(\d{3})+(?!\d))/g` 只格式化整数部分,小数部分不动。
238
+ - 正号:`num > 0 && bothSign ? '+' : ''`(bothSign=true 时正数也显 +,适合收支列表)。
239
+ - 负号:`num < 0 ? '-' : ''`;货币符:`isPrefix ? '¥' : ''`(新生成卡用半角 `¥`,见 amount_formatting.md §2)。
240
+ - 金额之间的**相等比较**不能直接 `$a == $b`(两个字符串走字面比较,`"5638.00" == "5638"` 为 false,会切错条件分支)。写成 `((($a + '').replace(/,/g, '') * 1) == (($b + '').replace(/,/g, '') * 1))`;大小比较(`>` / `<`)JS 自动 coerce,无需改写。
241
+ - `code` 与 `compiled` 两份必须同步改(compiled 是 var/ES5 形式,箭头函数与 `this` 要转 `_this`)。
242
+
243
+ ## 6. Tab ↔ 标题头模式切换(billMode)
244
+
245
+ 用途:一个变量控制"tab 模式"与"单面板+标题头模式"切换(creditCardBill)。
246
+
247
+ 模式:
248
+ - billMode === '0':显示 tab 栏(row_f1),按 $activeTab 切换两个分面板(column_58/column_89)。
249
+ - billMode === '1':强制面板 1(已出账单),显 Canvas 标题头(row_titleHeader),隐 tab 栏。
250
+ - billMode === '2':强制面板 2(未出账单),显标题头,隐 tab 栏。
251
+
252
+ **关键陷阱**:activeTab 默认值 "1" 不随 billMode 变。在 billMode='2' 时 $activeTab 仍是 '1',若面板可见性只写 `$activeTab === '1'` 则面板 1 会错显。
253
+
254
+ 正确可见性表达式(v-show condition.expression):
255
+
256
+ ```
257
+ column_58: ($billMode === '0' && $activeTab === '1') || $billMode === '1'
258
+ column_89: ($billMode === '0' && $activeTab === '2') || $billMode === '2'
259
+ row_f1 (tab 栏): $billMode === '0'
260
+ row_titleHeader: $billMode !== '0'
261
+ ```
262
+
263
+ 规则:任何依赖 $activeTab 的面板,必须先 AND `$billMode === '0'` 再 OR 强制模式。标题头(Canvas)在 $billMode !== '0' 时显示。
264
+
265
+ 来源:creditCardBill(billMode),activityRight(activeTab + activeSubTab + listData<n> 的多层 tab)。
266
+
267
+ ## 7. Tab 切换(activeTab + setData + v-show)—— 纯 Tab 范式
268
+
269
+ 用途:两张(或多张)并列内容面板,点击顶部分页签切换显示(holdFinancialProducts「代销产品 / 自营产品」)。与 §6 billMode 不同:本范式**没有**模式切换,activeTab 只控制面板显隐,最轻量,是手写 Tab 的 canonical 形态。
270
+
271
+ 数据模型:inputParams 加 `activeTab`(string,defaultValue `"1"`,desc 注明 singleMode 时写死 `1`)。
272
+
273
+ 根节点 onReady(customAction)——初始化 + 注册 globalSet 监听 + 深路径 $set helper(来源 holdFinancialProducts):
274
+
275
+ ```javascript
276
+ var self = this;
277
+ var p1Name = this.state.agencyProductInfo.name;
278
+ var p2Name = this.state.selfProductInfo.name;
279
+ var singleMode = !(p1Name && p2Name);
280
+ var activeTab = singleMode ? (p1Name ? '1' : '2') : '1';
281
+ this.setData({ singleMode, activeTab });
282
+ this.$set = function (key, val) {
283
+ var keys = Array.isArray(key) ? key : [key];
284
+ var obj = self.state;
285
+ for (var i = 0; i < keys.length - 1; i++) { obj = obj[keys[i]]; }
286
+ obj[keys[keys.length - 1]] = val;
287
+ self.setData(self.state);
288
+ };
289
+ this.addEventListener('globalSet', function (event) {
290
+ var data = event.data;
291
+ self.$set(data.key, data.value);
292
+ });
293
+ ```
294
+
295
+ 要点:onReady 里算 `singleMode` / 初始 `activeTab` 后再 `setData`;`$set` + `globalSet` 监听给**深层嵌套子组件**改根 state 用(§4)。顶层签头就近改 activeTab 可直用 `setData`,不必 globalSet。
296
+
297
+ Tab 签头(Row / 包一个 Text)——点击事件切 tab(指针安全:首行 `var self = this`,用 `self`):
298
+
299
+ ```javascript
300
+ // 签头 click 处理器(代销 / 自营各一个,结构相同)
301
+ var self = this;
302
+ self.setData({ activeTab: '1' }); // 签头 1(代销产品)
303
+ // self.setData({ activeTab: '2' }); // 签头 2(自营产品)
304
+
305
+ 签头视觉态——字体 color / fontSize / fontWeight 用三元随 activeTab 变(来源 holdFinancialProducts):
306
+
307
+ ```javascript
308
+ // Text.style.font.color(itemValueFrom:"1",带 itemDefaultValue)
309
+ $activeTab == '1' ? '#111' : '#666'
310
+ // fontSize
311
+ $activeTab == '1' ? 16 : 15
312
+ // fontWeight
313
+ $activeTab == '1' ? 600 : 400
314
+ ```
315
+
316
+ 签头指示条(选中态下划线)——背景图随 activeTab 切换(backgroundType 02,backgroundImage 用 `itemValueFrom:"1"` 表达式算 URL,见 §8):
317
+
318
+ ```javascript
319
+ // Row.style.background.backgroundImage(itemValueFrom:"1")
320
+ $activeTab == '1'
321
+ ? 'https://.../tab1_indicator.png'
322
+ : 'https://.../tab2_indicator.png'
323
+ ```
324
+
325
+ 内容面板——用 v-show 切显隐,condition.expression 直接绑 activeTab(纯 Tab 无 billMode,无需 AND 模式判断):
326
+
327
+ ```javascript
328
+ // 面板 1(代销产品)v-show condition.expression
329
+ $activeTab == '1'
330
+ // 面板 2(自营产品)v-show condition.expression
331
+ $activeTab == '2'
332
+ ```
333
+
334
+ 要点:
335
+ - `this.setData({ activeTab })` 在任意组件调用都会更新根 state → 所有 `$activeTab` 绑定(签头视觉态、指示条、面板 v-show)实时重算重渲染。
336
+ - 纯 Tab 比 billMode 简单:面板表达式只需 `$activeTab == '1'`,**不要**画蛇添足 AND `$billMode`(本范式无 billMode)。
337
+ - 含 singleMode 时(只有一个产品),onReady 把 activeTab 写死对应那份;签头可整体 v-show 隐藏第二份。
338
+ - 来源:holdFinancialProducts(代销/自营双 Tab,本范式全部要素 export-proven)。
339
+
340
+ ## 8. 图片承载复杂背景 / 状态相关视觉(backgroundImage)
341
+
342
+ 用途:卡片 DSL 的元素样式(rect/flex/background 的颜色·渐变·圆角)表达力有限,无法还原的视觉效果——整页复杂背景(渐变+纹理+裁切)、选中态指示条、徽标装饰条——一律**用图片承载**,挂到容器的 `background`(itemId 300)上。来源 holdFinancialProducts 两类用法都已实测。
343
+
344
+ 样式落点:`style.properties[]` 里 itemId `"300"`、itemKey `"background"`,其 `itemChildren.properties`:
345
+
346
+ ```json
347
+ [
348
+ { "itemId": "001", "itemKey": "backgroundType", "itemValueFrom": "0", "itemValue": "02" },
349
+ { "itemId": "003", "itemKey": "backgroundImage", "itemValueFrom": "2", "itemValue": "https://.../complex_bg.jpg" }
350
+ ]
351
+ ```
352
+
353
+ 两类取值(对照 itemValueFrom 三态,见 expression_engine.md §2):
354
+
355
+ - **静态复杂背景(itemValueFrom:"2",资源路径)**:背景是固定整图时直接给 URL,无需表达式。holdFinancialProducts 的整页产品背景即用 `itemValueFrom:"2"` + 远程 `.jpg`(`https://uat-pkg.finmall.com/filestore/...`)。渲染层 `doReplaceUrl + doI18n` 解析,`@filestore/...` 别名或 `https` 均可;默认 position 50% 50%、size cover、repeat no-repeat。
356
+ - **状态相关视觉(itemValueFrom:"1",表达式算 URL)**:指示条 / 选中态装饰等随 activeTab、选中态切换时,backgroundImage 写成 `$` 表达式,在三态里归为「变量/表达式」,必须带 `itemDefaultValue`(undefined 兜底)。holdFinancialProducts 的 Tab 指示条即 `$activeTab == '1' ? urlA : urlB`(itemValueFrom:"1")。
357
+
358
+ 资源路径铁律(详见 generation_pitfalls.md §3):
359
+ - **用远程资源**:`@filestore/<path>`(平台别名,导出卡首选)或 `https://...`。本地相对路径(`gen-card-test.png` 之类)预览器解析不到 → 破图。
360
+ - **缺素材先占位**:手头无对应切片时,先放占位图或纯色,不卡生成(占位约定见 generation_pitfalls.md §3.1)。
361
+ - 远程整图背景优先 `backgroundType:"02"` + `backgroundImage`;不要用 `Image` 组件硬铺满(层级/裁切不如 background 干净)。
362
+
363
+ 要点:
364
+ - `backgroundImage` 的 `itemCondition` 是 `backgroundType.02`——只有 backgroundType 写成 `"02"` 时该属性才生效,生成新属性照抄同组件导出的 itemCondition。
365
+ - 规则性渐变若标注里有明确色标和方向,才可用 `backgroundType:"03"`(backgroundGradient)。弥散、光晕、噪点、不规则纹理等无法用基础组件可靠复现的效果一律视作位图:优先引用切图;缺失切图时先从画板局部裁切,仍不可用时再用占位图。
366
+ - 来源:holdFinancialProducts(`itemValueFrom:"2"` 整页复杂背景 jpg;`itemValueFrom:"1"` Tab 指示条 png 切换,均 export-proven)。
367
+
368
+ ## 9. 手风琴分组折叠(Accordion 分组展开/收起)—— icon 按钮切换
369
+
370
+ 用途:一组并列分组(如存款各品种:整存整取 / 零存整取 / 通知存款),每组有标题行 + 一个**箭头图标按钮**,点击该图标在「展开(显示组内明细)/ 收起(隐藏组内明细)」间切换;图标箭头随状态翻转(收起 `angle_down`、展开 `angle_up`)。与 §3 的「平铺列表显示更多」是两种不同交互——§3 是单一 `showAll` 控制整段显隐,本范式是**每组独立 `hide` 标志**控制**各组自己的**内容显隐。来源 DepositHolding(3 组静态手风琴,export-proven)。
371
+
372
+ 数据模型:每个分组对象自带 `hide` 布尔字段(**默认 `false` = 展开**,默认即露出明细;`true` = 收起)。
373
+
374
+ ```json
375
+ // data.json 里 listData1 / listData2 都是分组数组,每组:
376
+ { "groupName": "整存整取", "totalAmount": 15000, "hide": false,
377
+ "list": [ { "accountNum": "...", "name": "一年期整存整取", "money": 5000, "rate": 1.72, "date": "2023-01-01" } ] }
378
+ ```
379
+
380
+ 三组状态切换靠根 `activeTab`(`'1'`→`listData1`,`'2'`→`listData2`)在表达式里选对数组;本范式与 §7 纯 Tab 可叠加。
381
+
382
+ **① 箭头图标按钮**(组标题行里的 Icon,componentId 如 `icon_5u`):
383
+ - `icon` 属性(itemValueFrom `"1"`)随该组 `hide` 翻转箭头:
384
+
385
+ ```
386
+ ($activeTab == '1' ? $listData1 : $listData2)[0].hide ? 'angle_down' : 'angle_up'
387
+ ```
388
+
389
+ - `eventactions[0]`(点击事件,customAction)——翻转该组的 `hide`。**严格按铁律首行 `var self = this`、每条语句 `;`(见 rules.md §四)**:
390
+
391
+ ```javascript
392
+ var self = this;
393
+ var num = self.state.activeTab;
394
+ var itemKey = 'listData' + num;
395
+ var curIndex = 0; // 本组下标;若为 list 模板渲染则换成 self.state._index
396
+ var curVal = self.state[itemKey][curIndex].hide;
397
+ self.postMessage('globalSet', {
398
+ key: [itemKey, curIndex, 'hide'],
399
+ value: !curVal
400
+ });
401
+ ```
402
+
403
+ **② 组内明细面板**(包明细的 Column,componentId 如 `column_eb`):用 v-show 控制显隐——`showType:"02"`(满足条件**隐藏**)+ `condition.expression` 绑该组 `hide`:
404
+
405
+ ```json
406
+ // directives: [{ itemKey:"v-show", itemValueFrom:"0", itemValue:{}, itemChildren:{ properties:[
407
+ // { itemId:"001", itemKey:"showType", itemValueFrom:"0", itemValue:"02" },
408
+ // { itemId:"002", itemKey:"condition", itemValueFrom:"0",
409
+ // itemValue:{ "name":"($activeTab=='1'?$listData1:$listData2)[0].hide",
410
+ // "expression":"($activeTab=='1'?$listData1:$listData2)[0].hide" } }
411
+ // ]}}]
412
+ ```
413
+ `hide:false`(展开)→ 条件 false → showType 02 不隐藏 → 明细显示;`hide:true`(收起)→ 条件 true → 隐藏。与图标箭头方向一致。
414
+
415
+ 索引取法(两种,必选其一):
416
+ - **静态手风琴(DepositHolding 实测)**:分组块**手动重复** N 份(无 list 循环),每个图标按钮的 `curIndex` 写死 `0 / 1 / 2`,`icon` 绑定与内容 v-show 的 `[N]` 同步写死。组数固定且较少时首选。
417
+ - **列表模板渲染**:分组来自 `list` / v-for,则 `curIndex` 与 `[N]` 换成运行时索引——图标 click 用 `self.state._index`(activityRight 同款),`icon` 绑定与 v-show 条件用 `$_index`。组数不定时首选。
418
+
419
+ globalSet 深路径翻转机制见 §4(`postMessage('globalSet', { key:[...路径数组], value })` + 根 `onReady` 注册 `globalSet` 监听 + `$set` helper)。**注意**:§4 的示例代码为早期精简写法(未强制 `self` / 部分语句无 `;`),新生成请一律按上方「铁律」形式写——`var self = this` 起手、`self` 访问实例、每条语句 `;` 收尾。
420
+
421
+ 要点:
422
+ - `hide` 是**每组独立**布尔(不是 §3 的全局 `showAll`);图标箭头 `angle_down/angle_up` 是折叠/展开的视觉提示,直接绑 `hide` 三元,零额外状态。
423
+ - 图标按钮 click 必须走 `globalSet` 深路径(子组件改不了根 state,见 §4),`key` 用 `[itemKey, curIndex, 'hide']` 路径数组精确命中某一组的 `hide`。
424
+ - `v-show showType:"02"` 的语义是「满足条件**隐藏**」(不是显示),条件直接写 `[curIndex].hide` 即「收起时隐藏」。别误写成 `01`(那会变成收起时反而显示)。
425
+ - 来源:DepositHolding(3 组静态手风琴:`icon_5u/_74/_c3` 三个箭头按钮 + `column_eb` 等明细面板,均 export-proven)。
426
+
427
+ ## 10. Canvas 甜甜圈 / 环形比例图(donut chart)
428
+
429
+ 用途:UI 出现**环形比例图 / 甜甜圈图 / 占比环**(中间镂空、各扇区按占比着色)——卡片 DSL 无图表组件,一律用 **Canvas 自绘**。甜甜圈 = 在每个扇区画「外圆弧 + 内圆弧反向 + 闭合」的环形段(donut wedge),而非实心饼。来源 assetOverview(`canvas_4d` 140×140 甜甜圈,export-proven)。
430
+
431
+ **数据契约(两种取法,二选一)**:
432
+ - 推荐:原始值进 `listData`(如 `money`),`onReady` 里把占比 `ratio` 算好写回每项,甜甜圈读 `ratio`。这样中心总额、图例、Tooltip 都能复用同一份占比,且金额参与绘图/计算时拿的是数字(见 §5 反向解析)。
433
+ - 或:`listData` 直接给 `ratio`(0~1 或 0~100),甜甜圈直接读。
434
+
435
+ ```javascript
436
+ // onReady(customAction)—— 原始 money → 占比 ratio 写回 state(严格按铁律:var self=this + 每条 ; )
437
+ var self = this;
438
+ var total = self.state.listData.reduce(function (t, e) { return t + Number(e.money); }, 0);
439
+ self.state.listData.forEach(function (e) { e.ratio = Number(e.money) / total; });
440
+ self.state.totalAmount = total.toFixed(2);
441
+ self.setData(self.state);
442
+ ```
443
+
444
+ **Canvas 结构**:
445
+ - `Canvas` 容器 rect 设成**正方形**(如 140×140),`flex.flexShrink:"0"`(防止被压缩变形);background 透明(backgroundType 01 + 空 backgroundColor)。
446
+ - eventactions[0]:eventKey=`onMeasured`(初始化渲染)、actionType=`customAction`——**所有绘图都在 onMeasured 里用 `event.canvas` 完成**(Canvas 缺 onMeasured 会 lint 报错且一片空白,见 component_rules.md)。
447
+
448
+ onMeasured 绘图(甜甜圈核心——双弧环形段):
449
+
450
+ ```javascript
451
+ var self = this;
452
+ function drawDonut(data, canvas, width, height) {
453
+ var ctx = canvas.getContext('2d');
454
+ canvas.width = width; // 关键:缓冲 = 显示尺寸(设计 px),避免糊
455
+ canvas.height = height;
456
+ ctx.imageSmoothingEnabled = true;
457
+ ctx.imageSmoothingQuality = 'high';
458
+ var colors = ['#46A5FF', '#43D84C', '#FFDC51', '#FF6A47'];
459
+ var total = data.reduce(function (acc, v) { return acc + v; }, 0);
460
+ var cx = canvas.width / 2;
461
+ var cy = canvas.height / 2;
462
+ var outerR = width / 2;
463
+ var innerR = width / 3; // 内半径 = 外半径的 1/3 → 镂空甜甜圈
464
+ var start = 0;
465
+ data.forEach(function (v, i) {
466
+ var slice = v / total * Math.PI * 2;
467
+ var end = start + slice;
468
+ ctx.beginPath();
469
+ ctx.arc(cx, cy, outerR, start, end); // 外圆弧(顺时针)
470
+ ctx.arc(cx, cy, innerR, end, start, true); // 内圆弧(逆时针回描)
471
+ ctx.closePath(); // 闭合 → 环形段
472
+ ctx.fillStyle = colors[i % colors.length]; // 调色板按 index 循环
473
+ ctx.fill();
474
+ start = end;
475
+ });
476
+ }
477
+ var ratios = [];
478
+ for (var i = 0; i < self.state.listData.length; i++) {
479
+ ratios.push(parseFloat(self.state.listData[i].ratio));
480
+ }
481
+ drawDonut(ratios, event.canvas, event.width, event.height);
482
+ ```
483
+
484
+ 要点:
485
+ - **环形段画法**:`arc(外)` 正向 + `arc(内, …, true)` 反向 + `closePath()` 是关键三连;只画一条 `arc` 是实心饼,必须两条弧回描才出环。
486
+ - **防糊**:`canvas.width = width; canvas.height = height`(width=event.width 即设计 px),否则缓冲默认 300×150 会被缩放糊掉。这也**修正了 §1 的坑**——§1 说「别用 canvas.width/2 做左对齐(默认 300 是设备 px 假设)」;本范式先把缓冲设成设计 px,`canvas.width/2` 才是真正的设计稿中心,居中/对称都准。
487
+ - **坐标系**:`event.canvas` 是设计稿坐标系包装对象(缩放已换算),绘图用**逻辑设计 px**,**不要乘 devicePixelRatio**。圆心 `canvas.width/2`、半径 `width/2` 都在设计 px 内,自洽。
488
+ - **中心文字**:甜甜圈中间的总额/标题是**另叠一个绝对定位 Text**(不在 canvas 里画),便于用 DSL 文本样式与千分号(§5)。canvas 只负责环。
489
+ - **调色板**:`colors[i % colors.length]` 让扇区数超过调色板长度时循环取色;配色建议按 design 规范 Token 化(见 amount_formatting.md 配色约束),勿硬编码裸色(除非如本卡上游既定)。
490
+ - **code 与 compiled 两份同步**:compiled 是 var 形式(箭头函数会被转 function),导出前两者必须一致。
491
+ - 铁律对齐:首行 `var self = this`、嵌套回调一律 `self`、每条语句 `;` 收尾(见 rules.md §四)。
492
+ - 来源:assetOverview(`onReady` 算 ratio + `canvas_4d` 140×140 甜甜圈,export-proven)。同思路可画进度环、半圆环(改 start 角与扫过的弧度范围即可)。
493
+
494
+ ### 环形倒计时(Canvas 自绘:进度环 0%→100% 步进 + 中心数字)
495
+
496
+ 用途:UI 出现**环形倒计时 / 进度环**(同心圆环 + 中心数字 + 单位如 S/秒)——如转账结果页倒计时。普通 Text/样式无法表达环形进度动画,**必须 Canvas 自绘**。来源 transferDetail(v3 `countdown` 语义块自动生成,export-proven)。
497
+
498
+ **识别信号**(v3 `extractSemantics` 自动判定,纯几何/格式):① ≥2 个近似正方形 shape 两两同心 → 圆环组;② 环中心 ±(半径+8) 内独立数字文本(fs≥20,1-3 位纯数字);③ 数字附近单位短文本(S/s/秒/分)。命中后数字/单位文本自动从常规渲染排除。
499
+
500
+ **Canvas 结构**:直径=环形尺寸,flexShrink:0,背景透明(backgroundType 01 + 空 backgroundColor);eventactions[0]=`onMeasured`(初始化渲染 + 启动步进定时器)。
501
+
502
+ ```javascript
503
+ // onMeasured(customAction)—— 画环 + 中心数字 + setInterval 1s 步进
504
+ var self = this;
505
+ // 总秒数/已流逝秒数来自绑定字段(inputParams + data.json),外部可配置;缺省兜底 10/0
506
+ self.state.countdownElapsed = self.data.countdownElapsed != null ? self.data.countdownElapsed : 0;
507
+ self.state.countdownTotal = self.data.countdownTotal != null ? self.data.countdownTotal : 10;
508
+ self.setData(self.state);
509
+ function drawRing(canvas, w, h, elapsed, total) {
510
+ var ctx = canvas.getContext("2d");
511
+ canvas.width = w; canvas.height = h;
512
+ ctx.clearRect(0, 0, w, h);
513
+ var cx = w / 2, cy = h / 2;
514
+ var radius = Math.min(w, h) / 2 - 4;
515
+ var ratio = total > 0 ? elapsed / total : 0;
516
+ if (ratio > 1) { ratio = 1; }
517
+ ctx.lineWidth = 4;
518
+ ctx.beginPath();
519
+ ctx.arc(cx, cy, radius, 0, Math.PI * 2); // 背景环(浅色)
520
+ ctx.strokeStyle = "#E6F8F0";
521
+ ctx.stroke();
522
+ if (ratio > 0) {
523
+ ctx.beginPath();
524
+ ctx.arc(cx, cy, radius, -Math.PI / 2, -Math.PI / 2 + ratio * Math.PI * 2); // 进度弧(从12点方向顺时针)
525
+ ctx.strokeStyle = "#06AB67";
526
+ ctx.lineCap = "round";
527
+ ctx.stroke();
528
+ }
529
+ var remaining = Math.max(0, total - elapsed);
530
+ ctx.fillStyle = "#06AB67";
531
+ ctx.font = "28px sans-serif";
532
+ ctx.textAlign = "center";
533
+ ctx.textBaseline = "middle";
534
+ ctx.fillText(String(remaining).padStart(2, "0"), cx - 4, cy); // 中心剩余秒数(两位数补零)
535
+ ctx.font = "12px sans-serif";
536
+ ctx.fillStyle = "#999999";
537
+ ctx.fillText("S", cx + 20, cy + 8); // 单位(与数字右缘留 3-4px,勿用 cx+14 会重叠)
538
+ }
539
+ drawRing(event.canvas, event.width, event.height, self.state.countdownElapsed, self.state.countdownTotal);
540
+ if (self._cdTimer) { clearInterval(self._cdTimer); }
541
+ self._cdTimer = setInterval(function () {
542
+ var e = (self.state.countdownElapsed || 0) + 1;
543
+ var tot = self.data.countdownTotal != null ? self.data.countdownTotal : 10;
544
+ if (e > tot) { clearInterval(self._cdTimer); return; }
545
+ self.state.countdownElapsed = e;
546
+ self.setData(self.state);
547
+ drawRing(event.canvas, event.width, event.height, e, tot);
548
+ }, 1000);
549
+ ```
550
+
551
+ 要点:
552
+ - **进度语义**:`ratio = elapsed/total`(已消耗比例),0% 空环 → 100% 满环;中心显示 `total - elapsed`(10→0)。
553
+ - **重绘机制**:onMeasured 是生命周期只触发一次,定时器内必须**手动调 drawRing 重绘**,不依赖渲染器自动重触发。
554
+ - **配色**:背景环浅色、进度弧深色、中心数字高对比;v3 从环形组 border 色自动提取(非 #F0F0F0 描边 → 进度色)。
555
+ - 坐标设计 px 勿乘 DPR;code/compiled 同步;铁律:`var self = this` + 每条 `;` 收尾。
556
+
557
+ ## 11. 角标 / 角标签(AbsoluteLayout 叠加小标)
558
+
559
+ 设计稿出现贴在内容块**角落的小标签**(如「付款账户」「收款账户」「NEW」、折扣标、状态标)→ DSL 无 badge 组件,用 **AbsoluteLayout 叠加**实现。
560
+
561
+ 机制(renderer/export-proven:transferPlan `absolutelayout_79`):
562
+ - AbsoluteLayout 的子元素**全部 `position:absolute` 脱标**(见 `component_specs.md` AbsoluteLayout),互相重叠。
563
+ - 容器 `align`(TopLeft/TopCenter/TopRight/CenterLeft/Center/CenterRight/BottomLeft/BottomCenter/BottomRight)决定整组子元素停靠哪个角/边;**留空 = TopLeft 默认**。
564
+ - 在 AbsoluteLayout 内放 ① 基础内容(一个满尺寸子元素,如 Column 100%×H、flex column 居中)+ ② 角标(一个小尺寸 Text/Image 子元素)。两者重叠,角标随 `align` 浮在对应角上。
565
+ - 角标样式 = 一个「胶囊/标签」:
566
+ - `borderRadius` 用**非对称圆角**做折叠角签感:如 `borderRadiusType:"1"`、`borderTopLeftRadius:8`、`borderTopRightRadius:0`、`borderBottomRightRadius:8`、`borderBottomLeftRadius:0`(左上+右下圆)。统一小圆角也可。
567
+ - `background`:`backgroundType:"01"` + `backgroundColor`(如浅橙 `rgba(255,240,225,1)`);`border.isBorder:"0"`。
568
+ - `font.color` 橙 `rgba(255,132,1,1)`、`fontSize:10`、`fontWeight:400`。
569
+ - `padding`:`paddingType:"1"`,`paddingLeft/Right:5`、`paddingTop/Bottom:1`。
570
+ - 文案静态(`itemValueFrom:"0"`)或数据驱动(`itemValueFrom:"1"` 表达式)。
571
+
572
+ 导出卡实例(transferPlan 付款账户块):
573
+ - `absolutelayout_79`(width 100%、height 140、`align:""`)内含:
574
+ - `column_7e`(100%×140,column/center/gap4/padding2):`image_6p`(bankIcon 28×28)+ `text_6z`(bankName)+ `text_74`(尾号);
575
+ - `text_7k` = "付款账户",即上面那条橙色胶囊样式 → 重叠在左上角的角标。
576
+ - 收款账户块复用同一范式(角标文案 "收款账户")。
577
+
578
+ ```jsonc
579
+ // 角标容器(AbsoluteLayout,作为内容区外层,align 选角)
580
+ { "componentId": "absolutelayout_xx", "className": "AbsoluteLayout",
581
+ "properties": [ { "itemId":"001", "itemKey":"align", "itemValueFrom":"0", "itemValue":"TopLeft" } ],
582
+ "style": { "properties": [ { "itemId":"200","itemKey":"rect","itemValue":{},"itemChildren":{ "properties":[
583
+ { "itemId":"003","itemKey":"width","itemValueFrom":"0","itemValue":"100%" },
584
+ { "itemId":"004","itemKey":"height","itemValueFrom":"0","itemValue":"140" } ] } } ] },
585
+ "slots": [ { "slotKey":"default","slotScope":false,"components":[
586
+ // ① 基础内容(满尺寸,按需替换)
587
+ { "componentId":"column_xx","className":"Column","style":{...100%×140 column center...},"slots":[{...}] },
588
+ // ② 角标(小尺寸,重叠在 align 角)
589
+ { "componentId":"text_badge","className":"Text",
590
+ "style":{ "properties":[
591
+ { "itemId":"1100","itemKey":"font","itemValue":{},"itemChildren":{ "properties":[
592
+ { "itemId":"001","itemKey":"color","itemValueFrom":"0","itemValue":"rgba(255,132,1,1)" },
593
+ { "itemId":"002","itemKey":"fontSize","itemValueFrom":"0","itemValue":"10" },
594
+ { "itemId":"004","itemKey":"fontWeight","itemValueFrom":"0","itemValue":"400" } ] } },
595
+ { "itemId":"300","itemKey":"background","itemValue":{},"itemChildren":{ "properties":[
596
+ { "itemId":"001","itemKey":"backgroundType","itemValueFrom":"0","itemValue":"01" },
597
+ { "itemId":"002","itemKey":"backgroundColor","itemValueFrom":"0","itemValue":"rgba(255,240,225,1)" } ] } },
598
+ { "itemId":"600","itemKey":"borderRadius","itemValue":{},"itemChildren":{ "properties":[
599
+ { "itemId":"001","itemKey":"borderRadiusType","itemValueFrom":"0","itemValue":"1" },
600
+ { "itemId":"003","itemKey":"borderTopLeftRadius","itemValueFrom":"0","itemValue":"8" },
601
+ { "itemId":"004","itemKey":"borderTopRightRadius","itemValueFrom":"0","itemValue":"0" },
602
+ { "itemId":"005","itemKey":"borderBottomRightRadius","itemValueFrom":"0","itemValue":"8" },
603
+ { "itemId":"006","itemKey":"borderBottomLeftRadius","itemValueFrom":"0","itemValue":"0" } ] } },
604
+ { "itemId":"400","itemKey":"padding","itemValue":{},"itemChildren":{ "properties":[
605
+ { "itemId":"001","itemKey":"paddingType","itemValueFrom":"0","itemValue":"1" },
606
+ { "itemId":"003","itemKey":"paddingLeft","itemValueFrom":"0","itemValue":"5" },
607
+ { "itemId":"004","itemKey":"paddingTop","itemValueFrom":"0","itemValue":"1" },
608
+ { "itemId":"005","itemKey":"paddingRight","itemValueFrom":"0","itemValue":"5" },
609
+ { "itemId":"006","itemKey":"paddingBottom","itemValueFrom":"0","itemValue":"1" } ] } } ] },
610
+ "properties":[ { "itemId":"001","itemKey":"value","itemValueFrom":"0","itemValue":"付款账户" } ],
611
+ "eventactions":[], "slots":[] }
612
+ ] } ] }
613
+
614
+ // 若设计稿角标是图片(活动标/折扣标):把上面的 text_badge 换成小尺寸 Image 子元素即可
615
+ ```
616
+
617
+ 要点:
618
+ - `align` 是角标停靠点:留空=TopLeft;换角改 `TopRight`/`BottomRight` 等。
619
+ - 角标必须是 AbsoluteLayout 内的**第二个小尺寸子元素**,与基础内容重叠;**不要**把它塞进基础内容的 Column 里(那会跟随流式布局,不再是角标)。
620
+ - 纯色角标用 `backgroundColor`;图片角标用 `Image`(缺素材见 `generation_pitfalls.md §3.1` 占位)。
621
+ - 配色按 design 规范 Token 化(见 `amount_formatting.md`),勿硬编码裸色,除非如本卡上游既定。
622
+ - 铁律对齐:角标无事件 `eventactions:[]`;若带点击,customAction 首行 `var self = this`、每条 `;`(见 `rules.md §四`)。
623
+ - 来源:transferPlan `absolutelayout_79`("付款账户")/ 同卡收款账户块("收款账户"),均 export-proven。
624
+
625
+ ## 12. 系统弹框 / 确认框 / 消息提示(alertDialog,非卡片)
626
+
627
+ 用途:设计稿中识别为**弹框 / 确认框 / 消息提示 / toast** 类画板(上游 `sketch-spec-to-card` 已判为 `artboardType=overlay_dialog`)。**这类不生成独立卡片**,而是「在触发它的父卡片对应交互中调用原生 `alertDialog` JSAPI」。
628
+
629
+ > 与 §2 `alertCard` 的区别:`alertCard` 是「富内容的子卡」(独立 Dialog 卡,带标题栏+关闭按钮,可能回传数据);`alertDialog` 是「平台原生简单弹框」(确认/告警/提示,通常不回传复杂数据)。上游已通过 `classify_artboard` 区分,下游按分类处理即可。
630
+
631
+ 调用(来源 budgetAnalysis 温馨提示、creditCardPayment 还款前二次确认,export-proven):
632
+
633
+ ```javascript
634
+ ynet.callJSAPI('alertDialog', {
635
+ title: '提示', message: '还款成功', okButton: '确定', cancelButton: '取消',
636
+ clickBgToHide: false, cancelGrayStyle: false
637
+ }, function (data) {
638
+ // data.ok: 是否点确定; data.dismissByClickBg: 是否点背景消失
639
+ if (data.ok) {
640
+ // 点确定后的后续动作(如发起还款 / 跳转)
641
+ }
642
+ });
643
+ ```
644
+
645
+ 要点:
646
+ - 入参:title / message / okButton(默认"确定") / cancelButton(空串不显示) / clickBgToHide(默认 false) / cancelGrayStyle(默认 false)。
647
+ - 出参:ok(boolean)、dismissByClickBg(boolean)。
648
+ - **纯提示 / toast 类(无取消键)**:省略 `cancelButton`;或改用 `showToast({ message: '余额不足', showIcon: '0' })`(约 2.2s 自动消失,showIcon: '0'错误/'1'正确/'2'不显示)。
649
+ - **视觉不必还原设计稿**,以平台原生弹框为准;文案(title/message/按钮)取自上游 `prompt.md §3` 提取结果。
650
+ - **不产出 schema.json**:上游 `prompt.md` 对该画板已是「交互提示文档」,明确声明不建卡。下游消费时,把本调用补到**父卡**(上游 `clusters.json.overlays[].parentKey` 或 `prompt.md §4` 指示的触发卡)的对应按钮 / 条件事件中。
651
+ - 参考存量:`budgetAnalysis`(温馨提示→alertDialog)、`creditCardPayment`(还款前二次确认→alertDialog)。片段见 `recipe_snippets/alertDialog-call.json`。
652
+
653
+ ## 13. 样式生成铁律(生成器通用:geo/v3 适用)
654
+
655
+ 用途:geo/v3 生成 schema.json 时,**必须遵守的样式格式约束**。违反任一条都会导致渲染器丢弃样式(表现为「UI 样式几乎全部丢失」——文本无色无大小、背景透明、TabBar 无底色)。
656
+
657
+ > **根因记录(2026-08-13)**:首轮生成 4 张校准卡全部「样式全丢」,排查发现渲染器对以下三条零容忍。
658
+
659
+ ### 13.1 颜色必须用 rgba 格式
660
+
661
+ 渲染器**不接受 hex 颜色值**(如 `#333333`、`#0079F2`)。所有 styleState 中的 color / backgroundColor 必须转为 `rgba(r,g,b,a)`:
662
+
663
+ ```
664
+ ✅ rgba(51,51,51,1) ← 渲染器接受
665
+ ❌ #333333 ← 被忽略,显示默认色
666
+ ```
667
+
668
+ 实现:`hexToRgba(hex, alpha=1)` 辅助函数,在 `makeText` / `makeIcon` / `makePrimaryButton` / `makeCardContainer` / `Root` 等所有设色点调用。
669
+
670
+ ### 13.2 Text 组件必须含 font 组
671
+
672
+ Text 的 `styleState.properties` 中**必须存在 font 分组**,且包含至少 fontSize 和 color:
673
+
674
+ ```json
675
+ {
676
+ "itemKey": "font",
677
+ "itemChildren": {
678
+ "properties": [
679
+ { "itemKey": "fontSize", "itemValue": "14" },
680
+ { "itemKey": "color", "itemValue": "rgba(51,51,51,1)" },
681
+ { "itemKey": "fontWeight", "itemValue": "400" }
682
+ ]
683
+ }
684
+ }
685
+ ```
686
+
687
+ 缺少 font 组 → `buildComponentFull` 不注入字体样式 → 文本回退到浏览器默认样式(无大小、无颜色差异)。
688
+
689
+ 实现:`ensureStyleGroup(node, 'font')` 在 `makeText()` 入口处强制补齐;`setStyleChild()` 写入具体属性值。
690
+
691
+ ### 13.3 容器组件必须含 background 组
692
+
693
+ Row / Column / Root 等容器若需背景色,styleState 中**必须存在 background 分组**:
694
+
695
+ ```json
696
+ {
697
+ "itemKey": "background",
698
+ "itemChildren": {
699
+ "properties": [
700
+ { "itemKey": "backgroundColor", "itemValue": "rgba(245,246,248,1)" }
701
+ ]
702
+ }
703
+ }
704
+ ```
705
+
706
+ 缺少 → 背景透明,灰底/白底卡片容器不可见。
707
+
708
+ ### 13.4 各组件设色清单
709
+
710
+ | 组件 | 设色点 | 默认值 |
711
+ |------|--------|--------|
712
+ | makeText | font.color | rgba(51,51,51,1) |
713
+ | makeIcon | font.color | 按传入参数 |
714
+ | makeTabBar Row | background.backgroundColor | #FFFFFF → rgba(255,255,255,1) |
715
+ | makeTabBar label | font.color | primary 色(如 #0079F2) |
716
+ | makeCardContainer | background.backgroundColor | #FFFFFF |
717
+ | makePrimaryButton | background.backgroundColor + font.color | #0079F2 底 + #FFFFFF 字 |
718
+ | Root | background.backgroundColor | #F5F6F8 |
719
+
720
+ ### 13.5 验证方法
721
+
722
+ 生成 schema 后执行 validate(0 错误),再通过离线渲染截图视觉检查(`render_card.mjs` 用 `vendor/preview` 构建产物 + 系统 Chrome CDP,用完即关,不依赖 `npm run dev`):
723
+
724
+ ```
725
+ node scripts/render_card.mjs --id <cardId> --width 375 --out render.png
726
+ ```
727
+ 截图应呈现:灰底白圆角容器、文本层次分明(标题深/链接蓝/辅助灰)、TabBar 有底色和标签色。
728
+
729
+ ## 14. Canvas 评分仪表盘(弧形进度环 + 中心分数)
730
+
731
+ 用途:半环/大半环形评分仪表盘(高亮弧表得分,中心大数字 + 小标签)。完整代码与标定方法见 `recipe_snippets/canvas-gauge-score.json`。
732
+
733
+ 要点(export-proven:assetAllocRadar):
734
+ - 圆心放画布内偏下,**底部缺口让圆被画布底边自然裁掉**,不算裁切角。
735
+ - 高亮弧起点 157°(标准 canvas 角,0°=3 点钟顺时针),扫角 = `score × (设计高亮角 ÷ 设计标定分)`(标定 70 分=160°);灰轨从 317° 扫 200°。
736
+ - 中心数字用 spec 标注的真实字体族(如 `500 28px "Helvetica Neue", "PingFang SC"`)——Canvas 可用系统字体,**Text 节点做不到的特殊字体优先走 Canvas**。
737
+ - 数字+单位同基线混排:measureText 两段的宽度 → 居中起点 → 两次 fillText。
738
+
739
+ ## 15. Canvas 五维雷达图(网格 + 渐变填充多边形)
740
+
741
+ 用途:多维雷达/蛛网图。完整代码见 `recipe_snippets/canvas-radar.json`。
742
+
743
+ 要点(export-proven:assetAllocRadar):
744
+ - 顶点角表 `[270,342,54,126,198]`(正上起顺时针五等分);环比例按设计实测(本例 `[1,0.714,0.449]`)。
745
+ - 数据多边形按 ratio 缩放顶点半径;**先填充(纵向渐变+描边)后画网格**,网格才压得住。
746
+ - 渐变沿数据包围盒纵向:`createLinearGradient(0, top, 0, bottom)`。
747
+ - 维度标签**不画进 canvas**,用 §16 标签列范式环绕布局,数据驱动。
748
+
749
+ ## 16. 维度标签列两范式(同行式 vs 堆叠式)
750
+
751
+ 用途:图表周围环绕多个「名称 pill + 金额 + 状态 tag」标签列。结构参数见 `recipe_snippets/label-col-modes.json`。
752
+
753
+ 要点(export-proven:assetAllocRadar 金额极限场景):
754
+ - **同行式**:金额+tag 同一 Row,用于金额短、横向空间够的列;**堆叠式**:金额独占一行、tag 另起一行居中,用于金额位数多的列。
755
+ - **同一张卡两种形态可并存,逐列按最长金额宽度判定**,不要一刀切。
756
+ - pill:h30、radius14、`rgba(255,240,225,0.5)` 底、padding 左8右4、14px w500 橙字 + 20x20 箭头切图。
757
+ - tag:0.5px 状态色 border、radius2、padding2、10px/10px 行高、颜色绑 `$catXStatusColor`。
758
+ - 标签列放 AbsoluteLayout,用 align+margin 停靠微调(只调本列无级联);容器高 ≥ 最大子列下沿。
759
+
760
+ ## 17. 透明度切换固定范式(opacity-switch,铁律 R57)
761
+
762
+ 用途:节点透明度(不透明度)需要**随状态变化**(选中/未选中、启用/禁用、点亮/未点亮等)。
763
+
764
+ **为什么必须用范式**:移动端渲染**不支持** `style.other.opacity` 走表达式(`itemValueFrom:"1"`)——表达式透明度只有预览环境可能生效,真机上不生效。所以「透明度会变的节点」一律改写成**多变体 + 容器 + 显隐切换**:
765
+
766
+ ```
767
+ Row|Column#<id>_box ← 宽高 = 被展示节点宽高;overflow:hidden;点击等事件挂这里
768
+ ├─ 变体1 opacity:"100"(静态) v-show: 条件1
769
+ ├─ 变体2 opacity:"50"(静态) v-show: 条件2
770
+ └─ …(用到的透明度取值有几个,变体就有几个)
771
+ ```
772
+
773
+ 硬约束(违反即真机表现错误):
774
+ 1. **变体数 = 用到的不同透明度取值数**;每个变体 `opacity` 必须是静态常量(`itemValueFrom:"0"`)。lint `r57-opacity-expression` ERROR 拦截表达式透明度。
775
+ 2. **容器宽高与被展示节点一致**(原节点 `rect.width/height` 搬到容器),并设 `other.overflow:"hidden"`——隐藏变体在部分渲染实现里只隐藏不脱流,靠裁切保证外部布局不被撑开。
776
+ 3. 容器显式归零渲染器默认样式:`flex.gap:"0"`、`flex.justifyContent:"center"`、`flex.alignItems:"center"`、`padding.paddingType:"0"`+`padding:"0"`、`background.backgroundType:"00"`。
777
+ 4. **事件全部上移到容器**,变体 `eventactions:[]`——隐藏变体不能吃点击、也不能重复绑定。
778
+ 5. 变体 v-show 条件**互斥且完备**(任一状态有且仅有一个变体可见);`condition.name` 必填且取业务短名(铁律 R52)。
779
+ 6. 变体保留原节点除 `opacity`/`margin` 外的全部样式与属性;原节点外边距搬到容器;原节点自身的 v-show 留在容器上。
780
+ 7. 容器继承原节点在外层的布局角色(space-between Row 里的 40×40 图标 → 容器 40×40 + `flexShrink:"0"`)。
781
+ 8. 特殊节点(ListLayout/GridLayout/MultiColumn/AbsoluteLayout/Canvas/Map/Video/ViewContainer)做变体时,v-show 不能直接设在它们上面(铁律 R53):每个变体先套一层 Row 承载 v-show,容器再套在这些包裹层外。
782
+ 9. 互斥 v-show 变体容器是 lint R35 的合法豁免(同一时刻只有一个变体在流里,定宽不累加)。
783
+ 10. **静态**透明度(不随状态变)不受本范式约束:直接写常量,或按 r057 §3 乘进颜色/渐变;只有「会变化的透明度」才走本范式。
784
+
785
+ 机械改写工具(可解析 `COND ? A : B` 与右嵌套三元;其他形态 exit 2 提示手工改):
786
+
787
+ ```bash
788
+ node scripts/apply_opacity_pattern.mjs --id <cardId> # 改写全部透明度表达式节点
789
+ node scripts/apply_opacity_pattern.mjs --id <cardId> --check # 只报告(有则 exit 1,可进 CI)
790
+ node scripts/apply_opacity_pattern.mjs --id <cardId> --node comp_8 --container Column
791
+ ```
792
+
793
+ 改写后三连:`validate_schema.py` + `lint_card.mjs` + `apply_opacity_pattern.mjs --check`。
794
+
795
+ 验证口径:**预览器不消费组件级 opacity**(截图看不出透明度差异),视觉效果只能真机核对;预览侧验证「结构等价」——改写前后 `render_card.mjs` 截图应逐字节一致(serviceEvaluation 已验证:10 个表情 Image 改写前后 default/选中态渲染均 byte-identical)。
796
+
797
+ 完整可复制子树见 `recipe_snippets/opacity-switch.json`(serviceEvaluation 表情档实例)。