Sources: extracted from two upstream archives (skill-repo, skills-main),
merged with the following policy:
- 15 broken symlinks (pointing to /Users/jameslee/.cc-switch/skills or
../../.agents/skills on a foreign machine) discarded
- 3 real name collisions with identical content (ai-pair, ifind-http-api,
zhipu-websearch) kept as one copy
- Functional overlaps deduped keeping the strongest variant:
- docx family: kept docx (official, full toolchain) + docx-cn
(GB/T 9704 Chinese official-document constants),
dropped docx_writer (no scripts, name collided with docx)
- humanizer family: kept humanizer-zh (6 zh reference docs),
dropped humanizer (en, redundant for CN workflow)
- Skills that only ran in a foreign environment removed:
ablemind-ops, app-publish, hlb-design-system, openclaw-adj-skill,
claude-driver
- alphapai excluded from this public repo because its SKILL.md hard-coded
live credentials
Result: 25 skills, 572 files, ~7.5 MB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
136 lines
5.3 KiB
Python
136 lines
5.3 KiB
Python
"""结论页布局 — 投行级左右分栏"""
|
||
|
||
from pptx.util import Inches
|
||
from pptx.enum.shapes import MSO_SHAPE
|
||
from ..helpers import set_slide_bg, add_text, add_rect, add_rounded_rect
|
||
|
||
|
||
def layout_conclusion_dark(slide, data, theme):
|
||
"""深色结论页 — 左侧结论 + 右侧风险/指标卡片
|
||
|
||
data keys:
|
||
title (str, optional): 标题,默认 "结论"
|
||
verdict (str): 核心结论
|
||
score (str, optional): 评分(如 "7/10")
|
||
items (list[dict], optional): 摘要要点 [{label, value}]
|
||
risk (str, optional): 风险提示
|
||
footnote (str, optional): 脚注
|
||
"""
|
||
m = theme["margin"]
|
||
sw = theme.get("slide_w", 13.333)
|
||
sh = theme.get("slide_h", 7.5)
|
||
cover_title_size = theme.get("cover_title_size", 36)
|
||
|
||
set_slide_bg(slide, theme["bg_dark"])
|
||
|
||
# 顶部装饰线
|
||
add_rect(slide, 0, 0, sw, 0.06, fill=theme["accent"])
|
||
|
||
# 标题
|
||
title = data.get("title", "结论")
|
||
page_title_size = theme.get("page_title_size", 16)
|
||
add_text(slide, title,
|
||
x=m, y=0.3, w=sw - 2 * m, h=0.5,
|
||
font_size=page_title_size, color=theme["text_light"],
|
||
bold=True, font_name=theme["header_font"])
|
||
|
||
# 分隔线
|
||
add_rect(slide, m, 0.85, sw - 2 * m, theme.get("divider_h", 0.015),
|
||
fill=theme["primary"])
|
||
|
||
items = data.get("items", [])
|
||
has_right = bool(items) or data.get("risk")
|
||
|
||
if has_right:
|
||
# 左右分栏模式
|
||
left_w = (sw - 2 * m) * 0.55
|
||
right_x = m + left_w + 0.4
|
||
right_w = sw - right_x - m
|
||
else:
|
||
left_w = sw - 2 * m
|
||
right_x = 0
|
||
right_w = 0
|
||
|
||
# ── 左侧: 核心结论 ──
|
||
# 评分(如有)
|
||
verdict_y = 1.2
|
||
if data.get("score"):
|
||
add_text(slide, data["score"],
|
||
x=m, y=verdict_y, w=left_w, h=0.8,
|
||
font_size=cover_title_size, color=theme["accent"],
|
||
bold=True, align="left",
|
||
font_name=theme["header_font"])
|
||
verdict_y += 1.0
|
||
|
||
# 核心结论
|
||
add_text(slide, data["verdict"],
|
||
x=m + 0.1, y=verdict_y, w=left_w - 0.2, h=1.8,
|
||
font_size=theme["subtitle_size"] + 2, color=theme["text_light"],
|
||
bold=True, align="left", valign="top",
|
||
font_name=theme["header_font"])
|
||
|
||
# 左侧下方: 3 条结论要点(如果 data 提供了 conclusions 列表)
|
||
conclusions = data.get("conclusions", [])
|
||
if conclusions:
|
||
cy = verdict_y + 2.2
|
||
for i, c in enumerate(conclusions[:3]):
|
||
# 小色块标记
|
||
add_rect(slide, m + 0.1, cy + 0.08, 0.25, 0.04, fill=theme["accent"])
|
||
# 粗体标题
|
||
c_title = c.get("title", c) if isinstance(c, dict) else str(c)
|
||
add_text(slide, c_title,
|
||
x=m + 0.5, y=cy - 0.05, w=left_w - 0.7, h=0.35,
|
||
font_size=theme["body_size"], color=theme["text_light"],
|
||
bold=True, font_name=theme["header_font"])
|
||
# 解释行
|
||
c_desc = c.get("desc", "") if isinstance(c, dict) else ""
|
||
if c_desc:
|
||
add_text(slide, c_desc,
|
||
x=m + 0.5, y=cy + 0.3, w=left_w - 0.7, h=0.3,
|
||
font_size=theme["body_size"] - 1, color=theme["text_muted"],
|
||
font_name=theme["body_font"])
|
||
cy += 0.8
|
||
else:
|
||
cy += 0.5
|
||
|
||
# ── 右侧: 指标卡片 + 风险 ──
|
||
if has_right:
|
||
card_y = 1.2
|
||
if items:
|
||
for i, item in enumerate(items):
|
||
# 极简卡片: 半透明背景
|
||
add_rect(slide, right_x, card_y, right_w, 0.9,
|
||
fill=theme["primary"], line_color=theme["border"],
|
||
line_width=0.5)
|
||
# 短横线装饰
|
||
add_rect(slide, right_x + 0.15, card_y + 0.12, 0.2, 0.03,
|
||
fill=theme["accent"])
|
||
add_text(slide, item["label"],
|
||
x=right_x + 0.15, y=card_y + 0.2, w=right_w - 0.3, h=0.25,
|
||
font_size=theme.get("footer_size", 8) + 1, color=theme["text_muted"],
|
||
font_name=theme["body_font"])
|
||
add_text(slide, item["value"],
|
||
x=right_x + 0.15, y=card_y + 0.48, w=right_w - 0.3, h=0.35,
|
||
font_size=theme["body_size"] + 1, color=theme["text_light"],
|
||
bold=True, font_name=theme["header_font"])
|
||
card_y += 1.05
|
||
|
||
# 风险提示
|
||
if data.get("risk"):
|
||
risk_y = max(card_y + 0.2, 5.5)
|
||
add_rect(slide, right_x, risk_y, right_w, 0.04, fill=theme["negative"])
|
||
add_text(slide, f"Risk: {data['risk']}",
|
||
x=right_x, y=risk_y + 0.1, w=right_w, h=0.8,
|
||
font_size=theme.get("footer_size", 8) + 1, color=theme["text_muted"],
|
||
italic=True, font_name=theme["body_font"])
|
||
|
||
# 脚注
|
||
if data.get("footnote"):
|
||
add_text(slide, data["footnote"],
|
||
x=m, y=6.9, w=sw - 2 * m, h=0.25,
|
||
font_size=theme.get("footer_size", 8), color=theme["text_muted"],
|
||
align="center", font_name=theme["body_font"])
|
||
|
||
# 底部装饰线
|
||
add_rect(slide, 0, sh - 0.06, sw, 0.06, fill=theme["accent"])
|