@yujp_vc/ppt-assistant 1.5.2 → 1.5.3
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/README.md +18 -1
- package/build_deck.py +34 -1
- package/cordis.patch.yml +16 -2
- package/figure_audit.py +145 -0
- package/package.json +3 -2
- package/preset.yml +1 -1
- package/style-recipes.md +23 -1
package/README.md
CHANGED
|
@@ -69,6 +69,23 @@ bash run_py.sh gen_image.py --prompt-file hero.txt --out assets/hero.png \
|
|
|
69
69
|
|
|
70
70
|
`image_split` / `image_grid` 也升级为满格裁切 + 圆角 + 软阴影。所有图片一律 cover 裁切、不留白边,蒙版颜色由图片暗部采样,文字区按页型自动避让。
|
|
71
71
|
|
|
72
|
+
## 结构版式
|
|
73
|
+
|
|
74
|
+
| 版式 | 用途 | 关键字段 |
|
|
75
|
+
| --- | --- | --- |
|
|
76
|
+
| `architecture` | 3 层总体路线:三个框 + 箭头 + 底部共同验证条 | `items[3]{title,body}` `bottom_title` `bottom` |
|
|
77
|
+
|
|
78
|
+
学术/基金 deck 的总体路线优先用它重画,不把论文总图整张贴进来。
|
|
79
|
+
|
|
80
|
+
## 图件体检
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
bash run_py.sh figure_audit.py output.pptx
|
|
84
|
+
bash run_py.sh figure_audit.py output.pptx --strict # 有 DENSE 就 exit 2
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
它用 OCR 估计图内文字的实际字号,并结合图片占版面积,标出投影上读不了的密集图。经验线:图内文字低于 14pt 就不该直接上版;4800px 论文总图塞进 7in,30px 字只剩约 3pt。`--no-ocr` 可跳过 OCR,只按像素密度判断。
|
|
88
|
+
|
|
72
89
|
## 炫技版式
|
|
73
90
|
|
|
74
91
|
| 版式 | 用途 | 关键字段 |
|
|
@@ -92,7 +109,7 @@ bash run_py.sh gen_image.py --prompt-file hero.txt --out assets/hero.png \
|
|
|
92
109
|
## 视觉总监与风格配方
|
|
93
110
|
|
|
94
111
|
- 先明确受众、要促成的判断、内容类型、场景、情绪;再写七行风格简报:三个形容词 / 一个设计张力 / 密度 / 配色逻辑 / 字体逻辑 / 图片行为 / 空间节奏。
|
|
95
|
-
- 从 `<engine-root>/style-recipes.md` 选一套主配方:`consulting` `formal` `civic` `artfair` `editorial` `history`;需要时最多加一套辅助,声明主/辅。把 `meta.recipe` 记进 spec。
|
|
112
|
+
- 从 `<engine-root>/style-recipes.md` 选一套主配方:`consulting` `formal` `grant` `civic` `artfair` `editorial` `history`;需要时最多加一套辅助,声明主/辅。把 `meta.recipe` 记进 spec。
|
|
96
113
|
- 配方 tokens 落进 `meta`:`theme` `accent` `colors` `font` `titleFont`。`titleFont` 可让标题单独用衬线(如 `Noto Serif CJK SC`),正文仍用 `font`。
|
|
97
114
|
- 评审顺序:先 `contact_sheet.py` 出缩略图总览,看节奏/密度/重复,再抽封面、最密页、特殊媒体页、结束页全尺寸看。
|
|
98
115
|
|
package/build_deck.py
CHANGED
|
@@ -31,7 +31,7 @@ from charts import draw_chart, resolve_chart
|
|
|
31
31
|
from grasp_evidence import (candidate_projections, depth_colors, depth_pair,
|
|
32
32
|
gated_candidate_projections, gripper_projection)
|
|
33
33
|
|
|
34
|
-
LAYOUTS = {"cover", "cards", "process", "comparison", "table", "chart",
|
|
34
|
+
LAYOUTS = {"cover", "cards", "process", "architecture", "comparison", "table", "chart",
|
|
35
35
|
"image_split", "image_grid", "image_triptych", "ingredient_board", "ingredient_split", "state_triptych", "state_band", "state_gallery", "prep_stove", "heat_bars", "chart_dashboard", "closing",
|
|
36
36
|
"image_hero", "image_overlay", "image_wall",
|
|
37
37
|
"section_number", "image_statement",
|
|
@@ -92,6 +92,15 @@ def validate(spec: dict, base: Path) -> None:
|
|
|
92
92
|
layout = slide["layout"]
|
|
93
93
|
if not slide.get("title") and layout not in {"cover", "closing"}:
|
|
94
94
|
raise ValueError(f"slide {index}: title is required")
|
|
95
|
+
if layout == "architecture":
|
|
96
|
+
items = slide.get("items")
|
|
97
|
+
if not isinstance(items, list) or len(items) != 3:
|
|
98
|
+
raise ValueError(f"slide {index}: architecture requires exactly 3 items")
|
|
99
|
+
if any(not isinstance(item, dict) or not item.get("title") or not item.get("body")
|
|
100
|
+
for item in items):
|
|
101
|
+
raise ValueError(f"slide {index}: each architecture item requires title and body")
|
|
102
|
+
if slide.get("bottom") and not isinstance(slide["bottom"], str):
|
|
103
|
+
raise ValueError(f"slide {index}: architecture bottom must be text")
|
|
95
104
|
if layout in {"cards", "process", "comparison", "image_grid"}:
|
|
96
105
|
items = slide.get("items")
|
|
97
106
|
if layout == "comparison":
|
|
@@ -1147,6 +1156,30 @@ class DeckBuilder:
|
|
|
1147
1156
|
shape.fill.solid()
|
|
1148
1157
|
shape.fill.fore_color.rgb = RGBColor.from_string(self.colors["accent"])
|
|
1149
1158
|
shape.line.fill.background()
|
|
1159
|
+
elif layout == "architecture":
|
|
1160
|
+
items = page["items"]
|
|
1161
|
+
for index, item in enumerate(items):
|
|
1162
|
+
x = .60 + index * 4.10
|
|
1163
|
+
self.box(slide, x, 2.15, 3.55, 2.20, rounded=True)
|
|
1164
|
+
self.box(slide, x, 2.15, 3.55, .04, "B04A2A")
|
|
1165
|
+
self.text(slide, f"{index + 1:02d}", x + .18, 2.30, .55, .28,
|
|
1166
|
+
12, "accent", True)
|
|
1167
|
+
self.text(slide, item["title"], x + .18, 2.62, 3.20, .38,
|
|
1168
|
+
18, "text", True)
|
|
1169
|
+
self.text(slide, item["body"], x + .18, 3.04, 3.20, 1.18,
|
|
1170
|
+
14.5, "muted")
|
|
1171
|
+
if index < 2:
|
|
1172
|
+
shape = slide.shapes.add_shape(
|
|
1173
|
+
MSO_SHAPE.CHEVRON, Inches(x + 3.62), Inches(3.02),
|
|
1174
|
+
Inches(.30), Inches(.44))
|
|
1175
|
+
shape.fill.solid()
|
|
1176
|
+
shape.fill.fore_color.rgb = RGBColor.from_string(self.colors["accent"])
|
|
1177
|
+
shape.line.fill.background()
|
|
1178
|
+
if page.get("bottom"):
|
|
1179
|
+
self.box(slide, .60, 4.72, 12.10, 1.35, "F1E7DB")
|
|
1180
|
+
self.text(slide, page.get("bottom_title", "共同验证"), .90, 4.95,
|
|
1181
|
+
2.15, .30, 13, "accent", True)
|
|
1182
|
+
self.text(slide, page["bottom"], 3.15, 4.90, 9.20, .95, 14, "text")
|
|
1150
1183
|
elif layout == "image_split":
|
|
1151
1184
|
self.artwork(slide, page["image"], .6, 2.05, 7.1, 4.55,
|
|
1152
1185
|
radius=.05, shadow=True)
|
package/cordis.patch.yml
CHANGED
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
VISUAL DIRECTOR — 先当总监,再当排版工
|
|
53
53
|
- 先明确五件事:受众、要促成的判断/行动、内容类型、展示场景、情绪基调。缺一项就做合理假设,不要反复追问。
|
|
54
54
|
- 写七行风格简报(≤120 字):受众+行动 / 三个形容词 / 一个设计张力(如「密度 vs 留白」)/ 密度 / 配色逻辑 / 字体逻辑 / 图片行为+空间节奏。
|
|
55
|
-
- 只选一套主配方(<engine-root>/style-recipes.md:consulting / formal / civic / artfair / editorial / history
|
|
55
|
+
- 只选一套主配方(<engine-root>/style-recipes.md:consulting / formal / grant / civic / artfair / editorial / history);学术/基金/评审场景优先 `grant`。需要时最多加一套辅助,只借 1–2 个特征,声明主/辅,不平均折中。把 `meta.recipe` 记进 spec。
|
|
56
56
|
- 把配方 tokens 落进 `meta`:theme / accent / colors / font / titleFont。标题想用衬线就设 `titleFont`(如 Noto Serif CJK SC)。
|
|
57
57
|
- 配方是提示不是牢笼:证据、受众或场景提出更强要求时,让内容覆盖配方。
|
|
58
58
|
|
|
@@ -71,10 +71,23 @@
|
|
|
71
71
|
- 先搭垂直结构:页眉 → 标题 → 标题/正文间距 → 主体 → 结论/来源 → 页脚安全区。
|
|
72
72
|
- 密度先靠分组、对齐和证据结构增加,再考虑缩字号;同页版式轮廓可换,字体/边距/颜色角色/页脚规则必须一致。
|
|
73
73
|
|
|
74
|
+
ACADEMIC & GRANT — 学术/基金 5 页先过评审问题,不搬论文小节
|
|
75
|
+
- 场景命中「国自然 / 基金 / 研究方案 / 特色创新 / 论文评审」时,主配方选 `grant`;标题用判断句,正文 ≥18pt;禁止把 `3.1.4.1`、`4.4` 这类论文编号带上屏。
|
|
76
|
+
- 5 页骨架:封面 → 总体路线(`architecture`,3 个框 + 底部共同验证)→ 机制 1(`process`)→ 机制 2(`cards`/`table` 只留一个判断)→ 创新与验证(`kpi_showcase`/`cards`)。每页只回答一个评审问题。
|
|
77
|
+
- 不要用 2×4 长句表把创新点全塞进去;拆成 3 个创新支柱,每个配 1–2 个验证指标。
|
|
78
|
+
- 「三条主线 / 四级验证」要落到图上:三条主线用 `architecture` 的三框,四级验证放底部一条证据带,不用正文复述。
|
|
79
|
+
|
|
80
|
+
FIGURE CONTRACT — 图先能读,再谈好看
|
|
81
|
+
- 图片上版前先算有效字高:源图像素宽 / 版面英寸 = ppi;图中字高(px) / ppi × 72 = 实际 pt。图内文字低于 14pt,不允许直接上版。
|
|
82
|
+
- 论文总图/多机制拼图不要整张贴:4800px 的 fig18 塞进 7in,30px 字只剩约 3pt。这不是分辨率问题,是信息密度问题。
|
|
83
|
+
- 处理顺序:裁到一个机制 → 重画成 3–5 个可编辑框(`architecture`/`process`)→ 仍太密就拆页;一张图只承担一个判断。
|
|
84
|
+
- 架构页用 `architecture`:3 个框 + 箭头 + 底部共同验证条;标题、正文、图注全用真文本,不把截图当版面。
|
|
85
|
+
- 构建后跑 `figure_audit.py <pptx>`;P02/P04 这类 DENSE 行必须处理后再交付,不能因为 `quality_check` PASS 就放过。
|
|
86
|
+
|
|
74
87
|
PPT ENGINE — built-in `build_deck.py` (JSON spec → editable PPTX)
|
|
75
88
|
CLI: `bash <engine-root>/run_py.sh build_deck.py <spec.json> <output.pptx>`
|
|
76
89
|
- 规格中的图片路径相对于 spec 文件解析;不会扫描、导入或执行用户工作区脚本。
|
|
77
|
-
- 内置版式:cover / cards / process / comparison / image_split / image_grid / table / chart / closing,
|
|
90
|
+
- 内置版式:cover / cards / process / architecture / comparison / image_split / image_grid / table / chart / closing,
|
|
78
91
|
图片版式:image_hero / image_overlay / image_wall / image_triptych;
|
|
79
92
|
炫技版式:section_number(巨字 + 网格 + 进度点)、image_statement(满幅 + 巨型字艺)、
|
|
80
93
|
split_diagonal(双图斜切)、glass_overlay(满幅 + 毛玻璃面板)、heat_bars(甘特时间轴)、
|
|
@@ -105,6 +118,7 @@
|
|
|
105
118
|
- pexels_key: the Pexels API key (set here or via env PEXELS_API_KEY). Without a valid key, image_fetch falls back to loremflickr/picsum which return random/irrelevant images — always vision-verify fetched images match the keyword before using them.
|
|
106
119
|
|
|
107
120
|
SUPPLEMENTARY TOOLS (已装进 web profile,重启 GUI 后可用)
|
|
121
|
+
- `figure_audit.py <pptx>`:用 OCR 字高和图片面积标出“投影上读不了”的图;`--strict` 有 DENSE 就 exit 2。它是图件体检,不替代逐页视觉检查。
|
|
108
122
|
- @huiliyi37/dsh-office:pptx_create/pptx_read/pptx_edit + xlsx/pdf/docx 工具——读现有 PPT 结构、直接改文案、生成简单 deck。
|
|
109
123
|
- @deepseek-ai/dsh-mindmap:mm_generate 工具——把课件(PPT/PDF/Word)转成打印级 HTML 思维导图(复习用)。
|
|
110
124
|
- dshmarket(插件市场 UI)、dsh-find-plugin(agent 找插件)、dsh-hub(dsh.fish 注册表 hub 工具)。
|
package/figure_audit.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Flag dense technical figures that will be unreadable when projected.
|
|
3
|
+
|
|
4
|
+
A 4800 px-wide paper composite placed at 7 in has ~686 px/in. A 30 px label
|
|
5
|
+
inside it is then only ~3 pt on the slide -- high resolution does not help.
|
|
6
|
+
This audit estimates that effective label size from the source pixels and the
|
|
7
|
+
displayed size, and flags pictures that are both dense and large enough to be
|
|
8
|
+
the main evidence on a page.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import io
|
|
14
|
+
import json
|
|
15
|
+
import shutil
|
|
16
|
+
import subprocess
|
|
17
|
+
import sys
|
|
18
|
+
import tempfile
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from PIL import Image
|
|
22
|
+
from pptx import Presentation
|
|
23
|
+
from pptx.enum.shapes import MSO_SHAPE_TYPE
|
|
24
|
+
|
|
25
|
+
PAGE_W_IN = 13.333
|
|
26
|
+
PAGE_H_IN = 7.5
|
|
27
|
+
TESSERACT = shutil.which("tesseract")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def ocr_text_height(image: Image.Image) -> tuple[float, int]:
|
|
31
|
+
"""Median OCR word height in px; 0 if OCR is unavailable/no text."""
|
|
32
|
+
if not TESSERACT:
|
|
33
|
+
return 0.0, 0
|
|
34
|
+
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
|
35
|
+
image.save(tmp.name)
|
|
36
|
+
tmp_path = tmp.name
|
|
37
|
+
try:
|
|
38
|
+
proc = subprocess.run(
|
|
39
|
+
[TESSERACT, tmp_path, "stdout", "-l", "chi_sim+eng", "--psm", "11", "tsv"],
|
|
40
|
+
capture_output=True, text=True, timeout=30)
|
|
41
|
+
heights = []
|
|
42
|
+
for line in proc.stdout.splitlines()[1:]:
|
|
43
|
+
cols = line.split(" ")
|
|
44
|
+
if len(cols) < 12:
|
|
45
|
+
continue
|
|
46
|
+
text = cols[11].strip()
|
|
47
|
+
try:
|
|
48
|
+
conf = float(cols[10])
|
|
49
|
+
height = int(cols[9])
|
|
50
|
+
except ValueError:
|
|
51
|
+
continue
|
|
52
|
+
if text and conf >= 35 and height >= 3:
|
|
53
|
+
heights.append(height)
|
|
54
|
+
if len(heights) < 5:
|
|
55
|
+
return 0.0, len(heights)
|
|
56
|
+
heights.sort()
|
|
57
|
+
return float(heights[len(heights) // 2]), len(heights)
|
|
58
|
+
except Exception:
|
|
59
|
+
return 0.0, 0
|
|
60
|
+
finally:
|
|
61
|
+
Path(tmp_path).unlink(missing_ok=True)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def audit(path: Path, max_ppi: float = 450.0, min_area: float = 0.12,
|
|
65
|
+
use_ocr: bool = True) -> dict:
|
|
66
|
+
prs = Presentation(str(path))
|
|
67
|
+
rows = []
|
|
68
|
+
for page_no, slide in enumerate(prs.slides, 1):
|
|
69
|
+
for shape in slide.shapes:
|
|
70
|
+
if shape.shape_type != MSO_SHAPE_TYPE.PICTURE:
|
|
71
|
+
continue
|
|
72
|
+
try:
|
|
73
|
+
with Image.open(io.BytesIO(shape.image.blob)) as image:
|
|
74
|
+
px_w, px_h = image.size
|
|
75
|
+
image_rgb = image.convert("RGB")
|
|
76
|
+
except Exception:
|
|
77
|
+
continue
|
|
78
|
+
disp_w = shape.width / 914400
|
|
79
|
+
disp_h = shape.height / 914400
|
|
80
|
+
if disp_w <= 0 or disp_h <= 0:
|
|
81
|
+
continue
|
|
82
|
+
ppi = px_w / disp_w
|
|
83
|
+
area = (disp_w * disp_h) / (PAGE_W_IN * PAGE_H_IN)
|
|
84
|
+
gray = image_rgb.convert("L")
|
|
85
|
+
white_pct = sum(1 for value in gray.getdata() if value > 235) / (px_w * px_h) * 100
|
|
86
|
+
text_px, word_count = ocr_text_height(image_rgb) if use_ocr else (0.0, 0)
|
|
87
|
+
text_pt = text_px / ppi * 72.0 if text_px else 0.0
|
|
88
|
+
if text_px:
|
|
89
|
+
dense = (area >= min_area and text_pt < 13.0 and word_count >= 5
|
|
90
|
+
and white_pct >= 12.0)
|
|
91
|
+
else:
|
|
92
|
+
# No OCR evidence: only flag very high pixel density, where a
|
|
93
|
+
# pasted paper figure is almost certainly over-detailed.
|
|
94
|
+
dense = ppi >= max_ppi and area >= min_area
|
|
95
|
+
rows.append({
|
|
96
|
+
"page": page_no,
|
|
97
|
+
"name": shape.name,
|
|
98
|
+
"source_px": [px_w, px_h],
|
|
99
|
+
"display_in": [round(disp_w, 2), round(disp_h, 2)],
|
|
100
|
+
"ppi": round(ppi, 0),
|
|
101
|
+
"area_pct": round(area * 100, 1),
|
|
102
|
+
"text_pt": round(text_pt, 1) if text_px else None,
|
|
103
|
+
"word_count": word_count,
|
|
104
|
+
"white_pct": round(white_pct, 1),
|
|
105
|
+
"dense": dense,
|
|
106
|
+
})
|
|
107
|
+
return {"file": str(path), "pictures": rows,
|
|
108
|
+
"dense": [row for row in rows if row["dense"]]}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def main() -> int:
|
|
112
|
+
parser = argparse.ArgumentParser(description="Flag dense figures / unreadable slide images")
|
|
113
|
+
parser.add_argument("pptx", type=Path)
|
|
114
|
+
parser.add_argument("--max-ppi", type=float, default=450.0)
|
|
115
|
+
parser.add_argument("--min-area", type=float, default=0.12,
|
|
116
|
+
help="minimum picture area ratio of the slide (0-1)")
|
|
117
|
+
parser.add_argument("--strict", action="store_true", help="exit 2 when dense figures exist")
|
|
118
|
+
parser.add_argument("--no-ocr", action="store_true", help="skip OCR text-height estimate")
|
|
119
|
+
parser.add_argument("--json", action="store_true")
|
|
120
|
+
args = parser.parse_args()
|
|
121
|
+
if not args.pptx.is_file():
|
|
122
|
+
print(f"figure_audit: file not found: {args.pptx}", file=sys.stderr)
|
|
123
|
+
return 2
|
|
124
|
+
result = audit(args.pptx, args.max_ppi, args.min_area, use_ocr=not args.no_ocr)
|
|
125
|
+
if args.json:
|
|
126
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
127
|
+
else:
|
|
128
|
+
print(f"figure_audit: {len(result['pictures'])} picture(s)")
|
|
129
|
+
for row in result["pictures"]:
|
|
130
|
+
flag = "DENSE" if row["dense"] else "ok"
|
|
131
|
+
print(f" P{row['page']:02d} {row['name']}: "
|
|
132
|
+
f"{row['source_px'][0]}x{row['source_px'][1]}px -> "
|
|
133
|
+
f"{row['display_in'][0]}x{row['display_in'][1]}in, "
|
|
134
|
+
f"{row['ppi']:.0f} ppi, area {row['area_pct']}%, "
|
|
135
|
+
f"OCR text ~{row['text_pt']}pt, white {row['white_pct']}% [{flag}]")
|
|
136
|
+
if result["dense"]:
|
|
137
|
+
print("\nFix: crop to one mechanism, redraw as 3-5 editable boxes, "
|
|
138
|
+
"or split the figure across pages. Do not paste the whole paper composite.")
|
|
139
|
+
else:
|
|
140
|
+
print("No dense large figure detected.")
|
|
141
|
+
return 2 if args.strict and result["dense"] else 0
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
if __name__ == "__main__":
|
|
145
|
+
raise SystemExit(main())
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yujp_vc/ppt-assistant",
|
|
3
|
-
"version": "1.5.
|
|
4
|
-
"description": "PPT 助手 agent preset for DeepSeek Harness: a visual-director workflow with
|
|
3
|
+
"version": "1.5.3",
|
|
4
|
+
"description": "PPT 助手 agent preset for DeepSeek Harness: a visual-director workflow with seven style recipes, plan-first editable decks, GPT imagery/vision QC, showcase layouts, and a verified scoped-edit ledger.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
7
7
|
"type": "module",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"grasp_evidence.py",
|
|
23
23
|
"generate_ppt.py",
|
|
24
24
|
"generate_consensus.py",
|
|
25
|
+
"figure_audit.py",
|
|
25
26
|
"quality_check.py",
|
|
26
27
|
"check.py",
|
|
27
28
|
"ingest.py",
|
package/preset.yml
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
name: PPT 助手
|
|
2
|
-
description: 一条龙 PPT 生成 Agent
|
|
2
|
+
description: 一条龙 PPT 生成 Agent:视觉总监式风格简报与七套配方、金字塔页计划、自主 GPT 生图(含透明抠图与视觉验收)、满幅/渐变/毛玻璃/斜切/抠图压版等高级排版、品牌色与双字体系统、可核验的改稿台账、读取 PPT/PDF(含 OCR)、更改已有 PPT,并渲染自查(缩略图总览 + 视觉模型逐页审查)迭代到满意后输出可编辑 .pptx。
|
package/style-recipes.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 视觉配方(PPT 助手 · 视觉总监层)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
这不是模板库,是七套完整的视觉判断。先选一套当主配方,再用第二套做辅助(只借它的 1–2 个特征,不平均混合)。把 `meta.recipe` 记进 spec,保证整套不漂。
|
|
4
4
|
|
|
5
5
|
## 用法
|
|
6
6
|
|
|
@@ -143,6 +143,28 @@
|
|
|
143
143
|
|
|
144
144
|
---
|
|
145
145
|
|
|
146
|
+
## 7. 学术基金 · grant
|
|
147
|
+
|
|
148
|
+
- **三个形容词**:可信、克制、判断先行
|
|
149
|
+
- **设计张力**:大结论 vs 小证据——每页只回答评审的一个问题,图只承担一个机制
|
|
150
|
+
- **密度**:低到中。5 页一份;正文 18–22pt,图注 14pt,图内标签 ≥14pt;宁少一页,不塞一张论文总图
|
|
151
|
+
- **配色逻辑**:象牙纸底 60% + 墨色 30% + 单一深青强调 10%;不要 corporate 蓝(如 #2F5597);红只标风险/待确认
|
|
152
|
+
- **字体逻辑**:标题思源宋体 30–36,正文思源黑体 18–22;不要用 12pt 正文撑信息量
|
|
153
|
+
- **图片行为**:论文总图/架构图不整张贴——先裁到一个机制,或重画成 3–5 个可编辑框;截图必须有 ≥14pt 的标注;实拍只做证据,不做装饰
|
|
154
|
+
- **空间节奏**:封面 → 科学问题/总体路线(architecture)→ 机制 1(process)→ 机制 2(cards/table)→ 创新与验证(kpi/cards);不要目录、章节编号和论文小节号
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
"meta": {"theme": "paper", "recipe": "grant", "accent": "2F5D62",
|
|
158
|
+
"colors": {"bg": "F7F3EC", "panel": "FFFDF8", "text": "1C1B19", "muted": "6F675C"},
|
|
159
|
+
"font": "Noto Sans CJK SC", "titleFont": "Noto Serif CJK SC"}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
版式池:`cover` `architecture` `process` `cards` `kpi_showcase` `table` `closing`
|
|
163
|
+
骨架:封面 → 总体路线(三段共用一套状态)→ 规程解析/规划 → 执行证据/记忆恢复 → 创新点与验证指标
|
|
164
|
+
避免:整张 4800px 论文图塞进 6–7in、`3.1.4.1` 这类论文编号、2×4 长句表、12pt 正文、三张等权卡片却没有证据。
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
146
168
|
## 风格简报模板(没有匹配配方时)
|
|
147
169
|
|
|
148
170
|
写七行,不超过 120 字:
|