@lark-apaas/coding-steering 0.1.17 → 0.1.18-dev.2bb478f

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.
@@ -0,0 +1,180 @@
1
+ ---
2
+ name: trigger-guide
3
+ description: 自动化任务触发器代码开发指南,支持 cron 定时触发器、record_change 数据变更触发器和 webhook 触发器,包含 @Automation/@BindTrigger 装饰器用法、handler 入参解析和 Crontab 表达式规范。Use when 需要:(1) 为已创建的自动化任务/定时任务编写业务 handler,(2) 编写 automation 代码绑定触发器,或其他自动化任务相关开发
4
+ steering: true
5
+ steering-topic: trigger_guide
6
+ match-template-name: nestjs-react-fullstack
7
+ ---
8
+
9
+ ## 自动化任务配置与代码编写指引
10
+
11
+ ### 自动化任务配置
12
+
13
+ 1. 新建自动化任务触发器时无需 enable(激活),将任务创建好然后开发完代码即可。触发器随后交由用户主动操作、要求开始。
14
+
15
+ ### 目录结构
16
+
17
+ ```text
18
+ server
19
+ └── modules
20
+ └── xxx
21
+ ├── xxx.automation.ts
22
+ ├── xxx.module.ts // 必须在 module 中注册自动化任务类,并且在 app.module.ts 中引用并注册该 module,否则代码将不会生效。
23
+ └── 其他文件(如有的话)
24
+ ```
25
+
26
+ 文件命名规则:{模块名}.automation.ts
27
+
28
+ 注意:
29
+
30
+ 1. 每个模块只应该有一个存放自动化任务逻辑的文件,业务逻辑需要聚合到该文件中。
31
+ 2. 如果该模块只有对应的自动化任务,无需编写 Controller
32
+
33
+ ### 触发器类型
34
+
35
+ 触发器类型(`triggerType`)有三种:
36
+
37
+ - `record_change`:记录变更触发器,**有入参**
38
+ - `cron`:定时触发器,**无入参**
39
+ - `webhook`:Webhook 触发器,**有入参**
40
+
41
+ 各触发器 handler 的入参类型定义(`TaskHandlerArgs`、`DataChangeEventInput`、`WebhookEvent`)见 [触发器入参类型与代码示例](references/trigger-lifecycle.md)。
42
+
43
+ ### 指定值限制
44
+
45
+ 1. Webhook 触发器不可以设置指定值,并且告知用户。
46
+
47
+ ### 代码绑定
48
+
49
+ 你需要根据触发器创建后确定的自动化任务名字(应用内唯一),编写并绑定到对应的方法上:`@BindTrigger('<任务名字>')` 中的名字必须与创建触发器时确定的名字逐字相同,不能用 trigger ID 或方法名代替。`@Automation()` 标记的类需注册为对应 `<module>.module.ts` 的 provider,且该 module 必须被 `server/app.module.ts` 直接或传递 import,否则装饰器不会生效。完整代码示例见 [触发器入参类型与代码示例](references/trigger-lifecycle.md)。
50
+
51
+ ### 任务代码实现约束
52
+
53
+ 1. 执行自动化任务时无法获取用户信息。依赖用户信息的场景,实现路径如下:
54
+ - 需要查询数据库中的特定数据,给用户发消息:数据库中需要存储用户 id,使用从数据库中查询到的用户 id 进行后续操作
55
+ - 需要调用飞书能力给用户发消息:飞书能力不应该接受用户信息作为参数,而是应该在飞书能力配置里要求用户自己预先指定
56
+
57
+ 2. 入参解析规范(仅 record_change 和 webhook 触发器):
58
+ - 有入参的触发器方法签名为 `async methodName(event: TaskHandlerArgs)`,`cron` 触发器无入参
59
+ - `content.input` 是 JSON 字符串,先用 `typeof input === 'string'` 检查类型,再用 `JSON.parse()` 解析,需添加 try-catch 错误处理
60
+ - `record_change`:根据操作类型获取数据:INSERT/UPDATE 使用 `after` 字段,DELETE 使用 `before` 字段
61
+ - `webhook`:从 `method`、`path`、`query`、`headers`、`body` 中按需取用;`body` 本身也是 JSON 字符串,需要时再次 `JSON.parse()` 解析;`query` 和 `headers` 的值均为 `string[]`
62
+
63
+ ### 技术实现路径参考
64
+
65
+ 以下常见需求的推荐实现路径,帮助你在平台能力限制下找到合理的技术方案;完整代码见 [触发器入参类型与代码示例](references/trigger-lifecycle.md)。
66
+
67
+ - **场景一:管理页面控制定时任务启停** —— 平台侧不支持通过 API 动态启停触发器;定时触发器始终保持开启,在任务执行时查询数据库中的开关状态决定是否执行。
68
+ - **场景二:定时任务通知特定用户** —— 任务执行时无法获取用户上下文;在数据库预存目标用户 ID,执行时查询再调用飞书插件发送。
69
+ - **场景三:记录变更触发器防抖/去重** —— 利用数据库记录最近一次处理时间戳,对比 event 时间戳进行去重。
70
+ - **场景四:自定义定时任务触发时间** —— cron 创建后不可动态改;平台设固定高频定时器(如每 30 分钟),执行时读数据库配置判断是否命中。
71
+
72
+ ## Crontab 表达式规范
73
+
74
+ ### 基本结构
75
+
76
+ Crontab 表达式由 5 个字段组成:`<minute> <hour> <day> <month> <week>`
77
+
78
+ ### 字段说明
79
+
80
+ 1. **minute(分钟)**:0-59 的整数
81
+ 2. **hour(小时)**:0-23 的整数
82
+ 3. **day(日期)**:1-31 的整数,或大写字母 `L` 表示月份的最后一天
83
+ 4. **month(月份)**:1-12 的整数
84
+ 5. **week(星期)**:0-6 的整数,其中 0 表示星期天
85
+
86
+ ### 特殊字符
87
+
88
+ - **星号 `*`**:表示所有可能的值(每)
89
+ - 例:`* * * * *` 表示每分钟
90
+ - **逗号 `,`**:表示列表范围
91
+ - 例:`1,2,3 * * * *` 表示每小时的第 1、2、3 分钟
92
+ - **中杠 `-`**:表示数值范围
93
+ - 例:`1-10 * * * *` 表示每小时的第 1 到 10 分钟
94
+ - **正斜线 `/`**:表示间隔频率
95
+ - 例:`0 10-18/2 * * *` 表示每天 10 点到 18 点,每隔 2 小时执行
96
+
97
+ ## 输出要求
98
+
99
+ 1. 必须以 JSON 格式输出
100
+ 2. JSON 包含两个字段:
101
+ - `expression`:Crontab 表达式字符串
102
+ - `explanation`:中文说明,简要描述执行时间
103
+ 3. 如果用户描述不清晰,请询问具体细节
104
+
105
+ ## 示例
106
+
107
+ **用户输入**:每天早上 8 点执行
108
+
109
+ **输出**:
110
+
111
+ ```json
112
+ {
113
+ "expression": "0 8 * * *",
114
+ "explanation": "每天早上 8:00 执行"
115
+ }
116
+ ```
117
+
118
+ **用户输入**:每周一到周五的上午 9 点和下午 6 点执行
119
+
120
+ **输出**:
121
+
122
+ ```json
123
+ {
124
+ "expression": "0 9,18 * * 1-5",
125
+ "explanation": "每周一至周五的 9:00 和 18:00 执行"
126
+ }
127
+ ```
128
+
129
+ **用户输入**:每隔 30 分钟执行一次
130
+
131
+ **输出**:
132
+
133
+ ```json
134
+ {
135
+ "expression": "*/30 * * * *",
136
+ "explanation": "每隔 30 分钟执行一次"
137
+ }
138
+ ```
139
+
140
+ **用户输入**:每月最后一天的晚上 11 点执行
141
+
142
+ **输出**:
143
+
144
+ ```json
145
+ {
146
+ "expression": "0 23 L * *",
147
+ "explanation": "每月最后一天的 23:00 执行"
148
+ }
149
+ ```
150
+
151
+ **用户输入**:每个工作日的每小时第 15 和 45 分钟执行
152
+
153
+ **输出**:
154
+
155
+ ```json
156
+ {
157
+ "expression": "15,45 * * * 1-5",
158
+ "explanation": "每周一至周五,每小时的第 15 和 45 分钟执行"
159
+ }
160
+ ```
161
+
162
+ **用户输入**:每天上午 10 点到下午 6 点,每隔 2 小时执行
163
+
164
+ **输出**:
165
+
166
+ ```json
167
+ {
168
+ "expression": "0 10-18/2 * * *",
169
+ "explanation": "每天 10:00、12:00、14:00、16:00、18:00 执行"
170
+ }
171
+ ```
172
+
173
+ ## 注意事项
174
+
175
+ - 星期字段:0 和 7 都可以表示星期天(但本规范使用 0)
176
+ - 时间采用 24 小时制
177
+ - 月份和星期都从较小的数字开始计数
178
+ - 确保生成的表达式符合实际日历逻辑
179
+ - 由于技术限制,最小间隔为 30 分钟,如用户要求有误请直接拒绝用户并给出原因
180
+ - 输出必须是有效的 JSON 格式
@@ -1,36 +1,8 @@
1
- ---
2
- name: trigger-guide
3
- description: 自动化任务触发器配置与代码开发指南,支持 cron 定时触发器、record_change 数据变更触发器和 webhook 触发器,包含 @Automation/@BindTrigger 装饰器用法和 Crontab 表达式规范。Use when 需要:(1) 创建或配置自动化任务/定时任务,(2) 编写 automation 代码绑定触发器,或其他自动化任务相关开发
4
- steering: true
5
- steering-topic: trigger_guide
6
- match-template-name: nestjs-react-fullstack
7
- ---
1
+ # 触发器入参类型与代码示例
8
2
 
