@lark-apaas/coding-steering 0.1.32-beta.0 → 0.1.32-dev.3c06d32

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.
Files changed (61) hide show
  1. package/package.json +6 -6
  2. package/steering/design-html/skills/charts/SKILL.md +4 -0
  3. package/steering/design-html/skills/pptx-style-extract/SKILL.md +71 -26
  4. package/steering/design-html/skills/pptx-style-extract/font-fallback.yaml +3 -3
  5. package/steering/design-html/skills/pptx-style-extract/scripts/census.py +26 -14
  6. package/steering/design-html/skills/pptx-style-extract/scripts/check_v2.py +153 -8
  7. package/steering/design-html/skills/pptx-style-extract/scripts/draft.py +2810 -292
  8. package/steering/design-html/skills/pptx-style-extract/scripts/extract.py +544 -22
  9. package/steering/design-html/skills/pptx-style-extract/scripts/ooxml.py +19 -2
  10. package/steering/design-html/skills/pptx-style-extract/scripts/package.py +991 -165
  11. package/steering/design-html/skills/pptx-style-extract/scripts/parts.py +22 -3
  12. package/steering/design-html/skills/pptx-style-extract/scripts/query.py +4 -9
  13. package/steering/design-html/skills/pptx-style-extract/scripts/render_pages.py +20 -12
  14. package/steering/design-html/skills/pptx-style-extract/scripts/test_asset_judgment_package.py +556 -0
  15. package/steering/design-html/skills/pptx-style-extract/scripts/test_background_composite.py +364 -0
  16. package/steering/design-html/skills/pptx-style-extract/scripts/test_color_contract.py +60 -0
  17. package/steering/design-html/skills/pptx-style-extract/scripts/test_design_consumer_contract.py +76 -0
  18. package/steering/design-html/skills/pptx-style-extract/scripts/test_flow_layout_contract.py +528 -0
  19. package/steering/design-html/skills/pptx-style-extract/scripts/test_layout_css.py +1694 -0
  20. package/steering/design-html/skills/pptx-style-extract/scripts/test_logo_scope.py +479 -0
  21. package/steering/design-html/skills/pptx-style-extract/scripts/test_rounded_contract.py +112 -0
  22. package/steering/design-html/skills/pptx-style-extract/scripts/test_text_role_contract.py +315 -0
  23. package/steering/design-html/skills/pptx-style-extract/scripts/verify_layout_assets.py +400 -0
  24. package/steering/design-html/skills/pptx-style-extract/scripts/verify_logo_scope.py +12 -0
  25. package/steering/design-html/skills/pptx-style-extract/v2-format-spec.md +27 -15
  26. package/steering/design-html/skills/preflight/scripts/probe.sh +0 -0
  27. package/steering/nestjs-react-fullstack/skills/app-init-feasibility-guide/SKILL.md +1 -0
  28. package/steering/nestjs-react-fullstack/skills/authn-guide/SKILL.md +6 -0
  29. package/steering/nestjs-react-fullstack/skills/authz-guide/SKILL.md +5 -5
  30. package/steering/nestjs-react-fullstack/skills/authz-guide/references/dynamic-permission-guide.md +1 -1
  31. package/steering/nestjs-react-fullstack/skills/client-builtins-file-storage-service/SKILL.md +37 -113
  32. package/steering/nestjs-react-fullstack/skills/client-builtins-user-service/SKILL.md +13 -2
  33. package/steering/nestjs-react-fullstack/skills/code-fix/SKILL.md +7 -7
  34. package/steering/nestjs-react-fullstack/skills/coding-guide/SKILL.md +149 -24
  35. package/steering/nestjs-react-fullstack/skills/connections-sdk/SKILL.md +202 -0
  36. package/steering/nestjs-react-fullstack/skills/nestjs-cache/SKILL.md +255 -0
  37. package/steering/nestjs-react-fullstack/skills/plugin-guide/SKILL.md +158 -543
  38. package/steering/nestjs-react-fullstack/skills/plugin-guide/references/plugin-coding-guide.md +15 -1
  39. package/steering/nestjs-react-fullstack/skills/plugin-guide/references/table.md +30 -14
  40. package/steering/nestjs-react-fullstack/skills/raw-sql-boundary-audit/SKILL.md +63 -0
  41. package/steering/nestjs-react-fullstack/skills/server-builtins-file-storage-service/SKILL.md +1 -1
  42. package/steering/nestjs-react-fullstack/skills_common/trigger-guide/SKILL.md +284 -12
  43. package/steering/nestjs-react-fullstack/skills_local/plugin-guide/SKILL.md +4 -0
  44. package/steering/vite-react/skills/plugin-guide/SKILL.md +3 -1
  45. package/steering/vite-react/skills/react-three-fiber/SKILL.md +4 -0
  46. package/steering/nestjs-react-fullstack/skills/client-add-aily-web-chat/SKILL.md +0 -139
  47. package/steering/nestjs-react-fullstack/skills/feishu/SKILL.md +0 -269
  48. package/steering/nestjs-react-fullstack/skills/feishu/references/approval.md +0 -214
  49. package/steering/nestjs-react-fullstack/skills/feishu/references/attendance.md +0 -163
  50. package/steering/nestjs-react-fullstack/skills/feishu/references/bitable.md +0 -311
  51. package/steering/nestjs-react-fullstack/skills/feishu/references/calendar.md +0 -190
  52. package/steering/nestjs-react-fullstack/skills/feishu/references/contacts.md +0 -160
  53. package/steering/nestjs-react-fullstack/skills/feishu/references/doc.md +0 -257
  54. package/steering/nestjs-react-fullstack/skills/feishu/references/drive.md +0 -104
  55. package/steering/nestjs-react-fullstack/skills/feishu/references/events.md +0 -199
  56. package/steering/nestjs-react-fullstack/skills/feishu/references/id-convert.md +0 -128
  57. package/steering/nestjs-react-fullstack/skills/feishu/references/messaging.md +0 -207
  58. package/steering/nestjs-react-fullstack/skills/feishu/references/oauth.md +0 -165
  59. package/steering/nestjs-react-fullstack/skills/feishu/references/perm.md +0 -91
  60. package/steering/nestjs-react-fullstack/skills/feishu/references/wiki.md +0 -165
  61. package/steering/nestjs-react-fullstack/skills_common/trigger-guide/references/trigger-lifecycle.md +0 -301
