cardbuddy 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/init.js +0 -2
- package/dist/preview-editor/assets/index-DPK_SisH.css +1 -0
- package/dist/preview-editor/assets/index-kaTaTm71.js +343 -0
- package/dist/preview-editor/index.html +2 -2
- package/dist/vendored/server/core/ai-chat.js +923 -138
- package/dist/vendored/server/core/browser.js +17 -0
- package/dist/vendored/server/core/cardFiles.js +49 -1
- package/dist/vendored/server/core/cardImage.js +103 -2
- package/dist/vendored/server/core/chatHistory.js +40 -0
- package/dist/vendored/server/core/screenshot.js +94 -0
- package/dist/vendored/skills/card-skill-trainer/SKILL.md +7 -7
- package/dist/vendored/skills/card-skill-trainer/scripts/plan_next_round.mjs +1 -1
- package/dist/vendored/skills/card-skill-trainer/scripts/run_round.mjs +7 -2
- package/dist/vendored/skills/dsl-card-generator/references/component_rules.md +14 -0
- package/dist/vendored/skills/dsl-card-generator/references/generation_pitfalls.md +48 -0
- package/dist/vendored/skills/dsl-card-generator/references/previewer_contract.md +21 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/iterate.mjs +140 -3
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/scaffold.mjs +48 -1
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/v3_assemble.mjs +41 -4
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/v3_extract.mjs +57 -6
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/v3_shared.mjs +17 -3
- package/dist/vendored/skills/dsl-card-generator/scripts/v3_regression.mjs +8 -1
- package/dist/vendored/skills/finmall-card-sync/scripts/api_batch_sync.mjs +46 -2
- package/dist/vendored/skills/mbank-design-gen/SKILL.md +76 -0
- package/dist/vendored/skills/mbank-design-gen/assets/icons/icon-apps.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/icons/icon-arrow-right-blue.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/icons/icon-check.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/icons/icon-settings.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/icons/icon-sound.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/icons/icon-voice-switch.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/icons/progress-thumb.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/icons/tag-bg.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/icons/vs-badge.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/ref/ref-chat-options.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/ref/ref-home-scene.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/ref/ref-product-detail.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/ref/ref-rate-table.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/assets/robot.png +0 -0
- package/dist/vendored/skills/mbank-design-gen/references/component-library.md +127 -0
- package/dist/vendored/skills/mbank-design-gen/references/design-spec.md +261 -0
- package/dist/vendored/skills/mbank-design-gen/templates/mockup-template.html +138 -0
- package/dist/vendored/skills/mbank-design-gen/tools/screenshot.mjs +34 -0
- package/dist/vendored/skills/shared/spec-contract.md +10 -1
- package/dist/vendored/skills/sketch-spec-to-card/SKILL.md +1 -1
- package/dist/vendored/skills/sketch-spec-to-card/scripts/parse_meaxure.py +30 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/test_text_only_artboard.py +25 -3
- package/dist/vendored/skills/sketch-spec-to-card/scripts/vision.py +15 -3
- package/dist/vendored/templates/helloCard/schema.json +62 -1
- package/dist/vendored/tools/finmall-env.mjs +1 -18
- package/package.json +1 -1
- package/dist/preview-editor/assets/index-BkbBxDTT.css +0 -1
- package/dist/preview-editor/assets/index-Bro07DTa.js +0 -313
|
@@ -1164,6 +1164,140 @@ export function persistReport(result, { sp, dp, irPath, reset = false, design =
|
|
|
1164
1164
|
}
|
|
1165
1165
|
}
|
|
1166
1166
|
|
|
1167
|
+
/**
|
|
1168
|
+
* 报告摘要:给 stdout(= 模型上下文)用,全文已在 persistReport 里落盘。
|
|
1169
|
+
*
|
|
1170
|
+
* 为什么必须摘要(2026-10-09 实测):原版 `JSON.stringify(result, null, 2)` 把整份报告灌进上下文,
|
|
1171
|
+
* 单轮 6~15KB,而模型每轮的决策依据只有「gate 过了没有 / match 多少 / 差在哪个节点」。
|
|
1172
|
+
* 体积大头全在它不需要的东西上:
|
|
1173
|
+
* - `contributors.elements[]`:每个热区逐节点度量(rect/h/hSibling/designH/flags),
|
|
1174
|
+
* 而提示词自己写着「一轮只改 contributors[0].elements[0]」—— 后面的热区本轮根本不看;
|
|
1175
|
+
* - `elements[]`(--elements 的逐元素实测)、`fidelity.missing[]`/`invented[]` 的全文清单:
|
|
1176
|
+
* 门禁只判 `.length === 0`,逐条文案对「该不该改样式」没有增量信息;
|
|
1177
|
+
* - `geometry.collapsed[]`/`overWrapped[]` 全文、`lint.warningDetails`、`preflight.details`。
|
|
1178
|
+
* 15 轮迭代 = 9~22 万字符常驻,且每轮都重发(宿主把历史工具结果原样带回)⇒ 上下文斜率被它单独拉高。
|
|
1179
|
+
*
|
|
1180
|
+
* 摘要必须保留的字段(S5 全部决策都基于这些,删一条就会「照着不存在的结论调像素」):
|
|
1181
|
+
* ok / gate(含 checks.failed.verdict.stage)/ pixelDiff.match / pixelDiff.size.heightDelta
|
|
1182
|
+
* / best / stalled / rounds / conclusion / contributors[0] / structuralGaps
|
|
1183
|
+
* / contentOffset1x+hint / widthMismatch / geometry.counts / 各 errors 计数
|
|
1184
|
+
* / readiness / diffImage / renderImage / report(全文路径,供 --detail 或 fs_read 取全文)
|
|
1185
|
+
*
|
|
1186
|
+
* 削峰的边界(刻意保留的「第一条 + 计数」):
|
|
1187
|
+
* - contributors 只留 [0],其余热区只报个数;
|
|
1188
|
+
* - missing / invented / collapsed 等清单只留**前 3 条文本 + 总数**——
|
|
1189
|
+
* 前几条往往就是漏得最刺眼的(最长文案 / 最显眼的标题),模型据此能定位;要看全量有 report 路径。
|
|
1190
|
+
*/
|
|
1191
|
+
export function summarizeReport(result) {
|
|
1192
|
+
if (!result || typeof result !== 'object') return result
|
|
1193
|
+
// 幂等:已封装的摘要原样放过。宿主侧会对旧版未封装的全量报告做兜底裁剪,
|
|
1194
|
+
// 二次裁剪会把 {total,sample} 当数组处理、把内容清空 —— 这是「封装过就别再动」的硬约束。
|
|
1195
|
+
if (result.summary === true) return result
|
|
1196
|
+
const r = result
|
|
1197
|
+
const pd = r.pixelDiff
|
|
1198
|
+
/**
|
|
1199
|
+
* 清单类字段:留前 3 条 + 总数(不是只留计数 —— 模型要靠具体文案判断漏了什么)。
|
|
1200
|
+
* 容忍两种输入:数组(全量)与 {total,sample}(已封装)—— 后者原样返回。
|
|
1201
|
+
*/
|
|
1202
|
+
const list = (arr, map = (x) => x) => {
|
|
1203
|
+
if (arr && !Array.isArray(arr) && typeof arr === 'object') return arr.total != null || arr.sample ? arr : undefined
|
|
1204
|
+
const a = Array.isArray(arr) ? arr : []
|
|
1205
|
+
if (!a.length) return undefined
|
|
1206
|
+
return { total: a.length, sample: a.slice(0, 3).map(map), more: a.length > 3 ? `…另 ${a.length - 3} 条(见 report 全文)` : undefined }
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
const out = {
|
|
1210
|
+
ok: r.ok,
|
|
1211
|
+
cardId: r.cardId,
|
|
1212
|
+
// 全文落盘路径放第一行:模型要看被削掉的内容时有确定入口,不必重跑 --detail
|
|
1213
|
+
report: r.report,
|
|
1214
|
+
conclusion: r.conclusion,
|
|
1215
|
+
rounds: r.rounds,
|
|
1216
|
+
best: r.best,
|
|
1217
|
+
stalled: r.stalled,
|
|
1218
|
+
gate: r.gate,
|
|
1219
|
+
iterationReset: r.iterationReset,
|
|
1220
|
+
appliedSets: r.appliedSets,
|
|
1221
|
+
validate: r.validate
|
|
1222
|
+
? { errors: r.validate.errors, warnings: r.validate.warnings, envError: r.validate.envError, details: r.validate.details }
|
|
1223
|
+
: undefined,
|
|
1224
|
+
lint: r.lint
|
|
1225
|
+
? {
|
|
1226
|
+
errors: r.lint.errors,
|
|
1227
|
+
warnings: r.lint.warnings,
|
|
1228
|
+
infos: r.lint.infos,
|
|
1229
|
+
details: r.lint.details,
|
|
1230
|
+
warningDetails: r.lint.warningDetails,
|
|
1231
|
+
}
|
|
1232
|
+
: undefined,
|
|
1233
|
+
preflight: r.preflight
|
|
1234
|
+
? { errors: r.preflight.errors, details: list(r.preflight.details, (d) => (typeof d === 'string' ? d : d?.message || JSON.stringify(d))) }
|
|
1235
|
+
: undefined,
|
|
1236
|
+
readiness: r.readiness,
|
|
1237
|
+
widthMismatch: r.widthMismatch,
|
|
1238
|
+
contentOffset1x: r.contentOffset1x,
|
|
1239
|
+
contentOffsetHint: r.contentOffsetHint,
|
|
1240
|
+
// 像素主体:只留决策要用的三件(match / 尺寸差 / 热区),inkBox / diffPixels / totalPixels 是中间量
|
|
1241
|
+
pixelDiff: pd
|
|
1242
|
+
? {
|
|
1243
|
+
match: pd.match,
|
|
1244
|
+
diffRatio: pd.diffRatio,
|
|
1245
|
+
size: pd.size,
|
|
1246
|
+
contentOffset: pd.contentOffset,
|
|
1247
|
+
structuralGaps: pd.structuralGaps,
|
|
1248
|
+
// 热区本身已在 contributors 里归因过,这里只留坐标供「还有哪些区没归因」的自查
|
|
1249
|
+
hotspots: Array.isArray(pd.hotspots) ? pd.hotspots.slice(0, 3) : pd.hotspots,
|
|
1250
|
+
hotspotsTotal: Array.isArray(pd.hotspots) ? pd.hotspots.length : pd.hotspotsTotal,
|
|
1251
|
+
}
|
|
1252
|
+
: undefined,
|
|
1253
|
+
structuralGaps: r.structuralGaps,
|
|
1254
|
+
// 分项归因:只留首条热区(提示词钦定「一轮只改 contributors[0].elements[0]」),其余只报个数
|
|
1255
|
+
contributors: Array.isArray(r.contributors) ? r.contributors.slice(0, 1) : r.contributors,
|
|
1256
|
+
contributorsTotal: Array.isArray(r.contributors) ? r.contributors.length : r.contributorsTotal,
|
|
1257
|
+
contributorsNote: r.contributorsNote,
|
|
1258
|
+
geometry: r.geometry
|
|
1259
|
+
? {
|
|
1260
|
+
counts: r.geometry.counts,
|
|
1261
|
+
collapsed: list(r.geometry.collapsed),
|
|
1262
|
+
outOfCard: list(r.geometry.outOfCard),
|
|
1263
|
+
overWrapped: r.geometry.counts?.overWrapped != null ? undefined : list(r.geometry.overWrapped),
|
|
1264
|
+
}
|
|
1265
|
+
: undefined,
|
|
1266
|
+
geometryNote: r.geometryNote,
|
|
1267
|
+
fidelity: r.fidelity
|
|
1268
|
+
? {
|
|
1269
|
+
total: r.fidelity.total,
|
|
1270
|
+
matched: r.fidelity.matched,
|
|
1271
|
+
missing: list(r.fidelity.missing),
|
|
1272
|
+
invented: list(r.fidelity.invented),
|
|
1273
|
+
slices: r.fidelity.slices,
|
|
1274
|
+
symbols: r.fidelity.symbols,
|
|
1275
|
+
shapes: r.fidelity.shapes,
|
|
1276
|
+
}
|
|
1277
|
+
: undefined,
|
|
1278
|
+
render: r.render,
|
|
1279
|
+
designImage: r.designImage,
|
|
1280
|
+
diffImage: r.diffImage,
|
|
1281
|
+
renderImage: r.renderImage,
|
|
1282
|
+
diffImageNote: r.diffImageNote,
|
|
1283
|
+
elapsedMs: r.elapsedMs,
|
|
1284
|
+
}
|
|
1285
|
+
if (r.error || r.message) {
|
|
1286
|
+
out.error = r.error
|
|
1287
|
+
out.message = r.message
|
|
1288
|
+
}
|
|
1289
|
+
// 被削掉的字段名写清楚:模型必须知道「摘要 ≠ 全量」,否则会把「没提到」误读成「不存在」
|
|
1290
|
+
// fs_read 现在支持 offset 续读(尾巴会给下一段起点),所以「读全文」的正解是照着 offset 读到底,
|
|
1291
|
+
// 而不是一遍 fs_read —— 一遍只拿得到一屏。
|
|
1292
|
+
out.summaryNote =
|
|
1293
|
+
'本报告为摘要(contributors 只留首条热区、要素/几何清单只留前 3 条 + 总数、逐元素度量已省略);' +
|
|
1294
|
+
'需要全量明细用 fs_read 读 report 路径(长报告会带下一段 offset,照抄续读),或重跑加 --detail。'
|
|
1295
|
+
// 幂等标记:宿主侧(ai-chat.js)会对未封装的旧版全量报告做一次兜底裁剪,
|
|
1296
|
+
// 见标记后它会原样放过 —— 否则二次裁剪会把已封装的 {total,sample} 当数组处理、把内容清空。
|
|
1297
|
+
out.summary = true
|
|
1298
|
+
return out
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1167
1301
|
/* ── 核心管线 ──────────────────────────────────────────────── */
|
|
1168
1302
|
async function runPipeline({ ir: irPath, out: outDir, design: designPath, width, session, alwaysDiffImage, withElements, sets, specPath, skipPreflight, slicesDir: slicesDirArg, reset: resetFlag, noReadiness: noReadinessArg }) {
|
|
1169
1303
|
const t0 = performance.now()
|
|
@@ -1476,7 +1610,7 @@ async function runPipeline({ ir: irPath, out: outDir, design: designPath, width,
|
|
|
1476
1610
|
|
|
1477
1611
|
/* ── CLI ───────────────────────────────────────────────────── */
|
|
1478
1612
|
function parseArgs(argv) {
|
|
1479
|
-
const out = { ir: '', out: '', design: '', width: 0, keepAlive: false, diffImage: false, elements: false, sets: [], spec: '', gate: false, id: '', skipPreflight: false, slicesDir: '', reset: false, noReadiness: false }
|
|
1613
|
+
const out = { ir: '', out: '', design: '', width: 0, keepAlive: false, diffImage: false, elements: false, sets: [], spec: '', gate: false, id: '', skipPreflight: false, slicesDir: '', reset: false, noReadiness: false, detail: false }
|
|
1480
1614
|
for (let i = 0; i < argv.length; i++) {
|
|
1481
1615
|
const a = argv[i]
|
|
1482
1616
|
if (a === '--ir') out.ir = argv[++i] || ''
|
|
@@ -1488,6 +1622,8 @@ function parseArgs(argv) {
|
|
|
1488
1622
|
else if (a === '--slices-dir') out.slicesDir = argv[++i] || ''
|
|
1489
1623
|
else if (a === '--id') out.id = argv[++i] || ''
|
|
1490
1624
|
else if (a === '--gate') out.gate = true
|
|
1625
|
+
// stdout 默认只回摘要(省上下文);--detail 才回全量报告(模型真要看逐元素度量时才加)
|
|
1626
|
+
else if (a === '--detail') out.detail = true
|
|
1491
1627
|
else if (a === '--no-preflight') out.skipPreflight = true
|
|
1492
1628
|
// 逃生门:跳过 S4.9 内容就绪门禁(debug 用;正常流程**不该**用它)
|
|
1493
1629
|
else if (a === '--no-readiness') out.noReadiness = true
|
|
@@ -1625,7 +1761,7 @@ async function main() {
|
|
|
1625
1761
|
}
|
|
1626
1762
|
|
|
1627
1763
|
if (!args.ir || !args.out) {
|
|
1628
|
-
console.error('用法: node iterate.mjs --ir <IR.json> --out <cardDir> [--design <design.png>] [--width 343]')
|
|
1764
|
+
console.error('用法: node iterate.mjs --ir <IR.json> --out <cardDir> [--design <design.png>] [--width 343] [--detail]')
|
|
1629
1765
|
process.exit(1)
|
|
1630
1766
|
}
|
|
1631
1767
|
|
|
@@ -1644,7 +1780,8 @@ async function main() {
|
|
|
1644
1780
|
noReadiness: args.noReadiness,
|
|
1645
1781
|
})
|
|
1646
1782
|
|
|
1647
|
-
|
|
1783
|
+
// 默认摘要:stdout = 模型上下文,全量报告已落盘在 report 字段指向的文件(见 summarizeReport 头注)
|
|
1784
|
+
console.log(JSON.stringify(args.detail ? result : summarizeReport(result)))
|
|
1648
1785
|
if (!result.ok) process.exit(1)
|
|
1649
1786
|
// 门禁未达标不算脚本失败(报告照常回喂),但退出码要让调用方一眼看出没收敛
|
|
1650
1787
|
if (result.gate && result.gate.passed === false) process.exitCode = 2
|
|
@@ -388,6 +388,53 @@ function inferInputParams(bindings) {
|
|
|
388
388
|
|
|
389
389
|
const INTENTS = ['blank', 'list', 'detail', 'form', 'dialog', 'tab', 'canvas-head']
|
|
390
390
|
|
|
391
|
+
/**
|
|
392
|
+
* 空白根样式:margin / padding / gap 显式归零,压掉渲染器对无样式容器的默认注入
|
|
393
|
+
* (padding:2px / gap:4px / justify-content:center)。形状与存量卡根节点一致
|
|
394
|
+
* (style-meta.json:marginType / paddingType,'0' = 全边统一值)。
|
|
395
|
+
* 同一形状还有两份内联副本(禁跨包 import):server/core/cardFiles.js 的 blankResetStyle、
|
|
396
|
+
* skills/finmall-card-sync api_batch_sync.mjs 的 buildBlankSchema —— 改这里必须同步改那两处。
|
|
397
|
+
*/
|
|
398
|
+
function blankResetStyle() {
|
|
399
|
+
return {
|
|
400
|
+
properties: [
|
|
401
|
+
{
|
|
402
|
+
itemId: '100',
|
|
403
|
+
itemKey: 'flex',
|
|
404
|
+
itemValueFrom: '0',
|
|
405
|
+
itemValue: {},
|
|
406
|
+
itemChildren: {
|
|
407
|
+
properties: [{ itemId: '003', itemKey: 'gap', itemValueFrom: '0', itemValue: '0' }],
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
itemId: '400',
|
|
412
|
+
itemKey: 'padding',
|
|
413
|
+
itemValueFrom: '0',
|
|
414
|
+
itemValue: {},
|
|
415
|
+
itemChildren: {
|
|
416
|
+
properties: [
|
|
417
|
+
{ itemId: '001', itemKey: 'paddingType', itemValueFrom: '0', itemValue: '0' },
|
|
418
|
+
{ itemId: '002', itemKey: 'padding', itemValueFrom: '0', itemValue: '0' },
|
|
419
|
+
],
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
itemId: '500',
|
|
424
|
+
itemKey: 'margin',
|
|
425
|
+
itemValueFrom: '0',
|
|
426
|
+
itemValue: {},
|
|
427
|
+
itemChildren: {
|
|
428
|
+
properties: [
|
|
429
|
+
{ itemId: '001', itemKey: 'marginType', itemValueFrom: '0', itemValue: '0' },
|
|
430
|
+
{ itemId: '002', itemKey: 'margin', itemValueFrom: '0', itemValue: '0' },
|
|
431
|
+
],
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
391
438
|
/**
|
|
392
439
|
* 装配一张合法的最小卡片骨架。
|
|
393
440
|
* @returns {{schema:object, data:object}}
|
|
@@ -402,7 +449,7 @@ export function buildCardSkeleton(intent, opts = {}) {
|
|
|
402
449
|
const view = {
|
|
403
450
|
width: '375',
|
|
404
451
|
className: 'Root',
|
|
405
|
-
style:
|
|
452
|
+
style: blankResetStyle(),
|
|
406
453
|
properties: [],
|
|
407
454
|
eventactions: [],
|
|
408
455
|
components: rootChildren,
|
|
@@ -544,6 +544,18 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
544
544
|
for (const cluster of sem.chartClusters || []) {
|
|
545
545
|
addSemanticBound(cluster.y, cluster.y + cluster.height, true)
|
|
546
546
|
}
|
|
547
|
+
// 富文本段落块(多行段落按视觉行重组、逐片保留字重):占据真实纵向空间,必须登记边界——
|
|
548
|
+
// 否则下游行(辅助文案/徽章行/按钮)的顶距只能从更早的语义底沿起算,逐段累积下漂
|
|
549
|
+
// (r265 实测:首段之后所有内容整体 +88px,全卡溢出 ~400px)。
|
|
550
|
+
for (const rb of sem.richBlocks || []) {
|
|
551
|
+
const box = rect(rb.container)
|
|
552
|
+
const lineBottomOf = (rb.lines || []).reduce((maxBottom, line) => Math.max(
|
|
553
|
+
maxBottom,
|
|
554
|
+
(rect(line[0]).y || 0) + Math.max(...line.map((f) => rect(f).height || 0)),
|
|
555
|
+
), 0)
|
|
556
|
+
const bottom = Math.max((rb.y || 0) + (box.height || 0), lineBottomOf)
|
|
557
|
+
addSemanticBound(rb.y, bottom, true)
|
|
558
|
+
}
|
|
547
559
|
for (const aux of sem.auxTexts || []) {
|
|
548
560
|
addSemanticBound(aux.y, textBoundBottom(aux.y, aux.height, aux.fontSize))
|
|
549
561
|
// 主视觉领行(文本左侧大切片):流式行高取主视觉高度;主视觉溢出卡顶时流式
|
|
@@ -2819,7 +2831,10 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
2819
2831
|
// 拓扑驱动:先渲染「实时到账」类 mid 文本,表单行随后,footer 文本(温馨提示/最大金额)留到按钮之后。
|
|
2820
2832
|
const formTopY = sem.formRows && sem.formRows.length
|
|
2821
2833
|
? Math.min(...sem.formRows.map((f) => f.label.y))
|
|
2822
|
-
|
|
2834
|
+
// buttons[] 是归一化对象(几何挂在 x/y/width/height,没有 .rect),rect() 读不到会恒取 0 →
|
|
2835
|
+
// formTopY 塌成 0 时 midAux 全空、全部辅助文案并进 footAux(顶距退化成「同子列上一行估算底沿」)。
|
|
2836
|
+
// 归一化字段优先、spec 元素 rect 兜底。
|
|
2837
|
+
: (sem.buttons.length ? (Number.isFinite(sem.buttons[0].y) ? sem.buttons[0].y : (rect(sem.buttons[0]).y || 0))
|
|
2823
2838
|
: (scatterBand ? scatterBand.pillTop : 9999))
|
|
2824
2839
|
const midAux = (sem.postAmountTexts || []).filter((t) => (t.y || 0) < formTopY)
|
|
2825
2840
|
const footAux = (sem.postAmountTexts || []).filter((t) => (t.y || 0) >= formTopY)
|
|
@@ -2837,7 +2852,7 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
2837
2852
|
}
|
|
2838
2853
|
return bottom
|
|
2839
2854
|
}
|
|
2840
|
-
const lineBottom = (pat) => (pat.y || 0) + (
|
|
2855
|
+
const lineBottom = (pat) => (pat.y || 0) + estimatedTextBoxHeight(pat.height, pat.fontSize || 14)
|
|
2841
2856
|
// 上方元素底沿:前一项行底;第一项用显式传入的 beforeBottom(主金额底/按钮底/表单底),否则 null(回退固定值)
|
|
2842
2857
|
function auxRowPadding(pat, prevPat, isLast, beforeBottom) {
|
|
2843
2858
|
const gaps = {}
|
|
@@ -3447,6 +3462,17 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
3447
3462
|
})
|
|
3448
3463
|
previousFormBottom = rowVisualBottom
|
|
3449
3464
|
}
|
|
3465
|
+
// 表单行认领的图标(左侧 icon / 值前导媒体 / 行尾媒体 / 右侧 affordance)已在本行渲染,
|
|
3466
|
+
// 必须从孤立媒体兜底池回收——否则同一个切片渲染两遍(重影 + duplication 掉分),
|
|
3467
|
+
// 且兜底行会另起一段流式内容把按钮整体下推(r282 实测:卡图标在账户行与按钮行各出现一次,
|
|
3468
|
+
// 按钮行被顶出卡片底、y≈716 单带像素差异 88%)。
|
|
3469
|
+
if (Array.isArray(sem.isolatedBlocks)) {
|
|
3470
|
+
const _rowClaimed = new Set(sorted.flatMap((p2) => [p2.leftIconSlice, p2.valueLeadSlice,
|
|
3471
|
+
p2.trailingIconSlice, p2.rightAffordanceSlice]).filter(Boolean))
|
|
3472
|
+
if (_rowClaimed.size) {
|
|
3473
|
+
sem.isolatedBlocks = sem.isolatedBlocks.filter((bl) => !_rowClaimed.has(bl.element))
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3450
3476
|
// Interleave dividers that fall between form rows into formHost
|
|
3451
3477
|
const formTopY = sorted[0].label.y
|
|
3452
3478
|
const formBottomY = Math.max(...sorted.map((fr) => Math.max(...[fr.value, ...(fr.subValues || []), fr.label]
|
|
@@ -3708,6 +3734,9 @@ if (subsActive.length && !sem._subAmountsConsumed) {
|
|
|
3708
3734
|
})()
|
|
3709
3735
|
// 单按钮行的水平内边距按 spec 按钮边缘推导(flex-end→右缘、flex-start→左缘),
|
|
3710
3736
|
// 通用卡 padding 会把按钮钉在错误内缩上(如壳内右锚按钮偏出 16px)。
|
|
3737
|
+
// 注意:只按断言侧给单边内缩。两侧同时写几何内缩会把 width:100% 的按钮挤窄
|
|
3738
|
+
// (右锚 195+16 内缩 → 内容盒只剩 132px → 文案换行溢出 textOverflow 门,
|
|
3739
|
+
// r276 于 33 例上实测回归)。按钮宽度由下面的「设计宽 vs 撑满行」分支决定。
|
|
3711
3740
|
const btnLeftPad = singleBtn && singleJustify === 'flex-start'
|
|
3712
3741
|
? Math.max(0, Math.round((singleBtn.x || buildCardLeft) - buildCardLeft))
|
|
3713
3742
|
: T.pad('card')
|
|
@@ -3733,7 +3762,12 @@ if (subsActive.length && !sem._subAmountsConsumed) {
|
|
|
3733
3762
|
if (sem.buttons.length > 1) {
|
|
3734
3763
|
setStyleChild(b, 'flex', 'flexShrink', '1')
|
|
3735
3764
|
setStyleChild(b, 'rect', 'width', '100%')
|
|
3736
|
-
|
|
3765
|
+
// 单按钮宽度:设计宽 < 卡宽 ⇒ 标注带显式设计宽(含「壳内按钮的内缩宽」),用设计宽 +
|
|
3766
|
+
// flexShrink:0;水平位置交给行内边距与 justify(见上,只给断言侧单边内缩)。
|
|
3767
|
+
// 旧判据用「卡宽 ×0.8」这条魔数比例,落在 80%~100% 区间的设计宽会被并进「撑满行」分支、
|
|
3768
|
+
// 宽度被抹成行内容盒宽(r273 实测 279/343 的按钮渲染成 311 满宽);
|
|
3769
|
+
// 而右锚按钮(flex-end)本就该用设计宽——撑满行会把按钮左缘推到卡片左缘。
|
|
3770
|
+
} else if (btn.width && btn.width < (sem.cardRect?.width || 375) - 2) {
|
|
3737
3771
|
setStyleChild(b, 'flex', 'flexShrink', '0')
|
|
3738
3772
|
setStyleChild(b, 'rect', 'width', String(Math.round(btn.width)))
|
|
3739
3773
|
} else {
|
|
@@ -3786,7 +3820,10 @@ if (subsActive.length && !sem._subAmountsConsumed) {
|
|
|
3786
3820
|
for (let i = 0; i < footAux.length; i++) {
|
|
3787
3821
|
const pat = footAux[i]
|
|
3788
3822
|
const isLast = i === footAux.length - 1
|
|
3789
|
-
|
|
3823
|
+
// 顶距锚点优先取「真实语义底沿」而非同子列上一行的估算底沿:两者之间可能夹着
|
|
3824
|
+
// 其它通道渲染的块(序号徽章行 / 富文本段落 / 分割线),用上一行估算底沿会把夹层
|
|
3825
|
+
// 高度重复计入顶距、逐段累积下漂(r265 实测每段 +34px)。取不到语义底沿才回退。
|
|
3826
|
+
const beforeBottom = previousSemanticBottom(pat.y) ?? (i > 0 ? lineBottom(footAux[i - 1]) : btnBottom)
|
|
3790
3827
|
const geom = auxRowPadding(pat, i > 0 ? footAux[i - 1] : null, isLast, beforeBottom)
|
|
3791
3828
|
// 水平位置同 midAux 纪律:左 padding 由 spec x 坐标驱动(几何锚定与对齐意图无关),
|
|
3792
3829
|
// 缺了会把左对齐页脚(金额说明/提示语)甩到卡片默认内缩处。
|
|
@@ -2511,6 +2511,10 @@ const floatDecos = []
|
|
|
2511
2511
|
if (formConsumed.has(t) || panelTextConsumed.has(t)) return false
|
|
2512
2512
|
if (subAmountEls.has(t)) return false
|
|
2513
2513
|
if (_panelGroupMemberEls.has(t)) return false
|
|
2514
|
+
// 序号徽章行成员(数字壳 + 右侧内容)已被 badgeRows 通道整体渲染:同一文本元素不得
|
|
2515
|
+
// 再被 listTitle 认领。否则徽章行内容二次渲染(重影),且 postAmountTexts 的 y 上界被
|
|
2516
|
+
// 这个假标题截断——假标题下方的段落/页脚文案整批静默丢失(r265 实测 5 段文案蒸发)。
|
|
2517
|
+
if (_badgeRowTextEls.has(t)) return false
|
|
2514
2518
|
// 复合行模式:行标题判定 = 候选下方 18-110px 有波段,且【另一文本】在相同相对距离(±4px)也有波段
|
|
2515
2519
|
// (行结构周期性重复)。卡标题(如「我的账户」距首波段 88px,无周期同伴)不受影响。
|
|
2516
2520
|
if (repeatRowMode) {
|
|
@@ -2918,6 +2922,9 @@ const floatDecos = []
|
|
|
2918
2922
|
}))
|
|
2919
2923
|
// 面板组成员:由面板列表模板整体接管
|
|
2920
2924
|
&& !_panelGroupMemberEls.has(t)
|
|
2925
|
+
// 序号徽章行成员(数字 + 右侧内容):行结构已由 badgeRows 通道还原,
|
|
2926
|
+
// 再当列表项标题会把同一行渲染两次(徽章行 + ListLayout 项重影)。
|
|
2927
|
+
&& !_badgeRowTextEls.has(t)
|
|
2921
2928
|
})
|
|
2922
2929
|
// 同 y(±10)的 first-col 文本聚合为一项;按 y 排序
|
|
2923
2930
|
// P0/P1 升级:列表项必须存在「标题下方 18-36px 的描述文本」(desc)——否则摘要区
|
|
@@ -3590,20 +3597,46 @@ const floatDecos = []
|
|
|
3590
3597
|
} : null
|
|
3591
3598
|
|
|
3592
3599
|
// —— 底部控制件:只用“按钮区下方 + 居中 + 小尺寸近似方形媒体”判定;低置信度的纯文本不建模。
|
|
3600
|
+
// 「折叠/展开控件」是**卡底**控件,判据必须锚在卡底区域,不能只锚「按钮区之下」:
|
|
3601
|
+
// 无按钮卡里 buttonShapes 为空会让 btnRegionBottom 塌成 0,于是卡中任何
|
|
3602
|
+
// 「小尺寸近方形 + 横向居中」的切片都被误认(如账户行左侧的圆形卡标,
|
|
3603
|
+
// r282 实测:同一图标在账户行与卡底各渲染一次,且卡底多出的那一行把
|
|
3604
|
+
// 底部按钮整体顶出内容区,单带像素差异 88%)。
|
|
3605
|
+
// 补一道真值门:切片必须落在卡内下缘带内(底沿不低于卡底 − 带高),
|
|
3606
|
+
// 且不得高于卡片内容区(文本/媒体)底沿——控件在内容之下,不在内容之中。
|
|
3593
3607
|
const collapseBtn = (() => {
|
|
3608
|
+
const cardRectR = rect(card)
|
|
3609
|
+
const cardBottom = (cardRectR.y || 0) + (cardRectR.height || 0)
|
|
3594
3610
|
const btnRegionBottom = buttonShapes.length
|
|
3595
3611
|
? Math.max(...buttonShapes.map((s) => (rect(s).y || 0) + (rect(s).height || 0)))
|
|
3596
3612
|
: 0
|
|
3613
|
+
// 卡内下缘带高:不超过卡高 22%,至少容得下控件自身高度
|
|
3614
|
+
const bottomBandHeight = Math.max(24, Math.round((cardRectR.height || 0) * 0.22))
|
|
3615
|
+
// 内容底沿:所有文本/媒体元素的最低底沿(归一化对象用 rect() 读不到时按 0 略过)
|
|
3616
|
+
let contentBottom = 0
|
|
3617
|
+
for (const t of texts) {
|
|
3618
|
+
const tr = rect(t)
|
|
3619
|
+
contentBottom = Math.max(contentBottom, (tr.y || 0) + (tr.height || 0))
|
|
3620
|
+
}
|
|
3621
|
+
for (const s of slices) {
|
|
3622
|
+
const sr = rect(s)
|
|
3623
|
+
contentBottom = Math.max(contentBottom, (sr.y || 0) + (sr.height || 0))
|
|
3624
|
+
}
|
|
3597
3625
|
const mediaSlice = slices.find((s) => {
|
|
3598
3626
|
const sr = rect(s)
|
|
3599
3627
|
const width = sr.width || 0
|
|
3600
3628
|
const height = sr.height || 0
|
|
3601
3629
|
const center = (sr.x || 0) + width / 2
|
|
3602
|
-
const cardCenter = (
|
|
3630
|
+
const cardCenter = (cardRectR.x || 0) + ((cardRectR.width || 0) / 2)
|
|
3631
|
+
const bottom = (sr.y || 0) + height
|
|
3603
3632
|
return width >= 12 && width <= 36 && height >= 12 && height <= 36
|
|
3604
3633
|
&& Math.abs(width - height) <= Math.max(10, width * 0.5)
|
|
3605
3634
|
&& (sr.y || 0) > btnRegionBottom
|
|
3606
|
-
|
|
3635
|
+
// 落在卡内下缘带内(真值门)
|
|
3636
|
+
&& bottom >= cardBottom - bottomBandHeight
|
|
3637
|
+
// 在内容之下而非内容之中:控件顶沿不得低于内容底沿减去自身高度容差
|
|
3638
|
+
&& (sr.y || 0) >= contentBottom - height - 4
|
|
3639
|
+
&& Math.abs(center - cardCenter) <= Math.max(24, (cardRectR.width || 0) * 0.12)
|
|
3607
3640
|
})
|
|
3608
3641
|
const textEl = null
|
|
3609
3642
|
if (!mediaSlice) return null
|
|
@@ -4018,8 +4051,9 @@ const orphanSlices = slices.filter((s) => {
|
|
|
4018
4051
|
}
|
|
4019
4052
|
|
|
4020
4053
|
// —— 壳内右列文本(两列拓扑)——
|
|
4021
|
-
// 内容壳(面板壳/渐变壳)右半区独立成列的文本:>=2
|
|
4022
|
-
// (x 差 <=8、分属不同行),壳内左侧另有 >=2
|
|
4054
|
+
// 内容壳(面板壳/渐变壳)右半区独立成列的文本:>=2 个文本左边界落在列界之后且互相成列
|
|
4055
|
+
// (x 差 <=8、分属不同行),壳内左侧另有 >=2 个文本、或存在前导媒体(整块图/大切片)→
|
|
4056
|
+
// 该壳是「左列 + 右列」拓扑而非竖排单列。列界取前导媒体右缘,无媒体才回退「壳宽 45%」。
|
|
4023
4057
|
// 此前右列文本或被压成竖排堆叠(panelRows 路径),或整段落进主视觉死区不渲染(渐变壳路径)。
|
|
4024
4058
|
const shellButtonTextEls = new Set(buttons.map((button) => button.sourceEl).filter(Boolean))
|
|
4025
4059
|
const shellRightColumns = []
|
|
@@ -4031,7 +4065,22 @@ const orphanSlices = slices.filter((s) => {
|
|
|
4031
4065
|
// 标题行(与壳内首个文本同 band)整体保留为整行:名称 + 紧邻短标签不是右列成员
|
|
4032
4066
|
const headerEls = inner.filter((t) => Math.abs((rect(t).y || 0) - topY) <= 8)
|
|
4033
4067
|
const headerSet = new Set(headerEls)
|
|
4034
|
-
|
|
4068
|
+
// 前导媒体(壳内左侧的整块图 / 大切片):高显著大于壳内行高、右缘落在壳左半区。
|
|
4069
|
+
// 右列左界此前固定取「壳宽 45%」——该比例隐含「前导媒体宽 ≈ 壳宽一半」的假设;
|
|
4070
|
+
// 媒体较窄时(80px 图 / 311px 壳)右列起点落在比例线左侧,右列文本整体被划到左列一侧
|
|
4071
|
+
// → left.length 恒 < 2 → 两列拓扑被整条判死,右列文本退化成全宽竖排行。
|
|
4072
|
+
// 有前导媒体时改用其右缘为列界(几何真值),无媒体才回退比例线。
|
|
4073
|
+
const innerHeights = inner.map((t) => rect(t).height || 0).sort((a, b) => a - b)
|
|
4074
|
+
const innerLineH = innerHeights[Math.floor(innerHeights.length / 2)] || 14
|
|
4075
|
+
const leadMedia = [...isolatedBlocks, ...slices]
|
|
4076
|
+
.filter((el) => thinInside(el, shape))
|
|
4077
|
+
.map((el) => rect(el))
|
|
4078
|
+
.filter((r) => (r.height || 0) >= Math.max(24, innerLineH * 1.8)
|
|
4079
|
+
&& (r.x || 0) + (r.width || 0) <= (sr.x || 0) + (sr.width || 0) * 0.5)
|
|
4080
|
+
.sort((a, b) => ((a.x || 0) + (a.width || 0)) - ((b.x || 0) + (b.width || 0)))[0] || null
|
|
4081
|
+
const splitX = leadMedia
|
|
4082
|
+
? Math.max((sr.x || 0) + 2, (leadMedia.x || 0) + (leadMedia.width || 0) + 2)
|
|
4083
|
+
: (sr.x || 0) + (sr.width || 0) * 0.45
|
|
4035
4084
|
const columns = []
|
|
4036
4085
|
for (const t of inner.filter((item) => !headerSet.has(item) && (rect(item).x || 0) >= splitX)
|
|
4037
4086
|
.slice().sort((a, b) => (rect(a).x || 0) - (rect(b).x || 0))) {
|
|
@@ -4047,7 +4096,9 @@ const orphanSlices = slices.filter((s) => {
|
|
|
4047
4096
|
if (!best) continue
|
|
4048
4097
|
const bestSet = new Set(best.items)
|
|
4049
4098
|
const left = inner.filter((t) => !headerSet.has(t) && !bestSet.has(t) && (rect(t).x || 0) < splitX)
|
|
4050
|
-
|
|
4099
|
+
// 左列成员可以是一张整块图而非文本(「产品图 + 右列信息」是常见形态):
|
|
4100
|
+
// 有前导媒体时左列无文本也成立,否则该形态永远进不了两列通道。
|
|
4101
|
+
if (left.length < 2 && !leadMedia) continue
|
|
4051
4102
|
shellRightColumns.push({
|
|
4052
4103
|
shapeEl: shape,
|
|
4053
4104
|
x: sr.x || 0, y: sr.y || 0, width: sr.width || 0, height: sr.height || 0,
|
|
@@ -179,8 +179,17 @@ export function fixFlexShrinkOverflow(root) {
|
|
|
179
179
|
const children = getChildren(node)
|
|
180
180
|
if (!isAutoSize && children.length >= 2) {
|
|
181
181
|
const allShrinkZero = children.every((child) => getStyleVal(child, 'flex', 'flexShrink') === '0')
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
// 「子节点全为固定 px 宽」= 这些宽度是设计稿逐个标注出来的(并排药丸/标签/统计块),
|
|
183
|
+
// 它们的和就是设计真值。此时**不得**为末位放开可缩点:药丸被压窄后内部文案换行、
|
|
184
|
+
// 行高撑高,连带 textOverflow 与 structure 一起掉(r284 实测:149+12+150 恰等于
|
|
185
|
+
// 组宽 311,却被压成两行,textOverflow 0.889 < 门 0.90)。
|
|
186
|
+
// 真正的「溢出兜底」场景是子节点里混有 auto/百分比宽(内容长度不可知),
|
|
187
|
+
// 那种才需要放开一个可缩点让内容有地方收缩。
|
|
188
|
+
const allFixedPx = children.every((child) => {
|
|
189
|
+
const cw = getStyleVal(child, 'rect', 'width')
|
|
190
|
+
return cw != null && /^\d+(\.\d+)?$/.test(String(cw))
|
|
191
|
+
})
|
|
192
|
+
if (allShrinkZero && !allFixedPx) {
|
|
184
193
|
setStyleVal(children[children.length - 1], 'flex', 'flexShrink', '1')
|
|
185
194
|
}
|
|
186
195
|
}
|
|
@@ -474,9 +483,14 @@ export function amountExpr(varName, srcText, opts = {}) {
|
|
|
474
483
|
const unit = JSON.stringify(unitMatch[2])
|
|
475
484
|
return `${sign} + ${abs} + ${unit}`
|
|
476
485
|
}
|
|
486
|
+
// 货币符号必须进表达式:spec 把「¥」与「5,638.00」拆成两层时,裸符号层会被合并段
|
|
487
|
+
// (mergeCurrencyLayer)吞掉,表达式是符号唯一的载体——不输出前缀 ⇒ 渲染只剩数字
|
|
488
|
+
// ("¥5,638.00" → "5,638.00")。前缀取自原文、运行时与负号拼接顺序固定(负号在前)。
|
|
489
|
+
// 纯数字原文("2000" 配相邻"万元")不写前缀,避免凭空加符号。
|
|
490
|
+
const cur = hasCurrency ? JSON.stringify(curPrefix) + ' + ' : ''
|
|
477
491
|
// 列表行模板共用一套表达式:货币前缀必须逐行数据驱动($amtCurN),否则首行符号套到全行
|
|
478
492
|
if (opts.prefixVar) return `${sign} + $${opts.prefixVar} + ${abs}`
|
|
479
|
-
return `${sign} + ${abs}`
|
|
493
|
+
return `${sign} + ${cur}${abs}`
|
|
480
494
|
}
|
|
481
495
|
|
|
482
496
|
export function amountValueExpr(varName, srcText, { currencyPrefix = true } = {}) {
|
|
@@ -63,6 +63,7 @@ if (argv.includes('--help') || argv.includes('-h')) {
|
|
|
63
63
|
|
|
64
64
|
const workRoot = path.join(REPO, '.cache', 'v3-regression')
|
|
65
65
|
const workCards = path.join(workRoot, 'src', 'cards')
|
|
66
|
+
const workSlices = path.join(workRoot, '__slices')
|
|
66
67
|
const baselineDir = path.join(REPO, 'training', 'pass1', 'baseline')
|
|
67
68
|
const node = process.execPath
|
|
68
69
|
// 同 gate.mjs:改为统一解析(此前 `process.env.PYTHON || 'python3'` 不认 DSL_CARD_PYTHON、不探测)
|
|
@@ -103,6 +104,10 @@ for (const c of cases) {
|
|
|
103
104
|
continue
|
|
104
105
|
}
|
|
105
106
|
// 1) 首轮生成(无 decisions)
|
|
107
|
+
// 切图落点必须一并密闭化:卡片产物已隔离到 .cache/v3-regression,但切图默认仍写 src/slices
|
|
108
|
+
// —— 金标 spec 来自 training/fixtures(含仓库未跟踪的切图名),跑一次回归就把它们混进
|
|
109
|
+
// 受跟踪资产目录(实测污染 src/slices 2 个未跟踪文件),并让 ci-gate 的源码污染守卫报错。
|
|
110
|
+
// 口径与 tools/regress-fidelity.mjs 一致。
|
|
106
111
|
const gen = run(node, [
|
|
107
112
|
path.join(SCRIPTS, 'spec_to_schema_v3.mjs'),
|
|
108
113
|
'--spec', specPath,
|
|
@@ -110,7 +115,9 @@ for (const c of cases) {
|
|
|
110
115
|
'--cardIndex', String(c.card),
|
|
111
116
|
'--out', workCards,
|
|
112
117
|
'--force',
|
|
113
|
-
]
|
|
118
|
+
], {
|
|
119
|
+
DSL_CARD_SLICES_DIR: workSlices,
|
|
120
|
+
})
|
|
114
121
|
if (!gen.ok) {
|
|
115
122
|
results.push({ regId: c.regId, pass: false, reason: 'generate 失败', detail: gen.output.slice(-600) })
|
|
116
123
|
continue
|
|
@@ -740,8 +740,52 @@ async function syncBlankCard(page, card, { cardDir, appId, workspaceId }) {
|
|
|
740
740
|
};
|
|
741
741
|
}
|
|
742
742
|
|
|
743
|
-
/**
|
|
743
|
+
/**
|
|
744
|
+
* 最小合法空白 schema:满足 validate_schema.py 的顶层必填(version/pageId/name/dictionary/view)。
|
|
745
|
+
* 根样式 margin / padding / gap 显式归零,压掉渲染器对无样式容器的默认注入
|
|
746
|
+
* (padding:2px / gap:4px / justify-content:center)——形状与存量卡根节点一致
|
|
747
|
+
* (style-meta.json:marginType / paddingType,'0' = 全边统一值)。
|
|
748
|
+
* 同一形状还有两份内联副本(禁跨包 import):server/core/cardFiles.js 的 blankResetStyle、
|
|
749
|
+
* skills/dsl-card-generator lib/scaffold.mjs —— 改这里必须同步改那两处。
|
|
750
|
+
*/
|
|
744
751
|
function buildBlankSchema(card) {
|
|
752
|
+
const blankResetStyle = () => ({
|
|
753
|
+
properties: [
|
|
754
|
+
{
|
|
755
|
+
itemId: '100',
|
|
756
|
+
itemKey: 'flex',
|
|
757
|
+
itemValueFrom: '0',
|
|
758
|
+
itemValue: {},
|
|
759
|
+
itemChildren: {
|
|
760
|
+
properties: [{ itemId: '003', itemKey: 'gap', itemValueFrom: '0', itemValue: '0' }],
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
itemId: '400',
|
|
765
|
+
itemKey: 'padding',
|
|
766
|
+
itemValueFrom: '0',
|
|
767
|
+
itemValue: {},
|
|
768
|
+
itemChildren: {
|
|
769
|
+
properties: [
|
|
770
|
+
{ itemId: '001', itemKey: 'paddingType', itemValueFrom: '0', itemValue: '0' },
|
|
771
|
+
{ itemId: '002', itemKey: 'padding', itemValueFrom: '0', itemValue: '0' },
|
|
772
|
+
],
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
itemId: '500',
|
|
777
|
+
itemKey: 'margin',
|
|
778
|
+
itemValueFrom: '0',
|
|
779
|
+
itemValue: {},
|
|
780
|
+
itemChildren: {
|
|
781
|
+
properties: [
|
|
782
|
+
{ itemId: '001', itemKey: 'marginType', itemValueFrom: '0', itemValue: '0' },
|
|
783
|
+
{ itemId: '002', itemKey: 'margin', itemValueFrom: '0', itemValue: '0' },
|
|
784
|
+
],
|
|
785
|
+
},
|
|
786
|
+
},
|
|
787
|
+
],
|
|
788
|
+
});
|
|
745
789
|
return {
|
|
746
790
|
version: '1.0.0',
|
|
747
791
|
pageId: String(card.cardId),
|
|
@@ -752,7 +796,7 @@ function buildBlankSchema(card) {
|
|
|
752
796
|
view: {
|
|
753
797
|
width: '375',
|
|
754
798
|
className: 'Root',
|
|
755
|
-
style:
|
|
799
|
+
style: blankResetStyle(),
|
|
756
800
|
properties: [],
|
|
757
801
|
eventactions: [],
|
|
758
802
|
components: [],
|