9
- ## 自动化任务配置与代码编写指引
3
+ reference 承载 nestjs-react-fullstack 触发器 handler 的入参类型定义、完整代码示例与常见实现场景。先读主 [trigger-guide](../SKILL.md) 了解目录结构、绑定约束与配置要求。
10
4
 
11
- ### 自动化任务配置
12
-
13
- 1. 新建自动化任务触发器时无需 enable(激活),将任务创建好然后开发完代码即可。触发器随后交由用户主动操作、要求开始。
14
-
15
- ### 目录结构
16
-
17
- ```text
18
- server
19
- └── modules
20
- └── xxx
21
- ├── xxx.automation.ts
22
- ├── xxx.module.ts // 必须在 module 中注册自动化任务类,并且在 app.module.ts 中引用并注册该 module,否则代码将不会生效。
23
- └── 其他文件(如有的话)
24
- ```
25
-
26
- 文件命名规则:{模块名}.automation.ts
27
-
28
- 注意:
29
-
30
- 1. 每个模块只应该有一个存放自动化任务逻辑的文件,业务逻辑需要聚合到该文件中。
31
- 2. 如果该模块只有对应的自动化任务,无需编写 Controller
32
-
33
- ### 触发器类型与入参
5
+ ## 触发器类型与入参
34
6
 
