cardbuddy 0.15.0 → 0.16.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/preview-editor/assets/index-BkbBxDTT.css +1 -0
- package/dist/preview-editor/assets/{index-CIIfMlwI.js → index-Bro07DTa.js} +91 -88
- package/dist/preview-editor/index.html +2 -2
- package/dist/vendored/card-editor/runtime/adapter.js +2 -2
- package/dist/vendored/mcp/client.mjs +1 -1
- package/dist/vendored/server/core/ai-chat.js +251 -80
- package/dist/vendored/server/core/cardValidate.js +16 -5
- package/dist/vendored/skills/card-skill-trainer/SKILL.md +61 -6
- package/dist/vendored/skills/card-skill-trainer/scripts/diagnose_case.mjs +14 -82
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/diagnosis.mjs +207 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/layout-correction-sim.mjs +62 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/paradigm-router.mjs +76 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/pixel-worker-runner.mjs +31 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/pixel-worker.mjs +138 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/scoring.mjs +24 -20
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/telemetry.mjs +46 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/plan_next_round.mjs +4 -1
- package/dist/vendored/skills/card-skill-trainer/scripts/run_round.mjs +279 -90
- package/dist/vendored/skills/card-skill-trainer/scripts/score_round.mjs +74 -2
- package/dist/vendored/skills/card-skill-trainer/scripts/sink_rules.mjs +127 -0
- package/dist/vendored/skills/dsl-card-generator/SKILL.md +93 -27
- package/dist/vendored/skills/dsl-card-generator/references/dsl_structure.md +1 -15
- package/dist/vendored/skills/dsl-card-generator/references/expression_engine.md +11 -20
- package/dist/vendored/skills/dsl-card-generator/references/generation_pitfalls.md +169 -44
- package/dist/vendored/skills/dsl-card-generator/references/ir-format.md +87 -3
- package/dist/vendored/skills/dsl-card-generator/references/iteration_protocol.md +35 -6
- package/dist/vendored/skills/dsl-card-generator/references/quickref.md +28 -27
- package/dist/vendored/skills/dsl-card-generator/references/recipes.md +30 -8
- package/dist/vendored/skills/dsl-card-generator/references/rules.md +3 -2
- package/dist/vendored/skills/dsl-card-generator/references/scripts_reference.md +5 -2
- package/dist/vendored/skills/dsl-card-generator/references/slices_convention.md +2 -3
- package/dist/vendored/skills/dsl-card-generator/references/spec_to_schema_patterns.md +1 -4
- package/dist/vendored/skills/dsl-card-generator/scripts/add_component.mjs +5 -14
- package/dist/vendored/skills/dsl-card-generator/scripts/autofix_schema.py +88 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/check_fidelity.mjs +143 -42
- package/dist/vendored/skills/dsl-card-generator/scripts/check_topology.py +0 -35
- package/dist/vendored/skills/dsl-card-generator/scripts/expand_ir.mjs +99 -53
- package/dist/vendored/skills/dsl-card-generator/scripts/gate.mjs +5 -2
- package/dist/vendored/skills/dsl-card-generator/scripts/iterate.mjs +388 -29
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/component_contract.mjs +315 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/layout-tokens.mjs +1 -1
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/paths.mjs +23 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/paths.py +0 -37
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/python.mjs +99 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/adaptive-width.mjs +105 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/bindings.mjs +260 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/expr.mjs +52 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/image.mjs +96 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/registry.mjs +166 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/structure.mjs +127 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/walk.mjs +61 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/scope_alias.mjs +125 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/v3_assemble.mjs +325 -41
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/v3_extract.mjs +112 -6
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/v3_shared.mjs +0 -6
- package/dist/vendored/skills/dsl-card-generator/scripts/lint_card.mjs +119 -78
- package/dist/vendored/skills/dsl-card-generator/scripts/preflight.mjs +48 -50
- package/dist/vendored/skills/dsl-card-generator/scripts/readiness.mjs +634 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/register_card_template.mjs +5 -8
- package/dist/vendored/skills/dsl-card-generator/scripts/route_input.mjs +5 -10
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_audit.mjs +122 -8
- package/dist/vendored/skills/dsl-card-generator/scripts/v3_regression.mjs +3 -1
- package/dist/vendored/skills/dsl-card-generator/scripts/validate_schema.py +23 -15
- package/dist/vendored/skills/shared/exit-codes.md +79 -0
- package/dist/vendored/skills/shared/spec-contract.md +118 -0
- package/dist/vendored/skills/sketch-spec-to-card/SKILL.md +26 -9
- package/dist/vendored/skills/sketch-spec-to-card/references/spec-schema.md +3 -3
- package/dist/vendored/skills/sketch-spec-to-card/scripts/build_prompt.py +0 -8
- package/dist/vendored/skills/sketch-spec-to-card/scripts/cluster_states.py +14 -25
- package/dist/vendored/skills/sketch-spec-to-card/scripts/parse_meaxure.py +91 -47
- package/dist/vendored/skills/sketch-spec-to-card/scripts/png_utils.py +0 -5
- package/dist/vendored/skills/sketch-spec-to-card/scripts/test_text_only_artboard.py +75 -15
- package/dist/vendored/skills/sketch-spec-to-card/scripts/vision.py +40 -34
- package/package.json +1 -1
- package/dist/preview-editor/assets/index-DQn0zQBP.css +0 -1
- package/dist/vendored/skills/card-skill-trainer/scripts/test_missing_slice.py +0 -122
- package/dist/vendored/skills/dsl-card-generator/scripts/analyze_component_usage.mjs +0 -153
|
@@ -626,6 +626,12 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
626
626
|
}
|
|
627
627
|
}
|
|
628
628
|
for (const button of sem.buttons || []) addSemanticBound(button.y, button.y + (button.height || 0))
|
|
629
|
+
if (sem.vertexClusters?.length) {
|
|
630
|
+
for (const vc of sem.vertexClusters) {
|
|
631
|
+
const bottom = vc.y + (vc.height || 30) + 35
|
|
632
|
+
addSemanticBound(vc.y, bottom, true)
|
|
633
|
+
}
|
|
634
|
+
}
|
|
629
635
|
for (const divider of sem.dividers || []) addSemanticBound(divider.y, divider.y + divider.height)
|
|
630
636
|
for (const ul of sem.titleUnderlines || []) addSemanticBound(ul.y, ul.y + ul.height)
|
|
631
637
|
if (sem.topDecoration) {
|
|
@@ -1234,11 +1240,21 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
1234
1240
|
// 内容是左对齐块(如左起的标题簇),强行 center 会把整组推到中间。
|
|
1235
1241
|
const isGeometricCenter = Math.abs(leftPadding - rightPadding) <= 24
|
|
1236
1242
|
const effAlign = aux.align === 'center' && !isGeometricCenter ? 'left' : aux.align
|
|
1243
|
+
// 单 aux 与下方 List 之间的间隙:listTitle 路径有 titleBottomGap 桥接,单 aux 行缺底距,
|
|
1244
|
+
// 会把列表整体顶上 ~gap(本例 12px)。仅当该 aux 是列表前最后一个单 aux 时补底距。
|
|
1245
|
+
const auxBottom = (aux.y || 0) + Math.max(aux.height || 0, (aux.fontSize || 14) * 1.4)
|
|
1246
|
+
const listTop = sem.sampleItems[0]?.rowTop ?? sem.sampleItems[0]?.y ?? Infinity
|
|
1247
|
+
const hasLaterSingleAux = sem.auxTexts.some((a) =>
|
|
1248
|
+
a !== aux && !groupedAuxElements.has(a) && !a._consumed
|
|
1249
|
+
&& (a.y || 0) > (aux.y || 0) && (a.y || 0) < listTop)
|
|
1250
|
+
const bottomPadding = (sem.sampleItems.length && !hasLaterSingleAux && listTop > auxBottom)
|
|
1251
|
+
? Math.max(0, Math.round(listTop - auxBottom))
|
|
1252
|
+
: 0
|
|
1237
1253
|
const geometryPadding = effAlign === 'center'
|
|
1238
|
-
? `${topPadding} 0
|
|
1254
|
+
? `${topPadding} 0 ${bottomPadding} 0`
|
|
1239
1255
|
: effAlign === 'right'
|
|
1240
|
-
? `${topPadding} ${rightPadding}
|
|
1241
|
-
: `${topPadding} 0
|
|
1256
|
+
? `${topPadding} ${rightPadding} ${bottomPadding} 0`
|
|
1257
|
+
: `${topPadding} 0 ${bottomPadding} ${leftPadding}`
|
|
1242
1258
|
// 多值文本行(含 人名+手机号 等):按 generic multiToken 还原为「字面量 + 绑定」段,不再有任何「联系人块」专门逻辑
|
|
1243
1259
|
const mt = sem.bindings.multiToken && sem.bindings.multiToken[aux.text.trim()]
|
|
1244
1260
|
if (mt) {
|
|
@@ -1626,9 +1642,11 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
1626
1642
|
// 注意:标签拆进顶带后 group.label 已置空,锚点必须回落到选项组顶(group.y),
|
|
1627
1643
|
// 否则仍按 labelY 归顶带宿主,整组被带高裁掉。
|
|
1628
1644
|
const host = hostFor(group.label ? (group.labelY || group.y) : group.y)
|
|
1629
|
-
const anchorY = group.label ? (group.labelY || group.y) : group.y
|
|
1645
|
+
const anchorY = (group.label && !group.sideLabel) ? (group.labelY || group.y) : group.y
|
|
1630
1646
|
const topPadding = semanticTopGap(anchorY) ?? Math.max(0, Math.round(anchorY - previousOptionBottom))
|
|
1631
|
-
const leftPadding =
|
|
1647
|
+
const leftPadding = group.sideLabel && group.label
|
|
1648
|
+
|
|
1632
1649
|
? Math.max(0, Math.round((group.label.x || 0) - buildCardLeft))
|
|
1650
|
+
|
|
1633
1651
|
: Math.max(0, Math.round((group.x || 0) - buildCardLeft))
|
|
1634
1652
|
const rightPadding = Math.max(0, Math.round(buildCardRight - ((group.x || 0) + (group.width || 0))))
|
|
1635
1653
|
const groupColumn = container('Column', {
|
|
1636
1654
|
width: '100%',
|
|
@@ -1638,7 +1656,7 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
1638
1656
|
gap: T.gap('none'),
|
|
1639
1657
|
padding: `${topPadding} ${rightPadding} 0 ${leftPadding}`,
|
|
1640
1658
|
})
|
|
1641
|
-
if (group.label) {
|
|
1659
|
+
if (group.label && !group.sideLabel) {
|
|
1642
1660
|
const labelBottom = Math.max(0, Math.round(group.y - (anchorY + (group.label.height || 0))))
|
|
1643
1661
|
const labelRow = container('Row', {
|
|
1644
1662
|
width: '100%', justify: 'flex-start', align: 'center',
|
|
@@ -1695,6 +1713,14 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
1695
1713
|
continue
|
|
1696
1714
|
}
|
|
1697
1715
|
const chipsRow = container('Row', { width: '100%', justify: 'flex-start', align: 'center', gap: T.gap('tight') })
|
|
1716
|
+
let _chipTarget = chipsRow
|
|
1717
|
+
if (group.sideLabel && group.label) {
|
|
1718
|
+
const _lblText = wrapTitleWithUnderline(textFromSpec(group.label), group.label)
|
|
1719
|
+
_chipTarget = container('Row', { justify: 'flex-start', align: 'center', gap: T.gap('tight'), flexShrink: '0' })
|
|
1720
|
+
chipsRow.slots[0].components.push(_lblText)
|
|
1721
|
+
chipsRow.slots[0].components.push(_chipTarget)
|
|
1722
|
+
setStyleChild(chipsRow, 'flex', 'justifyContent', 'space-between')
|
|
1723
|
+
}
|
|
1698
1724
|
group.members.forEach((member, index) => {
|
|
1699
1725
|
if (index > 0) {
|
|
1700
1726
|
const previous = group.members[index - 1]
|
|
@@ -1749,7 +1775,7 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
1749
1775
|
chip.properties = [constProp('value', member.text)]
|
|
1750
1776
|
}
|
|
1751
1777
|
chip.eventactions = [clickAction(`// ${member.text} 点击`)]
|
|
1752
|
-
|
|
1778
|
+
_chipTarget.slots[0].components.push(chip)
|
|
1753
1779
|
})
|
|
1754
1780
|
groupColumn.slots[0].components.push(chipsRow)
|
|
1755
1781
|
insertSemantic(host, groupColumn, anchorY)
|
|
@@ -2178,15 +2204,16 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
2178
2204
|
const mcRightPad = mcAffordR
|
|
2179
2205
|
? Math.max(0, Math.round(buildCardRight - ((mcAffordR.x || 0) + (mcAffordR.width || 0))))
|
|
2180
2206
|
: 0
|
|
2207
|
+
const mcTopGap = semanticTopGap(sem.midCluster.y) ?? mcPad
|
|
2181
2208
|
const row = mcAffordEl
|
|
2182
2209
|
? container('Row', {
|
|
2183
2210
|
width: '100%', justify: 'space-between', align: 'center',
|
|
2184
|
-
padding: `${
|
|
2211
|
+
padding: `${mcTopGap} ${mcRightPad} ${mcPad} ${mcLeftPad}`,
|
|
2185
2212
|
})
|
|
2186
2213
|
: container('Row', {
|
|
2187
2214
|
width: '100%', justify: mcNearCenter ? 'center' : 'flex-start', align: 'center', gap: T.gap('tight'),
|
|
2188
2215
|
padding: mcNearCenter ? mcPad
|
|
2189
|
-
: `${
|
|
2216
|
+
: `${mcTopGap} 0 ${inFixedGradientShell(sem.midCluster.y) ? 0 : mcPad} ${mcLeftPad}`,
|
|
2190
2217
|
})
|
|
2191
2218
|
const mcClusterHost = mcAffordEl
|
|
2192
2219
|
? container('Row', { justify: 'flex-start', align: 'center', gap: T.gap('none') })
|
|
@@ -2572,12 +2599,35 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
2572
2599
|
const pillBottom = Math.max(...pills.map((b) => (b.y || 0) + (b.height || 0)))
|
|
2573
2600
|
const rowKeys = new Set(pills.map((b) => Math.round((b.y || 0) / 8)))
|
|
2574
2601
|
if (rowKeys.size < 2 || pillBottom - pillTop > 260) return null
|
|
2575
|
-
|
|
2602
|
+
let media = (sem.orphanBlocks || []).find((ob) => {
|
|
2576
2603
|
const w = ob.width || 0
|
|
2577
2604
|
const h = ob.height || 0
|
|
2578
2605
|
return w >= 60 && w <= 200 && Math.abs(w - h) <= 14
|
|
2579
2606
|
&& (ob.y || 0) >= pillTop - 80 && (ob.y || 0) + h <= pillBottom + 80
|
|
2580
2607
|
})
|
|
2608
|
+
// 纯矢量图形组兜底(雷达/环图等无切图导出的中心图形):isolatedBlocks 先于 orphanSlices
|
|
2609
|
+
// 消费了大块矢量组,散点带若只认 orphanBlocks 会漏检 → 胶囊退化成通用按钮行、金额散落
|
|
2610
|
+
// (位置分 0.57 根因)。同窗口同尺寸几何判据从 isolatedBlocks 认领,凭上游
|
|
2611
|
+
// annotationDefect.cropSlice(画板裁切产物)出真图,不认业务词。
|
|
2612
|
+
if (!media) {
|
|
2613
|
+
const vectorCand = (sem.isolatedBlocks || []).find((bl) => {
|
|
2614
|
+
const r = rect(bl.element)
|
|
2615
|
+
const w = r.width || 0
|
|
2616
|
+
const h = r.height || 0
|
|
2617
|
+
return w >= 60 && w <= 200 && Math.abs(w - h) <= 14
|
|
2618
|
+
&& (r.y || 0) >= pillTop - 80 && (r.y || 0) + h <= pillBottom + 80
|
|
2619
|
+
})
|
|
2620
|
+
if (vectorCand) {
|
|
2621
|
+
const vr = rect(vectorCand.element)
|
|
2622
|
+
media = {
|
|
2623
|
+
sliceEl: vectorCand.element,
|
|
2624
|
+
x: vr.x || 0,
|
|
2625
|
+
y: vr.y || 0,
|
|
2626
|
+
width: vr.width || 0,
|
|
2627
|
+
height: vr.height || 0,
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2581
2631
|
if (!media) return null
|
|
2582
2632
|
const mL = (media.x || 0) - cardX
|
|
2583
2633
|
const mR = mL + (media.width || 0)
|
|
@@ -2619,6 +2669,8 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
2619
2669
|
sem.buttons = (sem.buttons || []).filter((b) => !clusters.some((c) => c.pill === b))
|
|
2620
2670
|
sem.postAmountTexts = aux.filter((t) => !usedAux.has(t))
|
|
2621
2671
|
sem.orphanBlocks = (sem.orphanBlocks || []).filter((ob) => ob !== media)
|
|
2672
|
+
// 矢量兜底认领的中心媒体同步从 isolatedBlocks 移除,避免孤立媒体兜底再渲染一遍(同箭头先例)
|
|
2673
|
+
sem.isolatedBlocks = (sem.isolatedBlocks || []).filter((bl) => bl.element !== media.sliceEl)
|
|
2622
2674
|
const arrowEls = new Set(clusters.map((c) => c.arrow).filter(Boolean))
|
|
2623
2675
|
if (arrowEls.size) {
|
|
2624
2676
|
sem.slices = (sem.slices || []).filter((sl) => !arrowEls.has(sl))
|
|
@@ -2830,13 +2882,18 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
2830
2882
|
: null
|
|
2831
2883
|
const midAuxHasBlocksAfter = (sem.formRows && sem.formRows.length) || sem.buttons.length || footAux.length > 0
|
|
2832
2884
|
|| (midAuxLastBottom != null && nextSemanticTop(midAuxLastBottom - 1) != null)
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2885
|
+
const midGrouped = new Set()
|
|
2886
|
+
// 同行判定(与 auxTexts 分组的 centerNear 同口径):垂直中心差 ≤ 较矮元素高度的 0.75
|
|
2887
|
+
// (大小字号混排行,如 标题+描边pill)。centerNear 原版定义在 auxTexts 分组循环块内,此处不可见。
|
|
2888
|
+
const midCenterNear = (ea, eb) => {
|
|
2889
|
+
const ra = rect(ea), rb = rect(eb)
|
|
2890
|
+
const ca = (ra.y || 0) + (ra.height || 0) / 2
|
|
2891
|
+
const cb = (rb.y || 0) + (rb.height || 0) / 2
|
|
2892
|
+
return Math.abs(ca - cb) <= Math.min(ra.height || 0, rb.height || 0) * 0.75
|
|
2893
|
+
}
|
|
2894
|
+
const leadSliceOf = (pat) => {
|
|
2838
2895
|
const patH = pat.height || (pat.fontSize || 14) * 1.4
|
|
2839
|
-
const
|
|
2896
|
+
const idx = (sem.slices || []).findIndex((sl) => {
|
|
2840
2897
|
const sr = rect(sl)
|
|
2841
2898
|
const w = sr.width || 0
|
|
2842
2899
|
const h = sr.height || 0
|
|
@@ -2844,7 +2901,64 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
2844
2901
|
if ((sr.x || 0) + w > (pat.x || 0) - 2 || (sr.x || 0) < (pat.x || 0) - 56) return false
|
|
2845
2902
|
return Math.abs(((sr.y || 0) + h / 2) - ((pat.y || 0) + patH / 2)) <= Math.max(12, h)
|
|
2846
2903
|
})
|
|
2847
|
-
|
|
2904
|
+
return idx >= 0 ? sem.slices[idx] : null
|
|
2905
|
+
}
|
|
2906
|
+
for (let i = 0; i < midAux.length; i++) {
|
|
2907
|
+
const pat = midAux[i]
|
|
2908
|
+
if (midGrouped.has(pat)) continue
|
|
2909
|
+
// 同行成组(纯几何判据,与 auxTexts 分组同族):同一 y 带内的多条 postAmountText
|
|
2910
|
+
// (如 状态pill「营业中」+ 距离「距您986m」)必须合成一行,否则每条独立成行会把
|
|
2911
|
+
// 头部撑高一行、下方表单/按钮整体下移一行高(1219 网点预约实测 +16~20px 全卡纵移)。
|
|
2912
|
+
// 组内任一带行首图标或命中枚举统计行时不成组(保持单行路径的图标/绑定处理)。
|
|
2913
|
+
const lineGroup = [pat]
|
|
2914
|
+
for (let j = i + 1; j < midAux.length; j++) {
|
|
2915
|
+
const cand = midAux[j]
|
|
2916
|
+
if (midGrouped.has(cand)) continue
|
|
2917
|
+
if (!(sameRow(cand.sourceEl, pat.sourceEl, 8) || midCenterNear(cand.sourceEl, pat.sourceEl))) break
|
|
2918
|
+
lineGroup.push(cand)
|
|
2919
|
+
}
|
|
2920
|
+
const groupable = lineGroup.length > 1
|
|
2921
|
+
&& lineGroup.every((p) => !leadSliceOf(p) && !detectEnumStatStrip(p.text))
|
|
2922
|
+
if (groupable) {
|
|
2923
|
+
const host = hostFor(pat.y)
|
|
2924
|
+
const inTopHost = host !== card
|
|
2925
|
+
const groupIsLast = lineGroup[lineGroup.length - 1] === midAux[midAux.length - 1] && !midAuxHasBlocksAfter
|
|
2926
|
+
const beforeBottom = previousSemanticBottom(pat.y) ??
|
|
2927
|
+
(i > 0 ? lineBottom(midAux[i - 1]) : (mainBottom ?? previousAuxBottom))
|
|
2928
|
+
let pad = (inTopHost ? null : auxRowPadding(pat, i > 0 ? midAux[i - 1] : null, groupIsLast, beforeBottom)) || '6'
|
|
2929
|
+
const groupLeft = Math.min(...lineGroup.map((p) => p.x || buildCardLeft))
|
|
2930
|
+
const parts = String(pad).trim().split(/\s+/)
|
|
2931
|
+
while (parts.length < 4) parts.push('0')
|
|
2932
|
+
parts[3] = String(Math.max(0, Math.round(groupLeft - buildCardLeft)))
|
|
2933
|
+
pad = parts.join(' ')
|
|
2934
|
+
const row = container('Row', { width: '100%', justify: 'flex-start', align: 'center', gap: '0', padding: pad })
|
|
2935
|
+
// 组内逐项 spec 间隙用横向 spacer 承担(与 auxTexts 簇内同法;Row gap 表达不了逐项差异)
|
|
2936
|
+
let prevRight = null
|
|
2937
|
+
for (const item of [...lineGroup].sort((a, b) => (a.x || 0) - (b.x || 0))) {
|
|
2938
|
+
const itemRight = (item.x || 0) + (item.width || 0)
|
|
2939
|
+
if (prevRight != null) {
|
|
2940
|
+
const gapW = Math.max(0, Math.round((item.x || 0) - prevRight))
|
|
2941
|
+
if (gapW > 0) {
|
|
2942
|
+
const spacer = container('Column', { direction: 'column', padding: '0' })
|
|
2943
|
+
ensureStyleGroup(spacer, 'rect')
|
|
2944
|
+
ensureStyleGroup(spacer, 'flex')
|
|
2945
|
+
setStyleChild(spacer, 'rect', 'width', String(gapW))
|
|
2946
|
+
setStyleChild(spacer, 'rect', 'height', '1')
|
|
2947
|
+
setStyleChild(spacer, 'flex', 'flexShrink', '0')
|
|
2948
|
+
row.slots[0].components.push(spacer)
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
row.slots[0].components.push(auxContent(item))
|
|
2952
|
+
prevRight = itemRight
|
|
2953
|
+
}
|
|
2954
|
+
insertSemantic(host, row, pat.y)
|
|
2955
|
+
for (const g of lineGroup.slice(1)) midGrouped.add(g)
|
|
2956
|
+
continue
|
|
2957
|
+
}
|
|
2958
|
+
const host = hostFor(pat.y)
|
|
2959
|
+
// 行首图标(勾选/圆点等选项符号):文本左侧同行带内的小切片随行渲染并消费,
|
|
2960
|
+
// 否则选项行只剩文字、符号丢失(组件计数与像素都扣)。与 appendInlineTrailSlice 同族反方向。
|
|
2961
|
+
const leadSlice = leadSliceOf(pat)
|
|
2848
2962
|
// 渐变容器内的行:间距交给容器的 gap/padding(已在 topDecoration 渲染时统一设置),行自身 padding 归零,
|
|
2849
2963
|
// 否则会叠加出巨大下边距(如标题行被算成卡片末行 → paddingBottom=卡底-行底=169px)。
|
|
2850
2964
|
const inTopHost = host !== card
|
|
@@ -3139,6 +3253,9 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
3139
3253
|
const rowTop = previousFormBottom == null
|
|
3140
3254
|
? (semanticTopGap(fr.label.y) ?? T.pad('section'))
|
|
3141
3255
|
: Math.max(0, Math.round((Number.isFinite(rowVisualTop) ? rowVisualTop : fr.label.y) - previousFormBottom))
|
|
3256
|
+
if (process.env.DEBUG_FORMROW === '1') {
|
|
3257
|
+
console.error(`[formrow] ${fr.label.text} labelY=${fr.label.y} visualTop=${rowVisualTop} prevFormBottom=${previousFormBottom} rowTop=${rowTop} semGap=${previousFormBottom == null ? semanticTopGap(fr.label.y) : '-'}`)
|
|
3258
|
+
}
|
|
3142
3259
|
// 坐标驱动:左 padding 由 label 的 spec x 坐标推导,右 padding 取与左对称
|
|
3143
3260
|
const leftIcon = fr.leftIconSlice ? imageOrPlaceholder(fr.leftIconSlice, sem.slicesExports) : null
|
|
3144
3261
|
const rowLeftPad = Math.max(0, Math.round(((leftIcon ? rect(fr.leftIconSlice).x : fr.label.x) || buildCardLeft) - buildCardLeft))
|
|
@@ -3410,6 +3527,14 @@ function buildSchemaFromSemantics(sem, id, name, opts = {}) {
|
|
|
3410
3527
|
}
|
|
3411
3528
|
// Skip dividers that were interleaved into formHost
|
|
3412
3529
|
sem._interleavedDividers = interleavedDividers
|
|
3530
|
+
if (process.env.DEBUG_FORMROW === '1') {
|
|
3531
|
+
const host = hostFor(sorted[0].label.y)
|
|
3532
|
+
const padOf = (n) => { const g = (n.style?.properties || []).find((p) => p.itemKey === 'padding'); const c = g?.itemChildren?.properties || []; const o = {}; for (const p of c) o[p.itemKey] = p.itemValue; return JSON.stringify(o) }
|
|
3533
|
+
console.error(`[formhost] host=${host === card ? 'card' : 'topContentHost'} children:`)
|
|
3534
|
+
for (const ch of host.slots[0].components) {
|
|
3535
|
+
console.error(` - ${ch.className} y=${semanticYByNode.get(ch)} pad=${padOf(ch)} text=${JSON.stringify((ch.properties || []).find((p) => p.itemKey === 'value')?.itemValue || '').slice(0, 24)}`)
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3413
3538
|
insertSemantic(hostFor(sorted[0].label.y), formHost, sorted[0].label.y)
|
|
3414
3539
|
}
|
|
3415
3540
|
|
|
@@ -4097,7 +4222,9 @@ if (subsActive.length && !sem._subAmountsConsumed) {
|
|
|
4097
4222
|
// desc 的 spec x 坐标可能与标题左沿不一致(百分比文本缩进在标题右沿之后),
|
|
4098
4223
|
// 用左 padding 补齐差值使 desc 对齐 spec 位置。
|
|
4099
4224
|
if (firstItem.descX != null && firstItem.y != null) {
|
|
4100
|
-
|
|
4225
|
+
// desc 缩进基准应是标题左沿(titleX),而非 rowLeftEdge(带图标时会被拉到图标左沿)。
|
|
4226
|
+
const _titleX = (firstItem.titleX != null ? firstItem.titleX : firstItem.rowLeftEdge)
|
|
4227
|
+
|| (rect(card).x || 0) + 16
|
|
4101
4228
|
const _descIndent = Math.max(0, Math.round((firstItem.descX || 0) - _titleX))
|
|
4102
4229
|
if (_descIndent >= 4) {
|
|
4103
4230
|
const _descWrap = container('Row', { width: '100%', justify: 'flex-start', align: 'flex-start', padding: `0 0 0 ${_descIndent}` })
|
|
@@ -4757,6 +4884,124 @@ for (const fd of sem.floatDecoBlocks || []) {
|
|
|
4757
4884
|
insertSemantic(hostFor(fd.y), band, fd.y)
|
|
4758
4885
|
}
|
|
4759
4886
|
|
|
4887
|
+
// Per-element absolute positioning for chart vertex labels (radar pentagon vertices).
|
|
4888
|
+
// Each text/shape positioned at exact spec coordinate via margin offsets (max IoU).
|
|
4889
|
+
if (sem.vertexClusters?.length) {
|
|
4890
|
+
const vcTopY = Math.min(...sem.vertexClusters.map((vc) => vc.y || 0))
|
|
4891
|
+
const vcBottomY = Math.max(...sem.vertexClusters.map((vc) => (vc.y || 0) + (vc.height || 30) + 35))
|
|
4892
|
+
const vcBandH = Math.max(0, Math.round(vcBottomY - vcTopY))
|
|
4893
|
+
const vcBand = container('AbsoluteLayout', { width: '100%' })
|
|
4894
|
+
ensureStyleGroup(vcBand, 'rect')
|
|
4895
|
+
setStyleChild(vcBand, 'rect', 'height', String(vcBandH))
|
|
4896
|
+
ensureStyleGroup(vcBand, 'other')
|
|
4897
|
+
setStyleChild(vcBand, 'other', 'overflow', 'visible')
|
|
4898
|
+
const cardY = buildCard.y || 0
|
|
4899
|
+
const cardLeft = buildCardLeft
|
|
4900
|
+
const absMargin = (node, x, y) => {
|
|
4901
|
+
ensureStyleGroup(node, 'margin')
|
|
4902
|
+
setStyleChild(node, 'margin', 'marginType', '1')
|
|
4903
|
+
setStyleChild(node, 'margin', 'marginTop', String(Math.max(0, Math.round(y - cardY))))
|
|
4904
|
+
setStyleChild(node, 'margin', 'marginLeft', String(Math.max(0, Math.round(x - cardLeft))))
|
|
4905
|
+
setStyleChild(node, 'margin', 'marginRight', '0')
|
|
4906
|
+
setStyleChild(node, 'margin', 'marginBottom', '0')
|
|
4907
|
+
}
|
|
4908
|
+
// Chart center media (radar/polygon chart): same AbsoluteLayout as pills
|
|
4909
|
+
if (sem.vertexClusters.chartMedia) {
|
|
4910
|
+
const cm = sem.vertexClusters.chartMedia
|
|
4911
|
+
const cmEl = cm.element
|
|
4912
|
+
const cropSlice = cmEl?.annotationDefect?.cropSlice
|
|
4913
|
+
const exportRel = cropSlice || resolveSliceExport(sem.slicesExports || [], cmEl?.name) || null
|
|
4914
|
+
if (exportRel) {
|
|
4915
|
+
const cmImg = imageFromSlice(cmEl, sem.slicesExports) || imageFromSlice({ name: cmEl?.name, annotationDefect: cmEl?.annotationDefect }, sem.slicesExports)
|
|
4916
|
+
if (cmImg) {
|
|
4917
|
+
ensureStyleGroup(cmImg, 'rect')
|
|
4918
|
+
setStyleChild(cmImg, 'rect', 'width', String(Math.round(cm.width || 98)))
|
|
4919
|
+
setStyleChild(cmImg, 'rect', 'height', String(Math.round(cm.height || 98)))
|
|
4920
|
+
absMargin(cmImg, cm.x || 0, cm.y || 0)
|
|
4921
|
+
vcBand.slots[0].components.push(cmImg)
|
|
4922
|
+
}
|
|
4923
|
+
} else {
|
|
4924
|
+
const cmBlock = container('Column', { direction: 'column', gap: T.gap('none'), padding: '0' })
|
|
4925
|
+
ensureStyleGroup(cmBlock, 'rect')
|
|
4926
|
+
setStyleChild(cmBlock, 'rect', 'width', String(Math.round(cm.width || 98)))
|
|
4927
|
+
setStyleChild(cmBlock, 'rect', 'height', String(Math.round(cm.height || 98)))
|
|
4928
|
+
const cmColor = cmEl?.backgroundFallback?.color || cmEl?.style?.fill?.color || '#DCDCDC'
|
|
4929
|
+
ensureStyleGroup(cmBlock, 'background')
|
|
4930
|
+
setStyleChild(cmBlock, 'background', 'backgroundType', '01')
|
|
4931
|
+
setStyleChild(cmBlock, 'background', 'backgroundColor', hexToRgba(cmColor, cmEl?.style?.opacity ?? 1))
|
|
4932
|
+
ensureStyleGroup(cmBlock, 'borderRadius')
|
|
4933
|
+
setStyleChild(cmBlock, 'borderRadius', 'borderRadiusType', '0')
|
|
4934
|
+
setStyleChild(cmBlock, 'borderRadius', 'borderRadius', String(Math.round((cm.width || 98) / 2)))
|
|
4935
|
+
absMargin(cmBlock, cm.x || 0, cm.y || 0)
|
|
4936
|
+
vcBand.slots[0].components.push(cmBlock)
|
|
4937
|
+
}
|
|
4938
|
+
}
|
|
4939
|
+
for (const vc of sem.vertexClusters) {
|
|
4940
|
+
if (vc.pill.fillColor) {
|
|
4941
|
+
const pillBg = container('Column', { direction: 'column', gap: T.gap('none'), padding: '0' })
|
|
4942
|
+
ensureStyleGroup(pillBg, 'rect')
|
|
4943
|
+
setStyleChild(pillBg, 'rect', 'width', String(Math.round(vc.width || 88)))
|
|
4944
|
+
setStyleChild(pillBg, 'rect', 'height', String(Math.round(vc.height || 30)))
|
|
4945
|
+
ensureStyleGroup(pillBg, 'background')
|
|
4946
|
+
setStyleChild(pillBg, 'background', 'backgroundType', '01')
|
|
4947
|
+
setStyleChild(pillBg, 'background', 'backgroundColor', hexToRgba(vc.pill.fillColor, vc.pill.opacity ?? 1))
|
|
4948
|
+
ensureStyleGroup(pillBg, 'borderRadius')
|
|
4949
|
+
setStyleChild(pillBg, 'borderRadius', 'borderRadiusType', '0')
|
|
4950
|
+
setStyleChild(pillBg, 'borderRadius', 'borderRadius', String(vc.pill.radius || 14))
|
|
4951
|
+
absMargin(pillBg, vc.x || 0, vc.y || 0)
|
|
4952
|
+
vcBand.slots[0].components.push(pillBg)
|
|
4953
|
+
}
|
|
4954
|
+
if (vc.pill.sourceEl) {
|
|
4955
|
+
const pr = rect(vc.pill.sourceEl)
|
|
4956
|
+
const catText = textFromSpec({ text: vc.pill.text, color: vc.pill.color, fontSize: vc.pill.fontSize, fontWeight: vc.pill.fontWeight })
|
|
4957
|
+
absMargin(catText, pr.x || 0, pr.y || 0)
|
|
4958
|
+
vcBand.slots[0].components.push(catText)
|
|
4959
|
+
}
|
|
4960
|
+
for (const t of (vc.texts || [])) {
|
|
4961
|
+
const tr = rect(t)
|
|
4962
|
+
const txtNode = textFromSpec({ text: textOf(t), color: ts(t).color, fontSize: ts(t).fontSize, fontWeight: ts(t).fontWeight })
|
|
4963
|
+
absMargin(txtNode, tr.x || 0, tr.y || 0)
|
|
4964
|
+
vcBand.slots[0].components.push(txtNode)
|
|
4965
|
+
}
|
|
4966
|
+
for (const sh of (vc.shapes || [])) {
|
|
4967
|
+
const sr = rect(sh)
|
|
4968
|
+
if (sh.type === 'shape' && (sr.width || 0) <= 30 && (sr.height || 0) <= 20 && (sr.width || 0) > 0) {
|
|
4969
|
+
const pbShell = container('Column', { direction: 'column', justify: 'center', align: 'center', gap: T.gap('none') })
|
|
4970
|
+
ensureStyleGroup(pbShell, 'rect')
|
|
4971
|
+
setStyleChild(pbShell, 'rect', 'width', String(Math.round(sr.width || 24)))
|
|
4972
|
+
setStyleChild(pbShell, 'rect', 'height', String(Math.round(sr.height || 14)))
|
|
4973
|
+
const shFill = sh.style?.fill?.color
|
|
4974
|
+
if (shFill) {
|
|
4975
|
+
ensureStyleGroup(pbShell, 'background')
|
|
4976
|
+
setStyleChild(pbShell, 'background', 'backgroundType', '01')
|
|
4977
|
+
setStyleChild(pbShell, 'background', 'backgroundColor', hexToRgba(shFill, sh.style?.opacity ?? 1))
|
|
4978
|
+
}
|
|
4979
|
+
const shRadius = sh.style?.radius
|
|
4980
|
+
const rVal = (shRadius && typeof shRadius === 'object') ? (shRadius.borderRadius || 0) : (Number(shRadius) || 0)
|
|
4981
|
+
ensureStyleGroup(pbShell, 'borderRadius')
|
|
4982
|
+
setStyleChild(pbShell, 'borderRadius', 'borderRadiusType', '0')
|
|
4983
|
+
setStyleChild(pbShell, 'borderRadius', 'borderRadius', String(rVal))
|
|
4984
|
+
absMargin(pbShell, sr.x || 0, sr.y || 0)
|
|
4985
|
+
vcBand.slots[0].components.push(pbShell)
|
|
4986
|
+
}
|
|
4987
|
+
}
|
|
4988
|
+
for (const sl of (vc.shapes || [])) {
|
|
4989
|
+
if (sl.type !== 'slice') continue
|
|
4990
|
+
const slr = rect(sl)
|
|
4991
|
+
const slMedia = imageFromSlice(sl, sem.slicesExports)
|
|
4992
|
+
if (slMedia) {
|
|
4993
|
+
ensureStyleGroup(slMedia, 'rect')
|
|
4994
|
+
setStyleChild(slMedia, 'rect', 'width', String(Math.round(slr.width || 20)))
|
|
4995
|
+
setStyleChild(slMedia, 'rect', 'height', String(Math.round(slr.height || 20)))
|
|
4996
|
+
absMargin(slMedia, slr.x || 0, slr.y || 0)
|
|
4997
|
+
vcBand.slots[0].components.push(slMedia)
|
|
4998
|
+
}
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
insertSemantic(hostFor(vcTopY), vcBand, vcTopY)
|
|
5002
|
+
}
|
|
5003
|
+
|
|
5004
|
+
|
|
4760
5005
|
|
|
4761
5006
|
// 薄壳分段横条(时间轴 / 进度分段):按壳几何逐段还原底色/描边/圆角,段间隙用等宽 spacer
|
|
4762
5007
|
// 承担(Row gap 是常量,表达不了逐段差异)。此前分段壳落在孤立媒体块通道,
|
|
@@ -4817,37 +5062,76 @@ for (const fd of sem.floatDecoBlocks || []) {
|
|
|
4817
5062
|
insertSemantic(hostFor(strip.y), row, strip.y)
|
|
4818
5063
|
}
|
|
4819
5064
|
|
|
4820
|
-
//
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
const
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
const
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
5065
|
+
// isolatedBlocks fallback after semantic layout
|
|
5066
|
+
// Group same-y blocks into one row (e.g. paired avatars)
|
|
5067
|
+
const _isoBlocks = (sem.isolatedBlocks || []).filter((block) =>
|
|
5068
|
+
!(sem.progressBars || []).length && !block.consumedByTabs)
|
|
5069
|
+
const _isoGroups = []
|
|
5070
|
+
for (const block of _isoBlocks) {
|
|
5071
|
+
const grp = _isoGroups.find((g) => Math.abs(g.y - (block.y || 0)) <= 2)
|
|
5072
|
+
if (grp) grp.blocks.push(block)
|
|
5073
|
+
else _isoGroups.push({ y: block.y || 0, blocks: [block] })
|
|
5074
|
+
}
|
|
5075
|
+
for (const group of _isoGroups) {
|
|
5076
|
+
const medias = []
|
|
5077
|
+
for (const block of group.blocks) {
|
|
5078
|
+
const media = imageOrPlaceholder(block.element, sem.slicesExports)
|
|
5079
|
+
if (!media) continue
|
|
5080
|
+
setStyleChild(media, 'rect', 'width', String(Math.round(block.width || 0)))
|
|
5081
|
+
setStyleChild(media, 'rect', 'height', String(Math.round(block.height || 0)))
|
|
5082
|
+
medias.push(media)
|
|
5083
|
+
}
|
|
5084
|
+
if (!medias.length) continue
|
|
5085
|
+
const previousBottom = previousSemanticBottom(group.y)
|
|
5086
|
+
const groupHeight = Math.max(...group.blocks.map((b) => (b.height || 0)))
|
|
5087
|
+
const nextTop = nextSemanticTop(group.y + groupHeight)
|
|
5088
|
+
const mediaBottom = group.y + groupHeight
|
|
5089
|
+
// Overlay: if block vertically overlaps with adjacent semantic elements
|
|
5090
|
+
// (e.g. transfer arrow between paired cards), render as zero-height AbsoluteLayout
|
|
5091
|
+
// to avoid taking vertical space in the flow.
|
|
5092
|
+
const _overlapsText = medias.length === 1 && (sem.auxTexts || []).some((text) => {
|
|
5093
|
+
const tY = text.y || 0
|
|
5094
|
+
const tH = Math.max(text.height || 0, (text.fontSize || 14) * 1.4)
|
|
5095
|
+
const tBottom = tY + tH
|
|
5096
|
+
const _blk = group.blocks[0]
|
|
5097
|
+
const bRight = (_blk.x || 0) + (_blk.width || 0)
|
|
5098
|
+
const tRight = (text.x || 0) + (text.width || 0)
|
|
5099
|
+
return tY < mediaBottom && tBottom > group.y + 2 &&
|
|
5100
|
+
(bRight <= (text.x || 0) + 2 || (_blk.x || 0) >= tRight - 2)
|
|
5101
|
+
})
|
|
5102
|
+
if (_overlapsText) {
|
|
5103
|
+
const _blk = group.blocks[0]
|
|
5104
|
+
const _media = medias[0]
|
|
5105
|
+
const _band = container('AbsoluteLayout', { width: '100%', height: '0' })
|
|
5106
|
+
ensureStyleGroup(_band, 'other')
|
|
5107
|
+
setStyleChild(_band, 'other', 'overflow', 'visible')
|
|
5108
|
+
ensureStyleGroup(_media, 'margin')
|
|
5109
|
+
setStyleChild(_media, 'margin', 'marginType', '1')
|
|
5110
|
+
setStyleChild(_media, 'margin', 'marginTop', String(Math.max(0, Math.round((_blk.y || 0) - (buildCard.y || 0)))))
|
|
5111
|
+
setStyleChild(_media, 'margin', 'marginLeft', String(Math.max(0, Math.round((_blk.x || 0) - buildCardLeft))))
|
|
5112
|
+
setStyleChild(_media, 'margin', 'marginRight', '0')
|
|
5113
|
+
setStyleChild(_media, 'margin', 'marginBottom', '0')
|
|
5114
|
+
_band.slots[0].components.push(_media)
|
|
5115
|
+
insertSemantic(hostFor(group.y), _band, group.y)
|
|
5116
|
+
continue
|
|
5117
|
+
}
|
|
5118
|
+
const leftmost = group.blocks.reduce((a, b) => ((a.x || 0) <= (b.x || 0) ? a : b))
|
|
5119
|
+
const rightmost = group.blocks.reduce((a, b) => (((a.x || 0) + (a.width || 0)) >= ((b.x || 0) + (b.width || 0)) ? a : b))
|
|
5120
|
+
const leftPadding = Math.max(0, Math.round((leftmost.x || buildCardLeft) - buildCardLeft))
|
|
5121
|
+
const rightPadding = Math.max(0, Math.round(buildCardRight - ((rightmost.x || buildCardLeft) + (rightmost.width || 0))))
|
|
5122
|
+
const row = container('Row', { width: '100%', direction: 'row', justify: medias.length > 1 ? 'space-between' : 'flex-start', align: 'center', padding: T.pad('root') })
|
|
4839
5123
|
setStyleChild(row, 'padding', 'paddingType', '1')
|
|
4840
5124
|
setStyleChild(row, 'padding', 'paddingTop', String(previousBottom == null
|
|
4841
|
-
? Math.max(0, Math.round(
|
|
4842
|
-
: Math.max(0, Math.round(
|
|
5125
|
+
? Math.max(0, Math.round(group.y - (buildCard.y || 0)))
|
|
5126
|
+
: Math.max(0, Math.round(group.y - previousBottom))))
|
|
4843
5127
|
const designedCardBottom = (buildCard.y || 0) + (buildCard.height || 0)
|
|
4844
5128
|
setStyleChild(row, 'padding', 'paddingBottom', String(Math.max(0, nextTop == null
|
|
4845
5129
|
? Math.round(designedCardBottom - mediaBottom)
|
|
4846
5130
|
: 0)))
|
|
4847
5131
|
setStyleChild(row, 'padding', 'paddingLeft', String(leftPadding))
|
|
4848
5132
|
setStyleChild(row, 'padding', 'paddingRight', String(rightPadding))
|
|
4849
|
-
row.slots[0].components.push(media)
|
|
4850
|
-
insertSemantic(hostFor(
|
|
5133
|
+
medias.forEach((media) => row.slots[0].components.push(media))
|
|
5134
|
+
insertSemantic(hostFor(group.y), row, group.y)
|
|
4851
5135
|
}
|
|
4852
5136
|
|
|
4853
5137
|
// 采样色带宿主顶距:带内首个内容的 spec 顶距搬到带宿主(带内行自身 padT 已因 containment 归零),
|