@topmindspace/tms-skills 0.1.4 → 0.1.5

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.
@@ -59,6 +59,7 @@ UI = TPL_DIR / 'ui.js'
59
59
  TEMPLATES = [TPL_DIR / 'presentation.html',
60
60
  TPL_DIR / 'research.html',
61
61
  TPL_DIR / 'architecture.html']
62
+ EXAMPLES = sorted((ROOT / 'assets' / 'examples').glob('*.html')) if (ROOT / 'assets' / 'examples').is_dir() else []
62
63
  BJ = ROOT / 'scripts' / 'build_pptx.js'
63
64
  EM = ROOT / 'scripts' / 'extract_model.py'
64
65
 
@@ -336,14 +337,19 @@ def main() -> int:
336
337
  ui_repl = ('/* __TOPPPT_UI_START__ */\n'
337
338
  '/* ══ TopPPT HTML 公共 UI 脚本(assets/templates/ui.js 的内联副本 · 由 scripts/sync_runtime.py 注入,禁止手改) ══ */\n'
338
339
  + ui_js + '\n/* __TOPPPT_UI_END__ */')
340
+ # 运行时同版本戳:对注入源 assets/pptx-export.js(rstrip 后)取 sha256[:16],
341
+ # 写入模板内联块,供 validate_report 比对防「模板未 sync」漂移。
342
+ runtime_sha = hashlib.sha256(runtime.encode('utf-8')).hexdigest()[:16]
339
343
  runtime_repl = ('/* __TOPPPT_RUNTIME_START__ */\n'
340
344
  '/* ══ PPTX 导出运行时(assets/pptx-export.js 的内联副本 · 由 scripts/sync_runtime.py 注入,禁止手改) ══ */\n'
345
+ f'/* __TOPPPT_RUNTIME_SHA__:{runtime_sha} */\n'
341
346
  + runtime + '\n/* __TOPPPT_RUNTIME_END__ */')
342
347
 
343
- # ③ 注入三份模式模板
344
- for tpl in TEMPLATES:
348
+ # ③ 注入三份模式模板 + 黄金样张(样张也内联运行时,须同戳)
349
+ inject_targets = list(TEMPLATES) + list(EXAMPLES)
350
+ for tpl in inject_targets:
345
351
  if not tpl.exists():
346
- print(f'警告: 模板不存在 {tpl}')
352
+ print(f'警告: 模板/样张不存在 {tpl}')
347
353
  ok = False
348
354
  continue
349
355
  ok = inject(tpl, ENGINE_PAT, engine_repl, '__TOPPPT_ENGINE__') and ok
@@ -649,7 +655,7 @@ process.exit(bad ? 1 : 0);
649
655
  print(f' model-schema.json sha256[:16] = {ms_digest} · 页型 {n_schema_pt} 种')
650
656
  print(f' 风格 token {n_styles} 套 · 三模式独立比例尺 {len([k for k in lc["modeTypeScale"] if not k.startswith("$")])} 套 · 页型几何 {n_pt} 组')
651
657
  print(f' 图表登记 {n_charts} 种(原生 {n_native} / 形状 {n_shape}) · 语义字阶 {n_typo} 级 · 12 列网格 {lc["grid"]["columns"]} 列 · 布局 IR {n_slots} 页型')
652
- print(' 已刷新: assets/pptx-export.js(常量块 + schema 块) · assets/style-gallery.html(常量块) · templates/{presentation,research,architecture}.html(引擎/UI/运行时内联副本)')
658
+ print(' 已刷新: assets/pptx-export.js(常量块 + schema 块) · assets/style-gallery.html(常量块) · templates/{presentation,research,architecture}.html + assets/examples/*.html(引擎/UI/运行时内联副本,含 __TOPPPT_RUNTIME_SHA__)')
653
659
  print(f' 双端单源引用校验: {"PASS" if ok else "WARN(build_pptx 应 require layout-constants.json;extract_model 应读 model-schema.json;页型四件套与图表登记须完整)"}')
654
660
  print(f' 说明: 页型几何 {n_pt} 组(多页型共享几何组) · schema 页型 {n_schema_pt} 种')
655
661
  return 0
@@ -28,6 +28,8 @@ research / architecture 仍须显式传 --layout-qa(不强制)。
28
28
  import sys
29
29
  import re
30
30
  import json
31
+ import hashlib
32
+ from typing import Optional
31
33
  from html.parser import HTMLParser
32
34
  from pathlib import Path
33
35
 
@@ -433,8 +435,22 @@ def _check_type_features(txt, chk, model):
433
435
  f"{len(missing)} 处: {missing[:4]}" if missing else "", level="WARN")
434
436
 
435
437
 
438
+ RUNTIME_SHA_RE = re.compile(r'/\* __TOPPPT_RUNTIME_SHA__:([0-9a-f]{16}) \*/')
439
+
440
+
441
+ def _pptx_export_source_sha() -> Optional[str]:
442
+ """当前技能包 assets/pptx-export.js 的 sha256[:16](与 sync_runtime 注入口径一致)。"""
443
+ pe = Path(__file__).resolve().parent.parent / 'assets' / 'pptx-export.js'
444
+ try:
445
+ body = pe.read_text(encoding='utf-8').rstrip('\n')
446
+ except OSError:
447
+ return None
448
+ return hashlib.sha256(body.encode('utf-8')).hexdigest()[:16]
449
+
450
+
436
451
  def _check_pptx_export(txt, chk):
437
- """PPTX 预览配套:有预览按钮就必须有内容模型 + 预览运行时;页面不得残留导出按钮。"""
452
+ """PPTX 预览配套:有预览按钮就必须有内容模型 + 预览运行时;页面不得残留导出按钮。
453
+ 另:内联运行时必须带 __TOPPPT_RUNTIME_SHA__ 且与当前 pptx-export.js 同源。"""
438
454
  has_btn = 'id="pptPreviewBtn"' in txt
439
455
  has_model = 'REPORT_MODEL' in txt
440
456
  has_runtime = ('g.TopPptHtml = api' in txt) and ('function slidesXml' in txt)
@@ -445,6 +461,20 @@ def _check_pptx_export(txt, chk):
445
461
  else:
446
462
  chk("含 PPTX 预览按钮与内容模型(建议保留)",
447
463
  has_model and has_runtime, "未集成预览按钮/模型", level="WARN")
464
+ # 同版本戳:有运行时块时强制核对(缺戳或漂移 = FAIL)
465
+ if has_runtime or '/* __TOPPPT_RUNTIME_START__ */' in txt:
466
+ m = RUNTIME_SHA_RE.search(txt)
467
+ expected = _pptx_export_source_sha()
468
+ if not m:
469
+ chk("运行时同版本戳(__TOPPPT_RUNTIME_SHA__)", False,
470
+ "内联运行时缺戳;请跑 scripts/sync_runtime.py")
471
+ elif expected is None:
472
+ chk("运行时同版本戳(__TOPPPT_RUNTIME_SHA__)", False,
473
+ "无法读取 assets/pptx-export.js 计算源哈希")
474
+ else:
475
+ got = m.group(1)
476
+ chk("运行时同版本戳(__TOPPPT_RUNTIME_SHA__)", got == expected,
477
+ f"stamp={got} ≠ source={expected}(模板未 sync 或源已改)")
448
478
  chk("页面无 PPTX 导出按钮(仅预览 + 提示词)",
449
479
  'id="pptxBtn"' not in txt and 'id="pptDownload"' not in txt,
450
480
  "残留导出按钮 pptxBtn/pptDownload", level="WARN")