mirror of
https://github.com/larksuite/cli.git
synced 2026-08-03 08:32:46 +08:00
Compare commits
1 Commits
feat/plugi
...
feat/lark-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
249ebec08a |
@@ -1,285 +1,64 @@
|
||||
---
|
||||
name: lark-slides
|
||||
version: 1.0.0
|
||||
description: "飞书幻灯片:创建和编辑幻灯片。创建演示文稿、读取幻灯片内容、管理幻灯片页面(创建、删除、读取、局部替换)。当用户需要创建或编辑幻灯片、读取或修改单个页面时使用。当用户给出 doubao.com 的 /slides/ URL/token 时,也应直接使用本 skill,不要因为域名不是飞书而回退到 WebFetch;路由依据是 URL 路径模式和 token,而不是域名。不负责:云文档内容编辑(走 lark-doc)、云文档里的独立画板对象(走 lark-whiteboard,注意 slide 内嵌的流程图/架构图仍属本 skill)、上传或下载普通文件(走 lark-drive)。"
|
||||
version: 2.0.0
|
||||
description: "飞书幻灯片:创建和编辑演示文稿——创建、读取全文/单页、管理页面(创建、删除、局部替换)。当用户需要创建或编辑幻灯片、读取或分析已有 PPT、修改单个页面时使用;给出 /slides/ 或 doubao.com 的 slides URL/token 时按路径与 token 路由,不回退 WebFetch。不负责:云文档内容编辑(lark-doc)、云文档里的独立画板对象(lark-whiteboard,注意 slide 内嵌的流程图/架构图仍属本 skill)、普通文件上传下载(lark-drive)。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["lark-cli"]
|
||||
cliHelp: "lark-cli slides --help"
|
||||
---
|
||||
|
||||
# slides (v1)
|
||||
# slides
|
||||
|
||||
**CRITICAL — 全局硬约束:PPT 的尺寸是 960x540,确保主体内容在页面边界内。**
|
||||
定位:本文件只做**路由 + 硬规则 + workflow 主干**,具体做法下沉到 references,按当前步骤即时读取。
|
||||
|
||||
**CRITICAL — 图片至关重要:必须有意识的主动多用图片!素材图使用生图工具和搜图工具,缺图时用生图工具生成配图补足;背景图必须使用生图工具,且生图指令中必须明确要求不要出现任何文字。**
|
||||
## 硬规则
|
||||
|
||||
**CRITICAL — 防文本溢出:所有承载突出信息和密集文字的 `<content>` 必须设置 `autoFit="normal-auto-fit"`,字号会在框内自动缩排以防溢出。**
|
||||
1. 画布固定 960x540,主体内容必须在边界内。
|
||||
2. `<img src>` 只能用飞书 `file_token`,禁 http(s) 外链;单图 ≤20 MB。
|
||||
3. 承载密集文字的 `<content>` 设 `autoFit="normal-auto-fit"` 防溢出。
|
||||
4. 提交整页 XML 前必须跑 overlap lint(命令与 lint code 见 `troubleshooting.md`),`error_count` 为 0 才能调接口。
|
||||
5. 生成 XML 前以 `references/slides_xml_schema_definition.xml` 为唯一协议真源,不凭记忆猜结构。
|
||||
6. 创建或大改后必须回读 XML 并按 `verify.md` 验证——agent 看不到渲染,此步不可省。
|
||||
7. 渐变必须用 `rgba()` 且带百分比停靠点,否则服务端回退白色。
|
||||
|
||||
## Quick Reference
|
||||
认证、权限、身份以 `../lark-shared/SKILL.md` 为准;slides 操作默认 `--as user`。
|
||||
|
||||
| 用户需求 | 优先动作 | 关键文档 / 命令 |
|
||||
|----------|----------|-----------------|
|
||||
| 新建 PPT | 先规划 `slide_plan.json`,再按复杂度选择一步或两步创建 | `planning-layer.md`、`visual-planning.md`、`asset-planning.md`、`slides +create` |
|
||||
| 从模板创建或编辑已有本地 PPTX | 导入 PPTX 为 Slides | `lark-slides-pptx-template-workflows.md` |
|
||||
| 编辑单个标题、文本块、图片或局部元素 | 优先块级替换/插入,不改页序 | `slides +replace-slide`、`lark-slides-replace-slide.md` |
|
||||
| 读取或分析已有 PPT | 解析 slides/wiki token,用 shortcut 回读全文 XML 或读取单页 XML,保存 `xml_presentation_id`、`slide_id`、`revision_id` | `slides +xml-get`、`xml_presentation.slide.get` |
|
||||
| 获取幻灯片页面截图 | 用 `slide_id` 或页号指定页面,一次不超过 10 页 | `slides +screenshot`、`lark-slides-screenshot.md` |
|
||||
| 上传或使用图片 | 先上传为 `file_token`,禁止直接写 http(s) 外链 | `slides +media-upload`,或 `+create --slides` 的 `@./path` 占位符 |
|
||||
| 绘制图表 | 原生图表用 `<chart>`,其他用 `<shape>` + `<line>`,只有复杂 Mermaid、SVG 用 `<whiteboard>` | `xml-schema-quick-ref.md`、`slides_chart_demo.xml` |
|
||||
| 绘制表格 | 优先用 `rect` 和 `text` 模拟,其他用 `<table>` | `xml-schema-quick-ref.md` |
|
||||
| 使用图标 | 禁止盲猜 `iconType`,必须先检索 IconPark,再写 `<icon iconType="...">`,图标必须填充颜色并和背景有足够对比,禁止使用 emoji 图标 | `iconpark_tool.py search → resolve`、`iconpark.md` |
|
||||
| 创建失败、空白页、3350001、布局异常 | 先回读状态,再按排障清单修复,不假设原操作原子成功 | `troubleshooting.md`、`validation-checklist.md` |
|
||||
## 路由表
|
||||
|
||||
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),认证、权限和全局参数均以 lark-shared 为准。**
|
||||
|
||||
**CRITICAL — 生成任何 XML 之前,MUST 先用 Read 工具读取 [xml-schema-quick-ref.md](references/xml-schema-quick-ref.md),禁止凭记忆猜测 XML 结构。**
|
||||
|
||||
**CRITICAL — 新建演示文稿或大幅改写页面时,MUST 先生成 `.lark-slides/plan/<deck-or-task-id>/slide_plan.json`,再生成 XML。先创建对应目录,规划层规则和中间产物生命周期见 [planning-layer.md](references/planning-layer.md)。仅替换一个标题、插入一个块等小型已有页编辑可豁免。**
|
||||
|
||||
**CRITICAL — 新建演示文稿或大幅改写页面时,生成 XML 前 MUST 读取 [visual-planning.md](references/visual-planning.md),确保 `layout_type`、`visual_focus`、`text_density` 实际改变页面几何、主视觉和文本量。**
|
||||
|
||||
**CRITICAL — 新建演示文稿或大幅改写页面时,规划 `asset_need` MUST 遵循 [asset-planning.md](references/asset-planning.md)。**
|
||||
|
||||
**CRITICAL — 将完整 `<slide>` XML 提交给 `slides +create --slides`、`xml_presentation.slide create` 或 `slides +replace-pages` 之前,MUST 先把待提交 XML 保存到本地文件并运行 [`scripts/xml_text_overlap_lint.py`](scripts/xml_text_overlap_lint.py);`summary.error_count` 必须为 0 才能调用接口。**
|
||||
|
||||
**CRITICAL — 创建或大幅改写后,MUST 按 [validation-checklist.md](references/validation-checklist.md) 做显式验证:回读全文 XML、核对页数和关键元素、检查空白/破损页、明显溢出、布局风险。**
|
||||
|
||||
**CRITICAL — 创建前自检或失败排障时,MUST 按 [troubleshooting.md](references/troubleshooting.md) 检查 XML 转义、结构、shell 截断、图片 token、3350001 和布局风险。**
|
||||
|
||||
**编辑已有幻灯片页面**:单个标题、文本块、图片或局部元素优先用 [`+replace-slide`](references/lark-slides-replace-slide.md)(块级替换/插入,不动页序);已有 Slides 的多页大改优先用 [`+replace-pages`](references/lark-slides-replace-pages.md) 在原 presentation 内批量重建页面,避免 `slides +create` 生成新链接。选择 action 和完整读-改-写流程见 [`lark-slides-edit-workflows.md`](references/lark-slides-edit-workflows.md)。
|
||||
|
||||
## 身份选择
|
||||
|
||||
飞书幻灯片通常是用户自己的内容资源。**默认应优先显式使用 `--as user`(用户身份)执行 slides 相关操作**,始终显式指定身份。
|
||||
|
||||
- **`--as user`(推荐)**:以当前登录用户身份创建、读取、管理演示文稿。执行前先完成用户授权:
|
||||
|
||||
```bash
|
||||
lark-cli auth login --domain slides
|
||||
```
|
||||
|
||||
- **`--as bot`**:仅在用户明确要求以应用身份操作,或需要让 bot 持有/创建资源时使用。使用 bot 身份时,要额外确认 bot 是否真的有目标演示文稿的访问权限。
|
||||
|
||||
**执行规则**:
|
||||
|
||||
1. 创建、读取、增删 slide、按用户给出的链接继续编辑已有 PPT,默认都先用 `--as user`。
|
||||
2. 如果出现权限不足,先检查当前是否误用了 bot 身份;不要默认回退到 bot。
|
||||
3. 只有在用户明确要求"用应用身份 / bot 身份操作",或当前工作流就是 bot 创建资源后再做协作授权时,才切换到 `--as bot`。
|
||||
|
||||
## 执行前必做
|
||||
|
||||
> **重要**:`references/slides_xml_schema_definition.xml` 是此 skill 唯一正确的 XML 协议来源;其他 md 仅是对它和 CLI schema 的摘要。
|
||||
|
||||
高频只读:
|
||||
|
||||
- [xml-schema-quick-ref.md](references/xml-schema-quick-ref.md)
|
||||
- [planning-layer.md](references/planning-layer.md)(新建 / 大幅改写)
|
||||
- [visual-planning.md](references/visual-planning.md)(新建 / 大幅改写)
|
||||
- [asset-planning.md](references/asset-planning.md)(新建 / 大幅改写)
|
||||
- [validation-checklist.md](references/validation-checklist.md)(创建 / 大幅改写后)
|
||||
|
||||
按需再读:
|
||||
|
||||
- 创建:[`lark-slides-create.md`](references/lark-slides-create.md)
|
||||
- 编辑:[`lark-slides-edit-workflows.md`](references/lark-slides-edit-workflows.md)、[`lark-slides-replace-slide.md`](references/lark-slides-replace-slide.md)、[`lark-slides-replace-pages.md`](references/lark-slides-replace-pages.md)
|
||||
- 截图:[`lark-slides-screenshot.md`](references/lark-slides-screenshot.md)
|
||||
- 图片:[`lark-slides-media-upload.md`](references/lark-slides-media-upload.md)
|
||||
- 图标:[`iconpark.md`](references/iconpark.md)、[`scripts/iconpark_tool.py`](scripts/iconpark_tool.py)
|
||||
- 排障:[`troubleshooting.md`](references/troubleshooting.md)
|
||||
- 完整协议:[`slides_xml_schema_definition.xml`](references/slides_xml_schema_definition.xml)
|
||||
| 场景 | 去读 |
|
||||
|---|---|
|
||||
| 新建 PPT | `design.md` → `planning.md` → `layout.md` → `xml-protocol.md` → `cli-operations.md` → `verify.md` |
|
||||
| 编辑已有页面(局部 / 多页) | `cli-operations.md`(编辑决策树 + replace verb) |
|
||||
| 从模板 / 本地 PPTX 二次创作 | `cli-operations.md`(导入) |
|
||||
| 读取 / 分析已有 PPT | `cli-operations.md`(xml-get + token 解析) |
|
||||
| 画图(图表 / 流程 / 架构 / 装饰) | `cli-operations.md`(元素选型决策树)→ 需要时 `whiteboard.md` |
|
||||
| 配图 / 图标 | `cli-operations.md`(media-upload + iconpark) |
|
||||
| 失败 / 空白页 / 3350001 / 布局异常 | `troubleshooting.md` |
|
||||
|
||||
## Workflow
|
||||
|
||||
> **这是演示文稿,不是文档。** 每页 slide 是独立的视觉画面,信息密度要适当,排版要留白。
|
||||
|
||||
### Design Ideas
|
||||
|
||||
不要生成无设计感的幻灯片。纯白背景 + 标题 + bullets 只能作为极简临时稿,不能作为正式交付。
|
||||
|
||||
开始写 XML 前,先在 `slide_plan.json` 里确定 deck 级视觉策略:
|
||||
|
||||
- **主题化配色**:配色必须服务本次主题、行业和受众,不要默认蓝色商务风。如果把同一套颜色换到另一个完全不同主题仍然成立,说明配色不够具体。
|
||||
- **主次比例**:选择 1 个主色承担约 60-70% 视觉权重,1-2 个辅助色承担结构和分区,1 个强调色只用于关键数字、结论或行动点。不要让所有颜色权重相同。
|
||||
- **背景一致性**:先确定全 deck 的背景策略,默认保持同一明暗基调和底色体系;只有分节、转场或强调页才有意改变背景,并必须通过相同主色、纹理、边栏或 motif 让变化看起来属于同一套设计。无论深浅,都要保证正文、图标和线条对比充足。
|
||||
- **统一 motif**:选择一个可复用视觉母题贯穿全文,例如粗侧边栏、圆形图标底、半出血图片区、编号节点、卡片左上角色块或大号数字。不要每页换一套装饰语言。
|
||||
|
||||
每页至少要有一个视觉元素:图片、图标、图表、表格、流程、对比结构、大号数字、示意图或由 shape 组成的抽象视觉。文本框本身不算主视觉。
|
||||
|
||||
可优先考虑这些页面形态:
|
||||
|
||||
- **双栏结构**:左文右图或左图右文,视觉区域占 35-45% 宽度。
|
||||
- **图标行**:图标在色块或圆形底中,右侧是短标题和一句解释。
|
||||
- **2x2 / 2x3 网格**:适合能力、模块、风险、行动项,每格内容保持同等层级。
|
||||
- **半出血视觉**:图片或抽象形状占据左/右半屏,文字覆盖或贴边排布。
|
||||
- **大数字卡片**:关键指标用 60-72pt 数字,下面配 10-14pt 标签。
|
||||
- **对比列**:before/after、方案 A/B、问题/解法用左右并列,标题和基线严格对齐。
|
||||
- **时间线/流程图**:步骤用节点和箭头表达,流程方向必须一眼可见。
|
||||
|
||||
字体和间距建议:
|
||||
|
||||
- 标题 36-44pt,关键结论可更大;正文 14-18pt;注释 10-12pt。
|
||||
- 正文默认左对齐;只在封面、结尾或大号数字场景中使用居中。
|
||||
- 页面边距至少 40px;内容块之间保持 24-40px 间距,并在同一 deck 内保持一致。
|
||||
- 卡片内边距要真实留出空间,不要让文字贴边;对齐 shape 和文字时要考虑文本框 padding。
|
||||
|
||||
常见错误必须避免:
|
||||
|
||||
- 不要所有页面复用同一种标题 + 三 bullets 版式。
|
||||
- 不要用低对比文字或低对比图标,例如浅灰字压在浅色背景上。
|
||||
- 不要让装饰线穿过文字,或让页脚、来源、编号挤压主体内容。
|
||||
- 不要把素材缺失表现为空白图片框;必须按 `fallback_if_missing` 生成 XML-native 视觉。
|
||||
- 不要留下模板占位文案、示例公司名、示例日期或与用户主题无关的原模板内容。
|
||||
- 不要使用 emoji。
|
||||
- 不要为了画出一个具象物体而堆叠 3 个以上仅用于拟形的 shape。
|
||||
|
||||
### 创建方式选择
|
||||
|
||||
| 场景 | 推荐方式 |
|
||||
|------|----------|
|
||||
| 简单 XML(1-3 页、结构简单、几乎无复杂中文和特殊字符) | `slides +create --slides '[...]'` 一步创建 |
|
||||
| 复杂 XML(多页、含中文、大段文本、复杂布局、嵌套引号、特殊字符较多) | **两步创建**:先 `slides +create` 创建空白 PPT,再用 `xml_presentation.slide create` 逐页添加 |
|
||||
| 已有 PPT 继续追加或插入页面 | 使用 `xml_presentation.slide create`,必要时配合 `before_slide_id` |
|
||||
|
||||
> [!WARNING]
|
||||
> `--slides '[...]'` 的风险点主要在 shell 参数传递,而不是单纯页数。即使只有 1 页,只要 XML 足够复杂,也建议使用两步创建法。
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `slides +create --slides` 底层会逐页创建,不是原子操作。中途失败时先记录 `xml_presentation_id`,回读确认当前状态,再继续修复或追加。
|
||||
|
||||
### 生成流程
|
||||
|
||||
```text
|
||||
Step 1: 需求澄清 & 读取知识
|
||||
- 澄清主题、受众、页数、风格;若用户上传 PPTX 作为模板,按顶部『用户自定义模板』规则处理
|
||||
- 读取 xml-schema-quick-ref.md;新建 / 大幅改写时还要读取 planning-layer.md、visual-planning.md、asset-planning.md
|
||||
Step 1 需求澄清 + 读知识
|
||||
- 澄清主题 / 受众 / 页数 / 风格;用户给 PPTX 底稿走模板流程
|
||||
- 读 xml-protocol.md;新建或大改再读 design.md、layout.md、planning.md
|
||||
|
||||
Step 2: 生成大纲 → 用户确认 → 写入 slide_plan.json
|
||||
- 生成结构化大纲供用户确认
|
||||
- 新建 / 大幅改写必须先创建目录并写入 `.lark-slides/plan/<deck-or-task-id>/slide_plan.json`
|
||||
- plan 字段、路径命名和 `asset_need` 结构按 planning-layer.md / asset-planning.md 执行
|
||||
Step 2 大纲 → 用户确认 → 写 slide_plan.json
|
||||
- 生成大纲交用户确认,再写 .lark-slides/plan/<id>/slide_plan.json
|
||||
- 大纲模板、plan 字段、资产规划见 planning.md
|
||||
|
||||
Step 3: 按 slide_plan.json 生成 XML → 创建
|
||||
Step 3 按 plan 生成 XML → 创建
|
||||
- 逐页消费 plan:key_message 定主结论,layout_type 定几何,visual_focus 定主视觉,text_density 定文本量
|
||||
- 缺少真实素材时必须用 `fallback_if_missing` 生成 XML-native 兜底视觉;不要留空
|
||||
- 调用创建或整页替换接口前,先保存待提交 XML 并运行 xml_text_overlap_lint.py;error_count 不为 0 必须先修
|
||||
- 创建方式按“创建方式选择”判断;图片、复杂 XML、转义和 3350001 排查按 lark-slides-create.md、media-upload.md、troubleshooting.md 执行
|
||||
- 提交前跑 overlap lint,error_count 必须为 0
|
||||
- 创建方式、jq 组装、图片、元素选型见 cli-operations.md
|
||||
|
||||
Step 4: 审查 & 交付
|
||||
- 创建完成后,必须用 `slides +xml-get` 读取全文 XML,并按 validation-checklist.md 做显式验证记录
|
||||
- 失败或部分成功按 troubleshooting.md 处理;局部问题优先用 `+replace-slide` 修正
|
||||
- 没问题 → 交付:告知用户演示文稿 ID 和访问方式
|
||||
Step 4 审查 & 交付
|
||||
- 回读全文 XML,按 verify.md 显式验证
|
||||
- 有问题优先 +replace-slide 局部修(cli-operations.md)
|
||||
- 失败排障见 troubleshooting.md;无问题则告知演示文稿 ID 和访问方式
|
||||
```
|
||||
|
||||
### jq 命令模板(编辑已有 PPT 时使用)
|
||||
## 读取原则
|
||||
|
||||
新建 PPT 推荐用 `+create --slides`。以下 jq 模板适用于向已有演示文稿追加页面的场景,可以避免手动转义双引号:
|
||||
|
||||
```bash
|
||||
# 追加到末尾
|
||||
lark-cli slides xml_presentation.slide create \
|
||||
--as user \
|
||||
--params '{"xml_presentation_id":"YOUR_ID"}' \
|
||||
--data "$(jq -n --arg content '<slide xmlns="http://www.larkoffice.com/sml/2.0">
|
||||
<style><fill><fillColor color="BACKGROUND_COLOR"/></fill></style>
|
||||
<data>
|
||||
<!-- 在这里放置 shape、line、table、chart 等元素 -->
|
||||
</data>
|
||||
</slide>' '{slide:{content:$content}}')"
|
||||
|
||||
# 插到指定页之前:before_slide_id 必须在 --data body 里,与 slide 同级
|
||||
# ⚠️ 不要把 before_slide_id 写进 --params —— CLI 会当未知 query 参数静默下发,服务端忽略,新页跑到末尾
|
||||
lark-cli slides xml_presentation.slide create \
|
||||
--as user \
|
||||
--params '{"xml_presentation_id":"YOUR_ID"}' \
|
||||
--data "$(jq -n --arg content '<slide ...>...</slide>' --arg before 'TARGET_SLIDE_ID' \
|
||||
'{slide:{content:$content}, before_slide_id:$before}')"
|
||||
```
|
||||
|
||||
> 渐变色必须使用 `rgba()` 格式并带百分比停靠点,如 `linear-gradient(135deg,rgba(15,23,42,1) 0%,rgba(56,97,140,1) 100%)`。使用 `rgb()` 或省略停靠点会导致服务端回退为白色。
|
||||
|
||||
### 大纲模板
|
||||
|
||||
生成大纲时使用以下格式,交给用户确认:
|
||||
|
||||
```text
|
||||
[PPT 标题] — [定位描述],面向 [目标受众]
|
||||
|
||||
页面结构(N 页):
|
||||
1. 封面页:[标题文案]
|
||||
2. [页面主题]:[要点1]、[要点2]、[要点3]
|
||||
3. [页面主题]:[要点描述]
|
||||
...
|
||||
N. 结尾页:[结尾文案]
|
||||
|
||||
风格:[配色方案],[排版风格]
|
||||
```
|
||||
|
||||
## 核心概念
|
||||
|
||||
### URL 格式与 Token
|
||||
|
||||
| URL 格式 | 示例 | Token 类型 | 处理方式 |
|
||||
|----------|------|-----------|----------|
|
||||
| `/slides/` | `https://example.larkoffice.com/slides/xxxxxxxxxxxxx` | `xml_presentation_id` | URL 路径中的 token 直接作为 `xml_presentation_id` 使用 |
|
||||
| `/wiki/` | `https://example.larkoffice.com/wiki/wikcnxxxxxxxxx` | `wiki_token` | ⚠️ **不能直接使用**,需要先查询获取真实的 `obj_token` |
|
||||
|
||||
> `+replace-slide` 和 `+media-upload` shortcut 会自动解析以上两种 URL;直接调用原生 API 时仍需手动解析 wiki 链接。
|
||||
|
||||
### Wiki 链接特殊处理(关键!)
|
||||
|
||||
知识库链接(`/wiki/TOKEN`)不能直接当 `xml_presentation_id`。直接调用原生 API 前,先查询 wiki 节点,确认 `node.obj_type == "slides"`,再用 `node.obj_token` 作为真实 presentation ID。
|
||||
|
||||
```bash
|
||||
lark-cli wiki spaces get_node --as user --params '{"token":"wiki_token"}'
|
||||
```
|
||||
|
||||
Shortcut `+replace-slide` 和 `+media-upload` 会自动解析 `/wiki/` URL;手动调用 `xml_presentations.*` / `xml_presentation.slide.*` 时才需要自己做这一步。
|
||||
|
||||
### 资源关系
|
||||
|
||||
```text
|
||||
Wiki Space (知识空间)
|
||||
└── Wiki Node (知识库节点, obj_type: slides)
|
||||
└── obj_token → xml_presentation_id
|
||||
|
||||
Slides (演示文稿)
|
||||
├── xml_presentation_id (演示文稿唯一标识)
|
||||
├── revision_id (版本号)
|
||||
└── Slide (幻灯片页面)
|
||||
└── slide_id (页面唯一标识)
|
||||
```
|
||||
|
||||
## Shortcuts 与 API
|
||||
|
||||
Shortcut 是对常用操作的高级封装(`lark-cli slides +<verb> [flags]`)。有 Shortcut 的操作优先使用。
|
||||
|
||||
| Shortcut | 说明 |
|
||||
|----------|------|
|
||||
| [`+create`](references/lark-slides-create.md) | 创建 PPT(可选 `--slides` 一步添加页面,支持 `<img src="@./local.png">` 占位符自动上传) |
|
||||
| [`+xml-get`](references/lark-slides-xml-get.md) | 读取全文或单页 XML,并可保存到本地文件,避免终端输出被截断 |
|
||||
| [`+media-upload`](references/lark-slides-media-upload.md) | 上传本地图片到指定演示文稿,返回 `file_token`(用作 `<img src="...">`),最大 20 MB |
|
||||
| [`+replace-slide`](references/lark-slides-replace-slide.md) | 对已有幻灯片页面进行块级替换/插入(`block_replace` / `block_insert`),自动注入 id 和 `<content/>`,不改变页序 |
|
||||
| [`+replace-pages`](references/lark-slides-replace-pages.md) | 在原演示文稿内批量重建多个页面:先创建新页到旧页前,再删除旧页;适合已有 Slides 的多页大改,不新建链接 |
|
||||
|
||||
没有 Shortcut 覆盖时使用原生 API。高频资源:`slides +xml-get` 读取全文;`xml_presentation.slide.create/delete/get/replace` 管理单页。
|
||||
|
||||
```bash
|
||||
lark-cli schema slides.<resource>.<method> # 调用 API 前必须先查看参数结构
|
||||
lark-cli slides <resource> <method> [flags] # 调用 API
|
||||
```
|
||||
|
||||
> **重要**:使用原生 API 时,必须先运行 `schema` 查看 `--data` / `--params` 参数结构,不要猜测字段格式。
|
||||
|
||||
## 核心规则
|
||||
|
||||
1. **先规划再写 XML**:新建演示文稿或大幅改写页面时,必须先写入 `.lark-slides/plan/<deck-or-task-id>/slide_plan.json`;模板、风格和大纲只能作为规划输入,不能绕过规划层
|
||||
2. **创建流程**:简单短 XML(1-3 页、结构简单、特殊字符少)可用 `slides +create --slides '[...]'` 一步创建;复杂内容、含图片/中文大段文本/嵌套引号/较多特殊字符,或超过 10 页时,默认先 `slides +create` 创建空白 PPT,再用 `xml_presentation.slide.create` 逐页添加
|
||||
3. **`<slide>` 直接子元素只有 `<style>`、`<data>`、`<note>`**:文本和图形必须放在 `<data>` 内
|
||||
4. **文本通过 `<content>` 表达**:必须用 `<content><p>...</p></content>`,不能把文字直接写在 shape 内
|
||||
5. **保存关键 ID**:后续操作需要 `xml_presentation_id`、`slide_id`、`revision_id`
|
||||
6. **删除谨慎**:删除操作不可逆,且至少保留一页幻灯片
|
||||
7. **编辑已有页面优先原链接更新**:修改单个 shape/img 用 `+replace-slide`(`block_replace` / `block_insert`),不要整页重建;已有 Slides 的多页整页重建用 `+replace-pages`,不要用 `slides +create` 新建整份 PPT;只有没有 shortcut 覆盖的特殊单页整页操作才手动 `slide.create` + `slide.delete`
|
||||
8. **`<img src>` 只能用上传到飞书 drive 的 `file_token`,禁止使用 http(s) 外链 URL**:飞书 slides 渲染端不会代理外链图片,外链 src 在 PPT 里通常不显示或显示破图。流程必须是「先把图存到本地 → 用 `slides +media-upload` 上传或 `+create --slides` 的 `@./path` 占位符自动上传 → 拿 `file_token` 写进 `<img src>`」。如果用户给了网图链接,先 `curl`/下载到 CWD 内再走上传流程,不要直接把外链 URL 塞进 `src`。**图片最大 20 MB**(slides upload API 不支持分片上传)。
|
||||
|
||||
> **注意**:如果 md 内容与 `slides_xml_schema_definition.xml` 或 `lark-cli schema slides.<resource>.<method>` 输出不一致,以后两者为准。
|
||||
- **按需即时读**:按当前 Step 只读该步指向的文件,不在开头一次性全读。
|
||||
- **唯一真源**:md 是摘要;与 `slides_xml_schema_definition.xml` 或 `lark-cli schema` 输出冲突时,以后两者为准。
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
# Asset Planning
|
||||
|
||||
新建演示文稿或大幅改写页面时,在写入 `slide_plan.json` 前后都可以参考本文件。目标是让 agent 主动识别有价值的图、图标、图表、流程图、时序图、架构图、装饰图案、截图或示意图需求,同时保持 deck 在没有真实素材时也能完整执行。
|
||||
|
||||
本文件只定义轻量资产规划。不要把它理解成素材采集流程。
|
||||
|
||||
## Core Rules
|
||||
|
||||
- Every planned asset must include a fallback visual plan. The fallback can use native charts, tables, whiteboard diagrams, placeholder regions, or XML shapes, text, and arrows as appropriate.
|
||||
- Asset needs must serve the page's `key_message` and `visual_focus`. Do not add decorative assets that do not clarify the page.
|
||||
- Prefer a few high-value asset plans over one asset on every page. For a 6-page technical or business deck, plan assets on at least 3 pages when the content allows.
|
||||
- If a real local asset already exists or the user provides one, it can be used through the normal media-upload workflow. Still keep `fallback_if_missing` in the plan.
|
||||
- Do not leave blank image boxes in final XML. If the asset is missing, render the fallback visual.
|
||||
|
||||
## JSON Shape
|
||||
|
||||
Use an object for one planned asset, or an array when a page genuinely needs multiple assets. Keep each item compact.
|
||||
|
||||
```json
|
||||
{
|
||||
"asset_type": "architecture_diagram",
|
||||
"purpose": "Show how API gateway, planner, XML generator, and Slides API interact.",
|
||||
"suggested_query": "agent native slides runtime architecture diagram",
|
||||
"fallback_if_missing": "Draw grouped boxes connected by arrows with short labels."
|
||||
}
|
||||
```
|
||||
|
||||
For a page without a meaningful asset need, use:
|
||||
|
||||
```json
|
||||
{
|
||||
"asset_type": "none",
|
||||
"purpose": "No external or simulated asset needed; the page is text-led.",
|
||||
"suggested_query": "",
|
||||
"fallback_if_missing": "Use typography, spacing, and simple accent shapes only."
|
||||
}
|
||||
```
|
||||
|
||||
## Supported Asset Types
|
||||
|
||||
- `paper_figure`: figure from a paper or technical article.
|
||||
- `architecture_diagram`: system components, data flow, dependency map, or model structure.
|
||||
- `icon`: small semantic symbol for a concept, step, role, or status.
|
||||
- `logo`: brand, product, team, or customer mark.
|
||||
- `chart`: column, bar, line, area, radar, pie, doughnut/ring, or combo data visual. Note: `<chart>` does not support funnel or scatter — map those to `<whiteboard>` SVG at generation time.
|
||||
- `infographic`: composed visual explanation, usually combining labels, numbers, and simple shapes.
|
||||
- `screenshot`: product UI, terminal output, workflow state, or page capture.
|
||||
- `flow_diagram`: process, sequence, decision tree, or mechanism diagram.
|
||||
- `none`: explicitly no asset needed.
|
||||
|
||||
Do not invent new asset types unless the user asks for a special visual format. If a need is close to these types, choose the closest one and explain the detail in `purpose`.
|
||||
|
||||
## Planning Guidance
|
||||
|
||||
Match asset type to slide role:
|
||||
|
||||
- `architecture-diagram` layout usually pairs with `architecture_diagram` or `flow_diagram`.
|
||||
- `process-flow` layout usually pairs with `flow_diagram`, `icon`, or `infographic`.
|
||||
- `comparison` layout often works with `icon`, `chart`, or `infographic`.
|
||||
- `timeline` layout often works with `icon`, `chart`, or shape-based milestone markers.
|
||||
- `big-number` layout often works with `chart` or `infographic`, but only if it supports the metric.
|
||||
- `image-left-text-right` and `image-right-text-left` can use `screenshot`, `paper_figure`, `logo`, or `infographic`; if missing, use a large placeholder diagram or stylized panel.
|
||||
|
||||
`suggested_query` is only a future lookup hint. Write it as a short phrase a human or later workflow could search, but do not execute the search unless the user separately requests real assets.
|
||||
|
||||
For `asset_type: "chart"`:
|
||||
|
||||
- If the visual is a supported standard data chart — column, bar, line, area, radar, pie, doughnut/ring, or combo — `fallback_if_missing` must still render as a native `<chart>`.
|
||||
- Do not imitate supported standard data visuals with manual drawing primitives or `<whiteboard>`.
|
||||
- Choose the data source explicitly:
|
||||
- `user_provided`: when the user provides concrete values, tables, CSV, or metric lists, use those values and do not replace them with mock data.
|
||||
- `mock_placeholder`: when the user asks for a placeholder, template, example, or chart position to replace later, use mock data in a native `<chart>`.
|
||||
- `mock_required_by_intent`: when the user does not provide concrete values but asks for data expression, charts, trends, comparisons, or distributions, use mock data in a native `<chart>`.
|
||||
- Mock data must be labeled as `模拟数据,仅占位,待替换真实数据` or equivalent. Do not present mock values as facts.
|
||||
- Manual drawing fallbacks are allowed only for unsupported chart types such as scatter, funnel, waterfall-like custom visuals, or decorative non-data visuals.
|
||||
|
||||
`fallback_if_missing` must be concrete enough to turn into XML, for example:
|
||||
|
||||
- "Draw a simplified attention matrix with 5 token labels, semi-transparent cells, and arrows to output token."
|
||||
- "Use three grouped boxes with arrows from client to gateway to service; add small protocol labels."
|
||||
- "Render a native `<chart>` using the user-provided series."
|
||||
- "Render a native `<chart>` with mock placeholder values and label it as `模拟数据,仅占位,待替换真实数据`."
|
||||
- "Use a bordered placeholder panel with product area labels, not an empty image."
|
||||
|
||||
Weak fallbacks to avoid:
|
||||
|
||||
- "Use a placeholder."
|
||||
- "Find another image."
|
||||
- "Leave blank if unavailable."
|
||||
- "Use generic decoration."
|
||||
|
||||
## Examples
|
||||
|
||||
Transformer Self-Attention page:
|
||||
|
||||
```json
|
||||
{
|
||||
"asset_type": "paper_figure",
|
||||
"purpose": "Explain token-to-token attention and why each output token mixes context.",
|
||||
"suggested_query": "Transformer self attention attention matrix diagram",
|
||||
"fallback_if_missing": "Draw a simplified attention matrix with token labels, colored weights, and arrows from input tokens to one highlighted output token."
|
||||
}
|
||||
```
|
||||
|
||||
System architecture page:
|
||||
|
||||
```json
|
||||
{
|
||||
"asset_type": "architecture_diagram",
|
||||
"purpose": "Show the runtime path from user prompt to plan, XML generation, Slides API creation, and fetch verification.",
|
||||
"suggested_query": "slides generation runtime architecture planner XML API verification",
|
||||
"fallback_if_missing": "Draw four grouped boxes connected left-to-right with arrows; put verification as a return arrow from Slides API to agent."
|
||||
}
|
||||
```
|
||||
|
||||
Business comparison page:
|
||||
|
||||
```json
|
||||
{
|
||||
"asset_type": "infographic",
|
||||
"purpose": "Make before/after differences scannable without dense bullet lists.",
|
||||
"suggested_query": "before after product workflow comparison infographic",
|
||||
"fallback_if_missing": "Use two side-by-side panels with matching icon circles and three parallel rows of concise labels."
|
||||
}
|
||||
```
|
||||
|
||||
## Plan To XML Contract
|
||||
|
||||
When generating XML:
|
||||
|
||||
1. If an asset exists and the workflow supports it, place it in the planned visual region.
|
||||
2. If no asset exists, immediately render `fallback_if_missing` with the planned XML-native element type. Supported standard data visuals still use native `<chart>`; other fallbacks may use shapes, text, lines, arrows, tables, whiteboard diagrams, or placeholder panels.
|
||||
3. Size the fallback to satisfy `visual_focus`; it should be a real page element, not a tiny decoration.
|
||||
4. Keep text-density limits. Do not compensate for missing assets by adding long bullet text.
|
||||
5. After creation, fetch the presentation and verify asset pages are not blank and that each planned fallback is visible when no real asset was used.
|
||||
95
skills/lark-slides/references/cli-operations.md
Normal file
95
skills/lark-slides/references/cli-operations.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# CLI 操作
|
||||
|
||||
用 `lark-cli slides` 把事做成:命令、身份、token 解析、创建 / 编辑 / 读取 / 截图 / 上传,以及元素与 verb 的选型。
|
||||
XML 语法见 `xml-protocol.md`,排版见 `design.md` / `layout.md`,验证见 `verify.md`,排障见 `troubleshooting.md`。
|
||||
|
||||
## 身份与认证
|
||||
|
||||
默认 `--as user` 并显式指定;权限不足先查是否误用 bot,不默认回退。仅用户明确要求应用身份、或需 bot 持有资源时才用 `--as bot`(bot 创建后会尝试给当前用户授 `full_access`,`permission_grant.status` 为 `granted` / `skipped` / `failed`)。不要擅自转移 owner,用户要转须单独确认。scope 细节以 `../lark-shared/SKILL.md` 为准。
|
||||
|
||||
## URL / Token 解析
|
||||
|
||||
- `/slides/<token>`:直接作 `xml_presentation_id`。
|
||||
- `/wiki/<token>`:不能直接用,先 `lark-cli wiki spaces get_node --as user --params '{"token":"<t>"}'` 拿 `obj_token` 并确认 `obj_type=="slides"`。
|
||||
- 层级:wiki node(`obj_type:slides`)→ `obj_token`(=`xml_presentation_id`)→ slide(`slide_id`)。
|
||||
- Shortcut(`+xml-get`/`+media-upload`/`+screenshot`/`+replace-slide`/`+replace-pages`)自动解析上述 URL;直接调原生 API 才需自己解析 wiki。
|
||||
|
||||
## 创建(`+create`)
|
||||
|
||||
```bash
|
||||
lark-cli slides +create --as user --title "项目汇报" # 空白
|
||||
lark-cli slides +create --as user --title "项目汇报" --slides '[...]' # 一步加页,每项一页完整 <slide>
|
||||
```
|
||||
|
||||
**方式选择**:简单短 XML(1-3 页、少中文/特殊字符)用 `--slides` 一步;复杂(多页、大段中文、嵌套引号、特殊字符)或 >10 页走两步(先建空白,再 `xml_presentation.slide create` 逐页)。风险在 shell 传参不在页数——1 页够复杂也走两步。
|
||||
|
||||
复杂 XML 用 `jq --rawfile` 组装免转义:`--slides "$(jq -n --rawfile s1 a.xml --rawfile s2 b.xml '[$s1,$s2]')"`。
|
||||
|
||||
`+create --slides` 里 `<img src="@./x.png">` 会自动上传替换为 `file_token`:路径须 CWD 内相对(绝对/`../` 报 `unsafe file path`)、同图去重、≤20 MB、缺文件校验阶段即报错。
|
||||
|
||||
> `--slides` 底层逐页调用、**非原子**。中途失败会停,已建内容保留——先记 `xml_presentation_id`,回读确认状态再续。
|
||||
|
||||
返回:`xml_presentation_id` / `title` / `url`(有则展示)/ `revision_id` / `slide_ids`。
|
||||
|
||||
提交整页 XML 前先跑 `python3 scripts/xml_text_overlap_lint.py --input <file>`,`summary.error_count` 为 0 才能调接口;lint code 含义与修法见 `troubleshooting.md`。
|
||||
|
||||
## 编辑决策树
|
||||
|
||||
| 需求 | 用 |
|
||||
|---|---|
|
||||
| 已知 `block_id`,换这块(改标题/换图/挪坐标) | `+replace-slide` `block_replace`(`replacement` 根 `id` 由 CLI 自动注入) |
|
||||
| 只加元素、不动布局 | `+replace-slide` `block_insert`(可选 `insert_before_block_id`,省略则追加页末) |
|
||||
| 一次动多个元素 | 单次 `--parts` 拼多条,整批原子、任一失败全批不生效、两 action 可混用(≤200 条) |
|
||||
| 多页整页重建、坐标重排 | `+replace-pages`(原 presentation 内先建新页再删旧页,不生成新链接) |
|
||||
| 无 shortcut 覆盖的特殊单页操作 | 手动 `slide.create` + `slide.delete` |
|
||||
|
||||
无字段级 patch:改一个坐标也要把整块新 XML 用 `block_replace` 写出。局部编辑不整页重建,已有 Slides 不用 `+create` 另建链接。
|
||||
|
||||
编辑元素约束:`<td>` 单元格只能 `block_replace` 不能 insert,且整表 `block_replace` 会重建内部 td id、旧 td block_id 立即失效;`<video>` / `<audio>` 非 SML 原生元素、不能写入(insert/replace 返 3350001);`<polyline>` 的 `points` 读回被服务端规整丢弃(几何已入库);除 `block_replace` / `block_insert` 外的 action(如 `str_replace`)会被 CLI 拒绝。
|
||||
|
||||
读-改-写:先 `+xml-get --slide-id` 读原页挑出目标块 short id,再 `+replace-slide` 改。`slide_id` 与页序不变。`--parts` 支持 `@file`/`-`。`--revision-id` 默认 `-1`(最新版),传不存在版本返 3350002;`--tid` 单人单次留空。
|
||||
|
||||
`+replace-pages` 用 `--pages`(每项 `slide_id`+完整 `<slide>`,不支持 `slide_number`、同批不重复 id),非原子,改前先 `--dry-run`/`--validate-only`;默认失败即停,`--continue-on-error` 可继续但最终 partial_failure 非零退出,每页 status 为 `replaced` / `create_failed` / `delete_failed`。
|
||||
|
||||
## 元素选型决策树
|
||||
|
||||
- 标准数据图表(柱/条/折线/面积/雷达/饼/环/组合)→ 原生 `<chart>`,禁手画替代。
|
||||
- 流程/时序/架构图 → 优先 `<whiteboard>` Mermaid,`<shape>`+`<line>` 仅 fallback。
|
||||
- `<chart>` 不支持的图类(散点/漏斗/瀑布)→ `<whiteboard>` SVG。
|
||||
- 装饰/示意 → whiteboard SVG 或 shape 组合;简单几何/连线 → `<shape>`+`<line>`。
|
||||
- 表格 → 优先 `rect`+`text` 模拟,其他才 `<table>`。
|
||||
|
||||
whiteboard 内部语法、以及按模型身份选 SVG / Mermaid 的规则见 `whiteboard.md`。
|
||||
|
||||
## 配图与图标
|
||||
|
||||
**图片**:新建用 `+create --slides` 的 `@` 占位符一步到位;给已有页加图用 `+media-upload --file ./pic.png --presentation "$PID"` 拿 `file_token`(`--file` 须 CWD 内相对、≤20 MB),再用 `+replace-slide` 的 `block_insert` 放入,坐标避开现有元素、`width:height` 对齐原图比例避免裁剪。
|
||||
|
||||
**图标**:禁盲猜 `iconType`。先 `python3 scripts/iconpark_tool.py search --query "<语义>" --limit 8` 检索,选中的写进 `<icon>` 且必须给非透明 `fillColor`、与背景对比充足;查不到用 shape/line/text 画 fallback,不留空图标位。
|
||||
|
||||
## 读取与截图
|
||||
|
||||
```bash
|
||||
lark-cli slides +xml-get --as user --presentation "$PID" --output .lark-slides/plan/$PID/readback.xml # 全文
|
||||
lark-cli slides +xml-get --as user --presentation "$PID" --slide-number 2 --raw # 单页
|
||||
lark-cli slides +screenshot --as user --presentation "$PID" --slide-number 1 # 截图已有页,一次≤10页(--slide-id+--slide-number 合计)
|
||||
lark-cli slides +screenshot --as user --content @slide.xml --output-name preview # 渲染本地单页 XML 预览
|
||||
```
|
||||
|
||||
`+xml-get`:`--slide-id`/`--slide-number` 二选一读单页(`--output` 须 CWD 内相对防截断);`--revision-id` 默认 `-1`;`--remove-attr-id` 仅全文只读检查;`--raw` 原文写 stdout。
|
||||
|
||||
> 截图受应用白名单限制,多数应用不可用。失败只记录,不引导申请 `slides:presentation:screenshot`,改走 `verify.md` 非截图验证,不谎称已截图验收。
|
||||
|
||||
## 从模板 / PPTX 导入
|
||||
|
||||
先把模板导入成 Slides(不必先加载 lark-drive),再在导入结果上二次创作:
|
||||
|
||||
```bash
|
||||
lark-cli drive +import --as user --file "<template.pptx>" --type slides --json
|
||||
```
|
||||
|
||||
可选 `--name "<标题>"` / `--folder-token <token>`。返回 `ready=false` / `timed_out=true` 时执行返回里的 `next_command`(等价 `lark-cli drive +task_result --scenario import --ticket <TICKET>`)。导入后必须回读、理解每页真实版式再编辑。模板是必须沿用的编辑底稿、非风格参考:只改内容不做设计,不为容纳长文案重画主体,不用新增大卡片遮住原图表/图片/关键 shape。
|
||||
|
||||
## 原生 API
|
||||
|
||||
调用前必须先查参数,不猜字段:`lark-cli schema slides.<resource>.<method>`。常用:`xml_presentations.get`(全文)、`xml_presentation.slide.create/get/delete/replace`(单页)。原生调用要自己解析 wiki URL,且 `before_slide_id` 必须放 `--data` body 与 slide 同级——放 `--params` 会被当未知 query 静默忽略、新页跑到末尾。
|
||||
40
skills/lark-slides/references/design.md
Normal file
40
skills/lark-slides/references/design.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 设计风格(全 deck)
|
||||
|
||||
deck 的整体观感——配色、字体、间距、motif、背景一致性,全 deck 定一次、跨页复用。每页坐标与版式几何见 `layout.md`。
|
||||
|
||||
写第一页 XML 前,先把这套视觉系统定下来,之后每页据此落到 SML。这是演示文稿不是文档;纯白背景 + 标题 + bullets 只能作极简临时稿,不能作正式交付。
|
||||
|
||||
## 配色
|
||||
|
||||
- **服务主题**:配色必须服务本次主题、行业和受众,不要默认蓝色商务风。同一套颜色换到另一个完全不同主题仍成立,说明配色不够具体。
|
||||
- **主次比例**:1 个主色承担约 60-70% 视觉权重,1-2 个辅助色承担结构和分区,1 个强调色只用于关键数字、结论或行动点。不要让所有颜色权重相同。
|
||||
- **color_roles**:primary 承担主 motif 与主视觉权重;secondary 用于分组区域、对比面板、支撑类目;accent 只标关键数字/结论/焦点。同一颜色不要在不同页表达不相关含义。
|
||||
|
||||
## 背景一致性
|
||||
|
||||
- 先定全 deck 的背景策略:内容页默认同一明暗基调和底色体系;只有分节、转场或强调页才有意改变背景。
|
||||
- 变化必须通过相同主色、纹理、边栏或 motif 让它看起来属于同一套设计;封面/结尾若用深色或图片主导,要与内容页共享颜色、motif 或几何。
|
||||
- 不要让页面在几近相同却不一致的底色之间漂移。无论深浅,都要保证正文、图标、线条对比充足。
|
||||
|
||||
## motif
|
||||
|
||||
选一个可复用视觉母题贯穿全文:粗侧边栏、header rail、圆形图标底、半出血图片区、编号节点、卡片左上角色块、大号数字或分节色带。motif 出现得足够一致,页面才会显得同属一套。不要每页换一套装饰语言。
|
||||
|
||||
## 字体与间距
|
||||
|
||||
- 标题 36-44pt,关键结论可更大;正文 14-18pt;注释 10-12pt。
|
||||
- 正文默认左对齐;封面、结尾或大号数字场景可居中。
|
||||
- 页面边距至少 40px;内容块之间保持 24-40px 间距,并在同一 deck 内保持一致。
|
||||
|
||||
## 反面项
|
||||
|
||||
- 不要用低对比文字或低对比图标,例如浅灰字压在浅色背景上。
|
||||
- 不要让装饰线穿过文字。
|
||||
- 不要使用 emoji。
|
||||
- 不要所有页面复用同一套「标题 + 三 bullets」版式。
|
||||
- 不要留模板占位文案、示例公司名、示例日期或与主题无关的原模板内容。
|
||||
- 不要为画一个具象物体堆叠 3 个以上仅用于拟形的 shape。
|
||||
|
||||
## 关键提醒
|
||||
|
||||
上述 color_roles、背景策略、motif 都只是**指引**,不会被自动应用。它们必须真正落到每页 SML 的 `<fillColor>` 与文本 `color` 上——不显式写进 XML 就不生效。
|
||||
@@ -1,91 +0,0 @@
|
||||
# 完整操作示例
|
||||
|
||||
本文档提供与 CLI schema 一致的调用示例,XML 内容均遵循 [slides_xml_schema_definition.xml](slides_xml_schema_definition.xml)。
|
||||
|
||||
> **重要**:新建 PPT 请使用 `slides +create --slides`,传入由 `<slide>` XML 字符串组成的 JSON 数组;每个元素必须是一页完整的 `<slide>`。复杂内容建议先创建空白 PPT,再通过 `xml_presentation.slide.create` 逐页添加。完整 `<presentation>` XML 可用于本地 lint 或读取,但不能直接作为 `+create` 的提交参数。
|
||||
|
||||
## 目录
|
||||
|
||||
- [示例 1:可靠创建 6 页 PPT](#示例-1可靠创建-6-页-ppt)
|
||||
- [示例 7: +replace-slide + block_insert 给已有页加图](#示例-7-replace-slide--block_insert-给已有页加图)
|
||||
- [示例 8: +replace-slide + block_replace 替换一个块](#示例-8-replace-slide--block_replace-替换一个块)
|
||||
|
||||
## 示例 1:可靠创建 6 页 PPT
|
||||
|
||||
### 1. 写入规划文件
|
||||
|
||||
```bash
|
||||
DECK_DIR=".lark-slides/plan/reliable-six-page-ppt"
|
||||
mkdir -p "$DECK_DIR"
|
||||
|
||||
# 按 planning-layer.md 写入 "$DECK_DIR/slide_plan.json",
|
||||
# 至少记录 6 页的顺序和标题。
|
||||
```
|
||||
|
||||
### 2. 为每页保存独立 XML
|
||||
|
||||
每个文件都是完整的 `<slide>`。下面的循环会生成 6 个独立 XML 文件;实际项目中可将每页主体替换为规划内容。
|
||||
|
||||
```bash
|
||||
titles=("主题与结论" "问题背景" "核心方法" "关键数据" "执行计划" "总结与行动")
|
||||
for i in {1..6}; do
|
||||
printf -v page '%02d' "$i"
|
||||
cat > "$DECK_DIR/slide-$page.xml" <<XML
|
||||
<slide xmlns="http://www.larkoffice.com/sml/2.0"><style><fill><fillColor color="rgb(248,250,252)"/></fill></style><data><shape type="rect" topLeftX="56" topLeftY="56" width="12" height="428"><fill><fillColor color="rgb(37,99,235)"/></fill></shape><shape type="text" topLeftX="100" topLeftY="160" width="760" height="90"><content textType="title" autoFit="normal-auto-fit"><p>${titles[$((i-1))]}</p></content></shape><shape type="text" topLeftX="100" topLeftY="290" width="700" height="70"><content textType="body" autoFit="normal-auto-fit"><p>页面主体内容。</p></content></shape></data></slide>
|
||||
XML
|
||||
done
|
||||
```
|
||||
|
||||
### 3. 逐页运行 lint
|
||||
|
||||
提交前检查每个独立 XML。`summary.error_count` 必须为 `0`,否则先修复 XML 或布局问题。
|
||||
|
||||
```bash
|
||||
for slide_xml in "$DECK_DIR"/slide-0{1,2,3,4,5,6}.xml; do
|
||||
python3 skills/lark-slides/scripts/xml_text_overlap_lint.py \
|
||||
--input "$slide_xml" | tee "${slide_xml%.xml}.lint.json"
|
||||
done
|
||||
|
||||
test "$(jq -s 'map(.summary.error_count) | add' "$DECK_DIR"/slide-0{1,2,3,4,5,6}.lint.json)" = "0"
|
||||
```
|
||||
|
||||
### 4. 使用 `+create` 创建 6 页 PPT
|
||||
|
||||
`--slides` 接收由 6 个完整 `<slide>` XML 字符串组成的 JSON 数组;使用 `jq --rawfile` 避免手动处理 XML 引号和换行。
|
||||
|
||||
```bash
|
||||
lark-cli slides +create --as user \
|
||||
--title "可靠创建 6 页 PPT" \
|
||||
--slides "$(jq -n \
|
||||
--rawfile s1 "$DECK_DIR/slide-01.xml" \
|
||||
--rawfile s2 "$DECK_DIR/slide-02.xml" \
|
||||
--rawfile s3 "$DECK_DIR/slide-03.xml" \
|
||||
--rawfile s4 "$DECK_DIR/slide-04.xml" \
|
||||
--rawfile s5 "$DECK_DIR/slide-05.xml" \
|
||||
--rawfile s6 "$DECK_DIR/slide-06.xml" \
|
||||
'[$s1, $s2, $s3, $s4, $s5, $s6]')" \
|
||||
> "$DECK_DIR/create.json"
|
||||
create_status=$?
|
||||
|
||||
if [ "$create_status" -ne 0 ]; then
|
||||
exit "$create_status"
|
||||
fi
|
||||
|
||||
if ! PRESENTATION_ID=$(jq -er '.data.xml_presentation_id | strings | select(length > 0)' "$DECK_DIR/create.json"); then
|
||||
echo "missing non-empty data.xml_presentation_id in $DECK_DIR/create.json" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$PRESENTATION_ID" > "$DECK_DIR/xml_presentation_id"
|
||||
```
|
||||
|
||||
如果创建中途失败,先保存已经返回的 `xml_presentation_id`,再回读确认实际已创建页数。
|
||||
|
||||
### 5. 用 `+xml-get` 回读全文 XML
|
||||
|
||||
```bash
|
||||
lark-cli slides +xml-get --as user \
|
||||
--presentation "$PRESENTATION_ID" \
|
||||
--output "$DECK_DIR/readback.xml" \
|
||||
--json | tee "$DECK_DIR/readback.json"
|
||||
```
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
# IconPark 图标
|
||||
|
||||
IconPark 图标通过 `<icon>` 写入 slides XML,`iconType` 必须来自本 skill 的离线索引,避免凭记忆拼路径。
|
||||
|
||||
## 机器优先流程
|
||||
|
||||
```bash
|
||||
python3 skills/lark-slides/scripts/iconpark_tool.py search --query "增长趋势" --limit 8
|
||||
python3 skills/lark-slides/scripts/iconpark_tool.py resolve --name chart-line
|
||||
python3 skills/lark-slides/scripts/iconpark_tool.py list-categories
|
||||
```
|
||||
|
||||
`search` 返回 JSON 数组,每项包含 `iconType`、`category`、`name`、`tags`、`score`。直接把选中的 `iconType` 写入 XML,并为图标指定可见颜色:
|
||||
|
||||
```xml
|
||||
<icon iconType="iconpark/Charts/chart-line.svg" topLeftX="80" topLeftY="120" width="32" height="32">
|
||||
<fill>
|
||||
<fillColor color="rgba(37, 99, 235, 1)"/>
|
||||
</fill>
|
||||
</icon>
|
||||
```
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 默认先检索:语义图标需求必须先用 `iconpark_tool.py search --limit 8` 或 `--limit 10`,让 agent 从候选里结合版面语义二次判断;不要阅读全文索引,也不要编造不存在的 `iconType`。
|
||||
- 图标用于概念提示、步骤、状态、指标、角色和导航;不要用无关装饰图标填充版面。
|
||||
- 常用尺寸:行内状态图标 16-24px,卡片标题图标 28-40px,主视觉图标 56-96px。
|
||||
- 视觉规范要求图标设置非透明 `fillColor`,显式指定颜色并和背景有足够对比;深色背景优先放在浅色圆形/方形底上,或使用 `rgba(255, 255, 255, 1)` 作为图标填充色。
|
||||
- 查不到合适图标时,用 shape、line、text 画 XML-native fallback,不留空图标位。
|
||||
|
||||
## 高频示例
|
||||
|
||||
| 语义 | iconType |
|
||||
|---|---|
|
||||
| 设置/配置 | `iconpark/Base/setting.svg` |
|
||||
| 目标 | `iconpark/Base/aiming.svg` |
|
||||
| 增长趋势 | `iconpark/Charts/positive-dynamics.svg` |
|
||||
| 折线趋势 | `iconpark/Charts/chart-line.svg` |
|
||||
| 占比 | `iconpark/Charts/chart-proportion.svg` |
|
||||
| 数据看板 | `iconpark/Charts/data-screen.svg` |
|
||||
| 成功 | `iconpark/Character/check-one.svg` |
|
||||
| 失败/风险 | `iconpark/Character/close-one.svg` |
|
||||
| 团队/用户 | `iconpark/Peoples/peoples.svg` |
|
||||
| 安全防护 | `iconpark/Safe/protect.svg` |
|
||||
| 全球/市场 | `iconpark/Travel/world.svg` |
|
||||
| 邮件/联系 | `iconpark/Office/envelope-one.svg` |
|
||||
@@ -1,156 +0,0 @@
|
||||
|
||||
# slides +create(创建飞书幻灯片)
|
||||
|
||||
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
|
||||
|
||||
创建一个新的飞书幻灯片演示文稿,可选一步添加页面内容。
|
||||
|
||||
- 禁止:从完整 <presentation> XML 解析/拆分/重序列化生成提交 payload。
|
||||
- 推荐:提交源直接就是单页 <slide> XML;+create --slides 只接受已经人工/程序直接生成的 slide 数组,不接受由
|
||||
presentation 动态拆出来的数组。
|
||||
|
||||
- 最稳:复杂 deck 默认空 deck + 单页 slide create,每次只提交一个 <slide>。
|
||||
|
||||
- 注意:复杂 XML 不适合直接塞命令行,中文、引号、特殊字符较多时,直接拼接 --slides 容易发生 shell 转义或截断。建议将每页 XML 保存为独立文件,使用 `jq --rawfile` 组装 JSON 数组,避免手动处理 XML 引号和换行。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 创建空白 PPT
|
||||
lark-cli slides +create --title "项目汇报"
|
||||
|
||||
# 创建 PPT + 添加 slide 页面
|
||||
lark-cli slides +create --title "项目汇报" --slides '[
|
||||
"<slide xmlns=\"http://www.larkoffice.com/sml/2.0\"><data><shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>封面</p></content></shape></data></slide>",
|
||||
"<slide xmlns=\"http://www.larkoffice.com/sml/2.0\"><data><shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>第二页</p></content></shape></data></slide>"
|
||||
]'
|
||||
|
||||
# 以应用身份创建(自动授权当前用户)
|
||||
lark-cli slides +create --title "项目汇报" --as bot
|
||||
|
||||
# 预览(不执行)
|
||||
lark-cli slides +create --title "项目汇报" --slides '[...]' --dry-run
|
||||
```
|
||||
|
||||
复杂内容建议按页保存 XML,再用 `jq --rawfile` 组装 `--slides` 参数:
|
||||
|
||||
```bash
|
||||
lark-cli slides +create --as user --title "项目汇报" \
|
||||
--slides "$(jq -n \
|
||||
--rawfile s1 .lark-slides/plan/project/slide-01.xml \
|
||||
--rawfile s2 .lark-slides/plan/project/slide-02.xml \
|
||||
'[$s1, $s2]')"
|
||||
```
|
||||
|
||||
`--rawfile` 会把文件内容作为字符串读入 JSON,自动处理 XML 中的引号和换行;不要手动拼接带大量转义符的 JSON 字符串。
|
||||
|
||||
## 返回值
|
||||
|
||||
工具成功执行后,返回一个 JSON 对象,包含以下字段:
|
||||
|
||||
- **`xml_presentation_id`**(string):演示文稿的唯一标识符,后续添加页面时需要此 ID
|
||||
- **`title`**(string):演示文稿标题
|
||||
- **`url`**(string,可选):演示文稿的在线链接,如有返回则务必展示给用户(需要 drive 相关权限;若获取失败则不返回此字段)
|
||||
- **`revision_id`**(integer):演示文稿版本号
|
||||
- **`slide_ids`**(string[],可选):仅传 `--slides` 时返回,成功添加的页面 ID 列表
|
||||
- **`slides_added`**(integer,可选):仅传 `--slides` 时返回,成功添加的页面数量
|
||||
- **`images_uploaded`**(integer,可选):仅 `--slides` 中含 `@<本地路径>` 占位符时返回,已上传的去重后图片数量
|
||||
- **`permission_grant`**(object,可选):仅 `--as bot` 时返回,说明是否已自动为当前 CLI 用户授予可管理权限
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 不传 `--slides` 时,`slides +create` 只创建空白演示文稿。创建后需要使用 `xml_presentation.slide create` 逐页添加 slide 内容。
|
||||
>
|
||||
> 传了 `--slides` 时,CLI 先创建空白演示文稿,再逐页调用 `xml_presentation.slide create` 添加页面。如果某一页添加失败,CLI 会停止并报错,已创建的演示文稿和已添加的页面会保留。
|
||||
>
|
||||
> 如果演示文稿是**以应用身份(bot)创建**的,如 `lark-cli slides +create --as bot`,CLI 会**尝试为当前 CLI 用户自动授予该演示文稿的 `full_access`(可管理权限)**。
|
||||
>
|
||||
> 以应用身份创建时,结果里会额外返回 `permission_grant` 字段,明确说明授权结果:
|
||||
> - `status = granted`:当前 CLI 用户已获得该演示文稿的可管理权限
|
||||
> - `status = skipped`:本地没有可用的当前用户 `open_id`,因此不会自动授权
|
||||
> - `status = failed`:演示文稿已创建成功,但自动授权用户失败
|
||||
>
|
||||
> **不要擅自执行 owner 转移。** 如果用户需要把 owner 转给自己,必须单独确认。
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--title` | 否 | 演示文稿标题(不传则默认 "Untitled") |
|
||||
| `--slides` | 否 | slide 内容 JSON 数组,每个元素是一个 `<slide>` XML 字符串(最多 10 个;超过 10 页请先用 `+create` 创建空白 PPT,再用 `xml_presentation.slide create` 逐页添加) |
|
||||
|
||||
## `--slides` 参数格式
|
||||
|
||||
```json
|
||||
[
|
||||
"<slide xmlns=\"http://www.larkoffice.com/sml/2.0\">...第1页XML...</slide>",
|
||||
"<slide xmlns=\"http://www.larkoffice.com/sml/2.0\">...第2页XML...</slide>"
|
||||
]
|
||||
```
|
||||
|
||||
JSON string 数组,每个元素是一页 slide 的完整 XML。CLI 内部负责包装成 API 所需的 `{"slide": {"content": "..."}}` 格式并逐页调用。
|
||||
|
||||
### 本地图片:`@<path>` 占位符
|
||||
|
||||
`<img>` 元素的 `src` 属性如果以 `@` 开头,CLI 会把它当作本地文件路径,自动上传到当前演示文稿,并把占位符替换为返回的 `file_token`。
|
||||
|
||||
```bash
|
||||
lark-cli slides +create --as user --title "图测试" --slides '[
|
||||
"<slide xmlns=\"http://www.larkoffice.com/sml/2.0\"><data><img src=\"@./assets/chart.png\" topLeftX=\"100\" topLeftY=\"100\" width=\"320\" height=\"180\"/></data></slide>"
|
||||
]'
|
||||
```
|
||||
|
||||
行为:
|
||||
|
||||
- 路径相对于**当前工作目录**(CWD)解析;**必须是 CWD 内的相对路径**(如 `./pic.png`、`./assets/x.png`)
|
||||
- 同一份图被多次引用时**只上传一次**(按路径去重)
|
||||
- `src` 不以 `@` 开头的会原样保留,但**只允许写 `slides +media-upload` 拿到的 `file_token`**;**禁止写 http(s) 外链 URL**:飞书 slides 渲染端不会代理外链图片,外链 src 通常显示破图。要用网图必须先下载到 CWD 内、再走上传流程
|
||||
- 单张图片最大 20 MB(slides upload API 不支持分片上传)
|
||||
- 校验阶段就会检查所有占位符文件存在及大小;缺文件或超限直接报错,不会创建空白 PPT 占位
|
||||
- 创空白 PPT → 上传所有图 → 替换 token → 逐页创建 slide,按这个顺序执行
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **路径必须在 CWD 内**:`@/abs/path/x.png` 或 `@../up/x.png` 这种会被 CLI 拒绝(报 `unsafe file path`)。如果素材在别的目录,先 `cd` 过去再执行。
|
||||
|
||||
### 给已有 PPT 加带图新页
|
||||
|
||||
`+create --slides` 只在新建 PPT 时使用 `@` 占位符。给已有 PPT 加带图新页要分两步(CLI 没封装这个组合):
|
||||
|
||||
```bash
|
||||
# 1) 上传图片
|
||||
TOKEN=$(lark-cli slides +media-upload --as user \
|
||||
--file ./pic.png --presentation $PRES_ID | jq -r .data.file_token)
|
||||
|
||||
# 2) 用返回的 file_token 创建带图新页
|
||||
lark-cli slides xml_presentation.slide create --as user \
|
||||
--params "{\"xml_presentation_id\":\"$PRES_ID\"}" \
|
||||
--data "{\"slide\":{\"content\":\"<slide xmlns=\\\"http://www.larkoffice.com/sml/2.0\\\"><data><img src=\\\"$TOKEN\\\" topLeftX=\\\"100\\\" topLeftY=\\\"100\\\" width=\\\"200\\\" height=\\\"200\\\"/></data></slide>\"}}"
|
||||
```
|
||||
|
||||
## 创建后续步骤
|
||||
|
||||
如果没有使用 `--slides`,`slides +create` 返回的 `xml_presentation_id` 用于后续操作:
|
||||
|
||||
```bash
|
||||
# 第 1 步:创建空白 PPT
|
||||
PRES_ID=$(lark-cli slides +create --title "项目汇报" | jq -r '.data.xml_presentation_id')
|
||||
|
||||
# 第 2 步:添加页面(使用返回的 xml_presentation_id)
|
||||
lark-cli slides xml_presentation.slide create --as user \
|
||||
--params "{\"xml_presentation_id\":\"$PRES_ID\"}" \
|
||||
--data '{
|
||||
"slide": {
|
||||
"content": "<slide xmlns=\"http://www.larkoffice.com/sml/2.0\">...</slide>"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## 常见错误
|
||||
|
||||
| 错误码 | 含义 | 解决方案 |
|
||||
|--------|------|----------|
|
||||
| 400 | 参数错误 | 检查参数格式是否正确 |
|
||||
| 403 | 权限不足 | 检查是否拥有 `slides:presentation:create` 和 `slides:presentation:write_only` scope |
|
||||
|
||||
## 相关命令
|
||||
|
||||
- [slides +xml-get](lark-slides-xml-get.md) — 读取 PPT 内容并保存到本地文件
|
||||
@@ -1,144 +0,0 @@
|
||||
# 编辑已有 PPT:读-改-写闭环
|
||||
|
||||
局部编辑走 **shortcut [`+replace-slide`](lark-slides-replace-slide.md)**(块级替换 / 插入),配合 `xml_presentation.slide.get` 读原页拿 `block_id`。已有 Slides 的多页整页重建走 **[`+replace-pages`](lark-slides-replace-pages.md)**,保持原 presentation 链接不变。
|
||||
|
||||
> 生成 XML 前**必读** [xml-schema-quick-ref.md](xml-schema-quick-ref.md)。
|
||||
|
||||
## 决策树:block_replace vs block_insert
|
||||
|
||||
| 需求 | 推荐 action | 理由 |
|
||||
|------|------------|------|
|
||||
| 已知某块的 `block_id`,要换这块内容(改标题、换图、挪坐标) | `block_replace` | 精准替换,原子性好;`replacement` 根 `id` 由 CLI 自动注入为 `block_id` |
|
||||
| 只加 1~N 个元素、不动现有布局 | `block_insert` | 新增不覆盖,可选 `insert_before_block_id` 指定位置 |
|
||||
| 一次动多个元素(如:换标题 + 加图) | 单次 `--parts` 里拼多条 | 整批作为原子事务,任一失败整批不生效;`block_replace` 和 `block_insert` 可混用 |
|
||||
| 多页版式重建、整页坐标重排 | `+replace-pages` | 原 presentation 内批量 create-before/delete-old,不生成新 Slides 链接 |
|
||||
|
||||
> **没有字段级 patch**:即便只想改一个 `shape` 的 `topLeftX`,也得把整个块的新 XML 写出来用 `block_replace`。这不是"微调",是块级重写。
|
||||
|
||||
## 最小读-改-写闭环
|
||||
|
||||
```bash
|
||||
PID="xml_presentation_id_here"
|
||||
SID="slide_id_here"
|
||||
|
||||
# 1. 读原页,从 XML 里挑出要改的块的 3 位 short id(如 bUn / bab)
|
||||
lark-cli slides xml_presentation.slide get --as user \
|
||||
--params "{\"xml_presentation_id\":\"$PID\",\"slide_id\":\"$SID\"}"
|
||||
|
||||
# 2. 用 +replace-slide 直接改那个块(不需要搬原 XML)
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation "$PID" --slide-id "$SID" \
|
||||
--parts '[{"action":"block_replace","block_id":"bUn","replacement":"<shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>新标题</p></content></shape>"}]'
|
||||
```
|
||||
|
||||
`slide_id` / 页序不会变。`block_replace` 的 `replacement` 根元素 `id` 会自动注入为 `block_id`,用户手写 XML 时不需要自己加。
|
||||
|
||||
## `revision_id` 参数
|
||||
|
||||
`--revision-id` 默认 `-1`,表示基于当前最新版执行。传具体版本号时,服务端以该版本为 base 应用变更:
|
||||
|
||||
```bash
|
||||
# 读时拿当前 revision_id
|
||||
REV=$(lark-cli slides xml_presentation.slide get --as user \
|
||||
--params "{\"xml_presentation_id\":\"$PID\",\"slide_id\":\"$SID\"}" \
|
||||
| jq '.data.revision_id')
|
||||
|
||||
# 写时传该版本号,服务端以此为 base
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation "$PID" --slide-id "$SID" --revision-id "$REV" \
|
||||
--parts '[{"action":"block_replace","block_id":"bUn","replacement":"<shape type=\"rect\" topLeftX=\"100\" topLeftY=\"100\" width=\"200\" height=\"100\"/>"}]'
|
||||
```
|
||||
|
||||
注意:传不存在的版本号(超过当前 revision)会返回 3350002 not found;不确定时用 `-1` 即可。
|
||||
|
||||
## `--tid` 事务锁
|
||||
|
||||
跨请求的并发事务 ID,多人协作长事务才用得上。**单人单次调用留空**即可。
|
||||
|
||||
## 两种 action 详解
|
||||
|
||||
### block_replace — 整块替换
|
||||
|
||||
适合"已知块 ID,要换这块整体内容"的场景。`replacement` 根元素的 `id="<block_id>"` 由 CLI 自动注入(用户手写的 XML 如果没带 `id` 直接省略即可;如果带了错的会被覆盖为正确值)。
|
||||
|
||||
```bash
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation "$PID" --slide-id "$SID" \
|
||||
--parts '[{"action":"block_replace","block_id":"bab","replacement":"<shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>新标题</p></content></shape>"}]'
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `action` | 是 | 固定为 `block_replace` |
|
||||
| `block_id` | 是 | 目标块的 3 位 short element ID(从 `slide.get` 返回的 XML 里读)|
|
||||
| `replacement` | 是 | 新 XML 片段;根元素 `id` 会被 CLI 自动注入为 `block_id` |
|
||||
|
||||
### block_insert — 整块插入
|
||||
|
||||
适合"只想加一个元素,不动现有元素"的场景(典型:给已有页加图)。
|
||||
|
||||
```bash
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation "$PID" --slide-id "$SID" \
|
||||
--parts "$(jq -n --arg token "$FILE_TOKEN" \
|
||||
'[{action:"block_insert",insertion:("<img src=\""+$token+"\" topLeftX=\"500\" topLeftY=\"100\" width=\"200\" height=\"150\"/>"),insert_before_block_id:"baa"}]')"
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `action` | 是 | 固定为 `block_insert` |
|
||||
| `insertion` | 是 | 要插入的完整 XML 片段 |
|
||||
| `insert_before_block_id` | 否 | 插到这个块之前;省略(不提供此字段)则追加到页面末尾 |
|
||||
|
||||
> **`<img>` 必须用 `file_token`**,不能用外链 URL——先 `slides +media-upload --file ./pic.png --presentation $PID` 拿 token。
|
||||
|
||||
### 批量 parts
|
||||
|
||||
一次 `--parts` 最多 200 条,按数组顺序串行执行。`block_replace` 和 `block_insert` 可以在同一批次混用。举例:一次性把标题块替换、然后在末尾追加一个装饰图。
|
||||
|
||||
```bash
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation "$PID" --slide-id "$SID" \
|
||||
--parts '[
|
||||
{"action":"block_replace","block_id":"bab","replacement":"<shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>新标题</p></content></shape>"},
|
||||
{"action":"block_insert","insertion":"<img src=\"<file_token>\" topLeftX=\"700\" topLeftY=\"400\" width=\"180\" height=\"100\"/>"}
|
||||
]'
|
||||
```
|
||||
|
||||
整批作为原子事务:任一条失败整批不生效。失败时后端通常返回 3350001;若响应中带 `failed_part_index` / `failed_reason` 字段,shortcut 会原样透传。
|
||||
|
||||
## 大 --parts 用 jq 或 stdin 组装
|
||||
|
||||
`--parts` 支持 `@file`(读文件)和 `-`(stdin)作为值来源,适合批量 XML 场景:
|
||||
|
||||
```bash
|
||||
# 从文件读
|
||||
lark-cli slides +replace-slide --as user --presentation "$PID" --slide-id "$SID" \
|
||||
--parts @parts.json
|
||||
|
||||
# 从 stdin 读
|
||||
cat parts.json | lark-cli slides +replace-slide --as user --presentation "$PID" --slide-id "$SID" \
|
||||
--parts -
|
||||
```
|
||||
|
||||
## 错误排查
|
||||
|
||||
| 现象 | 原因 | 对策 |
|
||||
|------|------|------|
|
||||
| 3350001,hint 含 "block_id not found" | `parts[i].block_id` 在当前页不存在 | 重新 `slide.get` 拿最新 XML,按里面的 short ID 再填 |
|
||||
| 3350002 not found | `--revision-id` 传了不存在的版本号 | 用 `-1` 或实际存在的 `revision_id` |
|
||||
| `<img>` 不显示 / 显示破图 | `src` 写了外链 URL | 换成通过 `+media-upload` 拿到的 `file_token` |
|
||||
| 3350001(block_replace 返回) | 正常情况下 CLI 已自动注入 `id` 和 `<content/>`;如果仍报错,确认 `block_id` 在当前页存在(重新 `slide.get`),检查 XML 结构是否合法;坐标是否超出 960×540 范围 | — |
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [lark-slides-replace-slide.md](lark-slides-replace-slide.md) — +replace-slide shortcut 参数详情
|
||||
- [lark-slides-replace-pages.md](lark-slides-replace-pages.md) — 多页整页重建 shortcut
|
||||
- [lark-slides-xml-presentation-slide-get.md](lark-slides-xml-presentation-slide-get.md) — slide.get 参考(拿 `block_id` / `revision_id`)
|
||||
- [lark-slides-xml-presentation-slide-replace.md](lark-slides-xml-presentation-slide-replace.md) — 底层 replace API 参考(一般直接用 shortcut 即可)
|
||||
- [lark-slides-media-upload.md](lark-slides-media-upload.md) — 上传图片拿 file_token
|
||||
- [xml-schema-quick-ref.md](xml-schema-quick-ref.md) — XML 元素和属性速查
|
||||
@@ -1,127 +0,0 @@
|
||||
|
||||
# slides +media-upload(上传本地图片到飞书幻灯片)
|
||||
|
||||
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
|
||||
|
||||
把本地图片上传到指定演示文稿的 drive 媒体库,返回 `file_token`。**返回的 token 作为 `<img src="...">` 的值塞进 slide XML 即可显示图片。**
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 直接传 xml_presentation_id
|
||||
lark-cli slides +media-upload --as user \
|
||||
--file ./pic.png \
|
||||
--presentation slidesXXXXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
# 传 slides URL 也行
|
||||
lark-cli slides +media-upload --as user \
|
||||
--file ./chart.png \
|
||||
--presentation "https://xxx.feishu.cn/slides/slidesXXXXXXXXXXXXXXXXXXXXXX"
|
||||
|
||||
# 传 wiki URL(CLI 自动 wiki.spaces.get_node 解析为真实 token,校验 obj_type=slides)
|
||||
lark-cli slides +media-upload --as user \
|
||||
--file ./pic.png \
|
||||
--presentation "https://xxx.feishu.cn/wiki/wikcnXXXXXX"
|
||||
|
||||
# 预览(不实际上传)
|
||||
lark-cli slides +media-upload --file ./pic.png --presentation $PRES_ID --dry-run
|
||||
```
|
||||
|
||||
## 返回值
|
||||
|
||||
```json
|
||||
{
|
||||
"file_token": "boxcnXXXXXXXXXXXXXXXXXXXXXX",
|
||||
"file_name": "pic.png",
|
||||
"size": 12345,
|
||||
"presentation_id": "slidesXXXXXXXXXXXXXXXXXXXXXX"
|
||||
}
|
||||
```
|
||||
|
||||
- **`file_token`**:把它写进 `<img src="...">`
|
||||
- **`file_name` / `size`**:上传文件元信息
|
||||
- **`presentation_id`**:解析后的真实 `xml_presentation_id`(wiki URL 解析后会变化)
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--file` | 是 | 本地图片路径,**必须是 CWD 内的相对路径**(如 `./pic.png`)。**最大 20 MB**(slides upload API 不支持分片上传) |
|
||||
| `--presentation` | 是 | `xml_presentation_id`、`/slides/<token>` URL,或 `/wiki/<token>` URL |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **路径必须在 CWD 内**:`--file /abs/path/x.png` 或 `--file ../up/x.png` 会被 CLI 拒绝(报 `unsafe file path`)。如果素材在别的目录,先 `cd` 过去再执行。
|
||||
|
||||
## 使用流程
|
||||
|
||||
### 给已有 PPT 加带图新页
|
||||
|
||||
```bash
|
||||
# 1) 上传图片
|
||||
TOKEN=$(lark-cli slides +media-upload --as user \
|
||||
--file ./pic.png \
|
||||
--presentation $PRES_ID | jq -r .data.file_token)
|
||||
|
||||
# 2) 用 file_token 创建带图新页
|
||||
lark-cli slides xml_presentation.slide create --as user \
|
||||
--params "{\"xml_presentation_id\":\"$PRES_ID\"}" \
|
||||
--data "{\"slide\":{\"content\":\"<slide xmlns=\\\"http://www.larkoffice.com/sml/2.0\\\"><data><img src=\\\"$TOKEN\\\" topLeftX=\\\"100\\\" topLeftY=\\\"100\\\" width=\\\"320\\\" height=\\\"180\\\"/></data></slide>\"}}"
|
||||
```
|
||||
|
||||
### 新建带图 PPT(推荐用 `+create --slides` 的 `@` 占位符,一步到位)
|
||||
|
||||
```bash
|
||||
# 不需要单独 +media-upload,写 src="@<本地路径>" 即可
|
||||
lark-cli slides +create --as user --title "图测试" --slides '[
|
||||
"<slide xmlns=\"http://www.larkoffice.com/sml/2.0\"><data><img src=\"@./pic.png\" topLeftX=\"100\" topLeftY=\"100\" width=\"320\" height=\"180\"/></data></slide>"
|
||||
]'
|
||||
```
|
||||
|
||||
详见 [+create 文档](lark-slides-create.md#本地图片path-占位符)。
|
||||
|
||||
### 给已有 PPT 的已有页加图
|
||||
|
||||
拿到 `file_token` 后走 [`+replace-slide`](lark-slides-replace-slide.md) 的 `block_insert`,不用搬原 XML、不改 `slide_id`、不打乱页序:
|
||||
|
||||
```bash
|
||||
PRES_ID=xxx
|
||||
SID=yyy # 要加图的那一页
|
||||
|
||||
# 1) 上传图片拿 file_token
|
||||
TOKEN=$(lark-cli slides +media-upload --as user \
|
||||
--file ./pic.png --presentation $PRES_ID | jq -r '.data.file_token')
|
||||
|
||||
# 2) block_insert 到页末(或用 insert_before_block_id 指定插入位置)
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation "$PRES_ID" --slide-id "$SID" \
|
||||
--parts "$(jq -n --arg token "$TOKEN" \
|
||||
'[{action:"block_insert",insertion:("<img src=\""+$token+"\" topLeftX=\"500\" topLeftY=\"100\" width=\"200\" height=\"150\"/>")}]')"
|
||||
```
|
||||
|
||||
注意事项:
|
||||
|
||||
1. **`<img>` 坐标避开现有元素** —— 先读现有元素 bbox 挑空白区;空间不够就先用 `block_replace` 挪动/缩小现有元素后再放图
|
||||
2. **`<img>` 的 `width:height` 对齐原图比例** —— 比例不一致会被裁剪,参见 [xml-schema-quick-ref.md](xml-schema-quick-ref.md) `<img>` 说明
|
||||
|
||||
## 工作原理
|
||||
|
||||
`+media-upload` 内部调用 `POST /open-apis/drive/v1/medias/upload_all`(单次上传,最大 20 MB),固定使用:
|
||||
|
||||
- `parent_type=slide_file`(slides 后端唯一接受的取值,已实测验证)
|
||||
- `parent_node=<xml_presentation_id>`
|
||||
|
||||
**不要尝试用 `slides_image`、`slide_image` 等 parent_type**——后端会返回 1061001 / 1061002 错误。这是 slides 的特殊约定。
|
||||
|
||||
## 常见错误
|
||||
|
||||
| 错误码 | 含义 | 解决方案 |
|
||||
|--------|------|----------|
|
||||
| 1061002 | params error / 不支持的 parent_type | 不要用原生 API 自己拼 parent_type;用 `+media-upload` 即可 |
|
||||
| 1061004 | forbidden:当前身份对该演示文稿无编辑权限 | 确认当前身份(user 或 bot)对目标 PPT 有编辑权限。bot 模式常见原因:PPT 不是该 bot 创建的——可用 `+create --as bot` 新建,或以 user 身份给 bot 授权 `lark-cli drive permission.members create --as user ...` |
|
||||
| 1061044 | parent node not exist | `--presentation` 给的 token 不对,或不是 slides 类型 |
|
||||
| 403 | 权限不足 | 检查 `docs:document.media:upload` scope;wiki URL 还需要 `wiki:node:read` |
|
||||
|
||||
## 相关命令
|
||||
|
||||
- [+create](lark-slides-create.md) — 新建 PPT(支持 `@` 占位符自动上传图片)
|
||||
- [+replace-slide](lark-slides-replace-slide.md) — 给已有页加图 / 换图(`block_insert` / `block_replace`)
|
||||
@@ -1,89 +0,0 @@
|
||||
# PPT Template Rewrite Principles
|
||||
|
||||
本页只约束“用户指定 PPT 模板、底稿、已有 PPTX/PDF/Slides,并要求基于它二次创作”的场景。核心原则:模板不是风格参考,而是必须沿用的编辑底稿。
|
||||
|
||||
## Import First
|
||||
|
||||
用户指定 PPT 模板时,先把模板导入成 Lark Slides。后续写入目标是导入后的 Slides,不是新建一个脱离模板的 deck,也不是先在本地重画 PPTX 再导入。
|
||||
|
||||
直接使用以下命令,不需要先加载 `lark-drive` skill:
|
||||
|
||||
```bash
|
||||
lark-cli drive +import --as user --file "<template.pptx>" --type slides --json
|
||||
```
|
||||
|
||||
可选参数:用 `--name "<title>"` 指定导入后的 Slides 标题;用 `--folder-token <FOLDER_TOKEN>` 指定目标文件夹。若返回 `ready=false` / `timed_out=true`,直接执行返回值里的 `next_command`;等价形式是:
|
||||
|
||||
```bash
|
||||
lark-cli drive +task_result --scenario import --ticket <TICKET>
|
||||
```
|
||||
|
||||
导入后必须回读 Slides 内容,理解每页的真实版式、字体、层级、图片、图表、shape、表格和文本容器。回读结果是模板二创的事实来源。
|
||||
|
||||
## Read Before Editing
|
||||
|
||||
编辑任何 PPT 页面前,必须先阅读该页面。
|
||||
|
||||
如果当前上下文中没有该页内容,必须重新读取页面;这里的“当前上下文”不包含 System Prompt。不能只凭记忆、文件名、缩略图印象或模板整体风格判断来编辑具体页面。
|
||||
|
||||
阅读页面时至少判断:
|
||||
|
||||
- 该页原本承担的角色,例如封面、章节页、目录、流程、对比、数据、总结。
|
||||
- 该页的主要版式结构,例如图文关系、箭头、时间线、节点、表格、图表、左右对照、背景图或产品图。
|
||||
- 哪些文本框、shape 标签、表格单元格或图表标签承载内容。
|
||||
- 原页面的字体、字号、颜色、对齐、层级和留白关系。
|
||||
|
||||
## Edit The Imported Slides Directly
|
||||
|
||||
理解页面后,直接在导入后的 Slides 上编辑。允许的操作包括:
|
||||
|
||||
- 填写、替换、凝练或删除文字。
|
||||
- 替换或补充图片。
|
||||
- 更新图表、表格、数字标签或节点标签里的内容。
|
||||
- 按需复制、删除或重排模板页。
|
||||
- 在源页面没有合适承载位置时,做局部、小范围新增元素。
|
||||
|
||||
新增元素只能补足内容缺口,不能成为新的主版式。页面主体仍应由模板原有版式承载。
|
||||
|
||||
## Preserve Design
|
||||
|
||||
模板二创必须严格沿用原版式和字体,只改内容,不做设计。
|
||||
|
||||
默认保留:
|
||||
|
||||
- 页面布局、视觉层级、留白和对齐关系。
|
||||
- 原字体、字号体系、颜色、文本框位置和 shape 顺序。
|
||||
- 背景图、图片、logo、图表、表格、装饰形状、线条、图标和页面结构。
|
||||
- 模板中不同页型之间的差异。
|
||||
|
||||
不要把模板页改造成统一的通用卡片、白板、标题栏、三栏、2x2 卡片或大面积遮罩。不要把模板当作背景图后另起一套设计系统。
|
||||
|
||||
## Content Only
|
||||
|
||||
内容必须优先进入原页面已有的文本框、shape 标签、节点、表格单元格、图表标签或注释容器。
|
||||
|
||||
如果原容器空间不足,优先:
|
||||
|
||||
- 凝练文字。
|
||||
- 降低字号但保持原字体体系。
|
||||
- 拆分到页面已有的邻近容器。
|
||||
- 使用模板已有的注释、标签或补充说明区域。
|
||||
- 复制同页或同模板中的原生容器样式做局部补充。
|
||||
|
||||
不要为了容纳长文案而重画页面主体结构。不要用新增大卡片遮住原图表、箭头、图片、背景或关键 shape。
|
||||
|
||||
## Readback And Tune
|
||||
|
||||
完成编辑后必须回读结果,并逐页微调。
|
||||
|
||||
回读时重点检查:
|
||||
|
||||
- 文字是否溢出、截断、压线或超出容器。
|
||||
- 文本是否遮挡图片、图表、shape、箭头、节点或其他文字。
|
||||
- shape 顺序是否导致内容被覆盖或遮住。
|
||||
- 新内容是否仍然落在模板原有版式中,而不是覆盖模板结构。
|
||||
- 字体、字号、颜色、对齐和层级是否仍贴近原页。
|
||||
|
||||
发现文字溢出时,优先凝练文字或缩减字号。发现遮挡时,调整 shape 顺序、局部位置或复用原有空白区域解决。只有在这些方法都不能满足内容表达时,才做局部新增或删除。
|
||||
|
||||
模板二创的完成标准不是“生成了一套看起来统一的新 PPT”,而是“原模板的版式、字体和视觉结构仍清晰存在,内容已经被准确替换,并且回读后没有溢出和遮挡”。
|
||||
@@ -1,95 +0,0 @@
|
||||
# slides +replace-pages(多页整页重建)
|
||||
|
||||
批量替换已有演示文稿里的多个页面,保持原 `xml_presentation_id` 和原 Slides 链接不变。适合多页版式大改、坐标重排、整页视觉重建;单个文本框、图片或 shape 的局部编辑仍优先用 [`+replace-slide`](lark-slides-replace-slide.md)。
|
||||
|
||||
> 重要:这是多步编排,不是后端原子事务。CLI 对每页执行“先创建新页到旧页前,再删除旧页”;创建失败时旧页会保留。删除失败时可能出现新旧页同时存在,需要按返回结果继续处理。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
lark-cli slides +replace-pages \
|
||||
--as user \
|
||||
--presentation <slides_url_or_xml_presentation_id> \
|
||||
--pages @pages.json
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `--presentation` | 是 | `xml_presentation_id`、`/slides/` URL 或 `/wiki/` URL |
|
||||
| `--pages` | 是 | JSON 数组,每项包含 `slide_id` 和 `content`;支持 literal、`@file`、stdin `-` |
|
||||
| `--dry-run` | 否 | 基于 `slide_id` 输入输出替换计划,不执行 create/delete |
|
||||
| `--continue-on-error` | 否 | 默认失败即停;开启后继续处理后续页,并在结果中标记失败项 |
|
||||
| `--validate-only` | 否 | 只校验输入并生成替换计划,不执行 Slides get/create/delete |
|
||||
|
||||
## pages.json
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"slide_id": "slide_short_id_1",
|
||||
"content": "<slide xmlns=\"http://www.larkoffice.com/sml/2.0\"><data></data></slide>"
|
||||
},
|
||||
{
|
||||
"slide_id": "slide_short_id_2",
|
||||
"content": "<slide xmlns=\"http://www.larkoffice.com/sml/2.0\"><data></data></slide>"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 每项必须提供 `slide_id`;不支持 `slide_number`。
|
||||
- `content` 必须是完整 `<slide>...</slide>` XML。
|
||||
- 同一批次不能重复 `slide_id`。
|
||||
- CLI 不会回读整份 presentation;如果 `slide_id` 已失效,create/delete 阶段会返回对应错误。
|
||||
|
||||
## Dry Run
|
||||
|
||||
```bash
|
||||
lark-cli slides +replace-pages --as user \
|
||||
--presentation "$PID" \
|
||||
--pages @pages.json \
|
||||
--dry-run
|
||||
```
|
||||
|
||||
输出包含 `xml_presentation_id`、`pages_count`、`plan`,以及每页的 `old_slide_id`、`insert_before_slide_id` 和动作 `create_before_then_delete_old`。Dry-run 只基于输入的 `slide_id` 构造计划,不会调用 `xml_presentations.get`,也不会执行 create/delete。
|
||||
|
||||
## 成功输出
|
||||
|
||||
```json
|
||||
{
|
||||
"xml_presentation_id": "xxx",
|
||||
"pages_count": 2,
|
||||
"status": "completed",
|
||||
"summary": {
|
||||
"replaced": 2,
|
||||
"failed": 0,
|
||||
"total": 2
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"old_slide_id": "old3",
|
||||
"new_slide_id": "new3",
|
||||
"status": "replaced"
|
||||
}
|
||||
],
|
||||
"revision_id": 123
|
||||
}
|
||||
```
|
||||
|
||||
如果使用 `--continue-on-error` 且任一页面失败,CLI 会继续处理后续页,但最终以 partial failure 非零退出;stdout 仍保留完整 `results`,顶层 `ok` 为 `false`,`status` 为 `partial_failure`。
|
||||
|
||||
`status` 可能为:
|
||||
|
||||
- `replaced`:新页创建成功,旧页删除成功。
|
||||
- `create_failed`:新页创建失败,旧页保留。
|
||||
- `delete_failed`:新页已创建,但旧页删除失败。
|
||||
|
||||
## 使用建议
|
||||
|
||||
1. 大幅改写前先 `slides +xml-get` 保存当前 XML,并记录要替换页面的 `slide_id`。
|
||||
2. 生成只含 `slide_id` 的 `pages.json` 后先跑 `--dry-run` 或 `--validate-only`。
|
||||
3. 默认不要开 `--continue-on-error`,除非能接受部分页面已替换。
|
||||
4. 替换后再回读全文 XML 并截图检查,确认页序、视觉和文本没有破损。
|
||||
@@ -1,240 +0,0 @@
|
||||
# slides +replace-slide(块级替换 / 插入)
|
||||
|
||||
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
|
||||
|
||||
对指定 slide 做块级替换或插入。编辑已有 PPT 的主路径——`slide_id` 不变、页序不动、只影响被指定的块。
|
||||
|
||||
相比直接调 `xml_presentation.slide.replace`,这个 shortcut 的四个额外价值:
|
||||
|
||||
1. `--presentation` 接受 `xml_presentation_id` / `/slides/` URL / `/wiki/` URL(wiki 自动解析);
|
||||
2. `block_replace` 的 `replacement` 根元素 `id="<block_id>"` 由 CLI 自动注入——底层 API 的硬约束(不注入返回 3350001);直接调原生 API 需自己加,用 Shortcut 则自动注入;
|
||||
3. `<shape>` 元素缺少 `<content/>` 子元素时由 CLI 自动注入——SML 2.0 schema 要求每个 `<shape>` 必须有 `<content/>` 子元素,缺失同样触发 3350001;自闭合的 `<shape .../>` 也会被自动展开为 `<shape ...><content/></shape>`;
|
||||
4. 3350001 错误时提供上下文感知的 hint,帮助 AI agent 和用户快速定位原因。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# block_insert:在页末追加一个新元素
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation slidesXXXXXXXXXXXXXXXXXXXXXX \
|
||||
--slide-id pfG \
|
||||
--parts '[{"action":"block_insert","insertion":"<shape type=\"rect\" topLeftX=\"500\" topLeftY=\"100\" width=\"200\" height=\"100\"/>"}]'
|
||||
|
||||
# block_replace:已知某块 id,整块替换(replacement 根 id 自动注入为 bUn)
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation slidesXXXXXXXXXXXXXXXXXXXXXX \
|
||||
--slide-id pfG \
|
||||
--parts '[{"action":"block_replace","block_id":"bUn","replacement":"<shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>新标题</p></content></shape>"}]'
|
||||
|
||||
# 大 --parts 走文件或 stdin(auto-gen 命令不支持 @file,但 shortcut 支持)
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation $PID --slide-id $SID --parts @parts.json
|
||||
cat parts.json | lark-cli slides +replace-slide --as user \
|
||||
--presentation $PID --slide-id $SID --parts -
|
||||
|
||||
# wiki URL 直接传(CLI 自动 get_node → 拿真实 xml_presentation_id)
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation "https://xxx.feishu.cn/wiki/wikcnXXXXXX" --slide-id pfG \
|
||||
--parts '[{"action":"block_insert","insertion":"<shape type=\"rect\" width=\"100\" height=\"100\"/>"}]'
|
||||
|
||||
# 预览(不实际调用)
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation $PID --slide-id $SID --parts "$PARTS" --dry-run
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--presentation` | 是 | `xml_presentation_id`、`/slides/<token>` URL,或 `/wiki/<token>` URL |
|
||||
| `--slide-id` | 是 | 页面 ID(`xml_presentation.slide.get` / `slides +xml-get` 都能拿到) |
|
||||
| `--parts` | 是 | JSON 数组(`[{...}, ...]`),单次最多 200 条。支持 `@<file>` 和 `-`(stdin)读取 |
|
||||
| `--revision-id` | 否 | 基础版本号;默认 `-1` 表示基于最新版执行;传具体版本号时,服务端以该版本为 base 执行;**传不存在的版本号(超过当前 revision)返回 3350002** |
|
||||
| `--tid` | 否 | 并发事务 ID;多人协作长事务才用,单次单人调用留空 |
|
||||
|
||||
## parts 元素结构
|
||||
|
||||
> **限制**:最多 200 条;`block_replace` 和 `block_insert` 可以在同一批次混用。**其他 action(含 `str_replace`)CLI 会直接报错拒绝**。
|
||||
|
||||
每条 part 按 `action` 取不同字段:
|
||||
|
||||
### action = `block_replace`
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `action` | 是 | `"block_replace"` |
|
||||
| `block_id` | 是 | 目标块的 3 位 short element ID(从 `slide.get` 返回 XML 里读) |
|
||||
| `replacement` | 是 | 新 XML 片段;**根元素 `id` 会被 CLI 自动注入为 `block_id`**,用户不用自己加(如果已经加了且不一致会被覆盖为正确值) |
|
||||
|
||||
### action = `block_insert`
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `action` | 是 | `"block_insert"` |
|
||||
| `insertion` | 是 | 要插入的 XML 片段 |
|
||||
| `insert_before_block_id` | 否 | 插到这个块之前;省略(不提供此字段)则追加到页末 |
|
||||
|
||||
## 合法根元素速查
|
||||
|
||||
`block_replace.replacement` 和 `block_insert.insertion` 必须以 SML 2.0 定义的合法元素为根。完整权威定义看 [`slides_xml_schema_definition.xml`](slides_xml_schema_definition.xml);这里只列能作为**根**的类型 + 每种类型的最小可工作片段。
|
||||
|
||||
| 元素 | 用途 | 关键点 |
|
||||
|---|---|---|
|
||||
| `<shape>` | 矩形/椭圆/三角/文本框等所有形状 | `type` 必填;`<content/>` 缺失时 CLI 会自动注入 |
|
||||
| `<line>` | 直线 | 需 `startX/startY/endX/endY` |
|
||||
| `<polyline>` | 折线 | `points` 读回时被服务端规整丢弃(几何已入库) |
|
||||
| `<img>` | 图片 | `src` 必须是 [`+media-upload`](lark-slides-media-upload.md) 返回的 `file_token`,不能是 URL |
|
||||
| `<icon>` | 图标 | `iconType` 取自 iconpark 资源;语义图标先用 `scripts/iconpark_tool.py search` 检索 |
|
||||
| `<table>` | 表格 | 整表替换会**重建内部 td id**,旧 td block_id 立即失效 |
|
||||
| `<td>` | 单元格局部替换 | 只能 `block_replace`,不能 `block_insert`;`block_id` 必须是最新 `slide.get` 拿到的 td id |
|
||||
| `<chart>` | 图表(line/bar/column/pie/area/radar/combo) | 必须嵌 `<chartPlotArea>` + `<chartData>` + `<dim1>/<dim2>/<chartField>` |
|
||||
| `<whiteboard>` | 画板(SVG 或 Mermaid) | 内嵌 `<svg>` 或 `<mermaid>`;`slide.get` 返回结构不含内部数据,但可直接写完整新 XML 做 `block_replace` 覆盖;详见 [`lark-slides-whiteboard.md`](lark-slides-whiteboard.md) |
|
||||
|
||||
**不可作为根元素**:
|
||||
|
||||
- `<video>` / `<audio>` —— SML 2.0 没有这两个原生元素;`<undefined type="video|audio">` 是**导出时**的占位符(服务端遇到不支持的类型时用它代替),**不能写入**。尝试 insert/replace 都会返回 3350001。
|
||||
|
||||
### 最小 XML 片段(JSON 嵌入时记得把 `"` 转义成 `\"`)
|
||||
|
||||
`<shape>`(文本框;`type` 还可选 `rect`/`ellipse`/`triangle`/`custom` 等):
|
||||
```xml
|
||||
<shape type="text" topLeftX="80" topLeftY="80" width="800" height="120">
|
||||
<content textType="title"><p>标题</p></content>
|
||||
</shape>
|
||||
```
|
||||
|
||||
`<img>`:
|
||||
```xml
|
||||
<img src="{file_token}" topLeftX="600" topLeftY="20" width="80" height="80"/>
|
||||
```
|
||||
|
||||
`<polyline>`:
|
||||
```xml
|
||||
<polyline topLeftX="10" topLeftY="10" width="100" height="50" points="0,0 50,50 100,0"/>
|
||||
```
|
||||
|
||||
`<table>`(2×2):
|
||||
```xml
|
||||
<table topLeftX="30" topLeftY="80">
|
||||
<colgroup><col span="2" width="110"/></colgroup>
|
||||
<tr><td><content><p>A</p></content></td><td><content><p>B</p></content></td></tr>
|
||||
<tr><td><content><p>C</p></content></td><td><content><p>D</p></content></td></tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
`<td>`(`block_replace` 单元格;`block_id` 必须是最新 `slide.get` 拿到的 td id):
|
||||
```xml
|
||||
<td><content><p>新内容</p></content></td>
|
||||
```
|
||||
|
||||
`<chart>`(`type` 改成 `bar`/`column`/`pie`/`area`/`radar`/`combo` 切换图型):
|
||||
```xml
|
||||
<chart topLeftX="30" topLeftY="300" width="300" height="200">
|
||||
<chartPlotArea><chartPlot type="line"/></chartPlotArea>
|
||||
<chartData>
|
||||
<dim1><chartField name="x" valueType="string">Q1,Q2,Q3,Q4</chartField></dim1>
|
||||
<dim2><chartField name="Sales" valueType="number">10,20,15,30</chartField></dim2>
|
||||
</chartData>
|
||||
</chart>
|
||||
```
|
||||
|
||||
## 返回值
|
||||
|
||||
```json
|
||||
{
|
||||
"xml_presentation_id": "slidesXXXXXXXXXXXXXXXXXXXXXX",
|
||||
"slide_id": "pfG",
|
||||
"parts_count": 1,
|
||||
"revision_id": 102
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `xml_presentation_id` | 解析后的真实 token(wiki URL 解析后会变化) |
|
||||
| `slide_id` | 与入参一致 |
|
||||
| `parts_count` | 本次提交的 parts 条数 |
|
||||
| `revision_id` | 成功后的新版本号,下次做乐观锁时用 |
|
||||
| `failed_part_index` | 有部分失败时存在,指向第几条 part 失败 |
|
||||
| `failed_reason` | 失败原因文字描述 |
|
||||
|
||||
整批作为原子事务:任一 part 失败则整批不生效,服务端通过 `failed_part_index` / `failed_reason` 告诉你是哪条;按此定位修正后重发。
|
||||
|
||||
## 使用流程
|
||||
|
||||
### 给已有页加图(典型场景)
|
||||
|
||||
```bash
|
||||
PID=xxx
|
||||
SID=yyy
|
||||
|
||||
# 1) 上传图片
|
||||
TOKEN=$(lark-cli slides +media-upload --as user \
|
||||
--file ./pic.png --presentation "$PID" | jq -r '.data.file_token')
|
||||
|
||||
# 2) block_insert 到页末
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation "$PID" --slide-id "$SID" \
|
||||
--parts "$(jq -n --arg token "$TOKEN" \
|
||||
'[{action:"block_insert",insertion:("<img src=\""+$token+"\" topLeftX=\"500\" topLeftY=\"100\" width=\"200\" height=\"150\"/>")}]')"
|
||||
```
|
||||
|
||||
### 改标题(block_replace)
|
||||
|
||||
```bash
|
||||
# 先拿原页 XML,从里面找到标题块的 3 位 short id(如 bUn)
|
||||
lark-cli slides xml_presentation.slide get --as user \
|
||||
--params "{\"xml_presentation_id\":\"$PID\",\"slide_id\":\"$SID\"}"
|
||||
|
||||
# block_replace 换掉整个标题块(id 自动注入)
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation "$PID" --slide-id "$SID" \
|
||||
--parts '[{"action":"block_replace","block_id":"bUn","replacement":"<shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>新标题</p></content></shape>"}]'
|
||||
```
|
||||
|
||||
### 批量:一次换标题 + 追加装饰图
|
||||
|
||||
`block_replace` 和 `block_insert` 可以在同一个 `--parts` 里混用,整批原子执行。
|
||||
|
||||
```bash
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation "$PID" --slide-id "$SID" \
|
||||
--parts '[
|
||||
{"action":"block_replace","block_id":"bab","replacement":"<shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>新标题</p></content></shape>"},
|
||||
{"action":"block_insert","insertion":"<img src=\"<file_token>\" topLeftX=\"700\" topLeftY=\"400\" width=\"180\" height=\"100\"/>"}
|
||||
]'
|
||||
```
|
||||
|
||||
### 乐观锁
|
||||
|
||||
```bash
|
||||
# 读时记录 revision_id
|
||||
REV=$(lark-cli slides xml_presentation.slide get --as user \
|
||||
--params "{\"xml_presentation_id\":\"$PID\",\"slide_id\":\"$SID\"}" \
|
||||
| jq '.data.revision_id')
|
||||
|
||||
# 写时传 --revision-id;传不存在的版本号(超过当前 revision)返回 3350002
|
||||
lark-cli slides +replace-slide --as user \
|
||||
--presentation "$PID" --slide-id "$SID" --revision-id "$REV" \
|
||||
--parts "$PARTS"
|
||||
```
|
||||
|
||||
## 常见错误
|
||||
|
||||
| 现象 | 原因 | 对策 |
|
||||
|------|------|------|
|
||||
| 3350001 + hint "block_id not found" | `parts[i].block_id` 在当前页不存在 | 重新 `slide.get` 拿最新 XML,按里面的 short ID 再填 |
|
||||
| 3350002 not found | `--revision-id` 传了不存在的版本号(超过当前 revision) | 用 `-1` 或用 `slide.get` 拿到的有效 `revision_id` |
|
||||
| `--parts[i] action "str_replace" is not supported` | CLI 不暴露 `str_replace` | 把替换需求改写成 `block_replace` / `block_insert` |
|
||||
| `--parts contains N items, exceeds maximum of 200` | 一次提交 parts 太多 | 拆多次调用 |
|
||||
| `--parts[i] (block_replace) requires non-empty block_id` / `replacement` | 字段缺失 | 按 parts 元素结构补齐 |
|
||||
| `<img>` 不显示 / 显示破图 | `src` 写了外链 URL | 换成通过 [`+media-upload`](lark-slides-media-upload.md) 拿到的 `file_token` |
|
||||
| 3350001 | `replacement` 不是合法单根 XML 片段,或 `block_id` 不存在 | CLI 已自动注入 `id` 和 `<content/>`;如果仍报错,重新 `slide.get` 拿最新 XML 确认 `block_id` 存在;检查 XML 结构是否合法;坐标是否超出 960×540 |
|
||||
| 403 | 权限不足 | 需要 `slides:presentation:update` 或 `slides:presentation:write_only`;wiki URL 还需要 `wiki:node:read` |
|
||||
|
||||
## 相关命令
|
||||
|
||||
- [xml_presentation.slide get](lark-slides-xml-presentation-slide-get.md) — 读原页拿 `block_id` / `revision_id`
|
||||
- [xml_presentation.slide replace](lark-slides-xml-presentation-slide-replace.md) — 底层 replace API 参考
|
||||
- [+media-upload](lark-slides-media-upload.md) — 上传图片拿 `file_token`
|
||||
- [lark-slides-edit-workflows.md](lark-slides-edit-workflows.md) — 读-改-写闭环 + 决策树
|
||||
@@ -1,97 +0,0 @@
|
||||
# slides +screenshot
|
||||
|
||||
## 用途
|
||||
|
||||
获取幻灯片页面截图并保存为本地图片文件。默认用于已存在 PPT 页面截图;传入 `--content` 时用于直接渲染单个 `<slide>` XML 片段预览。本 shortcut 会在 CLI 进程内解码并写入文件,stdout 只返回文件路径、大小、页面 ID 等元信息,避免把图片 Base64 输出给模型。
|
||||
|
||||
注意:该截图能力受应用白名单限制,绝大多数应用不可用。若截图失败,记录错误即可;不要引导用户申请 `slides:presentation:screenshot` 权限。后续按 `validation-checklist.md` 走非截图验证,不要声称已完成截图验收。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
lark-cli slides +screenshot --as user \
|
||||
--presentation '<xml_presentation_id 或 slides/wiki URL>' \
|
||||
--slide-number 1
|
||||
```
|
||||
|
||||
渲染本地 XML 内容:
|
||||
|
||||
```bash
|
||||
lark-cli slides +screenshot --as user \
|
||||
--content @slide.xml
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `--presentation` | list 模式必需 | `xml_presentation_id`、`/slides/` URL,或解析后为 slides 的 `/wiki/` URL。传 `--content` 时不能使用 |
|
||||
| `--slide-id` | list 模式至少提供 `--slide-id` / `--slide-number` 之一 | 页面 short ID;多页截图时重复传入;一次最多 10 页(`--slide-id` + `--slide-number` 合计小于等于 10) |
|
||||
| `--slide-number` | list 模式至少提供 `--slide-id` / `--slide-number` 之一 | 页面页号;多页截图时重复传入;一次最多 10 页(`--slide-id` + `--slide-number` 合计小于等于 10) |
|
||||
| `--content` | render 模式必需 | 要直接渲染的 `<slide>` XML 片段;支持直接传值、`@file`、`-` stdin。传入后不能同时传 `--slide-id` / `--slide-number` |
|
||||
| `--output-dir` | 否 | 输出目录,默认 `.lark-slides/screenshots`;必须是当前目录内的相对路径 |
|
||||
| `--output-name` | 否 | render 模式的输出文件名 stem;未指定时优先用返回的 `slide_id`,否则用 `rendered-slide`。若目标文件已存在,会自动追加递增后缀避免覆盖 |
|
||||
|
||||
## 示例
|
||||
|
||||
### 单页截图
|
||||
|
||||
```bash
|
||||
lark-cli slides +screenshot --as user \
|
||||
--presentation slides_example_presentation_id \
|
||||
--slide-number 1
|
||||
```
|
||||
|
||||
### 多页截图
|
||||
|
||||
一次不要超过 10 页;如需更多页面,分批调用。
|
||||
|
||||
```bash
|
||||
lark-cli slides +screenshot --as user \
|
||||
--presentation slides_example_presentation_id \
|
||||
--slide-number 1 \
|
||||
--slide-number 2 \
|
||||
--output-dir .lark-slides/screenshots/demo
|
||||
```
|
||||
|
||||
### 渲染 XML 预览
|
||||
|
||||
```bash
|
||||
lark-cli slides +screenshot --as user \
|
||||
--content @.lark-slides/out/demo/slide.xml \
|
||||
--output-name preview
|
||||
```
|
||||
|
||||
## 返回值
|
||||
|
||||
返回 JSON 不包含 Base64 图片内容:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"output_dir": ".lark-slides/screenshots",
|
||||
"screenshots": [
|
||||
{
|
||||
"slide_id": "slide_example_id",
|
||||
"slide_number": 1,
|
||||
"format": "png",
|
||||
"path": "/abs/path/.lark-slides/screenshots/slides_example_presentation_id_p001_slide_example_id.png",
|
||||
"size": 12345
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 优先使用 `slides +screenshot` 保存本地图片,不要把图片 Base64 打到 stdout。
|
||||
2. 已存在 PPT 页面截图时,不传 `--content`,用 `--presentation` + `--slide-id` 或 `--slide-number`。
|
||||
3. 本地 XML 预览时,传 `--content @file` 或 `--content -`,内容应为单个 `<slide>` XML 片段;此时不要传 `--presentation` / `--slide-id` / `--slide-number`。
|
||||
4. `slide_id` 是页面 short ID,页码请用 `--slide-number`。
|
||||
5. list 模式一次最多传 10 页(`--slide-id` + `--slide-number` 合计小于等于 10);更多页面请分批截图。
|
||||
6. list 模式默认文件名包含 presentation ID、页码和/或 slide ID;文件已存在时自动追加 `_2`、`_3` 等后缀,避免覆盖旧截图。
|
||||
7. 截图来自服务端渲染结果,适合创建/替换后验证页面是否为空白、破图或布局明显异常。
|
||||
@@ -1,331 +0,0 @@
|
||||
# Whiteboard 画板元素
|
||||
|
||||
`<whiteboard>` 放在 `<data>` 内,内部可放 **SVG** 或 **Mermaid**,用于绘制流程图、时序图、架构图、散点图、漏斗图、自定义图标、装饰图案等 `<chart>` 和 `<shape>` 难以覆盖的视觉内容。
|
||||
|
||||
普通柱状图、条形图、折线图、面积图、雷达图、饼图 / 环图和组合图应优先使用原生 `<chart>`。除非用户明确要求像素级自定义,或图表类型确实不受 `<chart>` 支持,否则不要用 `<whiteboard>` + SVG / Mermaid 重画这些标准图表。
|
||||
|
||||
> 前置条件:使用本文档前先阅读 [lark-slides SKILL.md](../SKILL.md)。
|
||||
|
||||
---
|
||||
|
||||
## `<chart>` 还是 `<whiteboard>`?
|
||||
|
||||
**先判断内容类型,再进入本文档:**
|
||||
|
||||
| 场景 | 推荐元素 |
|
||||
|------|---------|
|
||||
| 有结构化数据序列的柱/条/折线/面积/雷达/饼/环/组合图 | `<chart>` — 原生渲染,支持 legend / tooltip / 系列配色 |
|
||||
| 散点图、漏斗图(`<chart>` 不支持)或其他非原生数据视觉 | `<whiteboard>` SVG |
|
||||
| 流程图、时序图、架构图、类图、ER 图等拓扑图 | `<whiteboard>` Mermaid 或 SVG |
|
||||
| 自定义图标、徽标、示意性图形(需要 path/polygon 精确控制) | `<whiteboard>` SVG |
|
||||
| 进度条、波浪背景、装饰图案、像素级自定义可视化 | `<whiteboard>` SVG |
|
||||
|
||||
> 适合 `<chart>` 的内容就用 `<chart>`,不要用 SVG / Mermaid 手绘——原生渲染更省力、结构更稳定,也更容易被回读和后续编辑。
|
||||
|
||||
---
|
||||
|
||||
## whiteboard 公共属性
|
||||
|
||||
| 属性 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `topLeftX` | 是 | 左上角 X 坐标(slide 坐标系,slide 默认宽 960) |
|
||||
| `topLeftY` | 是 | 左上角 Y 坐标(slide 坐标系,slide 默认高 540) |
|
||||
| `width` | 是 | 画板宽度(像素) |
|
||||
| `height` | 是 | 画板高度(像素) |
|
||||
|
||||
> SVG 模式下 `<svg>` 需声明 `xmlns="http://www.w3.org/2000/svg"`;内容大小由子元素包围盒决定,`width`/`height`/`viewBox` 不影响渲染(仅当元素属性使用百分比值时需要 `viewBox` 提供计算基准)。Mermaid 模式不需要额外属性。
|
||||
|
||||
SVG 内的坐标相对于 whiteboard 自身左上角(0,0),与 slide 坐标系无关。
|
||||
|
||||
---
|
||||
|
||||
## SVG 还是 Mermaid?
|
||||
|
||||
选择分三步:**先排除原生 `<chart>`,再判断 whiteboard 类型,最后看当前模型身份**。
|
||||
|
||||
### 第一步:先确认是否应该使用 `<chart>`
|
||||
|
||||
如果内容是柱状图、条形图、折线图、面积图、雷达图、饼图 / 环图或组合图,返回使用原生 `<chart>`,不要继续套用本文档的 SVG / Mermaid 路径。
|
||||
|
||||
### 第二步:whiteboard 类型优先判断
|
||||
|
||||
以下类型**推荐 Mermaid**,自动布局、代码简洁;如需精确匹配品牌配色或自定义节点样式,可改用 SVG:
|
||||
|
||||
| 图表类型 | Mermaid 关键字 |
|
||||
|----------|--------------|
|
||||
| 流程图、决策树、架构图 | `flowchart TD` / `flowchart LR` |
|
||||
| 时序图 | `sequenceDiagram` |
|
||||
| 类图 | `classDiagram` |
|
||||
| 甘特图 | `gantt` |
|
||||
| 状态图 | `stateDiagram-v2` |
|
||||
| 思维导图 | `mindmap` |
|
||||
| ER 图 | `erDiagram` |
|
||||
|
||||
### 第三步:非原生图表与装饰元素按模型身份选路径
|
||||
|
||||
上表以外的场景(散点图、漏斗图、进度条、时间线、波浪背景、星点纹理等)需要精确控制坐标和配色,SVG 表达力更强,但各模型生成 SVG 的能力有差异:
|
||||
|
||||
| 模型身份 | 路径 |
|
||||
|----------|------|
|
||||
| Claude / Gemini / GPT / GLM | **SVG** — 精确控制坐标、颜色、透明度 |
|
||||
| Doubao / Seed / Other | **Mermaid** — 用 `gantt`、`flowchart` 等近似表达;确实无法用 Mermaid 表达时才回退到简单 SVG 矩形/线条 |
|
||||
|
||||
> **先自报身份再选路径**:在决定使用 SVG 之前,确认当前模型属于哪一类。不要跳过这一步。
|
||||
|
||||
---
|
||||
|
||||
## 模式一:SVG
|
||||
|
||||
### ⚠️ 设计品质要求
|
||||
|
||||
在 slide 里嵌入 `<whiteboard>` 的目的是**表达原生 `<chart>` 或基础 `<shape>` 难以覆盖的视觉关系**,不是把标准数据图表手绘一遍。
|
||||
|
||||
- **不要只用矩形加文字应付**:通篇纯白底色 + 方块 + 黑字等于白做,这是不及格输出
|
||||
- **非原生数据视觉必须有坐标系**:散点、漏斗等仍要有必要的坐标轴、刻度、数值标注或分段说明,不要只画点或色块
|
||||
- **字号必须有层级**:标题 ≠ 标签 ≠ 数值,混用同一字号会消灭视觉焦点
|
||||
- **配色要与 slide 主题呼应**:深色 slide 背景下图表用透明底或深色卡片;浅色背景下避免再加纯白底块
|
||||
- **每个 whiteboard 都是设计机会**:主动用圆角、半透明填充、清晰分组、节点状态等细节拉开与默认模板的差距
|
||||
- **写 SVG 前先判断背景亮度**:背景亮度 < 30% 时,装饰元素"对比不足"比"过强"危害更大,宁重勿轻;
|
||||
- **装饰层次用亮度跳跃,不用线性叠透明度**:`α=0.04→0.08→0.12` 的等差递增在深色底上几乎看不出差异(相邻层亮度差 ≈20);正确做法是非线性跳跃如 `0.10→0.40→0.70→1.0`,相邻层亮度差 ≥60。
|
||||
|
||||
### 语法
|
||||
|
||||
```xml
|
||||
<whiteboard width="400" height="300" topLeftX="500" topLeftY="120">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="50" y="50" width="80" height="200" rx="4" fill="rgba(59,130,246,0.85)"/>
|
||||
<text x="90" y="270" text-anchor="middle" font-size="12" fill="rgba(100,116,139,1)">ABC</text>
|
||||
</svg>
|
||||
</whiteboard>
|
||||
```
|
||||
|
||||
`<svg>` 需声明 `xmlns="http://www.w3.org/2000/svg"`;`width`/`height`/`viewBox` 无需填写,若元素属性使用百分比值则需额外声明 `viewBox`。
|
||||
|
||||
### ⚠️ 渲染包围盒规则
|
||||
|
||||
whiteboard 渲染时以**所有子元素的几何包围盒合并结果**为内容区域,自适应缩放到容器。
|
||||
|
||||
`<svg>` 上的 `width`、`height`、`viewBox` 不影响内容区域的计算,但 `viewBox` 有一个实际用途:**为百分比属性提供计算基准**。若元素使用 `width="50%"` 等百分比值,必须声明 `viewBox` 才能正确解析;绝对坐标元素则无需关心。推荐统一使用绝对坐标,避免引入百分比依赖。
|
||||
|
||||
### 支持的 SVG 元素
|
||||
|
||||
| 元素 | 说明 | 典型用途 |
|
||||
|------|------|---------|
|
||||
| `<rect>` | 矩形,支持 `rx` 圆角 | 卡片、进度条、分段色块 |
|
||||
| `<circle>` | 圆 | 节点、装饰点、环形图 |
|
||||
| `<ellipse>` | 椭圆 | 自定义轮廓图形 |
|
||||
| `<line>` | 直线 | 轴线、分隔线、连接线 |
|
||||
| `<path>` | 任意路径(支持 Q/C 曲线) | 波浪、曲线、弧形 |
|
||||
| `<text>` | 文本,支持中文 | 标签、数值 |
|
||||
| `<polygon>` | 多边形 | 箭头、星形、面积填充 |
|
||||
| `<g>` | 分组 | 批量变换、语义分组 |
|
||||
| `<linearGradient>` | 线性渐变定义,配合 `fill="url(#id)"` 使用 | 渐变背景、渐变填充 |
|
||||
|
||||
**颜色:** 统一用 `rgba(R,G,B,A)`,对深浅背景都友好。
|
||||
**虚线:** `stroke-dasharray="4,4"` 用于网格线 / 坐标轴。
|
||||
**变换:** `transform="translate(x,y)"` / `rotate(deg cx cy)` / `scale(n)` 均支持。
|
||||
|
||||
---
|
||||
### 元素计算
|
||||
|
||||
SVG 中只要涉及批量定位、等间距排布或数据映射,**建议额外运行一个 Python 脚本把坐标算出来再填入 SVG**,而不是手动估值。适用范围包括散点、漏斗、装饰性点阵、等间距圆、重复图案等;普通柱状图、折线图、饼图仍应回到原生 `<chart>`。
|
||||
|
||||
> **主动去算**:写 SVG 之前先运行脚本,把输出当注释贴在 `<svg>` 开头,再照着填坐标。估值几乎每次都需要反复调整,跳过这步反而更慢。
|
||||
|
||||
**散点图 / 装饰性点阵范式**
|
||||
|
||||
```python
|
||||
W, H = 360, 260
|
||||
origin_x, origin_y = 50, 216 # 左下角,SVG Y 轴向下
|
||||
cw, ch = 290, 184
|
||||
|
||||
points = [(12, 40), (28, 80), (45, 65)]
|
||||
x_min, x_max, y_min, y_max = 0, 50, 0, 100
|
||||
for i, (xv, yv) in enumerate(points):
|
||||
x = round(origin_x + (xv - x_min) / (x_max - x_min) * cw)
|
||||
y = round(origin_y - (yv - y_min) / (y_max - y_min) * ch)
|
||||
print(f"point-{i}: cx={x} cy={y}")
|
||||
```
|
||||
|
||||
**装饰性元素(等间距范式)**
|
||||
|
||||
```python
|
||||
n, total_w, cy, r = 8, 340, 40, 4
|
||||
step = total_w / (n - 1)
|
||||
for i in range(n):
|
||||
print(f"circle-{i}: cx={round(i * step)} cy={cy} r={r}")
|
||||
```
|
||||
|
||||
**最大包围盒 → whiteboard 尺寸**
|
||||
|
||||
所有元素坐标算完后,汇总出整体包围盒,直接作为 whiteboard 的 `width`/`height`:
|
||||
|
||||
```python
|
||||
# 每个元素登记 (x, y, w, h),含 stroke 外扩
|
||||
elements = [
|
||||
(10, 20, 80, 160), # item-0
|
||||
(107, 10, 80, 170), # item-1
|
||||
(204, 40, 80, 140), # item-2
|
||||
(0, 0, 300, 1), # x-axis
|
||||
]
|
||||
|
||||
xs = [x for x, y, w, h in elements]
|
||||
ys = [y for x, y, w, h in elements]
|
||||
x2 = [x + w for x, y, w, h in elements]
|
||||
y2 = [y + h for x, y, w, h in elements]
|
||||
|
||||
wb_w = max(x2) - min(xs)
|
||||
wb_h = max(y2) - min(ys)
|
||||
print(f"whiteboard width={wb_w} height={wb_h}")
|
||||
```
|
||||
|
||||
输出即 `<whiteboard width=... height=...>` 的值,无需手动估算。
|
||||
|
||||
---
|
||||
### 布局模式
|
||||
|
||||
**全屏装饰层**
|
||||
```xml
|
||||
<whiteboard width="960" height="540" topLeftX="0" topLeftY="0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
...
|
||||
</svg>
|
||||
</whiteboard>
|
||||
```
|
||||
|
||||
> ⚠️ 全屏装饰 whiteboard 必须放在所有 `<shape>` / `<img>` / `<table>` 之前,否则会遮挡文字内容。XML 中元素位置越靠后,渲染层级越高。
|
||||
|
||||
**侧栏图表(与文字 shape 并排)**
|
||||
```xml
|
||||
<!-- 左侧文字 -->
|
||||
<shape type="text" topLeftX="60" topLeftY="120" width="500" height="340">...</shape>
|
||||
<!-- 右侧图表 -->
|
||||
<whiteboard width="340" height="340" topLeftX="580" topLeftY="120">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
...
|
||||
</svg>
|
||||
</whiteboard>
|
||||
```
|
||||
|
||||
**底部装饰条**
|
||||
```xml
|
||||
<whiteboard width="960" height="100" topLeftX="0" topLeftY="440">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
...
|
||||
</svg>
|
||||
</whiteboard>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 禁止使用的 SVG 特性
|
||||
|
||||
以下特性在 slide `<whiteboard>` 渲染端不支持或行为不可预测,必须避免:
|
||||
|
||||
| 禁止 | 原因 | 替代方案 |
|
||||
|------|------|---------|
|
||||
| `<radialGradient>` | 渲染失败 | 用 `<linearGradient>` 或 `rgba()` 透明度模拟深浅层次 |
|
||||
| `<filter>`(阴影、模糊等) | 渲染失败 | 用半透明 `<rect>` 叠加模拟阴影 |
|
||||
| `<clipPath>` / `<mask>` | 渲染失败 | 调整元素坐标和尺寸自然裁切 |
|
||||
| `<pattern>` | 渲染失败 | 手动铺 `<circle>` / `<rect>` 点阵 |
|
||||
| `skewX` / `skewY` / `matrix(...)` | 空间扭曲,降级渲染 | 用 `rotate` + `translate` 替代 |
|
||||
| `<image>` 外链 URL | 不支持外链 | 先上传得到 file_token,再用 `<img>` 元素 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 模式二:Mermaid
|
||||
|
||||
### 语法
|
||||
|
||||
```xml
|
||||
<whiteboard topLeftX="72" topLeftY="60" width="816" height="360">
|
||||
<mermaid>
|
||||
<![CDATA[
|
||||
flowchart TD
|
||||
A[检查 lark-cli 与 jq] --> B[编写每页 slide XML]
|
||||
B --> C[通过 jq 生成 slides JSON]
|
||||
C --> D[执行 slides +create]
|
||||
D --> E[读取 xml_presentation_id]
|
||||
E --> F[回读并验证创建结果]
|
||||
]]>
|
||||
</mermaid>
|
||||
</whiteboard>
|
||||
```
|
||||
|
||||
**关键点:**
|
||||
- 内容用 `<![CDATA[...]]>` 包裹——Mermaid 语法里的 `[`、`>`、`-->` 是 XML 特殊字符,CDATA 避免转义问题
|
||||
- whiteboard 只需 `topLeftX`、`topLeftY`、`width`、`height`
|
||||
|
||||
### 支持的 Mermaid 图表类型
|
||||
|
||||
| 类型 | 关键字 | 适用场景 |
|
||||
|------|--------|---------|
|
||||
| 流程图 | `flowchart TD` / `flowchart LR` | 业务流程、决策树、工作流 |
|
||||
| 时序图 | `sequenceDiagram` | 系统交互、API 调用链 |
|
||||
| 甘特图 | `gantt` | 项目计划、里程碑 |
|
||||
| 类图 | `classDiagram` | 对象关系、架构设计 |
|
||||
| ER 图 | `erDiagram` | 数据库结构 |
|
||||
| 状态图 | `stateDiagram-v2` | 状态机、生命周期 |
|
||||
| 思维导图 | `mindmap` | 主题梳理、知识架构 |
|
||||
| 用户旅程 | `journey` | 用户体验路径 |
|
||||
|
||||
### Mermaid 布局建议
|
||||
|
||||
Mermaid 图表会自动撑满 whiteboard 区域。建议:
|
||||
- 流程图留足高度,节点较多时适当增加 height(比如 400-480)
|
||||
- 避免一页放超过 15 个节点,内容太密时考虑分页
|
||||
- 推荐尺寸参考:
|
||||
|
||||
| 图表类型 | 建议 width | 建议 height |
|
||||
|---------|-----------|------------|
|
||||
| 流程图(5-8 节点) | 720-816 | 300-400 |
|
||||
| 时序图(3-5 参与者) | 720-816 | 320-420 |
|
||||
| 甘特图 | 816 | 280-360 |
|
||||
| 思维导图 | 816 | 380-480 |
|
||||
|
||||
---
|
||||
|
||||
## 注意事项 & 已知问题
|
||||
|
||||
### z-order(SVG 模式)
|
||||
|
||||
whiteboard 在 XML 中的位置决定渲染层级:在 shape 前 → 在下层;在 shape 后 → 在上层。全屏装饰 whiteboard 应放在所有 shape 之前。
|
||||
|
||||
### Mermaid CDATA 必要性
|
||||
|
||||
Mermaid 语法包含 `[`、`>`、`-->`,不用 CDATA 直接写会破坏 XML 解析。始终使用 `<![CDATA[ ... ]]>`。
|
||||
|
||||
---
|
||||
|
||||
## 快速自检清单
|
||||
|
||||
**SVG 模式——结构检查:**
|
||||
- [ ] `<svg>` 声明了 `xmlns="http://www.w3.org/2000/svg"`
|
||||
- [ ] whiteboard 的 `width`/`height` 由所有元素的最大包围盒(含 stroke 外扩)计算得出,不手动估值
|
||||
- [ ] `topLeftX + width ≤ 960`,`topLeftY + height ≤ 540`
|
||||
- [ ] 无 `<radialGradient>` / `<filter>` / `<clipPath>`
|
||||
- [ ] 文字 `y` 坐标为 baseline 位置,最小值 ≥ font-size(避免被裁切)
|
||||
|
||||
**SVG 模式——视觉品质检查:**
|
||||
- [ ] 非原生数据视觉有必要的坐标轴、网格线、数值标注或分段说明,没有"裸点"或无解释色块
|
||||
- [ ] 字号有层级:标题 > 数值 > 轴标签,非全部相同
|
||||
- [ ] 单一数据系列用同一颜色,多系列用不同颜色且对比充足
|
||||
- [ ] 轴标签与图表元素互不遮挡,留有足够空间
|
||||
- [ ] 坐标推导有注释(写明 originX/Y、chartW/H、数据映射公式)
|
||||
|
||||
**Mermaid 模式:**
|
||||
- [ ] 内容包在 `<![CDATA[...]]>` 内
|
||||
- [ ] CDATA 结束符 `]]>` 不出现在 Mermaid 代码本身中
|
||||
- [ ] `topLeftX + width ≤ 960`,`topLeftY + height ≤ 540`
|
||||
- [ ] 节点数量合理(单图不超过 15-20 个节点)
|
||||
|
||||
**通用:**
|
||||
- [ ] XML 标签全部闭合,属性引号完整
|
||||
- [ ] 如果失败,检查是否是偶发 5001000,重试一次
|
||||
|
||||
---
|
||||
|
||||
## 参考
|
||||
|
||||
- [lark-slides SKILL.md](../SKILL.md)
|
||||
@@ -1,100 +0,0 @@
|
||||
# slides +xml-get(读取 XML)
|
||||
|
||||
读取已有演示文稿的完整 XML,或按 `slide_id` / 页码读取单页 XML。适合创建后验收、编辑前备份、获取 `slide_id` / `revision_id`,以及排查空白页、破图、文本溢出等问题。相比直接调用底层 `xml_presentations.get` / `xml_presentation.slide.get`,本 shortcut 会自动解析 Slides URL / Wiki URL,并可把 XML 保存到本地文件,避免终端输出被截断。
|
||||
|
||||
## 命令
|
||||
|
||||
|
||||
```bash
|
||||
lark-cli slides +xml-get \
|
||||
--as user \
|
||||
--presentation <slides_url_or_xml_presentation_id> \
|
||||
--output .lark-slides/plan/<deck-id>/readback.xml
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `--presentation` | 是 | `xml_presentation_id`、`/slides/` URL 或 `/wiki/` URL |
|
||||
| `--output` | 否 | 本地 XML 保存路径,必须是当前工作目录内的相对路径,不能传绝对路径。传入时 XML 内容保存到文件,stdout 只返回保存后的绝对路径、大小等简短元信息;省略时默认返回 JSON envelope |
|
||||
| `--slide-id` | 否 | 页面 short ID;传入后只读取该页 XML。不能和 `--slide-number` 同时使用 |
|
||||
| `--slide-number` | 否 | 1-based 页码;传入后只读取该页 XML。不能和 `--slide-id` 同时使用 |
|
||||
| `--revision-id` | 否 | 读取指定版本;默认 `-1`,表示最新版本 |
|
||||
| `--remove-attr-id` | 否 | 仅全文读取可用。移除返回 XML 中的 `id` 属性;适合只读检查,不适合精确块级编辑 |
|
||||
| `--raw` | 否 | 省略 `--output` 时直接把 XML 原文写到 stdout,不包 JSON envelope。不能和 `--output` / `--jq` / 非 json `--format` 同时使用 |
|
||||
| `--dry-run` | 否 | 预览将调用的 API 和输出方式,不读取真实 XML |
|
||||
|
||||
## 输出到文件
|
||||
|
||||
推荐普通工作流都传 `--output`,尤其是中大型 PPT。`--output` 必须是当前工作目录内的相对路径,例如 `.lark-slides/plan/$PID/readback.xml`,不要传 `/tmp/readback.xml` 这类绝对路径。XML 会写入本地文件,stdout 只保留元信息,便于后续脚本读取。
|
||||
|
||||
```bash
|
||||
lark-cli slides +xml-get --as user \
|
||||
--presentation "$PID" \
|
||||
--output .lark-slides/plan/$PID/readback.xml
|
||||
```
|
||||
|
||||
成功输出中的 `data` 类似:
|
||||
|
||||
```json
|
||||
{
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"path": "/abs/path/.lark-slides/plan/slides_example_presentation_id/readback.xml",
|
||||
"size": 123456,
|
||||
"content_saved": true,
|
||||
"revision_id": 12
|
||||
}
|
||||
```
|
||||
|
||||
其中 `path` 是 CLI 解析后的绝对路径。
|
||||
|
||||
如果传入 `--remove-attr-id`,返回元信息中会包含 `"remove_attr_id": true`。
|
||||
|
||||
## 读取单页
|
||||
|
||||
已知页面 short ID 时,用 `--slide-id`:
|
||||
|
||||
```bash
|
||||
lark-cli slides +xml-get --as user \
|
||||
--presentation "$PID" \
|
||||
--slide-id "$SID" \
|
||||
--output .lark-slides/plan/$PID/slide-$SID.xml
|
||||
```
|
||||
|
||||
已知页码时,用 `--slide-number`(页码从 1 开始):
|
||||
|
||||
```bash
|
||||
lark-cli slides +xml-get --as user \
|
||||
--presentation "$PID" \
|
||||
--slide-number 2 \
|
||||
--output .lark-slides/plan/$PID/slide-2.xml
|
||||
```
|
||||
|
||||
单页模式底层调用 `xml_presentation.slide.get`,返回或保存的是单个 `<slide>` XML 片段。`--slide-id` 和 `--slide-number` 不能同时传;`--remove-attr-id` 只支持全文读取。
|
||||
|
||||
## 输出到终端
|
||||
|
||||
省略 `--output` 时,CLI 默认输出 JSON envelope,XML 位于 `data.xml_presentation.content`(全文)或 `data.slide.content`(单页)。这个模式适合配合 `--jq` 临时提取:
|
||||
|
||||
```bash
|
||||
lark-cli slides +xml-get --as user \
|
||||
--presentation "$PID" \
|
||||
--jq '.data.xml_presentation.content'
|
||||
```
|
||||
|
||||
需要把 XML 原文直接写到 stdout 时,加 `--raw`:
|
||||
|
||||
```bash
|
||||
lark-cli slides +xml-get --as user \
|
||||
--presentation "$PID" \
|
||||
--slide-number 2 \
|
||||
--raw
|
||||
```
|
||||
|
||||
## 相关命令
|
||||
|
||||
- [slides +screenshot](lark-slides-screenshot.md) - 获取页面截图做视觉验证
|
||||
- [slides +replace-slide](lark-slides-replace-slide.md) - 局部替换或插入页面元素
|
||||
- [slides +replace-pages](lark-slides-replace-pages.md) - 多页整页重建
|
||||
- [xml_presentations get](lark-slides-xml-presentations-get.md) - 底层原生 API 参考
|
||||
@@ -1,125 +0,0 @@
|
||||
# lark-slides xml_presentation.slide delete
|
||||
|
||||
## 用途
|
||||
|
||||
删除指定 XML 演示文稿中的幻灯片页面。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide delete --as user --params '<json_params>'
|
||||
```
|
||||
|
||||
## 参数说明
|
||||
|
||||
| 参数 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `--params` | JSON string | 是 | 路径参数与查询参数 |
|
||||
|
||||
### params JSON 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"slide_id": "slide_example_id",
|
||||
"revision_id": -1,
|
||||
"tid": "idMock"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `xml_presentation_id` | string | 是 | 演示文稿的唯一标识符 |
|
||||
| `slide_id` | string | 是 | 要删除的幻灯片唯一标识符 |
|
||||
| `revision_id` | integer | 否 | 演示文稿版本号,`-1` 表示最新版本 |
|
||||
| `tid` | string | 否 | 锁的事务 ID |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 删除指定幻灯片
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide delete --as user --params '{
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"slide_id": "slide_example_id"
|
||||
}'
|
||||
```
|
||||
|
||||
### 结合查询删除(使用 jq)
|
||||
|
||||
```bash
|
||||
# 先读取 XML 内容,确认待删除页面
|
||||
lark-cli slides +xml-get --as user \
|
||||
--presentation "slides_example_presentation_id" \
|
||||
--output .lark-slides/plan/slides_example_presentation_id/readback.xml \
|
||||
--json
|
||||
|
||||
# 然后按已知 slide_id 删除
|
||||
lark-cli slides xml_presentation.slide delete --as user --params '{"xml_presentation_id":"slides_example_presentation_id","slide_id":"slide_example_id"}'
|
||||
```
|
||||
|
||||
## 返回值
|
||||
|
||||
成功时返回删除确认信息:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"revision_id": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 返回字段说明
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `data.revision_id` | integer | 删除后的最新版本号 |
|
||||
|
||||
## 常见错误
|
||||
|
||||
| 错误码 | 含义 | 解决方案 |
|
||||
|--------|------|----------|
|
||||
| 404 | 演示文稿不存在 | 检查 `xml_presentation_id` 是否正确 |
|
||||
| 404 | 幻灯片不存在 | 检查 `slide_id` 是否正确,或该幻灯片已被删除 |
|
||||
| 400 | 无法删除唯一幻灯片 | 演示文稿至少保留一页幻灯片 |
|
||||
| 403 | 权限不足 | 检查是否拥有 `slides:presentation:update` 或 `slides:presentation:write_only` scope |
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **执行前必做**: 使用 `lark-cli schema slides.xml_presentation.slide.delete` 查看最新的参数结构
|
||||
2. **删除不可逆**: 删除操作无法撤销,请确保已备份重要内容
|
||||
3. **至少保留一页**: 演示文稿必须至少保留一页幻灯片,删除最后一页会报错
|
||||
4. **版本控制**: 如果依赖版本号并发控制,删除前先确认 `revision_id`
|
||||
5. **获取 slide_id**: 创建幻灯片时请保存返回值;仅靠 `get` 返回的 XML 无法直接推导服务端 short ID
|
||||
|
||||
## 如何获取 slide_id
|
||||
|
||||
### 方法 1: 创建时保存
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide create --as user --params '{"xml_presentation_id":"slides_example_presentation_id"}' --data '{
|
||||
"slide": {
|
||||
"content": "<slide xmlns=\"http://www.larkoffice.com/sml/2.0\"><data><shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>新页面</p></content></shape></data></slide>"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
返回结果中的 `slide_id` 就是后续删除所需的值。
|
||||
|
||||
## 批量删除建议
|
||||
|
||||
如果需要删除多张幻灯片,建议先整理好待删 `slide_id` 列表,再逐个删除:
|
||||
|
||||
```bash
|
||||
for slide_id in sld_a sld_b sld_c; do
|
||||
lark-cli slides xml_presentation.slide delete --as user --params "{\"xml_presentation_id\":\"slides_example_presentation_id\",\"slide_id\":\"$slide_id\"}"
|
||||
done
|
||||
```
|
||||
|
||||
## 相关命令
|
||||
|
||||
- [slides +create](lark-slides-create.md) - 创建 PPT / 添加幻灯片页面
|
||||
- [slides +xml-get](lark-slides-xml-get.md) - 读取 PPT 内容并保存到本地文件
|
||||
@@ -1,110 +0,0 @@
|
||||
# lark-slides xml_presentation.slide get
|
||||
|
||||
## 用途
|
||||
|
||||
按 `slide_id` 拉取指定演示文稿单页的 XML 内容(可指定历史版本)。常用于"读-改-写"编辑闭环的第一步。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide get --as user --params '<json_params>'
|
||||
```
|
||||
|
||||
## 参数说明
|
||||
|
||||
| 参数 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `--params` | JSON string | 是 | 路径参数与查询参数 |
|
||||
|
||||
### params JSON 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"slide_id": "slide_example_id",
|
||||
"revision_id": -1
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `xml_presentation_id` | string | 是 | 目标演示文稿唯一标识 |
|
||||
| `slide_id` | string | 是 | 目标页面唯一标识 |
|
||||
| `revision_id` | integer | 否 | 版本号,`-1` 表示最新版(默认)|
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 读最新版本
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide get --as user --params '{
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"slide_id": "slide_example_id"
|
||||
}'
|
||||
```
|
||||
|
||||
### 只提取 XML 内容
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide get --as user \
|
||||
--params '{"xml_presentation_id":"slides_example_presentation_id","slide_id":"slide_example_id"}' \
|
||||
| jq -r '.data.slide.content'
|
||||
```
|
||||
|
||||
### 读指定历史版本
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide get --as user --params '{
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"slide_id": "slide_example_id",
|
||||
"revision_id": 42
|
||||
}'
|
||||
```
|
||||
|
||||
## 返回值
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"slide": {
|
||||
"slide_id": "slide_example_id",
|
||||
"content": "<slide id=\"slide_example_id\"><style/><data>...</data></slide>"
|
||||
},
|
||||
"revision_id": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `data.slide.slide_id` | string | 页面唯一标识 |
|
||||
| `data.slide.content` | string | 页面完整 XML(`<slide>` 根节点,不含 xmlns)|
|
||||
| `data.revision_id` | integer | 此次读到的版本号,可用于后续 replace 的乐观锁 |
|
||||
|
||||
## 常见错误
|
||||
|
||||
| 错误码 | 含义 | 解决方案 |
|
||||
|--------|------|----------|
|
||||
| 404 | 演示文稿或页面不存在 | 检查 `xml_presentation_id` / `slide_id` |
|
||||
| 403 | 权限不足 | 需要 `slides:presentation:read` scope,并对该 PPT 有访问权限 |
|
||||
| 400 | `revision_id` 不存在 | 传了无效版本号,用 `-1` 或真实存在的版本号 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **执行前必做**:`lark-cli schema slides.xml_presentation.slide.get` 查看最新参数结构
|
||||
2. **block_id 提取**:返回 XML 里每个顶层块(shape、img、table、chart、whiteboard 等)的 `id` 属性即为 `block_id`,通常是 3 字符短码,例如 `<shape id="bUn" ...>`。用以下命令列出当前页所有 block_id:
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide get --as user \
|
||||
--params "{\"xml_presentation_id\":\"$PID\",\"slide_id\":\"$SID\"}" \
|
||||
| jq -r '.data.slide.content' | grep -oE 'id="[^"]+"' | sed 's/id="//;s/"//'
|
||||
```
|
||||
|
||||
## 相关命令
|
||||
|
||||
- [slides +replace-slide](lark-slides-replace-slide.md) — 块级替换 shortcut(推荐)
|
||||
- [xml_presentation.slide replace](lark-slides-xml-presentation-slide-replace.md) — 底层 replace API 参考
|
||||
- [slides +xml-get](lark-slides-xml-get.md) — 读整个 PPT 并保存到本地文件
|
||||
- [lark-slides-edit-workflows.md](lark-slides-edit-workflows.md) — 读-改-写闭环
|
||||
@@ -1,189 +0,0 @@
|
||||
# lark-slides xml_presentation.slide replace
|
||||
|
||||
## 用途
|
||||
|
||||
对单页做**块级局部替换**:不覆盖整页,按 patch 列表做 `block_replace`(整块替换)或 `block_insert`(整块插入)。适合"只想加 / 换一个元素、不动其他元素"的场景。
|
||||
|
||||
> **推荐**:优先使用 [`+replace-slide`](lark-slides-replace-slide.md) Shortcut——它会自动注入 `id` 和 `<content/>`,直接调本 API 需自己处理这两个约束(见注意事项 5、6)。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide replace --as user --params '<json_params>' --data '<json_data>'
|
||||
```
|
||||
|
||||
## 参数说明
|
||||
|
||||
| 参数 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `--params` | JSON string | 是 | 路径参数与查询参数 |
|
||||
| `--data` | JSON string | 是 | patch 列表 |
|
||||
|
||||
### params JSON 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"slide_id": "slide_example_id",
|
||||
"revision_id": -1,
|
||||
"tid": "idMock"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `xml_presentation_id` | string | 是 | 演示文稿唯一标识 |
|
||||
| `slide_id` | string | 是 | 页面唯一标识 |
|
||||
| `revision_id` | integer | 否 | 默认 `-1`(以最新版为基准);传具体版本号做乐观锁 |
|
||||
| `tid` | string | 否 | 事务 ID,一般留空 |
|
||||
|
||||
### data JSON 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"parts": [
|
||||
{ "action": "block_replace", "block_id": "bab", "replacement": "<shape .../>" },
|
||||
{ "action": "block_insert", "insertion": "<img .../>", "insert_before_block_id": "baa" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `parts` | array | 是 | patch 列表,长度 1~200,顺序执行 |
|
||||
|
||||
### parts[] 字段(按 action 不同)
|
||||
|
||||
本期 CLI 文档化两种 action:
|
||||
|
||||
#### action = "block_replace" — 整块替换
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `action` | 是 | 固定 `block_replace` |
|
||||
| `block_id` | 是 | 目标块的 3 位 short element ID(从 `slide.get` 返回的 XML 里读到) |
|
||||
| `replacement` | 是 | 新 XML 片段,替换整个目标块 |
|
||||
|
||||
#### action = "block_insert" — 整块插入
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `action` | 是 | 固定 `block_insert` |
|
||||
| `insertion` | 是 | 要插入的完整 XML 片段 |
|
||||
| `insert_before_block_id` | 否 | 插到这个块之前;省略则追加到页面末尾 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### block_replace:换一个 shape 的整体内容
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide replace --as user --params '{
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"slide_id": "slide_example_id"
|
||||
}' --data '{
|
||||
"parts": [
|
||||
{
|
||||
"action": "block_replace",
|
||||
"block_id": "bab",
|
||||
"replacement": "<shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>新标题</p></content></shape>"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### block_insert:在已有页上加一张图
|
||||
|
||||
```bash
|
||||
# 先拿 file_token
|
||||
TOKEN=$(lark-cli slides +media-upload --file ./pic.png --presentation "$PID" --as user | jq -r '.data.file_token')
|
||||
|
||||
lark-cli slides xml_presentation.slide replace --as user --params "{
|
||||
\"xml_presentation_id\": \"$PID\",
|
||||
\"slide_id\": \"$SID\"
|
||||
}" --data "$(jq -n --arg token "$TOKEN" '{
|
||||
parts: [
|
||||
{
|
||||
action: "block_insert",
|
||||
insertion: ("<img src=\"" + $token + "\" topLeftX=\"500\" topLeftY=\"100\" width=\"200\" height=\"150\"/>")
|
||||
}
|
||||
]
|
||||
}')"
|
||||
```
|
||||
|
||||
### 多条 parts 原子执行
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide replace --as user --params '{
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"slide_id": "slide_example_id"
|
||||
}' --data '{
|
||||
"parts": [
|
||||
{"action":"block_replace","block_id":"bab","replacement":"<shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>新标题</p></content></shape>"},
|
||||
{"action":"block_insert","insertion":"<img src=\"<file_token>\" topLeftX=\"700\" topLeftY=\"400\" width=\"180\" height=\"100\"/>"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## 返回值
|
||||
|
||||
### 成功
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"revision_id": 105
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 失败(任一 part 失败,整批不生效)
|
||||
|
||||
失败时命令以非零退出码结束,stderr 返回类型化错误信封(`error.code`(如 3350001)/ `error.message` / `error.hint`),stdout 不会打印后端原始响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"identity": "user",
|
||||
"error": {
|
||||
"type": "api",
|
||||
"subtype": "...",
|
||||
"code": 3350001,
|
||||
"message": "...",
|
||||
"hint": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `data.revision_id` | integer | 成功时返回更新后最新版本号 |
|
||||
|
||||
## 常见错误
|
||||
|
||||
| 错误码 | 含义 | 解决方案 |
|
||||
|--------|------|----------|
|
||||
| 3350001 | `block_id` 在当前页不存在,或 XML 格式 / 结构错误 | 重新 `slide.get` 拿最新 XML,确认 `block_id` 存在;检查 `replacement` / `insertion` 是否合法 XML |
|
||||
| 400 | `parts` 长度超过 200 | 拆多次调用 |
|
||||
| 3350002 | `revision_id` 不存在(超过当前版本号) | 用 `-1` 或实际存在的 `revision_id` |
|
||||
| 400 | XML 格式错误 | `replacement` / `insertion` 必须为合法的 XML 片段,标签闭合 + 属性引号 |
|
||||
| 403 | 权限不足 | 需要 `slides:presentation:update` 或 `slides:presentation:write_only` |
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **parts 原子事务**:任一条失败整批回滚,不会出现"前几条成功、后几条失败"的中间态。
|
||||
2. **block_id 的获取**:`slide.get` 返回的 XML 里每个块(shape、img、table、chart、whiteboard 等)会带 3 位 short element ID,用这个值填 `block_id` / `insert_before_block_id`。
|
||||
3. **`<img>` 必须用 file_token**:不能用外链 URL——先 [`slides +media-upload`](lark-slides-media-upload.md) 拿 token。
|
||||
4. **不能字段级 patch**:要改一个块的某个属性(比如只改 `topLeftX`),得写整块新 XML 走 `block_replace`;API 不支持"只改一个字段"。
|
||||
5. **`block_replace` 要求 `replacement` 根元素带 `id="<block_id>"`**:底层 API 的硬约束,缺失会返回 3350001。推荐走 shortcut [`+replace-slide`](lark-slides-replace-slide.md)——它会自动把 `id` 注入到 `replacement` 根元素上,用户写 XML 时不用自己加。
|
||||
6. **`<shape>` 必须有 `<content/>` 子元素**:SML 2.0 schema 要求,缺失同样触发 3350001。shortcut [`+replace-slide`](lark-slides-replace-slide.md) 会自动注入 `<content/>`,直接调底层 API 需要自己加。
|
||||
7. **`<whiteboard>` 返回结构不含内部数据**:`slide.get` 返回的 whiteboard 块只有外层标签和位置属性,SVG / Mermaid 内容不会随 XML 一起返回。但 `block_replace` 仍然可以强行覆盖——直接写入完整新 whiteboard XML 即可。
|
||||
8. **执行前必做**:`lark-cli schema slides.xml_presentation.slide.replace` 查看最新参数结构。
|
||||
|
||||
## 相关命令
|
||||
|
||||
- [slides +replace-slide](lark-slides-replace-slide.md) — 块级替换 shortcut(推荐,自动注入 id)
|
||||
- [xml_presentation.slide get](lark-slides-xml-presentation-slide-get.md) — 读原页拿 block short ID
|
||||
- [slides +media-upload](lark-slides-media-upload.md) — 上传图片拿 file_token
|
||||
- [lark-slides-edit-workflows.md](lark-slides-edit-workflows.md) — 读-改-写闭环 + 决策树
|
||||
@@ -1,99 +0,0 @@
|
||||
# lark-slides xml_presentations get
|
||||
|
||||
## 用途
|
||||
|
||||
读取飞书幻灯片(PPT)演示文稿的完整 XML 内容信息。
|
||||
|
||||
## 底层原生命令形态
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentations get --as user --params '<json_params>'
|
||||
```
|
||||
|
||||
## 参数说明
|
||||
|
||||
| 参数 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `--params` | JSON string | 是 | 路径参数与查询参数,结构以 schema 为准 |
|
||||
|
||||
### params JSON 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"revision_id": -1
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `xml_presentation_id` | string | 是 | 演示文稿的唯一标识符 |
|
||||
| `revision_id` | integer | 否 | 版本号,`-1` 表示最新版本 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础示例
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentations get --as user \
|
||||
--params '{"xml_presentation_id":"slides_example_presentation_id","revision_id":-1}'
|
||||
```
|
||||
|
||||
### 指定版本读取
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentations get --as user \
|
||||
--params '{"xml_presentation_id":"slides_example_presentation_id","revision_id":10}'
|
||||
```
|
||||
|
||||
### 移除 XML id 属性后读取
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentations get --as user \
|
||||
--params '{"xml_presentation_id":"slides_example_presentation_id","revision_id":-1,"remove_attr_id":true}'
|
||||
```
|
||||
|
||||
## 返回值
|
||||
|
||||
成功时返回演示文稿的完整信息:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"xml_presentation": {
|
||||
"presentation_id": "slides_example_presentation_id",
|
||||
"revision_id": 1,
|
||||
"content": "<presentation xmlns=\"http://www.larkoffice.com/sml/2.0\" height=\"540\" width=\"960\">...</presentation>"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 返回字段说明
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `data.xml_presentation.presentation_id` | string | 演示文稿唯一标识 |
|
||||
| `data.xml_presentation.revision_id` | integer | 版本号 |
|
||||
| `data.xml_presentation.content` | string | XML 格式的完整内容 |
|
||||
|
||||
## 常见错误
|
||||
|
||||
| 错误码 | 含义 | 解决方案 |
|
||||
|--------|------|----------|
|
||||
| 404 | 演示文稿不存在 | 检查 `xml_presentation_id` 是否正确 |
|
||||
| 403 | 权限不足 | 检查是否拥有 `slides:presentation:read` scope,或是否有访问权限 |
|
||||
| 400 | 参数格式错误 | 确保 `--params` 是合法的 JSON 字符串 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 直接调用底层 API 前,使用 `lark-cli schema slides.xml_presentations.get` 查看最新的参数结构
|
||||
2. 返回的 XML 在 `data.xml_presentation.content` 字段中
|
||||
3. 如果只需要部分信息,可以使用 `jq` 等工具过滤返回结果
|
||||
|
||||
## 相关命令
|
||||
|
||||
- [slides +create](lark-slides-create.md) - 创建 PPT / 添加幻灯片页面
|
||||
- [xml_presentation.slide delete](lark-slides-xml-presentation-slide-delete.md) - 删除幻灯片页面
|
||||
78
skills/lark-slides/references/layout.md
Normal file
78
skills/lark-slides/references/layout.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# 版式几何(逐页)
|
||||
|
||||
本文件只管「东西摆哪」:坐标、分区、text-fit、各 layout 的几何与可复制模板骨架。观感(配色 / 字体 / motif)见 `design.md`,元素怎么画见 `xml-protocol.md` 与 `cli-operations.md`。
|
||||
|
||||
画布按 `960 x 540` 规划。模板可覆盖具体坐标,但不能覆盖下面的承重原则。
|
||||
|
||||
## 承重原则
|
||||
|
||||
- `layout_type` 必须真正改几何:元素位置、区域大小、对齐、节奏在不同页型间要有明显差异,不能每页都是标题+bullets。
|
||||
- `visual_focus` 定最大 / 最高对比区:可以是图片、图表、指标、引言、表格或 shape 抽象视觉;文本框本身不算主视觉。
|
||||
- 每页至少一个视觉元素(图片 / 图标 / 图表 / 表格 / 流程 / 对比 / 大号数字 / 示意),纯文本页不作正式交付。
|
||||
- `text_density` 卡可见文本上限:`low`=标题+1 句或 1-3 标签;`medium`=标题+2-4 短条 / 带标签区;`high`=表格 / 分栏 / 分组标签,禁用一个长 bullet 框。
|
||||
- 4 页以上,内容允许时至少用 4 种不同 layout 结构。
|
||||
- 边距 `60-80` px(整屏出血封面 / 半出血视觉除外)。
|
||||
- 标题区留纵向空间:内容页标题区 `y≈36..90`,正文一般 `y≥110` 起。
|
||||
- 别挤底边:非背景内容保持在 `y<500`(页脚除外)。
|
||||
- 少而大优于多而碎,宁可几个大块,不要一堆小文本框。
|
||||
- text-fit 是版式约束不是收尾:框放不下就先缩文 / 拆分 / 加空间,再写 XML。
|
||||
- 背景 / motif shape 必须插在内容元素之前,避免盖住文字、图、图表。
|
||||
|
||||
## text-fit 护栏
|
||||
|
||||
`960 x 540` 下的保守下限。用加粗、中文、中英混排或大行距时要**加高**。
|
||||
|
||||
| 文本用途 | 典型字号 | 最小框高 |
|
||||
|---|---|---|
|
||||
| 注释 1 行 | 10-12 | 18 |
|
||||
| 注释 2 行 | 10-12 | 30 |
|
||||
| 正文 1 行 | 13-16 | 24 |
|
||||
| 正文 2 行 | 13-16 | 40 |
|
||||
| 正文 2 行加粗 | 15-18 | 48 |
|
||||
| 大标题 1 行 | 24-32 | 42 |
|
||||
| 标题 2 行 | 34-44 | 110 |
|
||||
|
||||
- `height=18/22` 只给短标签,别塞长中文句 / 长英文短语。
|
||||
- 页脚 / 来源默认一短行;要更多就在页脚区上方另开真正的注释块。
|
||||
- 底部结论条:一行强调 `≥40` px,两行 `≥54` px。
|
||||
- 图形标签要短到能进 shape,宁可两短行不要一挤压长行;标签别压在连接线上。
|
||||
- 多 `<p>` 的文本块按多行显式给高,别指望渲染器自动撑开。
|
||||
- 中英混排比单语更难折行,宽度要多留。
|
||||
|
||||
## 12 种 layout 几何
|
||||
|
||||
每种必须产生可识别的坐标结构(去掉 label 也认得出)。
|
||||
|
||||
| layout | 用途 | 关键几何 / 分区 | 文本档 |
|
||||
|---|---|---|---|
|
||||
| `title-cover` | 立观点、开篇 | 一个主标题块 `x=70..120,y=150..250,w=700..820`+1 副标题行(非列表);可整屏出血 / 侧图 / 强调带;有右侧图则走分屏,标题居左中、视觉另占一区,连接线不穿标题 | `low` |
|
||||
| `section-divider` | 换节奏、分章 | 大号章节数字 / 章节标签 / 单句主张居中;页面留白,焦点=超大数字 / 竖强调条 / 通栏带 | 标题+1 短语,无 bullet |
|
||||
| `two-column` | 对比两相关点 / 论点配证据 | 主区分两平衡列,如左 `x=60,w=400` 右 `x=500,w=400`,各自有小标题 / 视觉锚;禁一个通栏 bullet 框 | `medium` 每列 2-3 条 / `high` 分组行 |
|
||||
| `image-left-text-right` | 视觉起上下文、右侧释义 | 左视觉占宽 `35-45%`,常满高 / 竖裁;右文 `x≈420` 起,强标题+短支撑;无真图则按 `asset_need` 造 shape 占位 | 右侧短,≤4 bullet;截图页用 2-3 解读卡 |
|
||||
| `image-right-text-left` | 先给论点、图强化 | 左文 `x≈60..90,w=400..460`;右视觉占宽 `35-45%`,与正文块对齐(非仅与标题) | 1 主张+2-3 支撑,callout 平行短 |
|
||||
| `big-number` | 一个指标 / 事实可记 | 指标为最大对象,字号常 `64-110`,区 `≥300 x 120`,`<content>` 设 `autoFit="normal-auto-fit"` 防溢出;配 1 解释+可选 2-3 小标签,别埋进 bullet | `low`/`medium` |
|
||||
| `timeline` | 序列 / 路线 / 阶段 | 横或竖主轴串 3-6 里程碑,每个=点 / 卡 / 日期 由线或箭头连;标题与序列分离,序列=焦点 | 每点短标签+可选 1 行 |
|
||||
| `comparison` | 抉择 / before-after / 方案权衡 | 2-3 个并列面板 / 列 / 类表结构,标题严格对齐易扫;用色 / 边框 / icon 标出优选 | 各列措辞平行,禁长短不齐 bullet |
|
||||
| `architecture-diagram` | 组件 / 依赖 / 系统流 | 主区=图非散文,分组框 / 泳道 / 箭头 / 短标签;优先 Mermaid `<whiteboard>`,shape+line 兜底 | 标签 1-5 词,≤1 短说明块 |
|
||||
| `process-flow` | 操作步骤 / 工作流 / 因果链 | 编号步骤箭头相连,3-5 步为宜(更多分组成阶段),流向一眼可见;优先 Mermaid,shape+line 兜底 | 每步动词标签+至多 1 短语,步长平行 |
|
||||
| `quote-highlight` | 客户声音 / 原则 / 论点 / 决策 | 引言 / 主张为主导文本对象,大字号 + 大留白 + 可选归属 badge;不与普通 bullet 节混排 | 1 引言+可选归属,无 bullet |
|
||||
| `conclusion` | 决策 / 建议 / 下一步 | 一个主导收尾句 / 行动号召+至多 3 张下一步卡 / 清单 / 负责人日期;焦点=建议非装饰 | 易记,避免复盘堆砌 |
|
||||
|
||||
## 截图 / 论文图页
|
||||
|
||||
按页面角色(方法总览、证据、对比、失败分析常见;封面 / 目录 / 结论一般不用)放置,非固定页号:
|
||||
|
||||
- 截图正常应为视觉焦点,不缩成装饰缩略图再堆密文;够大可读才用真图,太密则裁到相关区 / 出放大细节 / 用 shape 重画核心。
|
||||
- 图给足视觉区:常占宽 `50-65%` 或至少 `320` px 高;放进画框 / 面板并留边,别让坐标轴、caption、边缘标签被页边裁掉。
|
||||
- 配 2-3 条解读标注告诉观众看什么;外部 / 论文图必附一句简短来源。
|
||||
- 终稿 XML 须含受支持的 image token 或建期本地占位,不留外链 URL。
|
||||
|
||||
## 模板骨架
|
||||
|
||||
- **深色封面 / 结尾**:`<style>` 渐变底;主标题 `y≈160..190,w≈800,h=55..70` 居中,副标 / 底信各一短行。分屏封面改为左文(`x=60,w=450`)+右图框(如 `540,157,400x225`=16:9)。
|
||||
- **浅色内容页**:左侧竖色条(`rect w=4,h≈35`)+标题(`x=76,y=36,h=45`),正文 `x=60,y=100,w=840,h=380`,`textType="body" lineSpacing="multiple:1.8"`。
|
||||
- **横排指标卡**:3 张 `rect 260x140`,`x=60/350/640`;卡内数值(`h=50`,字号 36)压指标名(`h=25`,字号 14)。
|
||||
- **上图下文卡**:`rect 270x360`,`x=60/345/630`;图框 `240x180`(4:3)压标题+2 行描述。
|
||||
- **图左文右**:图框 `360x540`(2:3 竖幅)贴左边 `x=0,y=0`,右文 `x=410,w=490`(标题 / 分割线 / 一句话 / bullet 列)。
|
||||
|
||||
图框 `width:height` 须等于原图比例才不裁:横图别硬塞竖框(改「顶部横幅 960x240+下方文字」),竖图别塞横框。带图时 `<img>` 取 token 规则见 `cli-operations.md`。
|
||||
@@ -1,246 +0,0 @@
|
||||
# Planning Layer
|
||||
|
||||
新建演示文稿或大幅改写页面时,必须先写 `.lark-slides/plan/<deck-or-task-id>/slide_plan.json`,再生成 XML。这个文件是 deck 的设计中间层,用来把叙事、页面角色、布局、视觉重点和文字密度固定下来,避免从用户提示直接跳到 XML。
|
||||
|
||||
小型已有页编辑可豁免,例如只替换一个标题、改一个数字、插入一个块、上传并插入一张图。只要任务会重排多页、生成新 deck、替换整页结构,仍然需要规划层。
|
||||
|
||||
## Required Flow
|
||||
|
||||
1. 理解用户需求,必要时澄清主题、受众、页数、风格。
|
||||
2. 选择唯一 plan 目录:`.lark-slides/plan/<deck-or-task-id>/`。
|
||||
3. 先创建目录:`mkdir -p .lark-slides/plan/<deck-or-task-id>`。
|
||||
4. 写入 `.lark-slides/plan/<deck-or-task-id>/slide_plan.json`。
|
||||
5. 读取 `xml-schema-quick-ref.md`、`visual-planning.md` 和 `asset-planning.md`。
|
||||
6. 按 plan、visual planning 和 asset planning 规则逐页生成 XML,把 `layout_type`、`visual_focus`、`text_density` 转成具体页面几何和文本量约束,并把缺失素材转成可执行兜底视觉。
|
||||
7. 创建 PPT 后用 `slides +xml-get` 回读,核对页面数量、关键元素和 plan 到 XML 的对应关系。
|
||||
|
||||
## Plan Path
|
||||
|
||||
Use a separate plan directory per deck or task so multiple presentations in the same workspace cannot overwrite each other.
|
||||
|
||||
Recommended IDs:
|
||||
|
||||
- New deck before creation: title slug plus date/time, such as `q3-review-20260507-1805`.
|
||||
- Existing PPT rewrite: the `xml_presentation_id`.
|
||||
- Ambiguous or untitled task: short task slug plus date/time.
|
||||
|
||||
Rules:
|
||||
|
||||
- Do not reuse `.lark-slides/plan/slide_plan.json` as a shared path.
|
||||
- Create the directory before writing the file.
|
||||
- Reuse the same plan path for XML generation and post-create verification for that deck.
|
||||
|
||||
## Artifact Lifecycle
|
||||
|
||||
`.lark-slides/` is local agent state. It supports recovery, iteration, and later edits, but it should not be treated as source code or committed by default.
|
||||
|
||||
Keep:
|
||||
|
||||
- `.lark-slides/plan/<deck-or-task-id>/slide_plan.json` after successful creation or major rewrite. The plan is the editable design state for the deck.
|
||||
- A small manifest when useful for follow-up work, such as `xml_presentation_id`, slide IDs, `revision_id`, plan path, and verification status.
|
||||
|
||||
Clean or avoid keeping:
|
||||
|
||||
- Transient XML payloads after successful creation and verification. Prefer `/tmp` for throwaway XML, or delete generated XML files after success.
|
||||
- Stale XML drafts that no longer match the current presentation state.
|
||||
|
||||
Exception:
|
||||
|
||||
- If creation fails or partially succeeds, keep the relevant XML/debug payloads until recovery is complete. Record `xml_presentation_id` first, then fetch current state before retrying.
|
||||
|
||||
## JSON Shape
|
||||
|
||||
```json
|
||||
{
|
||||
"presentation_goal": "Explain the proposal and secure approval for the next phase.",
|
||||
"audience": "Product and engineering leaders who know the domain but need a concise decision narrative.",
|
||||
"theme_style": "Clean business style, light background, restrained blue accent, strong visual hierarchy.",
|
||||
"visual_system": {
|
||||
"background_strategy": "Content pages use one light base; cover and closing may use a related dark treatment with the same accent system.",
|
||||
"motif": "A reusable left accent bar and consistent card/header treatments.",
|
||||
"color_roles": {
|
||||
"primary": "Used for the dominant structural motif and about 60-70% of visual weight.",
|
||||
"secondary": "Used for grouped regions, comparison panels, or supporting categories.",
|
||||
"accent": "Used only for key numbers, conclusions, or focus markers."
|
||||
}
|
||||
},
|
||||
"typography_constraints": {
|
||||
"title_max_lines": 2,
|
||||
"body_max_lines_per_box": 2,
|
||||
"footer_max_lines": 1,
|
||||
"long_text_handling": "Shorten, split into multiple boxes, or move detail to speaker notes instead of shrinking into a tight box."
|
||||
},
|
||||
"verification_plan": {
|
||||
"check_background_consistency": true,
|
||||
"check_text_fit": true,
|
||||
"check_visual_focus": true,
|
||||
"check_asset_rendering": true
|
||||
},
|
||||
"slides": [
|
||||
{
|
||||
"page": 1,
|
||||
"title": "Proposal Title",
|
||||
"key_message": "The initiative is ready for a focused pilot.",
|
||||
"layout_type": "title-cover",
|
||||
"visual_focus": "Large title area with one concise supporting statement.",
|
||||
"asset_need": {
|
||||
"asset_type": "logo",
|
||||
"purpose": "Signal product or team identity on the opening page.",
|
||||
"suggested_query": "product logo",
|
||||
"fallback_if_missing": "Use a small text badge and abstract shape motif instead of a real logo."
|
||||
},
|
||||
"text_density": "low",
|
||||
"speaker_intent": "Frame the decision and establish the deck's point of view."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Required Fields
|
||||
|
||||
Top-level fields:
|
||||
|
||||
- `presentation_goal`: what the whole deck is trying to achieve.
|
||||
- `audience`: target readers or listeners and their assumed background.
|
||||
- `theme_style`: visual tone, palette direction, and professional style.
|
||||
- `visual_system`: deck-level visual rules that must stay stable across pages, including background strategy, recurring motif, and color roles.
|
||||
- `typography_constraints`: deck-level limits for line count, text box density, and how to handle long text before XML generation.
|
||||
- `verification_plan`: explicit checks to perform after creation or major edits; include background consistency, text fit, visual focus, and asset rendering when relevant.
|
||||
- `slides`: ordered page plans.
|
||||
|
||||
Each slide must include:
|
||||
|
||||
- `page`: 1-based page number.
|
||||
- `title`: slide title.
|
||||
- `key_message`: the one idea this page must land.
|
||||
- `layout_type`: planned page structure.
|
||||
- `visual_focus`: dominant visual object or region.
|
||||
- `asset_need`: planning-only structured asset metadata; no search, download, or upload required. Follow `asset-planning.md`.
|
||||
- `text_density`: `low`, `medium`, or `high`.
|
||||
- `speaker_intent`: why the speaker needs this page and how it advances the story.
|
||||
|
||||
Optional slide fields:
|
||||
|
||||
- `chart_contract`: required when the page plan includes a standard data chart that `<chart>` supports. Use this shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"chart_contract": {
|
||||
"required": true,
|
||||
"render_as": "native_chart",
|
||||
"chart_type": "line",
|
||||
"data_source": "mock_placeholder",
|
||||
"data_series_required": true,
|
||||
"placeholder_label_required": true,
|
||||
"manual_shape_fallback_allowed": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When `chart_contract.required == true`, XML generation must produce a `<chart>` element on that slide. A shape, line, polyline, or whiteboard approximation does not satisfy the plan.
|
||||
|
||||
`data_source` must be one of:
|
||||
|
||||
- `user_provided`: the user supplied concrete values, tables, CSV, or metric lists; use them and do not replace them with mock data.
|
||||
- `mock_placeholder`: the user asked for a placeholder, template, example, or later-replaceable chart position; use mock data in native `<chart>`.
|
||||
- `mock_required_by_intent`: the user did not provide concrete values but asked for data expression, charts, trends, comparisons, or distributions; use mock data in native `<chart>`.
|
||||
|
||||
`data_series_required` means the generated XML must include `<chartData>`. It does not require user-provided real-world values. When real values are unavailable but chart expression is part of the user's intent, write mock or placeholder values into native `<chart>` and label them clearly instead of switching to manual drawing primitives or metric blocks.
|
||||
|
||||
## Layout Vocabulary
|
||||
|
||||
Use one of these `layout_type` values unless the user explicitly needs a custom structure:
|
||||
|
||||
- `title-cover`
|
||||
- `section-divider`
|
||||
- `two-column`
|
||||
- `image-left-text-right`
|
||||
- `image-right-text-left`
|
||||
- `big-number`
|
||||
- `timeline`
|
||||
- `comparison`
|
||||
- `architecture-diagram`
|
||||
- `process-flow`
|
||||
- `quote-highlight`
|
||||
- `conclusion`
|
||||
|
||||
The value must affect XML geometry, not just appear as a label. For example, `timeline` should create a horizontal or vertical sequence, `comparison` should create distinct side-by-side regions, and `big-number` should reserve dominant space for a large metric.
|
||||
|
||||
## Text Density Rules
|
||||
|
||||
- `low`: title plus 1 short statement, or 1-3 very short labels.
|
||||
- `medium`: title plus 2-4 concise bullets or labeled regions.
|
||||
- `high`: allowed only when the user needs detail; use tables, columns, or grouped regions instead of a long bullet list.
|
||||
|
||||
Do not let all pages become title + bullet slides. For decks of 4 or more pages, aim for at least 4 different `layout_type` values when the content allows it.
|
||||
|
||||
Text density must be realistic for the planned geometry. If a page needs long titles, bilingual labels, paper figure captions, legal disclaimers, or dense technical wording, record how the text will be shortened, split, or moved to speaker notes. Do not rely on small font sizes or tight boxes to make text fit.
|
||||
|
||||
## Visual System Planning
|
||||
|
||||
Before generating XML, define a visual system that can survive the whole deck:
|
||||
|
||||
- `background_strategy`: specify the default background for normal content pages, and which page roles may intentionally differ. Do not let pages drift through near-identical but inconsistent background colors.
|
||||
- `motif`: choose one or two reusable structural devices, such as a side bar, header rail, numbered node, card treatment, diagram lane, or section band. The motif should appear consistently enough that pages feel related.
|
||||
- `color_roles`: assign primary, secondary, and accent roles. The same color must not mean unrelated things across pages.
|
||||
- `cover_content_relationship`: if the cover uses a different dark or image-led treatment, state how it connects to content pages through shared colors, motifs, or geometry.
|
||||
- `closing_relationship`: if the closing page mirrors the cover, state that explicitly so it looks intentional rather than like a new theme.
|
||||
|
||||
These are planning constraints, not decoration notes. They must affect coordinates, background fills, shape styles, and text placement in generated XML.
|
||||
|
||||
## Iterative Deck State
|
||||
|
||||
When continuing an existing deck, update the same plan path rather than creating a new disconnected plan. Keep the plan aligned with what has actually been created.
|
||||
|
||||
Recommended optional fields for long-running work:
|
||||
|
||||
- `deck_status`: current slide count, target slide count if known, and last verified revision or timestamp.
|
||||
- `created_slides`: page number, slide id when known, and the page role.
|
||||
- `assets_used`: source, local path when applicable, uploaded token when known, and which page uses it.
|
||||
- `open_issues`: known layout, text fit, asset, or consistency risks that still need correction.
|
||||
|
||||
Do not hard-code a page number just because a previous deck used that pattern. Plan by page role and evidence need, such as "method overview pages should use a figure when the source has a readable figure" instead of binding screenshots, charts, or diagrams to a fixed page index. The plan should describe decision rules, not a rigid template sequence.
|
||||
|
||||
## Asset Planning
|
||||
|
||||
`asset_need` is metadata. It can describe a desired figure, diagram, chart, icon, logo, screenshot, or fallback shape-based visual, but it must not require web search, local download, or media upload.
|
||||
|
||||
Use an object for one planned asset, an array for multiple real needs, or `asset_type: "none"` when no asset is useful. Each planned asset must include:
|
||||
|
||||
- `asset_type`: one of `paper_figure`, `architecture_diagram`, `icon`, `logo`, `chart`, `infographic`, `screenshot`, `flow_diagram`, or `none`.
|
||||
- `purpose`: why this asset helps the page's key message.
|
||||
- `suggested_query`: short future lookup hint only; do not execute it unless separately requested.
|
||||
- `fallback_if_missing`: concrete XML-native visual plan using shapes, labels, tables, whiteboard diagrams, or placeholder panels.
|
||||
- `chart_contract`: when `asset_type` is `chart` and the visual is a supported standard data chart, set this optional slide-level field so generation is locked to native `<chart>`.
|
||||
|
||||
For detailed rules and examples, read `asset-planning.md`.
|
||||
|
||||
Good examples:
|
||||
|
||||
- `{"asset_type":"architecture_diagram","purpose":"Explain component relationships.","suggested_query":"service architecture diagram","fallback_if_missing":"Draw a component diagram with grouped boxes, connector arrows, and short labels."}`
|
||||
- `{"asset_type":"logo","purpose":"Identify the customer context.","suggested_query":"customer logo","fallback_if_missing":"Use a text label in a small badge."}`
|
||||
- `{"asset_type":"chart","purpose":"Show adoption trend.","suggested_query":"monthly adoption trend chart","fallback_if_missing":"Render a native `<chart>` using the provided series when available; otherwise render a native `<chart>` with mock placeholder values and label it as 模拟数据,仅占位,待替换真实数据."}`
|
||||
|
||||
## XML Generation Contract
|
||||
|
||||
Before writing each slide XML, map the plan fields to concrete decisions:
|
||||
|
||||
- `key_message` determines the headline, dominant claim, or main takeaway.
|
||||
- `layout_type` determines the coordinate structure and element types. Use `visual-planning.md` for concrete layout rules.
|
||||
- `visual_focus` determines the largest visual region or emphasized object.
|
||||
- `text_density` caps visible text volume.
|
||||
- `asset_need` informs placeholder diagrams, icons, charts, screenshots, or shape-based fallback visuals only. Missing real assets must use `fallback_if_missing`, not blank regions.
|
||||
- `chart_contract` locks supported standard data charts to native `<chart>` output. Manual approximations are allowed only when the planned chart type is unsupported by `<chart>` or when the visual is explicitly non-data/decorative.
|
||||
|
||||
After creating the PPT, fetch the presentation and verify:
|
||||
|
||||
- Page count matches the plan.
|
||||
- Every page has the planned title and key message represented.
|
||||
- At least several pages have visibly different XML layout structures.
|
||||
- Planned `visual_focus` appears as a dominant visual region or object.
|
||||
- Asset planning is proportional to the deck topic and length: technical, research, product, and analytical decks should include meaningful planned visuals where they clarify the story, and each planned asset has a visible fallback if no real asset was used.
|
||||
- `text_density` is reflected in the amount of visible text.
|
||||
- Pages are not crowded, and any planned `timeline`, `comparison`, or `architecture-diagram` page uses its matching visual structure.
|
||||
- The actual backgrounds match `visual_system.background_strategy`; any dark, image-led, or emphasis page has an intentional relationship to the rest of the deck.
|
||||
- Text boxes respect `typography_constraints`; long labels, captions, footer text, and conclusion bars are not squeezed into boxes that are too short for the intended line count.
|
||||
- If real assets are used, the final XML contains renderable asset tokens or supported local placeholders for creation, not http URLs, stale local paths, or blank image boxes.
|
||||
119
skills/lark-slides/references/planning.md
Normal file
119
skills/lark-slides/references/planning.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# 规划(大纲 + slide_plan.json + 资产)
|
||||
|
||||
生成前把叙事、页面角色、视觉重点、文字密度和资产需求固定下来,避免从用户提示直接跳到 XML。观感规则见 `design.md`,版式几何见 `layout.md`,创建后核对见 `verify.md`。
|
||||
|
||||
## 大纲先给用户确认
|
||||
|
||||
生成 XML 前先产出一份大纲交用户确认,格式:
|
||||
|
||||
```text
|
||||
[PPT 标题] — [定位描述],面向 [目标受众]
|
||||
|
||||
页面结构(N 页):
|
||||
1. 封面页:[标题文案]
|
||||
2. [页面主题]:[要点1]、[要点2]、[要点3]
|
||||
...
|
||||
N. 结尾页:[结尾文案]
|
||||
|
||||
风格:[配色方案],[排版风格]
|
||||
```
|
||||
|
||||
确认过主题、受众、页数、风格后再进入 plan。
|
||||
|
||||
## 何时必写 plan
|
||||
|
||||
新建演示文稿、生成新 deck、重排多页或替换整页结构时,必须先写 `slide_plan.json` 再生成 XML。
|
||||
|
||||
小型已有页编辑可豁免:只替换一个标题、改一个数字、插入一个块、上传并插入一张图。只要任务会重排多页或替换整页结构,仍需规划层。
|
||||
|
||||
## Plan 目录与生命周期
|
||||
|
||||
每个 deck 用独立目录 `.lark-slides/plan/<id>/`,同一 workspace 里多个 deck 不互相覆盖。
|
||||
|
||||
- `<id>` 取值:新 deck 用标题 slug + 日期时间(如 `q3-review-20260507-1805`);改写已有 PPT 用 `xml_presentation_id`;无标题任务用短 slug + 日期时间。
|
||||
- 不复用 `.lark-slides/plan/slide_plan.json` 这类共享路径。
|
||||
- 先建目录再写文件:`mkdir -p .lark-slides/plan/<id>`。
|
||||
- 同一 deck 的 XML 生成和创建后核对复用同一 plan 路径。
|
||||
|
||||
`.lark-slides/` 是本地 agent 状态,支持恢复、迭代和后续编辑,不当源码、默认不提交。创建或大改写成功后保留 `slide_plan.json`(deck 可编辑的设计状态);核对通过后清理临时 XML(throwaway XML 放 `/tmp` 或成功后删除);创建失败或部分成功时保留相关 XML/debug 直到恢复完成,先记 `xml_presentation_id` 再回读当前状态后重试。
|
||||
|
||||
## slide_plan.json 形态
|
||||
|
||||
下为最小示例,实际填齐下文所有必填字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"presentation_goal": "...",
|
||||
"audience": "...",
|
||||
"theme_style": "...",
|
||||
"visual_system": { "background_strategy": "...", "motif": "...", "color_roles": {} },
|
||||
"typography_constraints": { "title_max_lines": 2, "long_text_handling": "..." },
|
||||
"verification_plan": {},
|
||||
"slides": [
|
||||
{
|
||||
"page": 1, "title": "...", "key_message": "...",
|
||||
"layout_type": "...", "visual_focus": "...",
|
||||
"asset_need": { "asset_type": "logo", "purpose": "...", "suggested_query": "...", "fallback_if_missing": "..." },
|
||||
"text_density": "low", "speaker_intent": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
顶层字段:
|
||||
|
||||
- `presentation_goal`:整个 deck 要达成什么。
|
||||
- `audience`:目标读者/听众及其假定背景。
|
||||
- `theme_style`:视觉基调、配色方向、专业风格。
|
||||
- `visual_system`:跨页必须稳定的 deck 级视觉规则(背景策略、复现 motif、颜色角色)。
|
||||
- `typography_constraints`:行数、文本框密度、长文本处理的 deck 级上限。
|
||||
- `verification_plan`:创建/大改后的显式检查(背景一致性、文字容纳、视觉重点、资产渲染)。
|
||||
- `slides`:有序页面规划。
|
||||
|
||||
每页字段:
|
||||
|
||||
- `page`:1-based 页码。 `title`:页标题。 `key_message`:本页必须落地的唯一观点。
|
||||
- `layout_type`:规划的页面结构(词表见 `layout.md`)。 `visual_focus`:主视觉对象或区域。
|
||||
- `asset_need`:规划态资产元数据,不触发搜索/下载/上传。
|
||||
- `text_density`:`low` / `medium` / `high`(规则见 `layout.md`)。
|
||||
- `speaker_intent`:讲者为何需要这页、如何推进叙事。
|
||||
|
||||
## chart_contract(可选页级字段)
|
||||
|
||||
页面计划含 `<chart>` 支持的标准数据图时必填:
|
||||
|
||||
```json
|
||||
{ "chart_contract": { "required": true, "render_as": "native_chart", "chart_type": "line",
|
||||
"data_source": "mock_placeholder", "data_series_required": true, "manual_shape_fallback_allowed": false } }
|
||||
```
|
||||
|
||||
`required == true` 时该页 XML 必须产出 `<chart>` 元素;shape/line/polyline/whiteboard 近似不满足契约。`data_series_required` 要求 XML 含 `<chartData>`,不要求真实值。
|
||||
|
||||
`data_source` 三值取一:
|
||||
|
||||
- `user_provided`:用户给了具体值/表/CSV/指标,用之,不替换成 mock。
|
||||
- `mock_placeholder`:用户要占位/模板/示例/可后替换的图位,用 mock 数据填原生 `<chart>`。
|
||||
- `mock_required_by_intent`:用户没给具体值但要求数据表达/趋势/对比/分布,用 mock 数据填原生 `<chart>`。
|
||||
|
||||
真实值缺失但图表表达属用户意图时,写 mock/占位值进原生 `<chart>` 并明确标注,不退回手绘或指标块。
|
||||
|
||||
## 资产规划
|
||||
|
||||
`asset_need` 是元数据,可描述图、图标、图表、流程图、时序图、架构图、装饰图案、截图或示意图需求,但不要求 web 搜索、本地下载或媒体上传。单个用对象,多个真实需求用数组,无用资产用 `asset_type: "none"`。
|
||||
|
||||
每项必带:
|
||||
|
||||
- `asset_type`:`paper_figure` / `architecture_diagram` / `icon` / `logo` / `chart` / `infographic` / `screenshot` / `flow_diagram` / `none` 之一。
|
||||
- `purpose`:为何有助本页 `key_message`。
|
||||
- `suggested_query`:未来查找提示,不主动执行。
|
||||
- `fallback_if_missing`:具体到能转 XML 的原生视觉方案(shape/label/table/whiteboard/占位面板)。
|
||||
|
||||
规则:
|
||||
|
||||
- 每项资产必带 `fallback_if_missing`,最终 XML 不留空图框,缺素材即渲染 fallback;弱写法禁用:「用占位」「另找图」「没有就留空」「用通用装饰」。
|
||||
- 资产必须服务本页 `key_message` 和 `visual_focus`,不加不澄清页面的装饰资产;少数高价值资产优于每页一图,6 页技术/商业 deck 内容允许时至少 3 页规划资产。
|
||||
- `chart` 类若为支持的标准数据图,`fallback_if_missing` 仍须渲染原生 `<chart>`,不用手绘/`<whiteboard>` 模仿;`<chart>` 不支持 funnel/scatter,此类映射到 `<whiteboard>` SVG。
|
||||
|
||||
## 续作更新
|
||||
|
||||
继续已有 deck 时更新同一 plan 路径,不新建断链 plan,保持 plan 与已创建内容对齐。按页面角色和证据需求规划(如「方法概览页在源有可读图时用图」),不因上个 deck 用过某页号就硬绑固定页号;plan 描述决策规则,不是刚性模板序列。
|
||||
@@ -1,201 +0,0 @@
|
||||
# Slide XML 模板
|
||||
|
||||
可直接复制使用的 slide XML 模板。纯文本/形状模板可使用 `jq` 包装后传给 `xml_presentation.slide.create`:
|
||||
|
||||
```bash
|
||||
lark-cli slides xml_presentation.slide create --as user \
|
||||
--params '{"xml_presentation_id":"YOUR_ID"}' \
|
||||
--data "$(jq -n --arg content 'PASTE_XML_HERE' '{slide:{content:$content}}')"
|
||||
```
|
||||
|
||||
> **带图模板不要直接按上面的命令提交。** 新建 PPT 时可在 `+create --slides` 中使用 `src="@./local.png"`,CLI 会自动上传并替换为 `file_token`;给已有 PPT 添加或修改图片时,必须先用 `slides +media-upload` 拿到 `file_token`,再写进 `<img src="...">`。
|
||||
|
||||
## 深色封面页
|
||||
|
||||
```xml
|
||||
<slide xmlns="http://www.larkoffice.com/sml/2.0">
|
||||
<style><fill><fillColor color="linear-gradient(135deg,rgba(15,23,42,1) 0%,rgba(56,97,140,1) 100%)"/></fill></style>
|
||||
<data>
|
||||
<shape type="text" topLeftX="80" topLeftY="160" width="800" height="70">
|
||||
<content><p textAlign="center"><strong><span color="rgb(255,255,255)" fontSize="44">主标题</span></strong></p></content>
|
||||
</shape>
|
||||
<shape type="text" topLeftX="80" topLeftY="250" width="800" height="35">
|
||||
<content><p textAlign="center"><span color="rgb(148,163,184)" fontSize="20">副标题</span></p></content>
|
||||
</shape>
|
||||
<shape type="text" topLeftX="80" topLeftY="420" width="800" height="25">
|
||||
<content><p textAlign="center"><span color="rgb(100,116,139)" fontSize="14">底部信息</span></p></content>
|
||||
</shape>
|
||||
</data>
|
||||
</slide>
|
||||
```
|
||||
|
||||
## 浅色内容页
|
||||
|
||||
```xml
|
||||
<slide xmlns="http://www.larkoffice.com/sml/2.0">
|
||||
<style><fill><fillColor color="rgb(248,250,252)"/></fill></style>
|
||||
<data>
|
||||
<shape type="rect" topLeftX="60" topLeftY="40" width="4" height="35">
|
||||
<fill><fillColor color="rgb(59,130,246)"/></fill>
|
||||
</shape>
|
||||
<shape type="text" topLeftX="76" topLeftY="36" width="600" height="45">
|
||||
<content><p><strong><span color="rgb(15,23,42)" fontSize="28">页面标题</span></strong></p></content>
|
||||
</shape>
|
||||
<shape type="text" topLeftX="60" topLeftY="100" width="840" height="380">
|
||||
<content textType="body" lineSpacing="multiple:1.8">
|
||||
<p><span color="rgb(51,65,85)" fontSize="15">正文段落</span></p>
|
||||
<ul>
|
||||
<li><p><span color="rgb(51,65,85)" fontSize="15">要点一</span></p></li>
|
||||
<li><p><span color="rgb(51,65,85)" fontSize="15">要点二</span></p></li>
|
||||
<li><p><span color="rgb(51,65,85)" fontSize="15">要点三</span></p></li>
|
||||
</ul>
|
||||
</content>
|
||||
</shape>
|
||||
</data>
|
||||
</slide>
|
||||
```
|
||||
|
||||
## 数据卡片页(横排指标)
|
||||
|
||||
```xml
|
||||
<slide xmlns="http://www.larkoffice.com/sml/2.0">
|
||||
<style><fill><fillColor color="rgb(248,250,252)"/></fill></style>
|
||||
<data>
|
||||
<shape type="text" topLeftX="60" topLeftY="36" width="600" height="45">
|
||||
<content><p><strong><span color="rgb(15,23,42)" fontSize="28">数据概览</span></strong></p></content>
|
||||
</shape>
|
||||
<!-- 卡片 1 -->
|
||||
<shape type="rect" topLeftX="60" topLeftY="100" width="260" height="140">
|
||||
<fill><fillColor color="rgb(255,255,255)"/></fill>
|
||||
<border color="rgba(0,0,0,0.08)" width="1"/>
|
||||
</shape>
|
||||
<shape type="text" topLeftX="60" topLeftY="115" width="260" height="50">
|
||||
<content><p textAlign="center"><strong><span color="rgb(59,130,246)" fontSize="36">数值</span></strong></p></content>
|
||||
</shape>
|
||||
<shape type="text" topLeftX="60" topLeftY="175" width="260" height="25">
|
||||
<content><p textAlign="center"><span color="rgb(100,116,139)" fontSize="14">指标名称</span></p></content>
|
||||
</shape>
|
||||
<!-- 卡片 2:topLeftX="350" -->
|
||||
<!-- 卡片 3:topLeftX="640" -->
|
||||
</data>
|
||||
</slide>
|
||||
```
|
||||
|
||||
## 带图版式
|
||||
|
||||
> **关键提醒**:`<img>` 的 `width:height` = 原图比例时才不会被裁剪。每个模板都标注了图框比例和建议原图比例,**选模板前先对照你的素材比例**,不要硬塞(如把横图放进竖框,会被左右裁掉大半)。把 `@./your-image.jpg` 替换为实际路径(仅 `+create --slides` 支持 `@` 占位符;其他场景需先用 `slides +media-upload` 拿 `file_token`)。
|
||||
|
||||
### 封面右图(左字右图)
|
||||
|
||||
图框 400×225(**16:9**),建议原图:横幅 16:9(桌面壁纸、产品 banner、landscape 照片)
|
||||
|
||||
```xml
|
||||
<slide xmlns="http://www.larkoffice.com/sml/2.0">
|
||||
<style><fill><fillColor color="linear-gradient(135deg,rgba(15,23,42,1) 0%,rgba(56,97,140,1) 100%)"/></fill></style>
|
||||
<data>
|
||||
<shape type="text" topLeftX="60" topLeftY="180" width="450" height="80">
|
||||
<content><p><strong><span color="rgb(255,255,255)" fontSize="44">主标题</span></strong></p></content>
|
||||
</shape>
|
||||
<shape type="text" topLeftX="60" topLeftY="270" width="450" height="40">
|
||||
<content><p><span color="rgb(186,230,253)" fontSize="20">副标题</span></p></content>
|
||||
</shape>
|
||||
<line startX="60" startY="350" endX="180" endY="350">
|
||||
<border color="rgb(59,130,246)" width="3"/>
|
||||
</line>
|
||||
<shape type="text" topLeftX="60" topLeftY="370" width="450" height="30">
|
||||
<content><p><span color="rgb(203,213,225)" fontSize="13">底部信息</span></p></content>
|
||||
</shape>
|
||||
<!-- 图框 400×225 = 16:9;原图建议 16:9 横幅 -->
|
||||
<img src="@./your-landscape.jpg" topLeftX="540" topLeftY="157" width="400" height="225"/>
|
||||
</data>
|
||||
</slide>
|
||||
```
|
||||
|
||||
### 三卡片带图(上图下文)
|
||||
|
||||
每个图框 240×180(**4:3**),建议原图:4:3 或接近正方形的图(产品照、截图、icon 类)
|
||||
|
||||
```xml
|
||||
<slide xmlns="http://www.larkoffice.com/sml/2.0">
|
||||
<style><fill><fillColor color="rgb(248,250,252)"/></fill></style>
|
||||
<data>
|
||||
<shape type="text" topLeftX="60" topLeftY="40" width="600" height="45">
|
||||
<content><p><strong><span color="rgb(15,23,42)" fontSize="28">核心亮点</span></strong></p></content>
|
||||
</shape>
|
||||
<line startX="60" startY="95" endX="140" endY="95">
|
||||
<border color="rgb(59,130,246)" width="3"/>
|
||||
</line>
|
||||
|
||||
<!-- 卡片 1 -->
|
||||
<shape type="rect" topLeftX="60" topLeftY="130" width="270" height="360">
|
||||
<fill><fillColor color="rgb(255,255,255)"/></fill>
|
||||
<border color="rgba(0,0,0,0.08)" width="1"/>
|
||||
</shape>
|
||||
<!-- 图框 240×180 = 4:3;原图建议 4:3 -->
|
||||
<img src="@./your-image-1.jpg" topLeftX="75" topLeftY="150" width="240" height="180"/>
|
||||
<shape type="text" topLeftX="75" topLeftY="345" width="240" height="30">
|
||||
<content><p><strong><span color="rgb(15,23,42)" fontSize="18">特性一</span></strong></p></content>
|
||||
</shape>
|
||||
<shape type="text" topLeftX="75" topLeftY="380" width="240" height="90">
|
||||
<content><p><span color="rgb(71,85,105)" fontSize="14">简短描述文案,控制在两行以内。</span></p></content>
|
||||
</shape>
|
||||
|
||||
<!-- 卡片 2:复制卡片 1,shape/img 的 topLeftX 改为 345 / 360 -->
|
||||
<!-- 卡片 3:复制卡片 1,shape/img 的 topLeftX 改为 630 / 645 -->
|
||||
</data>
|
||||
</slide>
|
||||
```
|
||||
|
||||
### 左右分栏(图在左,文在右)
|
||||
|
||||
图框 360×540(**2:3 竖幅**),建议原图:2:3 或 3:4 竖幅(人像照、产品竖拍、海报)
|
||||
|
||||
> 如果你只有横幅图,不要硬塞进这个竖框 —— 改用"顶部横幅图 + 下方文字"的版式(把这里的图框改成 960×240 横条放在顶部)。
|
||||
|
||||
```xml
|
||||
<slide xmlns="http://www.larkoffice.com/sml/2.0">
|
||||
<style><fill><fillColor color="rgb(255,255,255)"/></fill></style>
|
||||
<data>
|
||||
<!-- 图框 360×540 = 2:3;原图建议 2:3 或 3:4 竖幅 -->
|
||||
<img src="@./your-portrait.jpg" topLeftX="0" topLeftY="0" width="360" height="540"/>
|
||||
|
||||
<shape type="text" topLeftX="410" topLeftY="80" width="490" height="50">
|
||||
<content><p><strong><span color="rgb(15,23,42)" fontSize="30">场景标题</span></strong></p></content>
|
||||
</shape>
|
||||
<line startX="410" startY="140" endX="490" endY="140">
|
||||
<border color="rgb(59,130,246)" width="3"/>
|
||||
</line>
|
||||
<shape type="text" topLeftX="410" topLeftY="160" width="490" height="50">
|
||||
<content><p><span color="rgb(71,85,105)" fontSize="16">一句话描述这个场景的价值。</span></p></content>
|
||||
</shape>
|
||||
<shape type="text" topLeftX="410" topLeftY="230" width="490" height="250">
|
||||
<content textType="body" lineSpacing="multiple:1.8">
|
||||
<ul>
|
||||
<li><p><span color="rgb(51,65,85)" fontSize="15">要点一</span></p></li>
|
||||
<li><p><span color="rgb(51,65,85)" fontSize="15">要点二</span></p></li>
|
||||
<li><p><span color="rgb(51,65,85)" fontSize="15">要点三</span></p></li>
|
||||
</ul>
|
||||
</content>
|
||||
</shape>
|
||||
</data>
|
||||
</slide>
|
||||
```
|
||||
|
||||
## 深色结尾页
|
||||
|
||||
```xml
|
||||
<slide xmlns="http://www.larkoffice.com/sml/2.0">
|
||||
<style><fill><fillColor color="linear-gradient(135deg,rgba(15,23,42,1) 0%,rgba(56,97,140,1) 100%)"/></fill></style>
|
||||
<data>
|
||||
<shape type="text" topLeftX="80" topLeftY="190" width="800" height="55">
|
||||
<content><p textAlign="center"><strong><span color="rgb(255,255,255)" fontSize="36">感谢语或行动号召</span></strong></p></content>
|
||||
</shape>
|
||||
<line startX="410" startY="260" endX="550" endY="260">
|
||||
<border color="rgb(59,130,246)" width="2"/>
|
||||
</line>
|
||||
<shape type="text" topLeftX="80" topLeftY="280" width="800" height="30">
|
||||
<content><p textAlign="center"><span color="rgb(148,163,184)" fontSize="16">补充说明</span></p></content>
|
||||
</shape>
|
||||
</data>
|
||||
</slide>
|
||||
```
|
||||
@@ -1,45 +1,77 @@
|
||||
# Troubleshooting
|
||||
# 排障
|
||||
|
||||
本文件覆盖 lark-slides 的 XML 排障和常见失败处理。
|
||||
出错时怎么定位与修复。正常验证清单见 `verify.md`,命令与参数用法见 `cli-operations.md`,XML 语法见 `xml-protocol.md`。
|
||||
|
||||
## Failure Order
|
||||
## 创建前自检
|
||||
|
||||
遇到 `invalid param`、某一页创建失败、页面空白或布局错乱时,按顺序处理:
|
||||
生成 XML、发请求前先过一遍,多数失败能在这一步拦住:
|
||||
|
||||
1. 先判断是否已有可用的 `xml_presentation_id`:从成功 stdout、错误 hint、用户给定链接或已保存上下文中获取;没有 ID 时不要回读,直接按当前错误处理。
|
||||
2. 如果有 `xml_presentation_id`,再用 `slides +xml-get` 尝试回读,确认是否存在演示文稿、是否已有部分页面写入、或是否只是空 presentation。
|
||||
3. 检查失败页是否含未转义字符:`Q&A -> Q&A`,文本 `<` / `>` 写成 `<` / `>`,属性 URL `a=1&b=2 -> a=1&b=2`。
|
||||
4. 检查标签闭合、属性引号、`<content>` 结构,以及 `<slide>` 直接子元素。
|
||||
5. 如果使用 `--slides '[...]'`,怀疑 shell 截断时直接切到两步创建:先 `slides +create`,再用 `xml_presentation.slide create` 逐页添加。
|
||||
- **转义**:文本 `Q&A → Q&A`、`<` / `>` → `<` / `>`;属性 URL `a=1&b=2 → a=1&b=2`。
|
||||
- **结构合法**:标签闭合、属性引号成对、`<content>` 结构完整、`<slide>` 直接子元素合法。
|
||||
- **shell 截断**:`--slides '[...]'` 长参数 / 嵌套引号易被截断;复杂 XML 用 `jq --rawfile` 组装免转义,或直接走两步创建。
|
||||
- **图片 token**:`<img src>` 必须是 `+media-upload` 拿到的 `file_token`;`@path` 占位符只在 `+create --slides` 中替换,直接调 `slide.create` 不替换;禁外链 URL。
|
||||
- **坐标越界**:所有元素坐标限 960×540 画布内,越界会破图或报结构错误。
|
||||
|
||||
## Symptom Fixes
|
||||
## 提交前 overlap lint
|
||||
|
||||
| 看到的问题 | 处理方式 |
|
||||
|-----------|----------|
|
||||
| 文字被截断 / 看不全 | 增大 shape 的 `width` 或 `height`,或减少文本量 |
|
||||
| 元素重叠 | 调整 `topLeftX` / `topLeftY`,拉开间距 |
|
||||
| 页面大面积空白 | 回读确认内容是否写入;若内容存在,再缩小间距或增加主体元素 |
|
||||
| 文字和背景色太接近 | 深色背景用浅色文字,浅色背景用深色文字 |
|
||||
| 表格列宽不合理 | 调整 `colgroup` 中 `col` 的 `width` 值 |
|
||||
| 图表没有显示 | 检查 `chartPlotArea` 和 `chartData` 是否都包含,`dim1` / `dim2` 数据数量是否匹配 |
|
||||
| 图片被裁掉一部分 | `<img>` 的 `width` / `height` 是裁剪后尺寸;要整图显示就让 `width:height` 对齐原图比例 |
|
||||
| 图片不显示 / `<img src>` 仍是 `@path` | `@` 占位符只在 `+create --slides` 中替换;直接调 `xml_presentation.slide create` 必须先用 `+media-upload` 拿 `file_token` |
|
||||
| 新插入的 `<img>` 挡住原有元素 | `slide.get` 读原页,对照已有块坐标挑空白位置;空间不够就在同一批 `--parts` 里先移动/缩小现有块再插图 |
|
||||
| 渐变背景变成白色 | 渐变必须用 `rgba()` 格式 + 百分比停靠点,如 `linear-gradient(135deg,rgba(30,60,114,1) 0%,rgba(59,130,246,1) 100%)` |
|
||||
提交整页 XML 前本地必须跑,`summary.error_count == 0` 才能调接口:
|
||||
|
||||
## Common Errors
|
||||
```bash
|
||||
python3 scripts/xml_text_overlap_lint.py --input <presentation-or-slide>.xml
|
||||
```
|
||||
|
||||
| 错误码 / 信号 | 含义 | 解决方案 |
|
||||
|--------------|------|----------|
|
||||
| 400 XML 格式错误 | XML 语法错误 | 检查标签闭合、属性引号、特殊字符转义 |
|
||||
| 400 请求包装错误 | `--data` 未按 schema 包装 | 检查是否传入 `xml_presentation.content` 或 `slide.content` |
|
||||
| 创建成功但页面空白 / 内容缺失 / 布局错乱 | 常见于 `--slides '[...]'` 的 shell 转义或长参数传递问题 | 改用两步创建,并在创建后立即读取 XML 验证 |
|
||||
| 403 权限不足 | 身份或 scope 不匹配 | 先检查是否误用了 bot 身份,再确认 scope 和文档权限;无权限时根据错误响应引导用户解决 |
|
||||
| 404 演示文稿不存在 | `xml_presentation_id` 不正确或无权限 | 检查 token;wiki URL 需先解析真实 `obj_token` |
|
||||
| 404 幻灯片不存在 | `slide_id` 不正确 | 重新读取 presentation 或 slide,确认最新 ID |
|
||||
| 400 无法删除唯一幻灯片 | 演示文稿至少保留一页 | 先创建新页,再删除旧页 |
|
||||
| 1061002 媒体上传 params error | slides 媒体上传参数不符合约定 | 用 `slides +media-upload`,不要手拼原生 `medias/upload_all`;slides 唯一可用 `parent_type` 是 `slide_file` |
|
||||
| 1061004 forbidden | 当前身份对演示文稿无编辑权限 | 确认 user/bot 对目标 PPT 有编辑权限;bot 常见于 PPT 非该 bot 创建 |
|
||||
| 3350001 | XML 非 well-formed、XML 结构不符合服务端要求,或 replace 片段问题 | 优先检查未转义字符;replace 场景再看 `block_id` 和 `<content/>` |
|
||||
| 3350002 | `revision_id` 大于当前版本 | 用 `-1` 取当前版本,或重新用 `slides +xml-get` 取最新 `revision_id` |
|
||||
| validation: unsafe file path | `--file` 给了绝对路径或上层路径 | `--file` 必须是 CWD 内相对路径;先 `cd` 到素材目录再执行 |
|
||||
它查 XML well-formed、SXSD tag/attr 支持、IconPark 类型与填充可见性、文本明显重叠、whiteboard 与外部 sibling 边界重叠;**不查**越界、文本高度不足、图文压盖、底部拥挤——这些靠 `layout.md` 与回读把关。
|
||||
|
||||
| code | 含义 → 处理 |
|
||||
|---|---|
|
||||
| `xml_not_well_formed` | 语法/转义错 → 修标签闭合、引号、`&`/`<`/`>` 转义 |
|
||||
| `sml_prefixed_tag` | SML 用了命名空间前缀 → 用默认 xmlns 或无前缀标签 |
|
||||
| `sxsd_unsupported_tag` | 不支持的标签 → 按 hint 换(`textbox→<shape type="text">`、`image→<img>`) |
|
||||
| `sxsd_unsupported_attr` | 不支持的属性 → 按 hint 换(`x→topLeftX`、`fontColor→color`) |
|
||||
| `iconpark_unsupported_icon_type` | iconType 不在名单 → 用 `iconpark_tool.py` 重搜 |
|
||||
| `icon_missing_fill_color` / `icon_transparent_fill_color` | icon 无/透明填充 → 给非透明 `fillColor` |
|
||||
| `bbox_overlap` | 文本框估算区域重叠 → 拉开坐标 / 缩框 / 改分栏 |
|
||||
| `whiteboard_external_overlap` | whiteboard 与外部元素跨界重叠 → 按 hint 缩小/移动;接受风险则须截图 QA |
|
||||
|
||||
## 失败处理顺序
|
||||
|
||||
不假设任何操作原子成功(`+create --slides`、`+replace-pages` 均非原子,中途失败已建内容保留)。遇到 `invalid param` / 某页失败 / 空白 / 布局错乱时:
|
||||
|
||||
1. 先找是否已有 `xml_presentation_id`(成功 stdout、错误 hint、用户链接、已存上下文);没有 ID 就不回读,直接按当前错误处理。
|
||||
2. 有 ID 就 `slides +xml-get` 回读,确认演示文稿是否存在、是否已部分写入、还是空 presentation。
|
||||
3. 只修出问题的局部,不重建整份:用 `+replace-slide` 改坏页 / 补漏页,别用 `+create` 另起链接。
|
||||
4. 疑似 shell 截断时切两步创建:先 `+create` 建空白,再 `slide.create` 逐页添加。
|
||||
|
||||
## 错误码
|
||||
|
||||
| 码 / 信号 | 含义 | 对策 |
|
||||
|---|---|---|
|
||||
| 3350001 | XML 非 well-formed / 结构不符 / `block_id` 不存在 | 先查未转义字符与结构;replace 场景重新 `slide.get` 回读拿最新 3 位 short id 再填 |
|
||||
| 3350002 | `revision_id` 大于当前版本(不存在) | 用 `-1` 取最新,或回读拿实际 `revision_id` |
|
||||
| 400 XML 格式错误 | 标签 / 引号 / 转义问题 | 按创建前自检逐项排查 |
|
||||
| 400 请求包装错误 | `--data` 未按 schema 包装 | 确认含 `xml_presentation.content` 或 `slide.content` |
|
||||
| 403 权限不足 | 身份 / scope 不匹配 | 先查是否误用 bot,再确认 scope 与文档编辑权限 |
|
||||
| 404 演示文稿不存在 | `xml_presentation_id` 错或无权限 | 检查 token;wiki URL 先解析真实 `obj_token` |
|
||||
| 404 幻灯片不存在 | `slide_id` 错 | 回读 presentation / slide 确认最新 ID |
|
||||
| 400 无法删除唯一幻灯片 | 至少保留一页 | 先建新页再删旧页 |
|
||||
| 1061002 | 媒体上传 params error | 用 `slides +media-upload`,勿手拼 `medias/upload_all`;唯一可用 `parent_type` 是 `slide_file` |
|
||||
| 1061004 | forbidden,当前身份对 PPT 无编辑权限 | 确认 user/bot 对目标有编辑权限;bot 常见于 PPT 非其所建 |
|
||||
| 1061044 / unsafe file path | `--file` 给了绝对路径或 `../` 上层路径 | `--file` 须 CWD 内相对;先 `cd` 到素材目录 |
|
||||
|
||||
## 现象 → 原因 → 对策
|
||||
|
||||
| 现象 | 原因 | 对策 |
|
||||
|---|---|---|
|
||||
| 页面大面积空白 | 内容未写入,或间距过大 | 先回读确认是否写入;已写入则缩间距 / 加主体元素 |
|
||||
| 图片破图 / 不显示 | `src` 写了外链 URL 或仍是 `@path` | 换成 `+media-upload` 的 `file_token` |
|
||||
| 图片被裁掉 | `width:height` 与原图比例不符 | 对齐原图比例(`<img>` 尺寸即裁剪后尺寸) |
|
||||
| 新插入 `<img>` 挡住原元素 | 坐标压到已有块 | `slide.get` 对照已有块坐标挑空白位;空间不够就同批 `--parts` 先移 / 缩现有块再插 |
|
||||
| 文本溢出 / 看不全 | shape 太小或文本过多 | 增大 `width` / `height`,或减文本量 |
|
||||
| 元素重叠 | 坐标冲突 | 调 `topLeftX` / `topLeftY` 拉开间距 |
|
||||
| 文字看不清 | 与背景色太近 | 深底浅字、浅底深字 |
|
||||
| 渐变背景回退白色 | 渐变格式不合规 | 用 `rgba()` + 百分比停靠点,如 `linear-gradient(135deg,rgba(30,60,114,1) 0%,rgba(59,130,246,1) 100%)` |
|
||||
| 新页跑到末尾 | `before_slide_id` 放进了 `--params` | 原生调用须放 `--data` body 与 slide 同级,放 `--params` 会被当未知 query 静默忽略 |
|
||||
| 图表不显示 | 缺 `chartPlotArea` / `chartData`,或维度数不匹配 | 补齐两者,核对 `dim1` / `dim2` 数据数量 |
|
||||
| 表格列宽不合理 | `col` 宽度不当 | 调 `colgroup` 中 `col` 的 `width` |
|
||||
|
||||
批量 `--parts` 任一条失败整批不生效(返 3350001);若响应带 `failed_part_index` / `failed_reason`,shortcut 原样透传,据此定位坏条目。
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
# Validation Checklist
|
||||
|
||||
创建或大幅改写演示文稿后,必须做一次显式验证。目标是发现空白页、XML 损坏、内容截断、明显溢出、弱视觉层级和未验证输出。
|
||||
|
||||
小型已有页编辑也要做对应范围的验证:至少读取被改页面或全文 XML,确认目标元素已更新且未破坏周边结构。
|
||||
|
||||
## Required Flow
|
||||
|
||||
1. 记录创建或编辑返回的 `xml_presentation_id`,以及已知的 `slide_id` / `revision_id`。
|
||||
2. 用 `slides +xml-get` 回读全文 XML 到本地文件。
|
||||
3. 检查实际页数是否符合计划或用户要求。
|
||||
4. 检查每页 `<data>` 内是否有预期主要元素。
|
||||
5. 检查没有明显空白页、破损页、缺失标题或缺失主视觉。
|
||||
6. 检查页面不是全部退化为标题加 bullet list。
|
||||
7. 检查视觉层级:标题、主视觉、支撑信息三者可区分。
|
||||
8. 检查明显溢出和布局风险:重叠、越界、底部拥挤、长文本框。
|
||||
9. 在最终回复中给出简短验证记录。
|
||||
|
||||
回读命令:
|
||||
|
||||
```bash
|
||||
lark-cli slides +xml-get --as user \
|
||||
--presentation "YOUR_ID" \
|
||||
--output .lark-slides/plan/<deck-or-task-id>/readback.xml \
|
||||
--json
|
||||
```
|
||||
|
||||
## Automated XML Text Overlap Lint
|
||||
|
||||
提交前本地 XML 必须运行 XML 语法和文本重叠静态检查:
|
||||
|
||||
```bash
|
||||
python3 skills/lark-slides/scripts/xml_text_overlap_lint.py --input <presentation-or-slide.xml>
|
||||
```
|
||||
|
||||
通过标准:
|
||||
|
||||
- `summary.error_count == 0`。任何 error 都必须先修复再提交接口。
|
||||
- 当前工具检查 XML well-formed、SXSD tag/attr 支持情况、IconPark icon 类型和 icon 填充可见性、文本元素之间的明显重叠,以及 whiteboard 容器与外部 sibling 元素的可疑边界重叠;它不检查越界、文本高度不足、图文压盖、表格/图表压盖或底部拥挤。
|
||||
- 该工具不能替代页数核对、关键内容核对或真实视觉验收。
|
||||
|
||||
常见 code 的处理方向:
|
||||
|
||||
| code | 含义 | 处理方式 |
|
||||
|------|------|----------|
|
||||
| `xml_not_well_formed` | XML 语法错误或文本未转义 | 修复标签闭合、属性引号、`&` / `<` / `>` 转义 |
|
||||
| `sml_prefixed_tag` | SML 元素使用了命名空间前缀,如 `<ns0:slide>` 或 `<sml:shape>` | 使用 `<slide xmlns="http://www.larkoffice.com/sml/2.0">` 的默认命名空间,或使用无前缀标签 |
|
||||
| `sxsd_unsupported_tag` | 使用了 SXSD 不支持的标签 | 按 lint `hint` 替换为受支持标签;常见如 `textbox -> <shape type="text">`、`image -> <img>` |
|
||||
| `sxsd_unsupported_attr` | 支持的标签上使用了不支持的属性 | 按 lint `hint` 改为支持的属性;常见如 `x -> topLeftX`、`fontColor -> color` |
|
||||
| `iconpark_unsupported_icon_type` | `<icon>` 使用了 `iconpark-index.json` 中不存在的 `iconType` | 按 lint `hint` 改为名单内的 `iconType`,或先用 `scripts/iconpark_tool.py` 搜索 |
|
||||
| `icon_missing_fill_color` | 视觉规范要求 `<icon>` 设置 `<fill><fillColor color="..."/></fill>`,避免图标不可见 | 给 `<icon>` 添加显式非透明填充色,例如 `rgba(37, 99, 235, 1)` |
|
||||
| `icon_transparent_fill_color` | `<icon>` 的 `fillColor` 是透明色,不满足视觉可见性要求 | 改成与背景有足够对比的非透明颜色 |
|
||||
| `bbox_overlap` | 文本元素的估算绘制区域明显重叠 | 拉开文本坐标、缩小文本框/字号,或改成明确的分栏/分组结构 |
|
||||
| `whiteboard_external_overlap` | whiteboard 容器 bbox 与外部 sibling 元素跨边界重叠 | 按 lint `hint` 缩小或移动 whiteboard / 外部元素;若接受该风险,最终必须以截图 QA 或等价渲染视觉检查为准 |
|
||||
|
||||
## Page Count And Structure
|
||||
|
||||
- 实际页数必须等于用户要求或 `slide_plan.json` 的页数。
|
||||
- 如果创建过程部分失败,先记录已创建的 `xml_presentation_id`,再回读确认哪些页已写入。
|
||||
- 每页都应包含 `<data>`,且 `<data>` 内至少有一个非背景主体元素。
|
||||
- 封面、章节页、总结页可以文字较少,但不能只有空背景。
|
||||
- 技术解释页、对比页、流程页、架构页必须有匹配的结构元素,例如分组框、连线、时间轴、表格或图形化区域。
|
||||
|
||||
## Expected Elements
|
||||
|
||||
按 `slide_plan.json` 和用户要求逐页核对:
|
||||
|
||||
- 标题或主结论存在,并能对应 `key_message`。
|
||||
- `layout_type` 对应的主要结构已生成。
|
||||
- `visual_focus` 是页面中最醒目或最大的信息区域之一。
|
||||
- `text_density` 影响了文本量,没有用长 bullet 框替代规划。
|
||||
- `asset_need` 有真实素材时已放入正确区域;没有真实素材时,`fallback_if_missing` 已用 XML 形状、线条、标签、表格或图表兜底。
|
||||
|
||||
如果用户指定了关键页,例如“架构解释”“Self-Attention 机制解释”“对比或演进视角”“总结页”,最终验证记录必须逐项说明这些页已存在。
|
||||
|
||||
## Blank Or Broken Page Signals
|
||||
|
||||
把下面情况视为需要修复后再交付:
|
||||
|
||||
- `<data/>` 为空,或只有背景、装饰线、空 `<content/>`。
|
||||
- 关键文本没有出现在回读 XML 中。
|
||||
- 图片仍是 `@./path`,或 `<img src>` 是 http(s) 外链。
|
||||
- 页面依赖的图片区域为空,且没有 fallback visual。
|
||||
- 返回 XML 缺页、页序明显错误,或某页内容被 shell 截断。
|
||||
- 大量形状坐标完全相同,导致主体内容重叠。
|
||||
- 渐变背景回退成空白或白底,导致文字不可读。
|
||||
|
||||
## Whiteboard Elements
|
||||
|
||||
`slide.get` 回读 XML 时,`<whiteboard>` 块只返回位置属性(`topLeftX`、`topLeftY`、`width`、`height`),SVG / Mermaid 内容**不随 XML 返回**。
|
||||
|
||||
- whiteboard 验证可以核对坐标是否越界:`topLeftX + width ≤ 960`,`topLeftY + height ≤ 540`;lint 还会报告 whiteboard 容器与外部 sibling 元素的可疑边界重叠。
|
||||
- SVG 和 Mermaid 内容的正确性无法通过回读 XML 验证,需要人工视觉验收。
|
||||
- 不要在验证记录中声称 whiteboard 内容已验证,除非用户确认了视觉效果。
|
||||
|
||||
## Layout And Overflow Risk
|
||||
|
||||
优先修复这些明显风险:
|
||||
|
||||
- 正文或标签框高度不足,文本很可能被截断。
|
||||
- 多个主体元素在同一区域重叠,而不是有意叠加背景。
|
||||
- 重要内容越过画布边界,或贴近底部超过 `y=500`。
|
||||
- 高密度页使用单个长 bullet list,没有分栏、表格或分组。
|
||||
- 标题、主视觉、正文的字号和颜色差异太弱,视觉层级不清。
|
||||
- 所有内容页都是同一套标题加 bullets 坐标。
|
||||
|
||||
## Verification Record
|
||||
|
||||
最终回复必须包含简短验证记录,建议格式:
|
||||
|
||||
```text
|
||||
验证记录:
|
||||
- 回读:已执行 slides +xml-get,实际页数 N / 预期 N。
|
||||
- 关键页:架构解释 / Self-Attention / 对比或演进 / 总结页均存在。
|
||||
- 结构:检查了主要 shape/img/table/chart 元素,无明显空白页或破损页。
|
||||
- 布局:检查了标题层级、主视觉、重叠/越界/文本溢出风险。
|
||||
```
|
||||
|
||||
不要声称完成了人工视觉验收,除非确实打开或获取了可视化结果。仅从 XML 静态检查得出的结论,应表述为“静态检查未发现明显问题”。
|
||||
38
skills/lark-slides/references/verify.md
Normal file
38
skills/lark-slides/references/verify.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# 创建后验证
|
||||
|
||||
创建或大改(重排多页、新建 deck、整页重建结构)后的唯一验证清单。agent 看不到渲染,必须用回读 XML 补上「眼睛」,此步不可省。命令细节见 `cli-operations.md`,修复与系统性失败见 `troubleshooting.md`。
|
||||
|
||||
小改(换一个标题、改一个数字、插一个块、传一张图)不走全量流程,但至少回读被改页或全文,确认目标元素已更新、周边结构未破。
|
||||
|
||||
## 为什么必须回读
|
||||
|
||||
接口返回成功不等于页面正确:空白页、破图、XML 损坏、内容截断、溢出、弱层级都不体现在返回值里。唯一可靠信号是把实际 XML 拉回来逐条核对,把它当作对渲染的静态代理。
|
||||
|
||||
```bash
|
||||
lark-cli slides +xml-get --as user --presentation "$PID" \
|
||||
--output .lark-slides/plan/$PID/readback.xml
|
||||
```
|
||||
|
||||
`+screenshot` 受应用白名单限制多数不可用;可用时作补充视觉验收,失败只记录、不谎称已截图(见 `cli-operations.md`)。
|
||||
|
||||
## 验证清单
|
||||
|
||||
回读后逐条核对,中文记录结论:
|
||||
|
||||
- **页数**:实际页数 == plan / 用户要求;部分失败先记 `xml_presentation_id` 再回读确认哪些页已写入。
|
||||
- **内容落地**:每页有 `<data>` 且含非背景主体元素;标题与 `key_message` 逐页对应,用户点名的关键页(架构、机制解释、对比 / 演进、总结等)逐项确认存在。
|
||||
- **无退化**:不是所有页都退化成「标题 + 一个长 bullet 框」;高密度页用表格 / 分栏 / 分组而非单条长列表。
|
||||
- **无破损**:无空白页(`<data/>` 空、只剩背景 / 装饰线 / 空 `<content/>`)、无破图、无 XML 损坏、无缺页 / 页序错乱 / shell 截断、无大量坐标完全重合导致的压盖。
|
||||
- **无溢出与 text-fit 风险**:静态核对文本框高度对得上字号与行数(长中文 / 中英混排 / 多 `<p>` 尤其危险)、无越界、正文不贴底超过 `y=500`、图注 / footer / 结论条不被塞进过矮的框。
|
||||
- **背景 / motif 跨页一致**:普通内容页共用同一底色,无近似漂移;封面 / 章节 / 强调 / 结尾页的深色或图导底与其余页有明确关系(共享主色、motif、几何)。
|
||||
- **视觉层级与多样性**:标题、主视觉、支撑信息三层字号 / 颜色可区分;`visual_focus` 是页面最大 / 最醒目区域;≥4 页的 deck 有 ≥4 种不同 `layout_type` 几何,非同一套坐标复用。
|
||||
- **图片合法**:最终 XML 里图片是合法 `file_token`,不是 `@./path` 占位、不是 http(s) 外链、不是空图框;缺真实素材的位置已用 `fallback_if_missing`(shape / line / table / 图表 / 占位面板)兜底。
|
||||
|
||||
> whiteboard 回读只返回坐标(可核对 `topLeftX+width ≤ 960`、`topLeftY+height ≤ 540`),SVG / Mermaid 内容不随 XML 返回,正确性无法静态验证;不要在记录里声称 whiteboard 内容已验收,除非有视觉确认。
|
||||
|
||||
## 不通过怎么办
|
||||
|
||||
- 个别页问题:局部 `+replace-slide` 修(读-改-写,见 `cli-operations.md`),不整页重建、不 `+create` 另建链接。
|
||||
- 系统性失败(大面积破损、反复失败、接口 / 校验层报错):转 `troubleshooting.md` 定位根因。
|
||||
|
||||
最终回复给一段简短验证记录(回读 / 页数 / 关键页 / 结构 / 布局)。仅凭 XML 静态检查的结论只能表述为「静态检查未发现明显问题」,不得声称完成人工视觉验收。
|
||||
@@ -1,255 +0,0 @@
|
||||
# Visual Planning
|
||||
|
||||
新建演示文稿或大幅改写页面时,在 `slide_plan.json` 完成后、生成 XML 前读取本文件。目标是让 `layout_type`、`visual_focus`、`text_density` 变成实际页面几何,而不是只写在 plan 里。
|
||||
|
||||
默认画布按 `960 x 540` 规划。模板 XML 可以覆盖具体坐标,但不能覆盖这些原则:页面要有主视觉区域、文本要受密度约束、不同 `layout_type` 必须产生明显不同的坐标结构。
|
||||
|
||||
## Core Rules
|
||||
|
||||
- `layout_type` must change geometry: element positions, region sizes, alignment, and visual rhythm must differ across page types.
|
||||
- `visual_focus` determines the largest or highest-contrast region. It can be an image, diagram, metric, quote, table, or shape-based placeholder.
|
||||
- `text_density` caps visible text:
|
||||
- `low`: title plus one short statement, or 1-3 labels.
|
||||
- `medium`: title plus 2-4 concise bullets or labeled regions.
|
||||
- `high`: use a table, columns, grouped labels, or annotations. Do not use one long bullet box.
|
||||
- Do not create a deck where every content page is title plus bullets. For 4 or more pages, use at least 4 different layout structures when the content allows.
|
||||
- Keep generous margins. Use `60-80` px outer margins on standard content pages unless a full-bleed image or cover treatment is intentional.
|
||||
- Reserve vertical space for titles. A typical content title area is `y=36..90`; main content should usually start at `y>=110`.
|
||||
- Avoid crowding the bottom edge. Keep non-background content above `y=500` unless it is a footer.
|
||||
- Prefer fewer, larger objects over many small text boxes.
|
||||
- Keep backgrounds consistent with the deck's `visual_system.background_strategy`. Normal content pages should use the same base background unless there is a clear page-role reason to change.
|
||||
- Treat text fit as a layout constraint, not a cleanup step. If a text box is too small for the intended line count, shorten the text, split it, or allocate more space before creating XML.
|
||||
|
||||
## Background And Motif Consistency
|
||||
|
||||
Decks can vary page backgrounds, but variation must be intentional and legible:
|
||||
|
||||
- Pick one default background for ordinary content pages and reuse it exactly. Avoid near-identical drift such as several slightly different off-white values unless it encodes a clear section change.
|
||||
- Cover, section divider, emphasis, and conclusion pages may use a dark, image-led, or high-contrast background. They must still share the deck's primary color, motif, edge treatment, typography, or geometry.
|
||||
- If a cover uses a split composition, make the split visible in the background or layout. For example, reserve a darker text region and a related but distinct visual region instead of placing all elements on one flat field.
|
||||
- Reuse a small number of visual devices: side bar, card radius, node style, line weight, icon container, or footer treatment. Do not introduce a new decorative language on each page.
|
||||
- Insert background and motif shapes before content elements so they do not cover text, images, or diagrams.
|
||||
|
||||
## Text Fit Guardrails
|
||||
|
||||
Use these as conservative minimums on a 960 x 540 canvas. Increase height when using bold text, Chinese text, mixed Chinese/English, or line spacing above default.
|
||||
|
||||
| Text use | Typical font size | Minimum height |
|
||||
|----------|-------------------|----------------|
|
||||
| Caption, 1 line | 10-12 | 18 |
|
||||
| Caption, 2 lines | 10-12 | 30 |
|
||||
| Body, 1 line | 13-16 | 24 |
|
||||
| Body, 2 lines | 13-16 | 40 |
|
||||
| Body, 2 lines, bold | 15-18 | 48 |
|
||||
| Headline, 1 line | 24-32 | 42 |
|
||||
| Title, 2 lines | 34-44 | 110 |
|
||||
|
||||
Additional rules:
|
||||
|
||||
- Do not put long Chinese sentences or long English phrases into `height=18` or `height=22` boxes. Those heights are for short labels only.
|
||||
- Footer/source text should usually be one short line. If it needs more, make it a real caption block above the footer area.
|
||||
- Bottom conclusion bars should be at least `40` px tall for one emphasized line and at least `54` px tall for two lines.
|
||||
- Diagram labels should be short enough to fit the shape. Prefer two short lines over one cramped long line.
|
||||
- When a text block has more than one `<p>`, size the box for multiple lines explicitly. Do not assume the renderer will auto-expand.
|
||||
- If a line contains mixed Chinese and English, budget more width than either language alone; mixed text wraps less predictably.
|
||||
|
||||
## Layout Types
|
||||
|
||||
### `title-cover`
|
||||
|
||||
Purpose: introduce the deck's point of view.
|
||||
|
||||
Geometry:
|
||||
- Use one dominant title block, usually `x=70..120`, `y=150..250`, `width=700..820`.
|
||||
- Add one subtitle or context line, not a bullet list.
|
||||
- Optional visual focus can be a full-bleed background, large side image, accent band, or abstract shape motif.
|
||||
- If the cover has a right-side diagram, screenshot, or motif cluster, use a split layout: keep the title/subtitle region within the left or central text region, and reserve a separate visual region so labels and connectors do not cross the title.
|
||||
- For split covers, make the background reinforce the composition, such as a darker text side and a related visual panel. Avoid one flat field where title and diagram compete for attention.
|
||||
- Keep source metadata to one short line where possible. If it wraps, shorten author lists or move details to notes.
|
||||
- The main title should be controlled, normally one or two lines. Do not let it occupy both the text region and the visual region.
|
||||
|
||||
Text:
|
||||
- `low` only unless the user explicitly asks for detail.
|
||||
|
||||
### `section-divider`
|
||||
|
||||
Purpose: reset rhythm and mark a new chapter.
|
||||
|
||||
Geometry:
|
||||
- Use a large section number, chapter label, or single centered claim.
|
||||
- Keep the page sparse. A divider is not a content page.
|
||||
- Visual focus can be one oversized number, a vertical accent bar, or a full-width band.
|
||||
|
||||
Text:
|
||||
- Title plus one phrase. No bullets.
|
||||
|
||||
### `two-column`
|
||||
|
||||
Purpose: compare two related ideas or pair explanation with evidence.
|
||||
|
||||
Geometry:
|
||||
- Split main region into two balanced columns, for example left `x=60,width=400`, right `x=500,width=400`.
|
||||
- Each column needs its own heading or visual anchor.
|
||||
- Do not place one full-width bullet box under a normal title; that is not a two-column layout.
|
||||
|
||||
Text:
|
||||
- `medium`: 2-3 short items per column.
|
||||
- `high`: use grouped rows or mini table structure inside columns.
|
||||
|
||||
### `image-left-text-right`
|
||||
|
||||
Purpose: let a visual establish context, with text explaining implication.
|
||||
|
||||
Geometry:
|
||||
- Left visual region should occupy roughly `35-45%` of slide width, often full height or tall crop.
|
||||
- Right text region starts around `x=420` and should have a strong headline plus short support.
|
||||
- If no real image is available, create a shape-based placeholder visual that matches `asset_need`.
|
||||
- For dense screenshots, paper figures, or product captures with small labels, allocate a larger visual region when possible: often `50-65%` of slide width or at least `320` px height.
|
||||
- Place screenshots in a deliberate frame or panel, and leave enough margin so axes, captions, and edge labels are not cropped by the slide boundary.
|
||||
|
||||
Text:
|
||||
- Keep right-side text short. Avoid more than 4 bullets.
|
||||
- For screenshot explanation pages, prefer 2-3 interpretation cards or callouts instead of a paragraph block.
|
||||
|
||||
### `image-right-text-left`
|
||||
|
||||
Purpose: lead with a message, then reinforce it with a visual.
|
||||
|
||||
Geometry:
|
||||
- Left text region starts around `x=60..90`, width `400..460`.
|
||||
- Right visual region occupies roughly `35-45%` of slide width.
|
||||
- Align the image or placeholder with the main text block, not only with the title.
|
||||
- For dense screenshots, paper figures, or product captures with small labels, increase the visual region and reduce text. A readable image is more valuable than a fully populated text column.
|
||||
|
||||
Text:
|
||||
- Use one main claim and 2-3 supporting points.
|
||||
- Keep callouts parallel and short. If a callout needs more than two lines, split it into a separate note or a new slide.
|
||||
|
||||
### `big-number`
|
||||
|
||||
Purpose: make one metric or fact memorable.
|
||||
|
||||
Geometry:
|
||||
- Reserve the largest object for the metric: font size often `64-110`, region at least `300 x 120`.
|
||||
- Set `autoFit="normal-auto-fit"` on the metric's `<content>` so an oversized number shrinks to fit its box instead of overflowing.
|
||||
- Pair the number with one explanation and optional 2-3 small supporting labels.
|
||||
- Do not bury the number in a bullet list or small card.
|
||||
|
||||
Text:
|
||||
- `low` or `medium`. If detail is needed, add small annotations around the metric.
|
||||
- Supporting labels must not compete with the number. Use compact labels, legends, or mini-cards rather than long explanatory bars.
|
||||
|
||||
### `timeline`
|
||||
|
||||
Purpose: show sequence, roadmap, history, or phases.
|
||||
|
||||
Geometry:
|
||||
- Create a horizontal or vertical spine with 3-6 milestones.
|
||||
- Each milestone should have a dot/card/date label connected by a line or arrow.
|
||||
- Title is separate from the sequence. The sequence is the visual focus.
|
||||
|
||||
Text:
|
||||
- Each milestone gets a short label and optional one-line explanation.
|
||||
- Do not use paragraph-length milestone descriptions.
|
||||
|
||||
### `comparison`
|
||||
|
||||
Purpose: make a choice, before/after, old/new, or option tradeoff clear.
|
||||
|
||||
Geometry:
|
||||
- Use two or three distinct panels, columns, or a table-like structure.
|
||||
- Headings must be visually aligned so differences are easy to scan.
|
||||
- Use color, border, icon, or label treatment to highlight the preferred option or key difference.
|
||||
|
||||
Text:
|
||||
- Use parallel wording across columns.
|
||||
- Avoid uneven long bullet lists that destroy comparability.
|
||||
|
||||
### `architecture-diagram`
|
||||
|
||||
Purpose: explain components, dependencies, or system flow.
|
||||
|
||||
Implementation: prefer Mermaid `<whiteboard>` (see `lark-slides-whiteboard.md`); use `<shape>` + `<line>` as fallback.
|
||||
|
||||
Geometry:
|
||||
- Main visual area should be a diagram, not prose.
|
||||
- Use grouped boxes, lanes, arrows or lines, and short labels.
|
||||
- Keep diagram labels concise. Put explanation in notes or a small side caption if needed.
|
||||
|
||||
Text:
|
||||
- Prefer labels of 1-5 words.
|
||||
- Use no more than one short explanatory text block.
|
||||
- If a node label needs two lines, size the node and the text box for two lines. Do not let labels overlap connectors.
|
||||
|
||||
### `process-flow`
|
||||
|
||||
Purpose: show operational steps, workflow, or cause-effect path.
|
||||
|
||||
Implementation: prefer Mermaid `<whiteboard>` (see `lark-slides-whiteboard.md`); use `<shape>` + `<line>` as fallback.
|
||||
|
||||
Geometry:
|
||||
- Use numbered steps connected by arrows or lines.
|
||||
- 3-5 steps is ideal for one slide. If there are more, group them into phases.
|
||||
- The flow direction must be visually obvious.
|
||||
|
||||
Text:
|
||||
- Each step gets a verb-led label and one short descriptor at most.
|
||||
- Step labels should be parallel in length and grammar. If one step needs a long explanation, move the explanation to a side note or speaker notes.
|
||||
|
||||
### `quote-highlight`
|
||||
|
||||
Purpose: emphasize a customer voice, principle, thesis, or decision statement.
|
||||
|
||||
Geometry:
|
||||
- Quote or claim is the dominant text object.
|
||||
- Use large type, generous whitespace, and optional attribution or context badge.
|
||||
- Do not combine a quote-highlight page with a normal bullet section.
|
||||
|
||||
Text:
|
||||
- One quote or statement, plus optional attribution. No bullets.
|
||||
|
||||
### `conclusion`
|
||||
|
||||
Purpose: close with decision, recommendation, or next action.
|
||||
|
||||
Geometry:
|
||||
- Use one dominant closing statement or call to action.
|
||||
- Add up to 3 next-step cards, checklist items, or owner/date labels.
|
||||
- Visual focus should be the recommendation or action, not decorative filler.
|
||||
|
||||
Text:
|
||||
- Keep the final page easy to remember. Avoid recap overload.
|
||||
- Conclusion pages may mirror the cover background, but must clearly reuse the deck's motif or color roles so the ending feels intentional.
|
||||
|
||||
## Screenshot And Paper Figure Pages
|
||||
|
||||
When a page uses a real screenshot, chart, paper figure, or product capture:
|
||||
|
||||
- Choose screenshot placement based on page role, not a fixed slide number. Method overview, evidence, comparison, and failure-analysis pages are common candidates; title, agenda, and conclusion pages usually are not.
|
||||
- Use the real asset only when it is readable at slide size. If the figure is too dense, crop to the relevant region, create a zoomed detail, or redraw the core message with native shapes.
|
||||
- A screenshot should normally be the visual focus. Do not shrink it into a decorative thumbnail while surrounding it with dense text.
|
||||
- Pair the image with a small number of interpretive annotations that tell the audience what to notice.
|
||||
- Always include a short source caption when using external or paper-derived visuals.
|
||||
- Verify the final XML contains a supported image token or creation-time local placeholder, not an unsupported external URL.
|
||||
|
||||
## Plan To XML Checklist
|
||||
|
||||
Before creating XML for each page, answer these checks:
|
||||
|
||||
1. Which region is the visual focus, and is it the largest or most prominent object?
|
||||
2. Does the XML geometry match the `layout_type` description above?
|
||||
3. Does `text_density` limit the number of paragraphs, bullets, labels, and text boxes?
|
||||
4. Would this page still be recognizable if the `layout_type` label were removed from the plan?
|
||||
5. Across the deck, do multiple pages use genuinely different structures?
|
||||
6. Does the background follow the planned deck strategy, and are any deviations intentional?
|
||||
7. Are all text boxes large enough for their intended font size and line count?
|
||||
8. If the page uses a screenshot or paper figure, is it large enough to read and accompanied by concise interpretation?
|
||||
|
||||
After fetching the created presentation, verify:
|
||||
|
||||
- Use `timeline`, `comparison`, and `architecture-diagram` only when the content calls for them; do not force irrelevant page types.
|
||||
- Any planned `timeline`, `comparison`, or `architecture-diagram` page uses the matching sequence, side-by-side comparison, or component-and-connection structure.
|
||||
- Pages are not crowded and do not rely on long bullet boxes.
|
||||
- Main claim, supporting detail, and visual focus have clear hierarchy.
|
||||
- Static XML inspection should include text-fit risk: very short text boxes containing long text, multi-paragraph boxes with insufficient height, footer text that may wrap, and labels placed directly over connectors.
|
||||
- Background and motif consistency should be checked across pages, not only within one slide.
|
||||
81
skills/lark-slides/references/whiteboard.md
Normal file
81
skills/lark-slides/references/whiteboard.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# Whiteboard 画板
|
||||
|
||||
`<whiteboard>` 放在 `<data>` 内,内部承载 SVG 或 Mermaid。本文只讲两种模式的语法与坑;「何时用 whiteboard vs chart vs shape」见 `cli-operations.md` 元素选型决策树。
|
||||
|
||||
## 定位
|
||||
|
||||
承载 `<chart>` / `<shape>` 覆盖不了的视觉:流程 / 时序 / 架构图、散点 / 漏斗 / 瀑布、自定义图标与示意、装饰图案(波浪背景、点阵、进度条)。
|
||||
标准数据图(柱 / 条 / 折线 / 面积 / 雷达 / 饼 / 环 / 组合)用原生 `<chart>`,不要用 whiteboard 手绘重画——原生更省力、更稳、更易回读编辑。
|
||||
|
||||
## 选路:SVG 还是 Mermaid(按模型身份)
|
||||
|
||||
流程 / 时序 / 架构 / 类图 / ER 等拓扑图优先 Mermaid(自动布局)。散点 / 漏斗 / 进度条 / 波浪背景 / 星点纹理等需精确控坐标配色的场景,按模型身份定:
|
||||
|
||||
| 模型身份 | 路径 |
|
||||
|---|---|
|
||||
| Claude / Gemini / GPT / GLM | **SVG**,精确控制坐标、颜色、透明度 |
|
||||
| Doubao / Seed / 其他 | **Mermaid** 近似表达;确实无法表达时才回退简单 SVG 矩形 / 线条 |
|
||||
|
||||
决定用 SVG 前先确认自己属于哪类,不要跳过这步。
|
||||
|
||||
## 公共属性
|
||||
|
||||
`topLeftX` / `topLeftY` / `width` / `height` 四个必填,均为 slide 坐标系(默认宽 960 高 540)。约束 `topLeftX+width ≤ 960`、`topLeftY+height ≤ 540`。
|
||||
SVG 内坐标相对 whiteboard 自身左上角 (0,0),与 slide 坐标系无关。
|
||||
|
||||
## 模式一:SVG
|
||||
|
||||
用于散点 / 漏斗等非原生数据图、自定义图标、装饰层、像素级可视化。
|
||||
|
||||
设计品质要求:不要只用矩形加文字应付(纯白底 + 方块 + 黑字=不及格);散点 / 漏斗等非原生数据视觉仍要有坐标轴、刻度、数值标注或分段说明,不要只画裸点 / 色块;字号要有层级(标题 ≠ 标签 ≠ 数值);配色呼应 slide 主题(深底用透明底 / 深色卡片,浅底避免再加纯白块)。深色底(亮度 < 30%)装饰"对比不足"比"过强"危害更大,宁重勿轻;装饰层次用亮度跳跃而非线性叠透明度——`α=0.04→0.08→0.12` 在深底几乎不可见,改用 `0.10→0.40→0.70→1.0`、相邻层亮度差 ≥60。批量定位 / 等间距 / 数据映射先用脚本算坐标再填,别手估。
|
||||
|
||||
```xml
|
||||
<whiteboard topLeftX="500" topLeftY="120" width="400" height="300">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="50" y="50" width="80" height="200" rx="4" fill="rgba(59,130,246,0.85)"/>
|
||||
<text x="90" y="270" text-anchor="middle" font-size="12" fill="rgba(100,116,139,1)">ABC</text>
|
||||
</svg>
|
||||
</whiteboard>
|
||||
```
|
||||
|
||||
- `<svg>` 必须声明 `xmlns="http://www.w3.org/2000/svg"`。
|
||||
- **内容大小由所有子元素的几何包围盒(含 stroke 外扩)合并决定**,自适应缩放到容器;`<svg>` 上的 `width` / `height` / `viewBox` 不影响内容区域计算。
|
||||
- **仅当元素属性用百分比值(如 `width="50%"`)时才需 `viewBox`** 提供计算基准;推荐统一用绝对坐标,避免百分比依赖。
|
||||
- whiteboard 的 `width` / `height` 应等于该包围盒尺寸——批量定位 / 等间距 / 数据映射时先跑脚本算坐标与包围盒再填,别手估。
|
||||
|
||||
支持元素:`<rect>`(`rx` 圆角)、`<circle>`、`<ellipse>`、`<line>`、`<path>`(Q/C 曲线)、`<text>`(含中文,`y` 为 baseline,需 ≥ font-size 免被裁)、`<polygon>`、`<g>`、`<linearGradient>`(配 `fill="url(#id)"`)。
|
||||
颜色统一用 `rgba(R,G,B,A)`;虚线 `stroke-dasharray="4,4"`;变换支持 `translate` / `rotate(deg cx cy)` / `scale`。
|
||||
|
||||
**不支持(渲染失败或降级,须避免)**:`<radialGradient>`(改 `<linearGradient>` 或 rgba 透明度)、`<filter>` 阴影 / 模糊(改半透明 `<rect>` 叠加)、`<clipPath>` / `<mask>`(调坐标尺寸自然裁切)、`<pattern>`(手铺点阵)、`skewX/skewY/matrix()`(改 rotate+translate)、`<image>` 外链 URL(先上传拿 file_token 用 `<img>`)。
|
||||
|
||||
**z-order**:whiteboard 在 XML 中越靠后渲染层级越高。全屏装饰 whiteboard 必须放在所有 `<shape>` / `<img>` / `<table>` 之前,否则遮挡内容。
|
||||
|
||||
## 模式二:Mermaid
|
||||
|
||||
用于拓扑图,自动布局、代码简洁。
|
||||
|
||||
```xml
|
||||
<whiteboard topLeftX="72" topLeftY="60" width="816" height="360">
|
||||
<mermaid>
|
||||
<![CDATA[
|
||||
flowchart TD
|
||||
A[编写每页 slide XML] --> B[slides +create]
|
||||
B --> C[回读验证]
|
||||
]]>
|
||||
</mermaid>
|
||||
</whiteboard>
|
||||
```
|
||||
|
||||
- **内容必须用 `<![CDATA[ ... ]]>` 包裹**:Mermaid 语法里的 `[`、`>`、`-->` 是 XML 特殊字符,不包会破坏解析。CDATA 结束符 `]]>` 不得出现在 Mermaid 代码本身。
|
||||
- 图会自动撑满 whiteboard 区域,只需四个坐标属性。
|
||||
|
||||
常用图型与关键字:流程图 / 决策树 / 架构图 `flowchart TD`|`flowchart LR`、时序图 `sequenceDiagram`、类图 `classDiagram`、ER 图 `erDiagram`、状态图 `stateDiagram-v2`、甘特图 `gantt`、思维导图 `mindmap`、用户旅程 `journey`。
|
||||
单图节点控制在 15 个以内,过密考虑分页;节点多时适当加 `height`(流程图 300-400、时序 320-420、思维导图 380-480)。
|
||||
|
||||
## 常见坑
|
||||
|
||||
- **CDATA**:Mermaid 忘包 CDATA → XML 解析失败;SVG 模式无需 CDATA。
|
||||
- **转义**:SVG / Mermaid 里出现的 `&`、`<`、`>` 若非合法标签需注意,Mermaid 交给 CDATA 兜底。
|
||||
- **尺寸**:whiteboard `width` / `height` 与子元素包围盒不匹配会拉伸内容或留白——按包围盒算,别手估;坐标推导建议留注释(originX/Y、chartW/H、映射公式)。
|
||||
- 创建失败先排查是否偶发错误码,重试一次再判定。
|
||||
- **提交前自检**:非原生数据图有轴 / 网格 / 数值(无裸点);字号分层;单系列同色、多系列异色且对比足;轴标签不与元素遮挡。
|
||||
@@ -1,418 +0,0 @@
|
||||
# XML 格式指南
|
||||
|
||||
本文档基于 [slides_xml_schema_definition.xml](slides_xml_schema_definition.xml) 整理,说明飞书 Slides XML Schema(SML 2.0)的核心结构和常用写法。
|
||||
|
||||
## 基本结构
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<presentation xmlns="http://www.larkoffice.com/sml/2.0" width="960" height="540">
|
||||
<title>演示文稿标题</title>
|
||||
<slide>
|
||||
<style>
|
||||
<fill>
|
||||
<fillColor color="rgb(245, 245, 245)"/>
|
||||
</fill>
|
||||
</style>
|
||||
<data>
|
||||
<shape type="text" topLeftX="80" topLeftY="80" width="800" height="120">
|
||||
<content textType="title">
|
||||
<p>主标题</p>
|
||||
</content>
|
||||
</shape>
|
||||
</data>
|
||||
<note>
|
||||
<content textType="body">
|
||||
<p>这是演讲者备注。</p>
|
||||
</content>
|
||||
</note>
|
||||
</slide>
|
||||
</presentation>
|
||||
```
|
||||
|
||||
## 根元素
|
||||
|
||||
### `<presentation>`
|
||||
|
||||
协议标准写法应带命名空间 `http://www.larkoffice.com/sml/2.0`;当前服务端实现可能兼容不带 `xmlns` 的输入,但不作为协议保证。
|
||||
|
||||
**属性:**
|
||||
|
||||
| 属性 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `width` | positiveInteger | 是 | 演示文稿宽度,如 `960` |
|
||||
| `height` | positiveInteger | 是 | 演示文稿高度,如 `540` |
|
||||
| `id` | string | 否 | 演示文稿标识 |
|
||||
|
||||
**子元素:**
|
||||
|
||||
| 元素 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `<title>` | 否 | 演示文稿标题 |
|
||||
| `<theme>` | 否 | 全局主题 |
|
||||
| `<slide>` | 是 | 幻灯片页面,至少 1 页,最多 100 页 |
|
||||
|
||||
## 主题
|
||||
|
||||
### `<theme>`
|
||||
|
||||
`<theme>` 当前包含两部分:
|
||||
|
||||
- `<background>`:演示文稿级背景填充
|
||||
- `<textStyles>`:主题文本样式集合
|
||||
|
||||
`<textStyles>` 下可选子元素:
|
||||
|
||||
- `<title>`
|
||||
- `<headline>`
|
||||
- `<sub-headline>`
|
||||
- `<body>`
|
||||
- `<caption>`
|
||||
|
||||
这些元素定义的是主题默认样式,不是页面结构。常用属性:
|
||||
|
||||
| 属性 | 说明 |
|
||||
|------|------|
|
||||
| `fontFamily` | 字体 |
|
||||
| `fontSize` | 字号 |
|
||||
| `fontColor` | 字体颜色 |
|
||||
|
||||
## 幻灯片元素
|
||||
|
||||
### `<slide>`
|
||||
|
||||
单张幻灯片的结构比较严格。
|
||||
|
||||
**属性:**
|
||||
|
||||
| 属性 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `id` | string | 否 | 幻灯片标识 |
|
||||
|
||||
**直接子元素只有:**
|
||||
|
||||
| 元素 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `<style>` | 否 | 页面样式 |
|
||||
| `<data>` | 否 | 页面元素容器 |
|
||||
| `<note>` | 否 | 演讲者备注 |
|
||||
|
||||
这意味着 `<title>`、`<headline>`、`<body>`、`<caption>` 不能直接放在 `<slide>` 下。
|
||||
|
||||
## 文本内容模型
|
||||
|
||||
### `<content>`
|
||||
|
||||
实际页面文本通常通过 `<content>` 表达,常见位置有:
|
||||
|
||||
- `shape` 内部
|
||||
- `table/td` 内部
|
||||
- `note` 内部
|
||||
|
||||
**常用属性:**
|
||||
|
||||
| 属性 | 说明 |
|
||||
|------|------|
|
||||
| `textType` | `title` / `headline` / `sub-headline` / `body` / `caption` |
|
||||
| `verticalAlign` | 垂直对齐 |
|
||||
| `textAlign` | 水平对齐 |
|
||||
| `lineSpacing` | 行间距 |
|
||||
| `fontSize` | 字号 |
|
||||
| `fontFamily` | 字体 |
|
||||
| `color` | 字体颜色 |
|
||||
| `bold` / `italic` / `underline` / `strikethrough` | 内容级样式 |
|
||||
| `wrap` | 是否自动换行 |
|
||||
|
||||
**可包含的子元素:**
|
||||
|
||||
- `<p>`
|
||||
- `<ul>`
|
||||
- `<ol>`
|
||||
|
||||
### `<p>`
|
||||
|
||||
`<p>` 是段落元素,可混排纯文本和内联标签:
|
||||
|
||||
- `<br/>`
|
||||
- `<strong>`
|
||||
- `<em>`
|
||||
- `<u>`
|
||||
- `<span>`
|
||||
- `<del>`
|
||||
- `<a>`
|
||||
- `<shadow>`
|
||||
- `<outline>`
|
||||
|
||||
示例:
|
||||
|
||||
```xml
|
||||
<content textType="body" textAlign="left">
|
||||
<p>普通文本 <strong>加粗</strong> <em>斜体</em> <a href="https://example.com">链接</a></p>
|
||||
<ul>
|
||||
<li><p>列表项 1</p></li>
|
||||
<li><p>列表项 2</p></li>
|
||||
</ul>
|
||||
</content>
|
||||
```
|
||||
|
||||
## 常用页面元素
|
||||
|
||||
所有页面元素都放在 `<data>` 中。
|
||||
|
||||
### `<shape>`
|
||||
|
||||
`shape` 可表示普通形状,也可表示文本框。文本框推荐使用 `type="text"`。
|
||||
|
||||
```xml
|
||||
<shape type="text" topLeftX="80" topLeftY="80" width="800" height="120">
|
||||
<content textType="title">
|
||||
<p>主标题</p>
|
||||
</content>
|
||||
</shape>
|
||||
```
|
||||
|
||||
```xml
|
||||
<shape type="rect" topLeftX="700" topLeftY="120" width="180" height="120">
|
||||
<fill>
|
||||
<fillColor color="rgba(100, 149, 237, 0.25)"/>
|
||||
</fill>
|
||||
<border color="rgb(100, 149, 237)" width="2"/>
|
||||
</shape>
|
||||
```
|
||||
|
||||
**属性:**
|
||||
|
||||
| 属性 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `type` | 是 | 形状类型,`text` 表示文本框 |
|
||||
| `topLeftX` | 是 | 左上角 X 坐标 |
|
||||
| `topLeftY` | 是 | 左上角 Y 坐标 |
|
||||
| `width` | 是 | 宽度 |
|
||||
| `height` | 是 | 高度 |
|
||||
| `rotation` | 否 | 旋转角度 |
|
||||
| `flipX` / `flipY` | 否 | 翻转 |
|
||||
| `alpha` | 否 | 透明度 |
|
||||
|
||||
**可选子元素:**
|
||||
|
||||
- `<fill>`
|
||||
- `<border>`
|
||||
- `<reflection>`
|
||||
- `<shadow>`
|
||||
- `<content>`
|
||||
|
||||
### `<line>`
|
||||
|
||||
```xml
|
||||
<line startX="100" startY="200" endX="420" endY="200">
|
||||
<border color="rgb(43, 47, 54)" width="2"/>
|
||||
</line>
|
||||
```
|
||||
|
||||
`line` 使用的是 `startX` / `startY` / `endX` / `endY`,不是 `x1` / `y1` / `x2` / `y2`。
|
||||
|
||||
### `<img>`
|
||||
|
||||
```xml
|
||||
<img src="file_token_or_url" topLeftX="100" topLeftY="220" width="320" height="180"/>
|
||||
```
|
||||
|
||||
`img` 使用 `topLeftX` / `topLeftY`,不是 `x` / `y`。
|
||||
|
||||
`src` 只接受两种值:
|
||||
|
||||
| `src` 形式 | 说明 |
|
||||
|---|---|
|
||||
| `file_token`(如 `boxcnXXXXXXXXXXXXXXXXXXXXXX`) | 通过 `slides +media-upload` 上传后返回的 token |
|
||||
| `@<本地路径>`(如 `@./assets/chart.png`) | **仅在 `slides +create --slides` 中可用**:CLI 会自动上传该文件并替换为 file_token |
|
||||
|
||||
> **禁止使用 http(s) 外链 URL**:飞书 slides 渲染端不会代理外链图片,`src="https://..."` 在 PPT 里通常显示破图。要用网图必须先 `curl`/下载到 CWD 内,再走上传流程拿 `file_token`。
|
||||
|
||||
本地图片的两种姿势:
|
||||
|
||||
- **新建带图 PPT**:`+create --slides` 里直接写 `src="@./pic.png"`,CLI 在创空白 PPT 后、加 slides 前自动上传并替换 token
|
||||
- **给已有 PPT 加带图新页**:先 `slides +media-upload --file ./pic.png --presentation $PID` 拿 token,再用 token 写进 `xml_presentation.slide create` 的 XML
|
||||
|
||||
### `<icon>`
|
||||
|
||||
```xml
|
||||
<icon iconType="iconpark/Base/setting.svg" topLeftX="440" topLeftY="220" width="32" height="32"/>
|
||||
```
|
||||
|
||||
### `<table>`
|
||||
|
||||
表格结构为:
|
||||
|
||||
- `<table>`
|
||||
- `<colgroup>` / `<tr>`
|
||||
- `<tr>` 内为 `<td>`
|
||||
- `<td>` 内可放 `<content>`
|
||||
|
||||
### `<chart>`
|
||||
|
||||
图表元素必须至少包含:
|
||||
|
||||
- `<chartPlotArea>`
|
||||
- `<chartData>`
|
||||
|
||||
同时还可以包含:
|
||||
|
||||
- `<chartTitle>`
|
||||
- `<chartSubTitle>`
|
||||
- `<chartStyle>`
|
||||
- `<chartLegend>`
|
||||
- `<chartTooltip>`
|
||||
|
||||
完整图表类型覆盖示例见 [slides_chart_demo.xml](slides_chart_demo.xml),其中包含柱状、条形、折线、面积、饼 / 环、雷达等原生 `<chart>` 示例,以及散点、气泡、漏斗、帕累托、瀑布等 `<whiteboard>` SVG 图表示例。
|
||||
|
||||
组合图示例(来自 [slides_chart_demo.xml](slides_chart_demo.xml)):
|
||||
|
||||
```xml
|
||||
<chart width="556" height="350" topLeftX="42" topLeftY="132">
|
||||
<chartPlotArea>
|
||||
<chartPlot type="combo">
|
||||
<chartExtra/>
|
||||
<chartSeriesList>
|
||||
<chartSeries index="1" comboType="column"/>
|
||||
<chartSeries index="2" comboType="line" yAxisPosition="right">
|
||||
<chartTooltip format="0%"/>
|
||||
</chartSeries>
|
||||
</chartSeriesList>
|
||||
</chartPlot>
|
||||
<chartAxes>
|
||||
<chartAxis type="x">
|
||||
<chartLabel fontSize="10"/>
|
||||
</chartAxis>
|
||||
<chartAxis type="y" position="left">
|
||||
<chartGridLine color="rgb(226, 232, 240)"/>
|
||||
<chartLabel fontSize="10"/>
|
||||
</chartAxis>
|
||||
<chartAxis type="y" position="right">
|
||||
<chartLabel fontSize="10" format="0%"/>
|
||||
</chartAxis>
|
||||
</chartAxes>
|
||||
</chartPlotArea>
|
||||
<chartLegend position="bottom" fontSize="11"/>
|
||||
<chartData>
|
||||
<dim1>
|
||||
<chartField name="季度">24Q1,24Q2,24Q3,24Q4,25Q1,25Q2,25Q3,25Q4</chartField>
|
||||
</dim1>
|
||||
<dim2>
|
||||
<chartField name="营收">180,195,210,245,220,238,258,296</chartField>
|
||||
<chartField name="增速">0.08,0.12,0.15,0.18,0.22,0.22,0.23,0.21</chartField>
|
||||
</dim2>
|
||||
</chartData>
|
||||
<chartTitle fontSize="12" color="rgba(15, 30, 58, 1)" bold="true">营收(亿美元, 左轴) · 同比增速(%, 右轴)</chartTitle>
|
||||
<chartStyle>
|
||||
<chartBackground color="rgba(0, 0, 0, 0)"/>
|
||||
<chartBorder color="rgb(222, 224, 227)" width="0"/>
|
||||
<chartColorTheme>
|
||||
<color value="rgb(28, 71, 120)"/>
|
||||
<color value="rgb(240, 129, 54)"/>
|
||||
</chartColorTheme>
|
||||
</chartStyle>
|
||||
</chart>
|
||||
```
|
||||
|
||||
## 样式元素
|
||||
|
||||
### `<fill>`
|
||||
|
||||
```xml
|
||||
<fill>
|
||||
<fillColor color="rgb(100, 149, 237)"/>
|
||||
</fill>
|
||||
```
|
||||
|
||||
### `<border>`
|
||||
|
||||
```xml
|
||||
<border color="rgb(0, 0, 0)" width="2" dashArray="solid"/>
|
||||
```
|
||||
|
||||
### 颜色格式
|
||||
|
||||
```xml
|
||||
<fillColor color="rgb(255, 0, 0)"/>
|
||||
<fillColor color="rgba(255, 0, 0, 0.5)"/>
|
||||
<fillColor color="linear-gradient(90deg, rgb(255,0,0) 0%, rgb(0,0,255) 100%)"/>
|
||||
<fillColor color="radial-gradient(circle at 50% 50%, rgb(255,0,0) 0%, rgb(0,0,255) 100%)"/>
|
||||
```
|
||||
|
||||
## 演讲者备注
|
||||
|
||||
### `<note>`
|
||||
|
||||
```xml
|
||||
<note>
|
||||
<content textType="body">
|
||||
<p>这是演讲者备注内容。</p>
|
||||
</content>
|
||||
</note>
|
||||
```
|
||||
|
||||
## 完整示例
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<presentation xmlns="http://www.larkoffice.com/sml/2.0" width="960" height="540">
|
||||
<title>季度报告</title>
|
||||
<theme>
|
||||
<textStyles>
|
||||
<title fontFamily="思源黑体" fontSize="54" fontColor="rgba(0, 0, 0, 1)"/>
|
||||
<body fontFamily="思源黑体" fontSize="18" fontColor="rgba(43, 47, 54, 1)"/>
|
||||
</textStyles>
|
||||
</theme>
|
||||
<slide>
|
||||
<style>
|
||||
<fill>
|
||||
<fillColor color="rgb(245, 245, 245)"/>
|
||||
</fill>
|
||||
</style>
|
||||
<data>
|
||||
<shape type="text" topLeftX="80" topLeftY="72" width="760" height="100">
|
||||
<content textType="title">
|
||||
<p>2024 年第一季度报告</p>
|
||||
</content>
|
||||
</shape>
|
||||
<shape type="text" topLeftX="80" topLeftY="200" width="520" height="180">
|
||||
<content textType="body">
|
||||
<p>核心指标</p>
|
||||
<ul>
|
||||
<li><p>用户增长:+25%</p></li>
|
||||
<li><p>收入增长:+30%</p></li>
|
||||
<li><p>市场份额:15%</p></li>
|
||||
</ul>
|
||||
</content>
|
||||
</shape>
|
||||
<shape type="rect" topLeftX="660" topLeftY="180" width="180" height="140">
|
||||
<fill>
|
||||
<fillColor color="rgba(100, 149, 237, 0.25)"/>
|
||||
</fill>
|
||||
<border color="rgb(100, 149, 237)" width="2"/>
|
||||
</shape>
|
||||
</data>
|
||||
<note>
|
||||
<content textType="body">
|
||||
<p>讲到增长率时补充样本范围。</p>
|
||||
</content>
|
||||
</note>
|
||||
</slide>
|
||||
</presentation>
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. 始终带上命名空间 `xmlns="http://www.larkoffice.com/sml/2.0"`
|
||||
2. 用 `shape type="text"` + `content` 表达页面文本
|
||||
3. 用 `topLeftX` / `topLeftY`、`startX` / `startY` 等 schema 中定义的属性名
|
||||
4. 优先使用 `rgb` / `rgba` 颜色格式
|
||||
5. 特殊字符按 XML 规则转义
|
||||
6. 标准 16:9 页面建议使用 `width="960"` 和 `height="540"`
|
||||
|
||||
## 参考文档
|
||||
|
||||
- [xml-schema-quick-ref.md](xml-schema-quick-ref.md)
|
||||
- [slides_xml_schema_definition.xml](slides_xml_schema_definition.xml)
|
||||
- [examples.md](examples.md)
|
||||
- [slides_demo.xml](slides_demo.xml)
|
||||
140
skills/lark-slides/references/xml-protocol.md
Normal file
140
skills/lark-slides/references/xml-protocol.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# XML 协议(SML 2.0)
|
||||
|
||||
飞书 Slides 结构化标记语言。本文件是 `slides_xml_schema_definition.xml`(XSD,唯一事实源)的分层摘要,只保留生成 PPT 真正会用到的元素、属性与已知坑;任何冲突以 XSD 为准。版式与布局节奏见 layout.md,写盘/导出 CLI 见 cli-operations.md,`<whiteboard>` 内部的 mermaid/SVG 语法见 whiteboard.md。
|
||||
|
||||
- 画布固定 **960×540**(`<presentation width="960" height="540">`,两属性必填)。坐标原点在左上,X 右增、Y 下增,单位 px。
|
||||
- **播放时只显示画布内 (0,0)–(960,540) 的内容**。坐标域虽允许 X∈[-8640,10560]、Y∈[-4860,5940](可用于出血或临时移出画布),但超出画布的部分不可见——正常内容必须落在 0–960 × 0–540 内。
|
||||
- 命名:元素小写、属性 camelCase、枚举 kebab-case。渲染层叠 = 文档顺序,先写的在底层;装饰形状必须写在文字之前。
|
||||
- 所有元素的 `id` 均可选(创建时可不写;块级编辑时由 CLI 自动注入)。
|
||||
- 只允许 XSD 里定义的元素/属性,未定义的(如 `<formula>`、`iconKeywords`、臆造的字距/字体属性)一律校验失败。
|
||||
|
||||
## 元素分类(taxonomy)
|
||||
|
||||
- **根/结构**:`<presentation>` → `<title>?` `<theme>?` `<slide>`(1–100) → `<style>?` `<data>?` `<note>?`。`<slide>` 的直接子元素**只有** style/data/note,不能直接放页面元素或裸文本。
|
||||
- **页面元素**(`<data>` 的直接子元素,互为兄弟,靠坐标定位,不互相嵌套):`<shape>` `<img>` `<icon>` `<chart>` `<table>` `<line>` `<polyline>` `<whiteboard>` `<undefined>`。
|
||||
- **文本容器**:`<content>`——只能作为 `<shape>` 或 `<td>` 的子元素;承载所有文本样式默认值。
|
||||
- **结构文本**:`<p>` `<ul>`/`<ol>` `<li>`——控制文本流(段落、列表层级),不控制外观。
|
||||
- **内联元素**(只能在 `<p>` 及内联元素内部):`<span>` `<br/>` `<strong>` `<em>` `<u>` `<del>` `<a>` `<shadow>` `<outline>`。
|
||||
- **视觉属性**(一律作为**子元素**,不是属性):`<fill>`/`<fillColor>`/`<fillImg>`/`<fillPattern>`、`<border>`、`<shadow>`、`<reflection>`、`<crop>`、`<startArrow>`/`<endArrow>`。`<shape fill="...">`、`<td border="...">` 均非法。
|
||||
|
||||
## 最小示例
|
||||
|
||||
```xml
|
||||
<presentation width="960" height="540">
|
||||
<slide id="s1">
|
||||
<style><fill><fillColor color="rgba(248,249,251,1)"/></fill></style>
|
||||
<data>
|
||||
<shape type="text" topLeftX="80" topLeftY="70" width="800" height="70">
|
||||
<content textType="title" fontSize="40" fontFamily="思源黑体" color="rgba(31,35,41,1)" textAlign="left" verticalAlign="top">
|
||||
<p>页面标题</p>
|
||||
</content>
|
||||
</shape>
|
||||
<shape type="round-rect" topLeftX="80" topLeftY="200" width="360" height="140" presetHandlers="8">
|
||||
<fill><fillColor color="rgba(255,255,255,1)"/></fill>
|
||||
<border color="rgba(224,226,230,1)" width="1"/>
|
||||
<content fontSize="16" fontFamily="思源黑体" color="rgba(51,51,51,1)" textAlign="left" verticalAlign="top" paddingTop="16" paddingLeft="16">
|
||||
<p><span bold="true">要点</span>正文说明文字。</p>
|
||||
<ul><li><p>列表项一</p></li><li><p>列表项二</p></li></ul>
|
||||
</content>
|
||||
</shape>
|
||||
</data>
|
||||
<note><content><p>演讲者备注纯文本</p></content></note>
|
||||
</slide>
|
||||
</presentation>
|
||||
```
|
||||
|
||||
## 页面元素规范
|
||||
|
||||
### shape
|
||||
容器与图形,唯一可直接携带文本的元素。
|
||||
- 必填:`type`(见形状枚举,`text`=文本框)、`topLeftX` `topLeftY` `width` `height`。
|
||||
- 可选:`rotation`[0,360) `alpha`[0,1] `flipX`/`flipY` `path`(仅 `type="custom"`,SVG 路径串,且只有 custom 允许)`presetHandlers`(如 round-rect 圆角半径)`vert`(horz/vert/vert270/word-art-vert/word-art-vert-rtl/ea-vert)。
|
||||
- 子元素(各至多一个):`<fill>` `<border>` `<reflection>` `<shadow>` `<content>`。
|
||||
- 默认填充:`type="text"` 透明 `rgba(255,255,255,0)`,其余类型 `rgba(222,224,227,1)`。空 `<border/>`:text 透明边框,其余取填充色 S+10%/B-10%。
|
||||
- 形状枚举含 rect、round-rect、ellipse、triangle、diamond、parallelogram、trapezoid、pentagon…、star4/5/6…、各类 arrow、callout、`flow-chart-*`、`action-button-*`、donut/arc/pie/chevron/can/cube 等;完整列表见 XSD `ShapeType`。
|
||||
|
||||
### img
|
||||
- 必填:`src`、`topLeftX` `topLeftY` `width` `height`。`src` **只接受** `+media-upload` 返回的 file_token 或 `@本地相对路径` 占位符;**禁止** http/https 外链、禁止绝对路径。
|
||||
- 可选:`rotation` `flipX`/`flipY` `alpha` `alt` `exposure` `contrast` `saturation` `temperature`。
|
||||
- 子元素:`<crop>` `<border>` `<reflection>` `<shadow>` `<fill>`。
|
||||
- **width/height 是裁剪后的显示尺寸**。原图先等比缩放铺满该区域再裁切,比例不符会裁掉多余部分;信息类图(图表/截图/示意图)须按原始比例给尺寸,无法确定原图比例时不要在 `<crop>` 上写 offset(会拉伸变形)。
|
||||
- `<crop>` 属性:`type`(默认 rect,支持任意 ShapeType,如 ellipse 做头像、round-rect+`presetHandlers` 做圆角)、`leftOffset`/`rightOffset`/`topOffset`/`bottomOffset`(px,正值内裁、负值外扩)、`presetHandlers`、`path`(仅 custom)。
|
||||
|
||||
### icon
|
||||
IconPark 图标,独立视觉对象。
|
||||
- 必填:`topLeftX` `topLeftY` `width` `height`。
|
||||
- 关键属性 `iconType`:IconPark 索引路径,形如 `iconpark/Base/setting.svg`(默认值同此)。**不是** iconKeywords。
|
||||
- 可选:`rotation` `flipX`/`flipY` `alpha`。
|
||||
- 子元素:`<fill>` `<border>` `<reflection>` `<shadow>`。**图标必须有非透明填充才可见**——显式写 `<fill><fillColor color="rgba(...,1)"/></fill>`;空 `<fill/>` 用默认灰 `rgba(208,211,214,1)`,无 `<fill>` 标签则不填充(不可见)。
|
||||
|
||||
### line / polyline
|
||||
- `<line>` 用**绝对端点坐标** `startX` `startY` `endX` `endY`(全协议唯一的坐标例外;必填);`type` 默认 straight-connector1,可选 `alpha`。
|
||||
- `<polyline>`(折线/曲线)用外接矩形 `topLeftX/topLeftY/width/height`(必填)+ `type`(bent-connector2–5 / curved-connector2–5,默认 bent-connector2)+ `presetHandlers` `rotation` `flipX/flipY` `alpha`。
|
||||
- 两者 `<border>` **必填**——无 border 线不可见;空 `<border/>` = `rgba(43,47,54,1)` / 宽 2px。可选子元素 `<startArrow>` `<endArrow>`(`type`:none/arrow/empty-triangle/solid-triangle/empty-diamond/solid-diamond/empty-circle/solid-circle,`widthScale`/`heightScale`:sm/med/lg)、`<shadow>` `<reflection>`。
|
||||
|
||||
### table
|
||||
- 必填:`topLeftX` `topLeftY`(可选 `flipX/flipY`)。表格无 width/height,宽=各列 width 之和。
|
||||
- 结构:`<colgroup>`?→`<col span?="1" width?="110"/>`;`<tr height?="37">`(多个)→`<td colspan?="1" rowspan?="1">`(多个)。
|
||||
- `<td>` 子元素(均为子元素而非属性):`<borderTop>`/`<borderRight>`/`<borderBottom>`/`<borderLeft>`(各 BorderType)、`<fill>`、`<content>`。相邻单元格线冲突时右下覆盖左上。空 `<border*/>` = `rgba(221,222,223,1)`/1px;空 `<fill/>` = 白。
|
||||
- 单元格文字不随主题色;表头与斑马纹须在各行 `<content>` 上显式写 color 保证对比。
|
||||
|
||||
### chart(内联数据可视化,非 `.svg` 外链)
|
||||
- 必填属性:`topLeftX` `topLeftY` `width` `height`;可选 `rotation` `flipX/flipY` `alpha`。
|
||||
- 子元素:`<chartPlotArea>`(必需) `<chartData>`(必需) `<chartTitle>?` `<chartSubTitle>?` `<chartStyle>?` `<chartLegend>?` `<chartTooltip>?` `<reflection>?` `<shadow>?`。
|
||||
- `<chartPlotArea>` = `<chartPlot type="…">`(必需) + `<chartAxes>?`。`chartPlot type`:line/area/bar/column/pie/radar/combo;其下可挂全局 `<chartPoints>/<chartLines>/<chartAreas>/<chartBars>/<chartLabels>`、`<chartSeriesList>`(含多个 `<chartSeries index="…">`)、`<chartExtra>`(`<chartRadar>`/`<chartSmooth>`/`<chartStep>`/`<chartStack>`)。样式优先级:单元素 > 系列 > 全局。
|
||||
- `<chartAxes>` → 多个 `<chartAxis type="x|y|angle|radius">`(`position` 仅 y 轴,`max`/`min` 可选),子元素 `<chartTitle>` `<chartLabel>` `<chartAxisLine>`(空标签即显示轴线)`<chartGridLine>`。
|
||||
- `<chartData>` = `<dim1>`(恰 1 个 `<chartField>` 作分类/X 轴)+ `<dim2>`(≥1 个 `<chartField>`,每个 = 一个系列)。`<chartField name="…" valueType="string|number">` 内容为**逗号分隔 CSV**(禁数组);string 含逗号需双引号包裹。dim1 第 n 值对应 dim2 各系列第 n 值。
|
||||
- 配色写 `<chartStyle><chartColorTheme><color value="rgb(...)"/>…`,按系列索引循环。示例:
|
||||
```xml
|
||||
<chart topLeftX="42" topLeftY="132" width="270" height="350">
|
||||
<chartPlotArea><chartPlot type="column"/><chartAxes>
|
||||
<chartAxis type="x"><chartLabel fontSize="9"/></chartAxis>
|
||||
<chartAxis type="y" position="left"><chartGridLine color="rgb(226,232,240)"/></chartAxis>
|
||||
</chartAxes></chartPlotArea>
|
||||
<chartData>
|
||||
<dim1><chartField name="季度">2024Q1,2024Q2,2024Q3,2024Q4</chartField></dim1>
|
||||
<dim2><chartField name="Apple">52,48,55,68</chartField><chartField name="Samsung">60,58,63,72</chartField></dim2>
|
||||
</chartData>
|
||||
<chartLegend position="bottom" fontSize="10"/>
|
||||
<chartStyle><chartColorTheme><color value="rgb(28,71,120)"/><color value="rgb(240,129,54)"/></chartColorTheme></chartStyle>
|
||||
</chart>
|
||||
```
|
||||
|
||||
### whiteboard / undefined
|
||||
- `<whiteboard>`:必填 `topLeftX/topLeftY/width/height`;子元素二选一 `<mermaid>`(CDATA 包裹的 Mermaid 源码,适合流程/时序/思维/类/甘特/ER 图)或一个 SVG 命名空间元素(像素级自定义图形),可选 `<border>`。详见 whiteboard.md。
|
||||
- `<undefined type="video|audio">` 仅用于承接导出时不支持的类型,生成时不主动使用。
|
||||
|
||||
## 文本容器与结构文本
|
||||
|
||||
### content
|
||||
文本样式默认值的落点——属性被后代文本继承,`<span>` 只做片段级覆盖。所有样式属性均可省略。
|
||||
- `textType`(默认 body)定语义层级;未显式写 `fontSize` 时按 `<theme>` 的 textStyles 取字号(默认主题 title 54 / headline 38 / sub-headline 32 / body 16 / caption 12)。`content` 自身 `fontSize`(6–400)无 XSD 默认,写了即覆盖。
|
||||
- 其余样式属性(省略即用渲染端默认,**不取 `<theme>` 主题色**):`fontFamily`(自由字符串、任意字体,如 思源黑体 / 思源宋体 / Inter)、`color`(**深底必须显式设浅色,否则文字可能不可见**)、`textAlign`(left/center/right/justify/dist;不写时 text 框靠左、其余居中)、`verticalAlign`(默认 middle,卡片正文常设 top)、`lineSpacing`(默认 `multiple:1.5`)、`letterSpacing`、`bold`/`italic`/`underline`/`strikethrough`、`backgroundColor`、`wrap`(默认 true)。
|
||||
- **文本溢出(关键,与多数引擎不同)**:`autoFit` 默认 `no-auto-fit`——框装不下文字时**直接溢出、不做任何处理,没有默认缩排**。承载密集或突出文字的 `<content>` 必须显式写 `autoFit="normal-auto-fit"`(框内缩排字号防溢出);`shape-auto-fit` 则让 shape 尺寸反过来随文字增大。
|
||||
- 内边距 `paddingTop/Right/Bottom/Left`(0–1584,逐边独立覆盖):`shape type="text"` 默认 0,其他 shape 默认 5,`<td>` 默认 8。
|
||||
- 子元素仅 `<p>` `<ul>` `<ol>`;**禁止裸文本、禁止直接放页面元素**。
|
||||
|
||||
### p / ul / ol / li
|
||||
- `<p>` 只接受流属性:`textAlign` `lineSpacing`/`beforeLineSpacing`/`afterLineSpacing`(`fixed:N`/`multiple:N`)`letterSpacing` `level`[1,10] `list`(bullet/number/none) `listStyle` `marginLeft` `indent`。外观(字号/色/粗斜)走内部 `<span>`。**所有文字必须包在 `<p>` 里**,单行也要。
|
||||
- 段间距用相邻 `<p>` 或 `beforeLineSpacing`/`afterLineSpacing`;`<br/>` 只在一个 `<p>` 内换行(如 姓名`<br/>`职位),不可用于制造间距、不可用于段落分隔、首尾 `<br/>` 无效。
|
||||
- `<ul listStyle?>`/`<ol listStyle?>` 只接受 `listStyle`(ul 默认 circle-hollow-square;ol 默认 number-lower-alpha-lower-roman、另有 hierarchical-number/circle-number/chinese-formal 等)。`<li>` 无样式属性,且**恰含一个 `<p>`**;`<ol>` 的 `<li>` 可带 `index`。项目符号自动生成,勿手写。
|
||||
|
||||
### 内联元素
|
||||
- `<span>` 是片段样式覆盖点:`fontSize` `fontFamily` `color` `backgroundColor` `bold` `italic` `underline` `strikethrough` `baseline`(正=上标/负=下标)。
|
||||
- `<strong>/<em>/<u>/<del>` 语义装饰;`<a href="…">`(仅 http/https/mailto 等);`<shadow …>`/`<outline color width>` 可作用于文本片段。**禁用 Markdown**(`**粗**`、`$公式$` 均按字面渲染)。
|
||||
- 保留空格用 ` `、制表符用 `	`;标签间空白与换行被忽略。
|
||||
|
||||
## 颜色与样式
|
||||
|
||||
- 纯色 `rgb(r,g,b)` 或 `rgba(r,g,b,a)`(r,g,b∈0–255,a∈0–1),均可带空格。hex 与具名色(white/red 等)一律不可用。
|
||||
- 渐变:`linear-gradient(角度deg, 停靠点…)`、`radial-gradient(circle [at 50% 50%], …)`(圆心仅支持 `50% 50%`,其他值回退)、`rect-gradient(…)`、`shape-gradient(…)`。**每个停靠点必须是 `rgb()/rgba()` 颜色 + 整数百分比位置,至少两个停靠点**,否则整体回退为白色。示例:`linear-gradient(135deg, rgba(10,20,45,1) 0%, rgba(28,71,120,1) 100%)`。
|
||||
- `<fill>` 三选一子元素:`<fillColor color rotateWithShape?>`(纯色或渐变)、`<fillImg src alpha? …>`、`<fillPattern type foregroundColor? backgroundColor? alpha?>`(图案,type 如 pct5/dot-grid/diag-cross…)。优先级 fillPattern > fillImg > fillColor。
|
||||
- `<border>`:`color` `width`(整数 px) `dashArray`(solid/dash/dot/long-dash/round-dot/dash-dot/long-dash-dot/long-dash-dot-dot…) `compound`(single/double/thin-thick/thick-thin/three) `lineCap` `lineJoin`。
|
||||
- `<shadow>`:`color`(默认 rgba(0,0,0,0.25)) `offset`[0,200](默认 15) `blur`[0,100](默认 35) `angle`[0,360)(默认 45) `hScale`/`vScale`[-2,2] `hSkew`/`vSkew`[-90,90] `align`(默认 top-left)。作用于 shape/img/line/polyline/chart/icon。
|
||||
- `<reflection>`:`alpha` `offset`[0,200] `size`[0,1]。`<crop>` 见 img。
|
||||
|
||||
## 页面背景与备注
|
||||
|
||||
- 背景写在 `<slide>` 的 `<style><fill>…`(不继承任何全局主题;`<theme>` 与 outline 中的 color_palette 只是配色参考,不会自动套用)。省略 `<style>` = 白底。**不要**再在 `<data>` 里铺一个全屏 `<shape>` 模拟背景色。
|
||||
- `<fillImg>` 做背景图时,压暗要叠一层深色半透明 `<shape>` 蒙版,而非降低图片 alpha(降 alpha 会露出白底、发灰)。
|
||||
- `<note>` 演讲者备注:结构固定 `<note><content><p>纯文本</p></content></note>`,内部不支持任何富文本/列表/换行元素,也不能放裸文本。
|
||||
@@ -1,249 +0,0 @@
|
||||
# XML Schema 快速参考
|
||||
|
||||
本文档是 [slides_xml_schema_definition.xml](slides_xml_schema_definition.xml) 的精简版摘要;如果两者不一致,以 XSD 原文为准。
|
||||
|
||||
## 最重要的规则
|
||||
|
||||
1. 协议标准写法应使用 `<presentation xmlns="http://www.larkoffice.com/sml/2.0">`;当前服务端实现可能兼容不带 `xmlns` 的输入,但不作为协议保证
|
||||
2. `<presentation>` 直接子元素只有 `<title>`、`<theme>`、`<slide>`
|
||||
3. `<slide>` 直接子元素只有 `<style>`、`<data>`、`<note>`
|
||||
4. 页面中的文本通常通过 `<content>` 表达,而不是把 `<title>`、`<body>` 直接挂在 `<slide>` 下
|
||||
|
||||
## 最小可用示例
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<presentation xmlns="http://www.larkoffice.com/sml/2.0" width="960" height="540">
|
||||
<slide>
|
||||
<data>
|
||||
<shape type="text" topLeftX="80" topLeftY="80" width="800" height="120">
|
||||
<content textType="title">
|
||||
<p>标题</p>
|
||||
</content>
|
||||
</shape>
|
||||
</data>
|
||||
</slide>
|
||||
</presentation>
|
||||
```
|
||||
|
||||
## presentation 根元素
|
||||
|
||||
| 属性 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `width` | 是 | 演示文稿宽度,正整数 |
|
||||
| `height` | 是 | 演示文稿高度,正整数 |
|
||||
| `id` | 否 | 演示文稿标识 |
|
||||
|
||||
**子元素:** `<title>?`, `<theme>?`, `<slide>+`
|
||||
|
||||
## slide 元素
|
||||
|
||||
| 属性 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | 否 | 幻灯片标识 |
|
||||
|
||||
**子元素:**
|
||||
- `<style>?` - 页面样式,目前可放 `<fill>`
|
||||
- `<data>?` - 页面元素容器,可放 `shape`、`line`、`polyline`、`img`、`table`、`icon`、`chart`、`whiteboard`、`undefined`
|
||||
- `<note>?` - 演讲者备注,内部可放 `<content>`
|
||||
|
||||
## theme 与文本类型
|
||||
|
||||
XSD 中的 `title`、`headline`、`sub-headline`、`body`、`caption` 主要出现在:
|
||||
|
||||
- `<theme><textStyles>...</textStyles></theme>` 中,作为主题文本样式
|
||||
- `<content textType="...">` 中,作为内容的文本类型
|
||||
|
||||
`textStyles` 的 schema 默认值如下:
|
||||
|
||||
| textType | 默认字号 |
|
||||
|----------|----------|
|
||||
| `title` | 54 |
|
||||
| `headline` | 38 |
|
||||
| `sub-headline` | 32 |
|
||||
| `body` | 16 |
|
||||
| `caption` | 12 |
|
||||
|
||||
## content 内容模型
|
||||
|
||||
`<content>` 可出现在 `shape`、`table/td`、`note` 中,常用属性包括:
|
||||
|
||||
| 属性 | 说明 |
|
||||
|------|------|
|
||||
| `textType` | `title` / `headline` / `sub-headline` / `body` / `caption` |
|
||||
| `textAlign` | 文本对齐方式 |
|
||||
| `lineSpacing` | 行间距,schema 默认 `multiple:1.5` |
|
||||
| `fontSize` | 字号 |
|
||||
| `fontFamily` | 字体 |
|
||||
| `color` | 字体颜色 |
|
||||
| `bold` / `italic` / `underline` / `strikethrough` | 文本样式 |
|
||||
|
||||
`<content>` 的子元素只能是:
|
||||
|
||||
- `<p>`
|
||||
- `<ul>`
|
||||
- `<ol>`
|
||||
|
||||
### content 示例
|
||||
|
||||
```xml
|
||||
<content textType="body" textAlign="left">
|
||||
<p>正文内容 <strong>加粗</strong> <em>斜体</em> <a href="https://example.com">链接</a></p>
|
||||
<ul>
|
||||
<li><p>列表项 1</p></li>
|
||||
<li><p>列表项 2</p></li>
|
||||
</ul>
|
||||
</content>
|
||||
```
|
||||
|
||||
## data 常用元素
|
||||
|
||||
### shape
|
||||
|
||||
```xml
|
||||
<shape type="rect" topLeftX="120" topLeftY="120" width="240" height="120">
|
||||
<fill>
|
||||
<fillColor color="rgb(100, 149, 237)"/>
|
||||
</fill>
|
||||
<border color="rgb(0, 0, 0)" width="2"/>
|
||||
</shape>
|
||||
```
|
||||
|
||||
| 属性 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `type` | 是 | 形状类型,`text` 表示文本框 |
|
||||
| `topLeftX` | 是 | 左上角 X 坐标 |
|
||||
| `topLeftY` | 是 | 左上角 Y 坐标 |
|
||||
| `width` | 是 | 宽度 |
|
||||
| `height` | 是 | 高度 |
|
||||
| `rotation` | 否 | 旋转角度 |
|
||||
|
||||
### line
|
||||
|
||||
```xml
|
||||
<line startX="120" startY="120" endX="420" endY="120">
|
||||
<border color="rgb(43, 47, 54)" width="2"/>
|
||||
</line>
|
||||
```
|
||||
|
||||
### img
|
||||
|
||||
```xml
|
||||
<img src="file_token_or_url" topLeftX="80" topLeftY="120" width="320" height="180"/>
|
||||
```
|
||||
|
||||
`src` 只支持:`slides +media-upload` 返回的 `file_token`,或 `@<本地路径>` 占位符(仅 `+create --slides` 自动上传并替换)。**禁止使用 http(s) 外链 URL**——飞书 slides 渲染端不会代理外链图,外链 src 在 PPT 里通常不显示。本地图片详见 [lark-slides-create.md](lark-slides-create.md#本地图片path-占位符) / [lark-slides-media-upload.md](lark-slides-media-upload.md)。
|
||||
|
||||
> **注意**:`width`/`height` 是**裁剪后**的显示尺寸。比例和原图不一致时会自动裁剪(无法靠属性关闭),想避免裁剪就让 `width:height` 对齐原图比例。
|
||||
|
||||
### icon
|
||||
|
||||
```xml
|
||||
<icon iconType="iconpark/Base/setting.svg" topLeftX="80" topLeftY="120" width="32" height="32">
|
||||
<fill>
|
||||
<fillColor color="rgba(37, 99, 235, 1)"/>
|
||||
</fill>
|
||||
</icon>
|
||||
```
|
||||
|
||||
`iconType` 必须来自已验证的 IconPark 路径;视觉 lint 规范要求 `fillColor` 显式设置为非透明颜色,避免图标不可见。需要语义图标时,先运行 `scripts/iconpark_tool.py search --query "<语义>"`,不要凭记忆拼路径。更多规则见 [iconpark.md](iconpark.md)。
|
||||
|
||||
### whiteboard
|
||||
|
||||
```xml
|
||||
<!-- SVG 模式:<chart> 不支持的图表或自定义视觉、装饰元素 -->
|
||||
<whiteboard topLeftX="580" topLeftY="120" width="340" height="280">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="60" y="80" width="40" height="140" rx="3" fill="rgba(59,130,246,0.85)"/>
|
||||
<text x="80" y="238" text-anchor="middle" font-size="11" fill="rgba(100,116,139,1)">ABC</text>
|
||||
</svg>
|
||||
</whiteboard>
|
||||
|
||||
<!-- Mermaid 模式:流程图、时序图等结构化图表 -->
|
||||
<whiteboard topLeftX="72" topLeftY="100" width="816" height="340">
|
||||
<mermaid>
|
||||
<![CDATA[
|
||||
flowchart LR
|
||||
A[开始] --> B{判断}
|
||||
B -- 是 --> C[执行]
|
||||
B -- 否 --> D[结束]
|
||||
]]>
|
||||
</mermaid>
|
||||
</whiteboard>
|
||||
```
|
||||
|
||||
SVG 模式:`<svg>` 需声明 `xmlns="http://www.w3.org/2000/svg"`,内容大小由子元素包围盒决定;`width`/`height`/`viewBox` 不影响渲染,仅当元素使用百分比属性值时需声明 `viewBox`。\
|
||||
Mermaid 模式:内容用 `<![CDATA[...]]>` 包裹,避免 `[`、`>`、`-->` 等字符破坏 XML 解析。\
|
||||
详细用法见 [lark-slides-whiteboard.md](lark-slides-whiteboard.md)。
|
||||
|
||||
## 颜色与样式
|
||||
|
||||
### fill
|
||||
|
||||
```xml
|
||||
<fill>
|
||||
<fillColor color="rgb(255, 0, 0)"/>
|
||||
</fill>
|
||||
```
|
||||
|
||||
### border
|
||||
|
||||
```xml
|
||||
<border color="rgb(43, 47, 54)" width="2" dashArray="solid"/>
|
||||
```
|
||||
|
||||
### 颜色格式
|
||||
|
||||
```xml
|
||||
<fillColor color="rgb(255, 0, 0)"/>
|
||||
<fillColor color="rgba(255, 0, 0, 0.5)"/>
|
||||
<fillColor color="linear-gradient(90deg, rgb(255,0,0) 0%, rgb(0,0,255) 100%)"/>
|
||||
<fillColor color="radial-gradient(circle at 50% 50%, rgb(255,0,0) 0%, rgb(0,0,255) 100%)"/>
|
||||
```
|
||||
|
||||
> **注意**:渐变色必须使用 `rgba()` 格式并带百分比停靠点,例如 `linear-gradient(135deg,rgba(30,60,114,1) 0%,rgba(59,130,246,1) 100%)`。使用 `rgb()` 或省略停靠点会导致服务端将其回退为白色。此规则对页面背景和 shape fill 均适用。
|
||||
|
||||
### 页面背景
|
||||
|
||||
```xml
|
||||
<!-- 纯色背景 -->
|
||||
<slide>
|
||||
<style>
|
||||
<fill>
|
||||
<fillColor color="rgb(245, 245, 245)"/>
|
||||
</fill>
|
||||
</style>
|
||||
</slide>
|
||||
|
||||
<!-- 渐变背景(必须用 rgba + 百分比停靠点) -->
|
||||
<slide>
|
||||
<style>
|
||||
<fill>
|
||||
<fillColor color="linear-gradient(135deg,rgba(30,60,114,1) 0%,rgba(59,130,246,1) 100%)"/>
|
||||
</fill>
|
||||
</style>
|
||||
</slide>
|
||||
```
|
||||
|
||||
## 备注示例
|
||||
|
||||
```xml
|
||||
<note>
|
||||
<content textType="body">
|
||||
<p>这是演讲者备注。</p>
|
||||
</content>
|
||||
</note>
|
||||
```
|
||||
|
||||
## 详细参考
|
||||
|
||||
- [slides_xml_schema_definition.xml](slides_xml_schema_definition.xml)
|
||||
- [xml-format-guide.md](xml-format-guide.md)
|
||||
- [examples.md](examples.md)
|
||||
- [slides_demo.xml](slides_demo.xml)
|
||||
|
||||
## Schema 版本信息
|
||||
|
||||
- **版本**: 2.0.0
|
||||
- **命名空间**: http://www.larkoffice.com/sml/2.0
|
||||
- **发布日期**: 2025-11-03
|
||||
Reference in New Issue
Block a user