@@ -39,15 +39,15 @@ class Package:
39
39
  self.masters = sorted((n for n in self.names if MASTER_RE.match(n)), key=_num)
40
40
  self.themes, self.theme_discovery = self._find_themes(ct)
41
41
  # zip directory entries ('ppt/media/') must not be counted as assets —
42
- # 03 recon counted one, which is why its media totals run one high.
42
+ # they inflate media totals by one.
43
43
  self.media = sorted(n for n in self.names
44
44
  if n.startswith('ppt/media/') and not n.endswith('/'))
45
45
 
46
46
  def _find_themes(self, ct):
47
47
  """Theme parts come from [Content_Types].xml, not from a path pattern.
48
48
 
49
- Compressors relocate them — 飞书压缩版 puts them at
50
- ppt/slideMasters/theme/themeN.xml — and a `ppt/theme/` regex then finds
49
+ Compressors relocate them — some rewrite them under
50
+ ppt/slideMasters/theme/ — and a `ppt/theme/` regex then finds
51
51
  nothing to bind the masters to, so every schemeClr resolves to
52
52
  UNRESOLVED:no-scheme. The path regex stays as the fallback for packages
53
53
  whose content types are unreadable.
@@ -299,6 +299,22 @@ NV_TAGS = {'sp': 'p:nvSpPr', 'pic': 'p:nvPicPr', 'grpSp': 'p:nvGrpSpPr',
299
299
  'cxnSp': 'p:nvCxnSpPr', 'graphicFrame': 'p:nvGraphicFramePr'}
300
300
 
301
301
 
302
+ def blip_opacity(blip):
303
+ """Combine the alpha modulation transforms attached to one local picture."""
304
+ opacity = 1.0
305
+ found = False
306
+ for effect in blip:
307
+ if local(effect.tag) not in ('alphaMod', 'alphaModFix'):
308
+ continue
309
+ try:
310
+ amount = int(effect.get('amt'))
311
+ except (TypeError, ValueError):
312
+ continue
313
+ opacity *= max(0.0, min(amount / 100000.0, 1.0))
314
+ found = True
315
+ return round(opacity, 6) if found else None
316
+
317
+
302
318
  def walk_tree(el, ctx, out, path=(), xf=(1.0, 1.0, 0.0, 0.0), depth=0):
303
319
  sx, sy, dx, dy = xf
304
320
  U, W, H = ctx.units, ctx.units.w, ctx.units.h
@@ -393,6 +409,9 @@ def walk_tree(el, ctx, out, path=(), xf=(1.0, 1.0, 0.0, 0.0), depth=0):
393
409
  blip = sp.find('p:blipFill/a:blip', NS)
394
410
  if blip is not None:
395
411
  rec['media'] = ctx.media_of(blip.get(R_EMBED)) or ctx.media_of(blip.get(R_LINK))
412
+ opacity = blip_opacity(blip)
413
+ if opacity is not None:
414
+ rec['opacity'] = opacity
396
415
  svg = blip.find('a:extLst//asvg:svgBlip', NS)
397
416
  if svg is not None:
398
417
  rec['media_svg'] = ctx.media_of(svg.get(R_EMBED))
@@ -244,14 +244,10 @@ def _recipe_css(fill, line, radii, effects):
244
244
  dash = (line or {}).get('dash')
245
245
  style = 'dashed' if dash and 'dash' in dash else 'solid'
246
246
  out.append('border: %dpx %s %s' % (w, style, lc))
247
- if radii:
247
+ if radii and all(r >= 1 for r in radii):
248
248
  lo, hi = min(radii), max(radii)
249
249
  if abs(hi - lo) <= 0.5:
250
250
  out.append('border-radius: %gpx' % round(lo, 1))
251
- else:
252
- out.append('border-radius: %gpx\x00/* 源内 %g~%gpx 共 %d 档,归一档位由 L11 定 */'
253
- % (round(sum(radii) / len(radii), 1), round(lo, 1), round(hi, 1),
254
- len(set(round(r, 1) for r in radii))))
255
251
  for e in effects or []:
256
252
  if e.get('type') == 'outerShdw':
257
253
  col = _css_color(e.get('color')) or 'rgba(0,0,0,0.25)'
@@ -266,8 +262,8 @@ def _recipe_css(fill, line, radii, effects):
266
262
 
267
263
  def _sig(fill, line, effects):
268
264
  """分组键 = 填充 + 描边 + 效果。**不含圆角**——OOXML 圆角是 min(w,h) 的百分比,