35
7
  触发器类型(`triggerType`)有三种:
36
8
 
@@ -85,12 +57,11 @@ interface WebhookEvent {
85
57
  }
86
58
  ```
87
59
 
88
- ### 指定值限制
89
- 1. Webhook 触发器不可以设置指定值,并且告知用户。
60
+ `DataChangeEventInput.type` 只定义 `INSERT`、`UPDATE`、`DELETE`,不包含 `UPSERT`。
90
61
 
91
- ### 代码示例
62
+ ## 代码示例
92
63
 
93
- 你需要根据 `automation_trigger_manager` 工具返回的自动化任务名字,编写并绑定到对应的方法上。具体代码示例如下:
64
+ 根据触发器创建后确定的任务名字(应用内唯一),编写并绑定到对应的方法上。使用模板已有的 `@lark-apaas/fullstack-nestjs-core` 聚合入口导入 `Automation` / `BindTrigger`,不要求项目再感知底层 trigger 包。具体代码示例如下:
94
65
 
95
66
  ```typescript
96
67
  // 文件名:demo.automation.ts
@@ -184,23 +155,11 @@ export class DemoAutomationTasksService {
184
155
  }
185
156
  ```
186
157
 
187
- ### 任务代码实现约束
188
-
189
- 1. 执行自动化任务时无法获取用户信息。依赖用户信息的场景,实现路径如下:
190
- - 需要查询数据库中的特定数据,给用户发消息:数据库中需要存储用户 id,使用从数据库中查询到的用户 id 进行后续操作
191
- - 需要调用飞书能力给用户发消息:飞书能力不应该接受用户信息作为参数,而是应该在飞书能力配置里要求用户自己预先指定
192
-
193
- 2. 入参解析规范(仅 record_change 和 webhook 触发器):
194
- - 有入参的触发器方法签名为 `async methodName(event: TaskHandlerArgs)`,`cron` 触发器无入参
195
- - `content.input` 是 JSON 字符串,先用 `typeof input === 'string'` 检查类型,再用 `JSON.parse()` 解析,需添加 try-catch 错误处理
196
- - `record_change`:根据操作类型获取数据:INSERT/UPDATE 使用 `after` 字段,DELETE 使用 `before` 字段
197
- - `webhook`:从 `method`、`path`、`query`、`headers`、`body` 中按需取用;`body` 本身也是 JSON 字符串,需要时再次 `JSON.parse()` 解析;`query` 和 `headers` 的值均为 `string[]`
198
-
199
- ### 技术实现路径参考
158
+ ## 技术实现路径参考
200
159
 
201
160
  以下是一些常见需求的推荐实现路径,帮助你在平台能力限制下找到合理的技术方案。
202
161
 
203
- #### 场景一:用户需要管理页面控制定时任务的启停
162
+ ### 场景一:用户需要管理页面控制定时任务的启停
204
163
 
205
164
  平台侧不支持通过 API 动态启停触发器。推荐方案:**平台定时触发器始终保持开启,在任务执行时查询数据库中的开关状态,决定是否真正执行业务逻辑。**
206
165
 
@@ -233,7 +192,7 @@ export class ReportAutomationService {
233
192
  }
234
193
  ```
