dsh-courseware 0.1.0

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 (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +109 -0
  3. package/cordis.patch.yml +9 -0
  4. package/engine/pptxgen/__init__.py +8 -0
  5. package/engine/pptxgen/__main__.py +4 -0
  6. package/engine/pptxgen/builtin_template.py +218 -0
  7. package/engine/pptxgen/cli.py +575 -0
  8. package/engine/pptxgen/examples.py +290 -0
  9. package/engine/pptxgen/imagegen.py +564 -0
  10. package/engine/pptxgen/layoutmap.py +211 -0
  11. package/engine/pptxgen/patterns.py +106 -0
  12. package/engine/pptxgen/preview.py +126 -0
  13. package/engine/pptxgen/profile.py +456 -0
  14. package/engine/pptxgen/render.py +1673 -0
  15. package/engine/pptxgen/review.py +184 -0
  16. package/engine/pptxgen/schemacheck.py +122 -0
  17. package/engine/pptxgen/schemas/XAdES.xsd +466 -0
  18. package/engine/pptxgen/schemas/XAdESv141.xsd +15 -0
  19. package/engine/pptxgen/schemas/chartEx.xsd +838 -0
  20. package/engine/pptxgen/schemas/dml-chart.xsd +1499 -0
  21. package/engine/pptxgen/schemas/dml-chartDrawing.xsd +146 -0
  22. package/engine/pptxgen/schemas/dml-diagram.xsd +1085 -0
  23. package/engine/pptxgen/schemas/dml-drawing.xsd +63 -0
  24. package/engine/pptxgen/schemas/dml-lockedCanvas.xsd +11 -0
  25. package/engine/pptxgen/schemas/dml-main.xsd +3081 -0
  26. package/engine/pptxgen/schemas/dml-picture.xsd +23 -0
  27. package/engine/pptxgen/schemas/dml-spreadsheetDrawing.xsd +185 -0
  28. package/engine/pptxgen/schemas/dml-wordprocessingDrawing.xsd +287 -0
  29. package/engine/pptxgen/schemas/drawing-chart2012.xsd +129 -0
  30. package/engine/pptxgen/schemas/markup-compatibility.xsd +95 -0
  31. package/engine/pptxgen/schemas/opc-digSig.xsd +49 -0
  32. package/engine/pptxgen/schemas/opc-relationships.xsd +33 -0
  33. package/engine/pptxgen/schemas/pml.xsd +1676 -0
  34. package/engine/pptxgen/schemas/shared-additionalCharacteristics.xsd +28 -0
  35. package/engine/pptxgen/schemas/shared-bibliography.xsd +144 -0
  36. package/engine/pptxgen/schemas/shared-commonSimpleTypes.xsd +172 -0
  37. package/engine/pptxgen/schemas/shared-customXmlDataProperties.xsd +25 -0
  38. package/engine/pptxgen/schemas/shared-customXmlSchemaProperties.xsd +18 -0
  39. package/engine/pptxgen/schemas/shared-documentPropertiesCustom.xsd +59 -0
  40. package/engine/pptxgen/schemas/shared-documentPropertiesExtended.xsd +56 -0
  41. package/engine/pptxgen/schemas/shared-documentPropertiesVariantTypes.xsd +195 -0
  42. package/engine/pptxgen/schemas/shared-math.xsd +582 -0
  43. package/engine/pptxgen/schemas/shared-relationshipReference.xsd +25 -0
  44. package/engine/pptxgen/schemas/signatureInfo.xsd +103 -0
  45. package/engine/pptxgen/schemas/sml.xsd +4439 -0
  46. package/engine/pptxgen/schemas/visio.xsd +829 -0
  47. package/engine/pptxgen/schemas/vml-main.xsd +570 -0
  48. package/engine/pptxgen/schemas/vml-officeDrawing.xsd +509 -0
  49. package/engine/pptxgen/schemas/vml-presentationDrawing.xsd +12 -0
  50. package/engine/pptxgen/schemas/vml-spreadsheetDrawing.xsd +108 -0
  51. package/engine/pptxgen/schemas/vml-wordprocessingDrawing.xsd +96 -0
  52. package/engine/pptxgen/schemas/vmlDrawing.xsd +36 -0
  53. package/engine/pptxgen/schemas/wml.xsd +3643 -0
  54. package/engine/pptxgen/schemas/word12.xsd +66 -0
  55. package/engine/pptxgen/schemas/xlThreadedComments.xsd +59 -0
  56. package/engine/pptxgen/schemas/xlThreadedComments2.xsd +22 -0
  57. package/engine/pptxgen/schemas/xmldsig-core-schema.xsd +318 -0
  58. package/engine/pptxgen/shapes.py +417 -0
  59. package/engine/pptxgen/spec.py +529 -0
  60. package/engine/pptxgen/template.py +834 -0
  61. package/engine/pptxgen/util.py +218 -0
  62. package/engine/pptxgen/xmlutil.py +298 -0
  63. package/engine/pptxgen.sh +7 -0
  64. package/engine/requirements.txt +9 -0
  65. package/lib/index.js +186 -0
  66. package/package.json +34 -0
  67. package/skills/courseware/SKILL.md +130 -0
  68. package/skills/courseware/references/content-polish.md +121 -0
  69. package/skills/courseware/references/deck-spec.md +155 -0
  70. package/skills/courseware/references/design-review.md +121 -0
  71. package/skills/courseware/references/image-backends.md +94 -0
  72. package/skills/courseware/references/quality-gates.md +71 -0
  73. package/skills/courseware/references/roadmap.md +55 -0
  74. package/skills/courseware/references/template-fidelity.md +100 -0
  75. package/skills/courseware/workflows/generate.md +172 -0
  76. package/skills/courseware/workflows/quick.md +41 -0
  77. package/skills/courseware/workflows/revise.md +40 -0
  78. package/skills/courseware/workflows/routing.md +49 -0
  79. package/skills/courseware/workflows/stages/generate-images.md +74 -0
  80. package/skills/courseware/workflows/template-intake.md +112 -0
package/lib/index.js ADDED
@@ -0,0 +1,186 @@
1
+ // dsh-courseware: 中小学课件生成技能(courseware)的 DSH 插件包。
2
+ //
3
+ // 一个 Cordis 插件:把本包内 `skills/courseware` 注册进 `ctx.skills` 注册表
4
+ // 的 host 层,使 profile 里每个 agent 的技能目录都能看到并加载它。
5
+ // 技能正文随包分发(`../skills/<name>/SKILL.md`),提供器用
6
+ // `import.meta.url` 定位(包的组装事实,不是用户配置),按需读取正文。
7
+ //
8
+ // 提供器协议对齐 @deepseek-ai/dsh-skill-filesystem:
9
+ // - list() 扫描 skills/ 下的目录型技能(name/description/whenToUse 从
10
+ // YAML frontmatter 读取,正文留到 get() 才读)
11
+ // - get() 解析获胜候选的 SKILL.md,返回完整定义,resourceBase 指向
12
+ // 技能目录,使相对引用(workflows/、references/)可以解析
13
+ //
14
+ // 零运行时依赖:只使用 Node 内置模块。frontmatter 解析只覆盖 DSH 技能
15
+ // 发现消费的标量字段(name/description/whenToUse),并支持 `>` 折叠块。
16
+ //
17
+ // @module dsh-courseware
18
+ import { readdir, readFile } from 'node:fs/promises'
19
+ import { fileURLToPath } from 'node:url'
20
+ import { dirname, join } from 'node:path'
21
+
22
+ const name = 'dsh-courseware'
23
+ const inject = ['skills']
24
+
25
+ /** 打包技能提供器的注册优先级:低于本地 bundled 根(600),高于用户层默认。 */
26
+ const PACKAGED_SKILL_RANK = 550
27
+
28
+ /** 这批技能对外声明的来源桶(prompt 可见元数据)。 */
29
+ const SOURCE = 'custom'
30
+
31
+ /**
32
+ * 解析 SKILL.md 的 YAML frontmatter 块,返回元数据 + 正文。
33
+ * 只处理 DSH 技能发现消费的标量字段(name/description/whenToUse),
34
+ * 支持 `key: value`(含引号)、`key: >` 折叠块、`key: |` 字面块;
35
+ * 缩进的嵌套键(如 `metadata: version: "1.0.0"`)扁平化进 metadata,
36
+ * 不深度解析。
37
+ * @param text - 技能文件原始内容。
38
+ * @returns 解析出的元数据与正文;文件没有 frontmatter 块时返回 null。
39
+ */
40
+ function parseFrontmatter(text) {
41
+ if (!text.startsWith('---')) return null
42
+ const end = text.indexOf('\n---', 3)
43
+ if (end === -1) return null
44
+ const block = text.slice(3, end)
45
+ const body = text.slice(end + 4).replace(/^\n+/, '')
46
+ const metadata = {}
47
+ const lines = block.split('\n')
48
+ let key = null
49
+ let mode = null // null | '>' | '|'
50
+ let pending = null
51
+ const commit = () => {
52
+ if (key === null) return
53
+ metadata[key] = mode === '>' ? pending.join(' ').replace(/\s+/g, ' ').trim()
54
+ : mode === '|' ? pending.join('\n')
55
+ : pending
56
+ key = null; mode = null; pending = null
57
+ }
58
+ for (const line of lines) {
59
+ const trimmed = line.trim()
60
+ if (trimmed === '' || trimmed.startsWith('#')) {
61
+ if (mode !== null) pending.push('')
62
+ continue
63
+ }
64
+ const match = /^([A-Za-z][\w-]*):\s*(.*)$/.exec(trimmed)
65
+ const indented = line.startsWith(' ') || line.startsWith('\t')
66
+ if (match && !indented) {
67
+ commit()
68
+ key = match[1]
69
+ const value = match[2].trim()
70
+ if (value === '>') { mode = '>'; pending = [] }
71
+ else if (value === '|') { mode = '|'; pending = [] }
72
+ else {
73
+ mode = null; pending = value
74
+ if ((pending.startsWith('"') && pending.endsWith('"'))
75
+ || (pending.startsWith("'") && pending.endsWith("'"))) {
76
+ pending = pending.slice(1, -1)
77
+ }
78
+ }
79
+ } else if (mode !== null && pending !== null) {
80
+ // 折叠/字面块的续行:接在上一行后面
81
+ pending.push(trimmed)
82
+ } else if (match) {
83
+ // 缩进的嵌套键:扁平化进 metadata(不深度解析)
84
+ let value = match[2].trim()
85
+ if ((value.startsWith('"') && value.endsWith('"'))
86
+ || (value.startsWith("'") && value.endsWith("'"))) {
87
+ value = value.slice(1, -1)
88
+ }
89
+ metadata[match[1]] = value
90
+ }
91
+ }
92
+ commit()
93
+ return { metadata, body }
94
+ }
95
+
96
+ /**
97
+ * 读取并解析一个技能目录的 SKILL.md。
98
+ * @param skillFile - SKILL.md 的绝对路径。
99
+ * @param signal - 可选的取消信号,中止读取。
100
+ * @returns 解析后的技能记录;文件消失时返回 undefined。
101
+ */
102
+ async function parseSkillFile(skillFile, signal) {
103
+ let text
104
+ try {
105
+ text = await readFile(skillFile, 'utf8')
106
+ } catch {
107
+ return undefined
108
+ }
109
+ if (signal?.aborted) return undefined
110
+ const parsed = parseFrontmatter(text)
111
+ if (parsed === null) return undefined
112
+ return {
113
+ name: parsed.metadata.name ?? '',
114
+ description: parsed.metadata.description ?? '',
115
+ whenToUse: parsed.metadata.whenToUse,
116
+ metadata: parsed.metadata,
117
+ content: parsed.body,
118
+ }
119
+ }
120
+
121
+ /**
122
+ * 扫描本包 `skills/` 目录发现候选技能:每个子目录一个技能,内含 SKILL.md。
123
+ * @param skillsRoot - 本包 skills 目录的绝对路径。
124
+ * @param signal - 可选取消信号。
125
+ * @returns 候选列表。
126
+ */
127
+ async function discoverCandidates(skillsRoot, signal) {
128
+ let entries
129
+ try {
130
+ entries = await readdir(skillsRoot, { withFileTypes: true })
131
+ } catch {
132
+ return []
133
+ }
134
+ const candidates = []
135
+ for (const entry of entries) {
136
+ if (signal?.aborted) break
137
+ if (!entry.isDirectory()) continue
138
+ const skillDir = join(skillsRoot, entry.name)
139
+ const skillFile = join(skillDir, 'SKILL.md')
140
+ const parsed = await parseSkillFile(skillFile, signal)
141
+ if (parsed === undefined) continue
142
+ candidates.push({
143
+ name: parsed.name,
144
+ description: parsed.description,
145
+ ...(parsed.whenToUse !== undefined ? { whenToUse: parsed.whenToUse } : {}),
146
+ invocation: { modelInvocable: true, userInvocable: true },
147
+ source: SOURCE,
148
+ provider: name,
149
+ rank: PACKAGED_SKILL_RANK,
150
+ locator: skillDir,
151
+ path: skillFile,
152
+ ...(Object.keys(parsed.metadata).length > 0 ? { metadata: parsed.metadata } : {}),
153
+ })
154
+ }
155
+ return candidates
156
+ }
157
+
158
+ /** 把本包技能注册进 `ctx.skills`。 */
159
+ function apply(ctx) {
160
+ const skillsRoot = join(dirname(fileURLToPath(import.meta.url)), '..', 'skills')
161
+ ctx.skills.registerProvider((control) => ({
162
+ name,
163
+ async list(options) {
164
+ return discoverCandidates(skillsRoot, options.signal)
165
+ },
166
+ async get(candidate, options) {
167
+ const parsed = await parseSkillFile(candidate.path, options.signal)
168
+ if (parsed === undefined) return undefined
169
+ return {
170
+ name: parsed.name,
171
+ description: parsed.description,
172
+ ...(parsed.whenToUse !== undefined ? { whenToUse: parsed.whenToUse } : {}),
173
+ invocation: { modelInvocable: true, userInvocable: true },
174
+ source: SOURCE,
175
+ provider: name,
176
+ resourceBase: { kind: 'directory', path: candidate.locator },
177
+ path: candidate.path,
178
+ ...(Object.keys(parsed.metadata).length > 0 ? { metadata: parsed.metadata } : {}),
179
+ content: parsed.content,
180
+ }
181
+ },
182
+ }))
183
+ }
184
+
185
+ export { apply, name, inject, parseFrontmatter }
186
+ export default { apply, name, inject }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "dsh-courseware",
3
+ "description": "中小学课件生成技能包:拿用户自己的 .pptx 模板 + 课程大纲,产出套用模板风格的多页课件。由 DeepSeek Harness 的 courseware 技能 + pptxgen 引擎打包而成。",
4
+ "version": "0.1.0",
5
+ "private": false,
6
+ "type": "module",
7
+ "main": "lib/index.js",
8
+ "exports": {
9
+ ".": "./lib/index.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "lib",
14
+ "skills",
15
+ "engine",
16
+ "cordis.patch.yml",
17
+ "README.md"
18
+ ],
19
+ "license": "MIT",
20
+ "keywords": [
21
+ "dsh",
22
+ "deepseek-harness",
23
+ "plugin",
24
+ "skill",
25
+ "courseware",
26
+ "pptx",
27
+ "课件"
28
+ ],
29
+ "dsh": {
30
+ "bundle": {
31
+ "patch": "./cordis.patch.yml"
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,130 @@
1
+ ---
2
+ name: courseware
3
+ description: >
4
+ 面向中小学课堂的课件生成工作流:拿用户自己的 .pptx 模板 + 一份课程大纲(教案/文档/纯文本),
5
+ 产出套用该模板背景、配色、字体、页面骨架的课件 .pptx。支持多母版模板、配图生成、
6
+ 思维导图页、随堂练习与答案页、讲稿备注。当用户要求「用这个模板做一份课件 / 把这节课的教案
7
+ 做成 PPT / 生成上课用的幻灯片」,或提到 courseware、课件、教案转 PPT 时使用。
8
+ metadata:
9
+ version: "1.0.0"
10
+ engine: pptxgen
11
+ license: MIT
12
+ ---
13
+
14
+ # Courseware —— 中小学课件生成
15
+
16
+ 用**用户自己的模板**生成课件,而不是从零设计。本文件只负责**路由**与**全局纪律**;
17
+ 每个路线的具体步骤由它自己的流程文件拥有。
18
+
19
+ ## 硬规则 —— 引擎位置
20
+
21
+ 引擎(Python 包 `pptxgen` + 它的 venv)**随本 skill 一起分发**,位于
22
+ 技能基础目录的**上两级**:`<技能基础目录>/../../engine`。技能基础目录 =
23
+ 本文件所在目录(agent 会从资源提示 "Base directory for this skill" 拿到它)。
24
+
25
+ 引擎目录判定(按顺序取第一个成立的):
26
+
27
+ 1. `<技能基础目录>/../../engine`(本插件包自带的引擎)
28
+ 2. 环境变量 `PPTXGEN_ENGINE_HOME` 指向的目录(用户可自指引擎)
29
+
30
+ **都不存在 → 报告并停止,不要搜索、不要新建 venv、不要猜测路径。**
31
+
32
+ 首次使用需初始化 Python 虚拟环境(一次性,目标机器需有 Python 3.10+):
33
+
34
+ ```bash
35
+ python3 -m venv "<引擎目录>/.venv"
36
+ "<引擎目录>/.venv/bin/pip" install -r "<引擎目录>/requirements.txt"
37
+ ```
38
+
39
+ 调用方式(**可在任意工作目录执行**,引擎目录会自动加入 PYTHONPATH):
40
+
41
+ ```bash
42
+ "$ENGINE_HOME/pptxgen.sh" <命令>
43
+ ```
44
+
45
+ 其中 `ENGINE_HOME` 为上面判定出的引擎目录。若当前工作目录下就有
46
+ `"$ENGINE_HOME/pptxgen.sh"`,优先用它(在引擎目录里开发时)。
47
+
48
+ ### 引擎目录 vs 工作目录
49
+
50
+ 这是两回事,不要混:
51
+
52
+ | | 位置 | 内容 | 是否变动 |
53
+ |---|---|---|---|
54
+ | **引擎目录** | `$ENGINE_HOME` | `pptxgen/` 源码、`.venv/`、内置示例 | 固定不动 |
55
+ | **工作目录** | 当前项目 | `templates/`(模板)、`decks/`(课件规格)、`out/`(产物) | 每门课可新建一个 |
56
+
57
+ 在**工作目录**下要有 `templates/` 与 `decks/` 两个子目录;没有就建。
58
+ 所有 `inspect` / `build` / `check` 的路径参数都相对**工作目录**解析。
59
+
60
+ ## 硬规则 —— 先诊断模板
61
+
62
+ **在写任何课件内容之前,必须先跑 `inspect`。** 模板的母版数量、页面骨架、真实字体
63
+ 都只能从样张里读出来;跳过这一步会套错整套配色与背景。
64
+
65
+ ```bash
66
+ "$ENGINE_HOME/pptxgen.sh" inspect "templates/用户的模板.pptx"
67
+ ```
68
+
69
+ 把输出里的三块读给用户确认:**母版**、**样式档案**、**页型→版式映射**。
70
+
71
+ ## 路由
72
+
73
+ | 用户意图 | 路线 | 权威流程 |
74
+ |---|---|---|
75
+ | 用模板 + 大纲生成新课件 | **Generate** | [`workflows/generate.md`](workflows/generate.md) |
76
+ | 同上,但明确说"快一点、不用确认" | **Quick** | [`workflows/quick.md`](workflows/quick.md) |
77
+ | 只想让工具认一认模板 / 检查模板可用性 | **Template Intake** | [`workflows/template-intake.md`](workflows/template-intake.md) |
78
+ | 已有课件,想改内容 / 加配图 / 换模板 | **Revise** | [`workflows/revise.md`](workflows/revise.md) |
79
+
80
+ 选择唯一一条路线,然后只加载该路线拥有的流程文件。子流程与参考文档**不是**顶层路线。
81
+
82
+ **不要给用户列路线菜单。** 请求已匹配上表某一行时,直接进入该路线。
83
+
84
+ ## 全局纪律
85
+
86
+ 1. **串行执行** —— 按所选流程的步骤顺序走,不要跳步或并行准备。
87
+ 2. **闸门处停下** —— 遇到 `⛔ 闸门` 必须等用户明确确认,不要替用户决定。
88
+ 3. **不投机执行** —— 后续阶段的产物不要在它所属的步骤之前生成。
89
+ 4. **失败在所属层修复** —— 单页问题改页面;大纲问题改大纲;引擎问题改引擎。
90
+ 不要用"换一页"来掩盖引擎缺陷。
91
+ 5. **不静默降级** —— 配图生成失败、模板版式缺失、字号缩到下限,都必须显式报告,
92
+ 不要悄悄跳过。
93
+ 6. **改版面前先落锚点** —— 调整排版参数前把生成的 pptx 复制一份到
94
+ `out/_backup/`,改完对比不过就回滚。
95
+
96
+ ## 质量闭闸
97
+
98
+ 任何路线在交付前,必须跑完下面三条并**把原始输出贴给用户**:
99
+
100
+ ```bash
101
+ "$ENGINE_HOME/pptxgen.sh" check "out/xxx.pptx" --render
102
+ "$ENGINE_HOME/pptxgen.sh" review "decks/xxx.yaml" --pptx "out/xxx.pptx"
103
+ "$ENGINE_HOME/pptxgen.sh" preview "out/xxx.pptx" -o out/preview
104
+ ```
105
+
106
+ `check --render` 用真实渲染结果检查文字重叠与越界,并做 OOXML Schema 严格校验
107
+ (PowerPoint 同源规则)——这是**可证伪的证据**,不是自我评估。它报 0 问题才算闭闸。
108
+
109
+ `review` 是设计审查:版式多样性(相邻页不得同型)、信息密度下限、视觉锚点、
110
+ 润色落实(关键词高亮 / 图注 / 长列表分块),规则见
111
+ [`references/design-review.md`](references/design-review.md)。
112
+
113
+ 两者都通过后才交付;报问题则按「读全量 → 一次合并修复 → 单次复跑」处理,不要逐个试。
114
+
115
+ ## 已知短板
116
+
117
+ 我们**没有**吸收 ppt-master 的全部能力。能力对照、差距与取舍理由见
118
+ [`references/roadmap.md`](references/roadmap.md) —— 用户问"为什么不如某某工具"时读它,
119
+ 不要含糊其辞。
120
+
121
+ 最要紧的三条:
122
+ - 没有 SVG 自由版面层 → 版面受模板约束,不能任意设计
123
+ - 没有图片搜索与许可合规 → 只有 AI 生成这一条配图来源
124
+ - 没有原生图表/表格/公式 → 表格只能用文字或图片表达
125
+
126
+ ## 输出约定
127
+
128
+ - 生成的课件放在 `out/<课件名>.pptx`
129
+ - 预览图放在 `out/preview/`,总览图是 `out/preview/_总览.png`
130
+ - 交付时**必须**告诉用户:页数、`check --render` 结果、预览图路径
@@ -0,0 +1,121 @@
1
+ # 内容润色(讲义式润色)
2
+
3
+ **权威来源**:从一份优秀的同题成品(同一模板、同一教案)逆向总结出来的写法。
4
+ 模板骨架一律不动——只把**教案文字**加工成**能直接投屏讲课的讲义文字**。
5
+
6
+ > 核心判据:把教案原句直接贴上去 = 不及格。每一页都要看得见下面六件事里的至少三件。
7
+
8
+ ---
9
+
10
+ ## 一、六个动作
11
+
12
+ ### 1. 标题二次创作(不照抄教案标题)
13
+
14
+ 教案标题是给老师看的,页面标题是给学生看的。改成**短、具体、有钩子**的说法。
15
+
16
+ | 教案原句 | 润色后 |
17
+ |---|---|
18
+ | 雷达是怎么工作的? | 雷达工作的三步 |
19
+ | 怎么看雷达图? | 看懂雷达回波图 |
20
+ | 多普勒雷达 | 多普勒雷达:还能测出快慢 |
21
+ | 揭晓答案 | 答案揭晓 |
22
+ | 它们像哪种天气探测设备? | 猜一猜,他们像哪种设备? |
23
+ | 手摇式仪器:看清台风的路径 | 动手做:手摇式台风路径仪 |
24
+ | 卫星云图告诉我们什么? | 卫星云图能告诉我们什么 |
25
+
26
+ ### 2. 每页一句导语(`subtitle`)
27
+
28
+ 紧跟标题、15pt 左右、灰调颜色。**一句话说清这一页要干什么**,常用"公式/类比/悬念"。
29
+
30
+ - 发射 → 散射 → 接收,一个来回就完成了一次探测
31
+ - 看颜色,就能大致读出降水的强弱
32
+ - 看形态、看结构、看亮度、看纹理——云图上藏着一整天的天气
33
+ - 跟着两位「神仙」,一起去认识气象卫星和气象雷达
34
+ - 跟着说明书动手做一台仪器,看看卫星监测到的台风会怎么走。
35
+
36
+ ### 3. 分块小标题(把长列表拆成 2–3 块)
37
+
38
+ 一段 5 条以上的要点,学生记不住。拆成带小标题的块,每块 2–4 条:
39
+
40
+ - 一句话说清楚 / 它是怎么做到的
41
+ - 它们在天上做什么
42
+ - 科学家这样看云图
43
+ - 怎么测出速度 / 怎么测出距离 / 本领更强
44
+
45
+ 引擎里用 `pattern: cards`(每张卡=一个块,卡标题=小标题)或 `pattern: steps` 实现。
46
+
47
+ ### 4. 关键词内嵌着色(**最重要的一条**)
48
+
49
+ 整句保持正文色,**只把术语染色加粗**。这是"讲义感"的主要来源。
50
+
51
+ ```yaml
52
+ - text: 雷达是**无线电探测**,即用无线电的方法发现目标,并测定它们在空间的位置。
53
+ - text: 当电磁波探测到物体时,电磁波会**散射回到雷达接收器**处。
54
+ - text: 于是在雷达荧光屏上,回波的亮度就不一样——**越亮,说明水滴直径越大**。
55
+ - text: 按**形态、结构**识别云的种、属
56
+ ```
57
+
58
+ `**…**` 由引擎渲染成"模板标题色 + 加粗",其余文字保持正文色。
59
+
60
+ ### 5. 口语化 + 类比 + 记忆钩子
61
+
62
+ - 就像站得越高、看得越远
63
+ - 隔着高山大海,也能听见千里之外的声音
64
+ - 回来的时间越久,说明目标离我们越远
65
+ - 它「看」得又远又广,一次能看遍大半个地球
66
+ - 记住两个关键词:发射、回波
67
+ - 颜色由冷到暖,降水由弱到强
68
+
69
+ 术语用 `「」` 括起(「神仙」「风云」「多普勒效应」),与强调色区分开。
70
+
71
+ ### 6. 结论块 / 提示块 / 图注 / 手册指引
72
+
73
+ | 元素 | 写法 | 引擎实现 |
74
+ |---|---|---|
75
+ | 结论色块 | 深色底 + 白色粗体一句话 + 一行小字解释 | `pattern: callout` |
76
+ | 提示条 | "小提示:…" / "! 发射和回波之间的时间差,就是雷达测距离的秘密" | callout 的第二段或 bullets |
77
+ | 图注 | 图下方一句"这张图在说什么" | `image_caption` |
78
+ | 手册指引 | "请在手册第 15 页,把答案写下来" | bullets 里的一条 |
79
+ | 视频卡 | "▶ 播放动画片段" + 片名 + 时长 | bullets 里的一条 |
80
+ | 呼应页 | 结束页写"回顾手册第 15、16 页""别忘了整理实验台" | end 页 `subtitle` / items |
81
+
82
+ ---
83
+
84
+ ## 二、字号层级(相对值,最终由模板决定)
85
+
86
+ | 用途 | 参考字号 | 颜色 |
87
+ |---|---|---|
88
+ | 封面/结束页大标题 | 51–61pt | 模板标题色 |
89
+ | 页面标题 | 24pt | 模板标题色(深) |
90
+ | 卡片小标题 | 15–18pt | 模板强调色(亮) |
91
+ | 导语 / 金句 | 15pt | 中间调 |
92
+ | 正文 | 13.5–14.25pt | 近黑正文色 |
93
+ | 图注 / 次要说明 | 11.25–12.75pt | 灰调 |
94
+ | 页脚页码 | 11.25pt | 反白或灰调 |
95
+
96
+ **颜色只有一个来源:模板。** 同一个模板里所有颜色都是模板主色的深浅阶梯
97
+ (深色标题 / 亮色强调 / 近黑正文 / 灰调注释),不要引入模板之外的颜色
98
+ (内容本身有语义色的除外,例如雷达回波图的绿-黄-红)。
99
+
100
+ ---
101
+
102
+ ## 三、页面级骨架
103
+
104
+ 1. **封面**:课题 + 板块 + 第几课 + 授课年级 + 系列名
105
+ 2. **本课导航页**(教案里的"知识/能力/素养目标"→ 01/02/03 三张卡)
106
+ 3. 正文页:`module` 模块标签 + 标题条 + 导语 + 分块内容 + 配图
107
+ 4. **结束页**:一句话课程口号("千里眼看天,顺风耳听雨")+ 回顾清单 + 系列名
108
+
109
+ ---
110
+
111
+ ## 四、交付前自检清单
112
+
113
+ - [ ] 有没有哪一页的标题是**直接抄教案**的?→ 改写
114
+ - [ ] 每页有没有**导语**?
115
+ - [ ] 超过 5 条要点的页面,**分块**了吗?
116
+ - [ ] 每页至少有 **1–2 处 `**关键词**`** 强调?
117
+ - [ ] 有没有至少一处**类比或记忆钩子**?
118
+ - [ ] 结论/难点页有没有**色块**突出?
119
+ - [ ] 有图的页有没有**图注**(图在说什么)?
120
+ - [ ] 教案提到手册页码的地方,页面上有没有**指引**?
121
+ - [ ] 颜色是否**全部来自模板**(有无野色)?
@@ -0,0 +1,155 @@
1
+ # 参考:课件规格(YAML)字段全表
2
+
3
+ 规格文件是**唯一的课件内容来源**。工具只负责排版,不生成内容。
4
+
5
+ ## 顶层
6
+
7
+ ```yaml
8
+ deck: # 或 meta:
9
+ title: 小小气象预报员
10
+ subtitle: 气象生活板块 · 气象与生活
11
+ subject: 科学
12
+ grade: 三、四年级
13
+ teacher: 王老师
14
+ school: 阳光小学
15
+ date: 2025年 春
16
+
17
+ template: ../templates/第1课 神奇的天气魔法.pptx # 相对本文件所在目录
18
+
19
+ options:
20
+ answer_mode: end # end | inline | none
21
+ page_numbers: true
22
+ page_number_start: 1
23
+ chrome: auto # auto | off,模板装饰层
24
+ style_profile: auto # auto | off,从样张学到的页面骨架
25
+ fit_shrink_floor: 0.62 # 文字放不下时最多缩到原字号的百分比
26
+ auto_toc: false
27
+ images: # 配图设置,见 references/image-backends.md
28
+ backend: openai-compatible
29
+ cache_dir: ../out/images
30
+ size: "1024x1024"
31
+ style: 儿童科普插画风格,扁平化矢量插画,…
32
+
33
+ layout_map: # 覆盖页型→版式映射(见 template-fidelity.md)
34
+ cover: "封面"
35
+ content: 13
36
+
37
+ slides: # 页面列表
38
+ - type: cover
39
+ ...
40
+ ```
41
+
42
+ ## 页型与字段
43
+
44
+ | type | 中文别名 | 字段 |
45
+ |---|---|---|
46
+ | `cover` | 封面 | `title` `subtitle` `teacher` `school` `date` |
47
+ | `toc` | 目录 | `title` `items[]` |
48
+ | `section` | 章节/过渡 | `title` `subtitle` `items[]`(`index` 自动编号) |
49
+ | `objectives` | 目标/重难点 | `title` `goals[]` `key_points[]` `hard_points[]` |
50
+ | `content` | 知识点/正文 | `title` `subtitle` `bullets[]` `image` `images[]` `image_side` `image_caption` |
51
+ | `example` | 例题 | `title` `bullets[]`(题干) `steps[]`(步骤,自动编号) |
52
+ | `practice` | 练习/检测 | `title` `bullets[]`(说明) `questions[{q,options[],answer,analysis}]` |
53
+ | `summary` | 小结/思维导图 | `title` `mindmap{center,branches[]}` 或 `points[]` |
54
+ | `homework` | 作业 | `title` `items[]` `note` |
55
+ | `end` | 结束页 | `title` `subtitle` |
56
+
57
+ **所有页型都支持**:`module`(左上角模块标签文字)、`note`(讲稿备注)、
58
+ `image_prompt` + `image_side`(配图)、`layout`(手工指定版式)、`chrome`(false 关闭该页装饰层)。
59
+
60
+ ### 版面骨架 `pattern`(content / example 页)
61
+
62
+ 同一份模板里,正文区可以有多种排法,避免每页都是"左文右图"一个样子。
63
+ **模板骨架不变**:背景、装饰带(如左侧色带/渐变)、模块标签、标题条、主色与字体
64
+ 全部沿用模板——**换成紫色模板就是紫色,换成深色模板就自动用浅色文字**。
65
+ pattern 只重排正文区,选色一律从模板主题色/样式档案里取,不写死颜色。
66
+ 不写 `pattern` 时按内容自动判断(步骤型文字自动用 `steps`)。
67
+
68
+ | pattern | 适用内容 | 用到的字段 |
69
+ |---|---|---|
70
+ | `bullets` | 一般要点(默认,左文右图) | `bullets[]` |
71
+ | `steps` | 有先后顺序的步骤(第1步/第2步…),卡片 + 向下箭头 | `steps[]` 或 `bullets[]` |
72
+ | `flow` | 2–5 个环节的流程,横向方框 + 箭头 | `steps[]` 或 `bullets[]` |
73
+ | `compare` | A 与 B 的对照,左右两栏 + 中间圆标 | `compare{left,right,vs}` |
74
+ | `cards` | 并列要点,卡片网格(2–6 张) | `cards[]` 或 `bullets[]`(一级=标题,二级=内容) |
75
+ | `timeline` | 先后/因果,横向时间轴(上下交错) | `timeline[{time,text}]` |
76
+ | `callout` | 一个核心问题/结论 + 若干要点 | `key`(默认取第一条要点)或 `bullets[]` |
77
+ | `image_left` / `image_right` | 只是把配图换到左边/右边 | `image_side` |
78
+
79
+ ```yaml
80
+ - type: content
81
+ module: 科学概念
82
+ title: 雷达是怎么工作的?
83
+ pattern: flow
84
+ bullets: # 一级=一个环节,二级=补充说明
85
+ - text: 第一步:发射
86
+ level: 0
87
+ - text: 天线向空中发射电磁波
88
+ level: 1
89
+ # …
90
+
91
+ - type: content
92
+ title: 揭晓答案
93
+ pattern: compare
94
+ compare:
95
+ vs: ↔
96
+ left: { title: 千里眼 → 气象卫星, items: [在太空"看"地球] }
97
+ right: { title: 顺风耳 → 天气雷达, items: [发出无线电波"听"回声] }
98
+
99
+ - type: content
100
+ title: 雷达是跟蝙蝠学的吗?
101
+ pattern: timeline
102
+ timeline:
103
+ - { time: 千万年前, text: 蝙蝠演化出回声定位 }
104
+ - { time: 19 世纪, text: 人类发现电磁波 }
105
+ ```
106
+
107
+ 选型建议:**同一份课件里不要连续三页用同一种 pattern**;递进用 `steps`/`flow`,
108
+ 对照用 `compare`,并列用 `cards`,先后用 `timeline`,其余保持 `bullets`。
109
+ pattern 渲染失败会自动退回 `bullets` 并在生成报告里给出「注意」提示。
110
+
111
+ ### bullets 的写法
112
+
113
+ ```yaml
114
+ bullets:
115
+ - 一级要点 # 简写
116
+ - text: 一级要点
117
+ level: 0
118
+ - text: 二级要点(自动缩进换符号)
119
+ level: 1
120
+ ```
121
+
122
+ ### questions 的写法
123
+
124
+ ```yaml
125
+ questions:
126
+ - q: 下列图标中,表示"晴"的是?
127
+ options: [A. 晴天, B. 多云, C. 雨天] # 或分行写
128
+ answer: A
129
+ analysis: 太阳图标表示晴天,云量很少。
130
+ ```
131
+
132
+ `options` 里带 `A.` / `B、` 前缀时会自动渲染成圆形字母徽标。
133
+ 选项短且放得下时自动横排,否则竖排。
134
+
135
+ ### mindmap 的写法
136
+
137
+ ```yaml
138
+ mindmap:
139
+ center: 小小气象预报员
140
+ branches:
141
+ - title: 获取途径
142
+ items: [手机, 网络, 报纸, 电台电视]
143
+ - title: 五大环节
144
+ items: [气象观测, 数据收集, 综合分析, 预报会商, 预报发布]
145
+ ```
146
+
147
+ 分支数建议 3–6 个,每个分支 items 2–5 个。
148
+
149
+ ## 写内容的原则
150
+
151
+ 1. **一页一个知识点**。要点 3–6 条,每条不超过 ~40 个汉字。
152
+ 2. **标题条要短**。超过约 24 个汉字会被压字号。
153
+ 3. **`note` 写讲稿**,不要写进正文 —— 正文是给学生看的,讲稿是给老师看的。
154
+ 4. **模块名用教案里的原词**,不要自创("科学解释"而不是"知识讲解")。
155
+ 5. **教案里标了页码节奏(P2-3、P4)就照它切**。