269
- 同一配方在不同尺寸的卡上绝对 px 必然不同(feishu 三张 glassCard 实测 10.8/12.8/19.5),
270
- 把它计入键会把一个配方拆成三组;归一到哪一档是 L11 的判断,脚本只报区间。"""
265
+ 同一配方在不同尺寸的卡上绝对 px 必然不同,把它计入键会把一个配方拆成多组;
266
+ 只有组内每个形状都明确共享同一绝对半径时才输出组级圆角,否则留给逐形状 CSS。"""
271
267
  f = 'none'
272
268
  if isinstance(fill, dict):
273
269
  if fill.get('type') == 'solid':
@@ -305,8 +301,7 @@ def cmd_recipes(a, outdir):
305
301
  g = groups.setdefault(k, {'n': 0, 'parts': Counter(), 'sizes': [], 'radii': [],
306
302
  'fill': fill, 'line': line, 'fx': fx})
307
303
  g['n'] += 1
308
- if radius:
309
- g['radii'].append(radius)
304
+ g['radii'].append(radius or 0)
310
305
  g['parts'][short(s['part'])] += 1
311
306
  b = s.get('box') or {}
312
307
  if b.get('w'):
@@ -88,13 +88,15 @@ def css_gradient(grad):
88
88
  def background_css(bg):
89
89
  """页底 <p:bg> 描述符 → CSS background 值。解析不出返回 None,交上层回退到下一层底色。
90
90
 
91
- bgPr solid / bgRef 都带 `color`;gradient 与形状渐变共用 css_gradient;
92
- 背景图不重建(媒体未必导出),按无底色处理。
91
+ bgPr solid / bgRef 都带 `color`;线性 gradient 与形状渐变共用 css_gradient;
92
+ path 渐变和背景图不重建,按无底色处理,不能伪装成线性渐变。
93
93
  """
94
94
  if not bg:
95
95
  return None
96
96
  kind = bg.get('type')