235
194
 
236
- #### 场景二:定时任务需要将结果通知给特定用户
195
+ ### 场景二:定时任务需要将结果通知给特定用户
237
196
 
238
197
  自动化任务执行时无法获取当前用户上下文。推荐方案:**在数据库中预存需要通知的用户 ID,任务执行时从数据库查询目标用户,再调用飞书插件发送通知。**
239
198
 
@@ -267,7 +226,7 @@ export class NotifyAutomationService {
267
226
  }
268
227
  ```
269
228
 
270
- #### 场景三:记录变更触发器需要做防抖/去重
229
+ ### 场景三:记录变更触发器需要做防抖/去重
271
230
 
272
231
  高频数据变更场景下,同一条记录可能短时间内触发多次。推荐方案:**利用数据库记录最近一次处理时间戳,对比 event 时间戳进行去重。**
273
232
 
@@ -294,7 +253,7 @@ async handleOrderChange(event: TaskHandlerArgs) {
294
253
  }
295
254
  ```
296
255
 
297
- #### 场景四:用户需要自定义定时任务的触发时间
256
+ ### 场景四:用户需要自定义定时任务的触发时间
298
257
 
299
258
  平台侧的 cron 表达式在触发器创建后无法由用户动态修改。推荐方案:**平台设置一个固定的高频定时器(如每 30 分钟执行一次),在任务执行时从数据库读取用户配置的触发时间,判断当前是否命中再决定是否执行。**
300
259
 
