@topmindspace/tms-skills 0.3.6 → 0.4.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.
- package/CHANGELOG.md +153 -0
- package/README.en.md +12 -9
- package/README.md +12 -9
- package/bin/tms-skills.js +153 -13
- package/package.json +1 -1
- package/top-ppt-html/README.en.md +2 -2
- package/top-ppt-html/README.md +1 -1
- package/top-ppt-html/SKILL.md +8 -7
- package/top-ppt-html/assets/examples/2026-09-09-architecture-graphite-dark.html +58 -184
- package/top-ppt-html/assets/examples/2026-09-09-architecture-graphite-dark.model.json +13 -3
- package/top-ppt-html/assets/examples/2026-09-09-presentation-business-blue.html +58 -337
- package/top-ppt-html/assets/examples/2026-09-09-presentation-business-blue.model.json +15 -1
- package/top-ppt-html/assets/examples/2026-09-09-research-mckinsey.html +58 -930
- package/top-ppt-html/assets/examples/2026-09-09-research-mckinsey.model.json +15 -1
- package/top-ppt-html/assets/examples/2026-09-26-topmind-tms-skills-showcase.html +141 -31
- package/top-ppt-html/assets/icons/index.json +557 -0
- package/top-ppt-html/assets/icons//344/270/212/344/274/240.png +0 -0
- package/top-ppt-html/assets/icons//344/270/212/344/274/240.svg +1 -0
- package/top-ppt-html/assets/icons//344/270/213/350/275/275.png +0 -0
- package/top-ppt-html/assets/icons//344/270/213/350/275/275.svg +1 -0
- package/top-ppt-html/assets/icons//344/270/213/351/231/215.png +0 -0
- package/top-ppt-html/assets/icons//344/270/213/351/231/215.svg +1 -0
- package/top-ppt-html/assets/icons//344/271/246/347/255/276.png +0 -0
- package/top-ppt-html/assets/icons//344/271/246/347/255/276.svg +1 -0
- package/top-ppt-html/assets/icons//344/272/221.png +0 -0
- package/top-ppt-html/assets/icons//344/272/221.svg +1 -0
- package/top-ppt-html/assets/icons//344/275/215/347/275/256.png +0 -0
- package/top-ppt-html/assets/icons//344/275/215/347/275/256.svg +1 -0
- package/top-ppt-html/assets/icons//344/277/241/346/201/257.png +0 -0
- package/top-ppt-html/assets/icons//344/277/241/346/201/257.svg +1 -0
- package/top-ppt-html/assets/icons//345/210/206/344/272/253.png +0 -0
- package/top-ppt-html/assets/icons//345/210/206/344/272/253.svg +1 -0
- package/top-ppt-html/assets/icons//345/213/276/351/200/211.png +0 -0
- package/top-ppt-html/assets/icons//345/213/276/351/200/211.svg +1 -0
- package/top-ppt-html/assets/icons//345/215/240/346/257/224.png +0 -0
- package/top-ppt-html/assets/icons//345/215/240/346/257/224.svg +1 -0
- package/top-ppt-html/assets/icons//345/233/242/351/230/237.png +0 -0
- package/top-ppt-html/assets/icons//345/233/242/351/230/237.svg +1 -0
- package/top-ppt-html/assets/icons//345/233/276/350/241/250.png +0 -0
- package/top-ppt-html/assets/icons//345/233/276/350/241/250.svg +1 -0
- package/top-ppt-html/assets/icons//345/242/236/351/225/277.png +0 -0
- package/top-ppt-html/assets/icons//345/242/236/351/225/277.svg +1 -0
- package/top-ppt-html/assets/icons//345/256/211/345/205/250.png +0 -0
- package/top-ppt-html/assets/icons//345/256/211/345/205/250.svg +1 -0
- package/top-ppt-html/assets/icons//345/257/271/350/257/235.png +0 -0
- package/top-ppt-html/assets/icons//345/257/271/350/257/235.svg +1 -0
- package/top-ppt-html/assets/icons//345/267/245/345/205/267.png +0 -0
- package/top-ppt-html/assets/icons//345/267/245/345/205/267.svg +1 -0
- package/top-ppt-html/assets/icons//345/276/252/347/216/257.png +0 -0
- package/top-ppt-html/assets/icons//345/276/252/347/216/257.svg +1 -0
- package/top-ppt-html/assets/icons//346/210/220/346/236/234.png +0 -0
- package/top-ppt-html/assets/icons//346/210/220/346/236/234.svg +1 -0
- package/top-ppt-html/assets/icons//346/211/213/346/234/272.png +0 -0
- package/top-ppt-html/assets/icons//346/211/213/346/234/272.svg +1 -0
- package/top-ppt-html/assets/icons//346/225/210/347/216/207.png +0 -0
- package/top-ppt-html/assets/icons//346/225/210/347/216/207.svg +1 -0
- package/top-ppt-html/assets/icons//346/225/260/346/215/256.png +0 -0
- package/top-ppt-html/assets/icons//346/225/260/346/215/256.svg +1 -0
- package/top-ppt-html/assets/icons//346/226/207/344/273/266/345/244/271.png +0 -0
- package/top-ppt-html/assets/icons//346/226/207/344/273/266/345/244/271.svg +1 -0
- package/top-ppt-html/assets/icons//346/226/207/346/241/243.png +0 -0
- package/top-ppt-html/assets/icons//346/226/207/346/241/243.svg +1 -0
- package/top-ppt-html/assets/icons//346/227/227/345/270/234.png +0 -0
- package/top-ppt-html/assets/icons//346/227/227/345/270/234.svg +1 -0
- package/top-ppt-html/assets/icons//346/227/266/351/227/264.png +0 -0
- package/top-ppt-html/assets/icons//346/227/266/351/227/264.svg +1 -0
- package/top-ppt-html/assets/icons//346/230/237/346/240/207.png +0 -0
- package/top-ppt-html/assets/icons//346/230/237/346/240/207.svg +1 -0
- package/top-ppt-html/assets/icons//346/231/272/350/203/275.png +0 -0
- package/top-ppt-html/assets/icons//346/231/272/350/203/275.svg +1 -0
- package/top-ppt-html/assets/icons//346/235/203/351/231/220.png +0 -0
- package/top-ppt-html/assets/icons//346/235/203/351/231/220.svg +1 -0
- package/top-ppt-html/assets/icons//346/243/200/346/237/245.png +0 -0
- package/top-ppt-html/assets/icons//346/243/200/346/237/245.svg +1 -0
- package/top-ppt-html/assets/icons//346/264/236/345/257/237.png +0 -0
- package/top-ppt-html/assets/icons//346/264/236/345/257/237.svg +1 -0
- package/top-ppt-html/assets/icons//346/265/201/347/250/213.png +0 -0
- package/top-ppt-html/assets/icons//346/265/201/347/250/213.svg +1 -0
- package/top-ppt-html/assets/icons//346/270/205/345/215/225.png +0 -0
- package/top-ppt-html/assets/icons//346/270/205/345/215/225.svg +1 -0
- package/top-ppt-html/assets/icons//347/201/253/347/256/255.png +0 -0
- package/top-ppt-html/assets/icons//347/201/253/347/256/255.svg +1 -0
- package/top-ppt-html/assets/icons//347/201/257/346/263/241.png +0 -0
- package/top-ppt-html/assets/icons//347/201/257/346/263/241.svg +1 -0
- package/top-ppt-html/assets/icons//347/224/250/346/210/267.png +0 -0
- package/top-ppt-html/assets/icons//347/224/250/346/210/267.svg +1 -0
- package/top-ppt-html/assets/icons//347/233/256/346/240/207.png +0 -0
- package/top-ppt-html/assets/icons//347/233/256/346/240/207.svg +1 -0
- package/top-ppt-html/assets/icons//347/256/255/345/244/264.png +0 -0
- package/top-ppt-html/assets/icons//347/256/255/345/244/264.svg +1 -0
- package/top-ppt-html/assets/icons//347/273/204/347/273/207.png +0 -0
- package/top-ppt-html/assets/icons//347/273/204/347/273/207.svg +1 -0
- package/top-ppt-html/assets/icons//347/275/227/347/233/230.png +0 -0
- package/top-ppt-html/assets/icons//347/275/227/347/233/230.svg +1 -0
- package/top-ppt-html/assets/icons//350/241/250/346/240/274.png +0 -0
- package/top-ppt-html/assets/icons//350/241/250/346/240/274.svg +1 -0
- package/top-ppt-html/assets/icons//350/256/241/345/210/222.png +0 -0
- package/top-ppt-html/assets/icons//350/256/241/345/210/222.svg +1 -0
- package/top-ppt-html/assets/icons//350/256/276/347/275/256.png +0 -0
- package/top-ppt-html/assets/icons//350/256/276/347/275/256.svg +1 -0
- package/top-ppt-html/assets/icons//350/266/213/345/212/277.png +0 -0
- package/top-ppt-html/assets/icons//350/266/213/345/212/277.svg +1 -0
- package/top-ppt-html/assets/icons//351/202/256/344/273/266.png +0 -0
- package/top-ppt-html/assets/icons//351/202/256/344/273/266.svg +1 -0
- package/top-ppt-html/assets/icons//351/207/221/351/222/261.png +0 -0
- package/top-ppt-html/assets/icons//351/207/221/351/222/261.svg +1 -0
- package/top-ppt-html/assets/icons//351/223/276/346/216/245.png +0 -0
- package/top-ppt-html/assets/icons//351/223/276/346/216/245.svg +1 -0
- package/top-ppt-html/assets/icons//351/227/252/347/224/265.png +0 -0
- package/top-ppt-html/assets/icons//351/227/252/347/224/265.svg +1 -0
- package/top-ppt-html/assets/icons//351/243/216/351/231/251.png +0 -0
- package/top-ppt-html/assets/icons//351/243/216/351/231/251.svg +1 -0
- package/top-ppt-html/assets/pptx-export.js +44 -15
- package/top-ppt-html/assets/style-gallery.html +2 -2
- package/top-ppt-html/assets/templates/architecture.html +59 -17
- package/top-ppt-html/assets/templates/engine.css +12 -0
- package/top-ppt-html/assets/templates/presentation.html +59 -17
- package/top-ppt-html/assets/templates/research.html +59 -17
- package/top-ppt-html/package-lock.json +2 -2
- package/top-ppt-html/package.json +2 -2
- package/top-ppt-html/references/icons.md +22 -19
- package/top-ppt-html/references/layout-variants.md +69 -0
- package/top-ppt-html/references/style-pack.md +40 -0
- package/top-ppt-html/scripts/audit_css.py +1 -1
- package/top-ppt-html/scripts/build_pptx.js +228 -50
- package/top-ppt-html/scripts/check_icons.py +119 -0
- package/top-ppt-html/scripts/hydrate_charts.py +60 -5
- package/top-ppt-html/scripts/icon_lib.js +66 -37
- package/top-ppt-html/scripts/layout-constants.json +92 -5
- package/top-ppt-html/scripts/make_icon_pack.py +172 -0
- package/top-ppt-html/scripts/model-schema.json +4 -1
- package/top-ppt-html/scripts/regression.py +3 -3
- package/top-ppt-html/scripts/render_compare.py +1 -1
- package/top-ppt-html/scripts/render_from_model.py +116 -55
- package/top-ppt-html/scripts/scaffold_report.py +1 -1
- package/top-ppt-html/scripts/sync_runtime.py +14 -14
- package/top-ppt-html/scripts/validate_pptx.py +1 -0
- package/top-ppt-html/scripts/validate_report.py +157 -0
- package/topmind-cover/README.en.md +4 -4
- package/topmind-cover/README.md +4 -4
- package/topmind-cover/SKILL.md +36 -16
- package/topmind-cover/assets/examples/README.md +13 -7
- package/topmind-cover/assets/examples/bg-blur-alt.png +0 -0
- package/topmind-cover/assets/examples/bg-blur-wechat.png +0 -0
- package/topmind-cover/assets/examples/bg-blur.png +0 -0
- package/topmind-cover/assets/examples/big-type-wechat.png +0 -0
- package/topmind-cover/assets/examples/big-type.png +0 -0
- package/topmind-cover/assets/examples/brand-launch-wechat.png +0 -0
- package/topmind-cover/assets/examples/brand-launch.png +0 -0
- package/topmind-cover/assets/examples/gan-huo-wechat.png +0 -0
- package/topmind-cover/assets/examples/gan-huo.png +0 -0
- package/topmind-cover/assets/examples/ip-fun-wechat.png +0 -0
- package/topmind-cover/assets/examples/ip-fun.png +0 -0
- package/topmind-cover/assets/examples/magazine-wechat.png +0 -0
- package/topmind-cover/assets/examples/magazine.png +0 -0
- package/topmind-cover/assets/examples/minimal-wechat.png +0 -0
- package/topmind-cover/assets/examples/minimal.png +0 -0
- package/topmind-cover/assets/examples/news-flash-wechat.png +0 -0
- package/topmind-cover/assets/examples/news-flash.png +0 -0
- package/topmind-cover/assets/examples/overview.png +0 -0
- package/topmind-cover/assets/examples/paper-collage-alt.png +0 -0
- package/topmind-cover/assets/examples/paper-collage-wechat.png +0 -0
- package/topmind-cover/assets/examples/paper-collage.png +0 -0
- package/topmind-cover/assets/examples/tutorial-steps-wechat.png +0 -0
- package/topmind-cover/assets/examples/tutorial-steps.png +0 -0
- package/topmind-cover/assets/examples/white-clean-alt-wechat.png +0 -0
- package/topmind-cover/assets/examples/white-clean-alt.png +0 -0
- package/topmind-cover/assets/examples/white-clean-wechat.png +0 -0
- package/topmind-cover/assets/examples/white-clean.png +0 -0
- package/topmind-cover/package.json +2 -2
- package/topmind-cover/references/cover-styles.md +210 -35
- package/topmind-cover/references/impact-language.md +43 -2
- package/topmind-cover/references/ref-white-clean-1.png +0 -0
- package/topmind-cover/references/ref-white-clean-2.png +0 -0
- package/topmind-cover/scripts/crop-cover.py +19 -13
- package/topmind-wechat-post/README.en.md +2 -2
- package/topmind-wechat-post/README.md +2 -2
- package/topmind-wechat-post/SKILL.md +1 -1
- package/topmind-wechat-post/scripts/md2wechat.py +142 -30
- package/topmind-wechat-post/scripts/negative_tests.py +74 -0
- package/topmind-x-article/README.en.md +1 -1
- package/topmind-x-article/README.md +1 -1
- package/topmind-x-article/scripts/md2x.py +25 -5
- package/topmind-x-article/scripts/negative_tests.py +17 -0
|
@@ -13,6 +13,46 @@ from pathlib import Path
|
|
|
13
13
|
|
|
14
14
|
from render_from_model import extract_model_span
|
|
15
15
|
|
|
16
|
+
_ICON_DIR = Path(__file__).resolve().parent.parent / 'assets' / 'icons'
|
|
17
|
+
_ICON_CACHE: dict[str, str] = {}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _icon_svg(name: str) -> str | None:
|
|
21
|
+
"""取图标包 SVG(内联用);缺图标返回 None(调用方保留原标记,由 validate WARN)。"""
|
|
22
|
+
if name in _ICON_CACHE:
|
|
23
|
+
return _ICON_CACHE[name]
|
|
24
|
+
p = _ICON_DIR / f'{name}.svg'
|
|
25
|
+
try:
|
|
26
|
+
svg = p.read_text(encoding='utf-8').strip()
|
|
27
|
+
except OSError:
|
|
28
|
+
return None
|
|
29
|
+
_ICON_CACHE[name] = svg
|
|
30
|
+
return svg
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# 手写 HTML 模板里的图标占位:<i data-icon="增长"></i> → 内联 SVG(currentColor 随主题变色)
|
|
34
|
+
# 未知图标名保留原标记(validate_report 报 WARN,不静默吞掉)
|
|
35
|
+
ICON_MARK_RE = re.compile(r'<i\s+data-icon="([^"]+)"\s*(?:/>|>\s*</i\s*>)')
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def hydrate_icons(html: str) -> tuple[str, int, list[str]]:
|
|
39
|
+
"""内联 icon 标记。返回 (new_html, 成功替换数, 未知图标名列表)。"""
|
|
40
|
+
unknown: list[str] = []
|
|
41
|
+
replaced = [0]
|
|
42
|
+
|
|
43
|
+
def repl(m):
|
|
44
|
+
name = m.group(1)
|
|
45
|
+
svg = _icon_svg(name)
|
|
46
|
+
if svg is None:
|
|
47
|
+
if name not in unknown:
|
|
48
|
+
unknown.append(name)
|
|
49
|
+
return m.group(0)
|
|
50
|
+
replaced[0] += 1 # 只计真正替换的;未知名保留原标记,不计入
|
|
51
|
+
return svg
|
|
52
|
+
|
|
53
|
+
new_html, _ = ICON_MARK_RE.subn(repl, html)
|
|
54
|
+
return new_html, replaced[0], unknown
|
|
55
|
+
|
|
16
56
|
PLACEHOLDER_SVG_RE = re.compile(
|
|
17
57
|
r'<svg class="chart" data-chart="([^"]+)" viewBox="0 0 560 220">\s*'
|
|
18
58
|
r'<!--[\s\S]*?-->\s*'
|
|
@@ -336,20 +376,26 @@ def iter_chart_payloads(model: dict):
|
|
|
336
376
|
|
|
337
377
|
|
|
338
378
|
def hydrate(html: str, model: dict) -> str:
|
|
379
|
+
"""占位 SVG → 真实图表 SVG。
|
|
380
|
+
弹性(0.2.0):单图数据非法只留占位并记入 errors,不中断整份报告其余图的水合。"""
|
|
339
381
|
payloads = list(iter_chart_payloads(model))
|
|
340
382
|
idx = 0
|
|
383
|
+
errors: list = []
|
|
341
384
|
|
|
342
385
|
def repl(m):
|
|
343
386
|
nonlocal idx
|
|
344
387
|
if idx >= len(payloads):
|
|
345
388
|
return m.group(0)
|
|
346
389
|
ch, ctx = payloads[idx]
|
|
347
|
-
svg = build_svg(ch, ctx)
|
|
348
390
|
idx += 1
|
|
349
|
-
|
|
391
|
+
try:
|
|
392
|
+
return build_svg(ch, ctx)
|
|
393
|
+
except ChartDataError as e:
|
|
394
|
+
errors.append(str(e))
|
|
395
|
+
return m.group(0)
|
|
350
396
|
|
|
351
397
|
new_html, n = PLACEHOLDER_SVG_RE.subn(repl, html)
|
|
352
|
-
return new_html, n, idx, len(payloads)
|
|
398
|
+
return new_html, n, idx, len(payloads), errors
|
|
353
399
|
|
|
354
400
|
|
|
355
401
|
def main() -> int:
|
|
@@ -382,13 +428,22 @@ def main() -> int:
|
|
|
382
428
|
print(f'FAIL: REPORT_MODEL 根节点必须是对象(得到 {type(model).__name__})')
|
|
383
429
|
return 1
|
|
384
430
|
try:
|
|
385
|
-
new_html, n_ph, used, total = hydrate(html, model)
|
|
431
|
+
new_html, n_ph, used, total, chart_errors = hydrate(html, model)
|
|
386
432
|
except ChartDataError as e:
|
|
433
|
+
# 防御:hydrate 内部已按图捕获,此处仅兜底批量抽取阶段的非法数据
|
|
387
434
|
print(f'FAIL: 图表数据非法——{e}')
|
|
388
435
|
return 1
|
|
436
|
+
for e in chart_errors:
|
|
437
|
+
print(f'FAIL: 图表数据非法——{e}(该图保留占位,其余图已水合)')
|
|
438
|
+
rc = 1 if chart_errors else 0
|
|
439
|
+
new_html, n_icon, unknown_icons = hydrate_icons(new_html)
|
|
440
|
+
if unknown_icons:
|
|
441
|
+
print(f'WARN: 未知图标名(已保留原标记): {", ".join(unknown_icons)}')
|
|
389
442
|
path.write_text(new_html, encoding='utf-8')
|
|
390
443
|
left = new_html.count('见模型数据')
|
|
391
|
-
print(f'hydrated placeholders={n_ph} used_payloads={used}/{total} remaining_placeholder={left}')
|
|
444
|
+
print(f'hydrated placeholders={n_ph} icons={n_icon} used_payloads={used}/{total} remaining_placeholder={left}')
|
|
445
|
+
if rc:
|
|
446
|
+
return rc # 有图表数据非法:文件已落盘(坏图留占位),但以非零码报告
|
|
392
447
|
return 0 if left == 0 else 1
|
|
393
448
|
|
|
394
449
|
|
|
@@ -1,53 +1,82 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TopPPT HTML · 语义图标库(
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* TopPPT HTML · 语义图标库(48 · 24×24 · stroke 1.8 圆角线帽 · currentColor)
|
|
3
|
+
* 单源:assets/icons/<name>.svg + assets/icons/index.json(关键词映射 + 默认表)。
|
|
4
|
+
* 由 scripts/make_icon_pack.py 生成。HTML 内联 / build_pptx.js PNG 真导出共用。
|
|
5
|
+
* 图标图 objectName 统一 `icon:` 前缀,validate_pptx 单独计数,不进 pictures 内容图门禁。
|
|
6
6
|
*/
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const ICON_DIR = path.join(__dirname, '..', 'assets', 'icons');
|
|
9
13
|
const SVG_OPEN = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">';
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
15
|
+
let ICONS = {};
|
|
16
|
+
let ORDER = [];
|
|
17
|
+
let INDEX = { defaults: [], icons: {} };
|
|
18
|
+
|
|
19
|
+
function _load() {
|
|
20
|
+
try {
|
|
21
|
+
INDEX = JSON.parse(fs.readFileSync(path.join(ICON_DIR, 'index.json'), 'utf-8'));
|
|
22
|
+
} catch (e) {
|
|
23
|
+
INDEX = { defaults: [], icons: {} };
|
|
24
|
+
}
|
|
25
|
+
const names = Object.keys(INDEX.icons || {});
|
|
26
|
+
for (const name of names) {
|
|
27
|
+
try {
|
|
28
|
+
ICONS[name] = fs.readFileSync(path.join(ICON_DIR, `${name}.svg`), 'utf-8').trim();
|
|
29
|
+
} catch (e) { /* 缺文件则跳过,validate 会 WARN */ }
|
|
30
|
+
}
|
|
31
|
+
ORDER = Object.keys(ICONS);
|
|
32
|
+
if (!ORDER.length) {
|
|
33
|
+
// 极端回落:包不可读时给一个空壳,避免调用方崩溃
|
|
34
|
+
ICONS = { '信息': SVG_OPEN + '<circle cx="12" cy="12" r="9"/><path d="M12 11v5"/><path d="M12 7.5h.01"/></svg>' };
|
|
35
|
+
ORDER = ['信息'];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
_load();
|
|
39
|
+
|
|
40
|
+
/** 默认图标表(index.json defaults;选不出语义图标时轮换) */
|
|
41
|
+
function defaultNames() {
|
|
42
|
+
const ds = (INDEX.defaults || []).filter((n) => ICONS[n]);
|
|
43
|
+
return ds.length ? ds : ORDER;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 按标题语义挑图标名:关键词命中 → 默认表轮换(不盲目轮换全量 48)。
|
|
48
|
+
* 与 render_from_model.pick_icon_name 同策略(同读 index.json)。
|
|
49
|
+
*/
|
|
38
50
|
function pickIconName(title, idx) {
|
|
39
51
|
const t = String(title || '');
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
const meta = INDEX.icons || {};
|
|
53
|
+
for (const name of ORDER) {
|
|
54
|
+
const kws = (meta[name] && meta[name].keywords) || [name];
|
|
55
|
+
for (const kw of kws) {
|
|
56
|
+
if (kw && t.includes(kw)) return name;
|
|
57
|
+
}
|
|
42
58
|
}
|
|
43
|
-
|
|
59
|
+
const ds = defaultNames();
|
|
60
|
+
return ds[(idx || 0) % ds.length];
|
|
44
61
|
}
|
|
45
62
|
|
|
46
|
-
/** 渲染为带指定描边色的 SVG
|
|
63
|
+
/** 渲染为带指定描边色的 SVG 字符串(currentColor → 目标色) */
|
|
47
64
|
function iconSvg(name, color) {
|
|
48
|
-
const raw = ICONS[name] || ICONS[ORDER[0]];
|
|
65
|
+
const raw = ICONS[name] || ICONS[defaultNames()[0]] || ICONS[ORDER[0]];
|
|
49
66
|
const c = color || '#1a56a8';
|
|
50
67
|
return raw.replace('stroke="currentColor"', `stroke="${c}"`);
|
|
51
68
|
}
|
|
52
69
|
|
|
53
|
-
|
|
70
|
+
/** 预渲染 PNG(512px)文件路径;无 sharp 环境下 PPTX 通道的可靠回落 */
|
|
71
|
+
function iconPngPath(name) {
|
|
72
|
+
const n = ICONS[name] ? name : pickIconName(name, 0);
|
|
73
|
+
const p = path.join(ICON_DIR, `${n}.png`);
|
|
74
|
+
try {
|
|
75
|
+
fs.accessSync(p, fs.constants.R_OK);
|
|
76
|
+
return p;
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = { ICONS, ORDER, INDEX, pickIconName, iconSvg, iconPngPath, SVG_OPEN, defaultNames };
|
|
@@ -383,7 +383,7 @@
|
|
|
383
383
|
"legendRowH": 0.55,
|
|
384
384
|
"legendLabelW": 3.4,
|
|
385
385
|
"legendValW": 1.3,
|
|
386
|
-
"legendPctX":
|
|
386
|
+
"legendPctX": 10.8,
|
|
387
387
|
"legendPctW": 1.3
|
|
388
388
|
},
|
|
389
389
|
"steps": {
|
|
@@ -1408,7 +1408,35 @@
|
|
|
1408
1408
|
"preferContentFirst": true,
|
|
1409
1409
|
"warnBelow": 9.5
|
|
1410
1410
|
},
|
|
1411
|
-
"continuousTextRule": "语义连续的一句话 / so-what 主句 / 结论句 / 表格单元格正文不得拆成多个独立文本框(避免异常空格、断句、基线漂移);局部加粗高亮必须用同一文本框内富文本。"
|
|
1411
|
+
"continuousTextRule": "语义连续的一句话 / so-what 主句 / 结论句 / 表格单元格正文不得拆成多个独立文本框(避免异常空格、断句、基线漂移);局部加粗高亮必须用同一文本框内富文本。",
|
|
1412
|
+
"adaptiveText": {
|
|
1413
|
+
"$comment": "弹性文本单源(0.2.0):长标题/长列表/长表格单元格的降级规则。build_pptx.js 只读本表(AT),禁止在调用处散落 magic number。tier 语义:字符数按 CJK=1、拉丁/数字≈0.5 折算(与 estLines 同口径)。",
|
|
1414
|
+
"title": {
|
|
1415
|
+
"base": 30,
|
|
1416
|
+
"step1": 24,
|
|
1417
|
+
"longChars": 28,
|
|
1418
|
+
"longMax": 24,
|
|
1419
|
+
"xlongChars": 44,
|
|
1420
|
+
"xlongMax": 20
|
|
1421
|
+
},
|
|
1422
|
+
"list": {
|
|
1423
|
+
"denseItems": 6,
|
|
1424
|
+
"gapFactor": 0.5,
|
|
1425
|
+
"denseGapFactor": 0.25,
|
|
1426
|
+
"lineFactor": 1.45,
|
|
1427
|
+
"denseLineFactor": 1.3,
|
|
1428
|
+
"max": 14,
|
|
1429
|
+
"denseMax": 12
|
|
1430
|
+
},
|
|
1431
|
+
"table": {
|
|
1432
|
+
"cellMax": 12.5,
|
|
1433
|
+
"cellFloor": 9,
|
|
1434
|
+
"headMax": 13,
|
|
1435
|
+
"longCellChars": 24,
|
|
1436
|
+
"rowFloorIn": 0.22,
|
|
1437
|
+
"cellPadIn": 0.07
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1412
1440
|
},
|
|
1413
1441
|
"anchorTolerance": {
|
|
1414
1442
|
"$comment": "空间锚点容差(深度模式)。centerPct = 中心点偏差上限(占参考框局部宽高比例);keyPx = 关键锚点(图标/标签/箭头端点)偏差上限(px,按 1280×720 参考图计);exactPx = 用户要求 1:1 时的上限。",
|
|
@@ -1964,7 +1992,7 @@
|
|
|
1964
1992
|
}
|
|
1965
1993
|
},
|
|
1966
1994
|
"qualityGates": {
|
|
1967
|
-
"$comment": "v9+ 硬门禁。tagLeakPatterns / titleOnly / underfill / roundtripMin / chartSkew / chartOversize 同前。antiTruncation:截断迹象与溢出未拆页;chromeDriftIn:跨页 chrome y 容差(英寸)。",
|
|
1995
|
+
"$comment": "v9+ 硬门禁。tagLeakPatterns / titleOnly / underfill / roundtripMin / chartSkew / chartOversize 同前。antiTruncation:截断迹象与溢出未拆页;chromeDriftIn:跨页 chrome y 容差(英寸)。 contentCoverage:模型文本→渲染文本覆盖率下限(validate_report 只 WARN)。",
|
|
1968
1996
|
"tagLeakPatterns": [
|
|
1969
1997
|
"<a ",
|
|
1970
1998
|
"</a>",
|
|
@@ -2033,7 +2061,8 @@
|
|
|
2033
2061
|
"minCards": 3,
|
|
2034
2062
|
"shortPlainChars": 12,
|
|
2035
2063
|
"maxShortRatio": 0.5
|
|
2036
|
-
}
|
|
2064
|
+
},
|
|
2065
|
+
"contentCoverage": 0.95
|
|
2037
2066
|
},
|
|
2038
2067
|
"imageAdmission": {
|
|
2039
2068
|
"$comment": "图片资产准入门。forbidden = 禁止图片化的区域(主信息层与简单图表必须原生重建);allowed = 可保留为图片的复杂视觉资产;maxPageAreaPct = 单页图片覆盖面积上限(超过须逐项说明);fullSlideRiskPct = 按整页背景风险处理阈值(内容页默认失败);placeholderIsNative = 配图占位由原生形状渲染(不是图片,不触发 pictures 门禁)。",
|
|
@@ -3525,5 +3554,63 @@
|
|
|
3525
3554
|
]
|
|
3526
3555
|
}
|
|
3527
3556
|
}
|
|
3557
|
+
},
|
|
3558
|
+
"layoutVariants": {
|
|
3559
|
+
"$comment": "版式变体注册表(0.2.0 新增):复用既有页型/组件的组合版式,不新增 DSL 页型。variant 写在 section.variant;HTML 侧 render_from_model 按 base 页型渲染 + 变体类名;PPTX 侧 build_pptx 同名映射。新增变体:在此登记 + references/layout-variants.md 写骨架文档;禁止为变体另起渲染器。",
|
|
3560
|
+
"version": "0.1",
|
|
3561
|
+
"variants": {
|
|
3562
|
+
"bento-grid": {
|
|
3563
|
+
"name": "Bento 拼贴卡",
|
|
3564
|
+
"base": "cards",
|
|
3565
|
+
"html": ".grid.g-bento(复用 .card;首卡 .card--lead 跨 2 列)",
|
|
3566
|
+
"components": [
|
|
3567
|
+
"card",
|
|
3568
|
+
"card__ico",
|
|
3569
|
+
"pts_list"
|
|
3570
|
+
],
|
|
3571
|
+
"sectionAttr": "data-variant=\"bento-grid\"",
|
|
3572
|
+
"when": "4–6 张卡、其中 1 张是主结论/总览时;信息密度高于等权卡片",
|
|
3573
|
+
"budget": {
|
|
3574
|
+
"maxCards": 6,
|
|
3575
|
+
"recommendCards": "4–5",
|
|
3576
|
+
"leadMaxPoints": 4,
|
|
3577
|
+
"otherMaxPoints": 3
|
|
3578
|
+
},
|
|
3579
|
+
"pptx": "cards 分支:首卡跨 2 列,其余顺排(build_pptx.js variant 分支)"
|
|
3580
|
+
},
|
|
3581
|
+
"timeline": {
|
|
3582
|
+
"name": "图标时间线",
|
|
3583
|
+
"base": "timeline",
|
|
3584
|
+
"html": "复用 .tl;phase 字典加 icon 键 → 节点图标 .tl__ico[data-icon]",
|
|
3585
|
+
"components": [
|
|
3586
|
+
"tl",
|
|
3587
|
+
"icon:<name>"
|
|
3588
|
+
],
|
|
3589
|
+
"sectionAttr": "data-variant=\"timeline\"",
|
|
3590
|
+
"when": "阶段有明确类型语义(里程碑/风险/交付)且图标能加速扫读时;无语义用基础 timeline",
|
|
3591
|
+
"budget": {
|
|
3592
|
+
"maxPhases": 6,
|
|
3593
|
+
"iconPerPhase": 1,
|
|
3594
|
+
"descMaxChars": 60
|
|
3595
|
+
},
|
|
3596
|
+
"pptx": "timeline 分支:节点圆点旁贴 icon: 图标(build_pptx.js phaseKV 第 5 元)"
|
|
3597
|
+
},
|
|
3598
|
+
"2-col-feature": {
|
|
3599
|
+
"name": "双栏特性",
|
|
3600
|
+
"base": "twocol",
|
|
3601
|
+
"html": ".cols-2;左栏 .col--feature(复用 .card 语义:加粗标题 + accent 左线)",
|
|
3602
|
+
"components": [
|
|
3603
|
+
"cols-2",
|
|
3604
|
+
"card"
|
|
3605
|
+
],
|
|
3606
|
+
"sectionAttr": "data-variant=\"2-col-feature\"",
|
|
3607
|
+
"when": "左栏是核心主张/特性、右栏是支撑细节时;两栏对等论证用基础 twocol",
|
|
3608
|
+
"budget": {
|
|
3609
|
+
"maxParas": 6,
|
|
3610
|
+
"featureMaxParas": 2
|
|
3611
|
+
},
|
|
3612
|
+
"pptx": "twocol 分支:首栏加 accent 左线 + 标题放大(build_pptx.js variant 分支)"
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3528
3615
|
}
|
|
3529
|
-
}
|
|
3616
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""TopPPT HTML · 内置图标包生成器(单源)
|
|
4
|
+
|
|
5
|
+
产出 assets/icons/:
|
|
6
|
+
- <name>.svg 24×24 手绘线性图标(stroke=currentColor, 1.8, 圆角),HTML 内联用
|
|
7
|
+
- <name>.png 512px 预渲染(cairosvg),PPTX 通道用(python-pptx 不认 SVG,见报告)
|
|
8
|
+
- index.json 关键词→图标映射 + 默认图标表
|
|
9
|
+
|
|
10
|
+
用法:
|
|
11
|
+
python scripts/make_icon_pack.py # 写 SVG + index.json
|
|
12
|
+
python scripts/make_icon_pack.py --png # 另渲染 512px PNG(需 cairosvg)
|
|
13
|
+
|
|
14
|
+
图标全部手绘原创(24 网格),不复制第三方图标库。
|
|
15
|
+
其中 19 个沿用既有 icon_lib.js 的成熟路径(字节一致,现有渲染不变);
|
|
16
|
+
流程 采用 references/icons.md 文档版(git-branch 式,与文档对齐)。
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
25
|
+
OUT = ROOT / 'assets' / 'icons'
|
|
26
|
+
|
|
27
|
+
SVG_OPEN = ('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" '
|
|
28
|
+
'stroke="currentColor" stroke-width="1.8" stroke-linecap="round" '
|
|
29
|
+
'stroke-linejoin="round">')
|
|
30
|
+
|
|
31
|
+
# name -> (inner_svg, label, keywords)
|
|
32
|
+
ICONS: dict[str, tuple[str, str, list[str]]] = {
|
|
33
|
+
# ── 既有 19(与 icon_lib.js 现行字节一致) ──
|
|
34
|
+
'增长': ('<path d="M22 7l-8.5 8.5-5-5L2 17"/><path d="M16 7h6v6"/>',
|
|
35
|
+
'增长/上升', ['增长', '上升', '提升', '增加', 'up']),
|
|
36
|
+
'下降': ('<path d="M22 17l-8.5-8.5-5 5L2 7"/><path d="M16 17h6v-6"/>',
|
|
37
|
+
'下降/回落', ['下降', '回落', '减少', '降低', 'down']),
|
|
38
|
+
'数据': ('<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14c0 1.66 4.03 3 9 3s9-1.34 9-3V5"/><path d="M3 12c0 1.66 4.03 3 9 3s9-1.34 9-3"/>',
|
|
39
|
+
'数据库/底座', ['数据', '底座', 'database']),
|
|
40
|
+
'图表': ('<path d="M3 3v18h18"/><rect x="7" y="12" width="3" height="6" rx="1"/><rect x="12" y="8" width="3" height="10" rx="1"/><rect x="17" y="4" width="3" height="14" rx="1"/>',
|
|
41
|
+
'柱状图', ['图表', '柱状', 'bar']),
|
|
42
|
+
'趋势': ('<path d="M3 3v18h18"/><path d="M7 14l4-4 3 3 5-6"/><path d="M15 7h4v4"/>',
|
|
43
|
+
'趋势/折线', ['趋势', '折线', 'line']),
|
|
44
|
+
'占比': ('<path d="M21.2 15.9A10 10 0 1 1 8 2.8"/><path d="M22 12A10 10 0 0 0 12 2v10z"/>',
|
|
45
|
+
'环形/占比', ['占比', '环形', '份额', 'donut']),
|
|
46
|
+
'表格': ('<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M3 15h18M9 3v18M15 3v18"/>',
|
|
47
|
+
'表格/网格', ['表格', '矩阵', 'table']),
|
|
48
|
+
'效率': ('<path d="M12 15l3.5-5.5"/><path d="M20.2 15a8.5 8.5 0 1 0-16.4 0"/>',
|
|
49
|
+
'仪表盘/效率', ['效率', '仪表盘', '性能', 'gauge']),
|
|
50
|
+
'成果': ('<circle cx="12" cy="8" r="6"/><path d="M15.5 13 17 22l-5-3-5 3 1.5-9"/>',
|
|
51
|
+
'奖章/成果', ['成果', '奖章', '奖项', 'medal']),
|
|
52
|
+
'安全': ('<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/>',
|
|
53
|
+
'盾牌/安全', ['安全', '盾牌', 'shield']),
|
|
54
|
+
'权限': ('<rect x="3" y="11" width="18" height="10" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
|
|
55
|
+
'锁/权限', ['权限', '锁', 'lock']),
|
|
56
|
+
'检查': ('<circle cx="12" cy="12" r="10"/><path d="M8 12.5l2.5 2.5L16 9.5"/>',
|
|
57
|
+
'检查/对勾', ['检查', '对勾', '校验', 'check']),
|
|
58
|
+
'风险': ('<path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/><path d="M12 9v4M12 17h.01"/>',
|
|
59
|
+
'警告/风险', ['风险', '警告', 'warning']),
|
|
60
|
+
'团队': ('<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
|
|
61
|
+
'用户/团队', ['团队', '人力', 'team']),
|
|
62
|
+
'计划': ('<rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/>',
|
|
63
|
+
'日历/计划', ['计划', '日历', '日程', 'calendar']),
|
|
64
|
+
'智能': ('<rect x="4" y="4" width="16" height="16" rx="2"/><path d="M9 9h6v6H9zM9 1v3M15 1v3M9 20v3M15 20v3M1 9h3M1 15h3M20 9h3M20 15h3"/>',
|
|
65
|
+
'芯片/AI', ['智能', '芯片', 'AI', 'chip']),
|
|
66
|
+
'洞察': ('<circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/>',
|
|
67
|
+
'搜索/洞察', ['洞察', '搜索', '分析', 'search']),
|
|
68
|
+
'工具': ('<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>',
|
|
69
|
+
'扳手/工具', ['工具', '扳手', 'tool']),
|
|
70
|
+
'清单': ('<path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"/>',
|
|
71
|
+
'清单/条目', ['清单', '条目', 'checklist']),
|
|
72
|
+
# ── 流程:采用 references/icons.md 文档版(git-branch 式) ──
|
|
73
|
+
'流程': ('<circle cx="6" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="8" r="3"/><path d="M6 9v6M18 11a9 9 0 0 1-9 4"/>',
|
|
74
|
+
'分支/流程', ['流程', '分支', 'pipeline']),
|
|
75
|
+
# ── 新增 28(手绘原创) ──
|
|
76
|
+
'箭头': ('<path d="M4 12h15"/><path d="M13 6l6 6-6 6"/>',
|
|
77
|
+
'右箭头', ['箭头', '指向', 'arrow']),
|
|
78
|
+
'下载': ('<path d="M12 4v11"/><path d="M7 10.5l5 5 5-5"/><path d="M4 16v4h16v-4"/>',
|
|
79
|
+
'下载', ['下载', 'download']),
|
|
80
|
+
'上传': ('<path d="M12 20V9"/><path d="M7 13.5l5-5 5 5"/><path d="M4 16v4h16v-4"/>',
|
|
81
|
+
'上传', ['上传', 'upload']),
|
|
82
|
+
'分享': ('<circle cx="6" cy="12" r="2.5"/><circle cx="17.5" cy="5.5" r="2.5"/><circle cx="17.5" cy="18.5" r="2.5"/><path d="M8.3 10.9l6.9-4.2M8.3 13.1l6.9 4.2"/>',
|
|
83
|
+
'分享', ['分享', 'share']),
|
|
84
|
+
'循环': ('<path d="M20.5 15a8.5 8.5 0 1 1-2-8.9L21 8.5"/><path d="M21 3.5v5h-5"/>',
|
|
85
|
+
'循环/刷新', ['循环', '刷新', '迭代', '飞轮', 'refresh']),
|
|
86
|
+
'目标': ('<circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><path d="M12 12h.01"/>',
|
|
87
|
+
'同心圆靶心', ['目标', 'target']),
|
|
88
|
+
'闪电': ('<path d="M13 2 3 14h7l-1 8 10-12h-7l1-8z"/>',
|
|
89
|
+
'闪电/高效', ['闪电', '高效', '快速', '快捷']),
|
|
90
|
+
'勾选': ('<path d="M4 12.5l5 5L20 6.5"/>',
|
|
91
|
+
'对勾', ['勾选', '完成', 'done']),
|
|
92
|
+
'用户': ('<circle cx="12" cy="8" r="4"/><path d="M4 21c0-3.5 3.6-5.5 8-5.5s8 2 8 5.5"/>',
|
|
93
|
+
'单人/用户', ['用户', '客户', 'user']),
|
|
94
|
+
'组织': ('<rect x="9" y="2" width="6" height="6" rx="1"/><rect x="2" y="16" width="6" height="6" rx="1"/><rect x="16" y="16" width="6" height="6" rx="1"/><path d="M12 8v4M5 12h14M5 12v4M19 12v4"/>',
|
|
95
|
+
'组织架构/层级', ['组织', '层级', '架构', 'org']),
|
|
96
|
+
'灯泡': ('<path d="M12 3a6 6 0 0 0-3.4 11c.7.5 1.4 1.3 1.4 2.5h4c0-1.2.7-2 1.4-2.5A6 6 0 0 0 12 3z"/><path d="M10 19.5h4"/><path d="M10.8 22h2.4"/>',
|
|
97
|
+
'灯泡/创意', ['灯泡', '创意', '想法', 'idea']),
|
|
98
|
+
'文档': ('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6z"/><path d="M14 2v6h6"/><path d="M9 13h6M9 17h6"/>',
|
|
99
|
+
'文档/报告', ['文档', '报告', '文件', 'doc']),
|
|
100
|
+
'云': ('<path d="M7 18.5a4 4 0 0 1-.4-7.98A5.5 5.5 0 0 1 17.3 11 3.9 3.9 0 0 1 17 18.5H7z"/>',
|
|
101
|
+
'云/平台', ['云', '平台', 'cloud']),
|
|
102
|
+
'邮件': ('<rect x="2" y="4" width="20" height="16" rx="2"/><path d="M2.5 7.5 12 13l9.5-5.5"/>',
|
|
103
|
+
'信封/邮件', ['邮件', '邮箱', 'mail']),
|
|
104
|
+
'手机': ('<rect x="7" y="2" width="10" height="20" rx="2.5"/><path d="M11 18.5h2"/>',
|
|
105
|
+
'手机', ['手机', '移动', 'phone']),
|
|
106
|
+
'对话': ('<rect x="3" y="4" width="18" height="12" rx="2"/><path d="M8 16v4l4.5-4"/>',
|
|
107
|
+
'对话框', ['对话', '沟通', 'chat']),
|
|
108
|
+
'位置': ('<path d="M12 21.5s-7-5.3-7-11a7 7 0 0 1 14 0c0 5.7-7 11-7 11z"/><circle cx="12" cy="10.5" r="2.5"/>',
|
|
109
|
+
'定位/地点', ['位置', '地点', 'pin']),
|
|
110
|
+
'时间': ('<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/>',
|
|
111
|
+
'时钟/时间', ['时间', '时钟', 'clock']),
|
|
112
|
+
'金钱': ('<rect x="2" y="6" width="20" height="12" rx="2"/><circle cx="12" cy="12" r="2.8"/><path d="M5.5 12h.01M18.5 12h.01"/>',
|
|
113
|
+
'纸币/财务', ['金钱', '成本', '财务', 'money']),
|
|
114
|
+
'链接': ('<path d="M10 14a4.5 4.5 0 0 0 6.4.4l2.8-2.8a4.5 4.5 0 0 0-6.4-6.4l-1.6 1.6"/><path d="M14 10a4.5 4.5 0 0 0-6.4-.4l-2.8 2.8a4.5 4.5 0 0 0 6.4 6.4l1.6-1.6"/>',
|
|
115
|
+
'链条/链接', ['链接', 'link']),
|
|
116
|
+
'设置': ('<circle cx="12" cy="12" r="3.2"/><path d="M12 2.5v2.8M12 18.7v2.8M2.5 12h2.8M18.7 12h2.8M5.2 5.2l2 2M16.8 16.8l2 2M18.8 5.2l-2 2M7.2 16.8l-2 2"/>',
|
|
117
|
+
'齿轮/设置', ['设置', '齿轮', '配置', 'setting']),
|
|
118
|
+
'信息': ('<circle cx="12" cy="12" r="9"/><path d="M12 11v5"/><path d="M12 7.5h.01"/>',
|
|
119
|
+
'信息/提示', ['信息', '提示', 'info']),
|
|
120
|
+
'星标': ('<path d="m12 2.8 2.8 5.8 6.4.9-4.6 4.5 1.1 6.3-5.7-3-5.7 3 1.1-6.3L2.8 9.5l6.4-.9z"/>',
|
|
121
|
+
'五角星', ['星标', '收藏', 'star']),
|
|
122
|
+
'书签': ('<path d="M6.5 3h11V21l-5.5-3.8L6.5 21z"/>',
|
|
123
|
+
'书签', ['书签', 'bookmark']),
|
|
124
|
+
'文件夹': ('<path d="M3 7a2 2 0 0 1 2-2h4l2 2.5h8a2 2 0 0 1 2 2V18a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>',
|
|
125
|
+
'文件夹/资产', ['文件夹', '资产', 'folder']),
|
|
126
|
+
'火箭': ('<path d="M12 2.5c2.6 2.2 4 5.6 4 9.2l-4 2.8-4-2.8c0-3.6 1.4-7 4-9.2z"/><circle cx="12" cy="9" r="1.6"/><path d="M8.3 12.5 5 16.5l3.6-.6M15.7 12.5l3.3 4-3.6-.6"/><path d="M10.8 16.8c0 1.6.5 2.6 1.2 3.7.7-1.1 1.2-2.1 1.2-3.7"/>',
|
|
127
|
+
'火箭/发射', ['火箭', '发射', 'rocket']),
|
|
128
|
+
'旗帜': ('<path d="M5 21.5V4"/><path d="M5 4.5h12.5l-2.8 3.8 2.8 3.7H5"/>',
|
|
129
|
+
'旗帜/里程碑', ['旗帜', '里程碑', 'flag']),
|
|
130
|
+
'罗盘': ('<circle cx="12" cy="12" r="9"/><path d="m15.5 8.5-2.2 4.8-4.8 2.2 2.2-4.8z"/>',
|
|
131
|
+
'罗盘/方向', ['罗盘', '方向', '导航', 'compass']),
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
DEFAULTS = ['信息', '检查', '数据', '图表', '目标', '团队', '工具']
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def main() -> int:
|
|
138
|
+
OUT.mkdir(parents=True, exist_ok=True)
|
|
139
|
+
assert len(ICONS) == 48, f'图标数应为 48,实际 {len(ICONS)}'
|
|
140
|
+
assert all(d in ICONS for d in DEFAULTS), '默认图标必须在包内'
|
|
141
|
+
index = {'version': '0.2.0', 'count': len(ICONS),
|
|
142
|
+
'defaults': DEFAULTS, 'icons': {}}
|
|
143
|
+
for name, (inner, label, keywords) in ICONS.items():
|
|
144
|
+
svg = SVG_OPEN + inner + '</svg>'
|
|
145
|
+
(OUT / f'{name}.svg').write_text(svg, encoding='utf-8')
|
|
146
|
+
index['icons'][name] = {
|
|
147
|
+
'file': f'{name}.svg',
|
|
148
|
+
'png': f'{name}.png',
|
|
149
|
+
'label': label,
|
|
150
|
+
'keywords': keywords,
|
|
151
|
+
'default': name in DEFAULTS,
|
|
152
|
+
}
|
|
153
|
+
(OUT / 'index.json').write_text(
|
|
154
|
+
json.dumps(index, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
|
|
155
|
+
print(f'[make_icon_pack] OK: {len(ICONS)} svg + index.json → {OUT}')
|
|
156
|
+
if '--png' in sys.argv:
|
|
157
|
+
try:
|
|
158
|
+
import cairosvg
|
|
159
|
+
except ImportError:
|
|
160
|
+
print('[make_icon_pack] FAIL: 需 cairosvg(pip install cairosvg)才能渲染 PNG')
|
|
161
|
+
return 1
|
|
162
|
+
for name, (inner, _label, _kw) in ICONS.items():
|
|
163
|
+
svg = SVG_OPEN.replace('currentColor', '#1a56a8') + inner + '</svg>'
|
|
164
|
+
cairosvg.svg2png(bytestring=svg.encode('utf-8'),
|
|
165
|
+
write_to=str(OUT / f'{name}.png'),
|
|
166
|
+
output_width=512, output_height=512)
|
|
167
|
+
print(f'[make_icon_pack] OK: {len(ICONS)} png(512px) → {OUT}')
|
|
168
|
+
return 0
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
if __name__ == '__main__':
|
|
172
|
+
raise SystemExit(main())
|
|
@@ -39,10 +39,13 @@
|
|
|
39
39
|
"lead",
|
|
40
40
|
"soWhat",
|
|
41
41
|
"footnote",
|
|
42
|
+
"note",
|
|
42
43
|
"flags",
|
|
43
44
|
"image",
|
|
44
45
|
"exhibitNo",
|
|
45
|
-
"layoutPreset"
|
|
46
|
+
"layoutPreset",
|
|
47
|
+
"variant",
|
|
48
|
+
"anchor"
|
|
46
49
|
],
|
|
47
50
|
"mutualExclusionHint": "verdict 与 soWhat 互斥:二者共用 annotation 结论条槽位,同页双填必叠印。comparison 页填 verdict 时不要再填 soWhat(渲染器已自动二选一,但模型侧应只给一个)。",
|
|
48
51
|
"flagsHint": "flags = 待核实/待二次修改条目字符串数组(如 '2027 增速 xx%(口径未定)');渲染为 accent 强调色标注行,提示用户核对。",
|
|
@@ -64,10 +64,10 @@ def main() -> int:
|
|
|
64
64
|
node = find_node()
|
|
65
65
|
node_path = find_node_modules()
|
|
66
66
|
if not node:
|
|
67
|
-
print('
|
|
67
|
+
print('错误: ' + NODE_HINT)
|
|
68
68
|
return 2
|
|
69
69
|
if not node_path:
|
|
70
|
-
print('
|
|
70
|
+
print('错误: ' + NODE_MODULES_HINT)
|
|
71
71
|
return 2
|
|
72
72
|
env = node_env(node_path)
|
|
73
73
|
print(f'Node: {node}')
|
|
@@ -82,7 +82,7 @@ def main() -> int:
|
|
|
82
82
|
if args.only:
|
|
83
83
|
examples = [h for h in examples if args.only in h.stem]
|
|
84
84
|
if not examples:
|
|
85
|
-
print('
|
|
85
|
+
print('错误: assets/examples/ 下没有可回归的示例。')
|
|
86
86
|
return 2
|
|
87
87
|
|
|
88
88
|
for html in examples:
|
|
@@ -211,7 +211,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
211
211
|
|
|
212
212
|
pptx = Path(args.pptx).resolve()
|
|
213
213
|
if not pptx.exists():
|
|
214
|
-
print(f'
|
|
214
|
+
print(f'错误: 找不到 {pptx}')
|
|
215
215
|
return 2
|
|
216
216
|
out = Path(args.out).resolve() if args.out else pptx.parent / 'render-compare'
|
|
217
217
|
out.mkdir(parents=True, exist_ok=True)
|