97
97
  if kind == 'gradient':
98
+ if bg.get('path'):
99
+ return None
98
100
  return css_gradient(bg)
99
101
  if kind in ('image', 'none', 'pattern'):
100
102
  return None
@@ -279,18 +281,19 @@ def render_shape(shape, media_url, stats):
279
281
 
280
282
  HEAD = """<style>
281
283
  body{margin:0;background:#2b2b2b;font-family:%s}
282
- .page{position:relative;width:1920px;height:1080px;overflow:hidden}
284
+ .page{position:relative;width:%dpx;height:%dpx;overflow:hidden}
283
285
  .sp{position:absolute;box-sizing:border-box}
284
286
  .sp p{margin:0}
285
287
  .ph{position:absolute;left:6px;top:4px;font:16px/1.2 monospace;color:#ff2b88;background:#fff9;padding:1px 4px}
286
288
  </style>
287
- """ % CJK_STACK
289
+ """
288
290
 
291
+ # 缩略框跟着画布比例走,缩放比也一起算——写死 16:9 会把非 16:9 模板的页面裁掉一截
289
292
  INDEX_EXTRA = """<style>
290
- .wrap{width:960px;margin:0 auto;padding:16px 0}
293
+ .wrap{width:%dpx;margin:0 auto;padding:16px 0}
291
294
  .lbl{color:#eee;font:13px/1.6 monospace;margin:14px 0 4px}
292
- .box{width:960px;height:540px;overflow:hidden;margin-bottom:6px}
293
- .box .page{transform:scale(.5);transform-origin:top left}
295
+ .box{width:%dpx;height:%dpx;overflow:hidden;margin-bottom:6px}
296
+ .box .page{transform:scale(%g);transform-origin:top left}
294
297
  </style>
295
298
  """
296
299
 
@@ -508,7 +511,8 @@ def render_pages_png(pages, outdir, data, scale=0.5):
508
511
  for m in data.get('media') or [] if m.get('exported') and m.get('out')}
509
512
  png_dir = os.path.join(outdir, 'ref', 'rebuild', 'png')
510
513
  os.makedirs(png_dir, exist_ok=True)
511
- W, H = int(1920 * scale), int(1080 * scale)
514
+ cpx = ((data.get('canvas') or {}).get('px') or [1920, 1080])
515
+ W, H = int(cpx[0] * scale), int(cpx[1] * scale)
512
516
  for kind, n, label, layers, default_color, page_bg in pages:
513
517
  canvas = Image.new('RGBA', (W, H), _rgba(page_bg) or (136, 136, 136, 255))
514
518
  g = _grad_stops(page_bg or '')
@@ -642,15 +646,19 @@ def main():
642
646
  tx1_of_master.get(master, '#000000'),
643
647
  layout_bg.get(part, '#888')))
644
648
 
645
- if only is not None:
646
- pages = [p for p in pages if p[0] != 'slide' or p[1] in only]
649
+ if only is not None: # 对样张和版式一律生效(form=3 的代表页是版式,不是样张)
650
+ pages = [p for p in pages if p[1] in only]
647
651
 
648
652
  stats = {'shapes': 0, 'text': 0, 'img_ok': 0, 'img_missing': 0, 'no_box': 0}
649
- index = [HEAD, INDEX_EXTRA, '<div class="wrap">']
653
+ cw, ch = ((data.get('canvas') or {}).get('px') or [1920, 1080])[:2]
654
+ head = HEAD % (CJK_STACK, cw, ch) # 视口跟画布走,非 16:9 模板不能按 1920x1080 裁
655
+ thumb_scale = 960.0 / cw
656
+ index = [head, INDEX_EXTRA % (960, 960, round(ch * thumb_scale), thumb_scale),
657
+ '<div class="wrap">']
650
658
  for kind, n, label, layers, default_color, page_bg in (() if args.no_html else pages):
651
659
  html = page_html(layers, default_color, page_bg, media_url, stats)
652
660
  with open(os.path.join(rebuild, '%s-%d.html' % (kind, n)), 'w', encoding='utf-8') as f:
653
- f.write(HEAD + html)
661
+ f.write(head + html)
654
662
  index += ['<div class="lbl">%s</div>' % esc(label),
655
663
  '<div class="box">%s</div>' % html]
656
664
  index.append('</div>')