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
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// MBank mockup 截图工具:把 375×812 的 mockup HTML 导出为 750×1624 @2x PNG。
|
|
3
|
+
// 用法:node <skill>/tools/screenshot.mjs <input.html> [output.png] [--full]
|
|
4
|
+
// --full 整页截图(长页 mockup 用;默认 812pt 视口截断)
|
|
5
|
+
// 依赖 playwright(仓库根 node_modules 已装;本脚本从自身位置向上解析)。
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
import { dirname, resolve } from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { pathToFileURL } from 'node:url';
|
|
10
|
+
|
|
11
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const { chromium } = require('playwright');
|
|
14
|
+
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const full = args.includes('--full');
|
|
17
|
+
const positional = args.filter((a) => !a.startsWith('--'));
|
|
18
|
+
const input = positional[0];
|
|
19
|
+
if (!input) {
|
|
20
|
+
console.error('usage: node screenshot.mjs <input.html> [output.png] [--full]');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const output = positional[1] || input.replace(/\.html$/i, '') + '.png';
|
|
24
|
+
|
|
25
|
+
const browser = await chromium.launch();
|
|
26
|
+
const page = await browser.newPage({
|
|
27
|
+
viewport: { width: 375, height: 812 },
|
|
28
|
+
deviceScaleFactor: 2, // @2x → 750 宽输出,与设计稿一致
|
|
29
|
+
});
|
|
30
|
+
await page.goto(pathToFileURL(resolve(input)).href, { waitUntil: 'networkidle' });
|
|
31
|
+
await page.waitForTimeout(300); // 字体/图片稳定
|
|
32
|
+
await page.screenshot({ path: output, fullPage: full });
|
|
33
|
+
await browser.close();
|
|
34
|
+
console.log(output);
|
|
@@ -45,12 +45,14 @@
|
|
|
45
45
|
| text_only | 纯文本画板(对话 / 问答 / 提示),整板无卡 | `[]` | 不产 | **不建卡**(上游按类型收口,勿人工绕过) | `classify_artboard`;`test_text_only_artboard.py` 用例 1 |
|
|
46
46
|
| overlay_dialog | 系统弹框 | `[]` | 视觉 scrim 判定类不产;名称兜底判定类产 fallback `card-1.png` | **不建卡** —— 由父卡 alertDialog 调用承载 | 用例 5 |
|
|
47
47
|
| subcard | 视觉 bottom-sheet 子卡(底部抽屉) | **1 张**(面板几何直取,**不走候选检测**) | 产 `card-1.png`(面板 rect + 顶圆角遮罩) | **建独立 Dialog 卡** —— 与普通卡同构:`rect` / `mask.radius` / 卡内 `elements` / `segments` 齐备 | `detect_artboard_cards` sheet 分支;`vision._bottom_sheet_rect`;用例 4 |
|
|
48
|
-
| card | 0 卡 ·
|
|
48
|
+
| card | 0 卡 · 结构丰富:候选被读成控件条 / 无候选(整卡单切图已由兜底提升为 1 张卡,见下注) | `[]` | 产 fallback `card-1.png`(去系统 chrome) | 建卡:以该兜底图 + `elements[]` 为准(**有图无坐标**) | `parse_meaxure.py:645`;`board_tag`(`:1768`);用例 2/6 |
|
|
49
49
|
|
|
50
50
|
> 注:本表首列**刻意不加反引号** —— `spec-contract.test.mjs` 以「首列反引号 + 整行 6 列」识别字段契约表,加反引号会让本表被误当成字段登记。
|
|
51
51
|
|
|
52
52
|
> **`subcard` 产卡(2026-09-24 修)**:此前 `bottom_sheet` 只写 `focusSkipReason` 不产 `cards`,导致 `cluster_states` 承认它是独立逻辑卡(`pageId=card-N`、`isDialog=true`)而 spec 层给不出任何卡级数据。现由 `detect_artboard_cards` 的 sheet 分支把面板几何(`vision._bottom_sheet_rect` 早已算出 rect + 顶圆角)落成 1 张卡;`focusSkipReason` **保持不变**(`classify_artboard` 靠它判 `subcard`,改动会连带打翻 `is_text_only_artboard` / `board_verdict` / trainer 的 `is_popup_case`)。**旧版存量 spec 仍可能是 `subcard` + `cards=[]` → 重跑上游,不要在下游补卡。**
|
|
53
53
|
|
|
54
|
+
> **整卡单切图兜底(2026-09-28)**:设计稿把卡片整体导成一张大切图时(图层树只剩「编组 N」大图),`detect_artboard_cards` 在**常规候选 0 卡**时把该切图提升为 1 张真卡。纯几何判据:宽 ≥ 55%W、高 ≥ 12%H、纵向落在 10%..95%H、区域归属元素只有切图本身(混入任何文本/其他切图标注不兜底)。此前这类画板 `cards=[]` → manifest `trainingEligible=false` → 数据集构建整板跳过,「对话 + 内嵌卡」形态永远进不了训练。兜底后 `cards[0].rect` = 切图几何、卡内 elements 只有该 slice,下游按普通 Image 卡建卡。仅 0 卡时触发,已有卡的画板行为不变。
|
|
55
|
+
>
|
|
54
56
|
> `readiness.mjs` 门 **F**:`cards=[]` 的三类(text_only / overlay_dialog / card)只在 `checks.spec.notes` 记说明,**均不进 `blockers`**;`cards` 非空时只校验 `rect` / `mask` 是否缺失(产卡与否由 Workflow A.5 决定,不是契约缺失)。
|
|
55
57
|
> 门 F 真正拦的只有:`cards` 非空时 `cards[i].rect` / `cards[i].mask` 缺失(旧版产物)、`--card` 序号越界、spec 解析失败。
|
|
56
58
|
|
|
@@ -116,3 +118,10 @@
|
|
|
116
118
|
- 判定口径:字段出现次数 < `cards[]` 总数即存在旧产物。
|
|
117
119
|
- 处置:**重跑上游**(`parse_meaxure.py --src <导出> --draft <稿名> --build`)而非手工补字段。
|
|
118
120
|
- 不要为了兼容旧产物在下游写 `if (!card.mask)` 分支 —— 契约规定 `mask` 必需,兼容分支会让"上游漏产"永远不被发现。
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
### upstream-structural-focus-image-card(r264 训练回灌)
|
|
124
|
+
> lesson: `r264-l01` status: `promoted`
|
|
125
|
+
|
|
126
|
+
整卡单切图形态(设计稿把卡片整体导出成一张大切图,图层树只剩该图、卡内无任何文本/按钮标注)此前在解析器永远 0 卡:_candidate_visuals 跳过 is_slice → 候选全被低结构门拒绝 → spec.cards=[] → manifest trainingEligible=false → 数据集构建整板跳过,而 is_text_only_artboard 又承认这种板「有卡」(has_card_like_visual),两处口径互相矛盾。修复三件套:① detect_artboard_cards 在常规候选 0 卡时把合格大切图兜底提升为 1 张真卡(判据纯几何:宽≥55%W、高≥12%H、纵向 10%..95%H、区域归属元素仅切图本身,混入任何其他标注不兜底);② card_focus_metrics 把切片当内容而非壳——面积≥55%卡区的壳层排除规则只适用 shape/symbol,切片无条件计数并新增 sliceAreaRatio 字段;③ 训练器上游结构聚焦门新增图像卡通道 sliceAreaRatio≥0.6。生成器对该形态的忠实还原 = 把切片绑为卡背景图(R31 消毒空格→连字符),像素/结构/色彩门按同图对比正常评分。
|
|
127
|
+
|
|
@@ -159,7 +159,7 @@ python skills/sketch-spec-to-card/scripts/cluster_states.py --out <UI目录> --a
|
|
|
159
159
|
|
|
160
160
|
1. 每个候选必须是一个**完整业务卡边界**:有独立容器、标题/内容主体和业务语义,不是对话气泡、单个按钮、图标、状态条或背景装饰。
|
|
161
161
|
2. 一张画板的多个卡片必须分别落成 `card-1.png`、`card-2.png`;不允许把多张业务卡合成一张截图。
|
|
162
|
-
3. 识别不到完整卡片,或画板只是弹窗、toast、底部抽屉等交互态,输出 `trainingEligible=false`
|
|
162
|
+
3. 识别不到完整卡片,或画板只是弹窗、toast、底部抽屉等交互态,输出 `trainingEligible=false` 并记录跳过原因,不进入普通卡训练。**例外(整卡单切图,2026-09-28)**:设计稿把卡片整体导成一张大切图(图层树只剩该图、卡内无文本/按钮标注)时,`detect_artboard_cards` 在常规候选 0 卡后把合格大切图兜底提升为 1 张真卡(纯几何判据:宽≥55%W、高≥12%H、纵向 10%..95%H、区域归属元素仅切图本身;混入任何其他标注不兜底)——`trainingEligible=true`,下游按图像卡(切片绑背景图)建卡。
|
|
163
163
|
4. 低置信度候选宁可拒绝并进入 `rejectedCards`,不得为了提高召回把按钮或气泡当卡片。
|
|
164
164
|
5. `focus-audit` 必须保留 `acceptedCards/rejectedCards/skippedNoCards` 证据,后续训练只消费通过该门的普通卡。
|
|
165
165
|
|
|
@@ -555,6 +555,36 @@ def detect_artboard_cards(ab_raw):
|
|
|
555
555
|
"reason": reason,
|
|
556
556
|
"metrics": focus_metrics,
|
|
557
557
|
})
|
|
558
|
+
|
|
559
|
+
# 整卡单切图兜底(whole-card-as-one-slice):常规候选全被拒(0 卡)时,若板上存在
|
|
560
|
+
# 卡片级大盘切图(卡片被设计稿整体导成一张图——图层树只剩「编组 N」大图),
|
|
561
|
+
# 把该切图提升为 1 张卡。此前这类板 cards 恒为空 → manifest trainingEligible=false
|
|
562
|
+
# → 数据集构建整板跳过,「对话 + 内嵌卡」形态永远进不了训练;而
|
|
563
|
+
# is_text_only_artboard 早已承认这种板「有卡」(has_card_like_visual),两处口径不一致。
|
|
564
|
+
# 判据(纯几何,与 has_card_like_visual 同口径,禁业务词):
|
|
565
|
+
# · 宽 ≥ 55%W、高 ≥ 12%H,纵向落在 10%..95%H(标题栏之下、输入坞/Home 之上);
|
|
566
|
+
# · 「纯单切图」:区域归属元素只有这张大图本身(无文本/其他切图被归属进来)——
|
|
567
|
+
# 标注里只要还有可建结构,就维持低结构门的保守性,不走图像卡路线;
|
|
568
|
+
# · 仅在 0 卡时触发,已有卡的画板行为不变(冻结锚点安全)。
|
|
569
|
+
if not cards:
|
|
570
|
+
for ve in flattened:
|
|
571
|
+
if not ve.is_slice:
|
|
572
|
+
continue
|
|
573
|
+
sx, sy, sw, sh = ve.rect
|
|
574
|
+
if sw < 0.55 * W or sh < 0.12 * H:
|
|
575
|
+
continue
|
|
576
|
+
if sy < 0.10 * H or (sy + sh) > 0.95 * H:
|
|
577
|
+
continue
|
|
578
|
+
region = (sx, sy, sw, sh)
|
|
579
|
+
owned = vision.elements_in_rect(flattened, region)
|
|
580
|
+
if len(owned) != 1 or owned[0] is not ve:
|
|
581
|
+
continue # 区域里混有任何其他标注 → 不兜底,交给既有裁决
|
|
582
|
+
cards.append((
|
|
583
|
+
sx, sy, sw, sh, owned,
|
|
584
|
+
[(region, list(ve.radius or []))],
|
|
585
|
+
vision.card_focus_metrics(owned, region, W, H),
|
|
586
|
+
))
|
|
587
|
+
break # 兜底只提 1 张:多重叠大切图不做连环提升
|
|
558
588
|
return {
|
|
559
589
|
"W": W, "H": H, "layers": raw_layers,
|
|
560
590
|
"overlay": bool(overlay), "sheet": bool(bottom_sheet),
|
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|
|
|
7
7
|
1. 纯文本对话页(信用卡返现 01/03 型):气泡里只有文字 → text_only,被过滤;
|
|
8
8
|
2. 对话 + 内嵌矢量卡(理财 06 型):卡壳里是 3 宫格选项,检测器会读成「控件条」→ 保留;
|
|
9
|
-
3. 对话 + 卡片切图(房贷 32 型):卡片被整体导成一张切图 →
|
|
9
|
+
3. 对话 + 卡片切图(房贷 32 型):卡片被整体导成一张切图 → 整卡单切图兜底产 1 张卡
|
|
10
|
+
(2026-09-28 前 cards 恒为 0,训练集永远盖不到该形态);
|
|
10
11
|
4. 底部抽屉(喜豆 04/05/06 型):alertCard 子卡形态 → 保留(subcard),
|
|
11
12
|
且**产 1 张卡**(矩形由 `vision._bottom_sheet_rect` 几何直取,非候选检测);
|
|
12
13
|
5. 全屏遮罩弹框 → 保留(overlay_dialog);
|
|
13
14
|
6. 0 候选的普通文字页:保守保留,交给下游裁决(维持既有行为)。
|
|
14
15
|
|
|
15
16
|
断言分两层:`kind/skip`(画板类型判定,6 例)+ `cards` 数(产出,注意卡片型用例
|
|
16
|
-
允许 0 —— kind 与 cards
|
|
17
|
+
允许 0 —— kind 与 cards 数解耦是上游有意设计;整卡单切图兜底后用例 3 产 1 张)
|
|
18
|
+
+ 抽屉元素归属(边界气泡不得渗入)。
|
|
17
19
|
"""
|
|
18
20
|
import sys
|
|
19
21
|
from pathlib import Path
|
|
@@ -131,7 +133,7 @@ PLAIN_NO_CANDIDATE = ab("03信用卡返现_有信用卡_无返现记录", chrome
|
|
|
131
133
|
CASES = [
|
|
132
134
|
(CHAT_TEXT_ONLY, "text_only", True, 0, "纯文本对话页(只有问答气泡)"),
|
|
133
135
|
(CHAT_WITH_VECTOR_CARD, "card", False, 0, "对话 + 内嵌矢量卡(3 宫格选项)"),
|
|
134
|
-
(CHAT_WITH_SLICED_CARD, "card", False,
|
|
136
|
+
(CHAT_WITH_SLICED_CARD, "card", False, 1, "对话 + 卡片切图(整卡单切图兜底)"),
|
|
135
137
|
(BOTTOM_SHEET, "subcard", False, 1, "底部抽屉(alertCard 子卡)→ 几何直取 1 张卡"),
|
|
136
138
|
(OVERLAY_DIALOG, "overlay_dialog", False, 0, "全屏遮罩弹框(alertDialog)"),
|
|
137
139
|
(PLAIN_NO_CANDIDATE, "card", False, 0, "0 候选的普通文字页(保守保留)"),
|
|
@@ -196,6 +198,26 @@ def main():
|
|
|
196
198
|
assert vision.has_card_like_visual(flat_text_only, 375, 812) is False, "30x30 图标切图不该判为卡片"
|
|
197
199
|
print("[PASS] has_card_like_visual:中段大盘切图算卡,小图标不算")
|
|
198
200
|
|
|
201
|
+
# 整卡单切图兜底(2026-09-28):0 卡画板上的卡片级大盘切图提升为 1 张真卡,
|
|
202
|
+
# 让「对话 + 内嵌卡(整卡导出为一张图)」形态进入训练集;纯单切图才兜底。
|
|
203
|
+
found = pm.detect_artboard_cards(CHAT_WITH_SLICED_CARD)
|
|
204
|
+
assert len(found["cards"]) == 1, f"整卡单切图应兜底为 1 张卡,实际 {len(found['cards'])}"
|
|
205
|
+
fx, fy, fw, fh, f_els, f_shapes, _fm = found["cards"][0]
|
|
206
|
+
assert (fx, fy, fw, fh) == (16, 276, 343, 280), f"兜底卡矩形应取切图几何,实际 {(fx, fy, fw, fh)}"
|
|
207
|
+
assert len(f_els) == 1 and f_els[0].is_slice, "纯单切图兜底:归属元素应只有切图本身"
|
|
208
|
+
# 混入其他标注(气泡文字渗进切图区域)→ 不兜底,维持 0 卡
|
|
209
|
+
MIXED = ab("32房贷利率调整_混合标注", chrome() + bubble(
|
|
210
|
+
196, 100, 163, 42, text("调整下我的房贷利率", 208, 110, 140, 22)) + [
|
|
211
|
+
L("应答气泡", "shape", 16, 156, 343, 108, radius=[0, 12, 12, 12], hexv="#FFFFFFFF"),
|
|
212
|
+
text("您的贷款为深圳地区二套住房贷款……", 30, 166, 315, 88),
|
|
213
|
+
L("编组 4", "slice", 16, 276, 343, 280),
|
|
214
|
+
text("浮层标注", 30, 300, 200, 24),
|
|
215
|
+
])
|
|
216
|
+
found_mixed = pm.detect_artboard_cards(MIXED)
|
|
217
|
+
assert len(found_mixed["cards"]) == 0, \
|
|
218
|
+
f"切图区域内混有文本标注时不得兜底,实际 {len(found_mixed['cards'])} 卡"
|
|
219
|
+
print("[PASS] 整卡单切图兜底:纯切图 → 1 卡(矩形=切图几何);混入标注 → 不兜底")
|
|
220
|
+
|
|
199
221
|
# 同名画板消歧(2026-09-24):Sketch 允许两个画板**完全同名**——实测「地址管理」稿
|
|
200
222
|
# 25 板里两个「11…_失败_再次修改」(375×1232/129 层 vs 375×1252/130 层,两个不同设计)。
|
|
201
223
|
# 目录名直接用 name 时后跑的会整板覆盖先跑的(产物静默丢失),且 manifest 两条 entry
|
|
@@ -899,7 +899,13 @@ def _is_divider(ve):
|
|
|
899
899
|
|
|
900
900
|
|
|
901
901
|
def card_focus_metrics(elements, rect, width, height):
|
|
902
|
-
"""量化候选区域的结构复杂度,供聚焦提取统一过滤。
|
|
902
|
+
"""量化候选区域的结构复杂度,供聚焦提取统一过滤。
|
|
903
|
+
|
|
904
|
+
切片(slice)是**内容**(设计稿导出的真实图像),不是卡壳——「面积 ≥ 55% 卡区
|
|
905
|
+
跳过」的壳层排除规则只适用于 shape/symbol 背景层,不得吞掉切片计数,否则
|
|
906
|
+
「整卡 = 一张大切图」的形态会被统计成零结构(真踩过:slice 兜底卡 rich=0)。
|
|
907
|
+
sliceAreaRatio:切片覆盖卡区的面积比,供下游识别「图像卡」形态。
|
|
908
|
+
"""
|
|
903
909
|
x, y, w, h = rect
|
|
904
910
|
card_area = max(w * h, 1)
|
|
905
911
|
text_count = 0
|
|
@@ -907,6 +913,7 @@ def card_focus_metrics(elements, rect, width, height):
|
|
|
907
913
|
slice_count = 0
|
|
908
914
|
symbol_count = 0
|
|
909
915
|
shape_count = 0
|
|
916
|
+
slice_area = 0.0
|
|
910
917
|
for ve in elements:
|
|
911
918
|
if _intersect_ratio(ve.rect, rect) < 0.35:
|
|
912
919
|
continue
|
|
@@ -914,12 +921,16 @@ def card_focus_metrics(elements, rect, width, height):
|
|
|
914
921
|
if ve.area > 0:
|
|
915
922
|
text_count += 1
|
|
916
923
|
continue
|
|
917
|
-
if _is_divider(ve)
|
|
924
|
+
if _is_divider(ve):
|
|
918
925
|
continue
|
|
919
926
|
if ve.is_slice:
|
|
920
927
|
slice_count += 1
|
|
921
928
|
rich_count += 1
|
|
922
|
-
|
|
929
|
+
slice_area += _intersect_ratio(ve.rect, rect) * ve.area
|
|
930
|
+
continue
|
|
931
|
+
if ve.area >= 0.55 * card_area:
|
|
932
|
+
continue
|
|
933
|
+
if ve.type == "symbol":
|
|
923
934
|
symbol_count += 1
|
|
924
935
|
rich_count += 1
|
|
925
936
|
elif ve.type == "shape" and (ve.fills or ve.border or any(v > 0 for v in _radius_values(ve.radius))):
|
|
@@ -932,6 +943,7 @@ def card_focus_metrics(elements, rect, width, height):
|
|
|
932
943
|
"textCount": text_count,
|
|
933
944
|
"richComponentCount": rich_count,
|
|
934
945
|
"sliceCount": slice_count,
|
|
946
|
+
"sliceAreaRatio": min(1.0, slice_area / card_area),
|
|
935
947
|
"symbolCount": symbol_count,
|
|
936
948
|
"shapeCount": shape_count,
|
|
937
949
|
}
|
|
@@ -55,7 +55,68 @@
|
|
|
55
55
|
"width": "375",
|
|
56
56
|
"className": "Root",
|
|
57
57
|
"style": {
|
|
58
|
-
"properties": [
|
|
58
|
+
"properties": [
|
|
59
|
+
{
|
|
60
|
+
"itemId": "100",
|
|
61
|
+
"itemKey": "flex",
|
|
62
|
+
"itemValueFrom": "0",
|
|
63
|
+
"itemValue": {},
|
|
64
|
+
"itemChildren": {
|
|
65
|
+
"properties": [
|
|
66
|
+
{
|
|
67
|
+
"itemId": "003",
|
|
68
|
+
"itemKey": "gap",
|
|
69
|
+
"itemValueFrom": "0",
|
|
70
|
+
"itemValue": "0"
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"itemId": "400",
|
|
77
|
+
"itemKey": "padding",
|
|
78
|
+
"itemValueFrom": "0",
|
|
79
|
+
"itemValue": {},
|
|
80
|
+
"itemChildren": {
|
|
81
|
+
"properties": [
|
|
82
|
+
{
|
|
83
|
+
"itemId": "001",
|
|
84
|
+
"itemKey": "paddingType",
|
|
85
|
+
"itemValueFrom": "0",
|
|
86
|
+
"itemValue": "0"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"itemId": "002",
|
|
90
|
+
"itemKey": "padding",
|
|
91
|
+
"itemValueFrom": "0",
|
|
92
|
+
"itemValue": "0"
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"itemId": "500",
|
|
99
|
+
"itemKey": "margin",
|
|
100
|
+
"itemValueFrom": "0",
|
|
101
|
+
"itemValue": {},
|
|
102
|
+
"itemChildren": {
|
|
103
|
+
"properties": [
|
|
104
|
+
{
|
|
105
|
+
"itemId": "001",
|
|
106
|
+
"itemKey": "marginType",
|
|
107
|
+
"itemValueFrom": "0",
|
|
108
|
+
"itemValue": "0"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"itemId": "002",
|
|
112
|
+
"itemKey": "margin",
|
|
113
|
+
"itemValueFrom": "0",
|
|
114
|
+
"itemValue": "0"
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
]
|
|
59
120
|
},
|
|
60
121
|
"properties": [],
|
|
61
122
|
"eventactions": [],
|
|
@@ -32,7 +32,7 @@ function parseEnvFile(file) {
|
|
|
32
32
|
* @param {string} [root] 项目根(含 .env.cb);默认 process.cwd()
|
|
33
33
|
* @returns {{
|
|
34
34
|
* FINMALL_BASE_URL:string, FINMALL_APP_ID:string, FINMALL_WORKSPACE_ID:string,
|
|
35
|
-
* CARDBUDDY_CDP_PORT:number, CARDBUDDY_DEV_PORT:number
|
|
35
|
+
* CARDBUDDY_CDP_PORT:number, CARDBUDDY_DEV_PORT:number
|
|
36
36
|
* }}
|
|
37
37
|
*/
|
|
38
38
|
export function loadFinmallEnv(root = process.cwd()) {
|
|
@@ -51,8 +51,6 @@ export function loadFinmallEnv(root = process.cwd()) {
|
|
|
51
51
|
CARDBUDDY_CDP_PORT: Number(env.CARDBUDDY_CDP_PORT || 9222),
|
|
52
52
|
/** 本地 dev server 监听端口(vite / 截图 / MCP / 各类工具默认连此端口) */
|
|
53
53
|
CARDBUDDY_DEV_PORT: Number(env.CARDBUDDY_DEV_PORT || 5180),
|
|
54
|
-
/** 远端编辑器 Web 访问别名(站点下挂载路径,部署到别的目录时改这里) */
|
|
55
|
-
FINMALL_EDITOR_PATH: (env.FINMALL_EDITOR_PATH || '/ubomp-h5-sub').replace(/\/+$/, ''),
|
|
56
54
|
}
|
|
57
55
|
}
|
|
58
56
|
|
|
@@ -64,7 +62,6 @@ export const FINMALL_APP_ID = d.FINMALL_APP_ID
|
|
|
64
62
|
export const FINMALL_WORKSPACE_ID = d.FINMALL_WORKSPACE_ID
|
|
65
63
|
export const CARDBUDDY_CDP_PORT = d.CARDBUDDY_CDP_PORT
|
|
66
64
|
export const CARDBUDDY_DEV_PORT = d.CARDBUDDY_DEV_PORT
|
|
67
|
-
export const FINMALL_EDITOR_PATH = d.FINMALL_EDITOR_PATH
|
|
68
65
|
|
|
69
66
|
/**
|
|
70
67
|
* 本地 dev server 的 HTTP 基址。host 默认 127.0.0.1;
|
|
@@ -76,21 +73,7 @@ export function devApiBase(host = '127.0.0.1') {
|
|
|
76
73
|
return `http://${host}:${CARDBUDDY_DEV_PORT}`
|
|
77
74
|
}
|
|
78
75
|
|
|
79
|
-
/**
|
|
80
|
-
* 构造远端编辑器访问 URL(线上属性面板比对 / 真机预览用)。
|
|
81
|
-
* @param {string|number} pageId 数字型 cardId(pageId)
|
|
82
|
-
* @returns {string}
|
|
83
|
-
*/
|
|
84
|
-
export function remoteEditorUrl(pageId) {
|
|
85
|
-
return (
|
|
86
|
-
`${FINMALL_BASE_URL}${FINMALL_EDITOR_PATH}/` +
|
|
87
|
-
`?platform=flame&appId=${FINMALL_APP_ID}&workspaceId=${FINMALL_WORKSPACE_ID}` +
|
|
88
|
-
`#/inner_card/editor/design?id=${pageId}&showType=view`
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
76
|
export default {
|
|
93
77
|
...d,
|
|
94
78
|
devApiBase,
|
|
95
|
-
remoteEditorUrl,
|
|
96
79
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cardbuddy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Local dev service for the CardBuddy card editor — decouples card CRUD (sync/save/validate/render-status/screenshot) from the static public editor, so the editor can be deployed to any static host while each developer runs a local service against their own card source.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|