@@ -340,113 +299,3 @@ export class ScheduleAutomationService {
340
299
  ```
341
300
 
342
301
  > 注意:由于平台最小调度间隔为 30 分钟,用户可配置的时间精度也应限制为 30 分钟的整数倍(如 `09:00`、`09:30`),前端做好校验提示。
343
-
344
- ## Crontab 表达式规范
345
-
346
- ### 基本结构
347
-
348
- Crontab 表达式由 5 个字段组成:`<minute> <hour> <day> <month> <week>`
349
-
350
- ### 字段说明
351
-
352
- 1. **minute(分钟)**:0-59 的整数
353
- 2. **hour(小时)**:0-23 的整数
354
- 3. **day(日期)**:1-31 的整数,或大写字母 `L` 表示月份的最后一天
355
- 4. **month(月份)**:1-12 的整数
356
- 5. **week(星期)**:0-6 的整数,其中 0 表示星期天
357
-
358
- ### 特殊字符
359
-
360
- - **星号 `*`**:表示所有可能的值(每)
361
- - 例:`* * * * *` 表示每分钟
362
- - **逗号 `,`**:表示列表范围
363
- - 例:`1,2,3 * * * *` 表示每小时的第 1、2、3 分钟
364
- - **中杠 `-`**:表示数值范围
365
- - 例:`1-10 * * * *` 表示每小时的第 1 到 10 分钟
366
- - **正斜线 `/`**:表示间隔频率
367
- - 例:`0 10-18/2 * * *` 表示每天 10 点到 18 点,每隔 2 小时执行
368
-
369
- ## 输出要求
370
-
371
- 1. 必须以 JSON 格式输出
372
- 2. JSON 包含两个字段:
373
- - `expression`:Crontab 表达式字符串
374
- - `explanation`:中文说明,简要描述执行时间
375
- 3. 如果用户描述不清晰,请询问具体细节
376
-
377
- ## 示例
378
-
379
- **用户输入**:每天早上 8 点执行
380
-
381
- **输出**:
382
-
383
- ```json
384
- {
385
- "expression": "0 8 * * *",
386
- "explanation": "每天早上 8:00 执行"
387
- }
388
- ```
389
-
390
- **用户输入**:每周一到周五的上午 9 点和下午 6 点执行
391
-
392
- **输出**:
393
-
394
- ```json
395
- {
396
- "expression": "0 9,18 * * 1-5",
397
- "explanation": "每周一至周五的 9:00 和 18:00 执行"
398
- }
399
- ```
400
-
401
- **用户输入**:每隔 30 分钟执行一次
402
-
403
- **输出**:
404
-
405
- ```json
406
- {
407
- "expression": "*/30 * * * *",
408
- "explanation": "每隔 30 分钟执行一次"
409
- }
410
- ```
411
-
412
- **用户输入**:每月最后一天的晚上 11 点执行
413
-
414
- **输出**:
415
-
416
- ```json
417
- {
418
- "expression": "0 23 L * *",
419
- "explanation": "每月最后一天的 23:00 执行"
420
- }
421
- ```
422
-
423
- **用户输入**:每个工作日的每小时第 15 和 45 分钟执行
424
-
425
- **输出**:
426
-
427
- ```json
428
- {
429
- "expression": "15,45 * * * 1-5",
430
- "explanation": "每周一至周五,每小时的第 15 和 45 分钟执行"
431
- }
432
- ```
433
-
434
- **用户输入**:每天上午 10 点到下午 6 点,每隔 2 小时执行
435
-
436
- **输出**:
437
-
438
- ```json
439
- {
440
- "expression": "0 10-18/2 * * *",
441
- "explanation": "每天 10:00、12:00、14:00、16:00、18:00 执行"
442
- }
443
- ```
444
-
445
- ## 注意事项
446
-
447
- - 星期字段:0 和 7 都可以表示星期天(但本规范使用 0)
448
- - 时间采用 24 小时制
449
- - 月份和星期都从较小的数字开始计数
450
- - 确保生成的表达式符合实际日历逻辑
451
- - 由于技术限制,最小间隔为 30 分钟,如用户要求有误请直接拒绝用户并给出原因
452
- - 输出必须是有效的 JSON 格式
@@ -1,125 +0,0 @@
1
- ---
2
- name: make-a-deck
3
- description: 制作演示文稿 / PPT / pitch deck / slides / keynote。从零新建、从文档材料提炼重组、或对已有 PPTX 重新设计。触发词:presentation, slides, deck, PPT, PPTX, keynote, pitch, 演示文稿, 幻灯片, 路演
4
- available-agents:
5
- - CreativeDesign
6
- ---
7
-
8
- # 制作演示文稿(deck)
9
-
10
- 把演示文稿做成单个自包含的 HTML 页面。HTML 是输出载体,但设计判断按 PPT 来做:固定画幅、强叙事、可投影 / 可异步阅读、每页只承担一个清楚的沟通任务。
11
-
12
- 把自己当成演示文稿设计师,不是网页开发者:像顾问、分析师、高管准备董事会材料那样思考——清晰、叙事流、后排可读。每一页都同时是版式设计和文案写作。开始落 HTML 前,先写大纲;好的大纲本身就是一次叙事结构训练。
13
-
14
- ## 信息密度
15
-
16
- 选一种密度,不要停在模糊的中间态:
17
-
18
- | 模式 | 场景 | 做法 |
19
- | --- | --- | --- |
20
- | 演讲型 / 低密度 | 发布会、公开演讲、keynote、现场 pitch | 一页一个观点,大标题强视觉、留白足、要点 1-3 条,必要时增加页数 |
21
- | 阅读型 / 高密度 | 内部汇报、评审、复盘、异步传阅 | 每页更自洽,可用表格 / 结构卡片 / 注释 / 图表,但层级要明确 |
22
-
23
- 无论哪种,都不要让页面滚动、溢出、重叠或用过小字体;放不下就拆页。**不要把整份文档直接贴进幻灯片**——这是最常见的失败模式。落 plan 时就想清楚:哪些内容更适合做成表格、图表、流程、引用页或图片页。
24
-
25
- ## 叙事与标题
26
-
27
- - 先写完整标题序列放进 `scratchpad.md`。只读标题就应能看懂整份 deck 的逻辑(像书的目录)。检查是否形成清楚路径:背景 → 问题 → 洞察 → 方案 → 证据 → 下一步。
28
- - 选定一种标题语法并全程一致:要么名词短语("市场机会""产品架构"),要么简短判断句("新用户增长主要来自自然流量")。
29
- - 每页正文只服务本页标题,不塞旁支。封面、章节页、转场页、结尾页也要服务故事,不做纯装饰。
30
-
31
- 避免这些"AI 味"标题(它们会暴露 deck 是 AI 生成的)——标题的任务是**定位页面、推进叙事**,不是替演讲者甩结论 / 喊 punchline:
32
-
33
- - "不是 X,而是 Y"式过度反转。
34
- - "关键时刻""魔法时刻"式空泛或故作深刻。
35
- - 过度夸张的行动号召、为制造张力而制造张力。
36
- - 每页固定一个 takeaway 盒子,导致标题和正文重复。
37
-
38
- ## 页面类型与节奏
39
-
40
- 写第一页前先做 page-type map:为每页标注页型、叙事作用和需要承载的素材。避免所有页面都变成"标题 + 三栏卡片"。
41
-
42
- 常用页型包括:
43
-
44
- - 封面 / 目录 / 使用说明:建立主题、范围、读法和关键承诺。
45
- - 章节分隔页:承载章节编号、主题、过渡判断或下一段问题,不做纯装饰页。
46
- - 观点 / 结论页:一句话 thesis + 1-3 个证据或影响。
47
- - 数据 / 指标页:KPI strip、图表或表格、口径 / 来源、短结论必须在同屏闭环。
48
- - 对比 / 选项页:A/B/多方案使用稳定代号、颜色和评价维度,贯穿方案封面、详情、排期和最终建议。
49
- - 流程 / 日程 / 预算 / 清单页:优先用表格、矩阵、时间轴、泳道或 checklist,不要把结构化信息改写成散文卡片。
50
- - 教学 / 练习页:保持稳定 scaffold,例如"编号 / 题型 / 进度 + 题干 + 条件 / 选项 + 答案 / 考点 / 易错点"。
51
- - 叙事 / 案例页:可以使用 prologue、chapter、turning point、proof、epilogue 等章节节拍,让情绪和判断同步推进。
52
-
53
- 每 3-5 页安排一次节奏变化:章节页、全屏观点页、数据页、对比页、表格页、案例页之间轮换。同层级的并列内容(成组案例、系列练习、多个方案)保持同一 scaffold,利于跨页比较——此时节奏变化靠在块之间插入章节页或小结页,而不是改动系列内页面的版式。不要把叙事功能不同的页面压平成同一个版式。变化来自页型和信息结构,不靠堆装饰。
54
-
55
- ## 内容组织与版式策略
56
- deck 每页先判断它要让观众完成什么阅读动作:抓结论、看证据、比较差异、理解过程、记住模型、看到风险、做选择,还是进入下一章节。版式、文字、图形和动效都只是表达手段,选择最能讲清这一页的组合。
57
- - 根据当前页内容现场生成结构,不局限于常见页型。可以保留强文字页,也可以把材料转成模型图、关系图、流程、对比矩阵、时间线、象限、分层结构、地图、系统图、路径图、图表注释、截图标注或视觉隐喻;还可以把重要句子放大成观点页。示例只是启发,不是清单。
58
- - 同一章节内可以有连续叙事,但每页的结构不必一样。核心页给足面积和视觉重量,支撑页可以更密集;重要概念可以用图形和标注解释,也可以用排版、引用、数字、对照文本或图文组合解释。
59
- - 单页可以承载“小型演示过程”:先出现结论,再让支撑内容逐步显现,最后高亮关键判断。支撑内容可以是文字、数字、图形、数据、截图或关系结构;是否使用动效由表达目的决定。
60
- - 如果一页只有少量概念,不要机械铺成几张卡片。先判断概念之间有没有关系:并列、递进、因果、闭环、漏斗、分层、坐标、路径、前后对比或组合模型。关系明确时可以画出来;关系不明确时,可以改成更有力量的文字观点页、引用页,或合并到相邻页。
61
- - 版式变化来自内容关系,不来自凑组件。不要为了“丰富”而加无依据内容、无意义图标或装饰图形;图形、图片和动效只有在能帮助观众理解时才使用。
62
-
63
- ## 版式系统(写第一页前先定)
64
-
65
- - 用 CSS 变量定义字号 / 间距,放在 `<head>` 的 `<style>` 里,**先于任何 slide**。这让整份 deck 改一个数(直接改变量,或用 Tweaks 滑块绑同一变量)就能统一缩放,slide 本体保持无脚本的静态 HTML。1920×1080 起点:
66
- ```css
67
- :root {
68
- --type-title: 64px; --type-subtitle: 44px; --type-body: 34px; --type-small: 28px;
69
- --pad-top: 100px; --pad-bottom: 80px; --pad-x: 100px;
70
- --gap-title: 52px; --gap-item: 28px;
71
- }
72
- ```
73
- 1280×720 时整体 ×0.67。每个 font-size 都用 `--type-*`,每个 padding / gap 都用 `--pad-*` / `--gap-*`。`--pad-bottom` 是结构性的底部呼吸空间,不是空白。
74
- - 网页默认(14-16px 正文、48-72px 边距)对投影太小。标题 ≥ 48px,正文 / 注释**不得小于 24px**(验证器会对 < 24px 抛错)。用户说字号一般指 pt,按 PowerPoint / Keynote 换算:`px = pt × 1.333`("标题 36pt" → CSS 设 ~48px)。
75
- - 规划页面类型:封面、章节页、观点页、数据页、对比页、流程页、图片页、结束页各有稳定样式。同类页面的标题位置、页码、章节名、脚注、来源、关键数字、图片位置要**平行对齐**,方便观众跨页比较。
76
- - 为每种页型固定布局锚点:标题、章节号、页码、来源、图表、主视觉和关键数字的位置不要随机漂移。相同页型要像同一个系统,不同页型再负责制造节奏。
77
-
78
- ## 视觉设计
79
-
80
- 视觉服务演示场景,不是网页首页。
81
-
82
- - **明暗按需求选择**:根据品牌 / 主题 / 图片素材 / 演示场景选择浅色、暗色或混合背景。无论选择哪种,都要保证投影、截图和后排阅读的对比度。明暗切换应落在叙事节点上(章节转换、关键强调),不要无来由地跳变。
83
- - 字体克制(1-2 套):展示字体可有个性,正文必须稳定可读;整体对比清楚、信息块边界明确。
84
- - 图片先判断内容和用途:摄影 / 氛围图可满版裁切;截图、图表、产品界面、架构图必须完整展示(aspect-fit),不能裁掉关键边界或文字;透明图 / 细线图放到有对比的底色上。图上压字用品牌常见方式保护可读性(遮罩、渐变、模糊或文字容器)。
85
- - 视觉要有节奏变化:全图页、大数字页、表格页、引用页、流程页、文字页交替出现;不要整套都是同一种卡片。也不要每页硬加描边卡片、固定结论框或装饰分割线——只有内容需要分组时才用容器。
86
- - 扁平基线:好的 deck 不依赖阴影和悬浮卡片堆叠。优先用全页背景、色带、分隔线、编号、表格网格、图片裁切、对比色块和尺度差建立层次。只有在需要表达真实物件、票券、照片或舞台层次时才少量使用阴影。
87
- - 不用 emoji、不临时手绘复杂假图;优先用用户素材、品牌资产、图标库或真实图片。
88
- - **空间重心**:内容集中在上方 2/3、底部留白,通常是**正确**的 slide 构图。看到 `align-items: flex-start` 加底部留空就想改成 `center`——那是网页设计的条件反射,忍住,留白是有意的。但留白有下界:内容页的主内容应占版心高度约 2/3 以上,撑不满就升格版式——放大成大数字、大卡片,或重排为居中的宣言页,而不是把原内容原样居中或任其悬浮在顶部。空本身不是缺陷,不均才是:同一页一处大空、一处拥挤,要重排。双栏页两列视觉重量要对等,悬殊时改 7:5 / 8:4 不对称栅格或并回单栏。
89
-
90
- ## HTML 实现(deck-stage 外壳)
91
-
92
- - **不要手写 stage / 缩放 / 导航 / 页码**。先调用 `copy_starter_component`,`kind: "deck-stage.js"`(连字符、含扩展名,照抄;传裸名字或错扩展名会失败)。用普通 `<script src="deck-stage.js"></script>` 引入(vanilla JS,不是 JSX)。
93
- - 用 `<deck-stage width="1920" height="1080">` 包住所有 slide,每页一个 `<section data-label="…">` 子元素。deck-stage 负责等比缩放、键盘 / 点击 / 翻页导航、页数与导航条、speaker-notes 的 postMessage,以及打印成 PDF(一页一张)。
94
- - deck-stage 会**自动**按位置生成 `data-screen-label` 并注入 `data-miaoda-validate`——你只需给每页写 `data-label`(人类可读的页名),不用手写另两个。
95
- - `data-label` 使用可读页名或章节名,例如"30秒结论""A方案日程""数据·增长产出",不要写成无语义的 `slide-1`。
96
- - **不要**在 slide `<section>` 上自行设置 position / inset / width / height——deck-stage 会绝对定位每个子元素。
97
- - 演讲者备注写在全局 `<script type="application/json" id="speaker-notes">`(置于 `<deck-stage>` 之外),内容是一个**按页顺序排列的扁平字符串数组**——第 N 个元素就是第 N 个 `<section>` 的备注(含封面,从 0 起按位置对齐);某页没有备注也要用空串 `""` 占位,让数组长度始终等于页数。随翻页 postMessage 给宿主。
98
- ```html
99
- <script type="application/json" id="speaker-notes">
100
- ["首页开场白…", "", "第 3 页的讲解要点…"]
101
- </script>
102
- ```
103
-
104
- ### slide 正文写成静态 HTML,不要用脚本生成
105
-
106
- 这条最影响用户体验,理解**为什么**再执行:当一页正文是 `<deck-stage>` 里的普通静态标记时,用户在编辑态点任意标题 / 段落就能直接改字,编辑器会把改动即时 splice 回源文件;一旦这页由 `<script type="text/babel">`、React 组件或"遍历 JS 数组"渲染,这条直改路径就断了——每次微调都要通过 chat message 往返到你这里,更慢、也更难让用户自己打磨 deck。所以凡是静态页能表达的(文字、布局、背景、图片、表格),就把字面元素写进 HTML、用 CSS 定样式;只有当这页**真的**需要静态标记给不了的行为(交互图表、真实 demo、真实状态)才引入 babel / React 或额外 `<script>`。同样的渲染结果,静态版永远优先,因为它可直接编辑。
107
-
108
- 两个细节保住"可直接编辑":
109
-
110
- - 每段可编辑文字放在自己的叶子节点里——把"Revenue"放进 `<h2>` 内它自己的 `<span>`,别写成 `<h2>Revenue <span class="sub">2025</span></h2>` 这种文本和子元素混在同一父节点。
111
- - 重复结构写出来、不要生成——三条 bullet 就写三个 `<li>`,不要用数组渲染一个 `<li>` 三次。重复正是重点:它让用户改第二条时不碰第一条。
112
- - 例外:`tweaks-panel.jsx` 是挨着 slide 的控制面板、不是 slide 正文,可以用 `<script type="text/babel">`;它不影响各静态 slide 各自走直改路径。
113
-
114
- ## 交付前自检
115
-
116
- - 每页 16:9,无溢出 / 重叠 / 裁切;字号符合投影阅读(正文没有小到像网页)。
117
- - 留白均匀:同一页没有一处大空、一处拥挤;内容页主内容占版心高度约 2/3 以上;双栏视觉重量对等。
118
- - 只读标题能讲通故事;标题语法全程一致,没有 punchline / takeaway 盒子。
119
- - 已建立 page-type map,并通过页型轮换避免单一上下布局。
120
- - 章节分隔页推动叙事,而不是只做装饰。
121
- - 演讲型页面没堆太多字;阅读型页面没变成文档截图。
122
- - 同类页面布局、标题位置、页码、章节标识平行一致;图片没被不合理拉伸,截图 / 图表完整展示。
123
- - 多方案、教学练习、叙事案例、数据汇报等特定场景使用了稳定 scaffold。
124
- - slide 正文是静态可编辑 HTML,没用脚本循环生成;`data-label`、speaker-notes、打印分页完整。
125
- - deck-stage 外壳、导航、页码来自 starter component,没有手写重复实现。