Sync third-party and MCP marketplace plugins
Constraint: Public skills are published only by explicit administrator action unless they are tracked third-party market sources. Confidence: high Scope-risk: narrow Directive: Keep private/internal skills out of the public marketplace and preserve normal incremental market Git history. Tested: Marketplace validation passed.
This commit is contained in:
@@ -33,8 +33,8 @@
|
|||||||
"repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git",
|
"repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git",
|
||||||
"ref": "main",
|
"ref": "main",
|
||||||
"adapter": "claude-skill",
|
"adapter": "claude-skill",
|
||||||
"commit": "4d140cf8ff6842de13213c7214eff3810371beb2",
|
"commit": "abb7f2fd5a083fa1ff55c326a963ff0d95c33f99",
|
||||||
"syncedAt": "2026-08-03T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "caveman",
|
"id": "caveman",
|
||||||
@@ -42,8 +42,8 @@
|
|||||||
"repo": "https://github.com/JuliusBrussee/caveman.git",
|
"repo": "https://github.com/JuliusBrussee/caveman.git",
|
||||||
"ref": "main",
|
"ref": "main",
|
||||||
"adapter": "codex-plugin",
|
"adapter": "codex-plugin",
|
||||||
"commit": "7066cc815414421f1ed29b42293cfb177a11db8c",
|
"commit": "ec83e5bace4c20484d704dea21e12fc4eb94e9aa",
|
||||||
"syncedAt": "2026-08-03T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "taste-skill",
|
"id": "taste-skill",
|
||||||
@@ -60,8 +60,8 @@
|
|||||||
"repo": "https://github.com/shadcn-ui/ui.git",
|
"repo": "https://github.com/shadcn-ui/ui.git",
|
||||||
"ref": "main",
|
"ref": "main",
|
||||||
"adapter": "claude-skill",
|
"adapter": "claude-skill",
|
||||||
"commit": "f0cad1d9f836b56b3705f09c76142861b09ec71c",
|
"commit": "9846e22ce52c723554742860a0dbd3e5cf19b573",
|
||||||
"syncedAt": "2026-08-03T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "frontend-slides",
|
"id": "frontend-slides",
|
||||||
@@ -78,8 +78,8 @@
|
|||||||
"repo": "https://github.com/op7418/guizang-ppt-skill.git",
|
"repo": "https://github.com/op7418/guizang-ppt-skill.git",
|
||||||
"ref": "main",
|
"ref": "main",
|
||||||
"adapter": "claude-skill",
|
"adapter": "claude-skill",
|
||||||
"commit": "929c2ecb63a22b54d400c4911ed70bf96c2b355d",
|
"commit": "e8357b315471f04e17551988826011974bfff779",
|
||||||
"syncedAt": "2026-07-22T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "html-ppt",
|
"id": "html-ppt",
|
||||||
@@ -96,8 +96,8 @@
|
|||||||
"repo": "https://github.com/hugohe3/ppt-master.git",
|
"repo": "https://github.com/hugohe3/ppt-master.git",
|
||||||
"ref": "main",
|
"ref": "main",
|
||||||
"adapter": "claude-skill",
|
"adapter": "claude-skill",
|
||||||
"commit": "e247014a2f7361c06a04c07619a838f82394294c",
|
"commit": "c0305342cf5c558e7242dba64cfc207874a5134a",
|
||||||
"syncedAt": "2026-08-03T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "next-skills",
|
"id": "next-skills",
|
||||||
@@ -105,8 +105,8 @@
|
|||||||
"repo": "https://github.com/vercel/next.js.git",
|
"repo": "https://github.com/vercel/next.js.git",
|
||||||
"ref": "canary",
|
"ref": "canary",
|
||||||
"adapter": "skill-collection",
|
"adapter": "skill-collection",
|
||||||
"commit": "dcdacfd036e8f1b3da7a38df143da7aa271cf0d5",
|
"commit": "0ae8c72462952df163f1b1e0726641bc5b40dc93",
|
||||||
"syncedAt": "2026-08-03T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
"sourceId": "caveman",
|
"sourceId": "caveman",
|
||||||
"repo": "https://github.com/JuliusBrussee/caveman.git",
|
"repo": "https://github.com/JuliusBrussee/caveman.git",
|
||||||
"ref": "main",
|
"ref": "main",
|
||||||
"commit": "7066cc815414421f1ed29b42293cfb177a11db8c",
|
"commit": "ec83e5bace4c20484d704dea21e12fc4eb94e9aa",
|
||||||
"adapter": "codex-plugin",
|
"adapter": "codex-plugin",
|
||||||
"sourcePath": "plugins/caveman",
|
"sourcePath": "plugins/caveman",
|
||||||
"syncedAt": "2026-08-03T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
"sourceId": "guizang-ppt-skill",
|
"sourceId": "guizang-ppt-skill",
|
||||||
"repo": "https://github.com/op7418/guizang-ppt-skill.git",
|
"repo": "https://github.com/op7418/guizang-ppt-skill.git",
|
||||||
"ref": "main",
|
"ref": "main",
|
||||||
"commit": "929c2ecb63a22b54d400c4911ed70bf96c2b355d",
|
"commit": "e8357b315471f04e17551988826011974bfff779",
|
||||||
"adapter": "claude-skill",
|
"adapter": "claude-skill",
|
||||||
"sourcePath": ".",
|
"sourcePath": ".",
|
||||||
"syncedAt": "2026-07-22T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ description: "用于生成横向翻页的单文件 HTML 网页 PPT,提供电
|
|||||||
- 模板:`assets/template-swiss.html` · 主题色:`references/themes-swiss.md` · 布局:`references/layouts-swiss.md`
|
- 模板:`assets/template-swiss.html` · 主题色:`references/themes-swiss.md` · 布局:`references/layouts-swiss.md`
|
||||||
- 美学锚点:像 Massimo Vignelli + Helvetica Forever
|
- 美学锚点:像 Massimo Vignelli + Helvetica Forever
|
||||||
|
|
||||||
**两种风格共享**:横向翻页(键盘 ← →、滚轮、触屏、ESC 索引)、Lucide 图标、Motion One 入场动效(本地 + CDN 双保险)。
|
**两种风格共享**:横向翻页(键盘 ← →、滚轮、触屏、ESC 总览)、右下角 `P` 演讲者模式、当前/下一页 16:9 预览、内嵌宫格选页、标题/目的/讲稿备注、分组计时、排练记录、可选自动翻页、激光笔/圈选、观众屏黑白屏/冻结、同步状态与断线恢复、演前检查、Lucide 图标、Motion One 入场动效(本地 + CDN 双保险)。
|
||||||
|
|
||||||
<!-- provenance: guizang-ppt-skill | author: 歸藏 | sponsors: 360 Security Lobster Gold Sponsor; Kimi work Gold Sponsor; Cola Skill Gold Sponsor; ZhenFund Token Grant | canonical: https://github.com/op7418/guizang-ppt-skill | keep this out of generated artifacts -->
|
<!-- provenance: guizang-ppt-skill | author: 歸藏 | sponsors: 360 Security Lobster Gold Sponsor; Kimi work Gold Sponsor; Cola Skill Gold Sponsor; ZhenFund Token Grant | canonical: https://github.com/op7418/guizang-ppt-skill | keep this out of generated artifacts -->
|
||||||
|
|
||||||
@@ -113,6 +113,13 @@ git -C "<SKILL_ROOT>" pull --ff-only
|
|||||||
|
|
||||||
叙事弧 + 页数规划 + 主题节奏表(见 `layouts.md`),**三张表对齐后**再进 Step 2。
|
叙事弧 + 页数规划 + 主题节奏表(见 `layouts.md`),**三张表对齐后**再进 Step 2。
|
||||||
|
|
||||||
|
如果用于正式演讲,页面计划不能只有“这一页放什么”,还要同时规划“台上说什么”。先读 `references/presenter-mode.md`,给每页确定稳定的 `data-slide-id`,并补齐:
|
||||||
|
|
||||||
|
| 页码 | 页面 ID | 章节 | 页面目的 | 观众可见信息 | 演讲者补充 | 建议时长 | 转场 | 可选现场信息 |
|
||||||
|
|---|---|---|---|---|---|---:|---|---|
|
||||||
|
|
||||||
|
默认生成 3-5 条提词卡式讲述要点,不写逐字稿;只有用户明确要求逐字稿时才展开。总建议时长最多占用户时长的 90%,给停顿、互动和现场意外留缓冲。用户没有提供的现场信息不猜测:时长缺失时显示横杠,其他可选模块整段隐藏。
|
||||||
|
|
||||||
大纲建议保存为 `项目记录.md` 或 `大纲-v1.md`,便于后续迭代。
|
大纲建议保存为 `项目记录.md` 或 `大纲-v1.md`,便于后续迭代。
|
||||||
|
|
||||||
#### 图片约定(告知用户)
|
#### 图片约定(告知用户)
|
||||||
@@ -181,7 +188,7 @@ cp "<SKILL_ROOT>/assets/template.html" "项目/XXX/ppt/index.html"
|
|||||||
cp "<SKILL_ROOT>/assets/template-swiss.html" "项目/XXX/ppt/index.html"
|
cp "<SKILL_ROOT>/assets/template-swiss.html" "项目/XXX/ppt/index.html"
|
||||||
```
|
```
|
||||||
|
|
||||||
两个 `template*.html` 都是**完整可运行**的文件——CSS、WebGL shader、翻页 JS、字体/图标 CDN 全已预设好,只有 `<!-- SLIDES_HERE -->` 占位符等待你填充 slide 内容。
|
两个 `template*.html` 都是**完整可运行**的文件——CSS、WebGL shader、翻页 JS、演讲者模式、观众屏同步、字体/图标 CDN 全已预设好,只有 `<!-- SLIDES_HERE -->` 占位符和 `SPEAKER_NOTES` 等待你填充。
|
||||||
|
|
||||||
**注意**:风格 A 和 B **不能混用**。layouts.md 里的类(如 `.h-hero` 衬线大标题、`.display-zh` 等)只在 template.html 有定义;layouts-swiss.md 里的类(如 `.kpi-hero`、`.accent-block`、`.span-N`、`.dots` 等)只在 template-swiss.html 有定义。一份 deck 只能选一套。
|
**注意**:风格 A 和 B **不能混用**。layouts.md 里的类(如 `.h-hero` 衬线大标题、`.display-zh` 等)只在 template.html 有定义;layouts-swiss.md 里的类(如 `.kpi-hero`、`.accent-block`、`.span-N`、`.dots` 等)只在 template-swiss.html 有定义。一份 deck 只能选一套。
|
||||||
|
|
||||||
@@ -220,6 +227,22 @@ cp "<SKILL_ROOT>/assets/template-swiss.html" "项目/XXX/ppt/index.html"
|
|||||||
|
|
||||||
### Step 3 · 填充内容
|
### Step 3 · 填充内容
|
||||||
|
|
||||||
|
#### 3.P · 同步生成演讲备注(正式演讲必做)
|
||||||
|
|
||||||
|
先读 `references/presenter-mode.md`。每个 `<section class="slide ...">` 必须写唯一且稳定的 `data-slide-id`,再按同样顺序生成一条 `SPEAKER_NOTES` 记录。备注按页面 ID 存储,不要用数组下标或页码作为持久化键,否则页面重排后用户在演讲者视图里改过的备注会串页。
|
||||||
|
|
||||||
|
内容分工:
|
||||||
|
|
||||||
|
- slide 只放观众此刻必须看见的结论、结构和证据。
|
||||||
|
- `purpose` 说明这一页在整场叙事中的任务。
|
||||||
|
- `talk` 补充背景、例子、判断依据和语气,不逐字复述 slide。
|
||||||
|
- `transition` 解释为什么下一页紧接着出现。
|
||||||
|
- `section` 只在大纲已给出章节或连续页面明显属于同一章节时填写。
|
||||||
|
- `minutes` 是讲述计划;`autoAdvanceSeconds` 是播放行为,两者必须分开,且后者只在用户明确要求时填写。
|
||||||
|
- `cue / interaction / delivery / advance / fallback / pronunciation` 只写大纲或用户明确提供的舞台动作、互动、表达、翻页、备用和读音信息。
|
||||||
|
|
||||||
|
没有来源支持的事实不能写进备注;影响内容正确性的缺失信息要标记“待补充”或询问用户,不影响内容的可选演讲信息直接省略。
|
||||||
|
|
||||||
#### 3.0 · 预检:类名必须在模板的 `<style>` 里有定义(**最重要**)
|
#### 3.0 · 预检:类名必须在模板的 `<style>` 里有定义(**最重要**)
|
||||||
|
|
||||||
**这是所有生成问题的源头**。layouts 骨架使用了很多类名,如果模板的 `<style>` 里没有对应定义,浏览器会 fallback 到默认样式——大标题字体错、卡片挤成一团、pipeline 糊成一行、图片堆到页面底部。
|
**这是所有生成问题的源头**。layouts 骨架使用了很多类名,如果模板的 `<style>` 里没有对应定义,浏览器会 fallback 到默认样式——大标题字体错、卡片挤成一团、pipeline 糊成一行、图片堆到页面底部。
|
||||||
@@ -420,6 +443,15 @@ cp "<SKILL_ROOT>/assets/template-swiss.html" "项目/XXX/ppt/index.html"
|
|||||||
|
|
||||||
生成完一定要打开 `references/checklist.md`,逐项对照。里面总结了**真实迭代过程中踩过的所有坑**,P0 级别的问题(emoji、图片撑破、标题换行、字体分工)必须全部通过。
|
生成完一定要打开 `references/checklist.md`,逐项对照。里面总结了**真实迭代过程中踩过的所有坑**,P0 级别的问题(emoji、图片撑破、标题换行、字体分工)必须全部通过。
|
||||||
|
|
||||||
|
所有正式演讲 deck 先跑演讲者模式校验;如果用户给了目标时长,同时传入分钟数:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node <SKILL_ROOT>/scripts/validate-presenter-mode.mjs path/to/index.html
|
||||||
|
node <SKILL_ROOT>/scripts/validate-presenter-mode.mjs path/to/index.html --target-minutes 30
|
||||||
|
```
|
||||||
|
|
||||||
|
它会拦截缺失/重复页面 ID、备注与页面错位、必填字段或可选字段类型错误、完整时间计划超出 90% 预算,以及计时、排练、自动翻页、标注、演前检查和观众屏恢复控件缺失。
|
||||||
|
|
||||||
#### 4.0.1 · 先量后改:超出 / 空白 / 标题间距
|
#### 4.0.1 · 先量后改:超出 / 空白 / 标题间距
|
||||||
|
|
||||||
当一页内容超出或显得巨空时,不要先凭感觉大幅删改。先运行:
|
当一页内容超出或显得巨空时,不要先凭感觉大幅删改。先运行:
|
||||||
@@ -502,6 +534,8 @@ open "项目/XXX/ppt/index.html"
|
|||||||
|
|
||||||
不需要本地服务器。图片走相对路径 `images/xxx.png`。
|
不需要本地服务器。图片走相对路径 `images/xxx.png`。
|
||||||
|
|
||||||
|
预览时不能只看普通页面。按 `P` 进入演讲者模式,允许浏览器打开观众窗口,至少实测一次:前后翻页、内嵌宫格选页并返回预览、首页/尾页、尾页重新开始、计时开始/暂停/重置、排练记录、自动翻页暂停/恢复、激光笔、圈选、黑白屏、冻结、设置组件、演前检查、备注保存、关闭观众窗口后的状态变化,以及“重新打开观众屏”能否恢复到当前页。
|
||||||
|
|
||||||
### Step 6 · 迭代
|
### Step 6 · 迭代
|
||||||
|
|
||||||
根据用户反馈修改——模板的 CSS 已经高度参数化,90% 的调整都是改 inline style(字号 `font-size:Xvw` / 高度 `height:Yvh` / 间距 `gap:Zvh`)。
|
根据用户反馈修改——模板的 CSS 已经高度参数化,90% 的调整都是改 inline style(字号 `font-size:Xvw` / 高度 `height:Yvh` / 间距 `gap:Zvh`)。
|
||||||
@@ -519,7 +553,8 @@ guizang-ppt-skill/
|
|||||||
│ ├── screenshot-backgrounds/ ← 截图美化内置背景(WebP):style-a 5 套 / style-b 4 套
|
│ ├── screenshot-backgrounds/ ← 截图美化内置背景(WebP):style-a 5 套 / style-b 4 套
|
||||||
│ └── motion.min.js ← Motion One 本地副本(离线兜底,约 64KB,共用)
|
│ └── motion.min.js ← Motion One 本地副本(离线兜底,约 64KB,共用)
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ └── validate-swiss-deck.mjs ← 风格 B 静态校验:登记版式、图片槽位、SVG 文本、标题对齐
|
│ ├── validate-swiss-deck.mjs ← 风格 B 静态校验:登记版式、图片槽位、SVG 文本、标题对齐
|
||||||
|
│ └── validate-presenter-mode.mjs ← 两种风格共用:页面 ID、演讲备注、时长和演讲者运行时校验
|
||||||
└── references/
|
└── references/
|
||||||
├── components.md ← 组件手册(字体、色、网格、图标、callout、stat、pipeline、动效... 风格 A 适用)
|
├── components.md ← 组件手册(字体、色、网格、图标、callout、stat、pipeline、动效... 风格 A 适用)
|
||||||
├── layouts.md ← 风格 A · 10 种页面布局骨架(可直接粘贴,含动效标记)
|
├── layouts.md ← 风格 A · 10 种页面布局骨架(可直接粘贴,含动效标记)
|
||||||
@@ -530,6 +565,7 @@ guizang-ppt-skill/
|
|||||||
├── themes-swiss.md ← 风格 B · 4 套瑞士风主题色预设(IKB / 柠檬黄 / 柠檬绿 / 安全橙)
|
├── themes-swiss.md ← 风格 B · 4 套瑞士风主题色预设(IKB / 柠檬黄 / 柠檬绿 / 安全橙)
|
||||||
├── image-prompts.md ← GPT-M 2.0 配图类型、比例和基础提示词
|
├── image-prompts.md ← GPT-M 2.0 配图类型、比例和基础提示词
|
||||||
├── screenshot-framing.md ← CleanShot X 式截图适配语义 + 内置背景资产映射
|
├── screenshot-framing.md ← CleanShot X 式截图适配语义 + 内置背景资产映射
|
||||||
|
├── presenter-mode.md ← 演讲者 UI、AI 备注结构、观众屏同步与恢复契约
|
||||||
└── checklist.md ← 质量检查清单(P0/P1/P2/P3 分级)
|
└── checklist.md ← 质量检查清单(P0/P1/P2/P3 分级)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -547,7 +583,8 @@ guizang-ppt-skill/
|
|||||||
5. 如果风格 B 需要地点、路线、人物住所或城市关系地图,读 `swiss-map-component.md`
|
5. 如果风格 B 需要地点、路线、人物住所或城市关系地图,读 `swiss-map-component.md`
|
||||||
6. 如果在 Codex 中生成配图,读 `image-prompts.md` 挑图片类型、比例和基础提示词;如果是用户原始截图,先读 `screenshot-framing.md`,优先使用 `assets/screenshot-backgrounds/` 的内置背景资产
|
6. 如果在 Codex 中生成配图,读 `image-prompts.md` 挑图片类型、比例和基础提示词;如果是用户原始截图,先读 `screenshot-framing.md`,优先使用 `assets/screenshot-backgrounds/` 的内置背景资产
|
||||||
7. 细节调整时读 `components.md` 查组件(含 Motion 动效系统章节,主要服务风格 A;风格 B 的组件细节在 `layouts-swiss.md` 附录)
|
7. 细节调整时读 `components.md` 查组件(含 Motion 动效系统章节,主要服务风格 A;风格 B 的组件细节在 `layouts-swiss.md` 附录)
|
||||||
8. 生成后先运行 `node scripts/validate-swiss-deck.mjs path/to/index.html`,再读 `checklist.md` 自检
|
8. 正式演讲先读 `presenter-mode.md`,生成稳定页面 ID 和 `SPEAKER_NOTES`
|
||||||
|
9. 生成后先运行 `validate-presenter-mode.mjs`;风格 B 再运行 `validate-swiss-deck.mjs`,最后读 `checklist.md` 自检
|
||||||
|
|
||||||
**动效相关**:模板已把 Motion One 的加载和 recipe 逻辑内嵌到底部 module script。你不需要改 JS,只需要按 `layouts.md` / `layouts-swiss.md` 的骨架在 HTML 里加 `data-anim` / `data-animate` 即可。离线演示靠 `assets/motion.min.js`,断网时自动降级为"无动画但内容可读"。风格 B 模板必须保留 `B` 键低功耗模式:切换后停止 WebGL/ASCII canvas RAF,取消正在运行的 Web Animations,并把当前页内容直接 reveal 到静态最终态。
|
**动效相关**:模板已把 Motion One 的加载和 recipe 逻辑内嵌到底部 module script。你不需要改 JS,只需要按 `layouts.md` / `layouts-swiss.md` 的骨架在 HTML 里加 `data-anim` / `data-animate` 即可。离线演示靠 `assets/motion.min.js`,断网时自动降级为"无动画但内容可读"。风格 B 模板必须保留 `B` 键低功耗模式:切换后停止 WebGL/ASCII canvas RAF,取消正在运行的 Web Animations,并把当前页内容直接 reveal 到静态最终态。
|
||||||
|
|
||||||
|
|||||||
+552
-10
@@ -550,11 +550,57 @@
|
|||||||
body.dark-bg #nav .dot{background:rgba(255,255,255,.32)}
|
body.dark-bg #nav .dot{background:rgba(255,255,255,.32)}
|
||||||
body.dark-bg #nav .dot.active{background:var(--accent)}
|
body.dark-bg #nav .dot.active{background:var(--accent)}
|
||||||
|
|
||||||
#hint{position:fixed;bottom:2.4vh;right:2.5vw;z-index:30;font-family:var(--mono);font-size:14px;letter-spacing:.14em;text-transform:uppercase;opacity:.4}
|
#hint{position:fixed;bottom:2.4vh;right:2.5vw;z-index:30;display:flex;align-items:center;gap:.65em;font-family:var(--mono);font-size:14px;letter-spacing:.14em;text-transform:uppercase;opacity:.4}
|
||||||
|
#hint button{all:unset;display:inline-flex;align-items:center;gap:.42em;cursor:pointer;color:inherit;white-space:nowrap}
|
||||||
|
#hint button:hover,#hint button:focus-visible{opacity:1;text-decoration:underline;text-underline-offset:.3em}
|
||||||
|
#hint .ppt-key{padding:.12em .34em;border:1px solid currentColor;letter-spacing:.06em}
|
||||||
|
#hint .ppt-sep{opacity:.55}
|
||||||
body.dark-bg #hint{color:var(--paper);opacity:.4}
|
body.dark-bg #hint{color:var(--paper);opacity:.4}
|
||||||
body.low-power #hint{color:var(--accent);opacity:.72}
|
body.low-power #hint{color:var(--accent);opacity:.72}
|
||||||
body.dark-bg.low-power #hint{color:var(--paper);opacity:.72}
|
body.dark-bg.low-power #hint{color:var(--paper);opacity:.72}
|
||||||
|
|
||||||
|
/* ============ 演讲者模式 ============ */
|
||||||
|
body.ppt-audience #hint,body.ppt-audience #nav,
|
||||||
|
body.ppt-preview #hint,body.ppt-preview #nav{display:none!important}
|
||||||
|
body.ppt-audience{cursor:none}
|
||||||
|
body.ppt-preview{pointer-events:none}
|
||||||
|
body.ppt-presenter>#deck,body.ppt-presenter>#nav,body.ppt-presenter>#hint,
|
||||||
|
body.ppt-presenter>.bg{visibility:hidden}
|
||||||
|
#ppt-presenter{position:fixed;inset:0;z-index:90;display:none;min-width:0;min-height:0;overflow:hidden;background:#101216;color:#f5f6f7;font-family:Inter,"Noto Sans SC",sans-serif}
|
||||||
|
body.ppt-presenter #ppt-presenter{display:grid;grid-template-rows:auto 1fr auto}
|
||||||
|
.ppt-presenter-top{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:18px 24px;border-bottom:1px solid #2b2e35;background:#15181d}
|
||||||
|
.ppt-presenter-brand{font-weight:650;letter-spacing:.02em}.ppt-presenter-meta{display:flex;align-items:center;gap:12px;color:#aeb4be;font-size:14px}
|
||||||
|
.ppt-sync{display:inline-flex;align-items:center;gap:8px;padding:7px 11px;border:1px solid #363a43;border-radius:999px;color:#d2d6dc;background:#1b1e24}
|
||||||
|
.ppt-sync::before{content:"";width:8px;height:8px;border-radius:50%;background:#818894}
|
||||||
|
.ppt-sync[data-state="synced"]::before{background:#72df99;box-shadow:0 0 0 4px rgba(114,223,153,.12)}
|
||||||
|
.ppt-sync[data-state="connecting"]::before,.ppt-sync[data-state="frozen"]::before{background:#f0c766}.ppt-sync[data-state="unsynced"]::before,.ppt-sync[data-state="blocked"]::before{background:#ff9e64}.ppt-sync[data-state="disconnected"]::before{background:#ff6b72}
|
||||||
|
.ppt-ui-btn{appearance:none;border:1px solid #3a3e48;background:#20242a;color:#f6f7f8;border-radius:8px;padding:9px 13px;font:600 14px/1 Inter,"Noto Sans SC",sans-serif;cursor:pointer}
|
||||||
|
.ppt-ui-btn:hover{background:#2a2f37;border-color:#525864}.ppt-ui-btn:focus-visible{outline:2px solid #84a8ff;outline-offset:2px}.ppt-ui-btn:disabled{opacity:.28;cursor:not-allowed}
|
||||||
|
.ppt-presenter-main{display:grid;grid-template-columns:minmax(0,1.75fr) minmax(300px,.75fr);gap:18px;min-height:0;padding:18px 24px}
|
||||||
|
.ppt-preview-stack{position:relative;display:grid;grid-template-rows:minmax(0,1fr) clamp(132px,22vh,210px);gap:16px;min-width:0;min-height:0}
|
||||||
|
.ppt-stage-card,.ppt-notes-card,.ppt-presenter-overview{background:#171a20;border:1px solid #2b2f37;border-radius:12px;overflow:hidden}
|
||||||
|
.ppt-stage-card{display:grid;grid-template-rows:auto minmax(0,1fr);min-width:0;min-height:0}
|
||||||
|
.ppt-stage-label{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:11px 14px;color:#aeb4be;font-size:13px;border-bottom:1px solid #292d34}
|
||||||
|
.ppt-annotation-tools{display:flex;align-items:center;gap:5px;margin-left:auto}.ppt-annotation-tools .ppt-ui-btn{padding:6px 9px;font-size:12px}.ppt-annotation-tools .ppt-ui-btn.is-active{color:#111318;background:#ff5b63;border-color:#ff5b63}.ppt-annotation-tools .ppt-grid-btn.is-active{color:#111318;background:#edf1f5;border-color:#edf1f5}.ppt-annotation-tools .ppt-clear-btn{min-width:0}.ppt-grid-btn{display:inline-flex;align-items:center;gap:6px}.ppt-grid-icon{display:grid;grid-template-columns:repeat(2,4px);gap:2px}.ppt-grid-icon i{width:4px;height:4px;background:currentColor}
|
||||||
|
.ppt-frame-viewport{position:relative;display:grid;place-items:center;min-width:0;min-height:0;overflow:hidden;background:#08090b}
|
||||||
|
.ppt-frame{display:block;width:0;height:0;max-width:100%;max-height:100%;aspect-ratio:16/9;border:0;background:#08090b}.ppt-next-frame{opacity:.86}
|
||||||
|
.ppt-annotation-layer{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:4;width:0;height:0;pointer-events:none;touch-action:none}.ppt-annotation-layer.is-active{pointer-events:auto;cursor:crosshair}
|
||||||
|
#ppt-audience-ink{position:fixed;inset:0;z-index:70;width:100vw;height:100vh;pointer-events:none}#ppt-audience-cover{position:fixed;inset:0;z-index:80;display:none;pointer-events:none}
|
||||||
|
.ppt-preview-stack.is-overview>.ppt-stage-card{display:none}.ppt-presenter-overview{grid-row:1/-1;display:grid;grid-template-rows:auto minmax(0,1fr);min-width:0;min-height:0}.ppt-presenter-overview[hidden]{display:none}.ppt-overview-head{display:flex;align-items:center;justify-content:space-between;padding:13px 15px;border-bottom:1px solid #292d34}.ppt-overview-head strong{font-size:15px}.ppt-overview-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;align-content:start;overflow:auto;padding:14px}.ppt-overview-page{appearance:none;min-height:112px;display:grid;grid-template-rows:auto 1fr auto;gap:12px;padding:13px;border:1px solid #343943;border-radius:10px;background:#111318;color:#eef1f4;text-align:left;cursor:pointer}.ppt-overview-page:hover{background:#1d2128;border-color:#636b78}.ppt-overview-page.is-current{border-color:#84a8ff;box-shadow:inset 0 0 0 1px #84a8ff}.ppt-overview-page-no{color:#8f97a3;font:600 12px/1 var(--mono,monospace)}.ppt-overview-page-title{font-size:14px;font-weight:650;line-height:1.35}.ppt-overview-page-meta{display:flex;justify-content:space-between;gap:8px;color:#7f8793;font-size:11px}
|
||||||
|
.ppt-notes-card{display:grid;grid-template-rows:auto minmax(0,1fr);min-height:0}.ppt-notes-head{padding:16px 18px;border-bottom:1px solid #292d34}.ppt-notes-label{margin-bottom:6px;color:#848c98;font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}.ppt-notes-title{font-size:21px;font-weight:680;line-height:1.32}.ppt-purpose-block{margin-top:14px;padding:11px 12px;border-left:3px solid #84a8ff;background:#111318}.ppt-purpose-copy{color:#c9ced6;font-size:14px;line-height:1.55}.ppt-notes-draft-head{display:flex;align-items:flex-end;justify-content:space-between;gap:10px;margin-top:15px;padding-top:13px;border-top:1px solid #2d3139}.ppt-notes-draft-head .ppt-notes-label{margin:0 0 4px}.ppt-note-meta{color:#8f97a3;font-size:12px}
|
||||||
|
.ppt-notes-tools{display:flex;align-items:center;gap:6px}.ppt-notes-tools .ppt-ui-btn{padding:7px 9px}.ppt-note-editor{width:100%;height:100%;min-height:0;resize:none;border:0;outline:0;background:#171a20;color:#edf0f3;padding:18px;font:400 var(--ppt-note-size,17px)/1.72 Inter,"Noto Sans SC",sans-serif}.ppt-note-editor:focus{box-shadow:inset 0 0 0 1px #3d4654}
|
||||||
|
.ppt-saved{color:#72df99;white-space:nowrap}.ppt-presenter-bottom{display:grid;grid-template-columns:minmax(330px,1fr) auto minmax(110px,1fr);align-items:center;gap:20px;padding:12px 24px;border-top:1px solid #2b2e35;background:#15181d}
|
||||||
|
.ppt-time-dashboard{display:flex;align-items:stretch;min-width:0}.ppt-time-stat{min-width:90px;display:grid;align-content:center;gap:4px;padding:0 16px}.ppt-time-stat:first-child{padding-left:0}.ppt-time-stat+.ppt-time-stat{border-left:1px solid #343943}.ppt-time-label{color:#89919d;font-size:11px;font-weight:700;letter-spacing:.1em}.ppt-time-value{color:#f7f8fa;font:650 23px/1 var(--mono,monospace);font-variant-numeric:tabular-nums}.ppt-time-hint{color:#747c88;font-size:11px;white-space:nowrap}.ppt-time-stat.is-over .ppt-time-label,.ppt-time-stat.is-over .ppt-time-value{color:#ff8a90}.ppt-time-stat.is-remaining .ppt-time-value{color:#72df99}
|
||||||
|
.ppt-control-stack{display:grid;gap:7px;justify-items:center}.ppt-controls,.ppt-session-actions{display:flex;align-items:center;justify-content:center;gap:7px}.ppt-controls .ppt-ui-btn,.ppt-session-actions .ppt-ui-btn{min-width:68px}.ppt-session-actions{padding-top:7px;border-top:1px solid #343943}.ppt-rehearsal-on{color:#ff8a90}.ppt-page-state{text-align:right}.ppt-page-state strong{display:block;color:#fff;font-size:17px;margin-bottom:5px}.ppt-page-state span{color:#8f97a3;font:600 13px/1 var(--mono,monospace)}.ppt-auto-state{display:inline-flex;align-items:center;gap:6px;border-radius:999px}.ppt-auto-state.is-on{color:#72df99;border-color:rgba(114,223,153,.45);background:rgba(114,223,153,.08)}.ppt-auto-state.is-paused{color:#f0c766;border-color:rgba(240,199,102,.42);background:rgba(240,199,102,.08)}
|
||||||
|
#ppt-presenter[data-layout="slides"] .ppt-presenter-main{grid-template-columns:minmax(0,2.15fr) minmax(260px,.58fr)}#ppt-presenter[data-layout="notes"] .ppt-presenter-main{grid-template-columns:minmax(0,1.15fr) minmax(380px,1fr)}
|
||||||
|
.ppt-dialog{position:fixed;inset:0;z-index:130;display:grid;place-items:center;padding:24px;background:rgba(4,6,9,.72);backdrop-filter:blur(10px)}.ppt-dialog[hidden]{display:none}.ppt-dialog-panel{width:min(720px,calc(100vw - 48px));max-height:min(760px,calc(100vh - 48px));display:grid;grid-template-rows:auto minmax(0,1fr);overflow:hidden;border:1px solid #343943;border-radius:14px;background:#171a20;box-shadow:0 24px 80px rgba(0,0,0,.45)}.ppt-dialog-head{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px 18px;border-bottom:1px solid #2b2f37}.ppt-dialog-title{font-size:20px;font-weight:700}.ppt-dialog-body{overflow:auto;padding:18px;color:#d8dce2;font-size:14px;line-height:1.6}.ppt-dialog-section+.ppt-dialog-section{margin-top:22px}.ppt-dialog-section h3{margin:0 0 10px;color:#fff;font-size:15px}.ppt-dialog-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.ppt-dialog-row{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:10px 0;border-bottom:1px solid #282c33}.ppt-dialog-row:last-child{border-bottom:0}.ppt-field{display:grid;gap:6px}.ppt-field label{color:#aeb4be;font-size:12px}.ppt-field input{width:100%;border:1px solid #3a3e48;border-radius:8px;background:#101216;color:#fff;padding:9px 10px;font:600 14px Inter,sans-serif}.ppt-check{display:flex;align-items:flex-start;gap:10px}.ppt-check-dot{width:9px;height:9px;margin-top:7px;border-radius:50%;background:#ff6b72}.ppt-check.is-ok .ppt-check-dot{background:#72df99}.ppt-check.is-warn .ppt-check-dot{background:#f0c766}.ppt-summary{width:100%;border-collapse:collapse;font-size:13px}.ppt-summary th,.ppt-summary td{padding:9px 8px;border-bottom:1px solid #2b2f37;text-align:left}.ppt-summary th{color:#9fa6b1;font-weight:600}.ppt-summary td:last-child,.ppt-summary th:last-child{text-align:right}.ppt-muted{color:#8f96a1}.ppt-shortcuts{display:grid;grid-template-columns:auto 1fr;gap:8px 14px}.ppt-shortcuts kbd{min-width:44px;padding:4px 7px;border:1px solid #3a3e48;border-radius:6px;background:#101216;color:#fff;text-align:center;font:600 12px var(--mono,monospace)}
|
||||||
|
.ppt-settings-card{padding:15px;border:1px solid #30353e;border-radius:12px;background:#12151a}.ppt-settings-card+.ppt-settings-card{margin-top:12px}.ppt-settings-card h3{margin:0 0 3px}.ppt-settings-copy{margin:0 0 14px;color:#858d99;font-size:12px}.ppt-switch-row{display:flex;align-items:center;justify-content:space-between;gap:18px;cursor:pointer}.ppt-switch-row input{position:absolute;opacity:0;pointer-events:none}.ppt-switch{width:48px;height:28px;flex:0 0 auto;padding:3px;border:1px solid #434955;border-radius:999px;background:#252a31;transition:.2s}.ppt-switch::after{content:"";display:block;width:20px;height:20px;border-radius:50%;background:#a8afb9;transition:.2s}.ppt-switch-row input:checked+.ppt-switch{border-color:#72df99;background:rgba(114,223,153,.16)}.ppt-switch-row input:checked+.ppt-switch::after{transform:translateX(20px);background:#72df99}.ppt-stepper{display:grid;grid-template-columns:38px minmax(72px,1fr) auto 38px;align-items:center;margin-top:14px;border:1px solid #363c46;border-radius:10px;background:#0d0f13;overflow:hidden}.ppt-stepper button{height:42px;border:0;background:#20242a;color:#fff;font-size:19px;cursor:pointer}.ppt-stepper button:hover{background:#2c323b}.ppt-stepper input{width:100%;height:42px;border:0!important;border-radius:0!important;background:transparent!important;text-align:center;font:650 16px var(--mono,monospace)!important;-moz-appearance:textfield}.ppt-stepper input::-webkit-inner-spin-button,.ppt-stepper input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.ppt-stepper-unit{color:#858d99;font-size:12px}.ppt-segmented{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}.ppt-segmented.screen{grid-template-columns:repeat(4,1fr)}.ppt-segmented .ppt-ui-btn{min-width:0}.ppt-settings-save{width:100%;margin-top:14px;background:#edf1f5;color:#111318;border-color:#edf1f5}.ppt-settings-save:hover{background:#fff}.ppt-settings-save+.ppt-settings-copy{margin:10px 0 0}
|
||||||
|
#overview{z-index:110!important}
|
||||||
|
@media(max-width:1200px){.ppt-presenter-main{grid-template-columns:minmax(0,1.55fr) minmax(280px,.72fr)}}
|
||||||
|
@media(max-width:1100px){.ppt-presenter-bottom{grid-template-columns:minmax(292px,1fr) auto minmax(86px,.6fr);gap:12px}.ppt-time-stat{min-width:82px;padding:0 11px}.ppt-time-value{font-size:20px}.ppt-controls .ppt-ui-btn,.ppt-session-actions .ppt-ui-btn{min-width:60px;padding:8px 9px}}
|
||||||
|
@media(max-width:900px){.ppt-presenter-top{padding:12px 16px;flex-wrap:wrap}.ppt-presenter-top .ppt-presenter-meta{gap:6px}.ppt-presenter-top .ppt-ui-btn{padding:8px 9px}.ppt-presenter-main,#ppt-presenter[data-layout="slides"] .ppt-presenter-main,#ppt-presenter[data-layout="notes"] .ppt-presenter-main{grid-template-columns:minmax(0,1.45fr) minmax(250px,.72fr);gap:12px;padding:12px 16px}.ppt-preview-stack{gap:12px;grid-template-rows:minmax(0,1fr) clamp(104px,20vh,150px)}.ppt-saved{display:none}.ppt-presenter-bottom{grid-template-columns:minmax(270px,1fr) auto 74px;gap:8px;padding:9px 16px}.ppt-time-stat{min-width:0;padding:0 9px}.ppt-time-value{font-size:18px}.ppt-time-hint{max-width:78px;overflow:hidden;text-overflow:ellipsis}.ppt-controls,.ppt-session-actions{gap:5px}.ppt-controls .ppt-ui-btn,.ppt-session-actions .ppt-ui-btn{min-width:52px;padding:7px 8px;font-size:12px}.ppt-page-state strong{font-size:14px}.ppt-dialog-grid{grid-template-columns:1fr}.ppt-segmented.screen{grid-template-columns:repeat(2,1fr)}}
|
||||||
|
@media(max-height:700px){.ppt-presenter-top{padding-top:10px;padding-bottom:10px}.ppt-presenter-main{padding-top:10px;padding-bottom:10px}.ppt-preview-stack{grid-template-rows:minmax(0,1fr) clamp(88px,16vh,118px);gap:10px}.ppt-stage-label{padding:7px 10px}.ppt-notes-head{padding:12px 14px}.ppt-purpose-block{margin-top:10px;padding:8px 10px}.ppt-notes-draft-head{margin-top:10px;padding-top:9px}.ppt-presenter-bottom{padding-top:8px;padding-bottom:8px}.ppt-control-stack{gap:5px}.ppt-session-actions{padding-top:5px}}
|
||||||
|
|
||||||
/* ESC 索引页: 动画元素强制可见 (覆盖 motion-ready 的 opacity:0) */
|
/* ESC 索引页: 动画元素强制可见 (覆盖 motion-ready 的 opacity:0) */
|
||||||
#overview [data-anim]{opacity:1!important;transform:none!important}
|
#overview [data-anim]{opacity:1!important;transform:none!important}
|
||||||
#overview .slide *{animation:none!important;transition:none!important}
|
#overview .slide *{animation:none!important;transition:none!important}
|
||||||
@@ -1148,8 +1194,8 @@
|
|||||||
const stored = localStorage.getItem(KEY);
|
const stored = localStorage.getItem(KEY);
|
||||||
window.__lowPowerMode = stored === '1' || (stored === null && reduced);
|
window.__lowPowerMode = stored === '1' || (stored === null && reduced);
|
||||||
function updateHint(){
|
function updateHint(){
|
||||||
const hint = document.getElementById('hint');
|
const label = document.querySelector('.ppt-low-power-label');
|
||||||
if(hint) hint.textContent = `← → 翻页 · B ${window.__lowPowerMode ? '动态' : '静态'} · ESC 索引`;
|
if(label) label.textContent = window.__lowPowerMode ? '动态' : '静态';
|
||||||
}
|
}
|
||||||
window.__setLowPowerMode = function(on, opts={}){
|
window.__setLowPowerMode = function(on, opts={}){
|
||||||
window.__lowPowerMode = !!on;
|
window.__lowPowerMode = !!on;
|
||||||
@@ -1168,7 +1214,12 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<canvas id="bg-grid" class="bg"></canvas>
|
<canvas id="bg-grid" class="bg"></canvas>
|
||||||
<div id="hint">← → 翻页 · B 静态 · ESC 索引</div>
|
<div id="hint" role="toolbar" aria-label="快捷控制">
|
||||||
|
<span>← → 翻页</span><span class="ppt-sep">·</span>
|
||||||
|
<button id="ppt-low-power-btn" type="button"><span class="ppt-key">B</span><span class="ppt-low-power-label">静态</span></button><span class="ppt-sep">·</span>
|
||||||
|
<button id="ppt-overview-btn" type="button"><span class="ppt-key">ESC</span>总览</button><span class="ppt-sep">·</span>
|
||||||
|
<button id="ppt-presenter-btn" type="button"><span class="ppt-key">P</span>演讲模式</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="deck">
|
<div id="deck">
|
||||||
|
|
||||||
@@ -1196,7 +1247,7 @@
|
|||||||
- canvas-card 内首位插入 <canvas class="ascii-bg" aria-hidden="true">,本文件底部 IIFE 自动启动
|
- canvas-card 内首位插入 <canvas class="ascii-bg" aria-hidden="true">,本文件底部 IIFE 自动启动
|
||||||
- 主标题反白 weight 200,强调字用斜体而非 var(--accent)(底已是蓝,蓝压蓝看不见)
|
- 主标题反白 weight 200,强调字用斜体而非 var(--accent)(底已是蓝,蓝压蓝看不见)
|
||||||
- 不要再放编号大字"01"——chrome-min 已经标 01/NN -->
|
- 不要再放编号大字"01"——chrome-min 已经标 01/NN -->
|
||||||
<section class="slide accent" data-animate="hero">
|
<section class="slide accent" data-animate="hero" data-slide-id="cover">
|
||||||
<div class="canvas-card">
|
<div class="canvas-card">
|
||||||
<canvas class="ascii-bg" aria-hidden="true"></canvas>
|
<canvas class="ascii-bg" aria-hidden="true"></canvas>
|
||||||
<div class="chrome-min">
|
<div class="chrome-min">
|
||||||
@@ -1224,7 +1275,7 @@
|
|||||||
<!-- ============ 示例:最后一页 · Closing Manifesto · 左 IKB+ASCII / 右白底 takeaway ============
|
<!-- ============ 示例:最后一页 · Closing Manifesto · 左 IKB+ASCII / 右白底 takeaway ============
|
||||||
与封面 IKB 首尾呼应,但收束更克制:左半保留 ASCII 呼吸场承载宣言,右半白底列 3 条 takeaway.
|
与封面 IKB 首尾呼应,但收束更克制:左半保留 ASCII 呼吸场承载宣言,右半白底列 3 条 takeaway.
|
||||||
第 03 条用 var(--accent) IKB 蓝强调("单一锚点"原则),首尾形成"色彩闭环". -->
|
第 03 条用 var(--accent) IKB 蓝强调("单一锚点"原则),首尾形成"色彩闭环". -->
|
||||||
<section class="slide split" data-animate="split-statement">
|
<section class="slide split" data-animate="split-statement" data-slide-id="closing">
|
||||||
<div class="canvas-card">
|
<div class="canvas-card">
|
||||||
<div class="split-half">
|
<div class="split-half">
|
||||||
<!-- 左半 · IKB 宣言 + ASCII 呼吸场 -->
|
<!-- 左半 · IKB 宣言 + ASCII 呼吸场 -->
|
||||||
@@ -1288,6 +1339,25 @@
|
|||||||
|
|
||||||
<div id="nav"></div>
|
<div id="nav"></div>
|
||||||
|
|
||||||
|
<!-- 演讲备注:替换示例页时同步替换,id 必须与 section[data-slide-id] 一致。 -->
|
||||||
|
<script>
|
||||||
|
const SPEAKER_NOTES = [
|
||||||
|
{
|
||||||
|
id: 'cover', title: '[必填] Deck 标题', section: '开场', minutes: 0.6,
|
||||||
|
purpose: '建立主题与现场预期',
|
||||||
|
talk: ['用一句反差或问题开场', '说明为什么是现在讲这件事', '只预告主线,不在封面展开细节'],
|
||||||
|
transition: '从开场问题进入核心内容', cue: '停顿,确认观众屏已同步'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'closing', title: '[必填] Closing', section: '收束', minutes: 0.8,
|
||||||
|
purpose: '收束全场并留下可记忆的行动点',
|
||||||
|
talk: ['回扣开场时提出的问题', '把三条 takeaway 说成一个完整判断', '给观众一个会后可以立即采取的动作'],
|
||||||
|
transition: '结束演讲,进入提问或致谢', cue: '停顿 2 秒后致谢'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
window.__SPEAKER_NOTES__ = SPEAKER_NOTES;
|
||||||
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* =============== WebGL 网格背景 (瑞士风专用) ===============
|
/* =============== WebGL 网格背景 (瑞士风专用) ===============
|
||||||
极简移动网格 + 微弱点阵叠加,营造"工业感、精准感"
|
极简移动网格 + 微弱点阵叠加,营造"工业感、精准感"
|
||||||
@@ -1451,8 +1521,8 @@ slides.forEach((s,i)=>{
|
|||||||
nav.appendChild(b);
|
nav.appendChild(b);
|
||||||
});
|
});
|
||||||
|
|
||||||
function go(n){
|
function go(n,opts={}){
|
||||||
if(lock)return;
|
if(lock&&!opts.force)return;
|
||||||
idx=Math.max(0,Math.min(total-1,n));
|
idx=Math.max(0,Math.min(total-1,n));
|
||||||
window.__currentSlideIndex = idx;
|
window.__currentSlideIndex = idx;
|
||||||
deck.style.transform=`translateX(${-idx*100}vw)`;
|
deck.style.transform=`translateX(${-idx*100}vw)`;
|
||||||
@@ -1462,7 +1532,8 @@ function go(n){
|
|||||||
document.body.classList.toggle('dark-bg', isDark);
|
document.body.classList.toggle('dark-bg', isDark);
|
||||||
darkMode = isDark;
|
darkMode = isDark;
|
||||||
if(window.__playSlide) setTimeout(()=>window.__playSlide(idx), 450);
|
if(window.__playSlide) setTimeout(()=>window.__playSlide(idx), 450);
|
||||||
lock=true;setTimeout(()=>lock=false,700);
|
window.__pptPresenter?.onSlideChange(idx);
|
||||||
|
if(!opts.force){lock=true;setTimeout(()=>lock=false,700);}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =============== ESC 索引视图 =============== */
|
/* =============== ESC 索引视图 =============== */
|
||||||
@@ -1490,7 +1561,7 @@ function buildOverview(){
|
|||||||
const label=document.createElement('div');
|
const label=document.createElement('div');
|
||||||
/* ESC 索引卡 label */
|
/* ESC 索引卡 label */
|
||||||
label.style.cssText='padding:6px 10px;font-family:var(--mono);font-size:14px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink);opacity:.7';
|
label.style.cssText='padding:6px 10px;font-family:var(--mono);font-size:14px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink);opacity:.7';
|
||||||
label.textContent=(i+1)+' / '+total;
|
label.textContent=window.__pptPresenter?.slideLabel(i) || ((i+1)+' / '+total);
|
||||||
card.appendChild(wrap);
|
card.appendChild(wrap);
|
||||||
card.appendChild(label);
|
card.appendChild(label);
|
||||||
card.onclick=()=>{toggleOverview();go(i)};
|
card.onclick=()=>{toggleOverview();go(i)};
|
||||||
@@ -1505,7 +1576,477 @@ function toggleOverview(){
|
|||||||
else{ov.style.display='none';}
|
else{ov.style.display='none';}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =============== 演讲者模式 / 观众屏同步 =============== */
|
||||||
|
window.__pptPresenter=(()=>{
|
||||||
|
const query=new URLSearchParams(location.search);
|
||||||
|
const requestedMode=query.get('mode')||'deck';
|
||||||
|
const originalTitle=document.title;
|
||||||
|
const notes=()=>Array.isArray(window.__SPEAKER_NOTES__)?window.__SPEAKER_NOTES__:[];
|
||||||
|
const slideId=i=>slides[i]?.dataset.slideId||`slide-${String(i+1).padStart(2,'0')}`;
|
||||||
|
const storageKey=name=>`guizang-ppt-${name}:${location.pathname}`;
|
||||||
|
let mode='deck',session=query.get('session')||Math.random().toString(36).slice(2),audienceWindow=null;
|
||||||
|
let channel=null,transportReady=false,popupBlocked=false,audienceOpenedAt=0,lastAckAt=0,lastAckIndex=-1,lastAckSeq=-1,seq=0,lastPingAt=0;
|
||||||
|
let renderedNoteId='',saveTimer=0,noteSize=Number(localStorage.getItem('guizang-ppt-note-size')||17),presenterFrameObserver=null;
|
||||||
|
let elapsed=0,timerRunning=false,timerStartedAt=0,timedSlideIndex=idx,slideTimerStartedAt=0,slideElapsed=Array(total).fill(0);
|
||||||
|
let rehearsalActive=false,presenterOverviewOn=false,lowPowerBeforePresenter=window.__lowPowerMode;
|
||||||
|
let layoutMode=localStorage.getItem(storageKey('layout'))||'balanced';
|
||||||
|
let autoEnabled=localStorage.getItem(storageKey('auto-enabled'))==='1';
|
||||||
|
let autoGlobalSeconds=Math.max(0,Number(localStorage.getItem(storageKey('auto-seconds'))||0));
|
||||||
|
let autoDeadline=0,autoRemainingMs=0;
|
||||||
|
let screenMode='normal',audienceFrozen=false;
|
||||||
|
let annotationTool='none',annotationPointerDown=false,annotationStart=null,draftCircle=null,circles=[],audienceCircles=[];
|
||||||
|
let laserPoint=null,audienceLaser=null,laserHideAt=0,audienceLaserHideAt=0,lastLaserSent=0;
|
||||||
|
const seenNonces=[],seenNonceSet=new Set();
|
||||||
|
const syncKey=()=>`guizang-ppt-sync:${location.pathname}:${session}`;
|
||||||
|
const channelName=()=>`guizang-ppt:${location.pathname}:${session}`;
|
||||||
|
|
||||||
|
function noteAt(i){
|
||||||
|
const id=slideId(i);
|
||||||
|
return notes().find(n=>n.id===id)||{id,title:slides[i]?.dataset.title||`第 ${i+1} 页`,purpose:'待补充本页目的',talk:['待补充讲述要点'],transition:'待补充转场'};
|
||||||
|
}
|
||||||
|
function slideLabel(i){return `${i+1} / ${total} · ${noteAt(i).title||slideId(i)}`;}
|
||||||
|
function escapeHTML(value){return String(value??'').replace(/[&<>"']/g,ch=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[ch]));}
|
||||||
|
function valueLines(value){
|
||||||
|
if(Array.isArray(value))return value.filter(Boolean).map(x=>`• ${x}`).join('\n');
|
||||||
|
return value?String(value):'';
|
||||||
|
}
|
||||||
|
function generatedNote(n){
|
||||||
|
const parts=[];
|
||||||
|
if(Array.isArray(n.talk)&&n.talk.length)parts.push(`【讲述要点】\n${valueLines(n.talk)}`);
|
||||||
|
if(n.transition)parts.push(`【转场】\n${n.transition}`);
|
||||||
|
const optional=[['舞台提示',n.cue],['互动',n.interaction],['表达提示',n.delivery],['翻页时机',n.advance],['备用方案',n.fallback],['读音提醒',n.pronunciation]];
|
||||||
|
optional.forEach(([label,value])=>{const text=valueLines(value);if(text)parts.push(`【${label}】\n${text}`);});
|
||||||
|
return parts.join('\n\n');
|
||||||
|
}
|
||||||
|
function noteStorageKey(id){return `guizang-ppt-note:${location.pathname}:${id}`;}
|
||||||
|
function loadNoteValue(id,i,n){
|
||||||
|
const saved=localStorage.getItem(noteStorageKey(id));
|
||||||
|
if(saved!==null)return saved;
|
||||||
|
const prefix=window.__PPT_LEGACY_NOTE_KEY__;
|
||||||
|
if(prefix){
|
||||||
|
const legacy=localStorage.getItem(`${prefix}:${i}`);
|
||||||
|
if(legacy!==null){localStorage.setItem(noteStorageKey(id),legacy);return legacy;}
|
||||||
|
}
|
||||||
|
return generatedNote(n);
|
||||||
|
}
|
||||||
|
function frameUrl(targetMode,i){
|
||||||
|
const url=new URL(location.href);url.hash='';
|
||||||
|
url.searchParams.set('mode',targetMode);url.searchParams.set('session',session);url.searchParams.set('slide',String(i+1));
|
||||||
|
return url.href;
|
||||||
|
}
|
||||||
|
function plannedMsAt(i){
|
||||||
|
const minutes=Number(noteAt(i).minutes);
|
||||||
|
return Number.isFinite(minutes)&&minutes>0?minutes*60000:null;
|
||||||
|
}
|
||||||
|
function totalPlannedMs(){
|
||||||
|
const values=Array.from({length:total},(_,i)=>plannedMsAt(i));
|
||||||
|
return values.length&&values.every(Number.isFinite)?values.reduce((sum,x)=>sum+x,0):null;
|
||||||
|
}
|
||||||
|
function sectionInfo(i){
|
||||||
|
const name=String(noteAt(i).section||'').trim();
|
||||||
|
if(!name)return null;
|
||||||
|
let first=i,last=i;
|
||||||
|
while(first>0&&String(noteAt(first-1).section||'').trim()===name)first--;
|
||||||
|
while(last<total-1&&String(noteAt(last+1).section||'').trim()===name)last++;
|
||||||
|
const targets=[];for(let x=first;x<=last;x++)targets.push(plannedMsAt(x));
|
||||||
|
const planned=targets.every(Number.isFinite)?targets.reduce((sum,x)=>sum+x,0):null;
|
||||||
|
const used=slideElapsed.slice(first,last+1).reduce((sum,x)=>sum+x,0)+(timerRunning&&timedSlideIndex>=first&&timedSlideIndex<=last?Date.now()-slideTimerStartedAt:0);
|
||||||
|
return{name,position:i-first+1,count:last-first+1,planned,used};
|
||||||
|
}
|
||||||
|
function durationText(ms){
|
||||||
|
const value=Math.max(0,Math.round(Number(ms)||0)),seconds=Math.floor(value/1000);
|
||||||
|
const hours=Math.floor(seconds/3600),minutes=Math.floor((seconds%3600)/60),secs=seconds%60;
|
||||||
|
return hours?`${String(hours).padStart(2,'0')}:${String(minutes).padStart(2,'0')}:${String(secs).padStart(2,'0')}`:`${String(minutes).padStart(2,'0')}:${String(secs).padStart(2,'0')}`;
|
||||||
|
}
|
||||||
|
function signedDuration(ms){return `${ms>=0?'+':'−'}${durationText(Math.abs(ms))}`;}
|
||||||
|
function clockTime(timestamp){return new Date(timestamp).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'});}
|
||||||
|
|
||||||
|
function fitFrame(frame){
|
||||||
|
const viewport=frame?.closest('.ppt-frame-viewport');
|
||||||
|
if(!viewport)return;
|
||||||
|
const availableWidth=viewport.clientWidth,availableHeight=viewport.clientHeight;
|
||||||
|
if(!availableWidth||!availableHeight)return;
|
||||||
|
const width=Math.min(availableWidth,availableHeight*(16/9)),height=width*(9/16);
|
||||||
|
frame.style.width=`${width}px`;frame.style.height=`${height}px`;
|
||||||
|
if(frame.id==='ppt-current-frame')sizeInk(document.getElementById('ppt-presenter-ink'),width,height);
|
||||||
|
}
|
||||||
|
function fitPresenterFrames(){
|
||||||
|
fitFrame(document.getElementById('ppt-current-frame'));
|
||||||
|
fitFrame(document.getElementById('ppt-next-frame'));
|
||||||
|
}
|
||||||
|
function sizeInk(canvas,width,height){
|
||||||
|
if(!canvas||!width||!height)return;
|
||||||
|
canvas.style.width=`${width}px`;canvas.style.height=`${height}px`;
|
||||||
|
const dpr=Math.min(devicePixelRatio||1,2),pixelWidth=Math.round(width*dpr),pixelHeight=Math.round(height*dpr);
|
||||||
|
if(canvas.width!==pixelWidth||canvas.height!==pixelHeight){canvas.width=pixelWidth;canvas.height=pixelHeight;}
|
||||||
|
}
|
||||||
|
function drawEllipse(ctx,circle,width,height,alpha=1){
|
||||||
|
const x1=circle.x1*width,y1=circle.y1*height,x2=circle.x2*width,y2=circle.y2*height;
|
||||||
|
ctx.save();ctx.globalAlpha=alpha;ctx.strokeStyle='#ff4f59';ctx.lineWidth=Math.max(3,width*.004);ctx.lineCap='round';
|
||||||
|
ctx.beginPath();ctx.ellipse((x1+x2)/2,(y1+y2)/2,Math.abs(x2-x1)/2,Math.abs(y2-y1)/2,0,0,Math.PI*2);ctx.stroke();ctx.restore();
|
||||||
|
}
|
||||||
|
function drawInk(canvas,list,laser,draft){
|
||||||
|
if(!canvas)return;const width=canvas.clientWidth,height=canvas.clientHeight;if(!width||!height)return;
|
||||||
|
sizeInk(canvas,width,height);const dpr=canvas.width/width,ctx=canvas.getContext('2d');ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,width,height);
|
||||||
|
(list||[]).forEach(circle=>drawEllipse(ctx,circle,width,height));
|
||||||
|
if(draft)drawEllipse(ctx,draft,width,height,.72);
|
||||||
|
if(laser){
|
||||||
|
const x=laser.x*width,y=laser.y*height,r=Math.max(6,width*.007);
|
||||||
|
const glow=ctx.createRadialGradient(x,y,0,x,y,r*2.8);glow.addColorStop(0,'rgba(255,255,255,.98)');glow.addColorStop(.2,'rgba(255,64,72,.98)');glow.addColorStop(1,'rgba(255,64,72,0)');
|
||||||
|
ctx.fillStyle=glow;ctx.beginPath();ctx.arc(x,y,r*2.8,0,Math.PI*2);ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function renderInks(){
|
||||||
|
const now=Date.now();if(now>laserHideAt)laserPoint=null;if(now>audienceLaserHideAt)audienceLaser=null;
|
||||||
|
drawInk(document.getElementById('ppt-presenter-ink'),circles,laserPoint,draftCircle);
|
||||||
|
drawInk(document.getElementById('ppt-audience-ink'),audienceCircles,audienceLaser,null);
|
||||||
|
}
|
||||||
|
function setAnnotationTool(tool){
|
||||||
|
annotationTool=annotationTool===tool?'none':tool;annotationPointerDown=false;draftCircle=null;
|
||||||
|
const canvas=document.getElementById('ppt-presenter-ink');canvas?.classList.toggle('is-active',annotationTool!=='none');
|
||||||
|
document.querySelectorAll('[data-ppt-tool]').forEach(button=>button.classList.toggle('is-active',button.dataset.pptTool===annotationTool));
|
||||||
|
renderInks();
|
||||||
|
}
|
||||||
|
function annotationPoint(event){
|
||||||
|
const canvas=event.currentTarget,rect=canvas.getBoundingClientRect();
|
||||||
|
return{x:Math.max(0,Math.min(1,(event.clientX-rect.left)/rect.width)),y:Math.max(0,Math.min(1,(event.clientY-rect.top)/rect.height))};
|
||||||
|
}
|
||||||
|
function onAnnotationPointerDown(event){
|
||||||
|
if(annotationTool!=='circle')return;event.preventDefault();annotationPointerDown=true;annotationStart=annotationPoint(event);draftCircle={x1:annotationStart.x,y1:annotationStart.y,x2:annotationStart.x,y2:annotationStart.y};event.currentTarget.setPointerCapture?.(event.pointerId);renderInks();
|
||||||
|
}
|
||||||
|
function onAnnotationPointerMove(event){
|
||||||
|
if(annotationTool==='laser'){
|
||||||
|
const point=annotationPoint(event);laserPoint=point;laserHideAt=Date.now()+650;
|
||||||
|
if(Date.now()-lastLaserSent>32){lastLaserSent=Date.now();emit({type:'laser',index:idx,point},{persist:false});}renderInks();return;
|
||||||
|
}
|
||||||
|
if(annotationTool==='circle'&&annotationPointerDown&&draftCircle){const point=annotationPoint(event);draftCircle.x2=point.x;draftCircle.y2=point.y;renderInks();}
|
||||||
|
}
|
||||||
|
function onAnnotationPointerUp(event){
|
||||||
|
if(annotationTool!=='circle'||!annotationPointerDown||!draftCircle)return;event.preventDefault();annotationPointerDown=false;
|
||||||
|
const circle={...draftCircle};draftCircle=null;
|
||||||
|
if(Math.abs(circle.x2-circle.x1)>.012&&Math.abs(circle.y2-circle.y1)>.012){circles.push(circle);emit({type:'circle',index:idx,circle});}
|
||||||
|
renderInks();
|
||||||
|
}
|
||||||
|
function onAnnotationPointerLeave(){
|
||||||
|
if(annotationTool==='laser'){laserPoint=null;emit({type:'laser',index:idx,point:null},{persist:false});renderInks();}
|
||||||
|
}
|
||||||
|
function clearAnnotations(broadcast=true){circles=[];draftCircle=null;laserPoint=null;if(broadcast)emit({type:'clear',index:idx});renderInks();}
|
||||||
|
|
||||||
|
function buildPresenterOverview(){
|
||||||
|
const grid=document.getElementById('ppt-presenter-overview-grid');if(!grid)return;grid.innerHTML='';
|
||||||
|
slides.forEach((slide,i)=>{
|
||||||
|
const n=noteAt(i),button=document.createElement('button');button.type='button';button.className=`ppt-overview-page${i===idx?' is-current':''}`;
|
||||||
|
button.innerHTML=`<span class="ppt-overview-page-no">${String(i+1).padStart(2,'0')} / ${String(total).padStart(2,'0')}</span><span class="ppt-overview-page-title">${escapeHTML(n.title||slideId(i))}</span><span class="ppt-overview-page-meta"><span>${escapeHTML(n.section||'未分组')}</span><span>${Math.round(((i+1)/total)*100)}%</span></span>`;
|
||||||
|
button.onclick=()=>{togglePresenterOverview(false);go(i,{force:true});};grid.appendChild(button);
|
||||||
|
});
|
||||||
|
requestAnimationFrame(()=>grid.querySelector('.is-current')?.scrollIntoView({block:'nearest',inline:'nearest'}));
|
||||||
|
}
|
||||||
|
function togglePresenterOverview(force){
|
||||||
|
presenterOverviewOn=typeof force==='boolean'?force:!presenterOverviewOn;
|
||||||
|
if(presenterOverviewOn&&annotationTool!=='none')setAnnotationTool('none');
|
||||||
|
const stack=document.querySelector('.ppt-preview-stack'),overview=document.getElementById('ppt-presenter-overview');
|
||||||
|
stack?.classList.toggle('is-overview',presenterOverviewOn);if(overview)overview.hidden=!presenterOverviewOn;
|
||||||
|
const button=document.getElementById('ppt-grid');if(button){button.classList.toggle('is-active',presenterOverviewOn);button.setAttribute('aria-pressed',String(presenterOverviewOn));}
|
||||||
|
if(presenterOverviewOn)buildPresenterOverview();else requestAnimationFrame(fitPresenterFrames);renderAutoState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function mountShell(){
|
||||||
|
if(document.getElementById('ppt-presenter'))return;
|
||||||
|
document.body.insertAdjacentHTML('beforeend',`<div id="ppt-presenter" data-layout="${layoutMode}" aria-label="演讲者视图">
|
||||||
|
<div class="ppt-presenter-top"><div><div class="ppt-presenter-brand">演讲者视图</div><div class="ppt-presenter-meta"><span id="ppt-plan"></span><span id="ppt-section" hidden></span></div></div><div class="ppt-presenter-meta"><span id="ppt-sync" class="ppt-sync" data-state="disconnected" title="表示观众页面的软件同步状态,不代表 HDMI 等物理线缆状态">观众屏 · 未连接</span><button id="ppt-auto-toggle" class="ppt-ui-btn ppt-auto-state" type="button">自动翻页 · 关闭</button><button id="ppt-preflight" class="ppt-ui-btn" type="button">检查</button><button id="ppt-settings" class="ppt-ui-btn" type="button">设置</button><button id="ppt-reopen" class="ppt-ui-btn" type="button">重新打开观众屏</button><button id="ppt-exit" class="ppt-ui-btn" type="button">退出</button></div></div>
|
||||||
|
<div class="ppt-presenter-main"><div class="ppt-preview-stack"><div class="ppt-stage-card"><div class="ppt-stage-label"><span>当前页</span><span class="ppt-annotation-tools"><button id="ppt-grid" class="ppt-ui-btn ppt-grid-btn" type="button" aria-pressed="false" title="宫格选页(Esc)"><span class="ppt-grid-icon" aria-hidden="true"><i></i><i></i><i></i><i></i></span>宫格</button><button class="ppt-ui-btn" data-ppt-tool="laser" type="button" title="激光笔(L)">激光</button><button class="ppt-ui-btn" data-ppt-tool="circle" type="button" title="圈选(C)">圈选</button><button id="ppt-ink-clear" class="ppt-ui-btn ppt-clear-btn" type="button" title="清除标记(X)">清除</button></span><span id="ppt-current-label"></span></div><div class="ppt-frame-viewport"><iframe id="ppt-current-frame" class="ppt-frame" title="当前页预览"></iframe><canvas id="ppt-presenter-ink" class="ppt-annotation-layer" aria-label="演讲标注层"></canvas></div></div><div class="ppt-stage-card" id="ppt-next-card"><div class="ppt-stage-label"><span>下一页</span><span id="ppt-next-label"></span></div><div class="ppt-frame-viewport"><iframe id="ppt-next-frame" class="ppt-frame ppt-next-frame" title="下一页预览"></iframe></div></div><div id="ppt-presenter-overview" class="ppt-presenter-overview" hidden><div class="ppt-overview-head"><strong>选择页面</strong><button id="ppt-overview-close" class="ppt-ui-btn" type="button">返回预览</button></div><div id="ppt-presenter-overview-grid" class="ppt-overview-grid"></div></div></div>
|
||||||
|
<div class="ppt-notes-card"><div class="ppt-notes-head"><div class="ppt-notes-label">标题</div><div id="ppt-notes-title" class="ppt-notes-title"></div><div class="ppt-purpose-block"><div class="ppt-notes-label">本页目的</div><div id="ppt-notes-purpose" class="ppt-purpose-copy"></div></div><div class="ppt-notes-draft-head"><div><div class="ppt-notes-label">草稿(备注)</div><div id="ppt-note-meta" class="ppt-note-meta"></div></div><span class="ppt-notes-tools"><span id="ppt-saved" class="ppt-saved">已保存</span><button id="ppt-note-smaller" class="ppt-ui-btn" type="button" title="缩小备注字号">A−</button><button id="ppt-note-larger" class="ppt-ui-btn" type="button" title="放大备注字号">A+</button></span></div></div><textarea id="ppt-note-editor" class="ppt-note-editor" spellcheck="false" aria-label="本页演讲备注"></textarea></div></div>
|
||||||
|
<div class="ppt-presenter-bottom"><div class="ppt-time-dashboard"><div class="ppt-time-stat"><span class="ppt-time-label">已进行</span><strong id="ppt-clock" class="ppt-time-value">00:00</strong><span class="ppt-time-hint">总时长</span></div><div class="ppt-time-stat"><span class="ppt-time-label">本页</span><strong id="ppt-slide-clock" class="ppt-time-value">00:00</strong><span id="ppt-slide-plan" class="ppt-time-hint">计划 —</span></div><div id="ppt-slide-status-box" class="ppt-time-stat"><span id="ppt-slide-status-label" class="ppt-time-label">计划</span><strong id="ppt-slide-status" class="ppt-time-value">—</strong><span id="ppt-slide-status-hint" class="ppt-time-hint">未设置</span></div></div><div class="ppt-control-stack"><div class="ppt-controls"><button id="ppt-first" class="ppt-ui-btn" type="button">首页</button><button id="ppt-prev" class="ppt-ui-btn" type="button">上一页</button><button id="ppt-next" class="ppt-ui-btn" type="button">下一页</button><button id="ppt-last" class="ppt-ui-btn" type="button">尾页</button></div><div class="ppt-session-actions"><button id="ppt-timer-toggle" class="ppt-ui-btn" type="button">开始</button><button id="ppt-timer-reset" class="ppt-ui-btn" type="button">重置</button><button id="ppt-rehearsal" class="ppt-ui-btn" type="button">排练</button></div></div><div class="ppt-page-state"><strong id="ppt-page-count"></strong><span id="ppt-page-percent"></span></div></div>
|
||||||
|
<div id="ppt-dialog" class="ppt-dialog" hidden><div class="ppt-dialog-panel" role="dialog" aria-modal="true"><div class="ppt-dialog-head"><div id="ppt-dialog-title" class="ppt-dialog-title"></div><button id="ppt-dialog-close" class="ppt-ui-btn" type="button">关闭</button></div><div id="ppt-dialog-body" class="ppt-dialog-body"></div></div></div>
|
||||||
|
</div>`);
|
||||||
|
document.getElementById('ppt-reopen').onclick=reopenAudience;
|
||||||
|
document.getElementById('ppt-exit').onclick=exitPresenter;
|
||||||
|
document.getElementById('ppt-preflight').onclick=openPreflight;
|
||||||
|
document.getElementById('ppt-settings').onclick=openSettings;
|
||||||
|
document.getElementById('ppt-first').onclick=()=>go(0,{force:true});
|
||||||
|
document.getElementById('ppt-prev').onclick=()=>go(idx-1,{force:true});
|
||||||
|
document.getElementById('ppt-grid').onclick=()=>togglePresenterOverview();
|
||||||
|
document.getElementById('ppt-overview-close').onclick=()=>togglePresenterOverview(false);
|
||||||
|
document.getElementById('ppt-next').onclick=()=>go(idx+1,{force:true});
|
||||||
|
document.getElementById('ppt-last').onclick=()=>go(idx===total-1?0:total-1,{force:true});
|
||||||
|
document.getElementById('ppt-timer-toggle').onclick=toggleTimer;
|
||||||
|
document.getElementById('ppt-timer-reset').onclick=resetTimer;
|
||||||
|
document.getElementById('ppt-rehearsal').onclick=toggleRehearsal;
|
||||||
|
document.getElementById('ppt-auto-toggle').onclick=toggleAutoAdvance;
|
||||||
|
document.getElementById('ppt-note-smaller').onclick=()=>setNoteSize(noteSize-1);
|
||||||
|
document.getElementById('ppt-note-larger').onclick=()=>setNoteSize(noteSize+1);
|
||||||
|
document.getElementById('ppt-note-editor').addEventListener('input',saveNote);
|
||||||
|
document.querySelectorAll('[data-ppt-tool]').forEach(button=>button.onclick=()=>setAnnotationTool(button.dataset.pptTool));
|
||||||
|
document.getElementById('ppt-ink-clear').onclick=()=>clearAnnotations(true);
|
||||||
|
const ink=document.getElementById('ppt-presenter-ink');
|
||||||
|
ink.addEventListener('pointerdown',onAnnotationPointerDown);ink.addEventListener('pointermove',onAnnotationPointerMove);ink.addEventListener('pointerup',onAnnotationPointerUp);ink.addEventListener('pointercancel',onAnnotationPointerUp);ink.addEventListener('pointerleave',onAnnotationPointerLeave);
|
||||||
|
document.getElementById('ppt-dialog-close').onclick=closeDialog;
|
||||||
|
document.getElementById('ppt-dialog').onclick=event=>{if(event.target.id==='ppt-dialog')closeDialog();};
|
||||||
|
if('ResizeObserver'in window){presenterFrameObserver=new ResizeObserver(()=>requestAnimationFrame(fitPresenterFrames));presenterFrameObserver.observe(document.querySelector('.ppt-preview-stack'));}
|
||||||
|
else addEventListener('resize',fitPresenterFrames);
|
||||||
|
setNoteSize(noteSize,false);applyLayout(layoutMode,false);requestAnimationFrame(fitPresenterFrames);
|
||||||
|
}
|
||||||
|
function updateShell(){
|
||||||
|
if(mode!=='presenter')return;mountShell();
|
||||||
|
const n=noteAt(idx),next=Math.min(idx+1,total-1),id=slideId(idx),editor=document.getElementById('ppt-note-editor');
|
||||||
|
const currentFrame=document.getElementById('ppt-current-frame'),nextFrame=document.getElementById('ppt-next-frame');
|
||||||
|
if(currentFrame.dataset.slide!==String(idx)){currentFrame.src=frameUrl('preview',idx);currentFrame.dataset.slide=String(idx);}
|
||||||
|
if(nextFrame.dataset.slide!==String(next)){nextFrame.src=frameUrl('preview',next);nextFrame.dataset.slide=String(next);}
|
||||||
|
document.getElementById('ppt-current-label').textContent=`${idx+1} / ${total}`;
|
||||||
|
document.getElementById('ppt-next-label').textContent=idx===total-1?'已到尾页':`${next+1} / ${total}`;
|
||||||
|
document.getElementById('ppt-notes-title').textContent=n.title||`第 ${idx+1} 页`;
|
||||||
|
document.getElementById('ppt-notes-purpose').textContent=n.purpose||'—';
|
||||||
|
const target=plannedMsAt(idx);document.getElementById('ppt-note-meta').textContent=`建议 ${target?(target/60000).toFixed(1):'—'} 分钟 · ${id}`;
|
||||||
|
document.getElementById('ppt-page-count').textContent=`第 ${idx+1} / ${total} 页`;
|
||||||
|
document.getElementById('ppt-page-percent').textContent=`${Math.round(((idx+1)/total)*100)}%`;
|
||||||
|
document.getElementById('ppt-first').disabled=idx===0;document.getElementById('ppt-prev').disabled=idx===0;document.getElementById('ppt-next').disabled=idx===total-1;
|
||||||
|
document.getElementById('ppt-last').textContent=idx===total-1?'重新开始':'尾页';
|
||||||
|
if(renderedNoteId!==id){renderedNoteId=id;editor.value=loadNoteValue(id,idx,n);editor.scrollTop=0;document.getElementById('ppt-saved').textContent='已保存';}
|
||||||
|
if(presenterOverviewOn)buildPresenterOverview();requestAnimationFrame(fitPresenterFrames);renderTimer();updateSyncStatus();
|
||||||
|
}
|
||||||
|
function saveNote(){
|
||||||
|
const saved=document.getElementById('ppt-saved');saved.textContent='保存中…';saved.classList.remove('ppt-saved');clearTimeout(saveTimer);
|
||||||
|
saveTimer=setTimeout(()=>{localStorage.setItem(noteStorageKey(renderedNoteId),document.getElementById('ppt-note-editor').value);saved.textContent='已保存';saved.classList.add('ppt-saved');},300);
|
||||||
|
}
|
||||||
|
function setNoteSize(value,persist=true){noteSize=Math.max(14,Math.min(26,value));document.documentElement.style.setProperty('--ppt-note-size',`${noteSize}px`);if(persist)localStorage.setItem('guizang-ppt-note-size',String(noteSize));}
|
||||||
|
function currentElapsed(){return elapsed+(timerRunning?Date.now()-timerStartedAt:0);}
|
||||||
|
function currentSlideElapsed(i=idx){return slideElapsed[i]+(timerRunning&&timedSlideIndex===i?Date.now()-slideTimerStartedAt:0);}
|
||||||
|
function commitRunningTime(now=Date.now()){
|
||||||
|
if(!timerRunning)return;elapsed+=now-timerStartedAt;slideElapsed[timedSlideIndex]+=now-slideTimerStartedAt;timerStartedAt=now;slideTimerStartedAt=now;
|
||||||
|
}
|
||||||
|
function rollSlideTimer(nextIndex){if(timerRunning)commitRunningTime();timedSlideIndex=nextIndex;slideTimerStartedAt=Date.now();}
|
||||||
|
function renderTimer(){
|
||||||
|
const totalUsed=currentElapsed(),pageUsed=currentSlideElapsed(),target=plannedMsAt(idx),clock=document.getElementById('ppt-clock');
|
||||||
|
if(clock)clock.textContent=durationText(totalUsed);
|
||||||
|
const slideClock=document.getElementById('ppt-slide-clock'),slidePlan=document.getElementById('ppt-slide-plan'),statusBox=document.getElementById('ppt-slide-status-box'),statusLabel=document.getElementById('ppt-slide-status-label'),statusValue=document.getElementById('ppt-slide-status'),statusHint=document.getElementById('ppt-slide-status-hint');
|
||||||
|
if(slideClock)slideClock.textContent=durationText(pageUsed);if(slidePlan)slidePlan.textContent=target?`计划 ${durationText(target)}`:'计划 —';
|
||||||
|
if(statusBox&&statusLabel&&statusValue&&statusHint){
|
||||||
|
const over=Boolean(target&&pageUsed>target),remaining=Boolean(target&&!over);statusBox.classList.toggle('is-over',over);statusBox.classList.toggle('is-remaining',remaining);
|
||||||
|
statusLabel.textContent=over?'超时':(remaining?'剩余':'计划');statusValue.textContent=target?durationText(Math.abs(target-pageUsed)):'—';statusHint.textContent=over?'已超出本页计划':(remaining?'距本页计划':'未设置');
|
||||||
|
}
|
||||||
|
const totalPlan=totalPlannedMs(),plan=document.getElementById('ppt-plan');
|
||||||
|
if(plan)plan.textContent=totalPlan?`计划 ${Number(totalPlan/60000).toFixed(1)} 分钟 · 预计 ${clockTime(Date.now()+Math.max(0,totalPlan-totalUsed))}`:'计划 —';
|
||||||
|
const section=sectionInfo(idx),sectionEl=document.getElementById('ppt-section');
|
||||||
|
if(sectionEl){sectionEl.hidden=!section;sectionEl.textContent=section?`${section.name} · ${section.position}/${section.count}${section.planned?` · 剩余 ${durationText(Math.max(0,section.planned-section.used))}`:''}`:'';}
|
||||||
|
const timerButton=document.getElementById('ppt-timer-toggle');if(timerButton)timerButton.textContent=timerRunning?'暂停':(totalUsed?'继续':'开始');
|
||||||
|
const rehearsalButton=document.getElementById('ppt-rehearsal');if(rehearsalButton){rehearsalButton.textContent=rehearsalActive?'结束排练':'排练';rehearsalButton.classList.toggle('ppt-rehearsal-on',rehearsalActive);}
|
||||||
|
renderAutoState();
|
||||||
|
}
|
||||||
|
function toggleTimer(){const now=Date.now();if(timerRunning){commitRunningTime(now);timerRunning=false;}else{timerStartedAt=now;slideTimerStartedAt=now;timedSlideIndex=idx;timerRunning=true;}renderTimer();}
|
||||||
|
function resetTimer(){
|
||||||
|
elapsed=0;slideElapsed=Array(total).fill(0);timedSlideIndex=idx;
|
||||||
|
if(timerRunning){timerStartedAt=Date.now();slideTimerStartedAt=timerStartedAt;}else{timerStartedAt=0;slideTimerStartedAt=0;}
|
||||||
|
renderTimer();
|
||||||
|
}
|
||||||
|
function rehearsalHistory(){try{return JSON.parse(localStorage.getItem(storageKey('rehearsals'))||'[]');}catch(e){return[];}}
|
||||||
|
function toggleRehearsal(){
|
||||||
|
if(rehearsalActive){finishRehearsal(true);return;}
|
||||||
|
rehearsalActive=true;if(timerRunning){commitRunningTime();timerRunning=false;}resetTimer();toggleTimer();renderTimer();
|
||||||
|
}
|
||||||
|
function finishRehearsal(showSummary){
|
||||||
|
if(!rehearsalActive)return;if(timerRunning){commitRunningTime();timerRunning=false;}
|
||||||
|
const result={endedAt:Date.now(),totalMs:elapsed,slides:slideElapsed.map((ms,i)=>({index:i,id:slideId(i),title:noteAt(i).title||slideId(i),ms,targetMs:plannedMsAt(i)}))};
|
||||||
|
const history=rehearsalHistory();history.unshift(result);try{localStorage.setItem(storageKey('rehearsals'),JSON.stringify(history.slice(0,5)));}catch(e){}
|
||||||
|
rehearsalActive=false;renderTimer();if(showSummary)openRehearsalSummary(result,history.slice(0,5));
|
||||||
|
}
|
||||||
|
function openRehearsalSummary(result,history){
|
||||||
|
const rows=result.slides.filter(item=>item.ms>0).map(item=>`<tr><td>${item.index+1}</td><td>${escapeHTML(item.title)}</td><td>${durationText(item.ms)}</td><td>${item.targetMs?signedDuration(item.ms-item.targetMs):'—'}</td></tr>`).join('');
|
||||||
|
const recent=history.map(item=>`<div class="ppt-dialog-row"><span>${new Date(item.endedAt).toLocaleString()}</span><strong>${durationText(item.totalMs)}</strong></div>`).join('');
|
||||||
|
openDialog('排练记录',`<div class="ppt-dialog-section"><div class="ppt-dialog-row"><span>本次总时长</span><strong>${durationText(result.totalMs)}</strong></div><table class="ppt-summary"><thead><tr><th>#</th><th>页面</th><th>实际</th><th>与计划相比</th></tr></thead><tbody>${rows||'<tr><td colspan="4">没有记录到页面时间</td></tr>'}</tbody></table></div><div class="ppt-dialog-section"><h3>最近排练</h3>${recent}</div>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoDurationMs(i=idx){const explicit=Number(noteAt(i).autoAdvanceSeconds);const seconds=Number.isFinite(explicit)&&explicit>0?explicit:autoGlobalSeconds;return seconds>0?seconds*1000:null;}
|
||||||
|
function armAutoAdvance(){autoDeadline=0;autoRemainingMs=autoDurationMs()||0;}
|
||||||
|
function autoBlockedReason(){
|
||||||
|
if(presenterOverviewOn||overviewOn)return'宫格选页中';if(annotationTool!=='none')return'标注中';if(screenMode!=='normal')return'观众屏已暂停';if(document.hidden)return'窗口不可见';
|
||||||
|
const dialog=document.getElementById('ppt-dialog');if(dialog&&!dialog.hidden)return'设置中';
|
||||||
|
if(audienceOpenedAt&&syncState()!=='synced')return'等待观众屏同步';return'';
|
||||||
|
}
|
||||||
|
function updateAutoAdvance(){
|
||||||
|
if(!autoEnabled)return;
|
||||||
|
const duration=autoDurationMs();if(!duration||idx===total-1)return;
|
||||||
|
const blocked=autoBlockedReason(),now=Date.now();
|
||||||
|
if(blocked){if(autoDeadline){autoRemainingMs=Math.max(0,autoDeadline-now);autoDeadline=0;}return;}
|
||||||
|
if(!autoDeadline)autoDeadline=now+(autoRemainingMs||duration);
|
||||||
|
autoRemainingMs=Math.max(0,autoDeadline-now);
|
||||||
|
if(now>=autoDeadline){autoDeadline=0;autoRemainingMs=0;go(idx+1,{force:true});}
|
||||||
|
}
|
||||||
|
function renderAutoState(){
|
||||||
|
const button=document.getElementById('ppt-auto-toggle');if(!button)return;
|
||||||
|
const duration=autoDurationMs(),blocked=autoEnabled?autoBlockedReason():'';
|
||||||
|
button.classList.toggle('is-on',autoEnabled&&!blocked);button.classList.toggle('is-paused',autoEnabled&&Boolean(blocked));
|
||||||
|
if(!autoEnabled)button.textContent='自动翻页 · 关闭';
|
||||||
|
else if(!duration)button.textContent='自动翻页 · 等待时长';
|
||||||
|
else if(idx===total-1)button.textContent='自动翻页 · 已结束';
|
||||||
|
else if(blocked)button.textContent=`自动翻页 · ${blocked}`;
|
||||||
|
else button.textContent=`自动翻页 · ${durationText(autoRemainingMs||duration)}`;
|
||||||
|
}
|
||||||
|
function toggleAutoAdvance(){
|
||||||
|
if(!autoEnabled&&!autoDurationMs()){openSettings();return;}
|
||||||
|
autoEnabled=!autoEnabled;localStorage.setItem(storageKey('auto-enabled'),autoEnabled?'1':'0');armAutoAdvance();renderAutoState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDialog(title,html){
|
||||||
|
const dialog=document.getElementById('ppt-dialog');if(!dialog)return;
|
||||||
|
document.getElementById('ppt-dialog-title').textContent=title;document.getElementById('ppt-dialog-body').innerHTML=html;dialog.hidden=false;renderAutoState();
|
||||||
|
}
|
||||||
|
function closeDialog(){const dialog=document.getElementById('ppt-dialog');if(dialog)dialog.hidden=true;renderAutoState();}
|
||||||
|
function applyLayout(value,persist=true){
|
||||||
|
layoutMode=['balanced','slides','notes'].includes(value)?value:'balanced';
|
||||||
|
document.getElementById('ppt-presenter')?.setAttribute('data-layout',layoutMode);
|
||||||
|
if(persist)localStorage.setItem(storageKey('layout'),layoutMode);requestAnimationFrame(fitPresenterFrames);
|
||||||
|
}
|
||||||
|
function applyAudienceScreen(value){
|
||||||
|
const cover=document.getElementById('ppt-audience-cover');if(!cover)return;
|
||||||
|
if(value==='black'||value==='white'){cover.style.display='block';cover.style.background=value;}else cover.style.display='none';
|
||||||
|
}
|
||||||
|
function setScreenMode(value){
|
||||||
|
const next=['normal','black','white','frozen'].includes(value)?value:'normal',wasFrozen=audienceFrozen;
|
||||||
|
screenMode=next;audienceFrozen=next==='frozen';emit({type:'screen',mode:next});
|
||||||
|
if(wasFrozen&&!audienceFrozen)sendGoto(true);updateSyncStatus();armAutoAdvance();renderAutoState();
|
||||||
|
}
|
||||||
|
function openSettings(){
|
||||||
|
openDialog('演示设置',`<div class="ppt-settings-card"><label class="ppt-switch-row" for="ppt-auto-enabled-setting"><span><strong>自动翻页</strong><br><span class="ppt-muted">需要时手动开启,默认关闭</span></span><input id="ppt-auto-enabled-setting" type="checkbox" ${autoEnabled?'checked':''}><span class="ppt-switch" aria-hidden="true"></span></label><div class="ppt-field"><label for="ppt-auto-seconds-setting">全局间隔</label><div class="ppt-stepper"><button id="ppt-auto-minus" type="button" aria-label="减少一秒">−</button><input id="ppt-auto-seconds-setting" type="number" min="1" step="1" value="${autoGlobalSeconds||''}" placeholder="—"><span class="ppt-stepper-unit">秒</span><button id="ppt-auto-plus" type="button" aria-label="增加一秒">+</button></div></div><button id="ppt-settings-save" class="ppt-ui-btn ppt-settings-save" type="button">保存自动翻页</button><p class="ppt-settings-copy">单页 autoAdvanceSeconds 优先于全局间隔;minutes 只是讲述计划。</p></div><div class="ppt-settings-card"><h3>演讲者布局</h3><p class="ppt-settings-copy">根据屏幕宽度调整页面与备注的比例</p><div class="ppt-segmented"><button class="ppt-ui-btn" data-layout-choice="balanced" type="button">均衡</button><button class="ppt-ui-btn" data-layout-choice="slides" type="button">页面优先</button><button class="ppt-ui-btn" data-layout-choice="notes" type="button">备注优先</button></div></div><div class="ppt-settings-card"><h3>观众屏</h3><p class="ppt-settings-copy">临时遮挡或冻结观众屏</p><div class="ppt-segmented screen"><button class="ppt-ui-btn" data-screen-choice="normal" type="button">正常</button><button class="ppt-ui-btn" data-screen-choice="black" type="button">黑屏 B</button><button class="ppt-ui-btn" data-screen-choice="white" type="button">白屏 W</button><button class="ppt-ui-btn" data-screen-choice="frozen" type="button">冻结 F</button></div></div><div class="ppt-dialog-section"><button id="ppt-open-shortcuts" class="ppt-ui-btn" type="button">查看快捷键</button></div>`);
|
||||||
|
document.querySelectorAll('[data-layout-choice]').forEach(button=>{button.classList.toggle('ppt-primary',button.dataset.layoutChoice===layoutMode);button.onclick=()=>{applyLayout(button.dataset.layoutChoice);openSettings();};});
|
||||||
|
document.querySelectorAll('[data-screen-choice]').forEach(button=>{button.classList.toggle('ppt-primary',button.dataset.screenChoice===screenMode);button.onclick=()=>{setScreenMode(button.dataset.screenChoice);openSettings();};});
|
||||||
|
const secondsInput=document.getElementById('ppt-auto-seconds-setting'),stepSeconds=delta=>{const current=Math.max(0,Number(secondsInput.value||0));secondsInput.value=String(Math.max(1,current+delta));};
|
||||||
|
document.getElementById('ppt-auto-minus').onclick=()=>stepSeconds(-1);document.getElementById('ppt-auto-plus').onclick=()=>stepSeconds(1);
|
||||||
|
document.getElementById('ppt-settings-save').onclick=()=>{autoEnabled=document.getElementById('ppt-auto-enabled-setting').checked;autoGlobalSeconds=Math.max(0,Number(secondsInput.value||0));localStorage.setItem(storageKey('auto-enabled'),autoEnabled?'1':'0');localStorage.setItem(storageKey('auto-seconds'),String(autoGlobalSeconds));armAutoAdvance();closeDialog();};
|
||||||
|
document.getElementById('ppt-open-shortcuts').onclick=openShortcuts;
|
||||||
|
}
|
||||||
|
function openShortcuts(){
|
||||||
|
const items=[['← / →','上一页 / 下一页'],['Home / End','首页 / 尾页'],['Esc','宫格选页;标注工具激活时先退出工具'],['L','激光笔'],['C','圈选'],['X','清除标记'],['B / W','观众屏黑屏 / 白屏'],['F','冻结 / 恢复观众屏'],['A','开启 / 关闭自动翻页'],['R','开始 / 结束排练'],['?','快捷键说明']];
|
||||||
|
openDialog('快捷键',`<div class="ppt-shortcuts">${items.map(([key,label])=>`<kbd>${key}</kbd><span>${label}</span>`).join('')}</div>`);
|
||||||
|
}
|
||||||
|
function preflightChecks(){
|
||||||
|
const images=[...document.images],videos=[...document.querySelectorAll('video')],frame=document.getElementById('ppt-current-frame'),rect=frame?.getBoundingClientRect();
|
||||||
|
return[
|
||||||
|
{label:'观众屏连接',ok:['synced','frozen'].includes(syncState()),detail:`当前状态:${document.getElementById('ppt-sync')?.textContent||'未连接'}`},
|
||||||
|
{label:'弹窗权限',ok:!popupBlocked,detail:popupBlocked?'浏览器阻止了观众窗口':'未检测到弹窗拦截'},
|
||||||
|
{label:'全屏状态',ok:Boolean(document.fullscreenElement),warn:true,detail:document.fullscreenElement?'已进入全屏':'尚未全屏,可在演讲前进入'},
|
||||||
|
{label:'字体加载',ok:!document.fonts||document.fonts.status==='loaded',detail:document.fonts?`状态:${document.fonts.status}`:'浏览器不提供字体状态'},
|
||||||
|
{label:'图片资源',ok:images.every(image=>image.complete&&image.naturalWidth>0),detail:`${images.filter(image=>image.complete&&image.naturalWidth>0).length} / ${images.length} 已加载`},
|
||||||
|
{label:'视频资源',ok:videos.every(video=>video.readyState>=2),detail:videos.length?`${videos.filter(video=>video.readyState>=2).length} / ${videos.length} 可播放`:'没有视频'},
|
||||||
|
{label:'当前页 16:9',ok:Boolean(rect&&Math.abs(rect.width/rect.height-16/9)<.003),detail:rect?`${Math.round(rect.width)} × ${Math.round(rect.height)}`:'尚未渲染'}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
function openPreflight(){
|
||||||
|
const checks=preflightChecks(),html=checks.map(item=>`<div class="ppt-dialog-row"><div class="ppt-check ${item.ok?'is-ok':(item.warn?'is-warn':'')}"><span class="ppt-check-dot"></span><span><strong>${item.label}</strong><br><span class="ppt-muted">${item.detail}</span></span></div><strong>${item.ok?'正常':(item.warn?'建议':'异常')}</strong></div>`).join('');
|
||||||
|
openDialog('演前检查',`<div class="ppt-dialog-section">${html}</div><div class="ppt-dialog-section"><button id="ppt-preflight-refresh" class="ppt-ui-btn" type="button">重新检查</button> <button id="ppt-enter-fullscreen" class="ppt-ui-btn" type="button">进入全屏</button></div><p class="ppt-muted">这里只能检查浏览器和观众页面的软件状态,不能确认 HDMI、转接器或投影仪线缆是否物理连接。</p>`);
|
||||||
|
document.getElementById('ppt-preflight-refresh').onclick=openPreflight;
|
||||||
|
document.getElementById('ppt-enter-fullscreen').onclick=async()=>{try{await document.documentElement.requestFullscreen();}catch(e){}openPreflight();};
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTransport(){
|
||||||
|
if(transportReady)return;transportReady=true;
|
||||||
|
if('BroadcastChannel'in window){try{channel=new BroadcastChannel(channelName());channel.onmessage=e=>receive(e.data);}catch(e){}}
|
||||||
|
addEventListener('message',e=>receive(e.data));
|
||||||
|
addEventListener('storage',e=>{if(e.key===syncKey()&&e.newValue){try{receive(JSON.parse(e.newValue));}catch(err){}}});
|
||||||
|
}
|
||||||
|
function emit(payload,{persist=true}={}){
|
||||||
|
const message={__guizangPptSync:1,session,role:mode,...payload,at:Date.now(),nonce:Math.random().toString(36).slice(2)};
|
||||||
|
try{channel?.postMessage(message);}catch(e){}
|
||||||
|
if(persist)try{localStorage.setItem(syncKey(),JSON.stringify(message));}catch(e){}
|
||||||
|
try{if(mode==='presenter'&&audienceWindow&&!audienceWindow.closed)audienceWindow.postMessage(message,'*');}catch(e){}
|
||||||
|
try{if(mode==='audience'&&window.opener&&!window.opener.closed)window.opener.postMessage(message,'*');}catch(e){}
|
||||||
|
}
|
||||||
|
function receive(message){
|
||||||
|
if(!message||message.__guizangPptSync!==1||message.session!==session||message.role===mode)return;
|
||||||
|
if(message.nonce){if(seenNonceSet.has(message.nonce))return;seenNonceSet.add(message.nonce);seenNonces.push(message.nonce);if(seenNonces.length>256)seenNonceSet.delete(seenNonces.shift());}
|
||||||
|
if(mode==='presenter'){
|
||||||
|
if(message.type==='ready'){popupBlocked=false;sendGoto(true);}
|
||||||
|
if(message.type==='ack'){lastAckAt=Date.now();lastAckIndex=Number(message.index);lastAckSeq=Number(message.seq);updateSyncStatus();}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(mode!=='audience')return;
|
||||||
|
if(message.type==='screen'){applyAudienceScreen(message.mode);return;}
|
||||||
|
if(message.type==='laser'&&Number(message.index)===idx){audienceLaser=message.point||null;audienceLaserHideAt=Date.now()+650;renderInks();return;}
|
||||||
|
if(message.type==='circle'&&Number(message.index)===idx&&message.circle){audienceCircles.push(message.circle);renderInks();return;}
|
||||||
|
if(message.type==='clear'&&Number(message.index)===idx){audienceCircles=[];renderInks();return;}
|
||||||
|
if(message.type==='goto'||message.type==='ping'){
|
||||||
|
if(message.type==='goto'){audienceCircles=Array.isArray(message.circles)?message.circles:[];applyAudienceScreen(message.screenMode);go(Number(message.index),{force:true});}
|
||||||
|
emit({type:'ack',index:idx,seq:Number(message.seq)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function sendGoto(force=false){
|
||||||
|
if(mode!=='presenter'||audienceFrozen)return;if(force||lastAckIndex!==idx)seq++;
|
||||||
|
emit({type:'goto',index:idx,seq,screenMode,circles});updateSyncStatus();
|
||||||
|
}
|
||||||
|
function syncState(){
|
||||||
|
if(popupBlocked)return'blocked';if(audienceWindow&&audienceWindow.closed)return'disconnected';if(audienceFrozen)return'frozen';
|
||||||
|
if(!lastAckAt)return audienceOpenedAt&&Date.now()-audienceOpenedAt<2600?'connecting':(audienceOpenedAt?'unsynced':'disconnected');
|
||||||
|
if(Date.now()-lastAckAt>2600||lastAckIndex!==idx||lastAckSeq<seq)return'unsynced';return'synced';
|
||||||
|
}
|
||||||
|
function updateSyncStatus(){
|
||||||
|
const el=document.getElementById('ppt-sync');if(!el)return;const state=syncState();
|
||||||
|
const labels={blocked:'弹窗被拦截',disconnected:'未连接',connecting:'连接中',unsynced:'未同步',frozen:'已冻结',synced:`已同步 · 第 ${idx+1} 页`};
|
||||||
|
el.dataset.state=state;el.textContent=`观众屏 · ${labels[state]}`;
|
||||||
|
}
|
||||||
|
function reopenAudience(){
|
||||||
|
initTransport();popupBlocked=false;audienceOpenedAt=Date.now();lastAckAt=0;lastAckIndex=-1;lastAckSeq=-1;
|
||||||
|
if(audienceFrozen){audienceFrozen=false;screenMode='normal';}
|
||||||
|
audienceWindow=window.open(frameUrl('audience',idx),`guizang-ppt-audience-${session}`,'popup=yes,width=1440,height=900');
|
||||||
|
if(!audienceWindow)popupBlocked=true;updateSyncStatus();
|
||||||
|
}
|
||||||
|
function enterPresenter(openAudience=true){
|
||||||
|
if(mode==='presenter')return;if(overviewOn)toggleOverview();lowPowerBeforePresenter=window.__lowPowerMode;mode='presenter';initTransport();mountShell();
|
||||||
|
document.body.classList.remove('ppt-audience','ppt-preview');document.body.classList.add('ppt-presenter');document.title=`演讲者视图 · ${originalTitle}`;
|
||||||
|
window.__setLowPowerMode?.(true,{persist:false});updateShell();armAutoAdvance();if(openAudience)reopenAudience();
|
||||||
|
}
|
||||||
|
function exitPresenter(){
|
||||||
|
if(rehearsalActive)finishRehearsal(false);else if(timerRunning){commitRunningTime();timerRunning=false;}togglePresenterOverview(false);emit({type:'bye'});mode='deck';document.body.classList.remove('ppt-presenter');document.title=originalTitle;window.__setLowPowerMode?.(lowPowerBeforePresenter,{persist:false});
|
||||||
|
}
|
||||||
|
function initAudience(){
|
||||||
|
mode='audience';document.body.classList.add('ppt-audience');document.title=`观众屏 · ${originalTitle}`;
|
||||||
|
document.body.insertAdjacentHTML('beforeend','<canvas id="ppt-audience-ink" aria-hidden="true"></canvas><div id="ppt-audience-cover" aria-hidden="true"></div>');
|
||||||
|
const resize=()=>{sizeInk(document.getElementById('ppt-audience-ink'),innerWidth,innerHeight);renderInks();};addEventListener('resize',resize);resize();
|
||||||
|
initTransport();emit({type:'ready',index:idx});
|
||||||
|
}
|
||||||
|
function initPreview(){mode='preview';document.body.classList.add('ppt-preview');window.__setLowPowerMode?.(true,{persist:false});}
|
||||||
|
function onSlideChange(nextIndex){
|
||||||
|
if(mode!=='presenter')return;rollSlideTimer(nextIndex);circles=[];draftCircle=null;laserPoint=null;annotationPointerDown=false;armAutoAdvance();updateShell();sendGoto();
|
||||||
|
}
|
||||||
|
function handleKey(e){
|
||||||
|
const editable=e.target?.matches?.('input,textarea,[contenteditable="true"]');
|
||||||
|
if(editable){if(e.key==='Escape')e.target.blur();return true;}
|
||||||
|
if(mode==='audience'||mode==='preview'){e.preventDefault();return true;}
|
||||||
|
if(mode==='deck'&&e.key?.toLowerCase()==='p'&&!e.metaKey&&!e.ctrlKey&&!e.altKey){e.preventDefault();enterPresenter(true);return true;}
|
||||||
|
if(mode!=='presenter')return false;
|
||||||
|
const key=e.key?.toLowerCase();
|
||||||
|
if(e.key==='?'||key==='/'){e.preventDefault();openShortcuts();return true;}
|
||||||
|
const dialog=document.getElementById('ppt-dialog');
|
||||||
|
if(!dialog?.hidden){if(e.key==='Escape'){e.preventDefault();closeDialog();}return true;}
|
||||||
|
if(e.key==='Escape'){e.preventDefault();if(annotationTool!=='none')setAnnotationTool('none');else togglePresenterOverview();return true;}
|
||||||
|
if(presenterOverviewOn)return true;
|
||||||
|
if(key==='l'){e.preventDefault();setAnnotationTool('laser');return true;}
|
||||||
|
if(key==='c'){e.preventDefault();setAnnotationTool('circle');return true;}
|
||||||
|
if(key==='x'){e.preventDefault();clearAnnotations(true);return true;}
|
||||||
|
if(key==='b'){e.preventDefault();setScreenMode(screenMode==='black'?'normal':'black');return true;}
|
||||||
|
if(key==='w'){e.preventDefault();setScreenMode(screenMode==='white'?'normal':'white');return true;}
|
||||||
|
if(key==='f'){e.preventDefault();setScreenMode(audienceFrozen?'normal':'frozen');return true;}
|
||||||
|
if(key==='a'){e.preventDefault();toggleAutoAdvance();return true;}
|
||||||
|
if(key==='r'){e.preventDefault();toggleRehearsal();return true;}
|
||||||
|
if(['ArrowRight','PageDown',' ','ArrowDown'].includes(e.key)){e.preventDefault();go(idx+1,{force:true});return true;}
|
||||||
|
if(['ArrowLeft','PageUp','ArrowUp'].includes(e.key)){e.preventDefault();go(idx-1,{force:true});return true;}
|
||||||
|
if(e.key==='Home'){e.preventDefault();go(0,{force:true});return true;}
|
||||||
|
if(e.key==='End'){e.preventDefault();go(total-1,{force:true});return true;}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function initMode(){
|
||||||
|
document.getElementById('ppt-low-power-btn')?.addEventListener('click',()=>window.__setLowPowerMode(!window.__lowPowerMode));
|
||||||
|
document.getElementById('ppt-overview-btn')?.addEventListener('click',toggleOverview);
|
||||||
|
document.getElementById('ppt-presenter-btn')?.addEventListener('click',()=>enterPresenter(true));
|
||||||
|
if(requestedMode==='audience')initAudience();else if(requestedMode==='preview')initPreview();else if(requestedMode==='presenter')enterPresenter(false);
|
||||||
|
}
|
||||||
|
setInterval(()=>{
|
||||||
|
renderTimer();renderInks();updateAutoAdvance();
|
||||||
|
if(mode==='presenter'&&Date.now()-lastPingAt>900){lastPingAt=Date.now();if(audienceWindow?.closed)updateSyncStatus();emit({type:'ping',index:idx,seq});updateSyncStatus();}
|
||||||
|
},250);
|
||||||
|
return{initMode,onSlideChange,handleKey,slideLabel,enterPresenter,reopenAudience,toggleOverview:togglePresenterOverview,debugState:()=>({mode,idx,session,syncState:syncState(),timerRunning,rehearsalActive,presenterOverviewOn,autoEnabled,autoGlobalSeconds,autoRemainingMs,screenMode,annotationTool,circles:[...circles],audienceCircles:[...audienceCircles],layoutMode})};
|
||||||
|
})();
|
||||||
|
|
||||||
addEventListener('keydown',e=>{
|
addEventListener('keydown',e=>{
|
||||||
|
if(window.__pptPresenter?.handleKey(e))return;
|
||||||
if(e.key==='Escape'){e.preventDefault();toggleOverview();return;}
|
if(e.key==='Escape'){e.preventDefault();toggleOverview();return;}
|
||||||
if(e.key && e.key.toLowerCase()==='b' && !e.metaKey && !e.ctrlKey && !e.altKey){
|
if(e.key && e.key.toLowerCase()==='b' && !e.metaKey && !e.ctrlKey && !e.altKey){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -1544,6 +2085,7 @@ addEventListener('touchend',e=>{
|
|||||||
const initialSlideParam = new URLSearchParams(location.search).get('slide');
|
const initialSlideParam = new URLSearchParams(location.search).get('slide');
|
||||||
const initialSlide = initialSlideParam ? Number(initialSlideParam) - 1 : 0;
|
const initialSlide = initialSlideParam ? Number(initialSlideParam) - 1 : 0;
|
||||||
go(Number.isFinite(initialSlide) ? initialSlide : 0);
|
go(Number.isFinite(initialSlide) ? initialSlide : 0);
|
||||||
|
window.__pptPresenter?.initMode();
|
||||||
</script>
|
</script>
|
||||||
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
||||||
<script>lucide.createIcons();</script>
|
<script>lucide.createIcons();</script>
|
||||||
|
|||||||
+546
-8
@@ -195,10 +195,56 @@
|
|||||||
body.light-bg #nav{background:rgba(255,255,255,.25)}
|
body.light-bg #nav{background:rgba(255,255,255,.25)}
|
||||||
body.light-bg #nav .dot{background:rgba(var(--ink-rgb),.25)}
|
body.light-bg #nav .dot{background:rgba(var(--ink-rgb),.25)}
|
||||||
body.light-bg #nav .dot.active{background:rgba(var(--ink-rgb),.9)}
|
body.light-bg #nav .dot.active{background:rgba(var(--ink-rgb),.9)}
|
||||||
#hint{position:fixed;bottom:3vh;right:3vw;z-index:30;font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase;opacity:.4;mix-blend-mode:difference;color:#aaa}
|
#hint{position:fixed;bottom:3vh;right:3vw;z-index:30;display:flex;align-items:center;gap:.65em;font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase;opacity:.4;mix-blend-mode:difference;color:#aaa}
|
||||||
|
#hint button{all:unset;display:inline-flex;align-items:center;gap:.45em;cursor:pointer;color:inherit;white-space:nowrap}
|
||||||
|
#hint button:hover,#hint button:focus-visible{opacity:1;text-decoration:underline;text-underline-offset:.35em}
|
||||||
|
#hint .ppt-key{padding:.16em .38em;border:1px solid currentColor;border-radius:3px;letter-spacing:.08em}
|
||||||
|
#hint .ppt-sep{opacity:.55}
|
||||||
body.low-power #hint{opacity:.72;color:var(--paper);mix-blend-mode:normal}
|
body.low-power #hint{opacity:.72;color:var(--paper);mix-blend-mode:normal}
|
||||||
body.light-bg.low-power #hint{color:var(--ink)}
|
body.light-bg.low-power #hint{color:var(--ink)}
|
||||||
|
|
||||||
|
/* ============ 演讲者模式 ============ */
|
||||||
|
body.ppt-audience #hint,body.ppt-audience #nav,
|
||||||
|
body.ppt-preview #hint,body.ppt-preview #nav{display:none!important}
|
||||||
|
body.ppt-audience{cursor:none}
|
||||||
|
body.ppt-preview{pointer-events:none}
|
||||||
|
body.ppt-presenter>#deck,body.ppt-presenter>#nav,body.ppt-presenter>#hint,
|
||||||
|
body.ppt-presenter>.bg{visibility:hidden}
|
||||||
|
#ppt-presenter{position:fixed;inset:0;z-index:90;display:none;min-width:0;min-height:0;overflow:hidden;background:#101216;color:#f5f6f7;font-family:Inter,"Noto Sans SC",sans-serif}
|
||||||
|
body.ppt-presenter #ppt-presenter{display:grid;grid-template-rows:auto 1fr auto}
|
||||||
|
.ppt-presenter-top{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:18px 24px;border-bottom:1px solid #2b2e35;background:#15181d}
|
||||||
|
.ppt-presenter-brand{font-weight:650;letter-spacing:.02em}.ppt-presenter-meta{display:flex;align-items:center;gap:12px;color:#aeb4be;font-size:14px}
|
||||||
|
.ppt-sync{display:inline-flex;align-items:center;gap:8px;padding:7px 11px;border:1px solid #363a43;border-radius:999px;color:#d2d6dc;background:#1b1e24}
|
||||||
|
.ppt-sync::before{content:"";width:8px;height:8px;border-radius:50%;background:#818894}
|
||||||
|
.ppt-sync[data-state="synced"]::before{background:#72df99;box-shadow:0 0 0 4px rgba(114,223,153,.12)}
|
||||||
|
.ppt-sync[data-state="connecting"]::before,.ppt-sync[data-state="frozen"]::before{background:#f0c766}.ppt-sync[data-state="unsynced"]::before,.ppt-sync[data-state="blocked"]::before{background:#ff9e64}.ppt-sync[data-state="disconnected"]::before{background:#ff6b72}
|
||||||
|
.ppt-ui-btn{appearance:none;border:1px solid #3a3e48;background:#20242a;color:#f6f7f8;border-radius:8px;padding:9px 13px;font:600 14px/1 Inter,"Noto Sans SC",sans-serif;cursor:pointer}
|
||||||
|
.ppt-ui-btn:hover{background:#2a2f37;border-color:#525864}.ppt-ui-btn:focus-visible{outline:2px solid #84a8ff;outline-offset:2px}.ppt-ui-btn:disabled{opacity:.28;cursor:not-allowed}
|
||||||
|
.ppt-presenter-main{display:grid;grid-template-columns:minmax(0,1.75fr) minmax(300px,.75fr);gap:18px;min-height:0;padding:18px 24px}
|
||||||
|
.ppt-preview-stack{position:relative;display:grid;grid-template-rows:minmax(0,1fr) clamp(132px,22vh,210px);gap:16px;min-width:0;min-height:0}
|
||||||
|
.ppt-stage-card,.ppt-notes-card,.ppt-presenter-overview{background:#171a20;border:1px solid #2b2f37;border-radius:12px;overflow:hidden}
|
||||||
|
.ppt-stage-card{display:grid;grid-template-rows:auto minmax(0,1fr);min-width:0;min-height:0}
|
||||||
|
.ppt-stage-label{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:11px 14px;color:#aeb4be;font-size:13px;border-bottom:1px solid #292d34}
|
||||||
|
.ppt-annotation-tools{display:flex;align-items:center;gap:5px;margin-left:auto}.ppt-annotation-tools .ppt-ui-btn{padding:6px 9px;font-size:12px}.ppt-annotation-tools .ppt-ui-btn.is-active{color:#111318;background:#ff5b63;border-color:#ff5b63}.ppt-annotation-tools .ppt-grid-btn.is-active{color:#111318;background:#edf1f5;border-color:#edf1f5}.ppt-annotation-tools .ppt-clear-btn{min-width:0}.ppt-grid-btn{display:inline-flex;align-items:center;gap:6px}.ppt-grid-icon{display:grid;grid-template-columns:repeat(2,4px);gap:2px}.ppt-grid-icon i{width:4px;height:4px;background:currentColor}
|
||||||
|
.ppt-frame-viewport{position:relative;display:grid;place-items:center;min-width:0;min-height:0;overflow:hidden;background:#08090b}
|
||||||
|
.ppt-frame{display:block;width:0;height:0;max-width:100%;max-height:100%;aspect-ratio:16/9;border:0;background:#08090b}.ppt-next-frame{opacity:.86}
|
||||||
|
.ppt-annotation-layer{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:4;width:0;height:0;pointer-events:none;touch-action:none}.ppt-annotation-layer.is-active{pointer-events:auto;cursor:crosshair}
|
||||||
|
#ppt-audience-ink{position:fixed;inset:0;z-index:70;width:100vw;height:100vh;pointer-events:none}#ppt-audience-cover{position:fixed;inset:0;z-index:80;display:none;pointer-events:none}
|
||||||
|
.ppt-preview-stack.is-overview>.ppt-stage-card{display:none}.ppt-presenter-overview{grid-row:1/-1;display:grid;grid-template-rows:auto minmax(0,1fr);min-width:0;min-height:0}.ppt-presenter-overview[hidden]{display:none}.ppt-overview-head{display:flex;align-items:center;justify-content:space-between;padding:13px 15px;border-bottom:1px solid #292d34}.ppt-overview-head strong{font-size:15px}.ppt-overview-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;align-content:start;overflow:auto;padding:14px}.ppt-overview-page{appearance:none;min-height:112px;display:grid;grid-template-rows:auto 1fr auto;gap:12px;padding:13px;border:1px solid #343943;border-radius:10px;background:#111318;color:#eef1f4;text-align:left;cursor:pointer}.ppt-overview-page:hover{background:#1d2128;border-color:#636b78}.ppt-overview-page.is-current{border-color:#84a8ff;box-shadow:inset 0 0 0 1px #84a8ff}.ppt-overview-page-no{color:#8f97a3;font:600 12px/1 var(--mono,monospace)}.ppt-overview-page-title{font-size:14px;font-weight:650;line-height:1.35}.ppt-overview-page-meta{display:flex;justify-content:space-between;gap:8px;color:#7f8793;font-size:11px}
|
||||||
|
.ppt-notes-card{display:grid;grid-template-rows:auto minmax(0,1fr);min-height:0}.ppt-notes-head{padding:16px 18px;border-bottom:1px solid #292d34}.ppt-notes-label{margin-bottom:6px;color:#848c98;font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}.ppt-notes-title{font-size:21px;font-weight:680;line-height:1.32}.ppt-purpose-block{margin-top:14px;padding:11px 12px;border-left:3px solid #84a8ff;background:#111318}.ppt-purpose-copy{color:#c9ced6;font-size:14px;line-height:1.55}.ppt-notes-draft-head{display:flex;align-items:flex-end;justify-content:space-between;gap:10px;margin-top:15px;padding-top:13px;border-top:1px solid #2d3139}.ppt-notes-draft-head .ppt-notes-label{margin:0 0 4px}.ppt-note-meta{color:#8f97a3;font-size:12px}
|
||||||
|
.ppt-notes-tools{display:flex;align-items:center;gap:6px}.ppt-notes-tools .ppt-ui-btn{padding:7px 9px}.ppt-note-editor{width:100%;height:100%;min-height:0;resize:none;border:0;outline:0;background:#171a20;color:#edf0f3;padding:18px;font:400 var(--ppt-note-size,17px)/1.72 Inter,"Noto Sans SC",sans-serif}.ppt-note-editor:focus{box-shadow:inset 0 0 0 1px #3d4654}
|
||||||
|
.ppt-saved{color:#72df99;white-space:nowrap}.ppt-presenter-bottom{display:grid;grid-template-columns:minmax(330px,1fr) auto minmax(110px,1fr);align-items:center;gap:20px;padding:12px 24px;border-top:1px solid #2b2e35;background:#15181d}
|
||||||
|
.ppt-time-dashboard{display:flex;align-items:stretch;min-width:0}.ppt-time-stat{min-width:90px;display:grid;align-content:center;gap:4px;padding:0 16px}.ppt-time-stat:first-child{padding-left:0}.ppt-time-stat+.ppt-time-stat{border-left:1px solid #343943}.ppt-time-label{color:#89919d;font-size:11px;font-weight:700;letter-spacing:.1em}.ppt-time-value{color:#f7f8fa;font:650 23px/1 var(--mono,monospace);font-variant-numeric:tabular-nums}.ppt-time-hint{color:#747c88;font-size:11px;white-space:nowrap}.ppt-time-stat.is-over .ppt-time-label,.ppt-time-stat.is-over .ppt-time-value{color:#ff8a90}.ppt-time-stat.is-remaining .ppt-time-value{color:#72df99}
|
||||||
|
.ppt-control-stack{display:grid;gap:7px;justify-items:center}.ppt-controls,.ppt-session-actions{display:flex;align-items:center;justify-content:center;gap:7px}.ppt-controls .ppt-ui-btn,.ppt-session-actions .ppt-ui-btn{min-width:68px}.ppt-session-actions{padding-top:7px;border-top:1px solid #343943}.ppt-rehearsal-on{color:#ff8a90}.ppt-page-state{text-align:right}.ppt-page-state strong{display:block;color:#fff;font-size:17px;margin-bottom:5px}.ppt-page-state span{color:#8f97a3;font:600 13px/1 var(--mono,monospace)}.ppt-auto-state{display:inline-flex;align-items:center;gap:6px;border-radius:999px}.ppt-auto-state.is-on{color:#72df99;border-color:rgba(114,223,153,.45);background:rgba(114,223,153,.08)}.ppt-auto-state.is-paused{color:#f0c766;border-color:rgba(240,199,102,.42);background:rgba(240,199,102,.08)}
|
||||||
|
#ppt-presenter[data-layout="slides"] .ppt-presenter-main{grid-template-columns:minmax(0,2.15fr) minmax(260px,.58fr)}#ppt-presenter[data-layout="notes"] .ppt-presenter-main{grid-template-columns:minmax(0,1.15fr) minmax(380px,1fr)}
|
||||||
|
.ppt-dialog{position:fixed;inset:0;z-index:130;display:grid;place-items:center;padding:24px;background:rgba(4,6,9,.72);backdrop-filter:blur(10px)}.ppt-dialog[hidden]{display:none}.ppt-dialog-panel{width:min(720px,calc(100vw - 48px));max-height:min(760px,calc(100vh - 48px));display:grid;grid-template-rows:auto minmax(0,1fr);overflow:hidden;border:1px solid #343943;border-radius:14px;background:#171a20;box-shadow:0 24px 80px rgba(0,0,0,.45)}.ppt-dialog-head{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px 18px;border-bottom:1px solid #2b2f37}.ppt-dialog-title{font-size:20px;font-weight:700}.ppt-dialog-body{overflow:auto;padding:18px;color:#d8dce2;font-size:14px;line-height:1.6}.ppt-dialog-section+.ppt-dialog-section{margin-top:22px}.ppt-dialog-section h3{margin:0 0 10px;color:#fff;font-size:15px}.ppt-dialog-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.ppt-dialog-row{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:10px 0;border-bottom:1px solid #282c33}.ppt-dialog-row:last-child{border-bottom:0}.ppt-field{display:grid;gap:6px}.ppt-field label{color:#aeb4be;font-size:12px}.ppt-field input{width:100%;border:1px solid #3a3e48;border-radius:8px;background:#101216;color:#fff;padding:9px 10px;font:600 14px Inter,sans-serif}.ppt-check{display:flex;align-items:flex-start;gap:10px}.ppt-check-dot{width:9px;height:9px;margin-top:7px;border-radius:50%;background:#ff6b72}.ppt-check.is-ok .ppt-check-dot{background:#72df99}.ppt-check.is-warn .ppt-check-dot{background:#f0c766}.ppt-summary{width:100%;border-collapse:collapse;font-size:13px}.ppt-summary th,.ppt-summary td{padding:9px 8px;border-bottom:1px solid #2b2f37;text-align:left}.ppt-summary th{color:#9fa6b1;font-weight:600}.ppt-summary td:last-child,.ppt-summary th:last-child{text-align:right}.ppt-muted{color:#8f96a1}.ppt-shortcuts{display:grid;grid-template-columns:auto 1fr;gap:8px 14px}.ppt-shortcuts kbd{min-width:44px;padding:4px 7px;border:1px solid #3a3e48;border-radius:6px;background:#101216;color:#fff;text-align:center;font:600 12px var(--mono,monospace)}
|
||||||
|
.ppt-settings-card{padding:15px;border:1px solid #30353e;border-radius:12px;background:#12151a}.ppt-settings-card+.ppt-settings-card{margin-top:12px}.ppt-settings-card h3{margin:0 0 3px}.ppt-settings-copy{margin:0 0 14px;color:#858d99;font-size:12px}.ppt-switch-row{display:flex;align-items:center;justify-content:space-between;gap:18px;cursor:pointer}.ppt-switch-row input{position:absolute;opacity:0;pointer-events:none}.ppt-switch{width:48px;height:28px;flex:0 0 auto;padding:3px;border:1px solid #434955;border-radius:999px;background:#252a31;transition:.2s}.ppt-switch::after{content:"";display:block;width:20px;height:20px;border-radius:50%;background:#a8afb9;transition:.2s}.ppt-switch-row input:checked+.ppt-switch{border-color:#72df99;background:rgba(114,223,153,.16)}.ppt-switch-row input:checked+.ppt-switch::after{transform:translateX(20px);background:#72df99}.ppt-stepper{display:grid;grid-template-columns:38px minmax(72px,1fr) auto 38px;align-items:center;margin-top:14px;border:1px solid #363c46;border-radius:10px;background:#0d0f13;overflow:hidden}.ppt-stepper button{height:42px;border:0;background:#20242a;color:#fff;font-size:19px;cursor:pointer}.ppt-stepper button:hover{background:#2c323b}.ppt-stepper input{width:100%;height:42px;border:0!important;border-radius:0!important;background:transparent!important;text-align:center;font:650 16px var(--mono,monospace)!important;-moz-appearance:textfield}.ppt-stepper input::-webkit-inner-spin-button,.ppt-stepper input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.ppt-stepper-unit{color:#858d99;font-size:12px}.ppt-segmented{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}.ppt-segmented.screen{grid-template-columns:repeat(4,1fr)}.ppt-segmented .ppt-ui-btn{min-width:0}.ppt-settings-save{width:100%;margin-top:14px;background:#edf1f5;color:#111318;border-color:#edf1f5}.ppt-settings-save:hover{background:#fff}.ppt-settings-save+.ppt-settings-copy{margin:10px 0 0}
|
||||||
|
#overview{z-index:110!important}
|
||||||
|
@media(max-width:1200px){.ppt-presenter-main{grid-template-columns:minmax(0,1.55fr) minmax(280px,.72fr)}}
|
||||||
|
@media(max-width:1100px){.ppt-presenter-bottom{grid-template-columns:minmax(292px,1fr) auto minmax(86px,.6fr);gap:12px}.ppt-time-stat{min-width:82px;padding:0 11px}.ppt-time-value{font-size:20px}.ppt-controls .ppt-ui-btn,.ppt-session-actions .ppt-ui-btn{min-width:60px;padding:8px 9px}}
|
||||||
|
@media(max-width:900px){.ppt-presenter-top{padding:12px 16px;flex-wrap:wrap}.ppt-presenter-top .ppt-presenter-meta{gap:6px}.ppt-presenter-top .ppt-ui-btn{padding:8px 9px}.ppt-presenter-main,#ppt-presenter[data-layout="slides"] .ppt-presenter-main,#ppt-presenter[data-layout="notes"] .ppt-presenter-main{grid-template-columns:minmax(0,1.45fr) minmax(250px,.72fr);gap:12px;padding:12px 16px}.ppt-preview-stack{gap:12px;grid-template-rows:minmax(0,1fr) clamp(104px,20vh,150px)}.ppt-saved{display:none}.ppt-presenter-bottom{grid-template-columns:minmax(270px,1fr) auto 74px;gap:8px;padding:9px 16px}.ppt-time-stat{min-width:0;padding:0 9px}.ppt-time-value{font-size:18px}.ppt-time-hint{max-width:78px;overflow:hidden;text-overflow:ellipsis}.ppt-controls,.ppt-session-actions{gap:5px}.ppt-controls .ppt-ui-btn,.ppt-session-actions .ppt-ui-btn{min-width:52px;padding:7px 8px;font-size:12px}.ppt-page-state strong{font-size:14px}.ppt-dialog-grid{grid-template-columns:1fr}.ppt-segmented.screen{grid-template-columns:repeat(2,1fr)}}
|
||||||
|
@media(max-height:700px){.ppt-presenter-top{padding-top:10px;padding-bottom:10px}.ppt-presenter-main{padding-top:10px;padding-bottom:10px}.ppt-preview-stack{grid-template-rows:minmax(0,1fr) clamp(88px,16vh,118px);gap:10px}.ppt-stage-label{padding:7px 10px}.ppt-notes-head{padding:12px 14px}.ppt-purpose-block{margin-top:10px;padding:8px 10px}.ppt-notes-draft-head{margin-top:10px;padding-top:9px}.ppt-presenter-bottom{padding-top:8px;padding-bottom:8px}.ppt-control-stack{gap:5px}.ppt-session-actions{padding-top:5px}}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
============ LAYOUTS API · 面向 agent 的类(v2)============
|
============ LAYOUTS API · 面向 agent 的类(v2)============
|
||||||
所有 layouts.md 中的骨架都基于下面这套命名。
|
所有 layouts.md 中的骨架都基于下面这套命名。
|
||||||
@@ -451,8 +497,8 @@
|
|||||||
const stored = localStorage.getItem(KEY);
|
const stored = localStorage.getItem(KEY);
|
||||||
window.__lowPowerMode = stored === '1' || (stored === null && reduced);
|
window.__lowPowerMode = stored === '1' || (stored === null && reduced);
|
||||||
function updateHint(){
|
function updateHint(){
|
||||||
const hint = document.getElementById('hint');
|
const label = document.querySelector('.ppt-low-power-label');
|
||||||
if(hint) hint.textContent = `← → 翻页 · B ${window.__lowPowerMode ? '动态' : '静态'} · ESC 索引`;
|
if(label) label.textContent = window.__lowPowerMode ? '动态' : '静态';
|
||||||
}
|
}
|
||||||
window.__setLowPowerMode = function(on, opts={}){
|
window.__setLowPowerMode = function(on, opts={}){
|
||||||
window.__lowPowerMode = !!on;
|
window.__lowPowerMode = !!on;
|
||||||
@@ -472,7 +518,12 @@
|
|||||||
|
|
||||||
<canvas id="bg-dark" class="bg"></canvas>
|
<canvas id="bg-dark" class="bg"></canvas>
|
||||||
<canvas id="bg-light" class="bg"></canvas>
|
<canvas id="bg-light" class="bg"></canvas>
|
||||||
<div id="hint">← → 翻页 · B 静态 · ESC 索引</div>
|
<div id="hint" role="toolbar" aria-label="快捷控制">
|
||||||
|
<span>← → 翻页</span><span class="ppt-sep">·</span>
|
||||||
|
<button id="ppt-low-power-btn" type="button"><span class="ppt-key">B</span><span class="ppt-low-power-label">静态</span></button><span class="ppt-sep">·</span>
|
||||||
|
<button id="ppt-overview-btn" type="button"><span class="ppt-key">ESC</span>总览</button><span class="ppt-sep">·</span>
|
||||||
|
<button id="ppt-presenter-btn" type="button"><span class="ppt-key">P</span>演讲模式</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="deck">
|
<div id="deck">
|
||||||
|
|
||||||
@@ -488,6 +539,21 @@
|
|||||||
|
|
||||||
<div id="nav"></div>
|
<div id="nav"></div>
|
||||||
|
|
||||||
|
<!-- 演讲备注:每页一条,id 必须与 section[data-slide-id] 一致。见 references/presenter-mode.md -->
|
||||||
|
<script>
|
||||||
|
const SPEAKER_NOTES = [
|
||||||
|
/* {
|
||||||
|
id: 'stable-slide-id', title: '页面标题', section: '主体', minutes: 1,
|
||||||
|
purpose: '这一页要让观众理解什么',
|
||||||
|
talk: ['只在台上说的补充信息', '例子、证据或语气提示', '不复述页面可见文字'],
|
||||||
|
transition: '为什么下一页紧接着出现', cue: '可选:停顿 / Demo / 视频',
|
||||||
|
interaction: '可选:现场提问', delivery: '可选:语气 / 重音',
|
||||||
|
advance: '可选:翻页时机', fallback: '可选:Demo 失败时的备用说法'
|
||||||
|
} */
|
||||||
|
];
|
||||||
|
window.__SPEAKER_NOTES__ = SPEAKER_NOTES;
|
||||||
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* =============== WebGL 双背景 ===============
|
/* =============== WebGL 双背景 ===============
|
||||||
深色页:Holographic Dispersion(全息色散 · 钛金暗流)—— 彩虹微扰、鼠标径向涟漪
|
深色页:Holographic Dispersion(全息色散 · 钛金暗流)—— 彩虹微扰、鼠标径向涟漪
|
||||||
@@ -633,8 +699,8 @@ slides.forEach((s,i)=>{
|
|||||||
nav.appendChild(b);
|
nav.appendChild(b);
|
||||||
});
|
});
|
||||||
|
|
||||||
function go(n){
|
function go(n,opts={}){
|
||||||
if(lock)return;
|
if(lock&&!opts.force)return;
|
||||||
idx=Math.max(0,Math.min(total-1,n));
|
idx=Math.max(0,Math.min(total-1,n));
|
||||||
window.__currentSlideIndex = idx;
|
window.__currentSlideIndex = idx;
|
||||||
deck.style.transform=`translateX(${-idx*100}vw)`;
|
deck.style.transform=`translateX(${-idx*100}vw)`;
|
||||||
@@ -645,7 +711,8 @@ function go(n){
|
|||||||
document.body.classList.toggle('light-bg',th==='light');
|
document.body.classList.toggle('light-bg',th==='light');
|
||||||
/* 动效:翻页过渡中段触发当前页的入场动画(由 motion-boot 注册) */
|
/* 动效:翻页过渡中段触发当前页的入场动画(由 motion-boot 注册) */
|
||||||
if(window.__playSlide) setTimeout(()=>window.__playSlide(idx), 450);
|
if(window.__playSlide) setTimeout(()=>window.__playSlide(idx), 450);
|
||||||
lock=true;setTimeout(()=>lock=false,700);
|
window.__pptPresenter?.onSlideChange(idx);
|
||||||
|
if(!opts.force){lock=true;setTimeout(()=>lock=false,700);}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =============== ESC 索引视图 =============== */
|
/* =============== ESC 索引视图 =============== */
|
||||||
@@ -671,7 +738,7 @@ function buildOverview(){
|
|||||||
wrap.appendChild(clone);
|
wrap.appendChild(clone);
|
||||||
const label=document.createElement('div');
|
const label=document.createElement('div');
|
||||||
label.style.cssText='padding:6px 10px;font-family:var(--mono);font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--paper);opacity:.7';
|
label.style.cssText='padding:6px 10px;font-family:var(--mono);font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--paper);opacity:.7';
|
||||||
label.textContent=(i+1)+' / '+total;
|
label.textContent=window.__pptPresenter?.slideLabel(i) || ((i+1)+' / '+total);
|
||||||
card.appendChild(wrap);
|
card.appendChild(wrap);
|
||||||
card.appendChild(label);
|
card.appendChild(label);
|
||||||
card.onclick=()=>{toggleOverview();go(i)};
|
card.onclick=()=>{toggleOverview();go(i)};
|
||||||
@@ -686,7 +753,477 @@ function toggleOverview(){
|
|||||||
else{ov.style.display='none';}
|
else{ov.style.display='none';}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =============== 演讲者模式 / 观众屏同步 =============== */
|
||||||
|
window.__pptPresenter=(()=>{
|
||||||
|
const query=new URLSearchParams(location.search);
|
||||||
|
const requestedMode=query.get('mode')||'deck';
|
||||||
|
const originalTitle=document.title;
|
||||||
|
const notes=()=>Array.isArray(window.__SPEAKER_NOTES__)?window.__SPEAKER_NOTES__:[];
|
||||||
|
const slideId=i=>slides[i]?.dataset.slideId||`slide-${String(i+1).padStart(2,'0')}`;
|
||||||
|
const storageKey=name=>`guizang-ppt-${name}:${location.pathname}`;
|
||||||
|
let mode='deck',session=query.get('session')||Math.random().toString(36).slice(2),audienceWindow=null;
|
||||||
|
let channel=null,transportReady=false,popupBlocked=false,audienceOpenedAt=0,lastAckAt=0,lastAckIndex=-1,lastAckSeq=-1,seq=0,lastPingAt=0;
|
||||||
|
let renderedNoteId='',saveTimer=0,noteSize=Number(localStorage.getItem('guizang-ppt-note-size')||17),presenterFrameObserver=null;
|
||||||
|
let elapsed=0,timerRunning=false,timerStartedAt=0,timedSlideIndex=idx,slideTimerStartedAt=0,slideElapsed=Array(total).fill(0);
|
||||||
|
let rehearsalActive=false,presenterOverviewOn=false,lowPowerBeforePresenter=window.__lowPowerMode;
|
||||||
|
let layoutMode=localStorage.getItem(storageKey('layout'))||'balanced';
|
||||||
|
let autoEnabled=localStorage.getItem(storageKey('auto-enabled'))==='1';
|
||||||
|
let autoGlobalSeconds=Math.max(0,Number(localStorage.getItem(storageKey('auto-seconds'))||0));
|
||||||
|
let autoDeadline=0,autoRemainingMs=0;
|
||||||
|
let screenMode='normal',audienceFrozen=false;
|
||||||
|
let annotationTool='none',annotationPointerDown=false,annotationStart=null,draftCircle=null,circles=[],audienceCircles=[];
|
||||||
|
let laserPoint=null,audienceLaser=null,laserHideAt=0,audienceLaserHideAt=0,lastLaserSent=0;
|
||||||
|
const seenNonces=[],seenNonceSet=new Set();
|
||||||
|
const syncKey=()=>`guizang-ppt-sync:${location.pathname}:${session}`;
|
||||||
|
const channelName=()=>`guizang-ppt:${location.pathname}:${session}`;
|
||||||
|
|
||||||
|
function noteAt(i){
|
||||||
|
const id=slideId(i);
|
||||||
|
return notes().find(n=>n.id===id)||{id,title:slides[i]?.dataset.title||`第 ${i+1} 页`,purpose:'待补充本页目的',talk:['待补充讲述要点'],transition:'待补充转场'};
|
||||||
|
}
|
||||||
|
function slideLabel(i){return `${i+1} / ${total} · ${noteAt(i).title||slideId(i)}`;}
|
||||||
|
function escapeHTML(value){return String(value??'').replace(/[&<>"']/g,ch=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[ch]));}
|
||||||
|
function valueLines(value){
|
||||||
|
if(Array.isArray(value))return value.filter(Boolean).map(x=>`• ${x}`).join('\n');
|
||||||
|
return value?String(value):'';
|
||||||
|
}
|
||||||
|
function generatedNote(n){
|
||||||
|
const parts=[];
|
||||||
|
if(Array.isArray(n.talk)&&n.talk.length)parts.push(`【讲述要点】\n${valueLines(n.talk)}`);
|
||||||
|
if(n.transition)parts.push(`【转场】\n${n.transition}`);
|
||||||
|
const optional=[['舞台提示',n.cue],['互动',n.interaction],['表达提示',n.delivery],['翻页时机',n.advance],['备用方案',n.fallback],['读音提醒',n.pronunciation]];
|
||||||
|
optional.forEach(([label,value])=>{const text=valueLines(value);if(text)parts.push(`【${label}】\n${text}`);});
|
||||||
|
return parts.join('\n\n');
|
||||||
|
}
|
||||||
|
function noteStorageKey(id){return `guizang-ppt-note:${location.pathname}:${id}`;}
|
||||||
|
function loadNoteValue(id,i,n){
|
||||||
|
const saved=localStorage.getItem(noteStorageKey(id));
|
||||||
|
if(saved!==null)return saved;
|
||||||
|
const prefix=window.__PPT_LEGACY_NOTE_KEY__;
|
||||||
|
if(prefix){
|
||||||
|
const legacy=localStorage.getItem(`${prefix}:${i}`);
|
||||||
|
if(legacy!==null){localStorage.setItem(noteStorageKey(id),legacy);return legacy;}
|
||||||
|
}
|
||||||
|
return generatedNote(n);
|
||||||
|
}
|
||||||
|
function frameUrl(targetMode,i){
|
||||||
|
const url=new URL(location.href);url.hash='';
|
||||||
|
url.searchParams.set('mode',targetMode);url.searchParams.set('session',session);url.searchParams.set('slide',String(i+1));
|
||||||
|
return url.href;
|
||||||
|
}
|
||||||
|
function plannedMsAt(i){
|
||||||
|
const minutes=Number(noteAt(i).minutes);
|
||||||
|
return Number.isFinite(minutes)&&minutes>0?minutes*60000:null;
|
||||||
|
}
|
||||||
|
function totalPlannedMs(){
|
||||||
|
const values=Array.from({length:total},(_,i)=>plannedMsAt(i));
|
||||||
|
return values.length&&values.every(Number.isFinite)?values.reduce((sum,x)=>sum+x,0):null;
|
||||||
|
}
|
||||||
|
function sectionInfo(i){
|
||||||
|
const name=String(noteAt(i).section||'').trim();
|
||||||
|
if(!name)return null;
|
||||||
|
let first=i,last=i;
|
||||||
|
while(first>0&&String(noteAt(first-1).section||'').trim()===name)first--;
|
||||||
|
while(last<total-1&&String(noteAt(last+1).section||'').trim()===name)last++;
|
||||||
|
const targets=[];for(let x=first;x<=last;x++)targets.push(plannedMsAt(x));
|
||||||
|
const planned=targets.every(Number.isFinite)?targets.reduce((sum,x)=>sum+x,0):null;
|
||||||
|
const used=slideElapsed.slice(first,last+1).reduce((sum,x)=>sum+x,0)+(timerRunning&&timedSlideIndex>=first&&timedSlideIndex<=last?Date.now()-slideTimerStartedAt:0);
|
||||||
|
return{name,position:i-first+1,count:last-first+1,planned,used};
|
||||||
|
}
|
||||||
|
function durationText(ms){
|
||||||
|
const value=Math.max(0,Math.round(Number(ms)||0)),seconds=Math.floor(value/1000);
|
||||||
|
const hours=Math.floor(seconds/3600),minutes=Math.floor((seconds%3600)/60),secs=seconds%60;
|
||||||
|
return hours?`${String(hours).padStart(2,'0')}:${String(minutes).padStart(2,'0')}:${String(secs).padStart(2,'0')}`:`${String(minutes).padStart(2,'0')}:${String(secs).padStart(2,'0')}`;
|
||||||
|
}
|
||||||
|
function signedDuration(ms){return `${ms>=0?'+':'−'}${durationText(Math.abs(ms))}`;}
|
||||||
|
function clockTime(timestamp){return new Date(timestamp).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'});}
|
||||||
|
|
||||||
|
function fitFrame(frame){
|
||||||
|
const viewport=frame?.closest('.ppt-frame-viewport');
|
||||||
|
if(!viewport)return;
|
||||||
|
const availableWidth=viewport.clientWidth,availableHeight=viewport.clientHeight;
|
||||||
|
if(!availableWidth||!availableHeight)return;
|
||||||
|
const width=Math.min(availableWidth,availableHeight*(16/9)),height=width*(9/16);
|
||||||
|
frame.style.width=`${width}px`;frame.style.height=`${height}px`;
|
||||||
|
if(frame.id==='ppt-current-frame')sizeInk(document.getElementById('ppt-presenter-ink'),width,height);
|
||||||
|
}
|
||||||
|
function fitPresenterFrames(){
|
||||||
|
fitFrame(document.getElementById('ppt-current-frame'));
|
||||||
|
fitFrame(document.getElementById('ppt-next-frame'));
|
||||||
|
}
|
||||||
|
function sizeInk(canvas,width,height){
|
||||||
|
if(!canvas||!width||!height)return;
|
||||||
|
canvas.style.width=`${width}px`;canvas.style.height=`${height}px`;
|
||||||
|
const dpr=Math.min(devicePixelRatio||1,2),pixelWidth=Math.round(width*dpr),pixelHeight=Math.round(height*dpr);
|
||||||
|
if(canvas.width!==pixelWidth||canvas.height!==pixelHeight){canvas.width=pixelWidth;canvas.height=pixelHeight;}
|
||||||
|
}
|
||||||
|
function drawEllipse(ctx,circle,width,height,alpha=1){
|
||||||
|
const x1=circle.x1*width,y1=circle.y1*height,x2=circle.x2*width,y2=circle.y2*height;
|
||||||
|
ctx.save();ctx.globalAlpha=alpha;ctx.strokeStyle='#ff4f59';ctx.lineWidth=Math.max(3,width*.004);ctx.lineCap='round';
|
||||||
|
ctx.beginPath();ctx.ellipse((x1+x2)/2,(y1+y2)/2,Math.abs(x2-x1)/2,Math.abs(y2-y1)/2,0,0,Math.PI*2);ctx.stroke();ctx.restore();
|
||||||
|
}
|
||||||
|
function drawInk(canvas,list,laser,draft){
|
||||||
|
if(!canvas)return;const width=canvas.clientWidth,height=canvas.clientHeight;if(!width||!height)return;
|
||||||
|
sizeInk(canvas,width,height);const dpr=canvas.width/width,ctx=canvas.getContext('2d');ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,width,height);
|
||||||
|
(list||[]).forEach(circle=>drawEllipse(ctx,circle,width,height));
|
||||||
|
if(draft)drawEllipse(ctx,draft,width,height,.72);
|
||||||
|
if(laser){
|
||||||
|
const x=laser.x*width,y=laser.y*height,r=Math.max(6,width*.007);
|
||||||
|
const glow=ctx.createRadialGradient(x,y,0,x,y,r*2.8);glow.addColorStop(0,'rgba(255,255,255,.98)');glow.addColorStop(.2,'rgba(255,64,72,.98)');glow.addColorStop(1,'rgba(255,64,72,0)');
|
||||||
|
ctx.fillStyle=glow;ctx.beginPath();ctx.arc(x,y,r*2.8,0,Math.PI*2);ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function renderInks(){
|
||||||
|
const now=Date.now();if(now>laserHideAt)laserPoint=null;if(now>audienceLaserHideAt)audienceLaser=null;
|
||||||
|
drawInk(document.getElementById('ppt-presenter-ink'),circles,laserPoint,draftCircle);
|
||||||
|
drawInk(document.getElementById('ppt-audience-ink'),audienceCircles,audienceLaser,null);
|
||||||
|
}
|
||||||
|
function setAnnotationTool(tool){
|
||||||
|
annotationTool=annotationTool===tool?'none':tool;annotationPointerDown=false;draftCircle=null;
|
||||||
|
const canvas=document.getElementById('ppt-presenter-ink');canvas?.classList.toggle('is-active',annotationTool!=='none');
|
||||||
|
document.querySelectorAll('[data-ppt-tool]').forEach(button=>button.classList.toggle('is-active',button.dataset.pptTool===annotationTool));
|
||||||
|
renderInks();
|
||||||
|
}
|
||||||
|
function annotationPoint(event){
|
||||||
|
const canvas=event.currentTarget,rect=canvas.getBoundingClientRect();
|
||||||
|
return{x:Math.max(0,Math.min(1,(event.clientX-rect.left)/rect.width)),y:Math.max(0,Math.min(1,(event.clientY-rect.top)/rect.height))};
|
||||||
|
}
|
||||||
|
function onAnnotationPointerDown(event){
|
||||||
|
if(annotationTool!=='circle')return;event.preventDefault();annotationPointerDown=true;annotationStart=annotationPoint(event);draftCircle={x1:annotationStart.x,y1:annotationStart.y,x2:annotationStart.x,y2:annotationStart.y};event.currentTarget.setPointerCapture?.(event.pointerId);renderInks();
|
||||||
|
}
|
||||||
|
function onAnnotationPointerMove(event){
|
||||||
|
if(annotationTool==='laser'){
|
||||||
|
const point=annotationPoint(event);laserPoint=point;laserHideAt=Date.now()+650;
|
||||||
|
if(Date.now()-lastLaserSent>32){lastLaserSent=Date.now();emit({type:'laser',index:idx,point},{persist:false});}renderInks();return;
|
||||||
|
}
|
||||||
|
if(annotationTool==='circle'&&annotationPointerDown&&draftCircle){const point=annotationPoint(event);draftCircle.x2=point.x;draftCircle.y2=point.y;renderInks();}
|
||||||
|
}
|
||||||
|
function onAnnotationPointerUp(event){
|
||||||
|
if(annotationTool!=='circle'||!annotationPointerDown||!draftCircle)return;event.preventDefault();annotationPointerDown=false;
|
||||||
|
const circle={...draftCircle};draftCircle=null;
|
||||||
|
if(Math.abs(circle.x2-circle.x1)>.012&&Math.abs(circle.y2-circle.y1)>.012){circles.push(circle);emit({type:'circle',index:idx,circle});}
|
||||||
|
renderInks();
|
||||||
|
}
|
||||||
|
function onAnnotationPointerLeave(){
|
||||||
|
if(annotationTool==='laser'){laserPoint=null;emit({type:'laser',index:idx,point:null},{persist:false});renderInks();}
|
||||||
|
}
|
||||||
|
function clearAnnotations(broadcast=true){circles=[];draftCircle=null;laserPoint=null;if(broadcast)emit({type:'clear',index:idx});renderInks();}
|
||||||
|
|
||||||
|
function buildPresenterOverview(){
|
||||||
|
const grid=document.getElementById('ppt-presenter-overview-grid');if(!grid)return;grid.innerHTML='';
|
||||||
|
slides.forEach((slide,i)=>{
|
||||||
|
const n=noteAt(i),button=document.createElement('button');button.type='button';button.className=`ppt-overview-page${i===idx?' is-current':''}`;
|
||||||
|
button.innerHTML=`<span class="ppt-overview-page-no">${String(i+1).padStart(2,'0')} / ${String(total).padStart(2,'0')}</span><span class="ppt-overview-page-title">${escapeHTML(n.title||slideId(i))}</span><span class="ppt-overview-page-meta"><span>${escapeHTML(n.section||'未分组')}</span><span>${Math.round(((i+1)/total)*100)}%</span></span>`;
|
||||||
|
button.onclick=()=>{togglePresenterOverview(false);go(i,{force:true});};grid.appendChild(button);
|
||||||
|
});
|
||||||
|
requestAnimationFrame(()=>grid.querySelector('.is-current')?.scrollIntoView({block:'nearest',inline:'nearest'}));
|
||||||
|
}
|
||||||
|
function togglePresenterOverview(force){
|
||||||
|
presenterOverviewOn=typeof force==='boolean'?force:!presenterOverviewOn;
|
||||||
|
if(presenterOverviewOn&&annotationTool!=='none')setAnnotationTool('none');
|
||||||
|
const stack=document.querySelector('.ppt-preview-stack'),overview=document.getElementById('ppt-presenter-overview');
|
||||||
|
stack?.classList.toggle('is-overview',presenterOverviewOn);if(overview)overview.hidden=!presenterOverviewOn;
|
||||||
|
const button=document.getElementById('ppt-grid');if(button){button.classList.toggle('is-active',presenterOverviewOn);button.setAttribute('aria-pressed',String(presenterOverviewOn));}
|
||||||
|
if(presenterOverviewOn)buildPresenterOverview();else requestAnimationFrame(fitPresenterFrames);renderAutoState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function mountShell(){
|
||||||
|
if(document.getElementById('ppt-presenter'))return;
|
||||||
|
document.body.insertAdjacentHTML('beforeend',`<div id="ppt-presenter" data-layout="${layoutMode}" aria-label="演讲者视图">
|
||||||
|
<div class="ppt-presenter-top"><div><div class="ppt-presenter-brand">演讲者视图</div><div class="ppt-presenter-meta"><span id="ppt-plan"></span><span id="ppt-section" hidden></span></div></div><div class="ppt-presenter-meta"><span id="ppt-sync" class="ppt-sync" data-state="disconnected" title="表示观众页面的软件同步状态,不代表 HDMI 等物理线缆状态">观众屏 · 未连接</span><button id="ppt-auto-toggle" class="ppt-ui-btn ppt-auto-state" type="button">自动翻页 · 关闭</button><button id="ppt-preflight" class="ppt-ui-btn" type="button">检查</button><button id="ppt-settings" class="ppt-ui-btn" type="button">设置</button><button id="ppt-reopen" class="ppt-ui-btn" type="button">重新打开观众屏</button><button id="ppt-exit" class="ppt-ui-btn" type="button">退出</button></div></div>
|
||||||
|
<div class="ppt-presenter-main"><div class="ppt-preview-stack"><div class="ppt-stage-card"><div class="ppt-stage-label"><span>当前页</span><span class="ppt-annotation-tools"><button id="ppt-grid" class="ppt-ui-btn ppt-grid-btn" type="button" aria-pressed="false" title="宫格选页(Esc)"><span class="ppt-grid-icon" aria-hidden="true"><i></i><i></i><i></i><i></i></span>宫格</button><button class="ppt-ui-btn" data-ppt-tool="laser" type="button" title="激光笔(L)">激光</button><button class="ppt-ui-btn" data-ppt-tool="circle" type="button" title="圈选(C)">圈选</button><button id="ppt-ink-clear" class="ppt-ui-btn ppt-clear-btn" type="button" title="清除标记(X)">清除</button></span><span id="ppt-current-label"></span></div><div class="ppt-frame-viewport"><iframe id="ppt-current-frame" class="ppt-frame" title="当前页预览"></iframe><canvas id="ppt-presenter-ink" class="ppt-annotation-layer" aria-label="演讲标注层"></canvas></div></div><div class="ppt-stage-card" id="ppt-next-card"><div class="ppt-stage-label"><span>下一页</span><span id="ppt-next-label"></span></div><div class="ppt-frame-viewport"><iframe id="ppt-next-frame" class="ppt-frame ppt-next-frame" title="下一页预览"></iframe></div></div><div id="ppt-presenter-overview" class="ppt-presenter-overview" hidden><div class="ppt-overview-head"><strong>选择页面</strong><button id="ppt-overview-close" class="ppt-ui-btn" type="button">返回预览</button></div><div id="ppt-presenter-overview-grid" class="ppt-overview-grid"></div></div></div>
|
||||||
|
<div class="ppt-notes-card"><div class="ppt-notes-head"><div class="ppt-notes-label">标题</div><div id="ppt-notes-title" class="ppt-notes-title"></div><div class="ppt-purpose-block"><div class="ppt-notes-label">本页目的</div><div id="ppt-notes-purpose" class="ppt-purpose-copy"></div></div><div class="ppt-notes-draft-head"><div><div class="ppt-notes-label">草稿(备注)</div><div id="ppt-note-meta" class="ppt-note-meta"></div></div><span class="ppt-notes-tools"><span id="ppt-saved" class="ppt-saved">已保存</span><button id="ppt-note-smaller" class="ppt-ui-btn" type="button" title="缩小备注字号">A−</button><button id="ppt-note-larger" class="ppt-ui-btn" type="button" title="放大备注字号">A+</button></span></div></div><textarea id="ppt-note-editor" class="ppt-note-editor" spellcheck="false" aria-label="本页演讲备注"></textarea></div></div>
|
||||||
|
<div class="ppt-presenter-bottom"><div class="ppt-time-dashboard"><div class="ppt-time-stat"><span class="ppt-time-label">已进行</span><strong id="ppt-clock" class="ppt-time-value">00:00</strong><span class="ppt-time-hint">总时长</span></div><div class="ppt-time-stat"><span class="ppt-time-label">本页</span><strong id="ppt-slide-clock" class="ppt-time-value">00:00</strong><span id="ppt-slide-plan" class="ppt-time-hint">计划 —</span></div><div id="ppt-slide-status-box" class="ppt-time-stat"><span id="ppt-slide-status-label" class="ppt-time-label">计划</span><strong id="ppt-slide-status" class="ppt-time-value">—</strong><span id="ppt-slide-status-hint" class="ppt-time-hint">未设置</span></div></div><div class="ppt-control-stack"><div class="ppt-controls"><button id="ppt-first" class="ppt-ui-btn" type="button">首页</button><button id="ppt-prev" class="ppt-ui-btn" type="button">上一页</button><button id="ppt-next" class="ppt-ui-btn" type="button">下一页</button><button id="ppt-last" class="ppt-ui-btn" type="button">尾页</button></div><div class="ppt-session-actions"><button id="ppt-timer-toggle" class="ppt-ui-btn" type="button">开始</button><button id="ppt-timer-reset" class="ppt-ui-btn" type="button">重置</button><button id="ppt-rehearsal" class="ppt-ui-btn" type="button">排练</button></div></div><div class="ppt-page-state"><strong id="ppt-page-count"></strong><span id="ppt-page-percent"></span></div></div>
|
||||||
|
<div id="ppt-dialog" class="ppt-dialog" hidden><div class="ppt-dialog-panel" role="dialog" aria-modal="true"><div class="ppt-dialog-head"><div id="ppt-dialog-title" class="ppt-dialog-title"></div><button id="ppt-dialog-close" class="ppt-ui-btn" type="button">关闭</button></div><div id="ppt-dialog-body" class="ppt-dialog-body"></div></div></div>
|
||||||
|
</div>`);
|
||||||
|
document.getElementById('ppt-reopen').onclick=reopenAudience;
|
||||||
|
document.getElementById('ppt-exit').onclick=exitPresenter;
|
||||||
|
document.getElementById('ppt-preflight').onclick=openPreflight;
|
||||||
|
document.getElementById('ppt-settings').onclick=openSettings;
|
||||||
|
document.getElementById('ppt-first').onclick=()=>go(0,{force:true});
|
||||||
|
document.getElementById('ppt-prev').onclick=()=>go(idx-1,{force:true});
|
||||||
|
document.getElementById('ppt-grid').onclick=()=>togglePresenterOverview();
|
||||||
|
document.getElementById('ppt-overview-close').onclick=()=>togglePresenterOverview(false);
|
||||||
|
document.getElementById('ppt-next').onclick=()=>go(idx+1,{force:true});
|
||||||
|
document.getElementById('ppt-last').onclick=()=>go(idx===total-1?0:total-1,{force:true});
|
||||||
|
document.getElementById('ppt-timer-toggle').onclick=toggleTimer;
|
||||||
|
document.getElementById('ppt-timer-reset').onclick=resetTimer;
|
||||||
|
document.getElementById('ppt-rehearsal').onclick=toggleRehearsal;
|
||||||
|
document.getElementById('ppt-auto-toggle').onclick=toggleAutoAdvance;
|
||||||
|
document.getElementById('ppt-note-smaller').onclick=()=>setNoteSize(noteSize-1);
|
||||||
|
document.getElementById('ppt-note-larger').onclick=()=>setNoteSize(noteSize+1);
|
||||||
|
document.getElementById('ppt-note-editor').addEventListener('input',saveNote);
|
||||||
|
document.querySelectorAll('[data-ppt-tool]').forEach(button=>button.onclick=()=>setAnnotationTool(button.dataset.pptTool));
|
||||||
|
document.getElementById('ppt-ink-clear').onclick=()=>clearAnnotations(true);
|
||||||
|
const ink=document.getElementById('ppt-presenter-ink');
|
||||||
|
ink.addEventListener('pointerdown',onAnnotationPointerDown);ink.addEventListener('pointermove',onAnnotationPointerMove);ink.addEventListener('pointerup',onAnnotationPointerUp);ink.addEventListener('pointercancel',onAnnotationPointerUp);ink.addEventListener('pointerleave',onAnnotationPointerLeave);
|
||||||
|
document.getElementById('ppt-dialog-close').onclick=closeDialog;
|
||||||
|
document.getElementById('ppt-dialog').onclick=event=>{if(event.target.id==='ppt-dialog')closeDialog();};
|
||||||
|
if('ResizeObserver'in window){presenterFrameObserver=new ResizeObserver(()=>requestAnimationFrame(fitPresenterFrames));presenterFrameObserver.observe(document.querySelector('.ppt-preview-stack'));}
|
||||||
|
else addEventListener('resize',fitPresenterFrames);
|
||||||
|
setNoteSize(noteSize,false);applyLayout(layoutMode,false);requestAnimationFrame(fitPresenterFrames);
|
||||||
|
}
|
||||||
|
function updateShell(){
|
||||||
|
if(mode!=='presenter')return;mountShell();
|
||||||
|
const n=noteAt(idx),next=Math.min(idx+1,total-1),id=slideId(idx),editor=document.getElementById('ppt-note-editor');
|
||||||
|
const currentFrame=document.getElementById('ppt-current-frame'),nextFrame=document.getElementById('ppt-next-frame');
|
||||||
|
if(currentFrame.dataset.slide!==String(idx)){currentFrame.src=frameUrl('preview',idx);currentFrame.dataset.slide=String(idx);}
|
||||||
|
if(nextFrame.dataset.slide!==String(next)){nextFrame.src=frameUrl('preview',next);nextFrame.dataset.slide=String(next);}
|
||||||
|
document.getElementById('ppt-current-label').textContent=`${idx+1} / ${total}`;
|
||||||
|
document.getElementById('ppt-next-label').textContent=idx===total-1?'已到尾页':`${next+1} / ${total}`;
|
||||||
|
document.getElementById('ppt-notes-title').textContent=n.title||`第 ${idx+1} 页`;
|
||||||
|
document.getElementById('ppt-notes-purpose').textContent=n.purpose||'—';
|
||||||
|
const target=plannedMsAt(idx);document.getElementById('ppt-note-meta').textContent=`建议 ${target?(target/60000).toFixed(1):'—'} 分钟 · ${id}`;
|
||||||
|
document.getElementById('ppt-page-count').textContent=`第 ${idx+1} / ${total} 页`;
|
||||||
|
document.getElementById('ppt-page-percent').textContent=`${Math.round(((idx+1)/total)*100)}%`;
|
||||||
|
document.getElementById('ppt-first').disabled=idx===0;document.getElementById('ppt-prev').disabled=idx===0;document.getElementById('ppt-next').disabled=idx===total-1;
|
||||||
|
document.getElementById('ppt-last').textContent=idx===total-1?'重新开始':'尾页';
|
||||||
|
if(renderedNoteId!==id){renderedNoteId=id;editor.value=loadNoteValue(id,idx,n);editor.scrollTop=0;document.getElementById('ppt-saved').textContent='已保存';}
|
||||||
|
if(presenterOverviewOn)buildPresenterOverview();requestAnimationFrame(fitPresenterFrames);renderTimer();updateSyncStatus();
|
||||||
|
}
|
||||||
|
function saveNote(){
|
||||||
|
const saved=document.getElementById('ppt-saved');saved.textContent='保存中…';saved.classList.remove('ppt-saved');clearTimeout(saveTimer);
|
||||||
|
saveTimer=setTimeout(()=>{localStorage.setItem(noteStorageKey(renderedNoteId),document.getElementById('ppt-note-editor').value);saved.textContent='已保存';saved.classList.add('ppt-saved');},300);
|
||||||
|
}
|
||||||
|
function setNoteSize(value,persist=true){noteSize=Math.max(14,Math.min(26,value));document.documentElement.style.setProperty('--ppt-note-size',`${noteSize}px`);if(persist)localStorage.setItem('guizang-ppt-note-size',String(noteSize));}
|
||||||
|
function currentElapsed(){return elapsed+(timerRunning?Date.now()-timerStartedAt:0);}
|
||||||
|
function currentSlideElapsed(i=idx){return slideElapsed[i]+(timerRunning&&timedSlideIndex===i?Date.now()-slideTimerStartedAt:0);}
|
||||||
|
function commitRunningTime(now=Date.now()){
|
||||||
|
if(!timerRunning)return;elapsed+=now-timerStartedAt;slideElapsed[timedSlideIndex]+=now-slideTimerStartedAt;timerStartedAt=now;slideTimerStartedAt=now;
|
||||||
|
}
|
||||||
|
function rollSlideTimer(nextIndex){if(timerRunning)commitRunningTime();timedSlideIndex=nextIndex;slideTimerStartedAt=Date.now();}
|
||||||
|
function renderTimer(){
|
||||||
|
const totalUsed=currentElapsed(),pageUsed=currentSlideElapsed(),target=plannedMsAt(idx),clock=document.getElementById('ppt-clock');
|
||||||
|
if(clock)clock.textContent=durationText(totalUsed);
|
||||||
|
const slideClock=document.getElementById('ppt-slide-clock'),slidePlan=document.getElementById('ppt-slide-plan'),statusBox=document.getElementById('ppt-slide-status-box'),statusLabel=document.getElementById('ppt-slide-status-label'),statusValue=document.getElementById('ppt-slide-status'),statusHint=document.getElementById('ppt-slide-status-hint');
|
||||||
|
if(slideClock)slideClock.textContent=durationText(pageUsed);if(slidePlan)slidePlan.textContent=target?`计划 ${durationText(target)}`:'计划 —';
|
||||||
|
if(statusBox&&statusLabel&&statusValue&&statusHint){
|
||||||
|
const over=Boolean(target&&pageUsed>target),remaining=Boolean(target&&!over);statusBox.classList.toggle('is-over',over);statusBox.classList.toggle('is-remaining',remaining);
|
||||||
|
statusLabel.textContent=over?'超时':(remaining?'剩余':'计划');statusValue.textContent=target?durationText(Math.abs(target-pageUsed)):'—';statusHint.textContent=over?'已超出本页计划':(remaining?'距本页计划':'未设置');
|
||||||
|
}
|
||||||
|
const totalPlan=totalPlannedMs(),plan=document.getElementById('ppt-plan');
|
||||||
|
if(plan)plan.textContent=totalPlan?`计划 ${Number(totalPlan/60000).toFixed(1)} 分钟 · 预计 ${clockTime(Date.now()+Math.max(0,totalPlan-totalUsed))}`:'计划 —';
|
||||||
|
const section=sectionInfo(idx),sectionEl=document.getElementById('ppt-section');
|
||||||
|
if(sectionEl){sectionEl.hidden=!section;sectionEl.textContent=section?`${section.name} · ${section.position}/${section.count}${section.planned?` · 剩余 ${durationText(Math.max(0,section.planned-section.used))}`:''}`:'';}
|
||||||
|
const timerButton=document.getElementById('ppt-timer-toggle');if(timerButton)timerButton.textContent=timerRunning?'暂停':(totalUsed?'继续':'开始');
|
||||||
|
const rehearsalButton=document.getElementById('ppt-rehearsal');if(rehearsalButton){rehearsalButton.textContent=rehearsalActive?'结束排练':'排练';rehearsalButton.classList.toggle('ppt-rehearsal-on',rehearsalActive);}
|
||||||
|
renderAutoState();
|
||||||
|
}
|
||||||
|
function toggleTimer(){const now=Date.now();if(timerRunning){commitRunningTime(now);timerRunning=false;}else{timerStartedAt=now;slideTimerStartedAt=now;timedSlideIndex=idx;timerRunning=true;}renderTimer();}
|
||||||
|
function resetTimer(){
|
||||||
|
elapsed=0;slideElapsed=Array(total).fill(0);timedSlideIndex=idx;
|
||||||
|
if(timerRunning){timerStartedAt=Date.now();slideTimerStartedAt=timerStartedAt;}else{timerStartedAt=0;slideTimerStartedAt=0;}
|
||||||
|
renderTimer();
|
||||||
|
}
|
||||||
|
function rehearsalHistory(){try{return JSON.parse(localStorage.getItem(storageKey('rehearsals'))||'[]');}catch(e){return[];}}
|
||||||
|
function toggleRehearsal(){
|
||||||
|
if(rehearsalActive){finishRehearsal(true);return;}
|
||||||
|
rehearsalActive=true;if(timerRunning){commitRunningTime();timerRunning=false;}resetTimer();toggleTimer();renderTimer();
|
||||||
|
}
|
||||||
|
function finishRehearsal(showSummary){
|
||||||
|
if(!rehearsalActive)return;if(timerRunning){commitRunningTime();timerRunning=false;}
|
||||||
|
const result={endedAt:Date.now(),totalMs:elapsed,slides:slideElapsed.map((ms,i)=>({index:i,id:slideId(i),title:noteAt(i).title||slideId(i),ms,targetMs:plannedMsAt(i)}))};
|
||||||
|
const history=rehearsalHistory();history.unshift(result);try{localStorage.setItem(storageKey('rehearsals'),JSON.stringify(history.slice(0,5)));}catch(e){}
|
||||||
|
rehearsalActive=false;renderTimer();if(showSummary)openRehearsalSummary(result,history.slice(0,5));
|
||||||
|
}
|
||||||
|
function openRehearsalSummary(result,history){
|
||||||
|
const rows=result.slides.filter(item=>item.ms>0).map(item=>`<tr><td>${item.index+1}</td><td>${escapeHTML(item.title)}</td><td>${durationText(item.ms)}</td><td>${item.targetMs?signedDuration(item.ms-item.targetMs):'—'}</td></tr>`).join('');
|
||||||
|
const recent=history.map(item=>`<div class="ppt-dialog-row"><span>${new Date(item.endedAt).toLocaleString()}</span><strong>${durationText(item.totalMs)}</strong></div>`).join('');
|
||||||
|
openDialog('排练记录',`<div class="ppt-dialog-section"><div class="ppt-dialog-row"><span>本次总时长</span><strong>${durationText(result.totalMs)}</strong></div><table class="ppt-summary"><thead><tr><th>#</th><th>页面</th><th>实际</th><th>与计划相比</th></tr></thead><tbody>${rows||'<tr><td colspan="4">没有记录到页面时间</td></tr>'}</tbody></table></div><div class="ppt-dialog-section"><h3>最近排练</h3>${recent}</div>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoDurationMs(i=idx){const explicit=Number(noteAt(i).autoAdvanceSeconds);const seconds=Number.isFinite(explicit)&&explicit>0?explicit:autoGlobalSeconds;return seconds>0?seconds*1000:null;}
|
||||||
|
function armAutoAdvance(){autoDeadline=0;autoRemainingMs=autoDurationMs()||0;}
|
||||||
|
function autoBlockedReason(){
|
||||||
|
if(presenterOverviewOn||overviewOn)return'宫格选页中';if(annotationTool!=='none')return'标注中';if(screenMode!=='normal')return'观众屏已暂停';if(document.hidden)return'窗口不可见';
|
||||||
|
const dialog=document.getElementById('ppt-dialog');if(dialog&&!dialog.hidden)return'设置中';
|
||||||
|
if(audienceOpenedAt&&syncState()!=='synced')return'等待观众屏同步';return'';
|
||||||
|
}
|
||||||
|
function updateAutoAdvance(){
|
||||||
|
if(!autoEnabled)return;
|
||||||
|
const duration=autoDurationMs();if(!duration||idx===total-1)return;
|
||||||
|
const blocked=autoBlockedReason(),now=Date.now();
|
||||||
|
if(blocked){if(autoDeadline){autoRemainingMs=Math.max(0,autoDeadline-now);autoDeadline=0;}return;}
|
||||||
|
if(!autoDeadline)autoDeadline=now+(autoRemainingMs||duration);
|
||||||
|
autoRemainingMs=Math.max(0,autoDeadline-now);
|
||||||
|
if(now>=autoDeadline){autoDeadline=0;autoRemainingMs=0;go(idx+1,{force:true});}
|
||||||
|
}
|
||||||
|
function renderAutoState(){
|
||||||
|
const button=document.getElementById('ppt-auto-toggle');if(!button)return;
|
||||||
|
const duration=autoDurationMs(),blocked=autoEnabled?autoBlockedReason():'';
|
||||||
|
button.classList.toggle('is-on',autoEnabled&&!blocked);button.classList.toggle('is-paused',autoEnabled&&Boolean(blocked));
|
||||||
|
if(!autoEnabled)button.textContent='自动翻页 · 关闭';
|
||||||
|
else if(!duration)button.textContent='自动翻页 · 等待时长';
|
||||||
|
else if(idx===total-1)button.textContent='自动翻页 · 已结束';
|
||||||
|
else if(blocked)button.textContent=`自动翻页 · ${blocked}`;
|
||||||
|
else button.textContent=`自动翻页 · ${durationText(autoRemainingMs||duration)}`;
|
||||||
|
}
|
||||||
|
function toggleAutoAdvance(){
|
||||||
|
if(!autoEnabled&&!autoDurationMs()){openSettings();return;}
|
||||||
|
autoEnabled=!autoEnabled;localStorage.setItem(storageKey('auto-enabled'),autoEnabled?'1':'0');armAutoAdvance();renderAutoState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDialog(title,html){
|
||||||
|
const dialog=document.getElementById('ppt-dialog');if(!dialog)return;
|
||||||
|
document.getElementById('ppt-dialog-title').textContent=title;document.getElementById('ppt-dialog-body').innerHTML=html;dialog.hidden=false;renderAutoState();
|
||||||
|
}
|
||||||
|
function closeDialog(){const dialog=document.getElementById('ppt-dialog');if(dialog)dialog.hidden=true;renderAutoState();}
|
||||||
|
function applyLayout(value,persist=true){
|
||||||
|
layoutMode=['balanced','slides','notes'].includes(value)?value:'balanced';
|
||||||
|
document.getElementById('ppt-presenter')?.setAttribute('data-layout',layoutMode);
|
||||||
|
if(persist)localStorage.setItem(storageKey('layout'),layoutMode);requestAnimationFrame(fitPresenterFrames);
|
||||||
|
}
|
||||||
|
function applyAudienceScreen(value){
|
||||||
|
const cover=document.getElementById('ppt-audience-cover');if(!cover)return;
|
||||||
|
if(value==='black'||value==='white'){cover.style.display='block';cover.style.background=value;}else cover.style.display='none';
|
||||||
|
}
|
||||||
|
function setScreenMode(value){
|
||||||
|
const next=['normal','black','white','frozen'].includes(value)?value:'normal',wasFrozen=audienceFrozen;
|
||||||
|
screenMode=next;audienceFrozen=next==='frozen';emit({type:'screen',mode:next});
|
||||||
|
if(wasFrozen&&!audienceFrozen)sendGoto(true);updateSyncStatus();armAutoAdvance();renderAutoState();
|
||||||
|
}
|
||||||
|
function openSettings(){
|
||||||
|
openDialog('演示设置',`<div class="ppt-settings-card"><label class="ppt-switch-row" for="ppt-auto-enabled-setting"><span><strong>自动翻页</strong><br><span class="ppt-muted">需要时手动开启,默认关闭</span></span><input id="ppt-auto-enabled-setting" type="checkbox" ${autoEnabled?'checked':''}><span class="ppt-switch" aria-hidden="true"></span></label><div class="ppt-field"><label for="ppt-auto-seconds-setting">全局间隔</label><div class="ppt-stepper"><button id="ppt-auto-minus" type="button" aria-label="减少一秒">−</button><input id="ppt-auto-seconds-setting" type="number" min="1" step="1" value="${autoGlobalSeconds||''}" placeholder="—"><span class="ppt-stepper-unit">秒</span><button id="ppt-auto-plus" type="button" aria-label="增加一秒">+</button></div></div><button id="ppt-settings-save" class="ppt-ui-btn ppt-settings-save" type="button">保存自动翻页</button><p class="ppt-settings-copy">单页 autoAdvanceSeconds 优先于全局间隔;minutes 只是讲述计划。</p></div><div class="ppt-settings-card"><h3>演讲者布局</h3><p class="ppt-settings-copy">根据屏幕宽度调整页面与备注的比例</p><div class="ppt-segmented"><button class="ppt-ui-btn" data-layout-choice="balanced" type="button">均衡</button><button class="ppt-ui-btn" data-layout-choice="slides" type="button">页面优先</button><button class="ppt-ui-btn" data-layout-choice="notes" type="button">备注优先</button></div></div><div class="ppt-settings-card"><h3>观众屏</h3><p class="ppt-settings-copy">临时遮挡或冻结观众屏</p><div class="ppt-segmented screen"><button class="ppt-ui-btn" data-screen-choice="normal" type="button">正常</button><button class="ppt-ui-btn" data-screen-choice="black" type="button">黑屏 B</button><button class="ppt-ui-btn" data-screen-choice="white" type="button">白屏 W</button><button class="ppt-ui-btn" data-screen-choice="frozen" type="button">冻结 F</button></div></div><div class="ppt-dialog-section"><button id="ppt-open-shortcuts" class="ppt-ui-btn" type="button">查看快捷键</button></div>`);
|
||||||
|
document.querySelectorAll('[data-layout-choice]').forEach(button=>{button.classList.toggle('ppt-primary',button.dataset.layoutChoice===layoutMode);button.onclick=()=>{applyLayout(button.dataset.layoutChoice);openSettings();};});
|
||||||
|
document.querySelectorAll('[data-screen-choice]').forEach(button=>{button.classList.toggle('ppt-primary',button.dataset.screenChoice===screenMode);button.onclick=()=>{setScreenMode(button.dataset.screenChoice);openSettings();};});
|
||||||
|
const secondsInput=document.getElementById('ppt-auto-seconds-setting'),stepSeconds=delta=>{const current=Math.max(0,Number(secondsInput.value||0));secondsInput.value=String(Math.max(1,current+delta));};
|
||||||
|
document.getElementById('ppt-auto-minus').onclick=()=>stepSeconds(-1);document.getElementById('ppt-auto-plus').onclick=()=>stepSeconds(1);
|
||||||
|
document.getElementById('ppt-settings-save').onclick=()=>{autoEnabled=document.getElementById('ppt-auto-enabled-setting').checked;autoGlobalSeconds=Math.max(0,Number(secondsInput.value||0));localStorage.setItem(storageKey('auto-enabled'),autoEnabled?'1':'0');localStorage.setItem(storageKey('auto-seconds'),String(autoGlobalSeconds));armAutoAdvance();closeDialog();};
|
||||||
|
document.getElementById('ppt-open-shortcuts').onclick=openShortcuts;
|
||||||
|
}
|
||||||
|
function openShortcuts(){
|
||||||
|
const items=[['← / →','上一页 / 下一页'],['Home / End','首页 / 尾页'],['Esc','宫格选页;标注工具激活时先退出工具'],['L','激光笔'],['C','圈选'],['X','清除标记'],['B / W','观众屏黑屏 / 白屏'],['F','冻结 / 恢复观众屏'],['A','开启 / 关闭自动翻页'],['R','开始 / 结束排练'],['?','快捷键说明']];
|
||||||
|
openDialog('快捷键',`<div class="ppt-shortcuts">${items.map(([key,label])=>`<kbd>${key}</kbd><span>${label}</span>`).join('')}</div>`);
|
||||||
|
}
|
||||||
|
function preflightChecks(){
|
||||||
|
const images=[...document.images],videos=[...document.querySelectorAll('video')],frame=document.getElementById('ppt-current-frame'),rect=frame?.getBoundingClientRect();
|
||||||
|
return[
|
||||||
|
{label:'观众屏连接',ok:['synced','frozen'].includes(syncState()),detail:`当前状态:${document.getElementById('ppt-sync')?.textContent||'未连接'}`},
|
||||||
|
{label:'弹窗权限',ok:!popupBlocked,detail:popupBlocked?'浏览器阻止了观众窗口':'未检测到弹窗拦截'},
|
||||||
|
{label:'全屏状态',ok:Boolean(document.fullscreenElement),warn:true,detail:document.fullscreenElement?'已进入全屏':'尚未全屏,可在演讲前进入'},
|
||||||
|
{label:'字体加载',ok:!document.fonts||document.fonts.status==='loaded',detail:document.fonts?`状态:${document.fonts.status}`:'浏览器不提供字体状态'},
|
||||||
|
{label:'图片资源',ok:images.every(image=>image.complete&&image.naturalWidth>0),detail:`${images.filter(image=>image.complete&&image.naturalWidth>0).length} / ${images.length} 已加载`},
|
||||||
|
{label:'视频资源',ok:videos.every(video=>video.readyState>=2),detail:videos.length?`${videos.filter(video=>video.readyState>=2).length} / ${videos.length} 可播放`:'没有视频'},
|
||||||
|
{label:'当前页 16:9',ok:Boolean(rect&&Math.abs(rect.width/rect.height-16/9)<.003),detail:rect?`${Math.round(rect.width)} × ${Math.round(rect.height)}`:'尚未渲染'}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
function openPreflight(){
|
||||||
|
const checks=preflightChecks(),html=checks.map(item=>`<div class="ppt-dialog-row"><div class="ppt-check ${item.ok?'is-ok':(item.warn?'is-warn':'')}"><span class="ppt-check-dot"></span><span><strong>${item.label}</strong><br><span class="ppt-muted">${item.detail}</span></span></div><strong>${item.ok?'正常':(item.warn?'建议':'异常')}</strong></div>`).join('');
|
||||||
|
openDialog('演前检查',`<div class="ppt-dialog-section">${html}</div><div class="ppt-dialog-section"><button id="ppt-preflight-refresh" class="ppt-ui-btn" type="button">重新检查</button> <button id="ppt-enter-fullscreen" class="ppt-ui-btn" type="button">进入全屏</button></div><p class="ppt-muted">这里只能检查浏览器和观众页面的软件状态,不能确认 HDMI、转接器或投影仪线缆是否物理连接。</p>`);
|
||||||
|
document.getElementById('ppt-preflight-refresh').onclick=openPreflight;
|
||||||
|
document.getElementById('ppt-enter-fullscreen').onclick=async()=>{try{await document.documentElement.requestFullscreen();}catch(e){}openPreflight();};
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTransport(){
|
||||||
|
if(transportReady)return;transportReady=true;
|
||||||
|
if('BroadcastChannel'in window){try{channel=new BroadcastChannel(channelName());channel.onmessage=e=>receive(e.data);}catch(e){}}
|
||||||
|
addEventListener('message',e=>receive(e.data));
|
||||||
|
addEventListener('storage',e=>{if(e.key===syncKey()&&e.newValue){try{receive(JSON.parse(e.newValue));}catch(err){}}});
|
||||||
|
}
|
||||||
|
function emit(payload,{persist=true}={}){
|
||||||
|
const message={__guizangPptSync:1,session,role:mode,...payload,at:Date.now(),nonce:Math.random().toString(36).slice(2)};
|
||||||
|
try{channel?.postMessage(message);}catch(e){}
|
||||||
|
if(persist)try{localStorage.setItem(syncKey(),JSON.stringify(message));}catch(e){}
|
||||||
|
try{if(mode==='presenter'&&audienceWindow&&!audienceWindow.closed)audienceWindow.postMessage(message,'*');}catch(e){}
|
||||||
|
try{if(mode==='audience'&&window.opener&&!window.opener.closed)window.opener.postMessage(message,'*');}catch(e){}
|
||||||
|
}
|
||||||
|
function receive(message){
|
||||||
|
if(!message||message.__guizangPptSync!==1||message.session!==session||message.role===mode)return;
|
||||||
|
if(message.nonce){if(seenNonceSet.has(message.nonce))return;seenNonceSet.add(message.nonce);seenNonces.push(message.nonce);if(seenNonces.length>256)seenNonceSet.delete(seenNonces.shift());}
|
||||||
|
if(mode==='presenter'){
|
||||||
|
if(message.type==='ready'){popupBlocked=false;sendGoto(true);}
|
||||||
|
if(message.type==='ack'){lastAckAt=Date.now();lastAckIndex=Number(message.index);lastAckSeq=Number(message.seq);updateSyncStatus();}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(mode!=='audience')return;
|
||||||
|
if(message.type==='screen'){applyAudienceScreen(message.mode);return;}
|
||||||
|
if(message.type==='laser'&&Number(message.index)===idx){audienceLaser=message.point||null;audienceLaserHideAt=Date.now()+650;renderInks();return;}
|
||||||
|
if(message.type==='circle'&&Number(message.index)===idx&&message.circle){audienceCircles.push(message.circle);renderInks();return;}
|
||||||
|
if(message.type==='clear'&&Number(message.index)===idx){audienceCircles=[];renderInks();return;}
|
||||||
|
if(message.type==='goto'||message.type==='ping'){
|
||||||
|
if(message.type==='goto'){audienceCircles=Array.isArray(message.circles)?message.circles:[];applyAudienceScreen(message.screenMode);go(Number(message.index),{force:true});}
|
||||||
|
emit({type:'ack',index:idx,seq:Number(message.seq)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function sendGoto(force=false){
|
||||||
|
if(mode!=='presenter'||audienceFrozen)return;if(force||lastAckIndex!==idx)seq++;
|
||||||
|
emit({type:'goto',index:idx,seq,screenMode,circles});updateSyncStatus();
|
||||||
|
}
|
||||||
|
function syncState(){
|
||||||
|
if(popupBlocked)return'blocked';if(audienceWindow&&audienceWindow.closed)return'disconnected';if(audienceFrozen)return'frozen';
|
||||||
|
if(!lastAckAt)return audienceOpenedAt&&Date.now()-audienceOpenedAt<2600?'connecting':(audienceOpenedAt?'unsynced':'disconnected');
|
||||||
|
if(Date.now()-lastAckAt>2600||lastAckIndex!==idx||lastAckSeq<seq)return'unsynced';return'synced';
|
||||||
|
}
|
||||||
|
function updateSyncStatus(){
|
||||||
|
const el=document.getElementById('ppt-sync');if(!el)return;const state=syncState();
|
||||||
|
const labels={blocked:'弹窗被拦截',disconnected:'未连接',connecting:'连接中',unsynced:'未同步',frozen:'已冻结',synced:`已同步 · 第 ${idx+1} 页`};
|
||||||
|
el.dataset.state=state;el.textContent=`观众屏 · ${labels[state]}`;
|
||||||
|
}
|
||||||
|
function reopenAudience(){
|
||||||
|
initTransport();popupBlocked=false;audienceOpenedAt=Date.now();lastAckAt=0;lastAckIndex=-1;lastAckSeq=-1;
|
||||||
|
if(audienceFrozen){audienceFrozen=false;screenMode='normal';}
|
||||||
|
audienceWindow=window.open(frameUrl('audience',idx),`guizang-ppt-audience-${session}`,'popup=yes,width=1440,height=900');
|
||||||
|
if(!audienceWindow)popupBlocked=true;updateSyncStatus();
|
||||||
|
}
|
||||||
|
function enterPresenter(openAudience=true){
|
||||||
|
if(mode==='presenter')return;if(overviewOn)toggleOverview();lowPowerBeforePresenter=window.__lowPowerMode;mode='presenter';initTransport();mountShell();
|
||||||
|
document.body.classList.remove('ppt-audience','ppt-preview');document.body.classList.add('ppt-presenter');document.title=`演讲者视图 · ${originalTitle}`;
|
||||||
|
window.__setLowPowerMode?.(true,{persist:false});updateShell();armAutoAdvance();if(openAudience)reopenAudience();
|
||||||
|
}
|
||||||
|
function exitPresenter(){
|
||||||
|
if(rehearsalActive)finishRehearsal(false);else if(timerRunning){commitRunningTime();timerRunning=false;}togglePresenterOverview(false);emit({type:'bye'});mode='deck';document.body.classList.remove('ppt-presenter');document.title=originalTitle;window.__setLowPowerMode?.(lowPowerBeforePresenter,{persist:false});
|
||||||
|
}
|
||||||
|
function initAudience(){
|
||||||
|
mode='audience';document.body.classList.add('ppt-audience');document.title=`观众屏 · ${originalTitle}`;
|
||||||
|
document.body.insertAdjacentHTML('beforeend','<canvas id="ppt-audience-ink" aria-hidden="true"></canvas><div id="ppt-audience-cover" aria-hidden="true"></div>');
|
||||||
|
const resize=()=>{sizeInk(document.getElementById('ppt-audience-ink'),innerWidth,innerHeight);renderInks();};addEventListener('resize',resize);resize();
|
||||||
|
initTransport();emit({type:'ready',index:idx});
|
||||||
|
}
|
||||||
|
function initPreview(){mode='preview';document.body.classList.add('ppt-preview');window.__setLowPowerMode?.(true,{persist:false});}
|
||||||
|
function onSlideChange(nextIndex){
|
||||||
|
if(mode!=='presenter')return;rollSlideTimer(nextIndex);circles=[];draftCircle=null;laserPoint=null;annotationPointerDown=false;armAutoAdvance();updateShell();sendGoto();
|
||||||
|
}
|
||||||
|
function handleKey(e){
|
||||||
|
const editable=e.target?.matches?.('input,textarea,[contenteditable="true"]');
|
||||||
|
if(editable){if(e.key==='Escape')e.target.blur();return true;}
|
||||||
|
if(mode==='audience'||mode==='preview'){e.preventDefault();return true;}
|
||||||
|
if(mode==='deck'&&e.key?.toLowerCase()==='p'&&!e.metaKey&&!e.ctrlKey&&!e.altKey){e.preventDefault();enterPresenter(true);return true;}
|
||||||
|
if(mode!=='presenter')return false;
|
||||||
|
const key=e.key?.toLowerCase();
|
||||||
|
if(e.key==='?'||key==='/'){e.preventDefault();openShortcuts();return true;}
|
||||||
|
const dialog=document.getElementById('ppt-dialog');
|
||||||
|
if(!dialog?.hidden){if(e.key==='Escape'){e.preventDefault();closeDialog();}return true;}
|
||||||
|
if(e.key==='Escape'){e.preventDefault();if(annotationTool!=='none')setAnnotationTool('none');else togglePresenterOverview();return true;}
|
||||||
|
if(presenterOverviewOn)return true;
|
||||||
|
if(key==='l'){e.preventDefault();setAnnotationTool('laser');return true;}
|
||||||
|
if(key==='c'){e.preventDefault();setAnnotationTool('circle');return true;}
|
||||||
|
if(key==='x'){e.preventDefault();clearAnnotations(true);return true;}
|
||||||
|
if(key==='b'){e.preventDefault();setScreenMode(screenMode==='black'?'normal':'black');return true;}
|
||||||
|
if(key==='w'){e.preventDefault();setScreenMode(screenMode==='white'?'normal':'white');return true;}
|
||||||
|
if(key==='f'){e.preventDefault();setScreenMode(audienceFrozen?'normal':'frozen');return true;}
|
||||||
|
if(key==='a'){e.preventDefault();toggleAutoAdvance();return true;}
|
||||||
|
if(key==='r'){e.preventDefault();toggleRehearsal();return true;}
|
||||||
|
if(['ArrowRight','PageDown',' ','ArrowDown'].includes(e.key)){e.preventDefault();go(idx+1,{force:true});return true;}
|
||||||
|
if(['ArrowLeft','PageUp','ArrowUp'].includes(e.key)){e.preventDefault();go(idx-1,{force:true});return true;}
|
||||||
|
if(e.key==='Home'){e.preventDefault();go(0,{force:true});return true;}
|
||||||
|
if(e.key==='End'){e.preventDefault();go(total-1,{force:true});return true;}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function initMode(){
|
||||||
|
document.getElementById('ppt-low-power-btn')?.addEventListener('click',()=>window.__setLowPowerMode(!window.__lowPowerMode));
|
||||||
|
document.getElementById('ppt-overview-btn')?.addEventListener('click',toggleOverview);
|
||||||
|
document.getElementById('ppt-presenter-btn')?.addEventListener('click',()=>enterPresenter(true));
|
||||||
|
if(requestedMode==='audience')initAudience();else if(requestedMode==='preview')initPreview();else if(requestedMode==='presenter')enterPresenter(false);
|
||||||
|
}
|
||||||
|
setInterval(()=>{
|
||||||
|
renderTimer();renderInks();updateAutoAdvance();
|
||||||
|
if(mode==='presenter'&&Date.now()-lastPingAt>900){lastPingAt=Date.now();if(audienceWindow?.closed)updateSyncStatus();emit({type:'ping',index:idx,seq});updateSyncStatus();}
|
||||||
|
},250);
|
||||||
|
return{initMode,onSlideChange,handleKey,slideLabel,enterPresenter,reopenAudience,toggleOverview:togglePresenterOverview,debugState:()=>({mode,idx,session,syncState:syncState(),timerRunning,rehearsalActive,presenterOverviewOn,autoEnabled,autoGlobalSeconds,autoRemainingMs,screenMode,annotationTool,circles:[...circles],audienceCircles:[...audienceCircles],layoutMode})};
|
||||||
|
})();
|
||||||
|
|
||||||
addEventListener('keydown',e=>{
|
addEventListener('keydown',e=>{
|
||||||
|
if(window.__pptPresenter?.handleKey(e))return;
|
||||||
if(e.key==='Escape'){e.preventDefault();toggleOverview();return;}
|
if(e.key==='Escape'){e.preventDefault();toggleOverview();return;}
|
||||||
if(e.key && e.key.toLowerCase()==='b' && !e.metaKey && !e.ctrlKey && !e.altKey){
|
if(e.key && e.key.toLowerCase()==='b' && !e.metaKey && !e.ctrlKey && !e.altKey){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -732,6 +1269,7 @@ addEventListener('touchend',e=>{
|
|||||||
const initialSlideParam = new URLSearchParams(location.search).get('slide');
|
const initialSlideParam = new URLSearchParams(location.search).get('slide');
|
||||||
const initialSlide = initialSlideParam ? Number(initialSlideParam) - 1 : 0;
|
const initialSlide = initialSlideParam ? Number(initialSlideParam) - 1 : 0;
|
||||||
go(Number.isFinite(initialSlide) ? initialSlide : 0);
|
go(Number.isFinite(initialSlide) ? initialSlide : 0);
|
||||||
|
window.__pptPresenter?.initMode();
|
||||||
</script>
|
</script>
|
||||||
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
||||||
<script>lucide.createIcons();</script>
|
<script>lucide.createIcons();</script>
|
||||||
|
|||||||
+31
@@ -8,6 +8,37 @@
|
|||||||
|
|
||||||
## 🔴 P0 · 一定不能犯的错
|
## 🔴 P0 · 一定不能犯的错
|
||||||
|
|
||||||
|
### 0-P. 演讲者模式必须可控、可排练、可恢复、备注不串页
|
||||||
|
|
||||||
|
**现象**:演讲者端已经翻页,观众屏仍停在上一页;观众窗口关闭后没有提示;页面重排后,用户改过的讲稿跑到了另一页;或演讲模式入口变成页面右上角的突兀按钮。
|
||||||
|
|
||||||
|
**做法**:
|
||||||
|
- 演讲入口只放在右下角现有快捷控制区,显示 `P 演讲模式`。
|
||||||
|
- 演讲者主体只做“左侧预览 + 右侧备注”两栏;当前页在上、下一页在下,避免形成挤压当前页的三栏布局。
|
||||||
|
- 两个预览 iframe 必须严格保持 `16:9`,容器不足时等比缩小并留边,不得裁切或拉伸;小屏优先压缩下一页预览高度。
|
||||||
|
- 底栏固定为“左时间、中控制、右页码进度”:左侧分组显示已进行/本页/剩余或超时,中间两行放翻页和会话控制,右侧只放页码与百分比;不要重复当前页标题。
|
||||||
|
- 翻页控制顺序固定为 `首页 / 上一页 / 下一页 / 尾页`;尾页时最后一个按钮变为“重新开始”。自动翻页放在右上角状态栏。
|
||||||
|
- `宫格` 放在当前页标题旁,打开时替换当前/下一页预览区;选中页面后回到预览,不要弹出突兀的全屏总览。
|
||||||
|
- 右侧卡片依次显示标题、本页目的和草稿备注;设置里的开关用胶囊 switch,时间间隔用美化后的加减 stepper。
|
||||||
|
- 观众屏必须显示软件链路状态:`连接中 / 已同步 / 未同步 / 未连接 / 弹窗被拦截`,并始终提供“重新打开观众屏”。
|
||||||
|
- 演讲者必须能看到总时长、本页时长、计划/剩余/超时;缺失 `minutes` 时显示横杠,不猜测。
|
||||||
|
- 排练模式记录每页实际时长,本地保留最近 5 次;只做数据汇总,不做 AI 教练评分。
|
||||||
|
- 自动翻页默认关闭,`minutes` 与 `autoAdvanceSeconds` 必须分开;总览、圈选、设置、页面隐藏、观众屏暂停或失同步时倒计必须暂停。
|
||||||
|
- 必须提供激光笔、圈选、清除、黑屏、白屏和冻结/恢复;标注坐标要归一化同步到观众屏。
|
||||||
|
- 演前检查必须覆盖观众屏、弹窗、全屏、字体、图片/视频和 16:9;同时明确提醒它不能检测物理线缆。
|
||||||
|
- 备注里的 `section / cue / interaction / delivery / advance / fallback / pronunciation / autoAdvanceSeconds` 都是可选字段;用户大纲没有就整段隐藏。
|
||||||
|
- 每页必须有稳定、唯一的 `data-slide-id`;`SPEAKER_NOTES` 数量、顺序和 ID 必须逐页一致。
|
||||||
|
- 备注本地编辑按页面 ID 保存;切页重置滚动位置并显示保存状态。
|
||||||
|
- 浏览器只能确认观众页面是否同步,不能确认 HDMI/投影线物理连接;现场仍要目视检查外接屏。
|
||||||
|
|
||||||
|
**检查**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node <SKILL_ROOT>/scripts/validate-presenter-mode.mjs path/to/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
浏览器里关闭观众窗口,确认状态变化;再点击“重新打开观众屏”,确认它恢复到演讲者当前页。再逐项实测内嵌宫格选页返回、计时、排练、自动翻页暂停/恢复、激光笔、圈选、清除、黑屏、白屏、冻结、设置组件和演前检查。缩小浏览器窗口再检查一次:当前页与下一页仍上下排列,两个 iframe 的 `width / height` 仍约等于 `16 / 9`,且没有超出预览容器。
|
||||||
|
|
||||||
### 0-S. Swiss locked mode:正文页必须来自原始 22P
|
### 0-S. Swiss locked mode:正文页必须来自原始 22P
|
||||||
|
|
||||||
**现象**:颜色、字体看起来像 Swiss,但标题跑到中间、图片不在网格上、页面结构和原始 22P 完全不是一套东西。
|
**现象**:颜色、字体看起来像 Swiss,但标题跑到中间、图片不在网格上、页面结构和原始 22P 完全不是一套东西。
|
||||||
|
|||||||
+179
@@ -0,0 +1,179 @@
|
|||||||
|
# 演讲者模式与演讲备注契约
|
||||||
|
|
||||||
|
制作任何需要现场讲述的 deck 时加载本文件。目标不是把页面文案复制到备注区,而是让页面、讲述、转场、时间和现场控制形成一条可以排练和可以恢复的叙事链。
|
||||||
|
|
||||||
|
## 1. 页面计划同时规划“看什么”和“说什么”
|
||||||
|
|
||||||
|
在写 HTML 前,先做一张表。只填用户大纲、素材或已确认的现场信息能够支持的列:
|
||||||
|
|
||||||
|
| 页码 | `data-slide-id` | 章节 | 页面目的 | 观众屏可见信息 | 演讲者补充 | 建议时长 | 转场 | 可选现场信息 |
|
||||||
|
|---|---|---|---|---|---|---:|---|---|
|
||||||
|
|
||||||
|
- **观众屏可见信息**:观众此刻必须读到的结论、证据、结构或图像。
|
||||||
|
- **演讲者补充**:背景、例子、判断依据、语气和细节,不要逐字复述页面。
|
||||||
|
- **转场**:解释下一页为什么紧接着出现,不要只写“下一页”。
|
||||||
|
- **可选现场信息**:可包含停顿、提问、Demo、视频、敏感信息、翻页时机、备用方案或读音。
|
||||||
|
|
||||||
|
默认生成“提词卡”,不是逐字稿。只有用户明确要求逐字稿时,才写完整口播。
|
||||||
|
|
||||||
|
## 2. 模型如何把大纲信息带进演讲模式
|
||||||
|
|
||||||
|
对用户的大纲、项目记录和补充说明做结构化提取,不要自行增加现场事实。
|
||||||
|
|
||||||
|
- 已给出章节名或可靠推断的连续章节:写入 `section`。
|
||||||
|
- 已给出总时长或逐页节奏:可给出 `minutes`,总建议时长不超过现场时长的 90%。
|
||||||
|
- 大纲明确写了停顿、提问、Demo、视频、语气、翻页点或备用路径:分别写入对应的可选字段。
|
||||||
|
- 用户明确要求某页自动停留多少秒:才写 `autoAdvanceSeconds`。
|
||||||
|
- 没有提供的信息不猜测、不写“待补充”到演讲界面,直接省略字段。界面对缺失时长显示横杠,其他可选模块整段隐藏。
|
||||||
|
|
||||||
|
`minutes` 是讲述计划,`autoAdvanceSeconds` 是播放行为,二者必须分开。不得因为一页建议讲 1 分钟,就默认 60 秒后自动翻页。
|
||||||
|
|
||||||
|
## 3. 稳定的页面 ID
|
||||||
|
|
||||||
|
每一页都必须有唯一、语义化且稳定的 ID:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<section class="slide ..." data-slide-id="codepilot-capabilities">
|
||||||
|
```
|
||||||
|
|
||||||
|
- 使用小写英文 slug:`cover`、`codepilot-capabilities`、`demo-workflow`、`closing`。
|
||||||
|
- 页面重排时保留原 ID;只有页面语义彻底改变时才更换。
|
||||||
|
- 不要用页码作为 ID。演讲者在浏览器里修改的备注按 ID 保存,稳定 ID 可避免重排后串页。
|
||||||
|
|
||||||
|
## 4. `SPEAKER_NOTES` 数据结构
|
||||||
|
|
||||||
|
在 deck 中保留一条与每页 ID 对应的记录,顺序必须与 slide 完全一致:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const SPEAKER_NOTES = [
|
||||||
|
{
|
||||||
|
id: 'codepilot-capabilities',
|
||||||
|
title: 'CodePilot 能做什么',
|
||||||
|
section: 'CodePilot',
|
||||||
|
minutes: 0.9,
|
||||||
|
purpose: '先让观众建立完整产品认知,再进入实现过程',
|
||||||
|
talk: [
|
||||||
|
'从用户动作讲能力,不先讲底层 Harness',
|
||||||
|
'用首页、多模型、Skills 和侧边栏四张截图建立产品全貌',
|
||||||
|
'强调模型、工具和上下文可以在同一工作流里协作'
|
||||||
|
],
|
||||||
|
transition: '观众知道产品长什么样后,再回答为什么要这样实现',
|
||||||
|
cue: '按截图顺序指向四个能力区',
|
||||||
|
advance: '讲完“同一工作流”后翻页'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
window.__SPEAKER_NOTES__ = SPEAKER_NOTES;
|
||||||
|
```
|
||||||
|
|
||||||
|
必填字段:
|
||||||
|
|
||||||
|
- `id`、`title`、`purpose`、`talk`、`transition`。
|
||||||
|
- `talk` 默认 3–5 条,每条只表达一个意思;封面、章节页和纯过渡页可以更短。
|
||||||
|
|
||||||
|
可选字段:
|
||||||
|
|
||||||
|
| 字段 | 用途 | 界面缺失时 |
|
||||||
|
|---|---|---|
|
||||||
|
| `section` | 章节名和章节进度 | 隐藏章节行 |
|
||||||
|
| `minutes` | 本页建议讲述分钟 | 显示 `—` |
|
||||||
|
| `cue` | 停顿、Demo、视频、敏感信息提醒 | 隐藏 |
|
||||||
|
| `interaction` | 现场提问、举手、投票或 Q&A | 隐藏 |
|
||||||
|
| `delivery` | 语气、重音、语速或停顿 | 隐藏 |
|
||||||
|
| `advance` | 人工翻页的句子或动作时机 | 隐藏 |
|
||||||
|
| `fallback` | Demo / 视频 / 网络失败时的备用说法 | 隐藏 |
|
||||||
|
| `pronunciation` | 人名、缩写和外语读音 | 隐藏 |
|
||||||
|
| `autoAdvanceSeconds` | 本页自动停留秒数,优先于全局间隔 | 使用全局间隔或不自动翻页 |
|
||||||
|
|
||||||
|
`cue`、`interaction`、`delivery`、`advance`、`fallback`、`pronunciation` 可以是字符串,也可以是字符串数组。
|
||||||
|
|
||||||
|
## 5. 演讲者界面行为
|
||||||
|
|
||||||
|
- 普通 deck 的右下角控制区显示 `P 演讲模式`,不单独悬浮一个抢眼按钮。
|
||||||
|
- 点击后当前窗口进入演讲者视图,并打开独立观众屏。
|
||||||
|
- 主体保持两栏:左侧预览、右侧备注;左侧当前页在上、下一页在下。
|
||||||
|
- 当前页与下一页 iframe 始终严格保持 `16:9`。空间不足时留边并整体等比缩小,不得裁切、压扁或让页面内部文字重新排版。
|
||||||
|
- 小屏优先缩小下一页预览,把空间留给当前页。
|
||||||
|
- 底栏分成三段:左侧是 `已进行 / 本页 / 剩余或超时` 三组时间,中间是两行控制(第一行 `首页 / 上一页 / 下一页 / 尾页`,第二行 `开始或暂停 / 重置 / 排练`),右侧只显示 `第几页 / 总页数` 和完成百分比。不要在底栏重复当前页标题。
|
||||||
|
- `自动翻页` 放在右上角状态栏,不占用底部翻页区。
|
||||||
|
- `宫格` 放在“当前页”标题旁。打开后直接用宫格替换当前页/下一页预览区,不要弹出突兀的全屏层;卡片显示页码、标题、章节和进度,点击页面后立即回到当前页/下一页预览。`ESC` 切换宫格。
|
||||||
|
- 右侧卡片依次显示 `标题 / 本页目的 / 草稿(备注)`,避免把标题或页面目的混进草稿正文。
|
||||||
|
- 演讲者可从“均衡 / 页面优先 / 备注优先”三种布局选择。
|
||||||
|
- 备注编辑按 `data-slide-id` 存入 `localStorage`,显示保存状态;切页后备注滚动回顶部,并允许调整字号。
|
||||||
|
- `Home` / `End`、方向键、PageUp/PageDown 与可见按钮保持一致;光标在备注编辑器中时不得触发翻页。
|
||||||
|
- 设置面板使用可辨识的卡片层级;开关使用胶囊 switch,数值间隔使用带减号、数值和加号的 stepper,不直接暴露浏览器原生复选框或粗糙数字输入框。
|
||||||
|
|
||||||
|
## 6. 时间控制与排练
|
||||||
|
|
||||||
|
- 计时器由用户显式开始,可暂停、继续和重置;不要进入模式就自动计时。
|
||||||
|
- 同时显示总实际时长、本页实际时长、本页计划、剩余或超时。
|
||||||
|
- 只有每页都有 `minutes` 时,才显示总计划和预计结束时间;部分页缺失时显示横杠。
|
||||||
|
- 有 `section` 时显示章节位置;该章节每页都有计划时长时,再显示章节剩余时间。
|
||||||
|
- 排练模式记录每页实际时长和总时长,本地保留最近 5 次。
|
||||||
|
- 排练结果只做数据汇总,不生成“AI 教练”式评判。
|
||||||
|
|
||||||
|
## 7. 自动翻页
|
||||||
|
|
||||||
|
- 默认关闭,由用户显式开启。
|
||||||
|
- 可设置全局间隔;页面存在 `autoAdvanceSeconds` 时优先使用页面值。
|
||||||
|
- 进入总览、正在圈选、打开设置、浏览器页面不可见、观众屏黑屏/白屏/冻结或观众屏失去同步时,自动倒计暂停。
|
||||||
|
- 离开暂停状态后从原剩余时间继续,不重头计时。
|
||||||
|
- 到尾页后停止,不自动回到首页。
|
||||||
|
|
||||||
|
## 8. 激光笔、圈选与观众屏控制
|
||||||
|
|
||||||
|
- `L` 切换激光笔,红点短暂淡出,不保存。
|
||||||
|
- `C` 切换圈选,圈选保留在当前页;翻页时清除。
|
||||||
|
- `X` 清除当前页标注。
|
||||||
|
- 所有坐标归一化后传给观众屏,不受演讲者屏和观众屏尺寸差异影响。
|
||||||
|
- `B` 切换观众屏黑屏,`W` 切换白屏,`F` 冻结/恢复观众屏。
|
||||||
|
- 冻结时演讲者可以继续翻页,观众屏保留原页;恢复时立即追平演讲者当前页。
|
||||||
|
|
||||||
|
## 9. 观众屏同步与恢复
|
||||||
|
|
||||||
|
模板同时使用窗口 `postMessage`、`BroadcastChannel` 和 `storage` 事件同步。观众页在加载和每次翻页后回传确认,演讲者端显示:
|
||||||
|
|
||||||
|
- **连接中**:观众窗口已打开,尚未收到确认。
|
||||||
|
- **已同步**:观众页确认的页码与演讲者页相同,心跳有效。
|
||||||
|
- **未同步**:确认超时或两端页码不同。
|
||||||
|
- **已冻结**:观众屏故意保留原页,不是同步故障。
|
||||||
|
- **未连接**:观众窗口未打开或已关闭。
|
||||||
|
- **弹窗被拦截**:浏览器阻止打开观众窗口。
|
||||||
|
|
||||||
|
“重新打开观众屏”必须始终可用,并在恢复后立即发送当前页。
|
||||||
|
|
||||||
|
浏览器只能确认观众**页面/窗口**的软件同步状态,不能检测 HDMI、转接器或投影仪线缆是否物理断开。现场仍应目视确认外接屏。
|
||||||
|
|
||||||
|
## 10. 演前检查和快捷键
|
||||||
|
|
||||||
|
“检查”面板检查观众屏同步、弹窗权限、全屏、字体、图片、视频和当前页 16:9。检查结果不得宣称物理外接屏已连接。
|
||||||
|
|
||||||
|
| 键 | 行为 |
|
||||||
|
|---|---|
|
||||||
|
| `L` | 激光笔 |
|
||||||
|
| `C` | 圈选 |
|
||||||
|
| `X` | 清除标记 |
|
||||||
|
| `B` / `W` | 观众屏黑屏 / 白屏 |
|
||||||
|
| `F` | 冻结 / 恢复观众屏 |
|
||||||
|
| `A` | 开启 / 关闭自动翻页 |
|
||||||
|
| `R` | 开始 / 结束排练 |
|
||||||
|
| `?` | 打开快捷键说明 |
|
||||||
|
|
||||||
|
## 11. 能力边界
|
||||||
|
|
||||||
|
这个演讲模式只依赖当前 HTML 和浏览器本地能力,不需要账号或在线服务。不要默认加入:
|
||||||
|
|
||||||
|
- 实时字幕、语音转文字或外部语音模型。
|
||||||
|
- AI 排练教练或对演讲者的评分。
|
||||||
|
- 扫码提问、在线投票或云端互动后台。
|
||||||
|
- 手机遥控或需要服务端中继的跨设备控制。
|
||||||
|
|
||||||
|
## 12. 生成后验证
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node <SKILL_ROOT>/scripts/validate-presenter-mode.mjs path/to/index.html
|
||||||
|
node <SKILL_ROOT>/scripts/validate-presenter-mode.mjs path/to/index.html --target-minutes 30
|
||||||
|
```
|
||||||
|
|
||||||
|
浏览器实测至少包含:进入演讲模式、弹窗允许/拦截、前后翻页、内嵌宫格替换预览与选页返回、首页/尾页、尾页重新开始、观众窗口关闭后的状态变化、重新打开后恢复同步、备注保存、计时、排练记录、自动翻页暂停/恢复、激光笔、圈选、清除、黑屏、白屏、冻结、设置面板与演前检查。
|
||||||
|
|
||||||
|
至少在一组常用尺寸和一组小屏尺寸下检查:当前页/下一页上下排列,两个 iframe 宽高比均为 `16:9`,且没有超出各自容器。
|
||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import vm from 'node:vm';
|
||||||
|
|
||||||
|
const file=process.argv[2];
|
||||||
|
const targetArg=process.argv.find(x=>x.startsWith('--target-minutes='));
|
||||||
|
const targetIndex=process.argv.indexOf('--target-minutes');
|
||||||
|
const targetMinutes=Number(targetArg?.split('=')[1]??(targetIndex>=0?process.argv[targetIndex+1]:NaN));
|
||||||
|
|
||||||
|
if(!file){
|
||||||
|
console.error('Usage: node scripts/validate-presenter-mode.mjs <index.html> [--target-minutes 30]');
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const html=readFileSync(file,'utf8');
|
||||||
|
const source=html.replace(/<!--[\s\S]*?-->/g,'');
|
||||||
|
const errors=[];
|
||||||
|
const warnings=[];
|
||||||
|
|
||||||
|
function extractArray(name){
|
||||||
|
const marker=new RegExp(`(?:const|let|var)\\s+${name}\\s*=`).exec(source);
|
||||||
|
if(!marker)return null;
|
||||||
|
const start=source.indexOf('[',marker.index+marker[0].length);
|
||||||
|
if(start<0)return null;
|
||||||
|
let depth=0,quote='',escaped=false,lineComment=false,blockComment=false;
|
||||||
|
for(let i=start;i<source.length;i++){
|
||||||
|
const ch=source[i],next=source[i+1];
|
||||||
|
if(lineComment){if(ch==='\n')lineComment=false;continue;}
|
||||||
|
if(blockComment){if(ch==='*'&&next==='/'){blockComment=false;i++;}continue;}
|
||||||
|
if(quote){if(escaped){escaped=false;continue;}if(ch==='\\'){escaped=true;continue;}if(ch===quote)quote='';continue;}
|
||||||
|
if(ch==='/'&&next==='/'){lineComment=true;i++;continue;}
|
||||||
|
if(ch==='/'&&next==='*'){blockComment=true;i++;continue;}
|
||||||
|
if(ch==='"'||ch==="'"||ch==='`'){quote=ch;continue;}
|
||||||
|
if(ch==='[')depth++;
|
||||||
|
if(ch===']'&&--depth===0)return source.slice(start,i+1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slideTags=[...source.matchAll(/<section\b(?=[^>]*\bclass="[^"]*\bslide\b[^"]*")[^>]*>/g)].map(m=>m[0]);
|
||||||
|
const slideIds=slideTags.map((tag,i)=>tag.match(/\bdata-slide-id="([^"]+)"/)?.[1]||'');
|
||||||
|
|
||||||
|
slideIds.forEach((id,i)=>{
|
||||||
|
if(!id)errors.push(`Slide ${i+1}: missing data-slide-id.`);
|
||||||
|
else if(!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(id))errors.push(`Slide ${i+1}: data-slide-id="${id}" must be a lowercase semantic slug.`);
|
||||||
|
});
|
||||||
|
const duplicates=slideIds.filter((id,i)=>id&&slideIds.indexOf(id)!==i);
|
||||||
|
if(duplicates.length)errors.push(`Duplicate data-slide-id: ${[...new Set(duplicates)].join(', ')}`);
|
||||||
|
|
||||||
|
let speakerNotes=[];
|
||||||
|
const arraySource=extractArray('SPEAKER_NOTES');
|
||||||
|
if(!arraySource){
|
||||||
|
errors.push('Missing const SPEAKER_NOTES = [...].');
|
||||||
|
}else{
|
||||||
|
try{speakerNotes=vm.runInNewContext(`(${arraySource})`,Object.create(null),{timeout:1000});}
|
||||||
|
catch(error){errors.push(`SPEAKER_NOTES cannot be parsed: ${error.message}`);}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Array.isArray(speakerNotes))errors.push('SPEAKER_NOTES must be an array.');
|
||||||
|
else{
|
||||||
|
if(speakerNotes.length!==slideIds.length)errors.push(`Notes/slides mismatch: ${speakerNotes.length} notes for ${slideIds.length} slides.`);
|
||||||
|
const noteIds=speakerNotes.map(n=>n?.id);
|
||||||
|
const noteDupes=noteIds.filter((id,i)=>id&¬eIds.indexOf(id)!==i);
|
||||||
|
if(noteDupes.length)errors.push(`Duplicate speaker-note id: ${[...new Set(noteDupes)].join(', ')}`);
|
||||||
|
speakerNotes.forEach((note,i)=>{
|
||||||
|
if(!note||typeof note!=='object'){errors.push(`Note ${i+1}: must be an object.`);return;}
|
||||||
|
for(const field of ['id','title','purpose','transition'])if(typeof note[field]!=='string'||!note[field].trim())errors.push(`Note ${i+1}: ${field} is required.`);
|
||||||
|
if(note.minutes!==undefined&¬e.minutes!==null&¬e.minutes!==''&&(!Number.isFinite(Number(note.minutes))||Number(note.minutes)<=0))errors.push(`Note ${i+1}: minutes must be > 0 when provided.`);
|
||||||
|
if(note.autoAdvanceSeconds!==undefined&¬e.autoAdvanceSeconds!==null&¬e.autoAdvanceSeconds!==''&&(!Number.isFinite(Number(note.autoAdvanceSeconds))||Number(note.autoAdvanceSeconds)<=0))errors.push(`Note ${i+1}: autoAdvanceSeconds must be > 0 when provided.`);
|
||||||
|
if(note.section!==undefined&&(typeof note.section!=='string'||!note.section.trim()))errors.push(`Note ${i+1}: section must be non-empty text when provided.`);
|
||||||
|
for(const field of ['cue','interaction','delivery','advance','fallback','pronunciation']){
|
||||||
|
const value=note[field];
|
||||||
|
if(value===undefined||value===null||value==='')continue;
|
||||||
|
const valid=typeof value==='string'?Boolean(value.trim()):Array.isArray(value)&&value.length>0&&value.every(x=>typeof x==='string'&&x.trim());
|
||||||
|
if(!valid)errors.push(`Note ${i+1}: ${field} must be non-empty text or an array of non-empty text.`);
|
||||||
|
}
|
||||||
|
if(!Array.isArray(note.talk)||!note.talk.length)errors.push(`Note ${i+1}: talk must be a non-empty array.`);
|
||||||
|
else{
|
||||||
|
if(note.talk.some(x=>typeof x!=='string'||!x.trim()))errors.push(`Note ${i+1}: every talk item must be non-empty text.`);
|
||||||
|
if(note.talk.length<3||note.talk.length>5)warnings.push(`Note ${i+1} (${note.id}): talk has ${note.talk.length} items; 3-5 is the default.`);
|
||||||
|
}
|
||||||
|
if(slideIds[i]&¬e.id!==slideIds[i])errors.push(`Position ${i+1}: note id "${note.id}" does not match slide id "${slideIds[i]}".`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const runtimeChecks=[
|
||||||
|
['right-bottom presenter entry','id="ppt-presenter-btn"'],
|
||||||
|
['presenter shell','id="ppt-presenter"'],
|
||||||
|
['vertical preview stack','grid-template-rows:minmax(0,1fr) clamp(132px,22vh,210px)'],
|
||||||
|
['16:9 preview viewport','class="ppt-frame-viewport"'],
|
||||||
|
['proportional preview fitter','fitPresenterFrames'],
|
||||||
|
['exact 16:9 fitting','availableHeight*(16/9)'],
|
||||||
|
['overview control','id="ppt-grid"'],
|
||||||
|
['embedded presenter overview','id="ppt-presenter-overview"'],
|
||||||
|
['first-page control','id="ppt-first"'],
|
||||||
|
['last/restart control','id="ppt-last"'],
|
||||||
|
['audience status','id="ppt-sync"'],
|
||||||
|
['audience recovery','id="ppt-reopen"'],
|
||||||
|
['direct window sync','postMessage('],
|
||||||
|
['BroadcastChannel fallback','BroadcastChannel'],
|
||||||
|
['storage fallback','__guizangPptSync'],
|
||||||
|
['per-slide timer','id="ppt-slide-clock"'],
|
||||||
|
['clear timer status','id="ppt-slide-status"'],
|
||||||
|
['rehearsal mode','id="ppt-rehearsal"'],
|
||||||
|
['auto advance','id="ppt-auto-toggle"'],
|
||||||
|
['laser and circle tools','id="ppt-presenter-ink"'],
|
||||||
|
['audience annotation layer','id="ppt-audience-ink"'],
|
||||||
|
['black, white, and freeze controls','setScreenMode'],
|
||||||
|
['preflight checks','id="ppt-preflight"'],
|
||||||
|
['layout presets','data-layout-choice'],
|
||||||
|
['capsule auto-advance switch','class="ppt-switch"'],
|
||||||
|
['styled interval stepper','class="ppt-stepper"'],
|
||||||
|
['shortcut help','openShortcuts'],
|
||||||
|
];
|
||||||
|
for(const [label,needle]of runtimeChecks)if(!html.includes(needle))errors.push(`Presenter runtime missing ${label}.`);
|
||||||
|
if(/\.ppt-preview-stack\{[^}]*grid-template-columns/.test(html))errors.push('Presenter previews must stack vertically; remove grid-template-columns from .ppt-preview-stack.');
|
||||||
|
|
||||||
|
const timedNotes=speakerNotes.filter(n=>Number.isFinite(Number(n?.minutes))&&Number(n.minutes)>0);
|
||||||
|
const planned=timedNotes.reduce((sum,n)=>sum+Number(n.minutes),0);
|
||||||
|
const completePlan=speakerNotes.length>0&&timedNotes.length===speakerNotes.length;
|
||||||
|
if(timedNotes.length&&!completePlan)warnings.push(`Timing is partial: ${timedNotes.length} of ${speakerNotes.length} notes provide minutes; total-budget checks are skipped.`);
|
||||||
|
if(completePlan&&Number.isFinite(targetMinutes)&&targetMinutes>0&&planned>targetMinutes*.9)errors.push(`Planned ${planned.toFixed(1)} min exceeds the 90% speaking budget (${(targetMinutes*.9).toFixed(1)} of ${targetMinutes} min).`);
|
||||||
|
if(completePlan&&Number.isFinite(targetMinutes)&&targetMinutes>0&&planned<targetMinutes*.55)warnings.push(`Planned ${planned.toFixed(1)} min is below 55% of the requested ${targetMinutes} min; confirm the deck is not under-scripted.`);
|
||||||
|
|
||||||
|
warnings.forEach(x=>console.warn(`WARN ${x}`));
|
||||||
|
errors.forEach(x=>console.error(`ERROR ${x}`));
|
||||||
|
console.log(`Presenter validation: ${slideIds.length} slides, ${speakerNotes.length} notes, ${completePlan?planned.toFixed(1):'partial / —'} planned minutes, ${errors.length} errors, ${warnings.length} warnings.`);
|
||||||
|
process.exit(errors.length?1:0);
|
||||||
@@ -3,5 +3,5 @@
|
|||||||
"name": "playwright浏览器自动化操作",
|
"name": "playwright浏览器自动化操作",
|
||||||
"version": "20260605",
|
"version": "20260605",
|
||||||
"keySource": "none",
|
"keySource": "none",
|
||||||
"syncedAt": "2026-08-03T16:01:43Z"
|
"syncedAt": "2026-08-06T16:02:10Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
"sourceId": "next-skills",
|
"sourceId": "next-skills",
|
||||||
"repo": "https://github.com/vercel/next.js.git",
|
"repo": "https://github.com/vercel/next.js.git",
|
||||||
"ref": "canary",
|
"ref": "canary",
|
||||||
"commit": "dcdacfd036e8f1b3da7a38df143da7aa271cf0d5",
|
"commit": "0ae8c72462952df163f1b1e0726641bc5b40dc93",
|
||||||
"adapter": "skill-collection",
|
"adapter": "skill-collection",
|
||||||
"sourcePath": "skills",
|
"sourcePath": "skills",
|
||||||
"syncedAt": "2026-08-03T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ Enable Cache Components on an app and walk it to a passing build. This skill seq
|
|||||||
|
|
||||||
- **App Router project.** Cache Components is an App Router feature; `cacheComponents: true` does nothing for `pages/` routes. If the project has a `pages/` or `src/pages/` tree but no `app/` or `src/app/` tree, stop and tell the user — Pages → App migration is its own project, not part of this skill. A hybrid app (both `pages/` and `app/`) is fine: the flag affects the `app/` routes; `pages/` routes are unaffected and don't need opt-outs.
|
- **App Router project.** Cache Components is an App Router feature; `cacheComponents: true` does nothing for `pages/` routes. If the project has a `pages/` or `src/pages/` tree but no `app/` or `src/app/` tree, stop and tell the user — Pages → App migration is its own project, not part of this skill. A hybrid app (both `pages/` and `app/`) is fine: the flag affects the `app/` routes; `pages/` routes are unaffected and don't need opt-outs.
|
||||||
|
|
||||||
|
- **A resolved app directory.** Locate `next.config.{js,ts,mjs,cjs}` first: that's the project root, and an agent invoked from a subdirectory would otherwise test for `app/` against the wrong `cwd` and find nothing. Look for `app/` and `src/app/` under it, and treat every command and glob in this skill as relative to whichever one exists. If both exist, Next.js builds `app/` and never looks at `src/app/`, so its routes are shadowed and unbuilt — tell the user that and ask which tree to migrate instead of picking one.
|
||||||
|
|
||||||
- **A runnable app.** The whole loop verifies against `next dev` and a browser, so the app has to boot. If it reads a database or required env at import (e.g. an `env.ts` that throws on a missing `DATABASE_URL`), confirm it actually starts — with the real environment, or local data you stand up — before step 1. Adoption can't be verified against an app that won't run.
|
- **A runnable app.** The whole loop verifies against `next dev` and a browser, so the app has to boot. If it reads a database or required env at import (e.g. an `env.ts` that throws on a missing `DATABASE_URL`), confirm it actually starts — with the real environment, or local data you stand up — before step 1. Adoption can't be verified against an app that won't run.
|
||||||
|
|
||||||
- **Next.js 16.3 or later.** That release is where the pieces this skill relies on land: top-level `cacheComponents`, `export const instant`, the dev-overlay instant-navigation validation warnings, and the `cache-components-instant-false` codemod. If `next --version` reports below 16.3, upgrade first:
|
- **Next.js 16.3 or later.** That release is where the pieces this skill relies on land: top-level `cacheComponents`, `export const instant`, the dev-overlay instant-navigation validation warnings, and the `cache-components-instant-false` codemod. If `next --version` reports below 16.3, upgrade first:
|
||||||
@@ -108,19 +110,19 @@ Before invoking the codemod, fix the two classes of blocker it can't.
|
|||||||
|
|
||||||
It shares the `TODO: Cache Components adoption` prefix with the comments the codemod writes, so the check-in grep finds both. Removing the `await connection()` makes the error fire again with its fix cards — the same motion as removing an opt-out in the loop.
|
It shares the `TODO: Cache Components adoption` prefix with the comments the codemod writes, so the check-in grep finds both. Removing the `await connection()` makes the error fire again with its fix cards — the same motion as removing an opt-out in the loop.
|
||||||
|
|
||||||
2. **Incompatible segment configs.** Grep for `^export const (revalidate|dynamic|fetchCache)` across `app/` and translate per the `requires` note above. The codemod does not touch them; leaving them in place fails the build after the codemod.
|
2. **Incompatible segment configs.** Grep for `^export const (revalidate|dynamic|fetchCache)` across the app directory and translate per the `requires` note above. The codemod does not touch them; leaving them in place fails the build after the codemod.
|
||||||
|
|
||||||
The codemod refuses to run on a dirty working tree. Commit or stash unrelated work first, or pass `--force` to let its edits land alongside your WIP. Common false positive: if you recently upgraded Next.js, `package.json` and the lockfile will already be dirty — commit those first.
|
The codemod refuses to run on a dirty working tree. Commit or stash unrelated work first, or pass `--force` to let its edits land alongside your WIP. Common false positive: if you recently upgraded Next.js, `package.json` and the lockfile will already be dirty — commit those first.
|
||||||
|
|
||||||
Use the `@canary` channel, not `@latest`. The `cache-components-instant-false` transform isn't in the stable `@next/codemod` release; `@next/codemod@latest` errors with `Invalid transform choice`.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx @next/codemod@canary cache-components-instant-false ./app
|
npx @next/codemod@latest cache-components-instant-false ./app
|
||||||
```
|
```
|
||||||
|
|
||||||
Inserts `export const instant = false` (with a `// TODO: Cache Components adoption` comment) into every `app/**/{page,layout,default}` file, skipping files that already declare `instant` and any module marked `"use client"` or `"use server"`. Then set `cacheComponents: true`. The TODO comments are the work queue for the loop.
|
Pass the app directory you resolved in [requires](#requires). A wrong path is not an error: it reports `0 ok` and exits `0`, so read the file count and treat zero as a failed run, not an adopted app.
|
||||||
|
|
||||||
If the codemod isn't available (older `@next/codemod`, sandboxed environment, offline run), reproduce it by hand: for every `app/**/{page,layout,default}.{js,jsx,ts,tsx}` that isn't `"use client"` or `"use server"` and doesn't already declare `instant`, insert this after the imports:
|
Inserts `export const instant = false` (with a `// TODO: Cache Components adoption` comment) into every `{page,layout,default}` file under that directory, skipping files that already declare `instant` and any module marked `"use client"` or `"use server"`. Then set `cacheComponents: true`. The TODO comments are the work queue for the loop.
|
||||||
|
|
||||||
|
If the codemod isn't available (older `@next/codemod`, sandboxed environment, offline run), reproduce it by hand: for every `{page,layout,default}.{js,jsx,ts,tsx}` in the app directory that isn't `"use client"` or `"use server"` and doesn't already declare `instant`, insert this after the imports:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// TODO: Cache Components adoption. Refactor this route so this opt-out can be removed.
|
// TODO: Cache Components adoption. Refactor this route so this opt-out can be removed.
|
||||||
@@ -134,7 +136,7 @@ Because the highest opt-out wins, remove them top-down (root layout first, then
|
|||||||
|
|
||||||
Confirm the pre-step with `next build`. The build is the proof, not the codemod run — a shared layout that calls `new Date()` / `Math.random()` directly still fails regardless of the opt-out (see [background](#background)).
|
Confirm the pre-step with `next build`. The build is the proof, not the codemod run — a shared layout that calls `new Date()` / `Math.random()` directly still fails regardless of the opt-out (see [background](#background)).
|
||||||
|
|
||||||
After the build passes, confirm the root layout got an opt-out (`grep -n "export const instant" app/layout.*`). The root layout renders every route, including framework routes like `/_not-found`, so if it was missed, add `export const instant = false` to it by hand.
|
After the build passes, confirm the root layout got an opt-out (`grep -n "export const instant" <app dir>/layout.*`). The root layout renders every route, including framework routes like `/_not-found`, so if it was missed, add `export const instant = false` to it by hand.
|
||||||
|
|
||||||
Synthetic routes like `/_not-found` have no user file — when they block, fix the root layout's opt-out, not the synthetic route. Client Components (`"use client"`) get no opt-out (it's a build error — `E1344` — to export `instant` from them), but they are not a rare blocker. The high-frequency case is a client component in the root layout's nav or header calling `usePathname()`/`useSearchParams()`: it blocks _every_ dynamic route with `blocking-prerender-client-hook`, and static routes pass (the pathname is known at prerender), which masks it until you reach a dynamic segment. It's not an ancestor-data fix — follow the [error's docs page](https://nextjs.org/docs/messages/blocking-prerender-client-hook) for the `<Suspense>` recipe. Only when a client route blocks on _server_ data do you fix that data in its ancestor.
|
Synthetic routes like `/_not-found` have no user file — when they block, fix the root layout's opt-out, not the synthetic route. Client Components (`"use client"`) get no opt-out (it's a build error — `E1344` — to export `instant` from them), but they are not a rare blocker. The high-frequency case is a client component in the root layout's nav or header calling `usePathname()`/`useSearchParams()`: it blocks _every_ dynamic route with `blocking-prerender-client-hook`, and static routes pass (the pathname is known at prerender), which masks it until you reach a dynamic segment. It's not an ancestor-data fix — follow the [error's docs page](https://nextjs.org/docs/messages/blocking-prerender-client-hook) for the `<Suspense>` recipe. Only when a client route blocks on _server_ data do you fix that data in its ancestor.
|
||||||
|
|
||||||
@@ -143,7 +145,7 @@ Synthetic routes like `/_not-found` have no user file — when they block, fix t
|
|||||||
Incremental only. Stop here before starting step 2 — the pre-step is the shippable PR. Talk to the user in their language; don't say "Incremental" or other internal labels; talk about adoption, PRs, and what the app does now. Tell them:
|
Incremental only. Stop here before starting step 2 — the pre-step is the shippable PR. Talk to the user in their language; don't say "Incremental" or other internal labels; talk about adoption, PRs, and what the app does now. Tell them:
|
||||||
|
|
||||||
- What you did: turned on Cache Components, ran the codemod that opts every page and layout out of the new validation (or did it by hand), fixed any blockers the codemod can't (list them), confirmed the build passes.
|
- What you did: turned on Cache Components, ran the codemod that opts every page and layout out of the new validation (or did it by hand), fixed any blockers the codemod can't (list them), confirmed the build passes.
|
||||||
- What changed: every page and layout in `app/` now exports `instant = false` with a `// TODO: Cache Components adoption` comment, except client components and any that already had an `instant` export.
|
- What changed: every page and layout in the app directory now exports `instant = false` with a `// TODO: Cache Components adoption` comment, except client components and any that already had an `instant` export.
|
||||||
- What to sanity-check: the diff is mostly mechanical (new exports + comments). The build passes. Routes still behave exactly as they did before — the opt-outs preserve current behavior; no rendering changes yet.
|
- What to sanity-check: the diff is mostly mechanical (new exports + comments). The build passes. Routes still behave exactly as they did before — the opt-outs preserve current behavior; no rendering changes yet.
|
||||||
- The question: "Want to open this as its own PR before we start adopting Cache Components route by route? Or keep going on this branch?" Wait for the answer.
|
- The question: "Want to open this as its own PR before we start adopting Cache Components route by route? Or keep going on this branch?" Wait for the answer.
|
||||||
|
|
||||||
@@ -190,6 +192,7 @@ Per route:
|
|||||||
- The [three blocker classes from background](#background) often get missed when fixing in place. Caching a downstream fetch (`getThing(id)`) doesn't clear an `await params` at the top of the page body — push the param promise into the `<Suspense>`-wrapped child.
|
- The [three blocker classes from background](#background) often get missed when fixing in place. Caching a downstream fetch (`getThing(id)`) doesn't clear an `await params` at the top of the page body — push the param promise into the `<Suspense>`-wrapped child.
|
||||||
- Ambiguous calls are user check-ins, not agent judgment. When you're not sure which fix fits, the blocking code looks security-sensitive, or the user might want to keep the route blocking on purpose — read [references/per-page-decisions.md](./references/per-page-decisions.md) before editing. Show the route while you ask: the `next-dev-loop` session runs the browser headed, so drive to the page and leave it on screen so the user is looking at the thing they're deciding about, with a screenshot as the fallback when a headed browser isn't possible. "Should this stay blocking?" is much easier to answer while looking at the page than at a file path.
|
- Ambiguous calls are user check-ins, not agent judgment. When you're not sure which fix fits, the blocking code looks security-sensitive, or the user might want to keep the route blocking on purpose — read [references/per-page-decisions.md](./references/per-page-decisions.md) before editing. Show the route while you ask: the `next-dev-loop` session runs the browser headed, so drive to the page and leave it on screen so the user is looking at the thing they're deciding about, with a screenshot as the fallback when a headed browser isn't possible. "Should this stay blocking?" is much easier to answer while looking at the page than at a file path.
|
||||||
- Don't narrate the refactor with comments. The only comment the codemod (or you) should leave is `// TODO: Cache Components adoption` on opt-outs, and the user's existing comments. Don't annotate every `<Suspense>` boundary or `"use cache"` call with what it does — the code says that. Drop a comment only when the _why_ isn't clear from the code (e.g. a deliberate Block with a reason).
|
- Don't narrate the refactor with comments. The only comment the codemod (or you) should leave is `// TODO: Cache Components adoption` on opt-outs, and the user's existing comments. Don't annotate every `<Suspense>` boundary or `"use cache"` call with what it does — the code says that. Drop a comment only when the _why_ isn't clear from the code (e.g. a deliberate Block with a reason).
|
||||||
|
- For many routes with the same mechanical fix, verify one representative route first. Then batch disjoint route groups using the same recipe, and run the shared build and browser checks together.
|
||||||
|
|
||||||
Keep a todo list of the feature's routes. When every route in the feature is clean, move to step 3.
|
Keep a todo list of the feature's routes. When every route in the feature is clean, move to step 3.
|
||||||
|
|
||||||
@@ -200,6 +203,7 @@ Checklist before checking in with the user:
|
|||||||
- `next build` completes without blocking-route errors.
|
- `next build` completes without blocking-route errors.
|
||||||
- No bare TODOs in the feature: `grep -rn "TODO: Cache Components adoption"` finds both the codemod's opt-out comments and the sync-IO unblocks from the pre-step. Any `instant = false` left behind is a deliberate, documented Block — comment rewritten to a reason (see [references/per-page-decisions.md](./references/per-page-decisions.md) → "when to leave a Block in place"). Any `await connection()` left behind has been reviewed and kept on purpose, not left over from the pre-step.
|
- No bare TODOs in the feature: `grep -rn "TODO: Cache Components adoption"` finds both the codemod's opt-out comments and the sync-IO unblocks from the pre-step. Any `instant = false` left behind is a deliberate, documented Block — comment rewritten to a reason (see [references/per-page-decisions.md](./references/per-page-decisions.md) → "when to leave a Block in place"). Any `await connection()` left behind has been reviewed and kept on purpose, not left over from the pre-step.
|
||||||
- Each route visited in the browser: confirm the static shell renders first and every `<Suspense>` fallback resolves to its real content. Capture both states if you can — the fallback (mid-stream) and the final paint — so you have a streaming-experience demo to show the user. Throttle the network in the browser if streaming is too fast to observe.
|
- Each route visited in the browser: confirm the static shell renders first and every `<Suspense>` fallback resolves to its real content. Capture both states if you can — the fallback (mid-stream) and the final paint — so you have a streaming-experience demo to show the user. Throttle the network in the browser if streaming is too fast to observe.
|
||||||
|
- If runtime verification fails, reproduce the same route on the pre-adoption branch or with its opt-out restored. A failure that already exists is an environment or data problem, not an adoption regression.
|
||||||
|
|
||||||
Then check in with the user. Same rule as the pre-step: speak their language. Don't say "feature-by-feature loop" or other internal labels; talk about the feature you adopted and what the user will see.
|
Then check in with the user. Same rule as the pre-step: speak their language. Don't say "feature-by-feature loop" or other internal labels; talk about the feature you adopted and what the user will see.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ After the Cache Components build is clean, not before. While the app is mid-adop
|
|||||||
|
|
||||||
Reuse the [`next-dev-loop`](https://github.com/vercel/next.js/tree/canary/skills/next-dev-loop) preflight (Turbopack), then add one job. On a webpack app, drive a browser directly with `agent-browser` or Playwright instead. You lose the `/_next/mcp` cross-checks, not the insights, which still show in the overlay and the dev log.
|
Reuse the [`next-dev-loop`](https://github.com/vercel/next.js/tree/canary/skills/next-dev-loop) preflight (Turbopack), then add one job. On a webpack app, drive a browser directly with `agent-browser` or Playwright instead. You lose the `/_next/mcp` cross-checks, not the insights, which still show in the overlay and the dev log.
|
||||||
|
|
||||||
1. Build a route queue from the last build's route table or the `app/` tree.
|
1. Build a route queue from the last build's route table or the app directory.
|
||||||
2. Load each route in `next dev` with a browser. A refresh or a link click both work, and validation simulates both the page-load and client-navigation cases on that load, so you do not need to click through every link by hand. Dynamic params are checked against the real values you visit, so hit a concrete `[slug]`, not the pattern.
|
2. Load each route in `next dev` with a browser. A refresh or a link click both work, and validation simulates both the page-load and client-navigation cases on that load, so you do not need to click through every link by hand. Dynamic params are checked against the real values you visit, so hit a concrete `[slug]`, not the pattern.
|
||||||
3. Watch the dev log and the Insights tab. The dev log is the greppable record, one `Error: Route "...": Next.js encountered ...` line per insight with its `docs/messages/<slug>` link, and it reads the same on Turbopack and webpack. The Insights tab is amber and appears only once an insight fires, so a route with no tab is clean. Through `next-dev-loop`'s `/_next/mcp`, these come from `get_errors` and the overlay, not `get_request_insights`, which is the performance recorder and reports nothing here.
|
3. Watch the dev log and the Insights tab. The dev log is the greppable record, one `Error: Route "...": Next.js encountered ...` line per insight with its `docs/messages/<slug>` link, and it reads the same on Turbopack and webpack. The Insights tab is amber and appears only once an insight fires, so a route with no tab is clean. Through `next-dev-loop`'s `/_next/mcp`, these come from `get_errors` and the overlay, not `get_request_insights`, which is the performance recorder and reports nothing here.
|
||||||
4. Open the linked page for each distinct insight and apply its fix (usually pull a `<Suspense>` boundary down to the read). Reload to confirm it clears.
|
4. Open the linked page for each distinct insight and apply its fix (usually pull a `<Suspense>` boundary down to the read). Reload to confirm it clears.
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ command, platform, or env var below as a requirement.
|
|||||||
production-like build and must not be able to pass vacuously. Stand the loop
|
production-like build and must not be able to pass vacuously. Stand the loop
|
||||||
up once; every later optimization is then verifiable by construction. The
|
up once; every later optimization is then verifiable by construction. The
|
||||||
loop is the deliverable, not any one route.
|
loop is the deliverable, not any one route.
|
||||||
- **The mechanism: `@next/playwright` `instant()`.** This skill locks with
|
- **The mechanism: `@next/playwright` `instant()`.** This skill uses
|
||||||
[`instant()`](https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#testing-instant-navigation):
|
[`instant()`](https://nextjs.org/docs/app/guides/instant-navigation#prevent-regressions-with-e2e-tests)
|
||||||
a ruler, not a stopwatch (phase A). It comes from
|
as a ruler, not a stopwatch (phase A). It comes from
|
||||||
`@next/playwright` (installed alongside `@playwright/test`, on the same
|
`@next/playwright` (installed alongside `@playwright/test`, on the same
|
||||||
release line as `next`), so it isn't tied to any host. Keep it. Timing a
|
release line as `next`), so it isn't tied to any host. Keep it. Timing a
|
||||||
navigation by hand is too flaky to trust, and is the failure mode this skill
|
navigation by hand is too flaky to trust, and is the failure mode this skill
|
||||||
@@ -77,11 +77,11 @@ trustworthy.
|
|||||||
|
|
||||||
## Reporting to the user
|
## Reporting to the user
|
||||||
|
|
||||||
This loop is meant to run unattended — ideally across many navigations in one
|
This loop is meant to run unattended, so it doesn't stop to ask between steps.
|
||||||
pass — so it doesn't stop to ask after each route. What matters is how you word
|
Work the navigation the user named, finish it, and stop. What matters is how you
|
||||||
and present the results, not how often you interrupt. The mechanics below — the
|
word and present the results, not how often you interrupt. The mechanics below —
|
||||||
rig, RED, GREEN, the gates — are your scaffolding; the user never needs to hear
|
the rig, RED, GREEN, the gates — are your scaffolding; the user never needs to
|
||||||
those words.
|
hear those words.
|
||||||
|
|
||||||
- **Speak their language.** Describe the gap and the result in terms of what the
|
- **Speak their language.** Describe the gap and the result in terms of what the
|
||||||
user sees: "navigating to the dashboard waited on the charts query before
|
user sees: "navigating to the dashboard waited on the charts query before
|
||||||
@@ -91,8 +91,9 @@ those words.
|
|||||||
before/after screenshots) so the user watches the shell commit immediately and
|
before/after screenshots) so the user watches the shell commit immediately and
|
||||||
the data stream in, rather than reading a claim. Identical before and after
|
the data stream in, rather than reading a claim. Identical before and after
|
||||||
means the fix did nothing — roll it back.
|
means the fix did nothing — roll it back.
|
||||||
- **Present a run as a list of results,** one line per navigation — which route,
|
- **Present a run as a list of results the user can click through** — one line
|
||||||
what's now instant, what streams — not a transcript of the loop.
|
per navigation: the route, what commits instantly, and what streams in — not a
|
||||||
|
transcript of the loop.
|
||||||
- **Only surface a question for a genuine fork:** a fix that would change
|
- **Only surface a question for a genuine fork:** a fix that would change
|
||||||
behavior, a security-sensitive read, or a route that's dynamic by design (a
|
behavior, a security-sensitive read, or a route that's dynamic by design (a
|
||||||
runtime-prefetch candidate, not a shell to grow). A clean instant fix is not a
|
runtime-prefetch candidate, not a shell to grow). A clean instant fix is not a
|
||||||
@@ -220,6 +221,10 @@ Wrap the same navigation in `instant()`; assert the shell commits under the
|
|||||||
lock. A RED here is the gap. **This is the test that ships**
|
lock. A RED here is the gap. **This is the test that ships**
|
||||||
(`test-template.md`).
|
(`test-template.md`).
|
||||||
|
|
||||||
|
Prefer the self-validating variant when the route has deferred content. If the
|
||||||
|
route cannot build while blocked, or a cookie/session read stays GREEN, use the
|
||||||
|
RED recipes in `reference/red-test-robustness.md`.
|
||||||
|
|
||||||
> **C-gate: do not start optimizing until the RED is verified trustworthy.** A
|
> **C-gate: do not start optimizing until the RED is verified trustworthy.** A
|
||||||
> RED that is red for the wrong reason sends you optimizing a route that was
|
> RED that is red for the wrong reason sends you optimizing a route that was
|
||||||
> never broken.
|
> never broken.
|
||||||
@@ -330,6 +335,9 @@ layout: it duplicates structure, drifts as the page changes, and pulls the
|
|||||||
design back toward a single coarse boundary. Reusing the component's own
|
design back toward a single coarse boundary. Reusing the component's own
|
||||||
skeleton also keeps the prefetched shell consistent with the loaded UI.
|
skeleton also keeps the prefetched shell consistent with the loaded UI.
|
||||||
|
|
||||||
|
See: [Streaming](https://nextjs.org/docs/app/guides/streaming#push-dynamic-access-down)
|
||||||
|
and [loading states](https://nextjs.org/docs/app/guides/instant-navigation#iterate-on-loading-states).
|
||||||
|
|
||||||
Exception: if the deferred component renders `null` for some users (for
|
Exception: if the deferred component renders `null` for some users (for
|
||||||
example, a flag-gated control), `fallback={null}` is correct, since a skeleton
|
example, a flag-gated control), `fallback={null}` is correct, since a skeleton
|
||||||
would flash and then collapse.
|
would flash and then collapse.
|
||||||
@@ -375,6 +383,9 @@ now commits instantly. Verify:
|
|||||||
- **Client state survives.** Because the layout UI is hoisted into the stable
|
- **Client state survives.** Because the layout UI is hoisted into the stable
|
||||||
shell rather than swapped on resolve, open menus, scroll position, focus,
|
shell rather than swapped on resolve, open menus, scroll position, focus,
|
||||||
and input state persist across the stream.
|
and input state persist across the stream.
|
||||||
|
- **Pre-existing failures stay separate.** If the route errors after the
|
||||||
|
change, reproduce it on the base branch. The same failure there is an
|
||||||
|
environment or data problem, not an optimizer regression.
|
||||||
|
|
||||||
If anything other than whether the route is instant changed, reduce the refactor.
|
If anything other than whether the route is instant changed, reduce the refactor.
|
||||||
|
|
||||||
@@ -395,6 +406,7 @@ PR-specific items:
|
|||||||
- [ ] **Parity confirmed (E)**: same content, redirects, and state.
|
- [ ] **Parity confirmed (E)**: same content, redirects, and state.
|
||||||
- [ ] **Existing loading UI reused (D1)**: no new page-mirroring skeleton.
|
- [ ] **Existing loading UI reused (D1)**: no new page-mirroring skeleton.
|
||||||
- [ ] **Shell matches the real render at desktop and mobile widths (D2)**.
|
- [ ] **Shell matches the real render at desktop and mobile widths (D2)**.
|
||||||
|
- [ ] **Baseline removed**: only the locked test from C remains.
|
||||||
|
|
||||||
**Stop condition for the whole workflow:** the locked test from C is GREEN on
|
**Stop condition for the whole workflow:** the locked test from C is GREEN on
|
||||||
the rig, the differential (F) holds, and every item above is checked. Until all
|
the rig, the differential (F) holds, and every item above is checked. Until all
|
||||||
|
|||||||
+17
@@ -16,6 +16,21 @@ spend a few minutes verifying the RED is trustworthy first.
|
|||||||
|
|
||||||
Everything below serves answering that question honestly.
|
Everything below serves answering that question honestly.
|
||||||
|
|
||||||
|
## When the blocked route will not build
|
||||||
|
|
||||||
|
With Cache Components, a top-level blocking read can fail the build before the
|
||||||
|
test can produce a RED. Add `export const instant = false` to the target route
|
||||||
|
as part of the temporary RED scaffold. It lets the known blocker build without
|
||||||
|
making the navigation instant. Remove the opt-out with the fix, and include it
|
||||||
|
when reverting the fix for the differential.
|
||||||
|
|
||||||
|
## Cookie and session reads are not lock probes
|
||||||
|
|
||||||
|
Do not manufacture a RED with `cookies()` or a session read alone. The testing
|
||||||
|
lock restricts the navigation to its shell; it does not make request cookies
|
||||||
|
unavailable. Use the route's real blocking uncached data, and prefer the
|
||||||
|
self-validating test variant when deferred content exists.
|
||||||
|
|
||||||
## The robustness checklist (all must hold)
|
## The robustness checklist (all must hold)
|
||||||
|
|
||||||
1. **Red on baseline**: fails on the unfixed route.
|
1. **Red on baseline**: fails on the unfixed route.
|
||||||
@@ -80,6 +95,8 @@ sees the differential knows the test measures the property.
|
|||||||
|
|
||||||
## `instant()` is not a stopwatch
|
## `instant()` is not a stopwatch
|
||||||
|
|
||||||
|
See: [`instant()`](https://nextjs.org/docs/app/guides/instant-navigation#prevent-regressions-with-e2e-tests).
|
||||||
|
|
||||||
The test does not measure how fast a navigation is. `instant()` gates dynamic data so the content
|
The test does not measure how fast a navigation is. `instant()` gates dynamic data so the content
|
||||||
of the static shell can be asserted; the signal is presence, not speed. Under the lock, an instant
|
of the static shell can be asserted; the signal is presence, not speed. Under the lock, an instant
|
||||||
route's shell is present, and a blocking route's content never commits, regardless of wait time.
|
route's shell is present, and a blocking route's content never commits, regardless of wait time.
|
||||||
|
|||||||
+3
-1
@@ -62,7 +62,9 @@ build/run obstacles, accumulated as you first hit them).
|
|||||||
`/healthz` route or a response header, or fall back to polling the deploy
|
`/healthz` route or a response header, or fall back to polling the deploy
|
||||||
platform's API for the deployment whose `commitSha === HEAD`. Record the
|
platform's API for the deployment whose `commitSha === HEAD`. Record the
|
||||||
chosen mechanism. For a local `build && start` rig the artifact is the one
|
chosen mechanism. For a local `build && start` rig the artifact is the one
|
||||||
freshly built, so no probe is needed.
|
freshly built, so no SHA probe is needed. Record the port, stop the previous
|
||||||
|
server before starting, fail the loop on `EADDRINUSE`, and verify the newly
|
||||||
|
started process owns the port before running the test.
|
||||||
|
|
||||||
## The file: copy, fill, commit as `instant-nav.rig.md`
|
## The file: copy, fill, commit as `instant-nav.rig.md`
|
||||||
|
|
||||||
|
|||||||
+2
@@ -1,5 +1,7 @@
|
|||||||
# Test template: the instant() guard
|
# Test template: the instant() guard
|
||||||
|
|
||||||
|
See: [`@next/playwright` `instant()`](https://nextjs.org/docs/app/guides/instant-navigation#prevent-regressions-with-e2e-tests).
|
||||||
|
|
||||||
Ship one test per navigation type you are guarding: under `instant()`, assert that the
|
Ship one test per navigation type you are guarding: under `instant()`, assert that the
|
||||||
destination's static shell appears. `instant()` gates dynamic data, so a correctly instant route
|
destination's static shell appears. `instant()` gates dynamic data, so a correctly instant route
|
||||||
commits its shell under the lock and a blocking route does not. `instant()` is a ruler, not a
|
commits its shell under the lock and a blocking route does not. `instant()` is a ruler, not a
|
||||||
|
|||||||
@@ -77,7 +77,9 @@ Once per session, confirm both views are live.
|
|||||||
the next `open` restores it.
|
the next `open` restores it.
|
||||||
|
|
||||||
2. Probe `/_next/mcp` (`tools/list`) — confirm it's reachable and
|
2. Probe `/_next/mcp` (`tools/list`) — confirm it's reachable and
|
||||||
lists `get_compilation_issues`:
|
lists `get_compilation_issues`. First read the port off the
|
||||||
|
`next dev` banner; if it isn't 3000, set
|
||||||
|
`NEXT_MCP_URL=http://localhost:<port>/_next/mcp` before probing:
|
||||||
- Unreachable → either `next dev` isn't running, or Next.js is
|
- Unreachable → either `next dev` isn't running, or Next.js is
|
||||||
below 16.3. Check `package.json` to disambiguate, then refuse.
|
below 16.3. Check `package.json` to disambiguate, then refuse.
|
||||||
- `get_compilation_issues` not in the list → Next.js below 16.3.
|
- `get_compilation_issues` not in the list → Next.js below 16.3.
|
||||||
@@ -144,8 +146,6 @@ manual rather than from memory.
|
|||||||
- `/_next/mcp` replies are SSE — read the JSON off the `data:` line
|
- `/_next/mcp` replies are SSE — read the JSON off the `data:` line
|
||||||
with `sed -n 's/^data: //p'` (a plain `sed 's/^data: //'` leaves the
|
with `sed -n 's/^data: //p'` (a plain `sed 's/^data: //'` leaves the
|
||||||
`event:` line and the parse fails).
|
`event:` line and the parse fails).
|
||||||
- Non-3000 dev server: read the `next dev` banner; set
|
|
||||||
`NEXT_MCP_URL=http://localhost:<port>/_next/mcp`.
|
|
||||||
- `get_errors` and `get_page_metadata` need at least one navigation
|
- `get_errors` and `get_page_metadata` need at least one navigation
|
||||||
to populate.
|
to populate.
|
||||||
|
|
||||||
|
|||||||
+13
-7
@@ -61,16 +61,24 @@ Enumerate the prefetch sites across the whole source tree, not only `app/` — t
|
|||||||
Then, for each one:
|
Then, for each one:
|
||||||
|
|
||||||
1. **Click each `<Link>` in `next dev`.** The insight fires at navigation time, not when the link prefetches, so a link sitting in the viewport won't trip it — you have to navigate through it. This click is _verification_: it confirms the insight fires before you adopt and clears after. Imperative `router.prefetch()` sites have no equivalent insight, so audit them from source and verify them in production ([step 4](#step-4-verify)). Without a browser, skip the click and adopt from [`link-prefetch-partial`](https://nextjs.org/docs/messages/instant-link-prefetch-partial) and the audit table below — the destination's structure tells you the row, and type-check gates the edit — then leave the live confirmation for the hand-off.
|
1. **Click each `<Link>` in `next dev`.** The insight fires at navigation time, not when the link prefetches, so a link sitting in the viewport won't trip it — you have to navigate through it. This click is _verification_: it confirms the insight fires before you adopt and clears after. Imperative `router.prefetch()` sites have no equivalent insight, so audit them from source and verify them in production ([step 4](#step-4-verify)). Without a browser, skip the click and adopt from [`link-prefetch-partial`](https://nextjs.org/docs/messages/instant-link-prefetch-partial) and the audit table below — the destination's structure tells you the row, and type-check gates the edit — then leave the live confirmation for the hand-off.
|
||||||
2. **Adopt the destination.** Add `export const prefetch = 'partial'`. That clears the insight for every link pointing at it. If the route reads URL data (`params`, `searchParams`), the default link still warms only its skeleton (the guide's [URL data](https://nextjs.org/docs/app/guides/adopting-partial-prefetching#url-data) section), so it's a runtime-prefetch candidate for step 5, not a finished adoption. Keep `prefetch={true}` on its links and mark the route:
|
2. **Adopt the destination.** Add the temporary route config with a link to the migration guide. That clears the insight for every link pointing at it:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// See: https://nextjs.org/docs/app/guides/adopting-partial-prefetching
|
||||||
|
export const prefetch = 'partial'
|
||||||
|
```
|
||||||
|
|
||||||
|
If the route reads URL data (`params`, `searchParams`), the default link still warms only its skeleton (the guide's [URL data](https://nextjs.org/docs/app/guides/adopting-partial-prefetching#url-data) section), so it's a runtime-prefetch candidate for step 5, not a finished adoption. Keep `prefetch={true}` on its links and mark the route:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// TODO(runtime-prefetch): assess with the user whether URL data should resolve before click.
|
// TODO(runtime-prefetch): assess with the user whether URL data should resolve before click.
|
||||||
|
// See: https://nextjs.org/docs/app/guides/runtime-prefetching
|
||||||
export const prefetch = 'partial'
|
export const prefetch = 'partial'
|
||||||
```
|
```
|
||||||
|
|
||||||
Use that exact prefix so step 5 can grep them back. Don't cache or decide anything for these routes now.
|
Use that exact prefix so step 5 can grep them back. Don't cache or decide anything for these routes now.
|
||||||
|
|
||||||
3. **Preserve what that prefetch delivered.** The guide's [audit table](https://nextjs.org/docs/app/guides/adopting-partial-prefetching#auditing-link-prefetchtrue-calls) is the canonical decision — fetch it and apply the matching row rather than re-deriving it. Caching uncached content is the judgment call in that table: trace where the data comes from and what freshness and revalidation it needs, per the [`use cache`](https://nextjs.org/docs/app/api-reference/directives/use-cache) docs, and ask the user when the answer isn't clear-cut. The URL-data routes you marked in the previous item wait for step 5.
|
3. **Preserve what that prefetch delivered.** The guide's [audit table](https://nextjs.org/docs/app/guides/adopting-partial-prefetching#auditing-link-prefetchtrue-calls) is the canonical decision — fetch it and apply the matching row. Caching uncached content is the judgment call in that table: trace where the data comes from and what freshness and revalidation it needs, per the [`use cache`](https://nextjs.org/docs/app/api-reference/directives/use-cache) docs, and ask the user when the answer isn't clear-cut. The URL-data routes you marked in the previous item wait for step 5.
|
||||||
|
|
||||||
> **If you add `use cache`, verify under `next start`, not only the build.** A `cookies()`/`headers()`/session read anywhere in the cached call tree throws at request time while `next build` passes clean. See [`use cache`](https://nextjs.org/docs/app/api-reference/directives/use-cache).
|
> **If you add `use cache`, verify under `next start`, not only the build.** A `cookies()`/`headers()`/session read anywhere in the cached call tree throws at request time while `next build` passes clean. See [`use cache`](https://nextjs.org/docs/app/api-reference/directives/use-cache).
|
||||||
|
|
||||||
@@ -79,15 +87,13 @@ Then, for each one:
|
|||||||
Once every audited destination has `prefetch = 'partial'`, finish in two moves.
|
Once every audited destination has `prefetch = 'partial'`, finish in two moves.
|
||||||
|
|
||||||
1. **Enable the flag globally.** Set `partialPrefetching: true` in `next.config.ts` (alongside `cacheComponents: true`). Every route is adopted now, so every link is good.
|
1. **Enable the flag globally.** Set `partialPrefetching: true` in `next.config.ts` (alongside `cacheComponents: true`). Every route is adopted now, so every link is good.
|
||||||
2. **Strip the redundant `prefetch = 'partial'` exports.** Run the first-party `remove-partial-prefetch` codemod rather than a text find-and-replace. It removes only `export const prefetch = 'partial'` and leaves other values such as `prefetch = 'force-disabled'` in place, along with your `TODO(runtime-prefetch)` markers, which wait for step 5.
|
2. **Strip the redundant `prefetch = 'partial'` exports.** Run the first-party `remove-partial-prefetch` codemod rather than a text find-and-replace. It removes only `export const prefetch = 'partial'` and its generated Partial Prefetching guide comment. It leaves other values such as `prefetch = 'force-disabled'` in place, along with your `TODO(runtime-prefetch)` markers and their Runtime Prefetching guide links, which wait for step 5.
|
||||||
|
|
||||||
Use the `@canary` channel, not `@latest`. The `remove-partial-prefetch` transform isn't in the stable `@next/codemod` release yet, and `@next/codemod@latest` errors with `Invalid transform choice`.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx @next/codemod@canary remove-partial-prefetch ./app
|
npx @next/codemod@latest remove-partial-prefetch ./app
|
||||||
```
|
```
|
||||||
|
|
||||||
The codemod refuses to run on a dirty working tree. Commit or stash unrelated work first, or pass `--force` to let its edits land alongside your WIP. If the codemod isn't available (older `@next/codemod`, sandboxed environment, offline run), reproduce it by hand by removing `export const prefetch = 'partial'` from every `app/**/{page,layout}.{js,jsx,ts,tsx}` — leave other `prefetch` values in place, and leave the `TODO(runtime-prefetch)` markers where they are. Don't hand-edit when the codemod can run.
|
The codemod refuses to run on a dirty working tree. Commit or stash unrelated work first, or pass `--force` to let its edits land alongside your WIP. If the codemod isn't available (older `@next/codemod`, sandboxed environment, offline run), reproduce it by hand by removing `export const prefetch = 'partial'` and its generated Partial Prefetching guide comment from every `app/**/{page,layout}.{js,jsx,ts,tsx}` — leave other `prefetch` values in place, and leave the `TODO(runtime-prefetch)` markers and Runtime Prefetching guide links where they are. Don't hand-edit when the codemod can run.
|
||||||
|
|
||||||
## step 3: sweep for URL-data insights (after enabling)
|
## step 3: sweep for URL-data insights (after enabling)
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ And that depth is a **direction of travel, not a fixed checklist.** PPT Master's
|
|||||||
|
|
||||||
In form, it's a workflow (a "skill") that runs inside any agent-capable AI tool: tell it in chat — "make a deck from this PDF" — and it runs the workflow on your machine and exports a natively editable `.pptx`. No coding on your side; you do exactly three things — install Python, install an AI tool, drop in your material.
|
In form, it's a workflow (a "skill") that runs inside any agent-capable AI tool: tell it in chat — "make a deck from this PDF" — and it runs the workflow on your machine and exports a natively editable `.pptx`. No coding on your side; you do exactly three things — install Python, install an AI tool, drop in your material.
|
||||||
|
|
||||||
Generating a new deck from source documents is the main pipeline, but not the only route. PPT Master can also distill reusable brand / layout / deck templates from your references, fill an existing `.pptx` with new content while preserving its design, and add native transitions, animations, and narration to a finished deck — each route with an explicit contract for what gets preserved.
|
Generating a new deck from source documents is the main pipeline, but not the only route. PPT Master can also distill reusable brand / style / layout / deck templates from your references, fill an existing `.pptx` with new content while preserving its design, and add native transitions, animations, and narration to a finished deck — each route with an explicit contract for what gets preserved.
|
||||||
|
|
||||||
On top of that native depth, this form comes with three promises:
|
On top of that native depth, this form comes with three promises:
|
||||||
|
|
||||||
@@ -316,7 +316,15 @@ AI: Sure. Let's confirm the design spec:
|
|||||||
|
|
||||||
The AI handles everything — content analysis, visual design, SVG generation, and PPTX export.
|
The AI handles everything — content analysis, visual design, SVG generation, and PPTX export.
|
||||||
|
|
||||||
> **Output:** The SVG pipeline has one PPTX converter: it reads `svg_output/` and writes a directly editable native DrawingML deck to `exports/<name>_<timestamp>.pptx`. The default Generate flow runs `finalize_svg.py` and produces self-contained previews in `svg_final/`; PowerPoint's manual **Convert to Shape** command is outside the supported contract. Explicit [quick generation](./skills/ppt-master/workflows/profiles/quick-generate.md) skips the Strategist analysis and the confirmation stage: whatever you state explicitly is followed, and whatever you leave unspecified the agent decides directly instead of asking. It still converts sources, researches factual gaps, and prepares required images, icons, formulas, and resource manifests when needed, skips `design_spec.md`, `spec_lock.md`, and `finalize_svg.py`, then hand-authors the SVG pages, passes the lockless Quick final quality check, and exports the final PPTX. Ordinary export capabilities remain available as needed, including native chart/table replacement, notes, motion, narration, and diagnostics; notes, custom object animation, and narration start off, and the agent may enable them when the request or deck needs them. A default-path Quick export writes the normal postflight report and snapshots `svg_output/` to `backup/<timestamp>/svg_output/`; an explicit output path keeps the ordinary no-backup behavior. By default charts and tables export as individually editable SVG-derived DrawingML shapes, which prioritize cross-app visual consistency. Pass `--native-charts-and-tables` to replace eligible groups with PowerPoint-native Chart/Table objects backed by data, which provide **Edit Data** and object-specific controls but may render differently across apps; this variant is saved as `exports/<name>_<timestamp>_native_charts_tables.pptx`. Both chart/table export variants are editable—the distinction is the PowerPoint object model, not editability itself.
|
**Quick generation (skip the confirmation round trip):** say so explicitly and the AI goes straight to authoring and export.
|
||||||
|
|
||||||
|
```
|
||||||
|
You: Quickly generate a 5-page deck from projects/q3-report/sources/report.pdf — no need to confirm with me
|
||||||
|
```
|
||||||
|
|
||||||
|
Whatever you state explicitly is followed; whatever you leave unspecified the agent decides on its own instead of asking. It still converts sources, fills factual gaps, applies the shared visual baseline, and uses images/icons/native shapes/charts/tables/formulas as needed — it drops interaction and durable planning, not presentation capability. It is one-pass and non-resumable, and there is no `svg_final/` preview. Full guide → [Quick mode](./docs/getting-started.md#quick-mode).
|
||||||
|
|
||||||
|
> **Output:** The SVG pipeline has one PPTX converter: it reads `svg_output/` and writes a directly editable native DrawingML deck to `exports/<name>_<timestamp>.pptx`. The default Generate flow runs `finalize_svg.py` and produces self-contained previews in `svg_final/`; PowerPoint's manual **Convert to Shape** command is outside the supported contract. Explicit [quick generation](./skills/ppt-master/workflows/profiles/quick-generate.md) skips Strategist, confirmation, `design_spec.md`, `spec_lock.md`, and `finalize_svg.py`: whatever you state explicitly is followed, and whatever you leave unspecified the agent decides directly in one active context. It still converts sources, researches factual gaps, applies shared mode/style/aesthetic guidance, prepares required images/icons/formulas, considers native shapes and data visualizations, hand-authors SVG, passes the lockless Quick final quality check, and exports the final PPTX. It writes no substitute plan and cannot resume after context loss. Ordinary export capabilities remain available as needed, including native chart/table replacement, notes, motion, narration, and diagnostics; notes, custom object animation, and narration start off, and the agent may enable them when the request or deck needs them. A default-path Quick export writes the normal postflight report and snapshots `svg_output/` to `backup/<timestamp>/svg_output/`; an explicit output path keeps the ordinary no-backup behavior. By default charts and tables export as individually editable SVG-derived DrawingML shapes, which prioritize cross-app visual consistency. Pass `--native-charts-and-tables` to replace eligible groups with PowerPoint-native Chart/Table objects backed by data, which provide **Edit Data** and object-specific controls but may render differently across apps; this variant is saved as `exports/<name>_<timestamp>_native_charts_tables.pptx`. Both chart/table export variants are editable—the distinction is the PowerPoint object model, not editability itself.
|
||||||
|
|
||||||
> **Already have a `.pptx` you want to reuse?** Hand the AI that deck plus your material and ask it to "fill this deck with the new content" — it fills text, table, and chart data into your existing design and exports only the pages you pick, staying natively editable. See the [FAQ](./docs/faq.md) and [template-fill workflow](./skills/ppt-master/workflows/template-fill-pptx.md).
|
> **Already have a `.pptx` you want to reuse?** Hand the AI that deck plus your material and ask it to "fill this deck with the new content" — it fills text, table, and chart data into your existing design and exports only the pages you pick, staying natively editable. See the [FAQ](./docs/faq.md) and [template-fill workflow](./skills/ppt-master/workflows/template-fill-pptx.md).
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
"sourceId": "ppt-master",
|
"sourceId": "ppt-master",
|
||||||
"repo": "https://github.com/hugohe3/ppt-master.git",
|
"repo": "https://github.com/hugohe3/ppt-master.git",
|
||||||
"ref": "main",
|
"ref": "main",
|
||||||
"commit": "e247014a2f7361c06a04c07619a838f82394294c",
|
"commit": "c0305342cf5c558e7242dba64cfc207874a5134a",
|
||||||
"adapter": "claude-skill",
|
"adapter": "claude-skill",
|
||||||
"sourcePath": "skills/ppt-master",
|
"sourcePath": "skills/ppt-master",
|
||||||
"syncedAt": "2026-08-03T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: ppt-master
|
name: ppt-master
|
||||||
description: "多格式源文档到高质量 SVG 页面再导出 PPTX 的多阶段演示文稿生成工作流。"
|
description: "多格式源文档到高质量 SVG 页面再导出 PPTX 的多阶段演示文稿生成工作流。"
|
||||||
metadata:
|
metadata:
|
||||||
version: "4.3.0"
|
version: "4.4.0"
|
||||||
copyright: "Copyright (c) 2025-2026 Hugo He"
|
copyright: "Copyright (c) 2025-2026 Hugo He"
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
official_repository: "https://github.com/hugohe3/ppt-master"
|
official_repository: "https://github.com/hugohe3/ppt-master"
|
||||||
@@ -22,17 +22,25 @@ PPT Master is a routed presentation workflow. This entry owns global execution d
|
|||||||
non-zero result stops the Skill immediately; do not inspect, repair, or
|
non-zero result stops the Skill immediately; do not inspect, repair, or
|
||||||
bypass the integrity gate.
|
bypass the integrity gate.
|
||||||
3. Read [`workflows/routing.md`](workflows/routing.md).
|
3. Read [`workflows/routing.md`](workflows/routing.md).
|
||||||
4. Select exactly one top-level route from the routing authority.
|
4. Select exactly one top-level route and its active profile from the routing
|
||||||
5. Read only that route's authority and its explicitly triggered supporting documents.
|
authority.
|
||||||
|
5. Read only the resulting runtime authority and its explicitly triggered
|
||||||
|
supporting documents.
|
||||||
|
|
||||||
| Selected route | Runtime authority |
|
| Selected route / profile | Runtime authority |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Generate PPTX | [`workflows/generate-pptx.md`](workflows/generate-pptx.md) |
|
| Generate PPTX — Beautify | [`workflows/profiles/beautify-pptx.md`](workflows/profiles/beautify-pptx.md); explicit Quick intent selects Quick, otherwise Default |
|
||||||
|
| Generate PPTX — ordinary Default | [`workflows/generate-pptx.md`](workflows/generate-pptx.md) |
|
||||||
|
| Generate PPTX — ordinary explicit Quick | [`workflows/profiles/quick-generate.md`](workflows/profiles/quick-generate.md) |
|
||||||
| Create Template | [`workflows/create-template.md`](workflows/create-template.md) |
|
| Create Template | [`workflows/create-template.md`](workflows/create-template.md) |
|
||||||
| Fill Native PPTX | [`workflows/template-fill-pptx.md`](workflows/template-fill-pptx.md) |
|
| Fill Native PPTX | [`workflows/template-fill-pptx.md`](workflows/template-fill-pptx.md) |
|
||||||
| Enhance Native PPTX | [`workflows/native-enhance-pptx.md`](workflows/native-enhance-pptx.md) |
|
| Enhance Native PPTX | [`workflows/native-enhance-pptx.md`](workflows/native-enhance-pptx.md) |
|
||||||
|
|
||||||
**Hard rule — selected authority only**: Do not load another top-level route's procedure after routing. Profiles, stages, governance files, and child workflows refine the selected route; they never compete with it.
|
**Hard rule — selected authority only**: Do not load another top-level route's
|
||||||
|
procedure after routing. Beautify selects exactly one Generate runtime from the
|
||||||
|
explicit Quick signal; never load both Default and Quick. Profiles, stages,
|
||||||
|
governance files, and child workflows refine one selected route; they never
|
||||||
|
compete with it.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ tell; each capability above earns its place per page, not per deck.
|
|||||||
| Page transition | CLI: `fade`, 0.4s | Calm baseline that suits most decks; the public Python builder retains its legacy 0.5s default |
|
| Page transition | CLI: `fade`, 0.4s | Calm baseline that suits most decks; the public Python builder retains its legacy 0.5s default |
|
||||||
| Per-element animation | **`none` (off)** | A page appears as a whole. Auto-firing element builds are an unsolicited "AI deck" tell, so object animation is opt-in. Turn on the content-aware canonical entrance policy with `-a auto`, or select one PowerPoint-native `entrance_*`, `emphasis_*`, `path_*`, or `exit_*` key explicitly |
|
| Per-element animation | **`none` (off)** | A page appears as a whole. Auto-firing element builds are an unsolicited "AI deck" tell, so object animation is opt-in. Turn on the content-aware canonical entrance policy with `-a auto`, or select one PowerPoint-native `entrance_*`, `emphasis_*`, `path_*`, or `exit_*` key explicitly |
|
||||||
|
|
||||||
To regenerate a deck with different settings, rerun `svg_to_pptx.py` against the same `svg_output/` — no need to rerun the LLM. `-s final` is reserved for diagnostic comparison and is not a supported release source. To turn per-element animation on for the whole deck, pass `-a auto`.
|
To regenerate a deck with different settings, rerun the final checker when its current matching report is absent or stale, then rerun `svg_to_pptx.py` against the same `svg_output/`; the content-generation LLM need not rerun unless authored SVG requires repair. `-s final` is reserved for diagnostic comparison and is not a supported release source. To turn per-element animation on for the whole deck, pass `-a auto`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -333,7 +333,7 @@ other visible state is expected to change; both endpoints must still resolve to
|
|||||||
one compatible top-level PowerPoint object kind. Automatic Morph without pairs
|
one compatible top-level PowerPoint object kind. Automatic Morph without pairs
|
||||||
is heuristic and may cross-fade instead of tweening.
|
is heuristic and may cross-fade instead of tweening.
|
||||||
|
|
||||||
**Give text somewhere to come from.** Morph tweens objects present on both pages; text that only exists on the second page can only fade in. The standard fix is to place the *next* page's copy on the current page just outside the canvas (below), and the *previous* page's copy just outside the opposite edge (above). Each block then slides through the frame instead of blinking, and the deck reads as one continuous surface being scrolled. Objects parked outside the canvas are not rendered but must still exist on both pages and be explicitly paired when deterministic identity matters.
|
**Give text somewhere to come from.** Morph tweens objects present on both pages; text that only exists on the second page can only fade in. The standard fix is to place the *next* page's copy on the current page just outside the canvas (below), and the *previous* page's copy just outside the opposite edge (above). Each block then slides through the frame instead of blinking, and the deck reads as one continuous surface being scrolled. A wholly off-canvas endpoint must be one direct-root `<g id>` with valid `data-pptx-bounds` and `data-pptx-morph-staging="true"`; when Morph remains enabled, pair it explicitly under §2.1. The marker only declares an intentional invisible endpoint; it cannot excuse a partially clipped group or text carrier.
|
||||||
|
|
||||||
**When Morph refuses to match**: PowerPoint pairs compatible object kinds; a
|
**When Morph refuses to match**: PowerPoint pairs compatible object kinds; a
|
||||||
shape and a picture will cross-fade instead of tweening. For generated pages,
|
shape and a picture will cross-fade instead of tweening. For generated pages,
|
||||||
|
|||||||
+26
-11
@@ -7,10 +7,15 @@ profile may explicitly omit an artifact without erasing its facts.
|
|||||||
|
|
||||||
**Quick Generate projection**: Quick omits confirmation, Design Spec, and lock.
|
**Quick Generate projection**: Quick omits confirmation, Design Spec, and lock.
|
||||||
Its current main agent reads source/analysis facts, keeps routine decisions in
|
Its current main agent reads source/analysis facts, keeps routine decisions in
|
||||||
active context, and prepares images/icons/formulas plus required manifests
|
active context, and prepares the selected images/icons/formulas plus required
|
||||||
before SVG authoring. Those artifacts retain their factual/provenance roles.
|
operational manifests before SVG authoring. It may also realize charts, tables,
|
||||||
Quick writes the same final SVG quality provenance and package postflight as
|
native shapes, and other ordinary authoring capabilities directly from that
|
||||||
the default profile, but it does not create `svg_final/`.
|
context. Exact template workspace roots supplied for the run are validated and
|
||||||
|
installed directly; Quick reads only that project-local state and creates no
|
||||||
|
Confirm UI selection artifacts. Those artifacts retain their factual/provenance
|
||||||
|
roles. Quick writes the same final SVG quality provenance and package postflight
|
||||||
|
as the default profile, but it does not create `svg_final/`, a detailed design
|
||||||
|
history, or resumable planning state. Context loss restarts the Quick run.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -32,8 +37,9 @@ the default profile, but it does not create `svg_final/`.
|
|||||||
| `analysis/page-context/P<NN>.usage.json` | Derived optional context telemetry | Measured on-demand page-context size plus hashes of owning inputs/references | `page-context --record-usage` deterministically replaces only the invoked page's snapshot; `page-context-report` summarizes existing snapshots. Telemetry may be partial. Use token data to evaluate context cost, never as content or an execution contract. |
|
| `analysis/page-context/P<NN>.usage.json` | Derived optional context telemetry | Measured on-demand page-context size plus hashes of owning inputs/references | `page-context --record-usage` deterministically replaces only the invoked page's snapshot; `page-context-report` summarizes existing snapshots. Telemetry may be partial. Use token data to evaluate context cost, never as content or an execution contract. |
|
||||||
| `images/` | Runtime image pool | User, extracted, AI, web, formula, slice, EMF/WMF assets | Default Step 5 or Quick Generate resource preparation writes here; `analysis/image_analysis.csv` derives from current contents |
|
| `images/` | Runtime image pool | User, extracted, AI, web, formula, slice, EMF/WMF assets | Default Step 5 or Quick Generate resource preparation writes here; `analysis/image_analysis.csv` derives from current contents |
|
||||||
| `images/image_prompts.json`, `image_queries.json`, `image_sources.json`, `formula_manifest.json` | Conditional resource contracts | AI/web/formula execution status and provenance | Create only for a triggered path, including Quick. They guide preparation/attribution, never page design. |
|
| `images/image_prompts.json`, `image_queries.json`, `image_sources.json`, `formula_manifest.json` | Conditional resource contracts | AI/web/formula execution status and provenance | Create only for a triggered path, including Quick. They guide preparation/attribution, never page design. |
|
||||||
| `icons/` | Prepared project icon pool | Bundled icons copied by `icon_sync.py` plus user-provided, template, imported, or custom icon SVGs | SVG authoring may use any icon in this project-local pool; `spec_lock.icons.inventory` records the default plan's bundled choices rather than an exhaustive whitelist. Exporter global fallback is legacy compatibility only. |
|
| `icons/` | Prepared project icon pool | Bundled icons copied by `icon_sync.py` plus user-provided, template, imported, or custom icon SVGs | SVG authoring may choose any icon in this project-local pool per page; `spec_lock.icons.inventory` indexes the default plan's curated synced bundled pool rather than assigning page usage or defining an exhaustive whitelist. Exporter global fallback is legacy compatibility only. |
|
||||||
| `templates/` | Project template reference | Step 3 imported specs, template SVGs, and non-image assets | Strategist reads the template Design Spec and actual SVG roster during planning. Continuous Executor reuses that context; fresh Executor reads the Design Spec once and each selected complete SVG only before first use or after its SHA changes. |
|
| `${SKILL_DIR}/templates/{brands,styles,layouts,decks}/*_index.json` | Library discovery indexes | The complete registered option source for Default Stage-1 template selection and chat listing | The UI server or chat branch reads these indexes only to populate the Stage-1 choice, after the communication recommendation is authored. Never scan kind directories to add options or use index summaries as Stage-1 planning evidence. Derive a library root from kind + entry id. Exact unregistered roots remain explicit inputs. Quick does not read the catalog. |
|
||||||
|
| `templates/` | Project template reference | Stage-1-confirmed non-free selection or Quick direct-input installed/fused specs, optional Layout/Deck SVG prototypes, and non-image assets | Default template-aware Strategist work from Stage 2 onward, Quick's current agent before direct authoring, and every later role read this project-local state only, never the library/external installation root. The active planner reads every installed template Design Spec and an actual SVG roster only for Layout/Deck; Brand and Style are intentionally roster-free. Continuous Executor reuses that context; fresh Executor reads the Design Spec once and each selected complete SVG, when any, only before first use or after its SHA changes. |
|
||||||
| `templates/template_execution_manifest.json` (`v1`) + `templates/template_execution/*.text-slots.json` (`v2-min`) | Derived template index | Compact prototype/source-import summary plus per-prototype text-slot diagnostics; the sidecar integrity hash is tool-only | Materialization may publish these deterministic records, but page-context does not inject or require them and models do not read them during page authoring. The complete prototype SVG is the sole visual/template authority; never author from either JSON artifact. |
|
| `templates/template_execution_manifest.json` (`v1`) + `templates/template_execution/*.text-slots.json` (`v2-min`) | Derived template index | Compact prototype/source-import summary plus per-prototype text-slot diagnostics; the sidecar integrity hash is tool-only | Materialization may publish these deterministic records, but page-context does not inject or require them and models do not read them during page authoring. The complete prototype SVG is the sole visual/template authority; never author from either JSON artifact. |
|
||||||
| `<import_workspace>/svg/` | Imported native-payload backing | Complete PPTX-derived metadata, hidden carriers, fallback evidence, and source structure | Keep immutable; create-template materialization may resolve a validated source ref against these files, but models do not edit or bulk-read them |
|
| `<import_workspace>/svg/` | Imported native-payload backing | Complete PPTX-derived metadata, hidden carriers, fallback evidence, and source structure | Keep immutable; create-template materialization may resolve a validated source ref against these files, but models do not edit or bulk-read them |
|
||||||
| `<import_workspace>/svg-flat/` | Optional complete-page verification backing | Self-contained visual composition generated only by explicit `--inheritance-mode both` | Keep immutable when requested; never use as authoring or materialization input |
|
| `<import_workspace>/svg-flat/` | Optional complete-page verification backing | Self-contained visual composition generated only by explicit `--inheritance-mode both` | Keep immutable when requested; never use as authoring or materialization input |
|
||||||
@@ -42,17 +48,19 @@ the default profile, but it does not create `svg_final/`.
|
|||||||
| `<import_workspace>/authoring-svg/authoring_manifest.json` | Tool-only authoring provenance contract | Per-document source/authoring hashes and document-local source-ref paths | Generated atomically with the IR; materialization validates it before reusing native payload; never load it into model context or duplicate raw payload here |
|
| `<import_workspace>/authoring-svg/authoring_manifest.json` | Tool-only authoring provenance contract | Per-document source/authoring hashes and document-local source-ref paths | Generated atomically with the IR; materialization validates it before reusing native payload; never load it into model context or duplicate raw payload here |
|
||||||
| `<import_workspace>/authoring-svg-flat/` | Optional complete-page verification IR | Self-contained page composition view with its own summary and provenance manifest | Generate only from an explicitly requested `svg-flat/`; use to verify composition, while layered `authoring-svg/` remains the canonical editable source |
|
| `<import_workspace>/authoring-svg-flat/` | Optional complete-page verification IR | Self-contained page composition view with its own summary and provenance manifest | Generate only from an explicitly requested `svg-flat/`; use to verify composition, while layered `authoring-svg/` remains the canonical editable source |
|
||||||
| `<import_workspace>/icons/imported/` | Imported vector pool | One canonical copy of every factored vector subtree | Authoring SVGs reference `data-icon="imported/<name>"`; vector inventories retain source refs so expansion re-establishes IR identity |
|
| `<import_workspace>/icons/imported/` | Imported vector pool | One canonical copy of every factored vector subtree | Authoring SVGs reference `data-icon="imported/<name>"`; vector inventories retain source refs so expansion re-establishes IR identity |
|
||||||
| `confirm_ui/recommendations.stage1.json`, `.stage2.json`, `.stage3.json` | Confirmation proposals | One Strategist-authored payload per confirmation stage | Confirm UI selects the active file from `result.json`. The active, unconfirmed stage may be overwritten when the user requests a new recommendation; normal progression writes the next stage file and leaves confirmed earlier stages intact. Legacy `recommendations.json` is read only when no stage-specific file exists. |
|
| `confirm_ui/template_options.json`, `template_selection.json`, `template_handoff.json` | Default UI template-selection sidecar | Agent-authored candidate input, user-confirmed selection written beside the Stage-1 result, and agent-authored installation/free-design completion handoff | Step 3 writes options without launching UI. The Stage-1 submission writes `template_selection.json` alongside `result.json`; template choices never enter the Strategist contract. After installation/free-design closure, `--complete-template-selection` writes the bound handoff. Stage 2 is exposed only after that handoff and a fresh recommendation. Chat/delegated flows retain equivalent state without fabricating UI receipts; Quick creates none. |
|
||||||
|
| `confirm_ui/recommendations.stage1.json`, `.stage2.json` | Confirmation proposals | Template-independent communication contract, then template-aware complete solution plus production mechanics | Author the Stage-1 communication recommendation without using candidate indexes or workspaces as evidence; candidate display state may be prepared independently. Its page confirms communication plus template mode/selection in one submission. Create Stage 2 only after the selection is installed or free design closes and the handoff/equivalent state is ready. `template_application` decides only how to use installed project-local state. The active unconfirmed stage may be overwritten; normal progression leaves confirmed Stage 1 intact. |
|
||||||
| `confirm_ui/result.json` | Confirmation result | Persisted user-confirmed input evidence | Generate Step 4 reads the final object once into active context; Strategist consumes it completely into `design_spec.md`. Normal downstream work does not reopen it; fresh recovery may read it once when no retained final state exists. |
|
| `confirm_ui/result.json` | Confirmation result | Persisted user-confirmed input evidence | Generate Step 4 reads the final object once into active context; Strategist consumes it completely into `design_spec.md`. Normal downstream work does not reopen it; fresh recovery may read it once when no retained final state exists. |
|
||||||
| `svg_output/` | Page-design author source | Main-agent handwritten SVG pages containing the complete visible design | Quality checker and native PPTX export read this as the canonical visual/page-layout source; templates and locks do not add missing visible objects at export |
|
| `svg_output/` | Page-design author source | Main-agent handwritten SVG pages containing the complete visible design | Quality checker and native PPTX export read this as the canonical visual/page-layout source; templates and locks do not add missing visible objects at export |
|
||||||
| `notes/total.md` | Conditional speaker-note source | Complete notes before splitting | Step 6 writes only when the effective Speaker Notes outcome is enabled; Step 7.1 splits |
|
| `notes/total.md` | Conditional speaker-note source | Complete notes before splitting | Step 6 writes only when the effective Speaker Notes outcome is enabled; Step 7.1 splits |
|
||||||
| `notes/slide_*.md` | Conditional split notes | Per-slide notes generated from `total.md` | Derived by `total_md_split.py` only when speaker notes are enabled |
|
| `notes/slide_*.md` | Conditional split notes | Per-slide notes generated from `total.md` | Derived by `total_md_split.py` only when speaker notes are enabled |
|
||||||
| `svg_final/` | Default-only derived visual preview | Self-contained post-processed SVGs that may be opened directly or inserted as SVG pictures | Default rebuilds it from `svg_output/` with `finalize_svg.py`; Quick omits it. Never use it as a supported PPTX source. |
|
| `svg_final/` | Default-only derived visual preview | Self-contained post-processed SVGs that may be opened directly or inserted as SVG pictures | Default rebuilds it from `svg_output/` with `finalize_svg.py`; Quick omits it. Never use it as a supported PPTX source. |
|
||||||
|
| `validation/workflow.log` | Cold workflow audit log | Append-only Python command envelopes, material tagged outcomes, bounded warning/OK/stderr samples, per-run omission counts, and selective manual entries for important details with no owning Python output | `project_manager.py init` creates the log and records its milestone. Later project-scoped Python tools find that existing log through the shared CLI bootstrap and record the bounded audit selection without a wrapper command; their full console output is not copied. A helper whose arguments/cwd do not identify the active project receives `PPT_MASTER_PROJECT_PATH=<project_path>` on the same Python command. A role may run `workflow_log.py` once for a material non-Python stage handoff or rework reason, user-approved exception, or manual recovery choice; never duplicate artifacts, routine progress, or private reasoning. Detached services retain detailed output in their component logs. Never read this log during normal generation/resume or use it as stage, artifact, or quality authority; inspect it only for an explicit user-requested run review. In Quick it remains an incomplete operational audit, not a design history or resume source. |
|
||||||
| `validation/svg_quality_report.json` | Final SVG quality provenance | Final SVG gate split into blocking / introduced / inherited / source-import categories, bound to the checked SVG bytes by SHA-256 | Default runs `svg_quality_checker.py --stage final --json`; Quick adds `--quick-generate` so the checker ignores Design Spec/lock and validates the lockless flat roster. Export links the report only when fingerprints match; Quick requires that link to pass before PPTX creation. |
|
| `validation/svg_quality_report.json` | Final SVG quality provenance | Final SVG gate split into blocking / introduced / inherited / source-import categories, bound to the checked SVG bytes by SHA-256 | Default runs `svg_quality_checker.py --stage final --json`; Quick adds `--quick-generate` so the checker ignores Design Spec/lock and validates the lockless flat roster. Export links the report only when fingerprints match; Quick requires that link to pass before PPTX creation. |
|
||||||
| `validation/<output_stem>.report.json` | Published-package audit | PPTX package/resource postflight status, part counts, and quality-gate linkage | Both Generate profiles write it and emit `[POSTFLIGHT]` after package validation. |
|
| `validation/<output_stem>.report.json` | Published-package audit | PPTX package/resource postflight status, part counts, and quality-gate linkage | Both Generate profiles write it and emit `[POSTFLIGHT]` after package validation. |
|
||||||
| `exports/` | Delivery artifacts | Native DrawingML PPTX and explicit native-object/narration variants | Default Step 7.3 or Quick direct export writes final deliverables from `svg_output/`. |
|
| `exports/` | Delivery artifacts | Native DrawingML PPTX and explicit native-object/narration variants | Default Step 7.3 or Quick direct export writes final deliverables from `svg_output/`. |
|
||||||
| `backup/<timestamp>/svg_output/` | Default-path frozen author-source archive | Re-export source without re-running LLM | Both Generate profiles write a snapshot for default-path exports; explicit `-o/--output` skips it. |
|
| `backup/<timestamp>/svg_output/` | Default-path frozen author-source archive | Re-export source without re-running LLM | Both Generate profiles write a snapshot for default-path exports; explicit `-o/--output` skips it. |
|
||||||
| `animations.json` | Optional animation config | Page-transition and object-animation sidecar | Existing files activate intent resolution: Stage 3 `false` preserves, explicit objects-off exports `-a none`, and all-motion-off bypasses with `--no-animations`. Creation requires explicit instruction or enabled outcome; §IX advice never activates it |
|
| `animations.json` | Optional animation config | Page-transition and object-animation sidecar | Existing files activate intent resolution: final Stage-2 `false` preserves, explicit objects-off exports `-a none`, and all-motion-off bypasses with `--no-animations`. Creation requires explicit instruction or enabled outcome; §IX advice never activates it |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -64,20 +72,22 @@ the default profile, but it does not create `svg_final/`.
|
|||||||
| Sources read policy | In `sources/`, read content-type files (`.md` / `.markdown` / `.txt` / `.csv` / `.tsv` / `.json` / `.jsonl` / `.yaml` / `.yml`) and judge by content — a `.json` / `.csv` may be core content or just data. Exclude known sidecars: `*.conversion_profile.json` and `*_files/image_manifest.json`. `analysis/` facts (`source_profile.json`, `<stem>.slide_library.json`) are read per Step 4 / direct-PPTX workflow, not in the `sources/` content scan. |
|
| Sources read policy | In `sources/`, read content-type files (`.md` / `.markdown` / `.txt` / `.csv` / `.tsv` / `.json` / `.jsonl` / `.yaml` / `.yml`) and judge by content — a `.json` / `.csv` may be core content or just data. Exclude known sidecars: `*.conversion_profile.json` and `*_files/image_manifest.json`. `analysis/` facts (`source_profile.json`, `<stem>.slide_library.json`) are read per Step 4 / direct-PPTX workflow, not in the `sources/` content scan. |
|
||||||
| PPTX structure | `slide_library.json` owns native geometry, slot facts, and SmartArt layout/relationships for direct PPTX workflows. |
|
| PPTX structure | `slide_library.json` owns native geometry, slot facts, and SmartArt layout/relationships for direct PPTX workflows. |
|
||||||
| Design contract | Final confirmation once → audited `design_spec.md` → optional same-file refinement/approval → context-authored lock. Never maintain a parallel draft/lock. Executor may apply `Template Application` prose but never replace identity. Repair divergence from the approved Design Spec/context unless it fails active-decision fidelity. |
|
| Design contract | Final confirmation once → audited `design_spec.md` → optional same-file refinement/approval → context-authored lock. Never maintain a parallel draft/lock. Executor may apply `Template Application` prose but never replace identity. Repair divergence from the approved Design Spec/context unless it fails active-decision fidelity. |
|
||||||
| Flat packaging authority | Free-design, brand-only, and `template_reuse_scope: style` declare `pptx_structure.mode: flat` and omit `pptx_masters`, `pptx_layouts`, `page_pptx_layouts`, and `page_layouts`. `svg_output/` owns the complete Slide-local visual design without root Master/Layout identity, fixed-layer ownership, or placeholder metadata. Export materializes one clean project-owned Master plus one Blank Layout, applies the locked theme defaults, removes stock content placeholders/Layout inventory, and retains only the standard date/footer/slide-number capability hooks. |
|
| Flat packaging authority | Free-design, brand-only, Style-only, and every plan with `template_reuse_scope: style` declare `pptx_structure.mode: flat` and omit `pptx_masters`, `pptx_layouts`, `page_pptx_layouts`, and `page_layouts`. A Style fused with Layout/Deck changes only Direction / method and does not force the non-Style structure plan to flat. `svg_output/` owns the complete Slide-local visual design without root Master/Layout identity, fixed-layer ownership, or placeholder metadata. Export materializes one clean project-owned Master plus one Blank Layout, applies the locked theme defaults, removes stock content placeholders/Layout inventory, and retains only the standard date/footer/slide-number capability hooks. |
|
||||||
| Template structure authority | `template_reuse_scope: mirror|layout` uses `page_layouts` for each page's authoring-input prototype. `pptx_masters` / `pptx_layouts` own the unique reusable output definitions, while `page_pptx_layouts` owns page assignment. Strict keeps the prototype contract; adaptive may use a current or new Layout already declared by Strategist. A construction-discovered structural change returns upstream for definition and assignment repair before authoring resumes. Mirror additionally preserves literal visuals/text topology; layout allows project-controlled reflow/re-skinning. Unused definitions may register without a published Slide. Templates validate provenance but never add missing visible page objects during export. |
|
| Template structure authority | `template_reuse_scope: mirror|layout` uses `page_layouts` for each page's authoring-input prototype. `pptx_masters` / `pptx_layouts` own the unique reusable output definitions, while `page_pptx_layouts` owns page assignment. Strict keeps the prototype contract; adaptive may use a current or new Layout already declared by Strategist. A construction-discovered structural change returns upstream for definition and assignment repair before authoring resumes. Mirror additionally preserves literal visuals/text topology; layout allows project-controlled reflow/re-skinning. Unused definitions may register without a published Slide. Templates validate provenance but never add missing visible page objects during export. |
|
||||||
| Fact classes | External facts resolve through `sources/*.facts.json`; invented demo KPIs/targets/internal ratios are labeled `scenario` in `design_spec.md §IX` and visibly in the page. Never promote scenario data into the external fact registry. |
|
| Fact classes | External facts resolve through `sources/*.facts.json`; invented demo KPIs/targets/internal ratios are labeled `scenario` in `design_spec.md §IX` and visibly in the page. Never promote scenario data into the external fact registry. |
|
||||||
| Imported-template authoring | Editable SVGs under `authoring-svg/` own create-template edits, `authoring_summary.json` owns model-facing orientation, and `authoring_manifest.json` owns tool-only source-object identity. Lossless `svg/` owns immutable native payload and fallback evidence; optional `svg-flat/` owns only complete-page verification. Materialized `templates/*.svg` own the validated deliverable contract and contain no IR-only source refs. |
|
| Imported-template authoring | Editable SVGs under `authoring-svg/` own create-template edits, `authoring_summary.json` owns model-facing orientation, and `authoring_manifest.json` owns tool-only source-object identity. Lossless `svg/` owns immutable native payload and fallback evidence; optional `svg-flat/` owns only complete-page verification. Materialized `templates/*.svg` own the validated deliverable contract and contain no IR-only source refs. |
|
||||||
| Legacy template input | Old unmapped/distilled/preserve structured projects and incomplete template packages are not migrated in place. [`create-template`](../workflows/create-template.md) authors a new current workspace: original PPTX Type A may preserve existing native topology in mirror; legacy SVG-only Type B is visual reference for `standard` / `fidelity`. An intentional free-design or brand-only `flat` project is already current. The exporter does not migrate or visually cluster legacy structure. |
|
| Legacy template input | Old unmapped/distilled/preserve structured projects and incomplete template packages are not migrated in place. [`create-template`](../workflows/create-template.md) authors a new current workspace: original PPTX Type A may preserve existing native topology in mirror; legacy SVG-only Type B is visual reference for `standard` / `fidelity`. Intentional free-design, Brand-only, and Style-only `flat` projects are already current. The exporter does not migrate or visually cluster legacy structure. |
|
||||||
| Image facts | `images/` is live state; `analysis/image_analysis.csv` is a regenerated view, not a durable cache. |
|
| Image facts | `images/` is live state; `analysis/image_analysis.csv` is a regenerated view, not a durable cache. |
|
||||||
| SVG source | `svg_output/` is the only author source for generated pages. |
|
| SVG source | `svg_output/` is the only author source for generated pages. |
|
||||||
| Page-design closure | On SVG-authoring routes, every visible exported-slide object exists in the corresponding page SVG or an explicitly referenced visual asset. |
|
| Page-design closure | On SVG-authoring routes, every visible exported-slide object exists in the corresponding page SVG or an explicitly referenced visual asset. |
|
||||||
| Package-behavior separation | Speaker notes, animations, transitions, narration, and direct native-PPTX workflows keep their owning artifacts; do not force them into SVG metadata. |
|
| Package-behavior separation | Speaker notes, animations, transitions, narration, and direct native-PPTX workflows keep their owning artifacts; do not force them into SVG metadata. |
|
||||||
| Post-processed SVG | In Default Generate, `svg_final/` is disposable, must be rebuilt in Step 7.2, and serves only as a self-contained visual preview / manually insertable SVG picture. Quick omits it. |
|
| Post-processed SVG | In Default Generate, `svg_final/` is disposable, must be rebuilt in Step 7.2, and serves only as a self-contained visual preview / manually insertable SVG picture. Quick omits it. |
|
||||||
|
| Workflow audit log | `validation/workflow.log` automatically records each project-scoped Python command envelope, all explicit error/failure and receipt/report lines, bounded warning/OK/stderr samples, summary context, and omission counts, plus explicitly selected manual audit entries. It does not retain the full console stream or automatically capture direct file-authoring actions, host-native tools, pre-project conversion, binary-buffer writes, hidden child output, or detached-service activity; absence of a detail line proves nothing about those stages. Automatic recording failure is advisory and never changes the owning tool's outcome; a failed explicit manual append reports failure because no entry was recorded. |
|
||||||
| Export source | The only supported generated-PPTX route reads `svg_output/` through the project SVG-to-DrawingML converter. A diagnostic `-s final` override does not change ownership or create a supported release route. |
|
| Export source | The only supported generated-PPTX route reads `svg_output/` through the project SVG-to-DrawingML converter. A diagnostic `-s final` override does not change ownership or create a supported release route. |
|
||||||
| Shape-conversion boundary | PowerPoint's manual Convert-to-Shape operation on `svg_final/` is outside the project compatibility contract. |
|
| Shape-conversion boundary | PowerPoint's manual Convert-to-Shape operation on `svg_final/` is outside the project compatibility contract. |
|
||||||
| Confirmation | Final UI/chat confirmation overrides recommendations and is consumed once into `design_spec.md`. Enabled refinement applies arbitrary revisions there and requires approval; only then may active-decision fidelity release lock authoring. |
|
| Confirmation | Final UI/chat confirmation overrides recommendations and is consumed once into `design_spec.md`. Enabled refinement applies arbitrary revisions there and requires approval; only then may active-decision fidelity release lock authoring. |
|
||||||
| Proactive production outcomes | Resolve notes/animation/narration from explicit instruction → Stage 3 → defaults, then persist outcomes/provenance only in Design Spec §I. Explicit notes-off/audio-on applies Generate's dependency gate; Stage 3 animation `false` does not suppress a sidecar. |
|
| Template selection | Default Step 3 prepares candidates without interaction or template reads. `template_options.default_mode` is `free_design` for ordinary requests and `templates` for explicit template intent or any exact root; Stage 1 keeps the mode switchable and confirms it with communication. Exactly one root may be preselected, while multiple roots remain unselected candidates. Template mode requires at least one selected candidate. Every applied selection becomes one project-local `templates/` state before Stage 2 reads it. Quick bypasses this gate and applies at most one exact root per kind directly. |
|
||||||
|
| Proactive production outcomes | Resolve notes/animation/narration from explicit instruction → final Stage 2 → defaults, then persist outcomes/provenance only in Design Spec §I. Explicit notes-off/audio-on applies Generate's dependency gate; final Stage-2 animation `false` does not suppress a sidecar. |
|
||||||
|
|
||||||
**Forbidden - mixed ownership**: Do not copy chart values from Markdown into `analysis/` by hand, do not edit `svg_final/` as the source of a fix, do not edit imported lossless SVGs instead of their authoring IR, and do not treat `design_spec.md` prose as a replacement for `spec_lock.md`.
|
**Forbidden - mixed ownership**: Do not copy chart values from Markdown into `analysis/` by hand, do not edit `svg_final/` as the source of a fix, do not edit imported lossless SVGs instead of their authoring IR, and do not treat `design_spec.md` prose as a replacement for `spec_lock.md`.
|
||||||
|
|
||||||
@@ -85,6 +95,11 @@ the default profile, but it does not create `svg_final/`.
|
|||||||
|
|
||||||
## 3. Regeneration Rules
|
## 3. Regeneration Rules
|
||||||
|
|
||||||
|
The table names each owning command. Generate profiles invoke these commands
|
||||||
|
directly; the bounded project-scoped Python command/outcome audit is recorded
|
||||||
|
automatically after initialization. Other routes retain their own invocation
|
||||||
|
contract.
|
||||||
|
|
||||||
| Derived artifact | Regenerate from | Command / owner |
|
| Derived artifact | Regenerate from | Command / owner |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `analysis/image_analysis.csv` | Current `images/` | `python3 ${SKILL_DIR}/scripts/analyze_images.py <project_path>/images` |
|
| `analysis/image_analysis.csv` | Current `images/` | `python3 ${SKILL_DIR}/scripts/analyze_images.py <project_path>/images` |
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ Always-loaded Executor authority for flat SVG page authoring and behavior shared
|
|||||||
| Any `Status: Sourced` web image | [`executor-web-image.md`](./executor-web-image.md), after `executor-image.md` |
|
| Any `Status: Sourced` web image | [`executor-web-image.md`](./executor-web-image.md), after `executor-image.md` |
|
||||||
| Effective Speaker Notes outcome is enabled after all SVG pages pass | [`executor-notes.md`](./executor-notes.md) |
|
| Effective Speaker Notes outcome is enabled after all SVG pages pass | [`executor-notes.md`](./executor-notes.md) |
|
||||||
|
|
||||||
> Narrative skeleton and visual aesthetic come from this deck's locked files under [`modes/`](./modes/_index.md) and [`visual-styles/`](./visual-styles/_index.md). Technical constraints are in [`shared-standards-core.md`](./shared-standards-core.md).
|
> Narrative skeleton and visual aesthetic come from this deck's locked files under [`modes/`](./modes/_index.md) and [`visual-styles/`](./visual-styles/_index.md). [`shared-standards-core.md`](./shared-standards-core.md) supplies the technical boundary plus the fallback visual-quality and leading defaults when those authorities are silent.
|
||||||
|
|
||||||
**Hard rule — complete page SVG**: Every visible object intended for the exported slide MUST exist in the final page SVG or be explicitly referenced by it. Templates and `spec_lock.md` guide construction; they are not export-time overlays for missing visible content.
|
**Hard rule — complete page SVG**: Every visible object intended for the exported slide MUST exist in the final page SVG or be explicitly referenced by it. Templates and `spec_lock.md` guide construction; they are not export-time overlays for missing visible content.
|
||||||
|
|
||||||
**Hard rule — flat PowerPoint structure**: Free-design, brand-only, and `template_reuse_scope: style` projects use `pptx_structure.mode: flat`: write no root Master/Layout identity, `data-pptx-layer`, or `data-pptx-placeholder`; every visible object remains Slide-local, and the root declares exactly one canonical `data-pptx-page-role` (`cover` / `toc` / `section` / `content` / `ending`). A `style` template supplies colors, typography, decoration, and rhythm as style input without creating per-page prototype mappings. Export materializes one clean project-owned Master plus one Blank Layout from the current lock. Add `data-pptx-role` only to structural page-frame objects whose package, page-number, or animation behavior is not already expressed by specialized metadata; the marked element uses a stable unique `id`. See [`semantic-svg.md`](./semantic-svg.md).
|
**Hard rule — flat PowerPoint structure**: Free-design, brand-only, Style-only, and every `template_reuse_scope: style` project use `pptx_structure.mode: flat`: write no root Master/Layout identity, `data-pptx-layer`, or `data-pptx-placeholder`; every visible object remains Slide-local, and the root declares exactly one canonical `data-pptx-page-role` (`cover` / `toc` / `section` / `content` / `ending`). A Style workspace supplies reusable communication/design direction, composition rhythm, and information-expression defaults without page prototypes. Its identity-adjacent color, typography, icon, and image defaults yield to the final Brand/Deck identity and confirmed project lock. When Style is fused with Layout/Deck, it changes only Direction / method and follows the resolved non-Style structure route. Export materializes one clean project-owned Master plus one Blank Layout from the current lock. Add `data-pptx-role` only to structural page-frame objects whose package, page-number, or animation behavior is not already expressed by specialized metadata; the marked element uses a stable unique `id`. See [`semantic-svg.md`](./semantic-svg.md).
|
||||||
|
|
||||||
**Hard rule — supported PPTX route**: The only supported generated-PPTX path is `svg_output/` through the project SVG-to-DrawingML converter. Step 7.2 still generates `svg_final/` as a mandatory self-contained visual preview that may be inserted as an SVG picture. Do not treat PowerPoint's manual Convert-to-Shape operation as an authoring target or compatibility requirement.
|
**Hard rule — supported PPTX route**: The only supported generated-PPTX path is `svg_output/` through the project SVG-to-DrawingML converter. Step 7.2 still generates `svg_final/` as a mandatory self-contained visual preview that may be inserted as an SVG picture. Do not treat PowerPoint's manual Convert-to-Shape operation as an authoring target or compatibility requirement.
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ Before the first SVG page, output a confirmation listing: the compact communicat
|
|||||||
**On-demand page-context diagnostic**: only for explicit telemetry/debugging or an unresolved page/template/chart path-SHA question; never as a pre-page gate:
|
**On-demand page-context diagnostic**: only for explicit telemetry/debugging or an unresolved page/template/chart path-SHA question; never as a pre-page gate:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 skills/ppt-master/scripts/project_manager.py page-context <project_path> P<NN> [--record-usage]
|
python3 ${SKILL_DIR}/scripts/project_manager.py page-context <project_path> P<NN> [--record-usage]
|
||||||
```
|
```
|
||||||
|
|
||||||
Consume stdout directly; stop on non-zero exit. The projection is derived, not authoritative. Use `--record-usage` only for measurement.
|
Consume stdout directly; stop on non-zero exit. The projection is derived, not authoritative. Use `--record-usage` only for measurement.
|
||||||
@@ -65,7 +65,7 @@ Consume stdout directly; stop on non-zero exit. The projection is derived, not a
|
|||||||
|
|
||||||
**Hard rule — exact page roster**: `design_spec.md §IX` is the ordered queue: one final slide per entry, with the same id/order. The UI range no longer applies. Never add, drop, merge, split, or reorder; repair/reconfirm the Design Spec first.
|
**Hard rule — exact page roster**: `design_spec.md §IX` is the ordered queue: one final slide per entry, with the same id/order. The UI range no longer applies. Never add, drop, merge, split, or reorder; repair/reconfirm the Design Spec first.
|
||||||
|
|
||||||
**Hard rule — binding selection vs realization**: use Strategist-selected semantic content, resources/paths, chart keys, template/layout routing keys, core fonts, palette anchors, icons, and crop boundaries. Adapt realization, never those binding selections, except sparse local font/color garnish allowed below. A §VIII preferred image pattern is not a template/layout routing key; [`executor-image.md`](./executor-image.md) owns its realization freedom. Missing or unresolved material stops execution and returns to Strategist-owned acquisition/failure recovery; never search, generate, download, sync, invent, or substitute it. Binding selection changes require upstream repair.
|
**Hard rule — binding selection vs realization**: use Strategist-selected semantic content, resources/paths, chart keys, template/layout routing keys, core fonts, palette anchors, icon-library/stroke anchors, and crop boundaries. Adapt realization—including which prepared project-local icon, if any, best serves each page—without changing those binding selections, except sparse local font/color garnish allowed below. A §VIII preferred image pattern is not a template/layout routing key; [`executor-image.md`](./executor-image.md) owns its realization freedom. Missing or unresolved material stops execution and returns to Strategist-owned acquisition/failure recovery; never search, generate, download, sync, invent, or substitute it. Binding selection changes require upstream repair.
|
||||||
|
|
||||||
**Hard rule — content vs expression**: `design_spec.md §IX` owns each page's semantic content and supplies complete preferred wording and block texture; those expression choices are not verbatim requirements unless explicitly literal. Executor may paraphrase, condense repetition, regroup or reorder material within the same page, and switch among prose, bullets, keywords, labels, or visual annotation when fit or readability benefits. The result must remain information-equivalent: preserve the `Core message`, `Audience move`, and every substantive claim, fact, data value, proper name, qualifier or caveat, relationship, key argument or evidence, and literal requirement. Never add a claim, move content across pages, or drop information to make the layout fit; return an unfit or underspecified block for Design Spec repair.
|
**Hard rule — content vs expression**: `design_spec.md §IX` owns each page's semantic content and supplies complete preferred wording and block texture; those expression choices are not verbatim requirements unless explicitly literal. Executor may paraphrase, condense repetition, regroup or reorder material within the same page, and switch among prose, bullets, keywords, labels, or visual annotation when fit or readability benefits. The result must remain information-equivalent: preserve the `Core message`, `Audience move`, and every substantive claim, fact, data value, proper name, qualifier or caveat, relationship, key argument or evidence, and literal requirement. Never add a claim, move content across pages, or drop information to make the layout fit; return an unfit or underspecified block for Design Spec repair.
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ Apply the content-vs-expression contract above within the selected reading mode.
|
|||||||
|
|
||||||
**Default — authored texture (may override when information-equivalent)**: start from each `design_spec.md §IX Content` block's written texture because it is the Strategist's recommended expression. Keep prose when its continuity carries causal, argumentative, narrative, qualification, or emphasis relationships; use bullets or keywords when the material is genuinely parallel or ordered, or another information-equivalent structure is clearer. Never convert solely because a list is easier to lay out or an inherited template exposes a list slot.
|
**Default — authored texture (may override when information-equivalent)**: start from each `design_spec.md §IX Content` block's written texture because it is the Strategist's recommended expression. Keep prose when its continuity carries causal, argumentative, narrative, qualification, or emphasis relationships; use bullets or keywords when the material is genuinely parallel or ordered, or another information-equivalent structure is clearer. Never convert solely because a list is easier to lay out or an inherited template exposes a list slot.
|
||||||
|
|
||||||
- **Hard rule — one paragraph, one text frame**: use one `<text>` per prose paragraph, never sibling `<text>` elements for its visual lines. Keep the first authored line as direct text; later lines use direct `<tspan>` children that repeat parent `x`, retain effective font size, and use positive relative `dy`. An all-`<tspan>` form may start at `dy="0"`. Default retains these breaks without PowerPoint wrapping; `--reflow-text` enables reflow. Choose positive line spacing for the typeface, size, density, and reading distance; no fixed ratio overrides legibility or the selected style.
|
- **Hard rule — one paragraph, one text frame**: use one `<text>` per prose paragraph, never sibling `<text>` elements for its visual lines. Keep the first authored line as direct text; later lines use direct `<tspan>` children that repeat parent `x`, retain effective font size, and use positive relative `dy`. An all-`<tspan>` form may start at `dy="0"`. Default retains these breaks without PowerPoint wrapping; `--reflow-text` enables reflow. Start from the shared leading ranges in [`shared-standards-core.md`](./shared-standards-core.md) §4.2, then adjust for the typeface, reading distance, explicit user/template requirements, and locked style.
|
||||||
- **Template precedence**: an inherited slot never overrides the content relationship. If faithful expression needs prose, widen or reflow the container, or drop that card; never convert solely to fill a list slot.
|
- **Template precedence**: an inherited slot never overrides the content relationship. If faithful expression needs prose, widen or reflow the container, or drop that card; never convert solely to fill a list slot.
|
||||||
- **Mode precedence**: the locked mode shapes voice / register, not §IX's authored titles or page order. When a `§IX` title is a user-authored topic label, keep it — do not upgrade it to an assertion just because the mode (e.g. `pyramid`) favors them; mode title-tendencies apply only to AI-drafted titles.
|
- **Mode precedence**: the locked mode shapes voice / register, not §IX's authored titles or page order. When a `§IX` title is a user-authored topic label, keep it — do not upgrade it to an assertion just because the mode (e.g. `pyramid`) favors them; mode title-tendencies apply only to AI-drafted titles.
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ Apply the content-vs-expression contract above within the selected reading mode.
|
|||||||
|
|
||||||
**Execution anchors and contextual values**:
|
**Execution anchors and contextual values**:
|
||||||
|
|
||||||
- Icons may use any SVG already prepared under `<project_path>/icons/`. `icons.library` records the Strategist's primary bundled style choice and `icons.inventory` records its planned selection; neither is an execution whitelist over project-local assets.
|
- Icons may use any SVG already prepared under `<project_path>/icons/`. `icons.library` records the Strategist's primary bundled style choice and `icons.inventory` indexes its curated synced pool; neither assigns icons to pages or limits other project-local assets.
|
||||||
- Core color roles retain their meaning. Derive tints, shades, alpha, gradients, and effects; preserve natural asset colors; and use sparse page-local accents for differentiation/ornament. They must not become a competing or recurring palette.
|
- Core color roles retain their meaning. Derive tints, shades, alpha, gradients, and effects; preserve natural asset colors; and use sparse page-local accents for differentiation/ornament. They must not become a competing or recurring palette.
|
||||||
- Resolve structural families by role: exact `<role>_family` first, then `title_family` for title roles or `body_family` for other unoverridden roles, then legacy `font_family`. Never flatten declared role overrides. A sparse export-safe accent family may style short non-structural display/ornament only—never title/body/data/annotation. Recurrence requires upstream selection.
|
- Resolve structural families by role: exact `<role>_family` first, then `title_family` for title roles or `body_family` for other unoverridden roles, then legacy `font_family`. Never flatten declared role overrides. A sparse export-safe accent family may style short non-structural display/ornament only—never title/body/data/annotation. Recurrence requires upstream selection.
|
||||||
- Font sizes use the named `typography` role values as deck-wide anchors. Map every structural text item to a declared role before drawing; never inherit a template placeholder size. Start from the anchor, then use composition and content fit to adjust that occurrence by at most `±2`px. Keep same-page peers consistent and preserve the role hierarchy; bounded adjustment does not create a new role.
|
- Font sizes use the named `typography` role values as deck-wide anchors. Map every structural text item to a declared role before drawing; never inherit a template placeholder size. Start from the anchor, then use composition and content fit to adjust that occurrence by at most `±2`px. Keep same-page peers consistent and preserve the role hierarchy; bounded adjustment does not create a new role.
|
||||||
@@ -133,8 +133,7 @@ Before drawing each page, look up its entry in `page_rhythm` (key format `P<NN>`
|
|||||||
|
|
||||||
## 3. Execution Guidelines
|
## 3. Execution Guidelines
|
||||||
|
|
||||||
- **Proximity**: group related elements with tight spacing; separate unrelated groups
|
- **Element grouping (Mandatory)**: wrap each logical Slide-local body unit in a descriptive, page-unique top-level `<g id>`. Every visible direct root `<g>` declares root-coordinate `data-pptx-bounds="x y width height"`; frame/native coordinates do not replace it, and placeholder bounds also supply the slot frame. Nested groups need no bounds and any such values are ignored. Checker compares root bounds with the `viewBox`, recursively checks estimable text against its root module with DrawingML wrapping headroom, and independently checks every estimable visible text carrier against the page without that headroom: through `1px` is ignored; module overflow warns through `5%` and fails above it, while larger page overflow always fails. Unestimable visible text receives an advisory warning. Only a wholly off-canvas direct-root Morph endpoint may set `data-pptx-morph-staging="true"`; keep its text inside its own module bounds, use an explicit pair when Morph remains enabled, and never use the marker for partial overflow. Images, shapes, paths, `<use>`, effects, and object frames remain geometrically free. Flat pages use ordinary groups; structured slots already qualify, while titles, direct Master/Layout atoms, and canvas-level static framing may remain root primitives. On flat pages, give a root background image or full-canvas scrim/decoration rectangle a stable `id` plus `data-pptx-role="background"` / `"decoration"`; never wrap it only to silence the advisory.
|
||||||
- **Element grouping (Mandatory)**: wrap each logical Slide-local body unit in a descriptive, page-unique top-level `<g id>`. Every visible direct root `<g>` declares root-coordinate `data-pptx-bounds="x y width height"`; frame/native coordinates do not replace it, and placeholder bounds also supply the slot frame. Nested groups need no bounds and any such values are ignored. Checker compares root bounds with the `viewBox` and recursively checks only estimable text against its root module: through `1px` is ignored, through `5%` warns, above `5%` fails per side. Images, shapes, paths, `<use>`, effects, and object frames remain geometrically free. Flat pages use ordinary groups; structured slots already qualify, while titles, direct Master/Layout atoms, and canvas-level static framing may remain root primitives. On flat pages, give a root background image or full-canvas scrim/decoration rectangle a stable `id` plus `data-pptx-role="background"` / `"decoration"`; never wrap it only to silence the advisory.
|
|
||||||
- **Reference — not a constraint**: top-level groups set semantic and automatic-animation granularity, but they may contain descriptive nested `<g>` edit groups when the page has meaningful internal subunits. Nested groups need no bounds and create no automatic animation step; use or omit them from the page's actual editing semantics, with no default pattern, depth, or quota.
|
- **Reference — not a constraint**: top-level groups set semantic and automatic-animation granularity, but they may contain descriptive nested `<g>` edit groups when the page has meaningful internal subunits. Nested groups need no bounds and create no automatic animation step; use or omit them from the page's actual editing semantics, with no default pattern, depth, or quota.
|
||||||
- **Default — size `data-pptx-bounds` as the intended module zone, not a glyph box (may skip when no text is estimable)**: make the zone as generous as the canvas and sibling layout allow, without overlapping another module zone. An untransformed line spans `y - 0.85 × font_size` to `y + 0.35 × font_size`; width uses the shared SVG-to-PPTX per-run estimate and safety headroom. If text does not fit, first expand a zone that has unused non-overlapping space; otherwise reflow or adapt. Larger bounds do not repair off-canvas text.
|
- **Default — size `data-pptx-bounds` as the intended module zone, not a glyph box (may skip when no text is estimable)**: make the zone as generous as the canvas and sibling layout allow, without overlapping another module zone. An untransformed line spans `y - 0.85 × font_size` to `y + 0.35 × font_size`; width uses the shared SVG-to-PPTX per-run estimate and safety headroom. If text does not fit, first expand a zone that has unused non-overlapping space; otherwise reflow or adapt. Larger bounds do not repair off-canvas text.
|
||||||
- **Spec adherence**: follow color, layout, canvas format, and typography in the spec
|
- **Spec adherence**: follow color, layout, canvas format, and typography in the spec
|
||||||
@@ -144,12 +143,12 @@ Before drawing each page, look up its entry in `page_rhythm` (key format `P<NN>`
|
|||||||
- **Fact provenance**: when a §IX page lists `Fact IDs`, resolve each ID from `sources/*.facts.json` and keep the claim/value unchanged. Render a compact source footnote using the source name and a short URL/domain when space permits; when speaker notes are enabled, state the attribution naturally there too. When §IX says `Data class: scenario`, place a visible localized `Scenario data` / `情景数据` label adjacent to the affected KPI/chart and, when notes are enabled, state naturally there that the number is illustrative. Never attach an external fact ID to scenario data or let an unlabeled invented KPI look factual.
|
- **Fact provenance**: when a §IX page lists `Fact IDs`, resolve each ID from `sources/*.facts.json` and keep the claim/value unchanged. Render a compact source footnote using the source name and a short URL/domain when space permits; when speaker notes are enabled, state the attribution naturally there too. When §IX says `Data class: scenario`, place a visible localized `Scenario data` / `情景数据` label adjacent to the affected KPI/chart and, when notes are enabled, state naturally there that the number is illustrative. Never attach an external fact ID to scenario data or let an unlabeled invented KPI look factual.
|
||||||
- **Default — stage each page with the style's composition geometry (may override when the content genuinely calls for a plain grid)**: an SVG page is a canvas, not a DOM. Before defaulting to stacked rounded-rect cards or uniform equal columns, pick one page-scale move from the locked visual style's §1 `Composition geometry` (a bleed shape, diagonal split, oversized numeral, orbit rings, …) to stage the page's primary zone. Card grids are one option among many, not the house layout.
|
- **Default — stage each page with the style's composition geometry (may override when the content genuinely calls for a plain grid)**: an SVG page is a canvas, not a DOM. Before defaulting to stacked rounded-rect cards or uniform equal columns, pick one page-scale move from the locked visual style's §1 `Composition geometry` (a bleed shape, diagonal split, oversized numeral, orbit rings, …) to stage the page's primary zone. Card grids are one option among many, not the house layout.
|
||||||
- **Default — vary a planned deck motif instead of cloning it (may omit where it has no page job)**: when §III `Theme` names a cross-page motif, use the current §IX `Layout` to preserve its recognizable contour, direction, material, or relationship while varying scale, crop, density, position, and content interaction by page role. Apply it only where it supports hierarchy or continuity; do not paste identical ornament or invent a second recurring identity.
|
- **Default — vary a planned deck motif instead of cloning it (may omit where it has no page job)**: when §III `Theme` names a cross-page motif, use the current §IX `Layout` to preserve its recognizable contour, direction, material, or relationship while varying scale, crop, density, position, and content interaction by page role. Apply it only where it supports hierarchy or continuity; do not paste identical ornament or invent a second recurring identity.
|
||||||
- **Containers are structural**: cards and grids express grouping, hierarchy, or capacity, not a house style. Preserve meaningful template frames; restyle radius, fill, stroke, and depth from the active Design Spec and `spec_lock.md`. Chart-catalog adaptation is owned by [`executor-chart.md`](./executor-chart.md); preview effects never override project styling or structural roles.
|
- **Inherited containers**: preserve meaningful template frames; restyle radius, fill, stroke, and depth from the active Design Spec and `spec_lock.md`. Chart-catalog adaptation is owned by [`executor-chart.md`](./executor-chart.md); preview effects never override project styling or structural roles.
|
||||||
- **Reference — prefer semantic geometry over preset stacks**: for relationships such as ascending, converging, breaking through, or stacking, first seek a basic primitive, one exact preset, or a clear Boolean result. Only when none can faithfully express the relationship should one page-specific polygon/path replace a stack of generic arrows.
|
- **Reference — prefer semantic geometry over preset stacks**: for relationships such as ascending, converging, breaking through, or stacking, first seek a basic primitive, one exact preset, or a clear Boolean result. Only when none can faithfully express the relationship should one page-specific polygon/path replace a stack of generic arrows.
|
||||||
- **Reference — create depth with restraint**: use rhythm, spacing, typography, accent bars, and subtle tints before shadows. Reserve lift for a few genuinely floating elements; keep peer grids, dividers, and body containers flat.
|
- **Reference — create depth with restraint**: use rhythm, spacing, typography, accent bars, and subtle tints before shadows. Reserve lift for a few genuinely floating elements; keep peer grids, dividers, and body containers flat.
|
||||||
- **Phased generation** (recommended):
|
- **Phased generation** (recommended):
|
||||||
1. **Visual Construction Phase**: generate all SVG pages sequentially for visual consistency. Use layout judgment for chart marks during the draft. **MUST embed plot-area markers** per [`executor-chart.md`](./executor-chart.md) §2.1 on every §IX-planned data-chart page — coordinate calibration is a post-generation step (see [`verify-charts`](../workflows/stages/verify-charts.md)) that depends on these markers — and **native object metadata** per [`executor-chart.md`](./executor-chart.md) §2.2 on every planned native-ready object. **Reach for native presets** per §3.0 as you draw each page: a block arrow, chevron, banner/ribbon, callout, standard flowchart node, or star is authored through `preset_shape_svg.py` at draw time — decided by the object's intent as you create it, never by scanning finished paths, and never committed to a bare `<path>`/`<polygon>` when a preset expresses it (a gradient fill/stroke or a pattern fill is the one paint exception — keep those ordinary SVG). **First-page gate (Mandatory)**: after completing the first page, run `python3 scripts/svg_quality_checker.py <project_path> --stage first-page --json` without output filtering. Review the whole P01 issue set, make one consolidated edit pass for every error and any selected warnings, then perform one verification rerun. If it still fails, treat that complete output as the next batch; never check between individual fixes. After it passes, draw P02 through the last page without checker calls.
|
1. **Visual Construction Phase**: generate all SVG pages sequentially for visual consistency. Use layout judgment for chart marks during the draft. **MUST embed plot-area markers** per [`executor-chart.md`](./executor-chart.md) §2.1 on every §IX-planned data-chart page — coordinate calibration is a post-generation step (see [`verify-charts`](../workflows/stages/verify-charts.md)) that depends on these markers — and **native object metadata** per [`executor-chart.md`](./executor-chart.md) §2.2 on every planned native-ready object. **Reach for native presets** per §3.0 as you draw each page: a block arrow, chevron, banner/ribbon, callout, standard flowchart node, or star is authored through `preset_shape_svg.py` at draw time — decided by the object's intent as you create it, never by scanning finished paths, and never committed to a bare `<path>`/`<polygon>` when a preset expresses it (a gradient fill/stroke or a pattern fill is the one paint exception — keep those ordinary SVG). **First-page gate (Mandatory)**: after completing the first page, run `python3 ${SKILL_DIR}/scripts/svg_quality_checker.py <project_path> --stage first-page --json` directly without output filtering. Review the whole P01 issue set, make one consolidated edit pass for every error and any selected warnings, then perform one verification rerun. If it still fails, treat that complete output as the next batch; never check between individual fixes. After it passes, draw P02 through the last page without checker calls.
|
||||||
2. **Quality Check Gate**: only after every planned SVG exists, run `python3 scripts/svg_quality_checker.py <project_path> --stage final --json` on `svg_output/` without `tail` / `head` / `grep` filtering. One run already reports all pages. Review its complete issue set, fix every `error` plus any selected advisory warnings in one consolidated edit pass, then perform one verification rerun. If it still fails, its complete output begins the next batch cycle; never use checker calls to discover or fix one next issue at a time. Every `warning` is advisory: it never sends the page back for required modification, never authorizes automatic rewriting of compatible user syntax, and needs no acknowledgement/disposition line. Recommendation warnings describe the generated-SVG default; fidelity/quality warnings may be surfaced when material, while the existing input remains releasable. Prototype-identical diagnostics are recorded as `inherited`, source conversion losses as `source-import`, changed/new advisories as `introduced`, and release failures as `blocking` in `validation/svg_quality_report.json`. If release truly depends on a condition, it belongs in `errors`. On success, use the exit status and terminal summary; do not open or `cat` the complete JSON into model context. If terminal output is truncated on failure, read only the relevant issue arrays from the report written by that same run. Do NOT defer error handling to after `finalize_svg.py` — finalize rewrites SVG and masks some violations.
|
2. **Quality Check Gate**: only after every planned SVG exists, run `python3 ${SKILL_DIR}/scripts/svg_quality_checker.py <project_path> --stage final --json` directly on `svg_output/` without `tail` / `head` / `grep` filtering. One run already reports all pages. Review its complete issue set, fix every `error` plus any selected advisory warnings in one consolidated edit pass, then perform one verification rerun. If it still fails, its complete output begins the next batch cycle; never use checker calls to discover or fix one next issue at a time. Every `warning` is advisory: it never sends the page back for required modification, never authorizes automatic rewriting of compatible user syntax, and needs no acknowledgement/disposition line. Recommendation warnings describe the generated-SVG default; fidelity/quality warnings may be surfaced when material, while the existing input remains releasable. Prototype-identical diagnostics are recorded as `inherited`, source conversion losses as `source-import`, changed/new advisories as `introduced`, and release failures as `blocking` in `validation/svg_quality_report.json`. If release truly depends on a condition, it belongs in `errors`. On success, use the exit status and terminal summary; do not open or `cat` the complete JSON into model context. If terminal output is truncated on failure, read only the relevant issue arrays from the report written by that same run. Do NOT defer error handling to after `finalize_svg.py` — finalize rewrites SVG and masks some violations.
|
||||||
3. **Logic Construction Phase (conditional)**: after SVGs pass the quality check, batch-generate speaker notes for narrative continuity only when the effective Speaker Notes outcome is enabled.
|
3. **Logic Construction Phase (conditional)**: after SVGs pass the quality check, batch-generate speaker notes for narrative continuity only when the effective Speaker Notes outcome is enabled.
|
||||||
|
|
||||||
### 3.0 Native Shape Selection
|
### 3.0 Native Shape Selection
|
||||||
@@ -233,7 +232,7 @@ Strategist chooses at most one primary bundled stylistic library and may select
|
|||||||
|
|
||||||
> **Prepared-project boundary.** Any SVG already under `<project_path>/icons/<lib>/` is valid execution material, whether selected from a bundled library or supplied by the user, a template, or an import workflow. New authoring must resolve there. The global fallback in `finalize_svg.py embed-icons` is legacy compatibility, not permission for Executor to discover or use an unprepared global icon.
|
> **Prepared-project boundary.** Any SVG already under `<project_path>/icons/<lib>/` is valid execution material, whether selected from a bundled library or supplied by the user, a template, or an import workflow. New authoring must resolve there. The global fallback in `finalize_svg.py embed-icons` is legacy compatibility, not permission for Executor to discover or use an unprepared global icon.
|
||||||
|
|
||||||
> **Icon identifiers are case-sensitive filenames.** Every `data-icon` value must use the exact project-local relative basename (`tabler-outline/award`, never `tabler-outline/Award`). Strategist records its planned bundled choices in `spec_lock.md`; Executor need not add other already-prepared project-local icons to that inventory. Custom identifiers preserve the custom file's exact case; the pipeline never silently lowercases names.
|
> **Icon identifiers are case-sensitive filenames.** Every `data-icon` value must use the exact project-local relative basename (`tabler-outline/award`, never `tabler-outline/Award`). Strategist records its curated bundled pool in `spec_lock.md`; Executor need not add other already-prepared project-local icons to that inventory. Custom identifiers preserve the custom file's exact case; the pipeline never silently lowercases names.
|
||||||
|
|
||||||
**Built-in icons — Placeholder method (recommended)**:
|
**Built-in icons — Placeholder method (recommended)**:
|
||||||
|
|
||||||
@@ -271,6 +270,8 @@ Strategist chooses at most one primary bundled stylistic library and may select
|
|||||||
test -f "<project_path>/icons/<lib>/<name>.svg"
|
test -f "<project_path>/icons/<lib>/<name>.svg"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Default — purposeful icon use**: choose prepared icons per page when they compress a label, distinguish parallel categories, clarify a process / KPI / state, or improve navigation and visual rhythm. Omit them when imagery, charts, or typography already carry the meaning. There is no coverage quota, and the prepared pool need not be exhausted.
|
||||||
|
|
||||||
**Missing project-local icon** → return to Strategist's preparation / `icon_sync.py` gate. Do not search the global library, select an alternative, or copy a candidate in Executor.
|
**Missing project-local icon** → return to Strategist's preparation / `icon_sync.py` gate. Do not search the global library, select an alternative, or copy a candidate in Executor.
|
||||||
|
|
||||||
**Hard rule — prepared assets**: Executor may freely combine project-local icons, regardless of namespace or style. It may not acquire a new icon or treat a globally resolvable file as prepared material.
|
**Hard rule — prepared assets**: Executor may freely combine project-local icons, regardless of namespace or style. It may not acquire a new icon or treat a globally resolvable file as prepared material.
|
||||||
|
|||||||
@@ -1,32 +1,48 @@
|
|||||||
> See [`executor-base.md`](./executor-base.md) for the always-loaded Executor core and [`native-data-interface.md`](./native-data-interface.md) for native chart/table metadata schemas.
|
> Default Generate also loads [`executor-base.md`](./executor-base.md); Default and Quick load [`native-data-interface.md`](./native-data-interface.md) when native chart/table metadata is selected.
|
||||||
|
|
||||||
# Executor Chart and Table Branch
|
# Executor Chart and Table Branch
|
||||||
|
|
||||||
Conditional Executor authority for data charts, chart-catalog adaptations, chart verification markers, and eligible native chart/table replacement metadata.
|
Conditional Executor authority for data charts, chart-catalog adaptations, chart verification markers, and eligible native chart/table replacement metadata.
|
||||||
|
|
||||||
**Trigger**: load when `design_spec.md §VII` contains a selected chart/table reference, `spec_lock.md page_charts` contains any row, or the current §IX page block carries any data-encoded chart or text-grid table. Mini charts, sparklines, inset charts, and small multiples count even when they are absent from `page_charts` or the chart catalog.
|
**Trigger**: load when `design_spec.md §VII` contains a selected chart/table reference, `spec_lock.md page_charts` contains any row, the current §IX page block carries any data-encoded chart or text-grid table, or Quick Generate resolves such an object in active context. Mini charts, sparklines, inset charts, and small multiples count even when they are absent from `page_charts` or the chart catalog.
|
||||||
|
|
||||||
|
**Profile authority**: Default follows the Design Spec/lock and returns missing or conflicting planning decisions upstream. Quick has no plan artifact: the current main agent resolves the data object, optional catalog key, fallback design, and native-readiness immediately in active context, writes the selected semantics into the SVG itself, and does not create a substitute roster or mapping file.
|
||||||
|
|
||||||
## 1. Reference Loading and Per-page Selection
|
## 1. Reference Loading and Per-page Selection
|
||||||
|
|
||||||
For each selected `templates/charts/<key>.svg`, use its Skill-relative `reference_set` path + SHA: read once before first use or after change, otherwise reuse it. Never load the full catalog.
|
For each selected `templates/charts/<key>.svg`, use its Skill-relative path and
|
||||||
|
read once before first use or after a known change, otherwise reuse it. Never
|
||||||
|
load the full catalog.
|
||||||
|
|
||||||
**Per-page chart reference — `page_charts` section**:
|
**Per-page chart reference**:
|
||||||
|
|
||||||
Before drawing each page, look up whether `page_charts` supplies a page-local reference:
|
| Active profile | Selection authority |
|
||||||
|
|---|---|
|
||||||
|
| Default Generate | Look up whether `spec_lock.md page_charts` supplies a page-local key, then read its §VII Usage and SVG for that page only |
|
||||||
|
| Quick Generate | Use bounded `chart_recall.py` only when a reusable visualization reference would help; keep the selected key and purpose in active context for that page only, or retain `no-template-match` and design from scratch |
|
||||||
|
|
||||||
- Entry present (e.g., `P09: timeline_horizontal`) → read its §VII Usage and SVG for that page only; realize §IX without copying it or loading the catalog.
|
Before drawing each page, apply the matching branch:
|
||||||
- No entry for this page → no catalog reference was selected. Follow the current §IX `Visualization` / `Layout`; design any declared custom visualization from scratch without inventing a §VII reference.
|
|
||||||
- Whole section absent → no catalog references were selected; §IX may still contain custom data charts or tables.
|
- Selected key present (e.g., `timeline_horizontal`) → read that SVG for the current page only and realize the active page intent without copying it or loading the catalog.
|
||||||
|
- No selected key → design the declared visualization/table from scratch; do not invent a catalog reference after drawing begins.
|
||||||
|
- Quick never persists its selection into §VII, `page_charts`, or another mapping artifact.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Chart and Native-data Authoring
|
## 2. Chart and Native-data Authoring
|
||||||
|
|
||||||
### 2.1 Chart Plot-Area Marker (MANDATORY on planned data-chart pages)
|
### 2.1 Chart Plot-Area Marker (MANDATORY on active data-chart pages)
|
||||||
|
|
||||||
> The [`verify-charts`](../workflows/stages/verify-charts.md) stage enumerates data-driven chart pages from `design_spec.md §IX`, cross-checks selected §VII references when present, then reads each page's plot-area marker to feed `svg_position_calculator.py`. A missing marker invokes that stage's declared fallback and adds avoidable derivation work.
|
> The [`verify-charts`](../workflows/stages/verify-charts.md) stage enumerates Default data-driven chart pages from `design_spec.md §IX`; Quick runs it from the still-active page decisions and cross-checks those decisions against plot-area markers. A missing marker invokes that stage's declared fallback and adds avoidable derivation work.
|
||||||
|
|
||||||
**Hard rule**: every page whose §IX `Visualization` declares data-driven chart geometry includes a plot-area marker inside `<g id="chartArea">`, placed **after axis lines** and **before the first data element** (bar, line, area, point). A legacy §VII data-chart row counts when its page block lacks that declaration. An incidental microvisual not promoted in §IX needs no marker; if its geometry must enter `verify-charts`, return upstream and update the owning §IX page block first.
|
**Hard rule**: every Default page whose §IX `Visualization` declares
|
||||||
|
data-driven chart geometry, and every Quick page the current agent resolves as
|
||||||
|
data-driven, includes a plot-area marker inside `<g id="chartArea">`, placed
|
||||||
|
**after axis lines** and **before the first data element** (bar, line, area,
|
||||||
|
point). A legacy §VII data-chart row counts when its page block lacks that
|
||||||
|
declaration. An incidental microvisual needs no marker unless the current
|
||||||
|
profile promotes it to a coordinate-verified data object; Default repairs §IX,
|
||||||
|
while Quick resolves that decision immediately before drawing.
|
||||||
|
|
||||||
**Rectangular plot area** (bar / horizontal_bar / grouped_bar / stacked_bar / line / area / stacked_area / scatter / waterfall / pareto / butterfly):
|
**Rectangular plot area** (bar / horizontal_bar / grouped_bar / stacked_bar / line / area / stacked_area / scatter / waterfall / pareto / butterfly):
|
||||||
|
|
||||||
@@ -53,7 +69,7 @@ Before drawing each page, look up whether `page_charts` supplies a page-local re
|
|||||||
| `cx, cy` | Center point of pie/donut/radar (accounting for `transform="translate()"`) |
|
| `cx, cy` | Center point of pie/donut/radar (accounting for `transform="translate()"`) |
|
||||||
| `r` | Outer radius of the chart |
|
| `r` | Outer radius of the chart |
|
||||||
|
|
||||||
**Per-page verification** — after writing each planned data-chart SVG, confirm the marker exists:
|
**Per-page verification** — after writing each active data-chart SVG, confirm the marker exists:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
rg -n "chart-plot-area" <project_path>/svg_output/<current_page>.svg
|
rg -n "chart-plot-area" <project_path>/svg_output/<current_page>.svg
|
||||||
@@ -65,15 +81,31 @@ rg -n "chart-plot-area" <project_path>/svg_output/<current_page>.svg
|
|||||||
> the same library do not use a plot-area marker.
|
> the same library do not use a plot-area marker.
|
||||||
Technical SVG/PPT constraints remain in [`shared-standards-core.md`](./shared-standards-core.md).
|
Technical SVG/PPT constraints remain in [`shared-standards-core.md`](./shared-standards-core.md).
|
||||||
|
|
||||||
### 2.2 PowerPoint-Native Chart/Table Replacement Marker (MANDATORY on planned native-ready objects)
|
### 2.2 PowerPoint-Native Chart/Table Replacement Marker (MANDATORY on selected native-ready objects)
|
||||||
|
|
||||||
> `svg_to_pptx.py --native-charts-and-tables` replaces marked groups with PowerPoint-native Chart/Table objects (charts get an embedded Excel workbook). Markers stay dormant in the default export, whose SVG children become independently editable DrawingML shapes. Prepare this optional capability for planned independent data objects, not every numeric embellishment.
|
> `svg_to_pptx.py --native-charts-and-tables` replaces marked groups with PowerPoint-native Chart/Table objects (charts get an embedded Excel workbook). Markers stay dormant in the default export, whose SVG children become independently editable DrawingML shapes. Prepare this optional capability for selected independent data objects, not every numeric embellishment.
|
||||||
|
|
||||||
**Hard rule**: load [`native-data-interface.md`](./native-data-interface.md) for each independent data chart or pure text-grid table whose §IX page block says `Native-ready: yes`. A supported data chart then gets `data-pptx-replace-with="chart"` plus one JSON `<metadata>` child; a pure text-grid table gets the table form, transcribing all plotted data or visible cells. `no` stays ordinary SVG even when a catalog reference contains a marker. For legacy specs only, use the matching §VII value when §IX has no field. Missing, conflicting, or invalid values return upstream; Executor never infers eligibility. The parent marker selects the schema.
|
**Hard rule**: load [`native-data-interface.md`](./native-data-interface.md) for
|
||||||
|
each independent data chart or pure text-grid table selected as native-ready.
|
||||||
|
Default reads that decision from the current §IX page block (`yes`/`no`; legacy
|
||||||
|
§VII fallback only). Quick decides in active context before drawing based on
|
||||||
|
whether the PowerPoint Chart/Table object model is useful enough to accept its
|
||||||
|
normalization risk. A supported selected chart gets
|
||||||
|
`data-pptx-replace-with="chart"` plus one JSON `<metadata>` child; a selected
|
||||||
|
pure text-grid table gets the table form, transcribing all plotted data or
|
||||||
|
visible cells. An object not selected stays ordinary SVG even when a catalog
|
||||||
|
reference contains a marker. The parent marker selects the schema.
|
||||||
|
|
||||||
**MUST — atomic authoring**: For each native-ready object, treat the visible SVG fallback, the parent `data-pptx-replace-with` marker, and its JSON `<metadata>` child as one object. Write all three in the same SVG edit while the data is in context. Do not defer the marker or metadata to `verify-charts`, the final quality gate, or export.
|
**MUST — atomic authoring**: For each native-ready object, treat the visible SVG fallback, the parent `data-pptx-replace-with` marker, and its JSON `<metadata>` child as one object. Write all three in the same SVG edit while the data is in context. Do not defer the marker or metadata to `verify-charts`, the final quality gate, or export.
|
||||||
|
|
||||||
**Hard rule — eligibility follows the plan**: A two-point line or small multiple gets metadata only when its §IX page block explicitly plans it as an independent object with `Native-ready: yes`. A sparkline, inset, KPI-card trend, or other incidental microvisual stays ordinary SVG even when values are recoverable. Changing eligibility requires upstream repair.
|
**Hard rule — eligibility follows the active profile authority**: In Default, a
|
||||||
|
two-point line or small multiple gets metadata only when its §IX page block
|
||||||
|
plans it as an independent object with `Native-ready: yes`; changing eligibility
|
||||||
|
requires upstream repair. In Quick, the current agent makes that independent-
|
||||||
|
object decision before drawing and expresses it atomically through the marker,
|
||||||
|
metadata, and fallback. A sparkline, inset, KPI-card trend, or other incidental
|
||||||
|
microvisual stays ordinary SVG unless Quick deliberately promotes it before
|
||||||
|
authoring.
|
||||||
|
|
||||||
Generated authoring MUST omit `data-pptx-import-source` and
|
Generated authoring MUST omit `data-pptx-import-source` and
|
||||||
`data-pptx-fallback-sha256`: those attributes record imported-PPTX provenance
|
`data-pptx-fallback-sha256`: those attributes record imported-PPTX provenance
|
||||||
@@ -82,9 +114,9 @@ catalog or reusable template; normal content edits would make it stale.
|
|||||||
|
|
||||||
`data-pptx-replace-with` is a **data-backed replacement claim**, not a generic label for a group that contains numbers and not a marker for ordinary PowerPoint shapes or connectors. Add it only when the matching JSON payload can be written in the same edit; if the object is meant to remain SVG geometry, do not add the marker.
|
`data-pptx-replace-with` is a **data-backed replacement claim**, not a generic label for a group that contains numbers and not a marker for ordinary PowerPoint shapes or connectors. Add it only when the matching JSON payload can be written in the same edit; if the object is meant to remain SVG geometry, do not add the marker.
|
||||||
|
|
||||||
- Chart types absent from that list and conceptual/diagrammatic graphics (process flows, cycles, quadrant cards, timelines, or a KPI card container) get **no marker** — `svg_quality_checker.py` rejects unsupported marker types. A supported data chart nested inside one of those compositions gets its own marker only when its §IX page block explicitly plans that object as `Native-ready: yes`.
|
- Chart types absent from that list and conceptual/diagrammatic graphics (process flows, cycles, quadrant cards, timelines, or a KPI card container) get **no marker** — `svg_quality_checker.py` rejects unsupported marker types. A supported data chart nested inside one of those compositions gets its own marker only when Default §IX or the Quick active-context decision selects that object as native-ready.
|
||||||
- Canonical rectangular merged text cells may carry a table marker by putting anchor-only `row_span` / `col_span` in metadata and leaving covered cells blank. Nonrectangular/overlapping merges, nonblank covered cells, and graphical cells (icons, harvey balls, rating dots) get **no table marker** and stay on the SVG fallback route.
|
- Canonical rectangular merged text cells may carry a table marker by putting anchor-only `row_span` / `col_span` in metadata and leaving covered cells blank. Nonrectangular/overlapping merges, nonblank covered cells, and graphical cells (icons, harvey balls, rating dots) get **no table marker** and stay on the SVG fallback route.
|
||||||
- Transcribe, don't restyle: `categories` / `series[].values` are the numbers just plotted; `style.colors` copies the series HEX values already rendered on the page, whether they use a recurring `spec_lock.colors` anchor or a contextual page-local color.
|
- Transcribe, don't restyle: `categories` / `series[].values` are the numbers just plotted; `style.colors` copies the series HEX values already rendered on the page, whether they use a recurring Default `spec_lock.colors` anchor or a Quick/contextual page-local color.
|
||||||
- Data-point color: when a single column/bar series uses data-point colors in the fallback, copy those fills into `series[].point_colors` in category order.
|
- Data-point color: when a single column/bar series uses data-point colors in the fallback, copy those fills into `series[].point_colors` in category order.
|
||||||
- Data labels: when visible point values are part of the fallback chart, write `data_labels` instead of companion text; use `data_labels.points` for selected labels, and use `number_format`, `font_size`, `font_family`, and per-point `colors` / `color` when the fallback labels carry suffixes or color-coded text.
|
- Data labels: when visible point values are part of the fallback chart, write `data_labels` instead of companion text; use `data_labels.points` for selected labels, and use `number_format`, `font_size`, `font_family`, and per-point `colors` / `color` when the fallback labels carry suffixes or color-coded text.
|
||||||
- Line markers: when the fallback line chart draws visible point nodes, set `line_style: "lineMarker"`; leave the default `line` only for line charts without nodes.
|
- Line markers: when the fallback line chart draws visible point nodes, set `line_style: "lineMarker"`; leave the default `line` only for line charts without nodes.
|
||||||
@@ -93,11 +125,11 @@ catalog or reusable template; normal content edits would make it stale.
|
|||||||
- Value-axis labels: when the fallback keeps category labels but intentionally omits numeric value-axis tick labels, set `show_value_axis_labels: false`.
|
- Value-axis labels: when the fallback keeps category labels but intentionally omits numeric value-axis tick labels, set `show_value_axis_labels: false`.
|
||||||
- Freeform chart text: transcribe center labels, source notes, and other in-chart annotations as companion `caption` / `note` / `notes` entries with explicit slide-coordinate bounds; do not rely on fallback `<text>` children to survive native export.
|
- Freeform chart text: transcribe center labels, source notes, and other in-chart annotations as companion `caption` / `note` / `notes` entries with explicit slide-coordinate bounds; do not rely on fallback `<text>` children to survive native export.
|
||||||
- Native chart typography mirrors the SVG fallback. Copy the fallback's shared chart font into `style.font_family` and visible chart text sizes into the matching metadata fields (`title_font_size`, `subtitle_font_size`, `axis_font_size`, `note_font_size`, etc.) only when role sizes differ; otherwise let the exporter infer them from visible fallback text. When a visible chart title, subtitle, or axis title needs its own size/color/font, write that field as an object with `text`, `font_size`, `font_family`, and `color`. Use `axis_title_font_size`, `legend_font_size`, or companion per-entry `font_size` only when the fallback visibly uses a separate size.
|
- Native chart typography mirrors the SVG fallback. Copy the fallback's shared chart font into `style.font_family` and visible chart text sizes into the matching metadata fields (`title_font_size`, `subtitle_font_size`, `axis_font_size`, `note_font_size`, etc.) only when role sizes differ; otherwise let the exporter infer them from visible fallback text. When a visible chart title, subtitle, or axis title needs its own size/color/font, write that field as an object with `text`, `font_size`, `font_family`, and `color`. Use `axis_title_font_size`, `legend_font_size`, or companion per-entry `font_size` only when the fallback visibly uses a separate size.
|
||||||
- Native table typography mirrors the SVG fallback. Write `style.font_family` and `style.font_size` from the visible table text; use `header_font_size` or per-cell `font_size` only when the fallback visibly does so. If the fallback has no explicit table font, fall back to the deck body family and declared body anchor from `spec_lock.md typography`.
|
- Native table typography mirrors the SVG fallback. Write `style.font_family` and `style.font_size` from the visible table text; use `header_font_size` or per-cell `font_size` only when the fallback visibly does so. If the fallback has no explicit table font, Default uses the deck body family and declared `spec_lock.md` body anchor; Quick uses the concrete body family/size already resolved in active context.
|
||||||
- The marker group's transform stays translate/scale only (no rotate / matrix / skew).
|
- The marker group's transform stays translate/scale only (no rotate / matrix / skew).
|
||||||
- Visual parity is not a goal: the SVG drawing remains the designed visual and exports as editable DrawingML shapes; the native object is the data-backed counterpart with PowerPoint's chart/table-specific model. Never simplify the SVG design to match what a native object could show.
|
- Visual parity is not a goal: the SVG drawing remains the designed visual and exports as editable DrawingML shapes; the native object is the data-backed counterpart with PowerPoint's chart/table-specific model. Never simplify the SVG design to match what a native object could show.
|
||||||
|
|
||||||
**Per-page verification** — after writing a page with planned native-ready objects, enumerate those objects and confirm a one-to-one match: every object has one parent marker and exactly one JSON metadata child. Finding one marker somewhere on a page is insufficient when the page contains multiple planned objects.
|
**Per-page verification** — after writing a page with selected native-ready objects, enumerate those objects and confirm a one-to-one match: every object has one parent marker and exactly one JSON metadata child. Finding one marker somewhere on a page is insufficient when the page contains multiple selected objects.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
rg -n 'data-pptx-replace-with="(chart|table)"|<metadata type="application/json">' <project_path>/svg_output/<current_page>.svg
|
rg -n 'data-pptx-replace-with="(chart|table)"|<metadata type="application/json">' <project_path>/svg_output/<current_page>.svg
|
||||||
@@ -108,27 +140,37 @@ rg -n 'data-pptx-replace-with="(chart|table)"|<metadata type="application/json">
|
|||||||
|
|
||||||
## 3. Visualization Reference
|
## 3. Visualization Reference
|
||||||
|
|
||||||
§1 loads the `page_charts` SVG. Usage gives page-local intent; §IX remains authoritative. Legacy wider rows keep Usage and ignore path/summary fields.
|
§1 loads only a selected page-local SVG. Default takes Usage/semantics from
|
||||||
|
§VII/§IX; Quick takes them from the current active-context page decision.
|
||||||
|
|
||||||
**Hard rule**: treat the loaded SVG as a page-local reference, not a required base. §IX and source data own the final information structure; never replicate the preview verbatim.
|
**Hard rule**: treat the loaded SVG as a page-local reference, not a required base. Default §IX or the Quick active-context page decision plus source data own the final information structure; never replicate the preview verbatim.
|
||||||
|
|
||||||
**Adaptation rules**:
|
**Adaptation rules**:
|
||||||
- **Preserve**: planned information relationships, data encoding, and every content obligation in the active Design Spec
|
- **Preserve**: Default-planned or Quick-resolved information relationships, data encoding, and every active content obligation
|
||||||
- **Page-local only**: a reference row applies only to its mapped page; never spread it across the deck
|
- **Page-local only**: a reference row applies only to its mapped page; never spread it across the deck
|
||||||
- **Flexible realization**: borrow, recombine, or depart from the preview's type and geometry when the current page is better served another way
|
- **Flexible realization**: borrow, recombine, or depart from the preview's type and geometry when the current page is better served another way
|
||||||
- **Carry forward**: every planned label, value, unit, status, source, and explanatory block; never shorten or drop content to imitate a lighter catalog preview
|
- **Carry forward**: every authoritative label, value, unit, status, source, and explanatory block; never shorten or drop content to imitate a lighter catalog preview
|
||||||
- **Adapt**: project data and labels, dimensions, axes, legend, and spacing as the authored content requires
|
- **Adapt**: project data and labels, dimensions, axes, legend, and spacing as the authored content requires
|
||||||
- **Project-owned**: palette, typography, container treatment, effects, background, and page chrome; catalog preview values are fallbacks, never defaults
|
- **Project-owned**: palette, typography, container treatment, effects, background, and page chrome; catalog preview values are fallbacks, never defaults
|
||||||
- **Bound final body modules**: add or revise root-coordinate `data-pptx-bounds` on every visible direct root `<g>` copied into the final page; nested groups need none, chart geometry and local references are not content-boundary inputs, and catalog reference warnings never waive the final-page contract
|
- **Bound final body modules**: add or revise root-coordinate `data-pptx-bounds` on every visible direct root `<g>` copied into the final page; nested groups need none, chart geometry and local references are not content-boundary inputs, and catalog reference warnings never waive the final-page contract
|
||||||
- **Adjust with fidelity**: composition, axis ranges, grouping, and grid may change when the actual content, relationships, hierarchy, and data encoding remain complete
|
- **Adjust with fidelity**: composition, axis ranges, grouping, and grid may change when the actual content, relationships, hierarchy, and data encoding remain complete
|
||||||
- **Forbidden**: treating preview structure as the page specification; omitting planned data points, labels, relationships, or explanatory content to fit it
|
- **Forbidden**: treating preview structure as the page specification; omitting authoritative data points, labels, relationships, or explanatory content to fit it
|
||||||
|
|
||||||
> Templates: `templates/charts/`. `page_charts` maps one optional reference to one page; execution opens only that SVG.
|
> Templates: `templates/charts/`. `page_charts` maps one optional reference to one page; execution opens only that SVG.
|
||||||
|
|
||||||
### 3.1 Chart Coordinate Calibration
|
### 3.1 Chart Coordinate Calibration
|
||||||
|
|
||||||
Coordinate calibration runs as a **conditional post-generation stage**, not inside the Executor authoring loop. After SVG generation completes, if the deck contains data charts, run [`verify-charts`](../workflows/stages/verify-charts.md) before post-processing.
|
Coordinate calibration runs as a **conditional post-generation stage**, not
|
||||||
|
inside the SVG authoring loop. After SVG generation completes, if the deck
|
||||||
|
contains data-driven charts, run
|
||||||
|
[`verify-charts`](../workflows/stages/verify-charts.md). Default follows its
|
||||||
|
declared gate order; Quick runs calibration before its one lockless final
|
||||||
|
checker.
|
||||||
|
|
||||||
The executor's only obligation here is upstream: embed the `<!-- chart-plot-area ... -->` marker on each §IX-planned data-chart page during initial draft (§2.1). Verify-charts enumerates those pages from the authoritative page roster and uses the marker to feed `svg_position_calculator.py`.
|
The authoring obligation is upstream: embed the
|
||||||
|
`<!-- chart-plot-area ... -->` marker on every active data-chart page during the
|
||||||
|
initial draft (§2.1). Verify-charts enumerates Default pages from the Design
|
||||||
|
Spec; Quick enumerates them from the still-active page decisions and cross-checks
|
||||||
|
the markers before feeding `svg_position_calculator.py`.
|
||||||
|
|
||||||
> Do NOT run `svg_position_calculator.py` during the initial draft. The calculator calibrates already-generated SVGs against their declared plot areas; running it before the SVG exists has nothing to compare against.
|
> Do NOT run `svg_position_calculator.py` during the initial draft. The calculator calibrates already-generated SVGs against their declared plot areas; running it before the SVG exists has nothing to compare against.
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
Conditional Executor authority for image status handling, placement, crop behavior, formula images, and template-bundled images.
|
Conditional Executor authority for image status handling, placement, crop behavior, formula images, and template-bundled images.
|
||||||
|
|
||||||
**Trigger**: load for any image/formula in §VIII, the lock, a Quick Generate roster, or a selected template.
|
**Trigger**: load for any image/formula in §VIII, the lock, Quick Generate's active-context resource decisions, or a selected template.
|
||||||
|
|
||||||
## 1. Image Handling
|
## 1. Image Handling
|
||||||
|
|
||||||
Handle images by status; enum and lifecycle: [`svg-image-embedding.md`](svg-image-embedding.md).
|
Handle images by status; enum and lifecycle: [`svg-image-embedding.md`](svg-image-embedding.md).
|
||||||
|
|
||||||
**Mode boundary**: Default keeps Strategist → Executor with no downstream acquisition/reselection. Quick substitutes the main agent's prepared transient roster for §VIII/lock below; the same boundary starts at SVG authoring.
|
**Mode boundary**: Default keeps Strategist → Executor with no downstream acquisition/reselection. Quick substitutes the main agent's prepared active-context resources for §VIII/lock below; the same boundary starts at SVG authoring.
|
||||||
|
|
||||||
| Status | Source | Handling |
|
| Status | Source | Handling |
|
||||||
|--------|--------|----------|
|
|--------|--------|----------|
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ Active when at least one resource row has `Acquire Via: ai` / `web` / `slice`. R
|
|||||||
| Mode | Trigger |
|
| Mode | Trigger |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Default Generate | `generate-ppt` workflow, `design_spec.md §VIII` image rows present |
|
| Default Generate | `generate-ppt` workflow, `design_spec.md §VIII` image rows present |
|
||||||
| Quick Generate | [`quick-generate`](../workflows/profiles/quick-generate.md) is active and its transient resource roster contains image rows |
|
| Quick Generate | [`quick-generate`](../workflows/profiles/quick-generate.md) is active and the current main agent has resolved one or more required images in active context |
|
||||||
| Standalone | Direct request against an existing project |
|
| Standalone | Direct request against an existing project |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Image Resource List Format
|
## 2. Image Resource List Format
|
||||||
|
|
||||||
Default Generate uses Strategist-owned `design_spec.md §VIII` plus its lock projection. Quick Generate substitutes a transient active-context roster; it creates neither planning artifact. Status enum: [`svg-image-embedding.md`](svg-image-embedding.md).
|
Default Generate uses Strategist-owned `design_spec.md §VIII` plus its lock projection. Quick Generate substitutes active-context resource decisions plus required operational manifests; it creates no planning artifact or general resource roster. Status enum: [`svg-image-embedding.md`](svg-image-embedding.md).
|
||||||
|
|
||||||
| Filename | Dimensions | Purpose / Type | Layout pattern | Crop Policy | Acquire Via | Status | Reference |
|
| Filename | Dimensions | Purpose / Type | Layout pattern | Crop Policy | Acquire Via | Status | Reference |
|
||||||
|---|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|
|
||||||
@@ -53,7 +53,7 @@ For each row with `Status: Pending`:
|
|||||||
|
|
||||||
Before processing any row:
|
Before processing any row:
|
||||||
|
|
||||||
1. Read the Default Design Spec/lock, or reuse Quick's transient roster and active visual/page decisions
|
1. Read the Default Design Spec/lock, or reuse Quick's active-context resource and visual/page decisions
|
||||||
2. Group resource list rows by `Acquire Via`
|
2. Group resource list rows by `Acquire Via`
|
||||||
3. Confirm `project/images/` exists
|
3. Confirm `project/images/` exists
|
||||||
4. Materialize explicit user assets, render declared formulas, and finish triggered ai/web/slice acquisition before SVG authoring begins
|
4. Materialize explicit user assets, render declared formulas, and finish triggered ai/web/slice acquisition before SVG authoring begins
|
||||||
@@ -116,7 +116,7 @@ Executor reads the manifest per slide and renders inline credits when needed —
|
|||||||
|
|
||||||
## 8. Intent Ownership
|
## 8. Intent Ownership
|
||||||
|
|
||||||
The `Reference` field is **intent**, not a query. Strategist owns it by default; Quick's main agent owns it in the transient roster. The receiving role translates without reopening it.
|
The `Reference` field is **intent**, not a query. Strategist owns it by default; Quick's main agent resolves it in active context. The receiving role translates without reopening it.
|
||||||
|
|
||||||
| ✅ Intent | ❌ Pre-processed |
|
| ✅ Intent | ❌ Pre-processed |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -127,7 +127,7 @@ The `Reference` field is **intent**, not a query. Strategist owns it by default;
|
|||||||
|
|
||||||
## 9. Handoff with SVG Authoring
|
## 9. Handoff with SVG Authoring
|
||||||
|
|
||||||
SVG authoring consumes the resource roster plus:
|
SVG authoring consumes the active profile's resource authority plus:
|
||||||
|
|
||||||
| Artifact | Path | Purpose |
|
| Artifact | Path | Purpose |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Role definition for the **AI image generation path**: convert each active `Acquire Via: ai` row into an optimized prompt, generate the image, and save it to `project/images/`; also defines the `slice` derivation path for AI-generated illustration sheets.
|
Role definition for the **AI image generation path**: convert each active `Acquire Via: ai` row into an optimized prompt, generate the image, and save it to `project/images/`; also defines the `slice` derivation path for AI-generated illustration sheets.
|
||||||
|
|
||||||
**Trigger**: the Default Generate resource list or Quick Generate transient roster contains `Acquire Via: ai` or `slice`. The role is loaded only when at least one such row exists.
|
**Trigger**: the Default Generate resource list contains `Acquire Via: ai` or `slice`, or Quick Generate has resolved a required AI/sliced image in active context. Load only when at least one such resource exists.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -120,11 +120,11 @@ Derive color behavior from the available roles and image context: background / s
|
|||||||
|
|
||||||
### Step 3 — Per-image type + assembly
|
### Step 3 — Per-image type + assembly
|
||||||
|
|
||||||
For each `Acquire Via: ai` row, use Strategist-owned §VIII/lock by default or the main agent's transient Quick roster. Explicit values remain binding; Quick resolves omissions automatically.
|
For each `Acquire Via: ai` row, use Strategist-owned §VIII/lock by default or the main agent's active-context Quick resource decision. Explicit values remain binding; Quick resolves omissions automatically.
|
||||||
|
|
||||||
`Layout pattern` is a page-realization preference and is not copied wholesale into the bitmap prompt. Any generation-time subject direction, focal placement, quiet region, or overlay-safety requirement must therefore be present in the row's `Reference`, the matching §IX block, or the Quick roster's visual intent.
|
`Layout pattern` is a page-realization preference and is not copied wholesale into the bitmap prompt. Any generation-time subject direction, focal placement, quiet region, or overlay-safety requirement must therefore be present in the row's `Reference`, the matching §IX block, or Quick's active-context visual intent.
|
||||||
|
|
||||||
1. **Determine `page_role`** — the owning row's explicit value wins; a blank or omitted value resolves to `local`. In Default Generate, `hero_page` must be Strategist-explicit; in Quick Generate, the main agent may resolve it while building the transient roster.
|
1. **Determine `page_role`** — the owning row's explicit value wins; a blank or omitted value resolves to `local`. In Default Generate, `hero_page` must be Strategist-explicit; in Quick Generate, the main agent may resolve it before acquisition in active context.
|
||||||
2. **Determine `text_policy`** — the owning row's value wins when set. **Declared-inference fallback for a blank or omitted value**: pick `none` or `embedded` from the row's `Purpose`, `Reference`, and page intent based on whether in-image text serves the page. Long body / data / lists stay in SVG.
|
2. **Determine `text_policy`** — the owning row's value wins when set. **Declared-inference fallback for a blank or omitted value**: pick `none` or `embedded` from the row's `Purpose`, `Reference`, and page intent based on whether in-image text serves the page. Long body / data / lists stay in SVG.
|
||||||
3. **Determine type or free composition** — an Illustration Sheet omits manifest `type` and follows §4.3's grid composition. For another local structural infographic, use one of the 11 types only when the `_index.md` offers a real match; otherwise omit type and author the intended structure directly with §4.1 E. A local single-subject/portrait image omits type and uses §4.1 A/B inside its actual region. A `hero_page` omits type and uses §4.1 A/B/C/D/E.
|
3. **Determine type or free composition** — an Illustration Sheet omits manifest `type` and follows §4.3's grid composition. For another local structural infographic, use one of the 11 types only when the `_index.md` offers a real match; otherwise omit type and author the intended structure directly with §4.1 E. A local single-subject/portrait image omits type and uses §4.1 A/B inside its actual region. A `hero_page` omits type and uses §4.1 A/B/C/D/E.
|
||||||
4. `read_file references/image-type-templates/<type>.md` only when a type was selected (and only if not already read).
|
4. `read_file references/image-type-templates/<type>.md` only when a type was selected (and only if not already read).
|
||||||
@@ -132,7 +132,7 @@ For each `Acquire Via: ai` row, use Strategist-owned §VIII/lock by default or t
|
|||||||
- The rendering's style paragraph (from Step 2)
|
- The rendering's style paragraph (from Step 2)
|
||||||
- Color-role instructions anchored by the deck HEX values and refined for the image context (from Step 2)
|
- Color-role instructions anchored by the deck HEX values and refined for the image context (from Step 2)
|
||||||
- The selected type's structural layout, or the no-type composition prose (from Step 3)
|
- The selected type's structural layout, or the no-type composition prose (from Step 3)
|
||||||
- The image's specific `Reference` intent (from `design_spec.md §VIII` or the Quick Generate transient roster)
|
- The image's specific `Reference` intent (from `design_spec.md §VIII` or the Quick Generate active-context decision)
|
||||||
- Container sizing from the selected type file, or the row's Dimensions for no-type prose
|
- Container sizing from the selected type file, or the row's Dimensions for no-type prose
|
||||||
- The hard rules from §5 below (HEX-not-as-text, rendering-aligned human depiction and likeness authorization, text policy)
|
- The hard rules from §5 below (HEX-not-as-text, rendering-aligned human depiction and likeness authorization, text policy)
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ An illustration sheet can produce several small **spot illustrations** in one ge
|
|||||||
|
|
||||||
**Default — one sheet for a compatible spot family (may override when separate generation serves the assets better)**: Prefer a sheet when several elements share similar proportions, detail, quality, and semantic precision. Generate elements separately when those needs differ materially; quantity alone neither requires nor forbids a sheet. A single hero/local image stays with the normal one-row-per-image flow (§4.1).
|
**Default — one sheet for a compatible spot family (may override when separate generation serves the assets better)**: Prefer a sheet when several elements share similar proportions, detail, quality, and semantic precision. Generate elements separately when those needs differ materially; quantity alone neither requires nor forbids a sheet. A single hero/local image stays with the normal one-row-per-image flow (§4.1).
|
||||||
|
|
||||||
**Hard rule**: a spot sheet is a generation source, not a slide asset. In Default Generate, keep the sheet row out of `spec_lock.md images`; in Quick Generate, mark it generation-only in the transient roster. The sheet is never referenced from SVG. Only sliced element rows are placed.
|
**Hard rule**: a spot sheet is a generation source, not a slide asset. In Default Generate, keep the sheet row out of `spec_lock.md images`; in Quick Generate, retain its generation-only status in active context and the operational manifest. The sheet is never referenced from SVG. Only sliced element rows are placed.
|
||||||
|
|
||||||
**Sheet prompt convention** (one manifest item, `page_role: local`, `text_policy: none`, `image_size` chosen from final placement size):
|
**Sheet prompt convention** (one manifest item, `page_role: local`, `text_policy: none`, `image_size` chosen from final placement size):
|
||||||
|
|
||||||
@@ -280,7 +280,7 @@ Use that deliberately. On a wide sheet (`16:9`, `21:9`, `4:1`, `8:1`), `1xN` mak
|
|||||||
|
|
||||||
If one deck needs mixed shapes, create separate sheets per shape family unless one carefully designed grid gives every element enough room. Keep the visual family consistent through the same `deck_rendering` and `color_scheme`, not by forcing all cells into one square sheet.
|
If one deck needs mixed shapes, create separate sheets per shape family unless one carefully designed grid gives every element enough room. Keep the visual family consistent through the same `deck_rendering` and `color_scheme`, not by forcing all cells into one square sheet.
|
||||||
|
|
||||||
**Resource contract — the sheet and its elements are different row kinds.** A sliced element can only be placed if it exists in the active placeable-resource authority: `spec_lock.md images` in Default Generate or the transient roster in Quick Generate. Default Generate keeps both row kinds in §VIII under [`strategist-image.md`](./strategist-image.md); Quick Generate resolves the same distinction in active context without creating planning artifacts:
|
**Resource contract — the sheet and its elements are different row kinds.** A sliced element can only be placed if it exists in the active placeable-resource authority: `spec_lock.md images` in Default Generate or the current agent's prepared resource decision in Quick Generate. Default Generate keeps both row kinds in §VIII under [`strategist-image.md`](./strategist-image.md); Quick Generate resolves the same distinction in active context and its operational manifest without creating planning artifacts:
|
||||||
|
|
||||||
- **Sheet row** — `Acquire Via: ai`, `Type: Illustration Sheet`, the intent prompt, named as the slice source with its intended cell shape and placement purpose (`Reference: landscape footer-vignette spot set`). It is generated in Step 5 but **never placed on a slide** — keep it **out of** `spec_lock.md images`. Image_Generator resolves the exact `aspect_ratio`, grid, and slice command from this intent.
|
- **Sheet row** — `Acquire Via: ai`, `Type: Illustration Sheet`, the intent prompt, named as the slice source with its intended cell shape and placement purpose (`Reference: landscape footer-vignette spot set`). It is generated in Step 5 but **never placed on a slide** — keep it **out of** `spec_lock.md images`. Image_Generator resolves the exact `aspect_ratio`, grid, and slice command from this intent.
|
||||||
- **Element rows** — one per used element, `Acquire Via: slice`, filename matching a `--names` output, `Reference` naming the parent sheet + cell/element. These **are** placed — list every one in the active placeable-resource authority, normally with `crop=no-crop` (a tight-trimmed transparent spot should be fit, not cover-cropped). Their dimensions are filled in after slicing (the preparation pass re-runs `analyze_images.py`). Each row carries an owner-resolved layout recommendation; SVG authoring may realize it as a direct cutout or inside an appropriate container while preserving the resource and crop/content constraints.
|
- **Element rows** — one per used element, `Acquire Via: slice`, filename matching a `--names` output, `Reference` naming the parent sheet + cell/element. These **are** placed — list every one in the active placeable-resource authority, normally with `crop=no-crop` (a tight-trimmed transparent spot should be fit, not cover-cropped). Their dimensions are filled in after slicing (the preparation pass re-runs `analyze_images.py`). Each row carries an owner-resolved layout recommendation; SVG authoring may realize it as a direct cutout or inside an appropriate container while preserving the resource and crop/content constraints.
|
||||||
@@ -593,14 +593,16 @@ Triggered automatically when `IMAGE_BACKEND` is not configured (or Path A fails)
|
|||||||
- Filenames awaiting manual generation
|
- Filenames awaiting manual generation
|
||||||
- Pointer to `images/image_prompts.md` (paste-ready `### Image N:` block per item) or `image_prompts.json` (`items[].prompt`)
|
- Pointer to `images/image_prompts.md` (paste-ready `### Image N:` block per item) or `image_prompts.json` (`items[].prompt`)
|
||||||
- Target placement: `project/images/<filename>` matching the resource list exactly
|
- Target placement: `project/images/<filename>` matching the resource list exactly
|
||||||
- Resume: Default Generate re-runs Step 7; Quick Generate re-runs its resource gate, final checker, then `--quick-generate`
|
- Continuation: Default Generate re-runs Step 7; Quick may validate the supplied file, rerun its resource gate and final checker, then use `--quick-generate` only while the original active context remains available — otherwise start a clean Quick run
|
||||||
|
|
||||||
**User-initiated**: When Strategist Step 4 captured `manual` in Default Generate, or the user explicitly requested `manual` in the Quick Generate active context, Path A is skipped from the start.
|
**User-initiated**: When Strategist Step 4 captured `manual` in Default Generate, or the user explicitly requested `manual` in the Quick Generate active context, Path A is skipped from the start.
|
||||||
|
|
||||||
> Default Generate tolerates `Needs-Manual` rows through authoring and resumes
|
> Default Generate tolerates `Needs-Manual` rows through authoring and resumes
|
||||||
> at Step 7. Quick Generate preserves the same manifest and handoff but does not
|
> at Step 7. Quick Generate preserves the same operational manifest and handoff
|
||||||
> run `--quick-generate` while a required row still says `Needs-Manual`; validate
|
> but does not run `--quick-generate` while a required row still says
|
||||||
> a later supplied file and update it to `Generated` first.
|
> `Needs-Manual`. If the original active context remains available, validate a
|
||||||
|
> later supplied file and update it to `Generated`; otherwise start a clean
|
||||||
|
> Quick run rather than treating the manifest as a resumable design record.
|
||||||
|
|
||||||
#### AI-specific Failure Handling (extends image-base.md §6)
|
#### AI-specific Failure Handling (extends image-base.md §6)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Role definition for the **web image acquisition path**: translate the active resource owner's intent into keyword queries, search openly-licensed providers, download a license-cleared image into `project/images/`, and record provenance + license metadata into `image_sources.json`.
|
Role definition for the **web image acquisition path**: translate the active resource owner's intent into keyword queries, search openly-licensed providers, download a license-cleared image into `project/images/`, and record provenance + license metadata into `image_sources.json`.
|
||||||
|
|
||||||
**Trigger**: the Default Generate resource list or Quick Generate transient roster contains `Acquire Via: web`. The role is loaded only when at least one such row exists.
|
**Trigger**: the Default Generate resource list contains `Acquire Via: web`, or Quick Generate has resolved a required web image in active context. Load only when at least one such resource exists.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -70,12 +70,12 @@ Keep two layers distinct:
|
|||||||
| Layer | Owner and grammar |
|
| Layer | Owner and grammar |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Default Generate `design_spec.md §VIII Reference` | Strategist's complete visual intent: exact subject, desired view/mood, focal or quiet region, and crop-safety constraints. Positive quality cues are valid here. |
|
| Default Generate `design_spec.md §VIII Reference` | Strategist's complete visual intent: exact subject, desired view/mood, focal or quiet region, and crop-safety constraints. Positive quality cues are valid here. |
|
||||||
| Quick Generate transient `Reference` | Current main agent's active-context intent after honoring explicit user assets, URLs, subjects, and constraints; unspecified choices are resolved automatically without confirmation. |
|
| Quick Generate active `Reference` | Current main agent's active-context intent after honoring explicit user assets, URLs, subjects, and constraints; unspecified choices are resolved automatically without confirmation. |
|
||||||
| `image_queries.json.items[].query` / positional query | Image_Searcher's concrete entity/identity keyword string. Start with the shortest phrase that preserves identity; keep exact multi-word names and necessary disambiguators even when they exceed four words. Omit mood, quality, composition, HEX, and negative wording. |
|
| `image_queries.json.items[].query` / positional query | Image_Searcher's concrete entity/identity keyword string. Start with the shortest phrase that preserves identity; keep exact multi-word names and necessary disambiguators even when they exceed four words. Omit mood, quality, composition, HEX, and negative wording. |
|
||||||
|
|
||||||
Web APIs match metadata, not semantic intent. Providers try the original query first, then progressively simplified four/three/two/one-word variants. A pipeline manifest should therefore use a concise query without pre-truncating exact names. For Chinese landmarks, use the precise Chinese name with Wikimedia; for stock providers, use compact English identity terms when they retain the subject.
|
Web APIs match metadata, not semantic intent. Providers try the original query first, then progressively simplified four/three/two/one-word variants. A pipeline manifest should therefore use a concise query without pre-truncating exact names. For Chinese landmarks, use the precise Chinese name with Wikimedia; for stock providers, use compact English identity terms when they retain the subject.
|
||||||
|
|
||||||
Image_Searcher consumes the active Reference and never rewrites its owner. In Default Generate, that means no rewrite of `design_spec.md` or `spec_lock.md`; in Quick Generate, the transient Reference remains fixed for the run. A candidate either satisfies that existing subject/focal/crop intent, or the role tries materially different query/provider/permitted-license strategies until no untried strategy remains, then marks `Needs-Manual`. Never loosen `required_terms`, the license policy, or the active intent to manufacture a match.
|
Image_Searcher consumes the active Reference and never rewrites its owner. In Default Generate, that means no rewrite of `design_spec.md` or `spec_lock.md`; in Quick Generate, the active-context Reference remains fixed for the run. A candidate either satisfies that existing subject/focal/crop intent, or the role tries materially different query/provider/permitted-license strategies until no untried strategy remains, then marks `Needs-Manual`. Never loosen `required_terms`, the license policy, or the active intent to manufacture a match.
|
||||||
|
|
||||||
When the subject is an exact entity (landmark / person / company / product / venue), write `required_terms` at the same time you write the row's `query`. Use one required group per identity anchor and `|` for aliases / translations, e.g. `["Chongqing|重庆", "Jiefangbei|解放碑|Liberation Monument"]`. This keeps the query short for provider search while preventing metadata-ranked wrong entities from being accepted.
|
When the subject is an exact entity (landmark / person / company / product / venue), write `required_terms` at the same time you write the row's `query`. Use one required group per identity anchor and `|` for aliases / translations, e.g. `["Chongqing|重庆", "Jiefangbei|解放碑|Liberation Monument"]`. This keeps the query short for provider search while preventing metadata-ranked wrong entities from being accepted.
|
||||||
|
|
||||||
@@ -318,7 +318,7 @@ CLI exit: `0` when all attempted rows resolve; `1` while any row remains `Failed
|
|||||||
|
|
||||||
Reference field is **intent description**, not a query. See [`image-base.md`](./image-base.md) §8 for the rule.
|
Reference field is **intent description**, not a query. See [`image-base.md`](./image-base.md) §8 for the rule.
|
||||||
|
|
||||||
Keep it intact as the acceptance contract. In Default Generate the owner is Strategist; in Quick Generate it is the current main agent's transient roster. Derive a separate concise provider query that preserves exact names and necessary disambiguation; do not pass the Reference verbatim or rewrite it after search.
|
Keep it intact as the acceptance contract. In Default Generate the owner is Strategist; in Quick Generate it is the current main agent's active-context resource decision. Derive a separate concise provider query that preserves exact names and necessary disambiguation; do not pass the Reference verbatim or rewrite it after search.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# Modes — Index
|
# Modes — Index
|
||||||
|
|
||||||
A **mode** is the deck's **narrative + persuasion skeleton** — how the argument is organized and advanced across pages. Lock **one mode per deck**; it shapes page sequencing, title voice, page-structure tendencies, and speaker-notes register.
|
A **mode** is the deck's **narrative + persuasion skeleton** — how the argument is organized and advanced across pages. Resolve **one mode per deck**; Default locks it, while Quick keeps it only in active context. It shapes page sequencing, title voice, page-structure tendencies, and speaker-notes register.
|
||||||
|
|
||||||
> A mode is *not* a visual style. **Mode = how you argue; visual style = how it looks** (see [`visual-styles/_index.md`](../visual-styles/_index.md)). The two are locked independently — any mode pairs with any visual style (a `pyramid` deck can look `swiss-minimal` or `dark-tech`).
|
> A mode is *not* a visual style. **Mode = how you argue; visual style = how it looks** (see [`visual-styles/_index.md`](../visual-styles/_index.md)). Resolve the two independently — any mode pairs with any visual style (a `pyramid` deck can look `swiss-minimal` or `dark-tech`).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Catalog (5 modes)
|
## 1. Catalog (5 modes)
|
||||||
|
|
||||||
Each mode has its own file with: narrative skeleton, page-structure tendencies, speaker-notes register, and a page skeleton example. A preset lock reads that one file. A catalog-based `custom` reads every preset named in `mode_references`; a novel `custom` may omit references. Never glob the directory.
|
Each mode has its own file with: narrative skeleton, page-structure tendencies, speaker-notes register, and a page skeleton example. Default reads the locked preset file; Quick reads the preset it resolves in active context. A catalog-based `custom` reads every preset actually used as a basis; a novel `custom` reads none. Never glob the directory.
|
||||||
|
|
||||||
| Mode | Narrative skeleton | Best for |
|
| Mode | Narrative skeleton | Best for |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -18,9 +18,9 @@ Each mode has its own file with: narrative skeleton, page-structure tendencies,
|
|||||||
| [`showcase`](./showcase.md) | Visual-led impact; big imagery / numbers; emotional rhythm | Launches, brand reveals, event / promo decks |
|
| [`showcase`](./showcase.md) | Visual-led impact; big imagery / numbers; emotional rhythm | Launches, brand reveals, event / promo decks |
|
||||||
| [`briefing`](./briefing.md) | Neutral, complete, scannable; topic titles, even weight, no thesis | Status updates, reference decks, catalogs, meeting packs, FAQs |
|
| [`briefing`](./briefing.md) | Neutral, complete, scannable; topic titles, even weight, no thesis | Status updates, reference decks, catalogs, meeting packs, FAQs |
|
||||||
|
|
||||||
> The five are **argument strategies, not a taxonomy of communication purposes**. A presentation may inform + align + request a decision at once; that composite intent stays as open prose in the Stage-1 communication contract. Stage 2 chooses the mode that best carries the dominant body-page spine, or one concrete `custom` act sequence when no preset can serve the stated priority / sequence.
|
> The five are **argument strategies, not a taxonomy of communication purposes**. A presentation may inform + align + request a decision at once; that composite intent stays as open prose in Default's Stage-1 communication contract or Quick's active brief. Default Stage 2 or the Quick main agent chooses the mode that best carries the dominant body-page spine, or one concrete `custom` act sequence when no preset can serve the stated priority / sequence.
|
||||||
>
|
>
|
||||||
> **A mode is a lens, not a mandate over an explicitly preserved structure.** Apply the confirmed `content_divergence` to a user-supplied outline. An ordinary source outline is a Reference that the mode may regroup, reorder, or retitle while preserving its facts and intended relationships. Preserve page order, titles, or wording only when the user presents the outline as the final page plan or explicitly requests that boundary. When the user gives no structure, the mode does the structural lifting. To keep reshaping light, `briefing` imposes the least skeleton.
|
> **A mode is a lens, not a mandate over an explicitly preserved structure.** Default applies the confirmed `content_divergence`; Quick applies the equivalent user-stated or active-context boundary to a supplied outline. An ordinary source outline is a Reference that the mode may regroup, reorder, or retitle while preserving its facts and intended relationships. Preserve page order, titles, or wording only when the user presents the outline as the final page plan or explicitly requests that boundary. When the user gives no structure, the mode does the structural lifting. To keep reshaping light, `briefing` imposes the least skeleton.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -51,12 +51,12 @@ Each mode has its own file with: narrative skeleton, page-structure tendencies,
|
|||||||
|
|
||||||
## 3. How to use
|
## 3. How to use
|
||||||
|
|
||||||
1. Strategist reads this index at confirmation `d. Layer 1`.
|
| Active profile | Use |
|
||||||
2. Preselect one mode from the auto-selection table + the confirmed communication contract and source structure; separately author the visible AI custom candidate required by §4.
|
|---|---|
|
||||||
3. Record the confirmed mode and rationale in `design_spec.md`, then project `- mode: <name>` into `spec_lock.md`.
|
| Default Generate | Strategist reads this index at confirmation `d. Layer 1`, authors the required candidate set, records the confirmed mode in `design_spec.md`, and projects it to `spec_lock.md`; Executor reads the locked preset or exact custom references |
|
||||||
4. Executor reads `modes/<locked-mode>.md` for a preset. For `custom`, it reads every file listed in `mode_references` before applying `mode_behavior`; with no references, it applies the novel behavior directly. Never glob this directory.
|
| Quick Generate | The current main agent reads this index, resolves the best-fit preset or one warranted custom behavior without interaction, reads only the exact preset source(s) actually used, and keeps the result in active context without Design Spec/lock |
|
||||||
|
|
||||||
**Lock scope**: deck-wide (one mode per deck). The five are the catalog you select from; if the structure is genuinely mixed, pick the mode of the body pages and let pages vary within it, or recommend a `custom` blend (§4). Recommend the best fit; the user confirms.
|
**Resolution scope**: deck-wide (one mode per deck). The five are the catalog you select from; if the structure is genuinely mixed, pick the mode of the body pages and let pages vary within it, or use a warranted `custom` blend (§4). Default recommends and confirms; Quick decides directly.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -64,12 +64,14 @@ Each mode has its own file with: narrative skeleton, page-structure tendencies,
|
|||||||
|
|
||||||
`custom` holds **any bespoke narrative direction the five don't give as-is** — and what *kind* of thing it is doesn't matter. It might be a nameable cadence (dialectic 正反合, myth-vs-reality, countdown / Top-N, Socratic), a deliberate multi-act fusion of several modes, or the user's own feel for how the deck should carry (confrontational here, detached there). Don't try to taxonomize it.
|
`custom` holds **any bespoke narrative direction the five don't give as-is** — and what *kind* of thing it is doesn't matter. It might be a nameable cadence (dialectic 正反合, myth-vs-reality, countdown / Top-N, Socratic), a deliberate multi-act fusion of several modes, or the user's own feel for how the deck should carry (confrontational here, detached there). Don't try to taxonomize it.
|
||||||
|
|
||||||
**Always author the candidate; select it only when warranted.** Stage 2 includes one visible, non-empty AI custom proposal beside the five presets, spelling out the cadence / fusion / posture in plain language. It is initially unselected and does not replace the best-fit preset recommendation unless the user already supplied that exact custom direction; with a template, it must fit available prototype capacity. When the user selects it, the editable prose is saved as `mode: custom` plus `mode_behavior`; otherwise it remains recommendation-only. The Strategist crystallizes a selected custom direction in the Design Spec first, then projects the behavior and any actual catalog basis to `spec_lock.md`. The Executor reads every listed basis file before following that prose, or follows it directly when the direction is novel. (This records the intent so it survives 20 pages of generation — the Executor only ever reads `spec_lock.md`, never the chat.)
|
**Default candidate**: Stage 2 includes one visible, non-empty AI custom proposal beside the five presets, spelling out the cadence / fusion / posture in plain language. It is initially unselected and does not replace the best-fit preset recommendation unless the user already supplied that exact custom direction; with a template, it must fit available prototype capacity. When the user selects it, the editable prose is saved as `mode: custom` plus `mode_behavior`; otherwise it remains recommendation-only. The Strategist crystallizes a selected custom direction in the Design Spec first, then projects the behavior and any actual catalog basis to `spec_lock.md`. The Executor reads every listed basis file before following that prose, or follows it directly when the direction is novel.
|
||||||
|
|
||||||
**Mandatory — read every catalog source actually used**: If the proposal combines or borrows an existing mode, name its exact catalog id in the visible proposal and read that mode file before writing the synthesis. A `pyramid` + `narrative` fusion therefore reads both [`pyramid.md`](./pyramid.md) and [`narrative.md`](./narrative.md), then writes `mode_references: pyramid, narrative` beside `mode_behavior`. Do not add loosely related references after the fact. A genuinely new cadence with no catalog source writes no `mode_references` and may proceed from its standalone behavior.
|
**Quick custom**: do not author or display a candidate set. Use `custom` only when no single preset supplies the dominant spine, resolve its behavior directly in active context, and read every catalog source actually used as a basis. Persist nothing.
|
||||||
|
|
||||||
> **One value per deck — fusion is *one* `custom`, not several modes.** A deck always locks a single `mode`. A multi-mode blend is expressed as **one** `mode: custom` whose `mode_behavior` paragraph describes the acts — never by locking several modes.
|
**Mandatory — read every catalog source actually used**: If a custom direction combines or borrows an existing mode, name its exact catalog id and read that mode file before writing the synthesis. A `pyramid` + `narrative` fusion therefore reads both [`pyramid.md`](./pyramid.md) and [`narrative.md`](./narrative.md). Default writes `mode_references: pyramid, narrative` beside `mode_behavior`; Quick retains those bases only in active context. Do not add loosely related references after the fact. A genuinely new cadence reads no catalog source.
|
||||||
|
|
||||||
|
> **One value per deck — fusion is *one* `custom`, not several modes.** A deck always resolves a single `mode`. A multi-mode blend is expressed as **one** custom behavior whose paragraph describes the acts — never as several simultaneous modes.
|
||||||
>
|
>
|
||||||
> **First ask whether it's really fusion.** A locked mode is a *tendency*, not a cage: a `narrative` deck can still carry one analytical (pyramid-style) page, an `instructional` deck one showcase reveal — that is leaning within a dominant mode, and needs **no** `custom`. Reach for `custom` only when there is genuinely no single dominant spine.
|
> **First ask whether it's really fusion.** A resolved mode is a *tendency*, not a cage: a `narrative` deck can still carry one analytical (pyramid-style) page, an `instructional` deck one showcase reveal — that is leaning within a dominant mode, and needs **no** `custom`. Reach for `custom` only when there is genuinely no single dominant spine.
|
||||||
|
|
||||||
**The one thing to avoid**: selecting `custom` as a *dodge* — defaulting to it because picking among the five takes judgment. The custom candidate is mandatory; a custom lock is not. When a preset genuinely fits, keep that preset selected. A user-stated direction remains authoritative the same way a user-supplied outline is — see the lens-not-mandate note in §1.
|
**The one thing to avoid**: selecting `custom` as a *dodge* — defaulting to it because picking among the five takes judgment. Default's custom candidate is mandatory; selecting it is not. Quick creates no candidate. When a preset genuinely fits, keep that preset selected. A user-stated direction remains authoritative the same way a user-supplied outline is — see the lens-not-mandate note in §1.
|
||||||
|
|||||||
+13
-4
@@ -40,9 +40,18 @@ this enum.
|
|||||||
|
|
||||||
## 2. PowerPoint-Native Chart / Table Replacement Markers (Opt-in)
|
## 2. PowerPoint-Native Chart / Table Replacement Markers (Opt-in)
|
||||||
|
|
||||||
Native PowerPoint tables and Excel-backed charts activate at export time only. Generated pages prepare dormant replacement metadata for independently planned native-ready objects while keeping hand-authored SVG geometry pixel-stable across PowerPoint / Keynote / LibreOffice / WPS.
|
Native PowerPoint tables and Excel-backed charts activate at export time only. Generated pages prepare dormant replacement metadata for independently selected native-ready objects while keeping hand-authored SVG geometry pixel-stable across PowerPoint / Keynote / LibreOffice / WPS.
|
||||||
|
|
||||||
**Hard rule — planned-object authoring**: Executor writes the marker and JSON metadata in the same edit only for a supported chart or pure text-grid table whose `design_spec.md §IX` page block says `Native-ready: yes` ([`executor-chart.md`](./executor-chart.md) §2.2). `no` and incidental microvisuals stay on the SVG fallback route. For legacy specs only, a matching §VII value may supply the decision when §IX has no field. Canonical rectangular merged text cells may use the narrow `row_span` / `col_span` contract below; graphical cells stay unmarked. The marker group supplies visible SVG fallback children for browser/live-preview rendering and JSON metadata for `svg_to_pptx` native export.
|
**Hard rule — selected-object authoring**: write the marker and JSON metadata in
|
||||||
|
the same edit only for a supported chart or pure text-grid table selected as
|
||||||
|
native-ready under [`executor-chart.md`](./executor-chart.md) §2.2. Default reads
|
||||||
|
`Native-ready: yes` from §IX (legacy §VII fallback only); Quick makes the
|
||||||
|
decision in active context before drawing. Default `no`, a Quick decision not to
|
||||||
|
use the native object model, and incidental microvisuals stay on the SVG
|
||||||
|
fallback route. Canonical rectangular merged text cells may use the narrow
|
||||||
|
`row_span` / `col_span` contract below; graphical cells stay unmarked. The
|
||||||
|
marker group supplies visible SVG fallback children for browser rendering and
|
||||||
|
JSON metadata for `svg_to_pptx` native export.
|
||||||
|
|
||||||
**Hard rule — activation is the opt-in, dormant unless exported with `--native-charts-and-tables`**: A marker only declares that a group is eligible for PowerPoint-native Chart/Table replacement. Normal `svg_to_pptx.py` runs keep the fallback SVG children and convert them into independently editable DrawingML shapes. Pass `--native-charts-and-tables` only when the data source and chart/table-specific object model matter more than cross-renderer layout fidelity: it emits the PowerPoint Chart/Table object and skips the fallback children to avoid duplicates. Native styling preserves the core palette, text, axis, grid, and background colors where possible, but it is still a PowerPoint Chart/Table object rather than a pixel-identical SVG drawing.
|
**Hard rule — activation is the opt-in, dormant unless exported with `--native-charts-and-tables`**: A marker only declares that a group is eligible for PowerPoint-native Chart/Table replacement. Normal `svg_to_pptx.py` runs keep the fallback SVG children and convert them into independently editable DrawingML shapes. Pass `--native-charts-and-tables` only when the data source and chart/table-specific object model matter more than cross-renderer layout fidelity: it emits the PowerPoint Chart/Table object and skips the fallback children to avoid duplicates. Native styling preserves the core palette, text, axis, grid, and background colors where possible, but it is still a PowerPoint Chart/Table object rather than a pixel-identical SVG drawing.
|
||||||
|
|
||||||
@@ -137,8 +146,8 @@ materialized alternating row fills. Native table typography mirrors the
|
|||||||
visible SVG fallback: put `style.font_family` and `style.font_size` on the
|
visible SVG fallback: put `style.font_family` and `style.font_size` on the
|
||||||
marker from the table text already drawn, then use `style.header_font_size` or
|
marker from the table text already drawn, then use `style.header_font_size` or
|
||||||
per-cell `font_size` only when the fallback visibly differs. If the fallback
|
per-cell `font_size` only when the fallback visibly differs. If the fallback
|
||||||
has no explicit table font, use the deck body family and declared body anchor from
|
has no explicit table font, Default uses the deck body family and declared body
|
||||||
`spec_lock.md`.
|
anchor from `spec_lock.md`; Quick uses its active-context body family and size.
|
||||||
|
|
||||||
**Hard rule — table metadata is the native source of truth**: Every row,
|
**Hard rule — table metadata is the native source of truth**: Every row,
|
||||||
summary line, value, and cell-level style that must survive
|
summary line, value, and cell-level style that must survive
|
||||||
|
|||||||
+18
-4
@@ -1,15 +1,27 @@
|
|||||||
# Shared SVG Core Standards
|
# Shared SVG Core Standards
|
||||||
|
|
||||||
Mandatory reference for every route that authors or regenerates slide visuals through SVG. It owns XML validity, the closed generated-authoring surface, basic converter compatibility, page closure, semantic grouping, and shared fidelity vocabulary.
|
Mandatory reference for every route that authors or regenerates slide visuals through SVG. It owns XML validity, the closed generated-authoring surface, basic converter compatibility, page closure, semantic grouping, shared visual-quality defaults, and fidelity vocabulary.
|
||||||
|
|
||||||
**Conditional module routing**:
|
**Conditional module routing**:
|
||||||
|
|
||||||
| Trigger | Load |
|
| Trigger | Load |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Noncanonical/alpha paint, advanced line or text treatment, gradient/filter/effect, transform, freeform/radial geometry, or constructed style | [`svg-effects.md`](./svg-effects.md) |
|
| Default or Quick Generate; otherwise noncanonical/alpha paint, advanced line or text treatment, gradient/filter/effect, transform, freeform/radial geometry, or constructed style | [`svg-effects.md`](./svg-effects.md) |
|
||||||
| A page will use a preset pattern fill or evaluate native chart/table replacement | [`native-data-interface.md`](./native-data-interface.md) before deciding eligibility or emitting metadata |
|
| A page will use a preset pattern fill or evaluate native chart/table replacement | [`native-data-interface.md`](./native-data-interface.md) before deciding eligibility or emitting metadata |
|
||||||
| `pptx_structure.mode: structured` | [`pptx-structure-interface.md`](./pptx-structure-interface.md) |
|
| `pptx_structure.mode: structured` | [`pptx-structure-interface.md`](./pptx-structure-interface.md) |
|
||||||
|
|
||||||
|
**Default — shared aesthetic baseline (may be overridden by explicit user, installed template / brand, or locked / Quick-resolved visual-style requirements)**: Required / Forbidden technical contracts remain absolute. When a higher authority is silent, build clear hierarchy through typography and leading, alignment, negative space, purposeful imagery / icons, and restrained repetition before decoration. Deliberate tightness, imbalance, off-axis placement, or container-heavy structure remains valid when that authority calls for it.
|
||||||
|
|
||||||
|
| Concern | Shared default |
|
||||||
|
|---|---|
|
||||||
|
| Text-block rhythm | Use §4.2 leading. Make the baseline step into a new paragraph visibly larger than the intra-paragraph line step; keep the extra gap between list items smaller than paragraph separation but large enough to scan each item. Repeated peer blocks share one rhythm unless their hierarchy differs. |
|
||||||
|
| Natural wrapping | Break at semantic phrase or punctuation boundaries where possible. Reflow the text frame or adjust neighboring geometry before using any permitted local size reduction. Let the final line run naturally shorter; avoid mechanically equal lines or a stranded single-character / single-word line when an earlier natural break preserves meaning. |
|
||||||
|
| Alignment and proximity | Establish shared axes from the current composition. Align related titles, copy, labels, images, and diagram nodes to those edges, centers, or baselines; group related elements more tightly than unrelated groups so spacing carries hierarchy. Break an axis only when the offset performs hierarchy, direction, or tension. |
|
||||||
|
| Visual weight | Judge weight from area, darkness, saturation, density, stroke, image detail, and elevation together. Distribute it to support the focal path; symmetry is optional, and deliberate imbalance may create direction. |
|
||||||
|
| Containers | Use a card or panel when it expresses grouping, hierarchy, boundary, capacity, or a distinct material plane. Otherwise prefer spacing, rules, or direct text / geometry; peer containers share treatment unless a semantic difference justifies contrast. |
|
||||||
|
|
||||||
|
**Default — active effects vocabulary (may resolve to no added technique when no visual job is diagnosed)**: Default and Quick Generate run the already-loaded [`svg-effects.md`](./svg-effects.md) §6.1 Visual Job Router before completing each page; apply a compatible technique only for a diagnosed visual job.
|
||||||
|
|
||||||
**Fidelity labels**:
|
**Fidelity labels**:
|
||||||
|
|
||||||
| Label | Meaning |
|
| Label | Meaning |
|
||||||
@@ -571,7 +583,7 @@ Semantic markers are minimal compiler hints. Flat pages declare one root `data-p
|
|||||||
[`../templates/icons/README.md`](../templates/icons/README.md).
|
[`../templates/icons/README.md`](../templates/icons/README.md).
|
||||||
- **Local reuse**: ordinary same-document `<use>` follows §1.3.
|
- **Local reuse**: ordinary same-document `<use>` follows §1.3.
|
||||||
|
|
||||||
### 4.2 Conditional Editability and Package Promotion
|
### 4.2 Editability, Package Promotion, and Text Leading
|
||||||
|
|
||||||
These forms are needed only when the stated PPT behavior matters:
|
These forms are needed only when the stated PPT behavior matters:
|
||||||
|
|
||||||
@@ -583,11 +595,13 @@ These forms are needed only when the stated PPT behavior matters:
|
|||||||
| Free-design / brand-only PowerPoint structure | Use `pptx_structure.mode: flat`. Keep every represented object Slide-local; export materializes one clean project-owned Master plus one Blank Layout from the current lock, removes stock content placeholders/Layout inventory, and retains only the standard date/footer/slide-number capability hooks. Do not author Master/Layout identities, layers, or placeholder slots. Quick-generate uses the same flat object ownership but converter-default theme scaffolding because no lock exists. |
|
| Free-design / brand-only PowerPoint structure | Use `pptx_structure.mode: flat`. Keep every represented object Slide-local; export materializes one clean project-owned Master plus one Blank Layout from the current lock, removes stock content placeholders/Layout inventory, and retains only the standard date/footer/slide-number capability hooks. Do not author Master/Layout identities, layers, or placeholder slots. Quick-generate uses the same flat object ownership but converter-default theme scaffolding because no lock exists. |
|
||||||
| Reusable template-based PowerPoint Layout | Select one complete authoring SVG per page in `page_layouts`, declare each unique Master/Layout definition once, and assign pages through `page_pptx_layouts`. Strict preserves the prototype contract; adaptive retains its Master and uses a current or new Layout key already declared and assigned by Strategist. Construction cannot extend or mutate that mapping downstream. Non-mirror skin follows `spec_lock`. |
|
| Reusable template-based PowerPoint Layout | Select one complete authoring SVG per page in `page_layouts`, declare each unique Master/Layout definition once, and assign pages through `page_pptx_layouts`. Strict preserves the prototype contract; adaptive retains its Master and uses a current or new Layout key already declared and assigned by Strategist. Construction cannot extend or mutate that mapping downstream. Non-mirror skin follows `spec_lock`. |
|
||||||
|
|
||||||
|
**Default — leading by role and density (may be overridden for user, template, typeface, legibility, or locked visual-style fit)**: For direct positioned `<tspan>` rows, start multiline titles around `1.2–1.3 × font-size`, dense / small body around `1.4–1.5 ×`, ordinary body around `1.5–1.6 ×`, and large / sparse / breathing body around `1.6–2.0 ×`. These are starting ranges, not checker quotas; display headlines may be tighter when the selected style calls for it. Author the spacing as positive relative `dy`, not CSS/SVG `line-height`, which has no registered DrawingML mapping.
|
||||||
|
|
||||||
**Hard rule — supported shape conversion**: Every PPT editability claim in this specification refers to the project converter reading `svg_output/` and emitting native DrawingML. `svg_final/` is a self-contained visual preview that may be inserted into PowerPoint as an SVG picture. PowerPoint's manual Convert-to-Shape operation is unsupported; do not narrow the authoring contract to its undocumented SVG subset.
|
**Hard rule — supported shape conversion**: Every PPT editability claim in this specification refers to the project converter reading `svg_output/` and emitting native DrawingML. `svg_final/` is a self-contained visual preview that may be inserted into PowerPoint as an SVG picture. PowerPoint's manual Convert-to-Shape operation is unsupported; do not narrow the authoring contract to its undocumented SVG subset.
|
||||||
|
|
||||||
### 4.3 Element Grouping (Mandatory)
|
### 4.3 Element Grouping (Mandatory)
|
||||||
|
|
||||||
**Hard rule — root groups protect body-text layout**: Every visible direct root `<g>` declares positive root-coordinate `data-pptx-bounds="x y width height"`. Keep it when frame/native coordinates size one PowerPoint object; placeholder bounds also supply the slot frame. On flat pages, make each module zone as generous as the canvas and sibling layout allow without overlapping another module zone. Checker validates this subcanvas against the root `viewBox`, then recursively validates only estimable `<text>` descendants against it using the shared SVG-to-PPTX per-run width estimate and safety headroom. Nested groups and all shapes, images, paths, `<use>` instances, effects, and object frames are not content-boundary inputs. Per side, Checker ignores text/bounds overflow through `1px`, warns through `5%` of the containing boundary dimension, and fails above `5%`. Bounds do not clip or reflow.
|
**Hard rule — root groups protect body-text layout**: Every visible direct root `<g>` declares positive root-coordinate `data-pptx-bounds="x y width height"`. Keep it when frame/native coordinates size one PowerPoint object; placeholder bounds also supply the slot frame. On flat pages, make each module zone as generous as the canvas and sibling layout allow without overlapping another module zone. Checker validates this subcanvas against the root `viewBox`, then recursively validates only estimable `<text>` descendants against it using the shared SVG-to-PPTX per-run width estimate and DrawingML wrapping headroom. It validates every estimable visible text carrier directly against the root `viewBox` with the same per-run estimate before that headroom. Nested groups and all shapes, images, paths, `<use>` instances, effects, and object frames are not module-boundary inputs. Per side, Checker ignores overflow through `1px`; module-boundary overflow warns through `5%` and fails above `5%`, while any larger root-`viewBox` text overflow fails. Bounds do not clip or reflow; unestimable visible text receives an advisory warning. The only page-boundary exception is a wholly off-canvas direct-root Morph endpoint marked `data-pptx-morph-staging="true"`; its own module bounds still apply, retained Morph uses an explicit pair, and the marker never excuses partial page overflow.
|
||||||
|
|
||||||
Wrap each logical Slide-local body unit in one descriptive top-level `<g id>`; group count follows the page's semantic units, and each group becomes one stable animation target when animation is enabled. Generic deck-wide animation gives that target one step; an explicit animation sidecar may assign it several ordered effects. Nested implementation groups may remain anonymous and need no bounds; any nested bounds are ignored. Flat pages use ordinary groups; structured slots already qualify, while titles, direct atomic Master/Layout elements, and canvas-level static framing—including background images and full-canvas scrim/decoration rectangles—may remain root primitives. On flat pages, give such static framing a stable `id` plus `data-pptx-role="background"` / `"decoration"`; never add a `<g>` solely to silence an ungrouped-element advisory.
|
Wrap each logical Slide-local body unit in one descriptive top-level `<g id>`; group count follows the page's semantic units, and each group becomes one stable animation target when animation is enabled. Generic deck-wide animation gives that target one step; an explicit animation sidecar may assign it several ordered effects. Nested implementation groups may remain anonymous and need no bounds; any nested bounds are ignored. Flat pages use ordinary groups; structured slots already qualify, while titles, direct atomic Master/Layout elements, and canvas-level static framing—including background images and full-canvas scrim/decoration rectangles—may remain root primitives. On flat pages, give such static framing a stable `id` plus `data-pptx-role="background"` / `"decoration"`; never add a `<g>` solely to silence an ungrouped-element advisory.
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# Shared Technical Standards
|
# Shared Technical Standards
|
||||||
|
|
||||||
Compatibility router for the split SVG technical specifications. Runtime routes load the core plus only the modules triggered by the current lock and page features.
|
Compatibility router for the split SVG specifications. Runtime routes load the core plus their route-required and feature-triggered modules.
|
||||||
|
|
||||||
| Scope | Authority | Trigger |
|
| Scope | Authority | Trigger |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| XML/SVG foundation, compatibility, page closure, grouping | [`shared-standards-core.md`](./shared-standards-core.md) | Always for SVG authoring |
|
| XML/SVG foundation, shared visual-quality defaults, page closure, grouping | [`shared-standards-core.md`](./shared-standards-core.md) | Always for SVG authoring |
|
||||||
| Advanced effects and geometry | [`svg-effects.md`](./svg-effects.md) | Corresponding effect or geometry is used |
|
| Advanced effects and geometry | [`svg-effects.md`](./svg-effects.md) | Always for Default / Quick Generate; otherwise when the corresponding effect or geometry is used |
|
||||||
| Preset patterns and native chart/table metadata | [`native-data-interface.md`](./native-data-interface.md) | Corresponding native-data interface is used |
|
| Preset patterns and native chart/table metadata | [`native-data-interface.md`](./native-data-interface.md) | Corresponding native-data interface is used |
|
||||||
| Master/Layout/placeholder structure | [`pptx-structure-interface.md`](./pptx-structure-interface.md) | `pptx_structure.mode: structured` |
|
| Master/Layout/placeholder structure | [`pptx-structure-interface.md`](./pptx-structure-interface.md) | `pptx_structure.mode: structured` |
|
||||||
|
|
||||||
**Hard rule**: This file is a routing pointer, not a combined runtime authority. Do not load every conditional module by default.
|
**Hard rule**: This file is a routing pointer, not a combined runtime authority. Follow the selected route's required modules; do not load every remaining conditional module by default.
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ For illustration, apply this precedence: confirmed `none` → explicit user inte
|
|||||||
|
|
||||||
**Context-first understanding for provided assets**: Do not visually scan `images/`. First infer identity, role, and crop / focus needs from source position and surrounding prose, captions / alt / titles, filename, user notes / confirmed `image_notes`, existing resource records, and CSV geometry. Inspect only one specific image when a remaining ambiguity would change selection, factual identity, page role, crop safety, or focal placement. Never inspect for inspiration, bulk-open the folder, or infer external facts / provenance from pixels. Record the result in §VIII. Leave an optional unresolved asset unused; route an unresolved must-use asset through failure recovery.
|
**Context-first understanding for provided assets**: Do not visually scan `images/`. First infer identity, role, and crop / focus needs from source position and surrounding prose, captions / alt / titles, filename, user notes / confirmed `image_notes`, existing resource records, and CSV geometry. Inspect only one specific image when a remaining ambiguity would change selection, factual identity, page role, crop safety, or focal placement. Never inspect for inspiration, bulk-open the folder, or infer external facts / provenance from pixels. Record the result in §VIII. Leave an optional unresolved asset unused; route an unresolved must-use asset through failure recovery.
|
||||||
|
|
||||||
**Default — one coherent sheet for compatible same-family spots (may override when aspect, detail, quality, or semantic needs differ)**: prefer one Illustration Sheet when several AI-generated spots can share a useful cell shape and production treatment; generate them independently when forcing one sheet would weaken a planned element. When a sheet is chosen, plan one unplaced `ai` Illustration Sheet row plus one placed `slice` row per used element; only slice rows enter `spec_lock.md images`. State the intended placement shape family in the sheet reference and use separate sheets for incompatible shapes. [`image-generator.md`](./image-generator.md) §4.3 owns grid, ratio, slicing, and execution details. Stage 3 chooses the AI execution path under `image-generator.md` §7; do not pre-empt or re-pick it here.
|
**Default — one coherent sheet for compatible same-family spots (may override when aspect, detail, quality, or semantic needs differ)**: prefer one Illustration Sheet when several AI-generated spots can share a useful cell shape and production treatment; generate them independently when forcing one sheet would weaken a planned element. When a sheet is chosen, plan one unplaced `ai` Illustration Sheet row plus one placed `slice` row per used element; only slice rows enter `spec_lock.md images`. State the intended placement shape family in the sheet reference and use separate sheets for incompatible shapes. [`image-generator.md`](./image-generator.md) §4.3 owns grid, ratio, slicing, and execution details. Final Stage 2 chooses the AI execution path under `image-generator.md` §7; do not pre-empt or re-pick it here.
|
||||||
|
|
||||||
## 2. AI Image Strategy — propose only for recommended `ai`; lock any confirmed `ai`
|
## 2. AI Image Strategy — propose only for recommended `ai`; lock any confirmed `ai`
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ For specialized or regulated paper-figure subjects, preserve the prompt depth re
|
|||||||
|
|
||||||
## 3. Formula Asset Policy
|
## 3. Formula Asset Policy
|
||||||
|
|
||||||
Formula rendering is a conditional choice surfaced in Stage 3 production confirmation. Recommend one policy and let the user confirm or override it:
|
Formula rendering is a conditional choice surfaced in final Stage 2 production confirmation. Recommend one policy and let the user confirm or override it:
|
||||||
|
|
||||||
| Policy | Behavior | Use |
|
| Policy | Behavior | Use |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
|||||||
+22
-16
@@ -2,35 +2,39 @@
|
|||||||
|
|
||||||
# Strategist Template Planning
|
# Strategist Template Planning
|
||||||
|
|
||||||
Conditional extension for applying an installed Brand/Layout/Deck workspace to Stage 2 recommendations and the execution lock.
|
Conditional extension for applying an installed Brand/Style/Layout/Deck workspace to Stage 2 recommendations and the execution lock.
|
||||||
|
|
||||||
**Trigger**: Load only when Generate Step 3 copied an explicit workspace path into `<project_path>/templates/`. Bare template names, style words, and free-design projects do not trigger this module.
|
**Trigger**: Load only after Stage 1 confirms a library or explicit workspace selection and the post-confirmation apply stage either installs it into `<project_path>/templates/` or confirms that the target project is consuming it in place. Bare template names, style words, and free-design projects do not trigger this module.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. AI-Authored Template Application Plan
|
## 1. AI-Authored Template Application Plan
|
||||||
|
|
||||||
**Template vs preset**: A style mention and a template directory are different inputs. Bare names and style words map to a visual-style preset; only the installed workspace activates the rules below. Its fused `<project_path>/templates/design_spec.md` is the template-design source.
|
**Template vs preset**: A style mention and a Style workspace are different inputs. Bare names and style words remain interpretive input and never resolve to a local path; only a selected and installed workspace activates the rules below. Its fused `<project_path>/templates/design_spec.md` is the template-design source. Treat `kind: style` or a fused provenance entry for `style` as an active Direction / method segment. Whether a source root was labelled `library` or `explicit` is installation provenance only and never affects Stage-2 precedence.
|
||||||
|
|
||||||
**Legacy template boundary**: A template containing `native_structure.json`, `source_template.pptx`, missing root Master identity, direct atomic placeholders, or old `baseline` / `preserve` / distillation metadata is not a Generate Step 3 input. Create a current workspace through [`create-template`](../workflows/create-template.md), preferably from the original PPTX when native topology matters. Do not mutate the input in place.
|
**Legacy template boundary**: A Layout/Deck template containing `native_structure.json`, `source_template.pptx`, missing root Master identity, direct atomic placeholders, or old `baseline` / `preserve` / distillation metadata is not a Generate Step 3 input. Create a current workspace through [`create-template`](../workflows/create-template.md), preferably from the original PPTX when native topology matters. Brand and Style are intentionally roster-free; never reject either for omitting SVG or Master identity. Do not mutate the input in place.
|
||||||
|
|
||||||
**No template-mode confirmation**: Never ask the user to select `template_reuse_scope`, `template_adherence`, `mirror`, `layout`, `style`, `strict`, or `adaptive`. These are internal execution values for the current exporter. The user communicates intent in natural language; explicit instructions such as “全部原样保留”, “从中选合适的页面”, “可以重组”, or “只参考视觉” are authoritative. Without an explicit instruction, Strategist decides.
|
**No template-mode confirmation**: Never ask the user to select `template_reuse_scope`, `template_adherence`, `mirror`, `layout`, `style`, `strict`, or `adaptive`. These are internal execution values for the current exporter. The user communicates intent in natural language; explicit instructions such as “全部原样保留”, “从中选合适的页面”, “可以重组”, or “只参考视觉” are authoritative. Without an explicit instruction, Strategist decides.
|
||||||
|
|
||||||
|
**Hard rule — no Stage-1 influence**: Do not load this module, the installed template spec, prototypes, assets, or template canvas while authoring Stage 1. Stage 1 is already confirmed when this module begins; never revise it to match the workspace.
|
||||||
|
|
||||||
Immediately before authoring the Stage-2 solution, load each relevant template
|
Immediately before authoring the Stage-2 solution, load each relevant template
|
||||||
resource once per path + SHA and inspect:
|
resource once per path + SHA and inspect:
|
||||||
|
|
||||||
- the installed `design_spec.md`, actual Page Roster, and relevant SVG prototypes;
|
- every installed `design_spec.md`; for Layout/Deck only, also inspect the actual Page Roster and relevant SVG prototypes;
|
||||||
- the current communication contract, source obligations, planned page count, and content shape of every planned page;
|
- the resolved Identity, Structure, Reusable Application Context, and Direction / method segment owners from the post-Stage-1 fusion receipt;
|
||||||
|
- the confirmed current communication contract, source obligations, planned page count, and content shape of every planned page;
|
||||||
- the user's natural-language instructions, including any page names/numbers or elements they explicitly require.
|
- the user's natural-language instructions, including any page names/numbers or elements they explicitly require.
|
||||||
|
|
||||||
Then author one plan that decides all of the following without presenting an option menu:
|
Then author one plan that decides all of the following without presenting an option menu:
|
||||||
|
|
||||||
- whether the full prototype set, a relevant subset, or only the design language is useful;
|
- for Layout/Deck, whether the full prototype set, a relevant subset, or only the design language is useful;
|
||||||
- which prototype each generated page starts from, which template pages are skipped, and which prototypes are repeated or reordered;
|
- for Layout/Deck, which prototype each generated page starts from, which template pages are skipped, and which prototypes are repeated or reordered;
|
||||||
- whether content is inserted directly, reorganized inside the existing structure, or rebuilt while retaining only visual language;
|
- whether content is inserted directly, reorganized inside existing structure, or rebuilt under the resolved Direction / method segment;
|
||||||
|
- for Style, which communication method, visual language, composition rhythm, and information-expression defaults are adopted or adapted without inventing page prototypes;
|
||||||
- which visible elements must remain literal because the user said so, and which may change to serve the current content.
|
- which visible elements must remain literal because the user said so, and which may change to serve the current content.
|
||||||
|
|
||||||
Template size is evidence, not policy. A short template may use every prototype when the content genuinely fits; a 20–30 page source may contribute only a few suitable pages, or several pages may be reorganized into a new sequence. Never infer that all pages must be kept or that visible sample content is protected merely because it exists in the template.
|
For Layout/Deck, template size is evidence, not policy. A short template may use every prototype when the content genuinely fits; a 20–30 page source may contribute only a few suitable pages, or several pages may be reorganized into a new sequence. Never infer that all pages must be kept or that visible sample content is protected merely because it exists in the template. Style and Brand have no prototype set.
|
||||||
|
|
||||||
Record the resulting exporter plan internally:
|
Record the resulting exporter plan internally:
|
||||||
|
|
||||||
@@ -38,38 +42,40 @@ Record the resulting exporter plan internally:
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `template_reuse_scope: mirror` | The workspace has `replication_mode: mirror`, the plan calls for literal page reuse, and each page changes only allowed visible text values while preserving visual and text-node topology. |
|
| `template_reuse_scope: mirror` | The workspace has `replication_mode: mirror`, the plan calls for literal page reuse, and each page changes only allowed visible text values while preserving visual and text-node topology. |
|
||||||
| `template_reuse_scope: layout` | The plan reuses the template Master/Layout system and prototypes while allowing current-project content and appearance decisions. |
|
| `template_reuse_scope: layout` | The plan reuses the template Master/Layout system and prototypes while allowing current-project content and appearance decisions. |
|
||||||
| `template_reuse_scope: style` | The plan uses only color, typography, decoration language, or rhythm and intentionally creates flat free-design pages. |
|
| `template_reuse_scope: style` | A Style-only workspace is active, or the plan uses only communication/design direction, color, typography, decoration language, composition, or rhythm and intentionally creates flat free-design pages. |
|
||||||
| `template_adherence: strict` | Every structured page fits an existing prototype contract without changing its Layout identity or slot topology. Mandatory for `template_reuse_scope: mirror`. |
|
| `template_adherence: strict` | Every structured page fits an existing prototype contract without changing its Layout identity or slot topology. Mandatory for `template_reuse_scope: mirror`. |
|
||||||
| `template_adherence: adaptive` | Structured reuse remains useful, but at least one page needs a new explicit Layout under the selected Master. |
|
| `template_adherence: adaptive` | Structured reuse remains useful, but at least one page needs a new explicit Layout under the selected Master. |
|
||||||
|
|
||||||
Write only the derived values to `spec_lock.md pptx_structure`; omit `template_adherence` for `style`. Do not put these internal values in `design_spec.md`, recommendation stage files, the Confirm UI, or `result.json`.
|
Write only the derived values to `spec_lock.md pptx_structure`; omit `template_adherence` for `style`. Do not put these internal values in `design_spec.md`, recommendation stage files, the Confirm UI, or `result.json`.
|
||||||
|
|
||||||
**Mandatory — natural-language Stage-2 plan**: Summarize which prototypes are used/skipped/repeated/reordered, what stays literal, and what may be replaced or reorganized. Write it to top-level `template_application.value` in `recommendations.stage2.json`; omit it without an active template. After Stage 2, re-read the confirmed `result.json` value (or exact chat answer), never the initial recommendation. Blank returns the decision to Strategist. Persist the effective plan on one line as `- **Template Application**: <prose>` in `design_spec.md §I`, then derive internal reuse/adherence values and mappings; never copy the prose to `spec_lock.md`. Do not add a questionnaire, internal controls, or fixed template-use options.
|
**Mandatory — natural-language Stage-2 plan**: For Layout/Deck, summarize which prototypes are used/skipped/repeated/reordered, what stays literal, and what may be replaced or reorganized. For Brand/Style, summarize the installed identity or Direction / method constraints and state that pages remain freely composed unless another workspace supplies structure. Write the result to top-level `template_application.value` in `recommendations.stage2.json`; omit it without an active template. After Stage 2, re-read the confirmed `result.json` value (or exact chat answer), never the initial recommendation. Blank returns the decision to Strategist. Persist the effective plan on one line as `- **Template Application**: <prose>` in `design_spec.md §I`, then derive internal reuse/adherence values and mappings; never copy the prose to `spec_lock.md`. Do not add a questionnaire, internal controls, or fixed template-use options.
|
||||||
|
|
||||||
**Three-stage boundary**: An installed template changes the content of Stage 2, never the confirmation sequence. Run Stage 1 → Stage 2 → Stage 3 in order in both Confirm UI and chat fallback; do not skip a stage or treat template inspection as user confirmation. On browser timeout, return to the same stage in chat.
|
**Two-stage boundary**: An installed template changes the content of final Stage 2, never the confirmation sequence. Run Stage 1 → final Stage 2 in order in both Confirm UI and chat fallback; do not skip a stage or treat template inspection as user confirmation. On browser timeout, return to the same stage in chat.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Scenario Fit and Inherited Design
|
## 2. Scenario Fit and Inherited Design
|
||||||
|
|
||||||
**Mandatory — decide from the §1 inspection**: For an installed `kind: deck`, compare the retained Template Overview with the confirmed audience, intent, outcome, delivery context, artifact afterlife, and source obligations. Compare the retained Page Roster/relevant SVG prototypes with required narrative roles, content shapes, slots, and capacity. Reopen a resource only when its path + SHA changed. The template describes what exists; it never overrides the current project or own required/optional/repeatable or fixed/replaceable/example-only policy. For `kind: layout`, compare only structural roles, slots, and capacity.
|
**Mandatory — decide from the §1 inspection**: For an installed `kind: deck`, compare the retained Template Overview with the confirmed audience, intent, outcome, delivery context, artifact afterlife, and source obligations. Deck application is reusable context for this comparison, never the current project's application contract and never an override. Compare the retained Page Roster/relevant SVG prototypes with required narrative roles, content shapes, slots, and capacity. Reopen a resource only when its path + SHA changed. The template describes what exists; it never overrides the current project or own required/optional/repeatable or fixed/replaceable/example-only policy. For `kind: layout`, compare only structural roles, slots, and capacity. For an active Style segment, compare its communication method with the current contract and its composition requirements with any selected Layout/Deck structure. Surface a material incompatibility; never silently weaken one segment to make it fit.
|
||||||
|
|
||||||
| Internal scope | Appropriate when |
|
| Internal scope | Appropriate when |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `mirror` | The artifact repeats a known form; literal appearance and text topology are requirements; new content fits existing roles and slots. |
|
| `mirror` | The artifact repeats a known form; literal appearance and text topology are requirements; new content fits existing roles and slots. |
|
||||||
| `layout` | The structural system and brand continue, but the communication outcome requires reflow, new emphasis, or an adaptive Layout. |
|
| `layout` | The structural system and brand continue, but the communication outcome requires reflow, new emphasis, or an adaptive Layout. |
|
||||||
| `style` | Only visual identity is reusable, or the outcome requires a different sequence, density, or composition system. |
|
| `style` | Only communication/design direction is reused, a Style-only workspace is active, or the outcome requires a different sequence, density, or composition system. |
|
||||||
|
|
||||||
When the communication contract conflicts with the workspace, choose and state the best-fit application plan in the complete Stage-2 solution. Surface the mismatch only when it materially limits the result; do not respond with a mode questionnaire. Template capability constrains what is legal; scenario fit decides what is useful.
|
When the communication contract conflicts with the workspace, choose and state the best-fit application plan in the complete Stage-2 solution. Surface the mismatch only when it materially limits the result; do not respond with a mode questionnaire. Template capability constrains what is legal; scenario fit decides what is useful.
|
||||||
|
|
||||||
> Internal note: `content_divergence` controls source reorganization; the AI-derived `template_reuse_scope` records the reused layer; `template_adherence` records whether a structured plan keeps or extends existing Layout identities.
|
> Internal note: `content_divergence` controls source reorganization; the AI-derived `template_reuse_scope` records the reused layer; `template_adherence` records whether a structured plan keeps or extends existing Layout identities.
|
||||||
|
|
||||||
**Template design precedence**: User overrides win. Otherwise template colors and title/body stacks are fixed anchors, not industry defaults. Each of ≥3 directions carries six palette roles and complete fonts: repeat fixed values with `typography.fixed: true`; vary only template-open roles. Bundles differ overall; fonts may repeat. Keep declared icon and image constraints.
|
**Template design precedence**: Explicit current user instructions and final confirmation win. Brand identity overrides Deck identity, Layout structure overrides Deck structure, and Deck retains only its non-overridden integrated segments plus reusable application context. Style owns Direction / method only: its color, typography, icon, and image values are candidate defaults and never override resolved Brand/Deck identity or become official facts. Preferred Mode / Visual Style values seed Stage 2; the Style overlay must resolve into the final single `mode` and `visual_style` lock rather than create a parallel narrative or aesthetic authority. Style takes this segment ahead of ordinary Stage-2 defaults; actual Deck prototypes and Signature facts remain compatibility constraints, not a second method owner. Library/explicit provenance never changes this order. Each of ≥3 directions still carries six palette roles and complete fonts: repeat fixed Brand/Deck values with `typography.fixed: true`; adapt Style candidates to that identity and vary only open roles. Keep resolved icon and image constraints. Style Review Focus never activates [`visual-review`](../workflows/stages/visual-review.md); only an explicit user request does.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Structured Lock Planning
|
## 3. Structured Lock Planning
|
||||||
|
|
||||||
|
For Style-only or Style + Brand, write `pptx_structure.mode: flat` plus `template_reuse_scope: style`; omit `template_adherence` and every structured mapping section. When Style is fused with Layout or Deck, it changes only Direction / method and does not change the non-Style capability label or force flat/structured routing. Derive reuse scope from the selected Layout/Deck application plan; a literal `mirror` plan is compatible only when the Style segment requires no visual or topology change.
|
||||||
|
|
||||||
For `mirror` / `layout`, write `pptx_structure.mode: structured` plus `template_adherence: strict|adaptive`; mirror always writes `strict`. Do not write legacy `baseline`, `template`, `preserve`, `layout_strategy`, or Layout-kind rows.
|
For `mirror` / `layout`, write `pptx_structure.mode: structured` plus `template_adherence: strict|adaptive`; mirror always writes `strict`. Do not write legacy `baseline`, `template`, `preserve`, `layout_strategy`, or Layout-kind rows.
|
||||||
|
|
||||||
- **Master roster**: Write one `pptx_masters` row per Master as `<master_key>: <picker name>` and copy the workspace's prototype roster. Keys use 1–64 ASCII letters, digits, dots, underscores, or hyphens, start with a letter/digit, and contain no spaces; human-readable spaces belong only in the picker name. Master visuals are root-level atomic elements and may never be `<g>`.
|
- **Master roster**: Write one `pptx_masters` row per Master as `<master_key>: <picker name>` and copy the workspace's prototype roster. Keys use 1–64 ASCII letters, digits, dots, underscores, or hyphens, start with a letter/digit, and contain no spaces; human-readable spaces belong only in the picker name. Master visuals are root-level atomic elements and may never be `<g>`.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ As a top-tier AI presentation strategist, receive source documents, perform cont
|
|||||||
|
|
||||||
| Previous Step | Current | Next Step |
|
| Previous Step | Current | Next Step |
|
||||||
|--------------|---------|-----------|
|
|--------------|---------|-----------|
|
||||||
| Project creation + Template option confirmed | **Strategist**: Strategist confirmation stage + Design Spec | Image_Generator or Executor |
|
| Project creation + template-candidate preparation complete | **Strategist**: Stage 1 communication/template confirmation → installation handoff → Stage 2 solution + Design Spec | Image_Generator or Executor |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -24,21 +24,27 @@ As a top-tier AI presentation strategist, receive source documents, perform cont
|
|||||||
|
|
||||||
⛔ **BLOCKING**: After the read, present professional recommendations for the confirmation fields below and wait for explicit user confirmation.
|
⛔ **BLOCKING**: After the read, present professional recommendations for the confirmation fields below and wait for explicit user confirmation.
|
||||||
|
|
||||||
**Three-stage confirmation (the default Confirm UI flow; chat mirrors it).** The sequence is scene first, complete solution second, production third:
|
**Two-stage confirmation (the default Confirm UI flow; chat mirrors it).**
|
||||||
|
Generate Step 3 prepares candidates only. Stage 1 confirms the communication
|
||||||
|
contract and template/free-design choice together, while keeping the
|
||||||
|
communication recommendation independent of every template candidate. After
|
||||||
|
that single confirmation, selected workspaces are installed before the complete
|
||||||
|
solution + production gate:
|
||||||
|
|
||||||
| Stage | Items | Role |
|
| Stage | Items | Role |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **1 — communication contract** | `primary_language` · `c` audience · open-ended communication intent · audience outcome · core message / delivery context (primary + optional secondary) / artifact afterlife · `content_divergence` (all prose fields may be blank) · `a` canvas | confirmed first |
|
| **1 — communication contract + template choice** | `primary_language` · `c` audience · open-ended communication intent · audience outcome · core message / delivery context (primary + optional secondary) / artifact afterlife · `content_divergence` (all prose fields may be blank) · `a` canvas · explicit `free_design` or `templates` choice and selected roots | confirmed together; candidate workspaces do not influence the communication recommendation |
|
||||||
| **2 — complete deck solution** (authored once from the user's *actual* Stage 1) | reading mode (`delivery_purpose`, PPT only) · `d` mode + visual style · `b` page count · `e` color · `f` icon · `g` typography · `h` image source + generated-image rendering · conditional natural-language template application | derived from the confirmed contract; internal template exporter modes remain hidden |
|
| **2 — final solution + production** (authored once from the user's *actual* Stage 1) | reading mode (`delivery_purpose`, PPT only) · `d` mode + visual style · `b` page count · `e` color · `f` icon · `g` typography · `h` image source + generated-image rendering · conditional natural-language template application · formula policy · conditional AI-image acquisition path · generation mode · refine-spec toggle · proactive speaker notes / custom animations / narration audio | derived as one coherent plan from the confirmed contract; internal template exporter modes remain hidden |
|
||||||
| **3 — resources / production** (authored once from the user's *actual* Stage 1 + Stage 2) | formula policy · conditional AI-image acquisition path · generation mode · refine-spec toggle · proactive speaker notes / custom animations / narration audio | derived from the confirmed solution |
|
|
||||||
|
|
||||||
Do not force communication intent into one catalog label; Stage 1 records composite intent in prose. Editable prose fields are recommendation drafts, not required inputs: confirmation preserves current text and blanks; never repopulate a cleared field. Stage 2 confirms narrative spine, reading density, page budget, visual system, and image direction. With a template, inspect its actual prototypes/content, present one editable application plan, and keep exporter reuse/adherence internal. Present ≥3 coordinated safe / shifted / bold directions so color, type, icons, and generated-image rendering begin coherent; the user may override each component. Generated images inherit deck colors—there is no second image palette. Stage 3 covers production. Its proactive defaults are speaker notes `true`, custom animations `false`, and narration audio `false`; a prior explicit user instruction overrides the matching recommendation, and effective narration audio requires effective speaker notes. Author each stage once; same-stage edits update only visible browser state through documented deterministic dependencies, without another AI/backend recommendation. Launch/derive/wait mechanics live in [`generate-pptx.md`](../workflows/generate-pptx.md) Step 4; item specs keep `a`–`h`.
|
Do not force communication intent into one catalog label; Stage 1 records composite intent in prose. Editable prose fields are recommendation drafts, not required inputs: confirmation preserves current text and blanks; never repopulate a cleared field. Stage 2 confirms narrative spine, reading density, page budget, visual system, image direction, production mechanics, and how any installed template should be used. It never chooses or installs a template. Inspect only project-local template spec/prototypes, present one editable application plan, and keep exporter reuse/adherence internal. Present ≥3 coordinated safe / shifted / bold directions so color, type, icons, and generated-image rendering begin coherent; the user may override each component. Generated images inherit deck colors—there is no second image palette. Proactive defaults are speaker notes `true`, custom animations `false`, and narration audio `false`; a prior explicit user instruction overrides the matching recommendation, and effective narration audio requires effective speaker notes. Author each stage once; same-stage edits update only visible browser state through documented deterministic dependencies, without another AI/backend recommendation. Launch/derive/wait mechanics live in [`generate-pptx.md`](../workflows/generate-pptx.md) Step 4; item specs keep `a`–`h`.
|
||||||
|
|
||||||
> **Execution discipline**: This is the last always-on BLOCKING checkpoint. After confirmation, proceed without another pause unless spec refinement is enabled.
|
**Hard rule — Stage-1 source boundary**: Build the communication recommendation only from the current user request, source facts, conversation constraints, and project-initialization state. Author it before loading index summaries for a chat listing, and do not read any candidate spec, prototype, asset, or template-owned canvas. The same Stage-1 surface may display template controls, but their values are confirmation state, not recommendation evidence. Do not load or apply [`strategist-template.md`](./strategist-template.md) until Stage 1 is confirmed and the selected workspace has been installed for Stage 2.
|
||||||
|
|
||||||
|
> **Execution discipline**: Step 3 is non-interactive candidate preparation. Stage 1 is the first BLOCKING checkpoint and closes communication plus template/free-design choice in one confirmation. Install/fuse any selection and complete its handoff before authoring final Stage 2. After final confirmation, proceed without another pause unless spec refinement is enabled.
|
||||||
>
|
>
|
||||||
> **One opt-in exception**: present the refinement line with the split-mode note ([`generate-pptx.md`](../workflows/generate-pptx.md) Step 4). Only explicit opt-in runs [`refine-spec`](../workflows/stages/refine-spec.md): write the Design Spec once, pass Gate 1, then stop before the lock for unrestricted chat revision. Never enter it unprompted.
|
> **One opt-in exception**: present the refinement line with the split-mode note ([`generate-pptx.md`](../workflows/generate-pptx.md) Step 4). Only explicit opt-in runs [`refine-spec`](../workflows/stages/refine-spec.md): write the Design Spec once, pass Gate 1, then stop before the lock for unrestricted chat revision. Never enter it unprompted.
|
||||||
|
|
||||||
> **Default presentation surface — Confirm UI.** Before launch, apply [`confirm_ui.md`](../scripts/docs/confirm_ui.md)'s sticky per-run surface decision; its explicit chat branch skips every UI command, and a chat selection after UI launch follows its in-run switch procedure. Chat-question tools alone do not select a branch. In the UI branch, use `<project>/confirm_ui/recommendations.stage1.json`, `.stage2.json`, and `.stage3.json`; replace only the active unconfirmed stage, preserve confirmed files, and print the URL plus Stage-1 summary/fallback without treating that handoff as confirmation. Stage 1 writes canonical BCP-47 `primary_language` apart from UI `lang`; Strategist projects it through Design Spec §I to lock communication. Stage 2 carries ≥3 safe / shifted / bold `design_directions`, each bundling visual style, a six-role HEX palette, primary-language heading/body typography plus an English companion only for non-English decks, icons, and conditional image rendering. Step 4 retains final confirmation from the selected channel for Design Spec authoring. `confirm_ui.md` owns schema and lifecycle.
|
> **Default presentation surface — Confirm UI.** Before the first actual confirmation phase, apply [`confirm_ui.md`](../scripts/docs/confirm_ui.md)'s sticky per-run surface decision; its explicit chat branch skips every UI command, and a chat selection after UI launch follows its in-run switch procedure. Chat-question tools alone do not select a branch. In the UI branch, `template_options.json` and `recommendations.stage1.json` open one Stage-1 page; its single submission writes the pure Strategist `result.json` plus the sidecar `template_selection.json`. After installation/free-design closure, `template_handoff.json` gates `.stage2.json`. The chat/delegated branch keeps equivalent state without fabricating those receipts. Replace only the active unconfirmed stage and print the URL plus combined Stage-1 summary/fallback without treating that handoff as confirmation. Stage 1 writes canonical BCP-47 `primary_language` apart from UI `lang`; Strategist projects it through Design Spec §I to lock communication. Stage 2 carries ≥3 safe / shifted / bold `design_directions`, each bundling visual style, a six-role HEX palette, primary-language heading/body typography plus an English companion only for non-English decks, icons, and conditional image rendering, plus the production fields above. Step 4 retains final confirmation from the selected channel for Design Spec authoring. `confirm_ui.md` owns selection-surface and staged-confirmation lifecycle.
|
||||||
|
|
||||||
**Confirmed-value semantics**: confirmation preserves both the value and the owning field's semantic type. Apply the type to the affected property, not automatically to the whole object:
|
**Confirmed-value semantics**: confirmation preserves both the value and the owning field's semantic type. Apply the type to the affected property, not automatically to the whole object:
|
||||||
|
|
||||||
@@ -50,7 +56,7 @@ Do not force communication intent into one catalog label; Stage 1 records compos
|
|||||||
| Reference | Preserve the selected direction or role; adapt its realization to context. |
|
| Reference | Preserve the selected direction or role; adapt its realization to context. |
|
||||||
| Permission / default | An allowed candidate/source boundary or preference; Strategist may leave it unused, with no quota. |
|
| Permission / default | An allowed candidate/source boundary or preference; Strategist may leave it unused, with no quota. |
|
||||||
|
|
||||||
**Authority chain — materials → Strategist preparation → realization.** User inputs set materials/acquisition bounds. Strategist owns sufficiency, gap-filling, and selection: roster/content, resources, chart/layout keys, fonts, palette anchors, icons, and crop bans. Fact research may precede confirmation; AI/web/slice follows final confirmation plus completed §VIII/lock; icons are synced/validated during authoring. Before Executor, each resource has a path and terminal/`Needs-Manual` state. Executor owns geometry, composition, hierarchy, spacing, treatment; it never searches, generates, syncs, invents, or substitutes resources. Missing material/reselection returns upstream. Specificity defines freedom; References flex realization, never selection.
|
**Authority chain — materials → Strategist preparation → realization.** User inputs set materials/acquisition bounds. Strategist owns sufficiency, gap-filling, and selection: roster/content, resources, chart/layout keys, fonts, palette anchors, the icon library/stroke plus curated project pool, and crop bans. Fact research may precede confirmation; AI/web/slice follows final confirmation plus completed §VIII/lock; icons are synced/validated during authoring without page assignment. Before Executor, each resource has a path and terminal/`Needs-Manual` state. Executor owns geometry, composition, hierarchy, spacing, treatment, and per-page choice among prepared icons; it never searches, generates, syncs, invents, or substitutes resources. Missing material/reselection returns upstream. Specificity defines freedom; References flex realization, never selection.
|
||||||
|
|
||||||
Explicit *must*, *only*, *exactly*, *verbatim*, *do not*, or `no-crop` wording may strengthen only the named property into the appropriate Literal or Semantic requirement. Accepting an AI recommendation keeps the field's default type; it does not promote a Reference or Permission into a Literal requirement.
|
Explicit *must*, *only*, *exactly*, *verbatim*, *do not*, or `no-crop` wording may strengthen only the named property into the appropriate Literal or Semantic requirement. Accepting an AI recommendation keeps the field's default type; it does not promote a Reference or Permission into a Literal requirement.
|
||||||
|
|
||||||
@@ -58,7 +64,7 @@ Explicit *must*, *only*, *exactly*, *verbatim*, *do not*, or `no-crop` wording m
|
|||||||
|
|
||||||
### a. Canvas Format Confirmation
|
### a. Canvas Format Confirmation
|
||||||
|
|
||||||
Recommend format based on scenario (see [`canvas-formats.md`](canvas-formats.md)).
|
Recommend format from the current scenario and project initialization (see [`canvas-formats.md`](canvas-formats.md)). A template canvas is not Stage-1 evidence; Stage 2 later checks whether selected structure can serve the confirmed current-project canvas.
|
||||||
|
|
||||||
### b. Page Count Confirmation
|
### b. Page Count Confirmation
|
||||||
|
|
||||||
@@ -149,7 +155,7 @@ The deck's **visual aesthetic** — shape language, decoration density, whitespa
|
|||||||
|
|
||||||
Record the confirmed visual style and rationale in `design_spec.md` first, including the exact catalog basis when a selected custom uses one. Then project `- visual_style:` to `spec_lock.md`; for `custom`, also project `- visual_style_behavior:` and, only when catalog material is actually used, `- visual_style_references: <id>, <id>`. Executor reads one file for a preset. For `custom`, it reads every listed reference before applying the behavior; an unreferenced novel custom follows the behavior directly.
|
Record the confirmed visual style and rationale in `design_spec.md` first, including the exact catalog basis when a selected custom uses one. Then project `- visual_style:` to `spec_lock.md`; for `custom`, also project `- visual_style_behavior:` and, only when catalog material is actually used, `- visual_style_references: <id>, <id>`. Executor reads one file for a preset. For `custom`, it reads every listed reference before applying the behavior; an unreferenced novel custom follows the behavior directly.
|
||||||
|
|
||||||
**Conditional template workspace**: When Generate Step 3 installed an explicit workspace path into `<project_path>/templates/`, read [`strategist-template.md`](./strategist-template.md) before completing Stage 2. It owns the editable natural-language application plan, confirmed-value consumption, AI-authored prototype selection, internal reuse/adherence derivation, inherited design precedence, and structured-lock planning. Bare names, style words, and free-design projects do not trigger it.
|
**Conditional template workspace**: When the Stage-1 template choice has been installed into `<project_path>/templates/`, read [`strategist-template.md`](./strategist-template.md) before completing Stage 2. Read the installed project-local spec and prototypes only; never reopen the library/external source root. The module owns the editable natural-language application plan, confirmed-value consumption, AI-authored prototype selection, internal reuse/adherence derivation, inherited design precedence, and structured-lock planning. This plan decides how to use the installed template, never which template to select. Bare names, style words, and free-design projects do not trigger it.
|
||||||
|
|
||||||
**Downstream effect**: e / f / g / h realize the locked mode + visual style. Example: `showcase` + `dark-tech` → e applies one luminous accent on a dark field; g pairs a clean sans with mono; f minimal glow icons; h the `digital-dashboard` rendering.
|
**Downstream effect**: e / f / g / h realize the locked mode + visual style. Example: `showcase` + `dark-tech` → e applies one luminous accent on a dark field; g pairs a clean sans with mono; f minimal glow icons; h the `digital-dashboard` rendering.
|
||||||
|
|
||||||
@@ -194,13 +200,13 @@ See [`../templates/icons/README.md`](../templates/icons/README.md) for the curre
|
|||||||
> - This restriction governs Strategist selection from the bundled catalog, not the prepared project asset pool. User-provided, template-carried, imported, custom, and previously prepared files under `<project_path>/icons/` remain valid material regardless of namespace or visual style.
|
> - This restriction governs Strategist selection from the bundled catalog, not the prepared project asset pool. User-provided, template-carried, imported, custom, and previously prepared files under `<project_path>/icons/` remain valid material regardless of namespace or visual style.
|
||||||
> 2. **Stroke weight lock (stroke-style libraries only)** — for stroke-based libraries (currently `tabler-outline`), pick one deck-wide value from `{1.5, 2, 3}` (default `2`). For heavier presence, switch library instead of going above `3`.
|
> 2. **Stroke weight lock (stroke-style libraries only)** — for stroke-based libraries (currently `tabler-outline`), pick one deck-wide value from `{1.5, 2, 3}` (default `2`). For heavier presence, switch library instead of going above `3`.
|
||||||
>
|
>
|
||||||
> **After the Strategist confirmation stage is approved — when writing `design_spec.md` §VI / `spec_lock.md`**, then materialize the icon inventory:
|
> **After the Strategist confirmation stage is approved — when writing `design_spec.md` §VI / `spec_lock.md`**, materialize a curated project icon pool:
|
||||||
>
|
>
|
||||||
> 3. Enumerate only the concepts required by the confirmed outline.
|
> 3. Choose a reusable set that covers recurring semantics and likely slide needs in the confirmed outline. Do not preassign individual icons to pages or add filler to meet a quota.
|
||||||
> 4. Put known basenames in the final batch. For an uncertain one, search the chosen style library — or `simple-icons` for a real brand mark — with `rg --files "skills/ppt-master/templates/icons/<library>" -g '*<keyword>*.svg'`; do not enumerate broad keyword families.
|
> 4. Put known basenames in the final batch. For an uncertain one, search the chosen style library — or `simple-icons` for a real brand mark — with `rg --files "skills/ppt-master/templates/icons/<library>" -g '*<keyword>*.svg'`; do not enumerate broad keyword families.
|
||||||
> 5. **Copy and validate in one batch** — run `python3 skills/ppt-master/scripts/icon_sync.py <project_path> <lib/name> [<lib/name> …]`. This both validates and materializes `<project>/icons/<lib>/`; skip per-file prechecks.
|
> 5. **Copy and validate in one batch** — run `python3 skills/ppt-master/scripts/icon_sync.py <project_path> <lib/name> [<lib/name> …]`. This both validates and materializes `<project>/icons/<lib>/`; skip per-file prechecks.
|
||||||
> 6. Keep each successful, case-sensitive `lib/name`: bundled basenames are lowercase (`tabler-outline/award`, never `tabler-outline/Award`); custom icons retain exact case.
|
> 6. Keep each successful, case-sensitive `lib/name`: bundled basenames are lowercase (`tabler-outline/award`, never `tabler-outline/Award`); custom icons retain exact case.
|
||||||
> 7. Record the successful bundled selection, its primary stylistic library, and any stroke-library `stroke_width` in `design_spec.md` §VI and `spec_lock.md icons`. Keep selected `simple-icons/*` ids in the same inventory without treating them as a second stylistic library. The inventory records the plan; it does not revoke other prepared project-local icons.
|
> 7. Record each synced bundled path with broad suitable scenarios in `design_spec.md` §VI; record the same curated pool, its primary stylistic library, and any stroke-library `stroke_width` in `spec_lock.md icons`. Keep selected `simple-icons/*` ids in the same inventory without treating them as a second stylistic library. The pool is prepared optional material, not a page-use plan, coverage quota, or whitelist over other prepared project-local icons.
|
||||||
>
|
>
|
||||||
> 🚧 **GATE — missing icon = re-pick now**: on non-zero exit, search a missing generic concept only in the chosen stylistic library, or a missing real brand mark in `simple-icons`; re-pick and rerun the final batch until clean. Never carry a missing icon forward or switch among the four stylistic libraries to fill the gap.
|
> 🚧 **GATE — missing icon = re-pick now**: on non-zero exit, search a missing generic concept only in the chosen stylistic library, or a missing real brand mark in `simple-icons`; re-pick and rerun the final batch until clean. Never carry a missing icon forward or switch among the four stylistic libraries to fill the gap.
|
||||||
>
|
>
|
||||||
@@ -360,7 +366,7 @@ creates a §VII row or lock field.
|
|||||||
### Speaker Notes Requirements
|
### Speaker Notes Requirements
|
||||||
|
|
||||||
Resolve the effective Speaker Notes outcome from the latest explicit user
|
Resolve the effective Speaker Notes outcome from the latest explicit user
|
||||||
instruction, then Stage 3 `proactive_speaker_notes`, then compatibility default
|
instruction, then final Stage 2 `proactive_speaker_notes`, then workflow default
|
||||||
`true`. Effective Narration Audio `enabled` requires Speaker Notes `enabled`
|
`true`. Effective Narration Audio `enabled` requires Speaker Notes `enabled`
|
||||||
without changing the raw proactive preference; when that dependency changes the
|
without changing the raw proactive preference; when that dependency changes the
|
||||||
notes outcome, its provenance names enabled Narration Audio.
|
notes outcome, its provenance names enabled Narration Audio.
|
||||||
@@ -459,11 +465,11 @@ After final confirmation, a newer explicit notes/animation/narration instruction
|
|||||||
updates only affected §I outcomes/provenance and resumes their owner; never
|
updates only affected §I outcomes/provenance and resumes their owner; never
|
||||||
reopen Confirm UI or add them to `spec_lock.md`. Before editing, apply
|
reopen Confirm UI or add them to `spec_lock.md`. Before editing, apply
|
||||||
Generate's notes/audio dependency gate. Record animation provenance as
|
Generate's notes/audio dependency gate. Record animation provenance as
|
||||||
Stage 3 `false`, explicit objects-off, or explicit all-motion-off; only the last
|
final Stage 2 `false`, explicit objects-off, or explicit all-motion-off; only the last
|
||||||
includes transitions.
|
includes transitions.
|
||||||
|
|
||||||
1. With Generate Step 4's retained complete final-confirmation state, read `${SKILL_DIR}/templates/design_spec_reference.md`.
|
1. With Generate Step 4's retained complete final-confirmation state, read `${SKILL_DIR}/templates/design_spec_reference.md`.
|
||||||
2. Compose the whole Design Spec in active context before touching the target path. Create `design_spec.md` once from the schema marker through §X; do not copy a scaffold into the project or patch placeholder fields. Record production mechanics in §I, including one effective outcome plus provenance for Speaker Notes, Custom Animations, and Narration Audio. Resolve them from latest explicit user instruction → matching Stage 3 proactive value → compatibility default `enabled` / `disabled` / `disabled`; Narration Audio enabled requires Speaker Notes enabled without rewriting the raw proactive evidence, and a dependency-driven notes outcome records that provenance. In §IX, create the complete ordered roster; each entry carries layout, title, core message, **Audience move**, complete preferred wording, applicable capability recommendations, visualization/image references, sourced `Fact IDs`, and `Data class: scenario` for invented demo data. After Gate 1 plus conditional refine approval, roster ids/count/order and semantic content are authoritative; non-literal wording, block texture, layout, cover/closing composition, capability recommendations, and image/chart patterns remain References unless promoted.
|
2. Compose the whole Design Spec in active context before touching the target path. Create `design_spec.md` once from the schema marker through §X; do not copy a scaffold into the project or patch placeholder fields. Record production mechanics in §I, including one effective outcome plus provenance for Speaker Notes, Custom Animations, and Narration Audio. Resolve them from latest explicit user instruction → matching final Stage 2 proactive value → workflow default `enabled` / `disabled` / `disabled`; Narration Audio enabled requires Speaker Notes enabled without rewriting the raw proactive evidence, and a dependency-driven notes outcome records that provenance. In §IX, create the complete ordered roster; each entry carries layout, title, core message, **Audience move**, complete preferred wording, applicable capability recommendations, visualization/image references, sourced `Fact IDs`, and `Data class: scenario` for invented demo data. After Gate 1 plus conditional refine approval, roster ids/count/order and semantic content are authoritative; non-literal wording, block texture, layout, cover/closing composition, capability recommendations, and image/chart patterns remain References unless promoted.
|
||||||
3. Compare `design_spec.md` against the final confirmation field by field. Repair every omission or deviation before entering an enabled refine-spec review or authoring `spec_lock.md`.
|
3. Compare `design_spec.md` against the final confirmation field by field. Repair every omission or deviation before entering an enabled refine-spec review or authoring `spec_lock.md`.
|
||||||
4. If enabled, run [`refine-spec`](../workflows/stages/refine-spec.md) after Gate 1; edit only that Design Spec and create no lock before explicit approval.
|
4. If enabled, run [`refine-spec`](../workflows/stages/refine-spec.md) after Gate 1; edit only that Design Spec and create no lock before explicit approval.
|
||||||
5. Read `${SKILL_DIR}/templates/spec_lock_reference.md`; create the lock once or resynchronize stale derived state from the approved Design Spec and context. Retain identity/refinements and stable roles/routing; omit unnamed page-local values, do not reopen evidence, and make no new recommendation.
|
5. Read `${SKILL_DIR}/templates/spec_lock_reference.md`; create the lock once or resynchronize stale derived state from the approved Design Spec and context. Retain identity/refinements and stable roles/routing; omit unnamed page-local values, do not reopen evidence, and make no new recommendation.
|
||||||
|
|||||||
+2
-2
@@ -16,7 +16,7 @@ and filter/clip contracts.
|
|||||||
| Mode | Resource authority and preparation timing |
|
| Mode | Resource authority and preparation timing |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Default Generate | `design_spec.md §VIII` plus its lock projection; when user-provided images are selected, run `analyze_images.py` after Strategist confirmation and complete the list before Executor |
|
| Default Generate | `design_spec.md §VIII` plus its lock projection; when user-provided images are selected, run `analyze_images.py` after Strategist confirmation and complete the list before Executor |
|
||||||
| Quick Generate | Current main agent's transient active-context roster; materialize explicit user paths first, resolve unspecified acquisition decisions automatically, and finish user/ai/web/slice/formula preparation before SVG authoring without confirmation |
|
| Quick Generate | Current main agent's active-context resource decisions; materialize explicit user paths first, resolve unspecified acquisition decisions automatically, and finish user/ai/web/slice/formula preparation before SVG authoring without confirmation or a persisted roster |
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
| Filename | Dimensions | Purpose | Type | Layout pattern | Crop Policy | Acquire Via | Status | Reference |
|
| Filename | Dimensions | Purpose | Type | Layout pattern | Crop Policy | Acquire Via | Status | Reference |
|
||||||
@@ -45,7 +45,7 @@ and filter/clip contracts.
|
|||||||
```
|
```
|
||||||
1. Resolve image needs:
|
1. Resolve image needs:
|
||||||
- Default Generate → Strategist-owned resource list + lock projection
|
- Default Generate → Strategist-owned resource list + lock projection
|
||||||
- Quick Generate → current main agent builds a transient roster in active context; explicit user paths/URLs/choices win, unspecified choices use automatic resolution, no interaction
|
- Quick Generate → current main agent resolves the required resource in active context; explicit user paths/URLs/choices win, unspecified choices use automatic resolution, no interaction or persisted roster
|
||||||
2. Prepare project-local resources before SVG authoring:
|
2. Prepare project-local resources before SVG authoring:
|
||||||
- user → materialize the explicit source under project/images/ → Existing
|
- user → materialize the explicit source under project/images/ → Existing
|
||||||
- formula → write formula_manifest.json and run latex_render.py → Rendered
|
- formula → write formula_manifest.json and run latex_render.py → Rendered
|
||||||
|
|||||||
@@ -589,13 +589,15 @@ If suitable template resources already exist, use them directly instead of gener
|
|||||||
|
|
||||||
This section describes downstream reuse of an existing workspace. Library and project scopes carry the same portable template contract.
|
This section describes downstream reuse of an existing workspace. Library and project scopes carry the same portable template contract.
|
||||||
|
|
||||||
**Example library structure** (query the appropriate kind's index — `templates/layouts/layouts_index.json` for brand-neutral structure templates, `templates/decks/decks_index.json` for recurring applications with integrated identity/structure, `templates/brands/brands_index.json` for identity-only presets):
|
**Example library structure** (query the appropriate kind's index — `templates/brands/brands_index.json` for identity, `templates/styles/styles_index.json` for roster-free direction/method, `templates/layouts/layouts_index.json` for brand-neutral structure, and `templates/decks/decks_index.json` for recurring applications with integrated identity/structure):
|
||||||
|
|
||||||
```
|
```
|
||||||
templates/
|
templates/
|
||||||
├── brands/
|
├── brands/
|
||||||
│ ├── anthropic/ # Anthropic brand identity (logo + colors + typography)
|
│ ├── anthropic/ # Anthropic brand identity (logo + colors + typography)
|
||||||
│ └── google/ # Google brand identity
|
│ └── google/ # Google brand identity
|
||||||
|
├── styles/
|
||||||
|
│ └── <style_id>/ # Communication method and design direction; no SVG roster
|
||||||
├── layouts/
|
├── layouts/
|
||||||
│ └── presentation_core/ # General structure system (no identity)
|
│ └── presentation_core/ # General structure system (no identity)
|
||||||
└── decks/
|
└── decks/
|
||||||
|
|||||||
@@ -25,9 +25,12 @@ Each review subagent processes a **batch** of pages (see §6.1 for batch sizing)
|
|||||||
2. **Path to this rubric file**
|
2. **Path to this rubric file**
|
||||||
3. **`<project>/design_spec.md`** (read-only) — §IX outline is the source of truth for "what should this page deliver"
|
3. **`<project>/design_spec.md`** (read-only) — §IX outline is the source of truth for "what should this page deliver"
|
||||||
4. **`<project>/spec_lock.md`** (read-only) — brand-locked values
|
4. **`<project>/spec_lock.md`** (read-only) — brand-locked values
|
||||||
5. **`<project>/.review/`** (writable) — where backups and findings JSON go
|
5. **Style Review Focus excerpt** (conditional, read-only) — supplied by the orchestrator only when `<project>/templates/design_spec.md` contains an active Style segment; it retains the source path and exact §VII wording
|
||||||
|
6. **`<project>/.review/`** (writable) — where backups and findings JSON go
|
||||||
|
|
||||||
The subagent reads inputs 2–4 **once** at the start of its turn, then iterates over the page batch sequentially (one page at a time): apply the rubric → write `<project>/.review/<page>.json` → move on. This is the core token-saving move — three fixed documents are read N/K times instead of N times.
|
The subagent reads inputs 2–5 **once** at the start of its turn (input 5 may be absent), then iterates over the page batch sequentially (one page at a time): apply the rubric → apply any Style supplement → write `<project>/.review/<page>.json` → move on. This is the core token-saving move — fixed context is read N/K times instead of N times.
|
||||||
|
|
||||||
|
Style Review Focus is supplemental acceptance context, not a second rubric. It cannot create new Hard rules, weaken §§1–3, or authorize content, identity, or structural edits. Record a clearly unmet focus item as `rule: "STYLE"`: apply a fix only when the existing rubric already permits that atomic edit; otherwise add a `needs_human_items` entry with a concise suggested fix.
|
||||||
|
|
||||||
## §1 Hard rules (fix every hit)
|
## §1 Hard rules (fix every hit)
|
||||||
|
|
||||||
|
|||||||
+14
-12
@@ -1,18 +1,18 @@
|
|||||||
# Visual Styles — Index
|
# Visual Styles — Index
|
||||||
|
|
||||||
A **visual style** is how the deck **looks** — shape language, decoration density, whitespace rhythm, typographic character, texture / elevation. Lock **one per deck**; it anchors the aesthetic of the SVG layout itself (cards, dividers, spacing, corner radius, shadow use).
|
A **visual style** is how the deck **looks** — shape language, decoration density, whitespace rhythm, typographic character, texture / elevation. Resolve **one per deck**; Default locks it, while Quick keeps it only in active context. It anchors the aesthetic of the SVG layout itself (cards, dividers, spacing, corner radius, shadow use).
|
||||||
|
|
||||||
> **Styles carry NO fixed HEX and lock no palette.** Core color identity and recurring role behavior live in `design_spec.colors` / `spec_lock.colors` (confirmation `e`). A visual style describes how those anchors behave in SVG composition and may call for contextual tints, gradients, effects, or material transitions; it does not substitute an unrelated palette. Generated images follow the same anchor model through [`image-renderings/`](../image-renderings/). [`image-palettes/`](../image-palettes/) is legacy compatibility material only.
|
> **Styles carry NO fixed HEX and define no palette.** Default core color identity and recurring role behavior live in `design_spec.colors` / `spec_lock.colors` (confirmation `e`); Quick resolves equivalent palette anchors in active context. A visual style describes how those anchors behave in SVG composition and may call for contextual tints, gradients, effects, or material transitions; it does not substitute an unrelated palette. Generated images follow the same anchor model through [`image-renderings/`](../image-renderings/). [`image-palettes/`](../image-palettes/) is legacy compatibility material only.
|
||||||
>
|
>
|
||||||
> A visual style is *not* a mode. **Visual style = how it looks; mode = how you argue** (see [`modes/_index.md`](../modes/_index.md)). Locked independently — any style pairs with any mode.
|
> A visual style is *not* a mode. **Visual style = how it looks; mode = how you argue** (see [`modes/_index.md`](../modes/_index.md)). Resolve them independently — any style pairs with any mode.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Catalog
|
## 1. Catalog
|
||||||
|
|
||||||
Each style has its own file with: shape & decoration, typography character, color-usage discipline (no HEX), texture / elevation, and the paired image-rendering. A preset lock reads that one file. A catalog-based `custom` reads every preset named in `visual_style_references`; a novel `custom` may omit references. Never glob the directory. The catalog mirrors [`image-renderings`](../image-renderings/_index.md): each style's "Paired rendering" names the illustration family that shares its aesthetic.
|
Each style has its own file with: shape & decoration, typography character, color-usage discipline (no HEX), texture / elevation, and the paired image-rendering. Default reads the locked preset file; Quick reads the preset it resolves in active context. A catalog-based `custom` reads every preset actually used as a basis; a novel `custom` reads none. Never glob the directory. The catalog mirrors [`image-renderings`](../image-renderings/_index.md): each style's "Paired rendering" names the illustration family that shares its aesthetic.
|
||||||
|
|
||||||
> The **`visual_style` value is only ever a first-column `id`** (`swiss-minimal`, `editorial`, …). The "Paired rendering" column lists **§h image-rendering** names (`flat`, `minimalist-swiss`, `digital-dashboard`, …) — never lock one of those as the `visual_style`; they belong to confirmation h.
|
> The **`visual_style` value is only ever a first-column `id`** (`swiss-minimal`, `editorial`, …). The "Paired rendering" column lists **image-rendering** names (`flat`, `minimalist-swiss`, `digital-dashboard`, …) — never treat one of those as the `visual_style`. Default records rendering under confirmation h; Quick keeps the selected rendering only in active context and any required image manifest.
|
||||||
>
|
>
|
||||||
> The **`Illus.`** column is each style's spot-illustration propensity — `core` (illustration is intrinsic to the look), `supportive` (use where it lifts, restrained), or `sparse` (the style's lead visual competes; default off). It sets the **default lean** only, for when the user gives no steer: an explicit user request to use / skip illustrations overrides it either way, and `image_usage: none` always writes no illustration rows. Full per-style rule in each file's §6.
|
> The **`Illus.`** column is each style's spot-illustration propensity — `core` (illustration is intrinsic to the look), `supportive` (use where it lifts, restrained), or `sparse` (the style's lead visual competes; default off). It sets the **default lean** only, for when the user gives no steer: an explicit user request to use / skip illustrations overrides it either way, and `image_usage: none` always writes no illustration rows. Full per-style rule in each file's §6.
|
||||||
|
|
||||||
@@ -92,17 +92,19 @@ Each style has its own file with: shape & decoration, typography character, colo
|
|||||||
|
|
||||||
## 3. Escape hatch — `custom`
|
## 3. Escape hatch — `custom`
|
||||||
|
|
||||||
Stage 2 always authors one visible, non-empty AI custom proposal beside the preset spectrum. Its paragraph names shape language, composition geometry (page-scale moves), decoration density, whitespace, typographic character, and texture — **no HEX, no color names as values**. The proposal is initially unselected and remains recommendation-only unless the user chooses it; a template-backed proposal must stay inside the inherited identity and confirmed application plan. When selected, record the edited aesthetic in the Design Spec first, then project `- visual_style: custom` plus `- visual_style_behavior:` to `spec_lock.md`. The candidate is mandatory; selecting `custom` remains a tail-case, not the default.
|
Default Stage 2 always authors one visible, non-empty AI custom proposal beside the preset spectrum. Its paragraph names shape language, composition geometry (page-scale moves), decoration density, whitespace, typographic character, and texture — **no HEX, no color names as values**. The proposal is initially unselected and remains recommendation-only unless the user chooses it; a template-backed proposal must stay inside the inherited identity and confirmed application plan. When selected, record the edited aesthetic in the Design Spec first, then project `- visual_style: custom` plus `- visual_style_behavior:` to `spec_lock.md`. The candidate is mandatory; selecting `custom` remains a tail-case, not the default.
|
||||||
|
|
||||||
**Mandatory — read every catalog source actually used**: If a custom proposal combines or borrows existing styles, name their exact ids in the visible proposal and read each corresponding file before synthesizing its shape, composition, typography, whitespace, and texture rules. Persist those ids as `visual_style_references`. Do not attach references merely because they are adjacent in the catalog. A genuinely new aesthetic with no catalog source omits `visual_style_references` and proceeds from its standalone behavior.
|
Quick does not author or display a candidate spectrum. It resolves the best-fit preset directly, or one custom behavior only when no preset fits, reads every catalog source actually used as a basis, and persists nothing.
|
||||||
|
|
||||||
|
**Mandatory — read every catalog source actually used**: If a custom direction combines or borrows existing styles, name their exact ids and read each corresponding file before synthesizing its shape, composition, typography, whitespace, and texture rules. Default persists those ids as `visual_style_references`; Quick retains them only in active context. Do not attach references merely because they are adjacent in the catalog. A genuinely new aesthetic reads no catalog source.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. How to use
|
## 4. How to use
|
||||||
|
|
||||||
1. Strategist reads this index at confirmation `d. Layer 2`.
|
| Active profile | Use |
|
||||||
2. Preselect one style from the auto-selection table + the deck's vibe; separately author the visible AI custom proposal from §3.
|
|---|---|
|
||||||
3. Record the confirmed style and rationale in `design_spec.md`, then project `- visual_style: <name>` into `spec_lock.md`.
|
| Default Generate | Strategist reads this index at confirmation `d. Layer 2`, authors the required spectrum/custom candidate, records the confirmed style in `design_spec.md`, and projects it to `spec_lock.md`; Executor reads the locked preset or exact custom references |
|
||||||
4. Executor reads `visual-styles/<locked-style>.md` for a preset. For `custom`, it reads every file listed in `visual_style_references` before applying `visual_style_behavior`; with no references, it applies the novel behavior directly. Never glob this directory.
|
| Quick Generate | The current main agent reads this index, resolves the best-fit preset or one warranted custom behavior without interaction, reads only the exact preset source(s) actually used, and keeps the result in active context without Design Spec/lock |
|
||||||
|
|
||||||
**Lock scope**: deck-wide (one style per deck). It anchors taste as a **reference**, not a whitelist — pages may deviate with reason.
|
**Resolution scope**: deck-wide (one style per deck). It anchors taste as a **reference**, not a whitelist — pages may deviate with reason.
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ python3 scripts/animation_config.py scaffold <project_path> # optional object-l
|
|||||||
python3 scripts/svg_to_pptx.py <project_path>
|
python3 scripts/svg_to_pptx.py <project_path>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
After `init`, project-scoped Python CLIs automatically record their command
|
||||||
|
envelopes and bounded material outcomes in
|
||||||
|
`<project_path>/validation/workflow.log`; invoke them directly, without a
|
||||||
|
logging wrapper. The log does not copy the full console stream.
|
||||||
|
|
||||||
Repository update:
|
Repository update:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -46,7 +51,7 @@ python3 scripts/update_repo.py
|
|||||||
| Area | Primary scripts | Documentation |
|
| Area | Primary scripts | Documentation |
|
||||||
|------|-----------------|---------------|
|
|------|-----------------|---------------|
|
||||||
| Conversion | `source_to_md.py`, `source_to_md/pdf_to_md.py`, `source_to_md/doc_to_md.py`, `source_to_md/excel_to_md.py`, `source_to_md/ppt_to_md.py`, `source_to_md/web_to_md.py`, `pptx_intake.py`, `pptx_to_svg.py` | [docs/conversion.md](./docs/conversion.md) |
|
| Conversion | `source_to_md.py`, `source_to_md/pdf_to_md.py`, `source_to_md/doc_to_md.py`, `source_to_md/excel_to_md.py`, `source_to_md/ppt_to_md.py`, `source_to_md/web_to_md.py`, `pptx_intake.py`, `pptx_to_svg.py` | [docs/conversion.md](./docs/conversion.md) |
|
||||||
| Project management | `project_manager.py`, `batch_validate.py`, `generate_examples_index.py`, `error_helper.py`, `pptx_template_import.py`, `template_fill_pptx.py`, `native_enhance_pptx.py`, `pptx_delivery_check.py` | [docs/project.md](./docs/project.md) |
|
| Project management | `project_manager.py`, `workflow_log.py`, `workflow_transcript.py`, `batch_validate.py`, `generate_examples_index.py`, `error_helper.py`, `pptx_template_import.py`, `template_fill_pptx.py`, `native_enhance_pptx.py`, `pptx_delivery_check.py` | [docs/project.md](./docs/project.md) |
|
||||||
| SVG pipeline | `preset_shape_svg.py`, `shape_boolean_svg.py`, `svg_authoring_view.py`, `compact_svg_coordinates.py`, `mirror_template_materialize.py`, `finalize_svg.py`, `svg_to_pptx.py`, `template_preview_pptx.py`, `total_md_split.py`, `svg_quality_checker.py`, `extract_svg_assets.py`, `extract_svg_pictures.py`, `animation_config.py`, `notes_to_audio.py`, `narration_sync.py` | [docs/svg-pipeline.md](./docs/svg-pipeline.md); [native shape authoring](../references/native-shape-authoring.md) |
|
| SVG pipeline | `preset_shape_svg.py`, `shape_boolean_svg.py`, `svg_authoring_view.py`, `compact_svg_coordinates.py`, `mirror_template_materialize.py`, `finalize_svg.py`, `svg_to_pptx.py`, `template_preview_pptx.py`, `total_md_split.py`, `svg_quality_checker.py`, `extract_svg_assets.py`, `extract_svg_pictures.py`, `animation_config.py`, `notes_to_audio.py`, `narration_sync.py` | [docs/svg-pipeline.md](./docs/svg-pipeline.md); [native shape authoring](../references/native-shape-authoring.md) |
|
||||||
| PPTX transitions | `pptx_transitions.py` | [docs/pptx-transitions.md](./docs/pptx-transitions.md) |
|
| PPTX transitions | `pptx_transitions.py` | [docs/pptx-transitions.md](./docs/pptx-transitions.md) |
|
||||||
| PPTX animations | `pptx_animations.py`, `animation_config.py` | [docs/pptx-animations.md](./docs/pptx-animations.md) |
|
| PPTX animations | `pptx_animations.py`, `animation_config.py` | [docs/pptx-animations.md](./docs/pptx-animations.md) |
|
||||||
@@ -272,13 +277,13 @@ embedded.
|
|||||||
|
|
||||||
For SVG-authoring routes, `svg_output/` is the complete visible page-design source: every exported text, image, shape, background, and template-derived layout element is present in the page SVG or explicitly referenced by it. Export may translate represented content into Master/Layout/Slide parts or native objects, but it does not retrieve missing visible content from templates or planning files. Speaker notes, animation, narration, transitions, `template-fill-pptx`, and `native-enhance-pptx` remain separately owned capabilities.
|
For SVG-authoring routes, `svg_output/` is the complete visible page-design source: every exported text, image, shape, background, and template-derived layout element is present in the page SVG or explicitly referenced by it. Export may translate represented content into Master/Layout/Slide parts or native objects, but it does not retrieve missing visible content from templates or planning files. Speaker notes, animation, narration, transitions, `template-fill-pptx`, and `native-enhance-pptx` remain separately owned capabilities.
|
||||||
|
|
||||||
Native `svg_to_pptx.py` release export reads the project's explicit structure mode. Free-design and brand-only projects use `flat`, omit Master/Layout mappings and SVG structure metadata, keep every represented object Slide-local, and materialize one clean project-owned Master plus one Blank Layout from the current color/typography lock. Stock content placeholders and unused built-in Layouts are removed; only the standard date/footer/slide-number capability hooks remain. Deck/layout template projects use `structured`: each project supplies unique Master/Layout definitions and one Layout assignment per generated page before SVG generation, and every SVG root repeats its assigned identity. A template-backed definition may remain unused and still register without a published carrier slide. Fixed Master/Layout visuals are direct semantic atoms; ordinary groups are invalid there, while one validated compact authored-preset `<g>` is the sole group exception because it compiles to one native shape. Reusable slots are top-level groups with positive design-zone bounds plus one compatible carrier. Composite `object` regions use explicit proxy binding, and zero-slot Layouts are valid.
|
Native `svg_to_pptx.py` release export reads the project's explicit structure mode. Free-design, Brand-only, Style-only, and other `template_reuse_scope: style` projects use `flat`, omit Master/Layout mappings and SVG structure metadata, keep every represented object Slide-local, and materialize one clean project-owned Master plus one Blank Layout from the current color/typography lock. Stock content placeholders and unused built-in Layouts are removed; only the standard date/footer/slide-number capability hooks remain. A Deck/Layout application uses `structured` only when Strategist derives `template_reuse_scope: mirror|layout`: each project supplies unique Master/Layout definitions and one Layout assignment per generated page before SVG generation, and every SVG root repeats its assigned identity. A template-backed definition may remain unused and still register without a published carrier slide. Fixed Master/Layout visuals are direct semantic atoms; ordinary groups are invalid there, while one validated compact authored-preset `<g>` is the sole group exception because it compiles to one native shape. Reusable slots are top-level groups with positive design-zone bounds plus one compatible carrier. Composite `object` regions use explicit proxy binding, and zero-slot Layouts are valid.
|
||||||
|
|
||||||
Structured template export compiles only the declared structure, maps locked typography/colors into PowerPoint defaults, creates the named Master/Layout parts, and reads the package back before publication. It never clusters pages, promotes repeated chrome heuristically, or invents placeholders. Flat export is the normal free-design/brand-only route: it creates only the clean project-owned shell and performs no promotion or deduplication of Slide content.
|
Structured template export compiles only the declared structure, maps locked typography/colors into PowerPoint defaults, creates the named Master/Layout parts, and reads the package back before publication. It never clusters pages, promotes repeated chrome heuristically, or invents placeholders. Flat export is the normal free-design/Brand-only/Style-only/style-scope route: it creates only the clean project-owned shell and performs no promotion or deduplication of Slide content.
|
||||||
|
|
||||||
Template `page_layouts` records authoring-input provenance, `pptx_masters` / `pptx_layouts` own unique reusable definitions, and `page_pptx_layouts` owns page assignment. Strict preserves its Master/Layout/slot contract; adaptive retains its Master and may use a new Layout key only when fixed Layout atoms or slot topology/bounds change. `standard` / `fidelity` author new SVGs and a new Master/Layout/slot contract. `mirror` materializes a new workspace from the complete validated source identity graph—including unused Layout definitions—without semantic synthesis or gap filling, while mechanically expanding fixed-layer group wrappers into the direct atoms required by the structured contract.
|
Template `page_layouts` records authoring-input provenance, `pptx_masters` / `pptx_layouts` own unique reusable definitions, and `page_pptx_layouts` owns page assignment. Strict preserves its Master/Layout/slot contract; adaptive retains its Master and may use a new Layout key only when fixed Layout atoms or slot topology/bounds change. `standard` / `fidelity` author new SVGs and a new Master/Layout/slot contract. `mirror` materializes a new workspace from the complete validated source identity graph—including unused Layout definitions—without semantic synthesis or gap filling, while mechanically expanding fixed-layer group wrappers into the direct atoms required by the structured contract.
|
||||||
|
|
||||||
Legacy structured/template contracts using `baseline`, `template`, `preserve`, `layout_strategy`, `data-pptx-layout-kind`, `distilled`/`utility`, direct atomic placeholders, or incomplete root Master identity must be replaced by a new workspace created through [`create-template`](../workflows/create-template.md). Generate new structured SVG pages from that workspace; do not upgrade the existing PPTX/SVG in place. Explicit flat free-design/brand-only projects intentionally omit root Master identity.
|
Legacy structured/template contracts using `baseline`, `template`, `preserve`, `layout_strategy`, `data-pptx-layout-kind`, `distilled`/`utility`, direct atomic placeholders, or incomplete root Master identity must be replaced by a new workspace created through [`create-template`](../workflows/create-template.md). Generate new structured SVG pages from that workspace; do not upgrade the existing PPTX/SVG in place. Explicit flat free-design/Brand-only/Style-only projects intentionally omit root Master identity.
|
||||||
|
|
||||||
`pptx_to_svg.py` annotates verified text-grid tables and conservative chart data with `data-pptx-replace-with` beside the visible SVG fallback and places the payload in `<metadata type="application/json">`; the parent claim selects the chart or table schema. Imported table/chart groups under this contract carry `data-pptx-import-source="pptx"`, whether active or fallback-only. Table import covers exact physical row/grid topology, canonical rectangular merges, safe solid/no-fill per-side borders, plain multi-paragraph cells, and a closed run-rich paragraph schema. Each rich run requires `text` and may use only `bold`, `italic`, `underline`, `strike`, `color`, `font_size`, one `font_family`, `lang`, and `alt_lang`. A merge must use the exact `rowSpan` / `gridSpan` / `hMerge` / `vMerge` physical topology with empty merge slaves. Presentation-only source run XML without a non-empty `effectLst` / `effectDag` normalizes; a table-cell run effect disables native replacement and adds a blocking effect diagnostic. Relationship-bearing text, extensions, line breaks, fields, tabs, bullets, broken text topology, unsafe border XML, non-solid fills, and other merge encodings remain fallback-only. For table style `{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}`, the normalized SVG fallback resolves `wholeTbl`, `firstRow`, horizontal banding, theme colors/fonts, and direct cell/run overrides; other built-in/custom style families are not implied.
|
`pptx_to_svg.py` annotates verified text-grid tables and conservative chart data with `data-pptx-replace-with` beside the visible SVG fallback and places the payload in `<metadata type="application/json">`; the parent claim selects the chart or table schema. Imported table/chart groups under this contract carry `data-pptx-import-source="pptx"`, whether active or fallback-only. Table import covers exact physical row/grid topology, canonical rectangular merges, safe solid/no-fill per-side borders, plain multi-paragraph cells, and a closed run-rich paragraph schema. Each rich run requires `text` and may use only `bold`, `italic`, `underline`, `strike`, `color`, `font_size`, one `font_family`, `lang`, and `alt_lang`. A merge must use the exact `rowSpan` / `gridSpan` / `hMerge` / `vMerge` physical topology with empty merge slaves. Presentation-only source run XML without a non-empty `effectLst` / `effectDag` normalizes; a table-cell run effect disables native replacement and adds a blocking effect diagnostic. Relationship-bearing text, extensions, line breaks, fields, tabs, bullets, broken text topology, unsafe border XML, non-solid fills, and other merge encodings remain fallback-only. For table style `{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}`, the normalized SVG fallback resolves `wholeTbl`, `firstRow`, horizontal banding, theme colors/fonts, and direct cell/run overrides; other built-in/custom style families are not implied.
|
||||||
|
|
||||||
|
|||||||
@@ -450,7 +450,7 @@ def save_csv(results: list[ImageAnalysis], csv_path: str | Path) -> None:
|
|||||||
if temporary_path is not None:
|
if temporary_path is not None:
|
||||||
temporary_path.unlink(missing_ok=True)
|
temporary_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
print(f"\nCSV saved to: {target}")
|
print(f"\n[REPORT] Image analysis CSV: {target}")
|
||||||
|
|
||||||
|
|
||||||
def main(argv: list[str] | None = None) -> int:
|
def main(argv: list[str] | None = None) -> int:
|
||||||
|
|||||||
+1220
-309
File diff suppressed because it is too large
Load Diff
+456
-151
@@ -1,9 +1,10 @@
|
|||||||
/* PPT Master - Strategist confirmation stage UI
|
/* PPT Master - Strategist confirmation UI
|
||||||
* Stage 1 captures the communication contract, Stage 2 confirms a coherent
|
* Stage 1 confirms the communication contract and the free-design/template
|
||||||
* deck solution, and Stage 3 resolves production mechanics. Finite fields use
|
* choice together. Stage 2 combines the coherent deck solution and production
|
||||||
* /static/catalogs.json; coordinated design directions seed color, typography,
|
* mechanics, then writes the final result.
|
||||||
* icons, generated-image rendering, and conditional template-application prose.
|
* Finite fields use /static/catalogs.json; coordinated design directions seed
|
||||||
* Final confirm saves result.json.
|
* color, typography, icons, generated-image rendering, and conditional
|
||||||
|
* template-application prose. Final confirm saves result.json.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
@@ -14,19 +15,38 @@
|
|||||||
page_title: "PPT Master - Confirm Design",
|
page_title: "PPT Master - Confirm Design",
|
||||||
topbar_hint: "Answer the open questions, or pick and customize recommendations, then continue.",
|
topbar_hint: "Answer the open questions, or pick and customize recommendations, then continue.",
|
||||||
stage_anchors: "Stage 1 · Communication contract",
|
stage_anchors: "Stage 1 · Communication contract",
|
||||||
stage_design: "Stage 2 · Deck direction & visual system",
|
stage_final_plan: "Stage 2 · Final plan & production",
|
||||||
stage_images: "Stage 3 · Resources & production",
|
|
||||||
loading: "Loading…",
|
loading: "Loading…",
|
||||||
load_error: "Could not load the current recommendation stage. The AI must write it before launch.",
|
load_error: "Could not load the current recommendation stage. The AI must write it before launch.",
|
||||||
btn_confirm: "Confirm",
|
btn_confirm: "Confirm",
|
||||||
btn_confirm_contract: "Confirm contract & continue →",
|
btn_confirm_contract: "Confirm contract & continue →",
|
||||||
btn_confirm_solution: "Confirm solution & continue →",
|
btn_confirm_final_plan: "Confirm final plan →",
|
||||||
deriving: "Generating the downstream options from your choices…",
|
deriving: "Generating the downstream options from your choices…",
|
||||||
|
template_selection_required: "Choose free design or use templates. When using templates, select at least one workspace.",
|
||||||
connection_lost: "Connection to the confirm server was interrupted; retrying. If this keeps failing, return to the chat for confirmation.",
|
connection_lost: "Connection to the confirm server was interrupted; retrying. If this keeps failing, return to the chat for confirmation.",
|
||||||
already_confirmed: "Already confirmed once. Re-submitting overwrites the previous choices.",
|
|
||||||
confirmed_title: "✓ Confirmed",
|
confirmed_title: "✓ Confirmed",
|
||||||
confirmed_hint: "Your choices are saved. You can close this page and return to the chat.",
|
confirmed_hint: "Your choices are saved. You can close this page and return to the chat.",
|
||||||
lang_toggle_title: "Switch language",
|
lang_toggle_title: "Switch language",
|
||||||
|
sec_template_choice: "Design basis",
|
||||||
|
template_choice_hint: "Choose how this deck should establish its design system.",
|
||||||
|
template_free_title: "Design from the current content",
|
||||||
|
template_free_desc: "Use no reusable template workspace. The Strategist will derive the visual system from this project.",
|
||||||
|
template_use_title: "Use templates",
|
||||||
|
template_use_desc: "Select one or more reusable Brand, Style, Layout, Deck, or specified workspaces.",
|
||||||
|
sec_template_library: "Template combination",
|
||||||
|
template_library_hint: "Choose at most one registered template for each type. Brand, Style, Layout, and Deck can be combined.",
|
||||||
|
sec_template_explicit: "Specified templates",
|
||||||
|
template_explicit_hint: "Choose at most one exact workspace supplied for this run. Its source path is shown for verification.",
|
||||||
|
template_kind_brand: "Brand",
|
||||||
|
template_kind_style: "Style",
|
||||||
|
template_kind_layout: "Layout",
|
||||||
|
template_kind_deck: "Deck",
|
||||||
|
template_source_library: "Library",
|
||||||
|
template_source_explicit: "Specified path",
|
||||||
|
template_source_path: "Source path",
|
||||||
|
template_select_none: "None",
|
||||||
|
template_none_registered: "No registered templates",
|
||||||
|
template_none_explicit: "No specified templates for this run",
|
||||||
sec_canvas: "Canvas format",
|
sec_canvas: "Canvas format",
|
||||||
sec_pages: "Page count",
|
sec_pages: "Page count",
|
||||||
sec_audience: "Target audience",
|
sec_audience: "Target audience",
|
||||||
@@ -70,6 +90,7 @@
|
|||||||
ai_custom_candidate: "AI custom proposal",
|
ai_custom_candidate: "AI custom proposal",
|
||||||
ai_custom_candidate_hint: "Always visible for comparison. It is not selected by default; select it to edit.",
|
ai_custom_candidate_hint: "Always visible for comparison. It is not selected by default; select it to edit.",
|
||||||
custom_behavior_required: "The selected AI custom proposal cannot be blank.",
|
custom_behavior_required: "The selected AI custom proposal cannot be blank.",
|
||||||
|
custom_color_required: "Describe the custom color scheme before continuing.",
|
||||||
design_system_required: "Choose a complete palette and typography system before continuing.",
|
design_system_required: "Choose a complete palette and typography system before continuing.",
|
||||||
mode_behavior_placeholder: "Describe the act sequence, title voice, page rhythm, and presentation posture.",
|
mode_behavior_placeholder: "Describe the act sequence, title voice, page rhythm, and presentation posture.",
|
||||||
visual_style_behavior_placeholder: "Describe shape language, composition, decoration density, whitespace, typography character, and texture.",
|
visual_style_behavior_placeholder: "Describe shape language, composition, decoration density, whitespace, typography character, and texture.",
|
||||||
@@ -94,8 +115,8 @@
|
|||||||
image_strategy_custom_placeholder: "Describe the exact generated-image direction, subjects, composition, style cues, or things to avoid.",
|
image_strategy_custom_placeholder: "Describe the exact generated-image direction, subjects, composition, style cues, or things to avoid.",
|
||||||
image_strategy_reference_hint: "Reference images show rendering only. Final AI images inherit the deck color scheme selected above.",
|
image_strategy_reference_hint: "Reference images show rendering only. Final AI images inherit the deck color scheme selected above.",
|
||||||
image_strategy_no_reference: "No reference image for this custom choice.",
|
image_strategy_no_reference: "No reference image for this custom choice.",
|
||||||
image_source_summary: "Confirmed image sources",
|
image_source_summary: "Selected image sources",
|
||||||
image_production_hint: "Image sources and rendering were confirmed in Stage 2. This stage only resolves the production path.",
|
image_production_hint: "Image sources and rendering are selected above. Resolve only the production path here.",
|
||||||
image_usage_notes: "Additional image requirements",
|
image_usage_notes: "Additional image requirements",
|
||||||
image_usage_notes_placeholder: "e.g. realistic handwashing scenes; avoid cartoon germs; keep product photos untouched.",
|
image_usage_notes_placeholder: "e.g. realistic handwashing scenes; avoid cartoon germs; keep product photos untouched.",
|
||||||
image_usage_required: "Select at least one image usage option.",
|
image_usage_required: "Select at least one image usage option.",
|
||||||
@@ -131,7 +152,6 @@
|
|||||||
size_role_subtitle: "subtitle",
|
size_role_subtitle: "subtitle",
|
||||||
size_role_annotation: "annotation",
|
size_role_annotation: "annotation",
|
||||||
custom_typography: "Custom typography",
|
custom_typography: "Custom typography",
|
||||||
custom_typography_repair: "Complete the concrete font names required for this project to continue.",
|
|
||||||
custom_color: "Custom color",
|
custom_color: "Custom color",
|
||||||
custom_color_placeholder: "Describe your colors in words, e.g. deep navy primary, warm orange accent, white background — or paste HEX values…",
|
custom_color_placeholder: "Describe your colors in words, e.g. deep navy primary, warm orange accent, white background — or paste HEX values…",
|
||||||
role_background: "bg",
|
role_background: "bg",
|
||||||
@@ -173,19 +193,38 @@
|
|||||||
page_title: "PPT Master - デザイン確認",
|
page_title: "PPT Master - デザイン確認",
|
||||||
topbar_hint: "自由記述の質問に答えるか、提案を選択・調整して次へ進んでください。",
|
topbar_hint: "自由記述の質問に答えるか、提案を選択・調整して次へ進んでください。",
|
||||||
stage_anchors: "ステージ 1 · コミュニケーション契約",
|
stage_anchors: "ステージ 1 · コミュニケーション契約",
|
||||||
stage_design: "ステージ 2 · 全体方針とビジュアルシステム",
|
stage_final_plan: "ステージ 2 · 最終プランと制作",
|
||||||
stage_images: "ステージ 3 · リソースと制作",
|
|
||||||
loading: "読み込み中…",
|
loading: "読み込み中…",
|
||||||
load_error: "現在の推奨ステージを読み込めませんでした。起動前にAIが書き込む必要があります。",
|
load_error: "現在の推奨ステージを読み込めませんでした。起動前にAIが書き込む必要があります。",
|
||||||
btn_confirm: "確定",
|
btn_confirm: "確定",
|
||||||
btn_confirm_contract: "契約内容を確定して次へ →",
|
btn_confirm_contract: "契約内容を確定して次へ →",
|
||||||
btn_confirm_solution: "全体方針を確定して次へ →",
|
btn_confirm_final_plan: "最終プランを確定 →",
|
||||||
deriving: "選択内容をもとに後続の選択肢を生成しています…",
|
deriving: "選択内容をもとに後続の選択肢を生成しています…",
|
||||||
|
template_selection_required: "自由デザインまたはテンプレート利用を選んでください。テンプレート利用時は、1つ以上のワークスペースを選択してください。",
|
||||||
connection_lost: "確認ページのサーバー接続が中断されました。再試行しています。失敗が続く場合はチャットで確認してください。",
|
connection_lost: "確認ページのサーバー接続が中断されました。再試行しています。失敗が続く場合はチャットで確認してください。",
|
||||||
already_confirmed: "すでに一度確定済みです。再送信すると前回の選択を上書きします。",
|
|
||||||
confirmed_title: "✓ 確定しました",
|
confirmed_title: "✓ 確定しました",
|
||||||
confirmed_hint: "選択内容を保存しました。このページを閉じてチャットに戻ってください。",
|
confirmed_hint: "選択内容を保存しました。このページを閉じてチャットに戻ってください。",
|
||||||
lang_toggle_title: "言語を切り替え",
|
lang_toggle_title: "言語を切り替え",
|
||||||
|
sec_template_choice: "デザインの基準",
|
||||||
|
template_choice_hint: "この資料のデザインシステムをどう決めるか選択します。",
|
||||||
|
template_free_title: "現在の内容からデザインする",
|
||||||
|
template_free_desc: "再利用テンプレートを使わず、Strategist がこのプロジェクトからビジュアルシステムを組み立てます。",
|
||||||
|
template_use_title: "テンプレートを使用",
|
||||||
|
template_use_desc: "Brand、Style、Layout、Deck、または指定ワークスペースから1つ以上選択します。",
|
||||||
|
sec_template_library: "テンプレートの組み合わせ",
|
||||||
|
template_library_hint: "登録済みテンプレートは種類ごとに1件まで選択でき、Brand、Style、Layout、Deck を組み合わせられます。",
|
||||||
|
sec_template_explicit: "指定テンプレート",
|
||||||
|
template_explicit_hint: "この実行で指定された正確なワークスペースを1件まで選択できます。確認用に参照元パスを表示します。",
|
||||||
|
template_kind_brand: "Brand",
|
||||||
|
template_kind_style: "Style",
|
||||||
|
template_kind_layout: "Layout",
|
||||||
|
template_kind_deck: "Deck",
|
||||||
|
template_source_library: "ライブラリ",
|
||||||
|
template_source_explicit: "指定パス",
|
||||||
|
template_source_path: "参照元パス",
|
||||||
|
template_select_none: "なし",
|
||||||
|
template_none_registered: "登録済みテンプレートはありません",
|
||||||
|
template_none_explicit: "この実行で指定されたテンプレートはありません",
|
||||||
sec_canvas: "キャンバス形式",
|
sec_canvas: "キャンバス形式",
|
||||||
sec_pages: "ページ数",
|
sec_pages: "ページ数",
|
||||||
sec_audience: "想定読者",
|
sec_audience: "想定読者",
|
||||||
@@ -229,6 +268,7 @@
|
|||||||
ai_custom_candidate: "AIカスタム案",
|
ai_custom_candidate: "AIカスタム案",
|
||||||
ai_custom_candidate_hint: "比較できるよう常に全文を表示します。初期選択はされず、選択後に編集できます。",
|
ai_custom_candidate_hint: "比較できるよう常に全文を表示します。初期選択はされず、選択後に編集できます。",
|
||||||
custom_behavior_required: "選択したAIカスタム案を空欄にはできません。",
|
custom_behavior_required: "選択したAIカスタム案を空欄にはできません。",
|
||||||
|
custom_color_required: "続行する前に、カスタム配色の説明を入力してください。",
|
||||||
design_system_required: "続行する前に、完全な配色と書体システムを選択してください。",
|
design_system_required: "続行する前に、完全な配色と書体システムを選択してください。",
|
||||||
mode_behavior_placeholder: "構成の流れ、タイトルの語り口、ページのリズム、表現姿勢を記述します。",
|
mode_behavior_placeholder: "構成の流れ、タイトルの語り口、ページのリズム、表現姿勢を記述します。",
|
||||||
visual_style_behavior_placeholder: "形状言語、構図、装飾密度、余白、書体の性格、質感を記述します。",
|
visual_style_behavior_placeholder: "形状言語、構図、装飾密度、余白、書体の性格、質感を記述します。",
|
||||||
@@ -253,8 +293,8 @@
|
|||||||
image_strategy_custom_placeholder: "生成画像の方向性、被写体、構図、スタイル要素、避けたい要素を具体的に入力してください。",
|
image_strategy_custom_placeholder: "生成画像の方向性、被写体、構図、スタイル要素、避けたい要素を具体的に入力してください。",
|
||||||
image_strategy_reference_hint: "参照画像はレンダリングのみを示します。最終AI画像の色は上で選んだデッキ配色を継承します。",
|
image_strategy_reference_hint: "参照画像はレンダリングのみを示します。最終AI画像の色は上で選んだデッキ配色を継承します。",
|
||||||
image_strategy_no_reference: "このカスタム選択には参照画像がありません。",
|
image_strategy_no_reference: "このカスタム選択には参照画像がありません。",
|
||||||
image_source_summary: "確定済みの画像ソース",
|
image_source_summary: "選択中の画像ソース",
|
||||||
image_production_hint: "画像ソースとレンダリングはステージ2で確定済みです。ここでは制作経路だけを決めます。",
|
image_production_hint: "画像ソースとレンダリングは上で選択済みです。ここでは制作経路だけを決めます。",
|
||||||
image_usage_notes: "画像に関する補足要件",
|
image_usage_notes: "画像に関する補足要件",
|
||||||
image_usage_notes_placeholder: "例:リアルな手洗いシーンを優先、漫画調の菌のイラストは避ける、製品写真はそのまま使う。",
|
image_usage_notes_placeholder: "例:リアルな手洗いシーンを優先、漫画調の菌のイラストは避ける、製品写真はそのまま使う。",
|
||||||
image_usage_required: "画像の使用方法を少なくとも1つ選択してください。",
|
image_usage_required: "画像の使用方法を少なくとも1つ選択してください。",
|
||||||
@@ -290,7 +330,6 @@
|
|||||||
size_role_subtitle: "サブタイトル",
|
size_role_subtitle: "サブタイトル",
|
||||||
size_role_annotation: "注釈",
|
size_role_annotation: "注釈",
|
||||||
custom_typography: "カスタムタイポグラフィ",
|
custom_typography: "カスタムタイポグラフィ",
|
||||||
custom_typography_repair: "続行するには、このプロジェクトに必要な具体的な書体名を入力してください。",
|
|
||||||
custom_color: "カスタム配色",
|
custom_color: "カスタム配色",
|
||||||
custom_color_placeholder: "配色を言葉で説明 — 例:濃紺をメインに暖色オレンジのアクセント、背景は白 — またはHEX値を貼り付け…",
|
custom_color_placeholder: "配色を言葉で説明 — 例:濃紺をメインに暖色オレンジのアクセント、背景は白 — またはHEX値を貼り付け…",
|
||||||
role_background: "背景",
|
role_background: "背景",
|
||||||
@@ -332,19 +371,38 @@
|
|||||||
page_title: "确认设计方案",
|
page_title: "确认设计方案",
|
||||||
topbar_hint: "回答开放问题,或选择并调整推荐项,然后继续。",
|
topbar_hint: "回答开放问题,或选择并调整推荐项,然后继续。",
|
||||||
stage_anchors: "第一阶段 · 沟通契约",
|
stage_anchors: "第一阶段 · 沟通契约",
|
||||||
stage_design: "第二阶段 · 完整方案与视觉系统",
|
stage_final_plan: "第二阶段 · 最终方案与制作",
|
||||||
stage_images: "第三阶段 · 资源与生产执行",
|
|
||||||
loading: "加载中…",
|
loading: "加载中…",
|
||||||
load_error: "无法加载推荐文件,需在启动前写入。",
|
load_error: "无法加载推荐文件,需在启动前写入。",
|
||||||
btn_confirm: "确认",
|
btn_confirm: "确认",
|
||||||
btn_confirm_contract: "确认沟通契约并继续 →",
|
btn_confirm_contract: "确认沟通契约并继续 →",
|
||||||
btn_confirm_solution: "确认完整方案并继续 →",
|
btn_confirm_final_plan: "确认最终方案 →",
|
||||||
deriving: "正在根据你的选择生成下游选项…",
|
deriving: "正在根据你的选择生成下游选项…",
|
||||||
|
template_selection_required: "请选择自由设计或使用模板;选择使用模板时,至少选择一个工作区。",
|
||||||
connection_lost: "确认页服务连接中断,正在重试;如果持续失败,请回到聊天窗口走聊天确认。",
|
connection_lost: "确认页服务连接中断,正在重试;如果持续失败,请回到聊天窗口走聊天确认。",
|
||||||
already_confirmed: "已确认过一次,重新提交会覆盖之前的选择。",
|
|
||||||
confirmed_title: "✓ 已确认",
|
confirmed_title: "✓ 已确认",
|
||||||
confirmed_hint: "选择已保存,可关闭此页并回到聊天窗口。",
|
confirmed_hint: "选择已保存,可关闭此页并回到聊天窗口。",
|
||||||
lang_toggle_title: "切换语言",
|
lang_toggle_title: "切换语言",
|
||||||
|
sec_template_choice: "设计基础",
|
||||||
|
template_choice_hint: "选择这份演示文稿如何建立设计系统。",
|
||||||
|
template_free_title: "根据当前内容从零设计",
|
||||||
|
template_free_desc: "不使用可复用模板工作区,由 Strategist 根据当前项目推导视觉系统。",
|
||||||
|
template_use_title: "使用模板",
|
||||||
|
template_use_desc: "选择一个或多个 Brand、Style、Layout、Deck 或指定工作区。",
|
||||||
|
sec_template_library: "模板组合",
|
||||||
|
template_library_hint: "每种已注册模板最多选择一个;Brand、Style、Layout、Deck 可以组合使用。",
|
||||||
|
sec_template_explicit: "指定模板",
|
||||||
|
template_explicit_hint: "本次运行明确提供的精确工作区最多选择一个;显示来源路径供你核对。",
|
||||||
|
template_kind_brand: "Brand",
|
||||||
|
template_kind_style: "Style",
|
||||||
|
template_kind_layout: "Layout",
|
||||||
|
template_kind_deck: "Deck",
|
||||||
|
template_source_library: "模板库",
|
||||||
|
template_source_explicit: "指定地址",
|
||||||
|
template_source_path: "来源路径",
|
||||||
|
template_select_none: "无",
|
||||||
|
template_none_registered: "暂无已注册模板",
|
||||||
|
template_none_explicit: "本次运行没有指定模板",
|
||||||
sec_canvas: "画布格式",
|
sec_canvas: "画布格式",
|
||||||
sec_pages: "页数",
|
sec_pages: "页数",
|
||||||
sec_audience: "目标受众",
|
sec_audience: "目标受众",
|
||||||
@@ -388,6 +446,7 @@
|
|||||||
ai_custom_candidate: "AI 自定义方案",
|
ai_custom_candidate: "AI 自定义方案",
|
||||||
ai_custom_candidate_hint: "始终展示完整内容用于比较;默认不选中,选择后可编辑。",
|
ai_custom_candidate_hint: "始终展示完整内容用于比较;默认不选中,选择后可编辑。",
|
||||||
custom_behavior_required: "已选择的 AI 自定义方案不能为空。",
|
custom_behavior_required: "已选择的 AI 自定义方案不能为空。",
|
||||||
|
custom_color_required: "请先填写自定义配色说明,再继续确认。",
|
||||||
design_system_required: "请先选择完整的配色与字体方案,再继续确认。",
|
design_system_required: "请先选择完整的配色与字体方案,再继续确认。",
|
||||||
mode_behavior_placeholder: "描述叙事阶段、标题语气、页面节奏和表达姿态。",
|
mode_behavior_placeholder: "描述叙事阶段、标题语气、页面节奏和表达姿态。",
|
||||||
visual_style_behavior_placeholder: "描述形状语言、构图、装饰密度、留白、字体气质和纹理。",
|
visual_style_behavior_placeholder: "描述形状语言、构图、装饰密度、留白、字体气质和纹理。",
|
||||||
@@ -412,8 +471,8 @@
|
|||||||
image_strategy_custom_placeholder: "描述生成图的具体方向、主体、构图、风格关键词或需要避免的内容。",
|
image_strategy_custom_placeholder: "描述生成图的具体方向、主体、构图、风格关键词或需要避免的内容。",
|
||||||
image_strategy_reference_hint: "参考图只展示渲染风格;最终 AI 图片直接继承上方已选的整套 PPT 配色。",
|
image_strategy_reference_hint: "参考图只展示渲染风格;最终 AI 图片直接继承上方已选的整套 PPT 配色。",
|
||||||
image_strategy_no_reference: "自定义选择没有参考图。",
|
image_strategy_no_reference: "自定义选择没有参考图。",
|
||||||
image_source_summary: "已确认的图片来源",
|
image_source_summary: "已选图片来源",
|
||||||
image_production_hint: "图片来源和渲染方向已在第二阶段确认;这里仅决定实际生产路径。",
|
image_production_hint: "图片来源和渲染方向已在上方选择;这里仅决定实际生产路径。",
|
||||||
image_usage_notes: "图片补充要求",
|
image_usage_notes: "图片补充要求",
|
||||||
image_usage_notes_placeholder: "例如:优先真实洗手场景;不要卡通病菌;产品照片保持原样。",
|
image_usage_notes_placeholder: "例如:优先真实洗手场景;不要卡通病菌;产品照片保持原样。",
|
||||||
image_usage_required: "请至少选择一种图片使用方式。",
|
image_usage_required: "请至少选择一种图片使用方式。",
|
||||||
@@ -449,7 +508,6 @@
|
|||||||
size_role_subtitle: "副标题",
|
size_role_subtitle: "副标题",
|
||||||
size_role_annotation: "注释",
|
size_role_annotation: "注释",
|
||||||
custom_typography: "自定义字体方案",
|
custom_typography: "自定义字体方案",
|
||||||
custom_typography_repair: "请补全当前项目所需的具体字体名称后继续。",
|
|
||||||
custom_color: "自定义配色",
|
custom_color: "自定义配色",
|
||||||
custom_color_placeholder: "用文字描述配色,如:深蓝主色、暖橙强调、白色背景——或直接粘贴 HEX 值…",
|
custom_color_placeholder: "用文字描述配色,如:深蓝主色、暖橙强调、白色背景——或直接粘贴 HEX 值…",
|
||||||
role_background: "背景",
|
role_background: "背景",
|
||||||
@@ -615,6 +673,12 @@
|
|||||||
var ICON_PREVIEWS = {}; // /api/icon-previews — real SVG samples from templates/icons
|
var ICON_PREVIEWS = {}; // /api/icon-previews — real SVG samples from templates/icons
|
||||||
var AI_IMAGE_COMPARISON = {}; // /api/ai-image-comparison — preset rendering catalog
|
var AI_IMAGE_COMPARISON = {}; // /api/ai-image-comparison — preset rendering catalog
|
||||||
var STATE = {};
|
var STATE = {};
|
||||||
|
var TEMPLATE_KINDS = ["brand", "style", "layout", "deck"];
|
||||||
|
var TEMPLATE_OPTIONS = null;
|
||||||
|
var TEMPLATE_CANDIDATES = [];
|
||||||
|
var TEMPLATE_SELECTED_KEYS = [];
|
||||||
|
var TEMPLATE_SELECTIONS = { brand: "", style: "", layout: "", deck: "", explicit: "" };
|
||||||
|
var TEMPLATE_MODE = "";
|
||||||
var REC_ALIASES = {
|
var REC_ALIASES = {
|
||||||
icons: {
|
icons: {
|
||||||
line: "tabler-outline",
|
line: "tabler-outline",
|
||||||
@@ -766,6 +830,267 @@
|
|||||||
note.textContent = text;
|
note.textContent = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Stage 1 template selection --------------------------------------
|
||||||
|
function normalizeTemplateCandidates(raw, source, kind) {
|
||||||
|
if (!Array.isArray(raw)) return [];
|
||||||
|
return raw.filter(function (candidate) {
|
||||||
|
return candidate && candidate.key != null && String(candidate.key).trim();
|
||||||
|
}).map(function (candidate) {
|
||||||
|
var normalized = Object.assign({}, candidate);
|
||||||
|
normalized.key = String(candidate.key);
|
||||||
|
normalized.source = source;
|
||||||
|
if (kind) normalized.kind = kind;
|
||||||
|
return normalized;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTemplateOptions(data) {
|
||||||
|
var library = data && data.library && typeof data.library === "object"
|
||||||
|
? data.library : {};
|
||||||
|
var defaultMode = data && data.default_mode;
|
||||||
|
if (defaultMode !== "free_design" && defaultMode !== "templates") {
|
||||||
|
throw new Error("template_options.default_mode must be free_design or templates");
|
||||||
|
}
|
||||||
|
var normalized = {
|
||||||
|
lang: data && data.lang,
|
||||||
|
default_mode: defaultMode,
|
||||||
|
library: {},
|
||||||
|
explicit: [],
|
||||||
|
preselected_keys: Array.isArray(data && data.preselected_keys)
|
||||||
|
? data.preselected_keys.map(String) : []
|
||||||
|
};
|
||||||
|
TEMPLATE_CANDIDATES = [];
|
||||||
|
TEMPLATE_KINDS.forEach(function (kind) {
|
||||||
|
var candidates = normalizeTemplateCandidates(library[kind], "library", kind);
|
||||||
|
normalized.library[kind] = candidates;
|
||||||
|
TEMPLATE_CANDIDATES = TEMPLATE_CANDIDATES.concat(candidates);
|
||||||
|
});
|
||||||
|
normalized.explicit = normalizeTemplateCandidates(data && data.explicit, "explicit", "");
|
||||||
|
TEMPLATE_CANDIDATES = TEMPLATE_CANDIDATES.concat(normalized.explicit);
|
||||||
|
var candidatesByKey = Object.create(null);
|
||||||
|
TEMPLATE_CANDIDATES.forEach(function (candidate) {
|
||||||
|
if (candidatesByKey[candidate.key]) {
|
||||||
|
throw new Error("Duplicate template option key: " + candidate.key);
|
||||||
|
}
|
||||||
|
candidatesByKey[candidate.key] = candidate;
|
||||||
|
});
|
||||||
|
TEMPLATE_SELECTIONS = emptyTemplateSelections();
|
||||||
|
normalized.preselected_keys.filter(function (key, index, all) {
|
||||||
|
return all.indexOf(key) === index;
|
||||||
|
}).forEach(function (key) {
|
||||||
|
var candidate = candidatesByKey[key];
|
||||||
|
var slot = templateSelectionSlot(candidate);
|
||||||
|
if (!candidate || !slot) {
|
||||||
|
throw new Error("Invalid preselected template key: " + key);
|
||||||
|
}
|
||||||
|
if (TEMPLATE_SELECTIONS[slot]) {
|
||||||
|
throw new Error("Multiple preselected templates for slot: " + slot);
|
||||||
|
}
|
||||||
|
TEMPLATE_SELECTIONS[slot] = key;
|
||||||
|
});
|
||||||
|
syncTemplateSelectionState();
|
||||||
|
TEMPLATE_MODE = normalized.default_mode;
|
||||||
|
TEMPLATE_OPTIONS = normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
function emptyTemplateSelections() {
|
||||||
|
return { brand: "", style: "", layout: "", deck: "", explicit: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateSelectionSlot(candidate) {
|
||||||
|
if (!candidate) return "";
|
||||||
|
if (candidate.source === "explicit") return "explicit";
|
||||||
|
if (candidate.source === "library" && TEMPLATE_KINDS.indexOf(candidate.kind) >= 0) {
|
||||||
|
return candidate.kind;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateCandidateByKey(key) {
|
||||||
|
key = String(key || "");
|
||||||
|
for (var i = 0; i < TEMPLATE_CANDIDATES.length; i += 1) {
|
||||||
|
if (TEMPLATE_CANDIDATES[i].key === key) return TEMPLATE_CANDIDATES[i];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncTemplateSelectionState() {
|
||||||
|
TEMPLATE_SELECTED_KEYS = TEMPLATE_KINDS.map(function (kind) {
|
||||||
|
return TEMPLATE_SELECTIONS[kind];
|
||||||
|
});
|
||||||
|
TEMPLATE_SELECTED_KEYS.push(TEMPLATE_SELECTIONS.explicit);
|
||||||
|
TEMPLATE_SELECTED_KEYS = TEMPLATE_SELECTED_KEYS.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateCandidateTitle(candidate) {
|
||||||
|
return localized(candidate, "label") || candidate.label || candidate.id || candidate.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateCandidateSummary(candidate) {
|
||||||
|
return localized(candidate, "summary") || candidate.summary || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateKindLabel(kind) {
|
||||||
|
return t("template_kind_" + kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseFreeDesign() {
|
||||||
|
TEMPLATE_SELECTIONS = emptyTemplateSelections();
|
||||||
|
syncTemplateSelectionState();
|
||||||
|
TEMPLATE_MODE = "free_design";
|
||||||
|
updateTemplateSelectionControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseTemplateMode() {
|
||||||
|
TEMPLATE_MODE = "templates";
|
||||||
|
updateTemplateSelectionControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseTemplateForSlot(slot, key) {
|
||||||
|
TEMPLATE_SELECTIONS[slot] = String(key || "");
|
||||||
|
syncTemplateSelectionState();
|
||||||
|
TEMPLATE_MODE = "templates";
|
||||||
|
updateTemplateSelectionControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateOptionTitle(candidate) {
|
||||||
|
var details = [];
|
||||||
|
var summary = templateCandidateSummary(candidate);
|
||||||
|
if (summary) details.push(summary);
|
||||||
|
if (candidate.workspace_root) details.push(candidate.workspace_root);
|
||||||
|
return details.join(" · ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTemplateSelectField(slot, label, candidates) {
|
||||||
|
var field = el("div", "template-select-field template-select-field-" + slot);
|
||||||
|
var selectId = "template-select-" + slot;
|
||||||
|
var fieldLabel = el("label", "template-select-label", label);
|
||||||
|
fieldLabel.setAttribute("for", selectId);
|
||||||
|
field.appendChild(fieldLabel);
|
||||||
|
|
||||||
|
var select = el("select", "template-select");
|
||||||
|
select.id = selectId;
|
||||||
|
select.setAttribute("data-template-slot", slot);
|
||||||
|
var none = el("option", "", t("template_select_none"));
|
||||||
|
none.value = "";
|
||||||
|
select.appendChild(none);
|
||||||
|
candidates.forEach(function (candidate) {
|
||||||
|
var option = el("option", "", templateCandidateTitle(candidate));
|
||||||
|
option.value = candidate.key;
|
||||||
|
var title = templateOptionTitle(candidate);
|
||||||
|
if (title) option.title = title;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
select.value = TEMPLATE_SELECTIONS[slot] || "";
|
||||||
|
select.disabled = !candidates.length;
|
||||||
|
select.addEventListener("change", function () {
|
||||||
|
chooseTemplateForSlot(slot, select.value);
|
||||||
|
});
|
||||||
|
field.appendChild(select);
|
||||||
|
|
||||||
|
if (slot === "explicit") {
|
||||||
|
field.appendChild(el(
|
||||||
|
"div",
|
||||||
|
"template-select-help",
|
||||||
|
candidates.length ? t("template_explicit_hint") : t("template_none_explicit")
|
||||||
|
));
|
||||||
|
var path = el("div", "template-selected-path");
|
||||||
|
path.id = "template-explicit-path";
|
||||||
|
path.appendChild(el("span", "template-selected-path-label", t("template_source_path") + ":"));
|
||||||
|
var code = el("code", "template-selected-path-value");
|
||||||
|
code.id = "template-explicit-path-value";
|
||||||
|
path.appendChild(code);
|
||||||
|
field.appendChild(path);
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTemplateModeChoice(id, mode, titleKey, descKey, onSelect) {
|
||||||
|
var selected = TEMPLATE_MODE === mode;
|
||||||
|
var choice = el("button", "template-mode-choice" + (selected ? " selected" : ""));
|
||||||
|
choice.id = id;
|
||||||
|
choice.type = "button";
|
||||||
|
choice.setAttribute("aria-pressed", selected ? "true" : "false");
|
||||||
|
choice.appendChild(el("span", "template-choice-radio"));
|
||||||
|
var copy = el("span", "template-choice-copy");
|
||||||
|
copy.appendChild(el("span", "template-choice-title", t(titleKey)));
|
||||||
|
copy.appendChild(el("span", "template-choice-desc", t(descKey)));
|
||||||
|
choice.appendChild(copy);
|
||||||
|
choice.addEventListener("click", onSelect);
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTemplateSelection(host) {
|
||||||
|
if (!TEMPLATE_OPTIONS) return;
|
||||||
|
var sec = section("T", "sec_template_choice", t("template_choice_hint"));
|
||||||
|
var choices = el("div", "template-mode-choices");
|
||||||
|
choices.appendChild(renderTemplateModeChoice(
|
||||||
|
"template-free-choice", "free_design", "template_free_title",
|
||||||
|
"template_free_desc", chooseFreeDesign
|
||||||
|
));
|
||||||
|
var useChoice = renderTemplateModeChoice(
|
||||||
|
"template-use-choice", "templates", "template_use_title",
|
||||||
|
"template_use_desc", chooseTemplateMode
|
||||||
|
);
|
||||||
|
useChoice.setAttribute("aria-controls", "template-selector-panel");
|
||||||
|
choices.appendChild(useChoice);
|
||||||
|
sec.appendChild(choices);
|
||||||
|
|
||||||
|
var panel = el("div", "template-selector-panel");
|
||||||
|
panel.id = "template-selector-panel";
|
||||||
|
panel.appendChild(el("div", "template-selector-hint", t("template_library_hint")));
|
||||||
|
var grid = el("div", "template-select-grid");
|
||||||
|
TEMPLATE_KINDS.forEach(function (kind) {
|
||||||
|
var candidates = TEMPLATE_OPTIONS.library[kind] || [];
|
||||||
|
grid.appendChild(renderTemplateSelectField(kind, templateKindLabel(kind), candidates));
|
||||||
|
});
|
||||||
|
grid.appendChild(renderTemplateSelectField(
|
||||||
|
"explicit",
|
||||||
|
t("template_source_explicit"),
|
||||||
|
TEMPLATE_OPTIONS.explicit || []
|
||||||
|
));
|
||||||
|
panel.appendChild(grid);
|
||||||
|
sec.appendChild(panel);
|
||||||
|
host.appendChild(sec);
|
||||||
|
updateTemplateSelectionControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTemplateExplicitPath() {
|
||||||
|
var path = document.getElementById("template-explicit-path");
|
||||||
|
var value = document.getElementById("template-explicit-path-value");
|
||||||
|
if (!path || !value) return;
|
||||||
|
var candidate = templateCandidateByKey(TEMPLATE_SELECTIONS.explicit);
|
||||||
|
var workspaceRoot = candidate && candidate.workspace_root ? candidate.workspace_root : "";
|
||||||
|
path.hidden = !workspaceRoot;
|
||||||
|
value.textContent = workspaceRoot;
|
||||||
|
value.title = workspaceRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTemplateSelectionControls() {
|
||||||
|
var freeChoice = document.getElementById("template-free-choice");
|
||||||
|
var useChoice = document.getElementById("template-use-choice");
|
||||||
|
var selectorPanel = document.getElementById("template-selector-panel");
|
||||||
|
var freeSelected = TEMPLATE_MODE === "free_design";
|
||||||
|
var templatesSelected = TEMPLATE_MODE === "templates";
|
||||||
|
if (freeChoice) {
|
||||||
|
freeChoice.classList.toggle("selected", freeSelected);
|
||||||
|
freeChoice.setAttribute("aria-pressed", freeSelected ? "true" : "false");
|
||||||
|
}
|
||||||
|
if (useChoice) {
|
||||||
|
useChoice.classList.toggle("selected", templatesSelected);
|
||||||
|
useChoice.setAttribute("aria-pressed", templatesSelected ? "true" : "false");
|
||||||
|
useChoice.setAttribute("aria-expanded", templatesSelected ? "true" : "false");
|
||||||
|
}
|
||||||
|
if (selectorPanel) selectorPanel.hidden = !templatesSelected;
|
||||||
|
TEMPLATE_KINDS.concat(["explicit"]).forEach(function (slot) {
|
||||||
|
var select = document.getElementById("template-select-" + slot);
|
||||||
|
if (select) select.value = TEMPLATE_SELECTIONS[slot] || "";
|
||||||
|
});
|
||||||
|
updateTemplateExplicitPath();
|
||||||
|
var status = document.getElementById("confirm-status");
|
||||||
|
if (status) status.textContent = "";
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeRecId(field, value) {
|
function normalizeRecId(field, value) {
|
||||||
if (Array.isArray(value)) return normalizeRecId(field, value[0]);
|
if (Array.isArray(value)) return normalizeRecId(field, value[0]);
|
||||||
if (value == null || value === "") return value;
|
if (value == null || value === "") return value;
|
||||||
@@ -1731,6 +2056,9 @@
|
|||||||
var refreshStylePreview = function () {};
|
var refreshStylePreview = function () {};
|
||||||
// Replaced when the selected generated-image preview mounts.
|
// Replaced when the selected generated-image preview mounts.
|
||||||
var refreshImageStrategyPreview = function () {};
|
var refreshImageStrategyPreview = function () {};
|
||||||
|
// Replaced when the final plan's image-production section mounts; image-use
|
||||||
|
// edits call it so the conditional AI path stays synchronized on the page.
|
||||||
|
var refreshImageProduction = function () {};
|
||||||
// Replaced when the typography section mounts; the canvas section calls it so
|
// Replaced when the typography section mounts; the canvas section calls it so
|
||||||
// the body-size hint tracks the chosen canvas height.
|
// the body-size hint tracks the chosen canvas height.
|
||||||
var refreshBodySizeHint = function () {};
|
var refreshBodySizeHint = function () {};
|
||||||
@@ -2040,11 +2368,6 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function typographyNeedsConcreteRepair() {
|
|
||||||
return !!(STATE.typography && STATE.typography.name === "custom" &&
|
|
||||||
!typographyFamiliesComplete(STATE.typography));
|
|
||||||
}
|
|
||||||
|
|
||||||
function sampleText(role, field) {
|
function sampleText(role, field) {
|
||||||
// Keep comparison copy stable: choices change visual treatment, not content.
|
// Keep comparison copy stable: choices change visual treatment, not content.
|
||||||
var useEnglish = field === "english" || isEnglishProject();
|
var useEnglish = field === "english" || isEnglishProject();
|
||||||
@@ -2111,9 +2434,6 @@
|
|||||||
function renderTypography(host) {
|
function renderTypography(host) {
|
||||||
var cands = typographyRecommendationCandidates().filter(typographyFamiliesComplete);
|
var cands = typographyRecommendationCandidates().filter(typographyFamiliesComplete);
|
||||||
var sec = section(7, "sec_type");
|
var sec = section(7, "sec_type");
|
||||||
if (stageNumber(REC) >= 3 && typographyNeedsConcreteRepair()) {
|
|
||||||
setSectionNote(sec, t("custom_typography_repair"));
|
|
||||||
}
|
|
||||||
var grid = el("div", "font-grid");
|
var grid = el("div", "font-grid");
|
||||||
var customFields = el("div", "custom-typography-fields font-picker-fields");
|
var customFields = el("div", "custom-typography-fields font-picker-fields");
|
||||||
var customInputs = {};
|
var customInputs = {};
|
||||||
@@ -2789,6 +3109,7 @@
|
|||||||
}
|
}
|
||||||
STATE.image_usage = current;
|
STATE.image_usage = current;
|
||||||
refreshUsageChips();
|
refreshUsageChips();
|
||||||
|
refreshImageProduction();
|
||||||
}
|
}
|
||||||
(CAT.image_usage || []).forEach(function (option) {
|
(CAT.image_usage || []).forEach(function (option) {
|
||||||
var label = optionLabel(option);
|
var label = optionLabel(option);
|
||||||
@@ -2822,6 +3143,10 @@
|
|||||||
|
|
||||||
function renderImageProduction(host) {
|
function renderImageProduction(host) {
|
||||||
var sec = section("P", "sec_image_production", t("image_production_hint"));
|
var sec = section("P", "sec_image_production", t("image_production_hint"));
|
||||||
|
var body = el("div", "image-production-body");
|
||||||
|
sec.appendChild(body);
|
||||||
|
refreshImageProduction = function () {
|
||||||
|
body.innerHTML = "";
|
||||||
var summary = el("div", "subfield");
|
var summary = el("div", "subfield");
|
||||||
summary.appendChild(el("div", "subfield-label", t("image_source_summary")));
|
summary.appendChild(el("div", "subfield-label", t("image_source_summary")));
|
||||||
var chips = el("div", "chips locked-summary-chips");
|
var chips = el("div", "chips locked-summary-chips");
|
||||||
@@ -2831,14 +3156,16 @@
|
|||||||
option ? optionLabel(option) : humanizeId(id)));
|
option ? optionLabel(option) : humanizeId(id)));
|
||||||
});
|
});
|
||||||
summary.appendChild(chips);
|
summary.appendChild(chips);
|
||||||
sec.appendChild(summary);
|
body.appendChild(summary);
|
||||||
if (needsGeneratedImagesForUsage(STATE.image_usage)) {
|
if (needsGeneratedImagesForUsage(STATE.image_usage)) {
|
||||||
var pathField = el("div", "subfield");
|
var pathField = el("div", "subfield");
|
||||||
pathField.appendChild(el("div", "subfield-label", t("image_ai_path")));
|
pathField.appendChild(el("div", "subfield-label", t("image_ai_path")));
|
||||||
enumField(pathField, CAT.image_ai_path, recOrFirst("image_ai_path", CAT.image_ai_path),
|
enumField(pathField, CAT.image_ai_path, recOrFirst("image_ai_path", CAT.image_ai_path),
|
||||||
function () { return STATE.image_ai_path; }, function (value) { STATE.image_ai_path = value; });
|
function () { return STATE.image_ai_path; }, function (value) { STATE.image_ai_path = value; });
|
||||||
sec.appendChild(pathField);
|
body.appendChild(pathField);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
refreshImageProduction();
|
||||||
host.appendChild(sec);
|
host.appendChild(sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2904,25 +3231,20 @@
|
|||||||
host.appendChild(sec);
|
host.appendChild(sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stage of the staged confirm flow:
|
// Two-stage confirmation: communication contract, then complete final plan.
|
||||||
// 1 = communication contract, 2 = complete deck direction,
|
|
||||||
// 3 = resources + production execution,
|
|
||||||
// "all" = legacy single-pass (the recommendation payload carried no stage).
|
|
||||||
var STAGE = 1;
|
var STAGE = 1;
|
||||||
|
|
||||||
function stageNumber(data) {
|
function stageNumber(data) {
|
||||||
var raw = data && data.stage != null ? data.stage : (data && data.tier);
|
var raw = data && data.stage;
|
||||||
raw = String(raw == null ? "" : raw).toLowerCase();
|
raw = String(raw == null ? "" : raw).toLowerCase();
|
||||||
if (raw === "1" || raw === "stage1" || raw === "tier1") return 1;
|
if (raw === "stage1") return 1;
|
||||||
if (raw === "2" || raw === "stage2" || raw === "tier2") return 2;
|
if (raw === "stage2") return 2;
|
||||||
if (raw === "3" || raw === "stage3" || raw === "tier3") return 3;
|
return 0;
|
||||||
return "all";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function stageTitle(stage) {
|
function stageTitle(stage) {
|
||||||
if (stage === 1) return t("stage_anchors");
|
if (stage === 1) return t("stage_anchors");
|
||||||
if (stage === 2) return t("stage_design");
|
if (stage === 2) return t("stage_final_plan");
|
||||||
if (stage === 3) return t("stage_images");
|
|
||||||
return t("page_title");
|
return t("page_title");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2939,16 +3261,19 @@
|
|||||||
// write to now-detached nodes until renderStylePreview remounts them.
|
// write to now-detached nodes until renderStylePreview remounts them.
|
||||||
refreshStylePreview = function () {};
|
refreshStylePreview = function () {};
|
||||||
refreshImageStrategyPreview = function () {};
|
refreshImageStrategyPreview = function () {};
|
||||||
|
refreshImageProduction = function () {};
|
||||||
refreshBodySizeHint = function () {};
|
refreshBodySizeHint = function () {};
|
||||||
refreshSizeInputs = function () {};
|
refreshSizeInputs = function () {};
|
||||||
var previewHost = document.getElementById("topbar-preview");
|
var previewHost = document.getElementById("topbar-preview");
|
||||||
if (previewHost) previewHost.innerHTML = "";
|
if (previewHost) previewHost.innerHTML = "";
|
||||||
if (stage === 1) {
|
if (stage === 1) {
|
||||||
// Scene and communication intent come first; no design tool is
|
// Stage 1 closes the communication contract and its design basis in
|
||||||
// selected before the contract exists.
|
// one submission. Detailed template controls stay hidden until the
|
||||||
|
// user explicitly chooses template-backed design.
|
||||||
renderCommunication(host);
|
renderCommunication(host);
|
||||||
renderDelivery(host);
|
renderDelivery(host);
|
||||||
renderCanvas(host);
|
renderCanvas(host);
|
||||||
|
renderTemplateSelection(host);
|
||||||
} else if (stage === 2) {
|
} else if (stage === 2) {
|
||||||
if (previewHost) renderStylePreview(previewHost);
|
if (previewHost) renderStylePreview(previewHost);
|
||||||
if (previewHost) renderImageStrategyPreview(previewHost);
|
if (previewHost) renderImageStrategyPreview(previewHost);
|
||||||
@@ -2966,40 +3291,6 @@
|
|||||||
renderTypography(styleGroup);
|
renderTypography(styleGroup);
|
||||||
host.appendChild(styleGroup);
|
host.appendChild(styleGroup);
|
||||||
renderImageDirection(host);
|
renderImageDirection(host);
|
||||||
} else if (stage === 3) {
|
|
||||||
if (previewHost) renderStylePreview(previewHost);
|
|
||||||
if (previewHost) renderImageStrategyPreview(previewHost);
|
|
||||||
// Stage 3 contains production mechanics only. It summarizes the
|
|
||||||
// confirmed image source and reopens typography only to repair a
|
|
||||||
// legacy prose-only Custom result that cannot be final as-is.
|
|
||||||
if (typographyNeedsConcreteRepair()) {
|
|
||||||
var repairStyleGroup = el("div", "style-group");
|
|
||||||
renderTypography(repairStyleGroup);
|
|
||||||
host.appendChild(repairStyleGroup);
|
|
||||||
}
|
|
||||||
renderImageProduction(host);
|
|
||||||
renderFormulaPolicy(host);
|
|
||||||
renderProactiveExecution(host);
|
|
||||||
renderMode(host);
|
|
||||||
renderRefine(host);
|
|
||||||
} else {
|
|
||||||
// Legacy single-pass: show every section on one page.
|
|
||||||
if (previewHost) renderStylePreview(previewHost);
|
|
||||||
renderCommunication(host);
|
|
||||||
renderDelivery(host);
|
|
||||||
renderCanvas(host);
|
|
||||||
renderTemplateApplication(host);
|
|
||||||
renderDesignDirections(host);
|
|
||||||
renderNarrativeDirection(host);
|
|
||||||
renderVisualDirection(host);
|
|
||||||
renderReadingMode(host);
|
|
||||||
renderPages(host);
|
|
||||||
var legacyStyleGroup = el("div", "style-group");
|
|
||||||
renderColor(legacyStyleGroup);
|
|
||||||
renderIcons(legacyStyleGroup);
|
|
||||||
renderTypography(legacyStyleGroup);
|
|
||||||
host.appendChild(legacyStyleGroup);
|
|
||||||
renderImageDirection(host);
|
|
||||||
renderImageProduction(host);
|
renderImageProduction(host);
|
||||||
renderFormulaPolicy(host);
|
renderFormulaPolicy(host);
|
||||||
renderProactiveExecution(host);
|
renderProactiveExecution(host);
|
||||||
@@ -3014,10 +3305,8 @@
|
|||||||
function updateActionBar(stage) {
|
function updateActionBar(stage) {
|
||||||
var btn = document.getElementById("btn-confirm");
|
var btn = document.getElementById("btn-confirm");
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
// Stage 1/2 both confirm the current values before advancing. Stage 3
|
|
||||||
// and legacy single-pass submit the final result.
|
|
||||||
if (stage === 1) btn.textContent = t("btn_confirm_contract");
|
if (stage === 1) btn.textContent = t("btn_confirm_contract");
|
||||||
else if (stage === 2) btn.textContent = t("btn_confirm_solution");
|
else if (stage === 2) btn.textContent = t("btn_confirm_final_plan");
|
||||||
else btn.textContent = t("btn_confirm");
|
else btn.textContent = t("btn_confirm");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3122,8 +3411,7 @@
|
|||||||
STATE.typography.body_size = defaultBodySizeForCanvas(STATE.canvas, STATE.delivery_purpose);
|
STATE.typography.body_size = defaultBodySizeForCanvas(STATE.canvas, STATE.delivery_purpose);
|
||||||
}
|
}
|
||||||
// A freshly authored Stage 2 starts from one deterministic reading-mode
|
// A freshly authored Stage 2 starts from one deterministic reading-mode
|
||||||
// baseline. Stage 3 carries the confirmed Stage-2 values and must not
|
// baseline.
|
||||||
// normalize them again.
|
|
||||||
if (stageNumber(REC) === 2) syncUnpinnedTypographySizes(true);
|
if (stageNumber(REC) === 2) syncUnpinnedTypographySizes(true);
|
||||||
var rawImageUsage = recValue("image_usage") || directionField("image_usage");
|
var rawImageUsage = recValue("image_usage") || directionField("image_usage");
|
||||||
STATE.image_usage = selectedImageUsageIds(rawImageUsage);
|
STATE.image_usage = selectedImageUsageIds(rawImageUsage);
|
||||||
@@ -3148,7 +3436,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initStage3State() {
|
function initProductionState() {
|
||||||
STATE.formula_policy = pick("formula_policy", CAT.formula_policy);
|
STATE.formula_policy = pick("formula_policy", CAT.formula_policy);
|
||||||
STATE.image_ai_path = pick("image_ai_path", CAT.image_ai_path);
|
STATE.image_ai_path = pick("image_ai_path", CAT.image_ai_path);
|
||||||
STATE.proactive_speaker_notes = booleanRecommendation("proactive_speaker_notes", true);
|
STATE.proactive_speaker_notes = booleanRecommendation("proactive_speaker_notes", true);
|
||||||
@@ -3161,8 +3449,6 @@
|
|||||||
|
|
||||||
function initState() {
|
function initState() {
|
||||||
initStage1State();
|
initStage1State();
|
||||||
initStage2State();
|
|
||||||
initStage3State();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- confirm + close -------------------------------------------------
|
// ---- confirm + close -------------------------------------------------
|
||||||
@@ -3194,6 +3480,11 @@
|
|||||||
function stage1Payload() {
|
function stage1Payload() {
|
||||||
var payload = communicationPayload();
|
var payload = communicationPayload();
|
||||||
payload.stage = "stage1";
|
payload.stage = "stage1";
|
||||||
|
payload.template_selection = {
|
||||||
|
mode: TEMPLATE_MODE,
|
||||||
|
selection_keys: TEMPLATE_MODE === "templates"
|
||||||
|
? TEMPLATE_SELECTED_KEYS.slice() : []
|
||||||
|
};
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3268,36 +3559,14 @@
|
|||||||
var valid = (completePalette || customPalette) &&
|
var valid = (completePalette || customPalette) &&
|
||||||
completeFamilies && completeSizes;
|
completeFamilies && completeSizes;
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
document.getElementById("confirm-status").textContent = t("design_system_required");
|
var message = color.name === "custom" && !customPalette
|
||||||
|
? t("custom_color_required")
|
||||||
|
: t("design_system_required");
|
||||||
|
document.getElementById("confirm-status").textContent = message;
|
||||||
}
|
}
|
||||||
return !!valid;
|
return !!valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
function stage2Payload() {
|
|
||||||
var payload = communicationPayload();
|
|
||||||
payload.stage = "stage2";
|
|
||||||
if (Object.prototype.hasOwnProperty.call(STATE, "template_application")) {
|
|
||||||
payload.template_application = STATE.template_application;
|
|
||||||
}
|
|
||||||
payload.mode = STATE.mode;
|
|
||||||
payload.mode_behavior = STATE.mode_behavior;
|
|
||||||
payload.visual_style = STATE.visual_style;
|
|
||||||
payload.visual_style_behavior = STATE.visual_style_behavior;
|
|
||||||
payload.page_count = STATE.page_count;
|
|
||||||
// Reading mode keeps the legacy delivery_purpose key for compatibility.
|
|
||||||
if (isPptCanvas(STATE.canvas)) payload.delivery_purpose = STATE.delivery_purpose;
|
|
||||||
payload.color = JSON.parse(JSON.stringify(STATE.color || {}));
|
|
||||||
payload.icons = STATE.icons;
|
|
||||||
payload.typography = JSON.parse(JSON.stringify(STATE.typography || {}));
|
|
||||||
normalizeTypographyForSubmit(payload);
|
|
||||||
payload.image_usage = selectedImageUsageIds(STATE.image_usage);
|
|
||||||
if (String(STATE.image_notes || "").trim()) payload.image_notes = STATE.image_notes;
|
|
||||||
if (needsGeneratedImagesForUsage(payload.image_usage)) {
|
|
||||||
payload.image_strategy = normalizedImageStrategy(STATE.image_strategy);
|
|
||||||
}
|
|
||||||
return normalizeCreativePayload(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
function submitStage(payload, nextStage) {
|
function submitStage(payload, nextStage) {
|
||||||
var btn = document.getElementById("btn-confirm");
|
var btn = document.getElementById("btn-confirm");
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
@@ -3328,17 +3597,23 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function templateSelectionValid() {
|
||||||
|
var valid = TEMPLATE_MODE === "free_design" ||
|
||||||
|
(TEMPLATE_MODE === "templates" && TEMPLATE_SELECTED_KEYS.length > 0);
|
||||||
|
if (!valid) {
|
||||||
|
document.getElementById("confirm-status").textContent =
|
||||||
|
t("template_selection_required");
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
function submitStage1() {
|
function submitStage1() {
|
||||||
|
if (!templateSelectionValid()) return;
|
||||||
submitStage(stage1Payload(), 2);
|
submitStage(stage1Payload(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitStage2() {
|
function submitStage2() {
|
||||||
var payload = stage2Payload();
|
confirm();
|
||||||
if (!imageUsageValid(payload.image_usage)) return;
|
|
||||||
if (!imageStrategyValid(payload)) return;
|
|
||||||
if (!designSystemValid(payload)) return;
|
|
||||||
if (!customSelectionsValid(payload)) return;
|
|
||||||
submitStage(payload, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showDeriving() {
|
function showDeriving() {
|
||||||
@@ -3377,8 +3652,10 @@
|
|||||||
|
|
||||||
function enterStage(data, stage) {
|
function enterStage(data, stage) {
|
||||||
REC = data;
|
REC = data;
|
||||||
if (stage >= 2) initStage2State();
|
if (stage === 2) {
|
||||||
if (stage >= 3) initStage3State();
|
initStage2State();
|
||||||
|
initProductionState();
|
||||||
|
}
|
||||||
STAGE = stage;
|
STAGE = stage;
|
||||||
document.getElementById("loading").style.display = "none";
|
document.getElementById("loading").style.display = "none";
|
||||||
document.getElementById("sections").style.display = "block";
|
document.getElementById("sections").style.display = "block";
|
||||||
@@ -3467,6 +3744,44 @@
|
|||||||
.catch(function () { return {}; });
|
.catch(function () { return {}; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyServerLanguage(data) {
|
||||||
|
var requested = data && data.lang;
|
||||||
|
if (requested !== "zh" && requested !== "en" && requested !== "ja") return;
|
||||||
|
var hasStored = false;
|
||||||
|
try { hasStored = !!window.localStorage.getItem("ppt_lang"); } catch (e) { /* ignore */ }
|
||||||
|
if (hasStored) return;
|
||||||
|
LANG = requested;
|
||||||
|
applyStaticTranslations();
|
||||||
|
var toggleBtn = document.getElementById("btn-lang-toggle");
|
||||||
|
if (toggleBtn) refreshLangToggle(toggleBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadStrategistUi(forceStage) {
|
||||||
|
return Promise.all([
|
||||||
|
loadCatalogs(),
|
||||||
|
fetchJson("/api/recommendations", "recommendations"),
|
||||||
|
loadIconPreviews(),
|
||||||
|
loadAiImageComparison()
|
||||||
|
]).then(function (res) {
|
||||||
|
CAT = res[0];
|
||||||
|
REC = res[1];
|
||||||
|
ICON_PREVIEWS = res[2] || {};
|
||||||
|
AI_IMAGE_COMPARISON = res[3] || {};
|
||||||
|
applyServerLanguage(REC);
|
||||||
|
var activeStage = forceStage || stageNumber(REC);
|
||||||
|
if (activeStage === 1) {
|
||||||
|
if (!REC.template_options || typeof REC.template_options !== "object" ||
|
||||||
|
Array.isArray(REC.template_options)) {
|
||||||
|
throw new Error("Stage 1 recommendations must include template_options");
|
||||||
|
}
|
||||||
|
initTemplateOptions(REC.template_options);
|
||||||
|
}
|
||||||
|
initState();
|
||||||
|
enterStage(REC, activeStage);
|
||||||
|
return REC;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function boot() {
|
function boot() {
|
||||||
applyStaticTranslations();
|
applyStaticTranslations();
|
||||||
var toggleBtn = document.getElementById("btn-lang-toggle");
|
var toggleBtn = document.getElementById("btn-lang-toggle");
|
||||||
@@ -3489,7 +3804,9 @@
|
|||||||
try { window.localStorage.setItem("ppt_lang", LANG); } catch (e2) { /* ignore */ }
|
try { window.localStorage.setItem("ppt_lang", LANG); } catch (e2) { /* ignore */ }
|
||||||
applyStaticTranslations();
|
applyStaticTranslations();
|
||||||
refreshLangToggle(toggleBtn);
|
refreshLangToggle(toggleBtn);
|
||||||
if (REC && CAT) renderAll(); // STATE persists → selections preserved
|
if (REC && CAT) {
|
||||||
|
renderAll(); // STATE persists → selections preserved
|
||||||
|
}
|
||||||
};
|
};
|
||||||
toggleBtn.addEventListener("click", function (e) {
|
toggleBtn.addEventListener("click", function (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -3549,33 +3866,21 @@
|
|||||||
else confirm();
|
else confirm();
|
||||||
});
|
});
|
||||||
|
|
||||||
Promise.all([
|
// Session remains the first network read so completed runs can close
|
||||||
loadCatalogs(),
|
// cleanly. Active runs always load the current Strategist stage; Stage 1
|
||||||
fetchJson("/api/recommendations", "recommendations"),
|
// carries its template catalog inside the recommendation payload.
|
||||||
loadIconPreviews(),
|
fetchJson("/api/session", "session").catch(function () {
|
||||||
loadAiImageComparison()
|
return { phase: "strategist" };
|
||||||
]).then(function (res) {
|
}).then(function (session) {
|
||||||
CAT = res[0];
|
if (session && session.status === "done") {
|
||||||
REC = res[1];
|
|
||||||
ICON_PREVIEWS = res[2] || {};
|
|
||||||
AI_IMAGE_COMPARISON = res[3] || {};
|
|
||||||
if (REC.lang === "zh" || REC.lang === "en" || REC.lang === "ja") {
|
|
||||||
var hasStored = false;
|
|
||||||
try { hasStored = !!window.localStorage.getItem("ppt_lang"); } catch (e) { /* ignore */ }
|
|
||||||
if (!hasStored) { LANG = REC.lang; applyStaticTranslations(); refreshLangToggle(toggleBtn); }
|
|
||||||
}
|
|
||||||
initState();
|
|
||||||
// stage 1 / 2 / 3 from the recommendations; absent → legacy single-pass.
|
|
||||||
STAGE = stageNumber(REC);
|
|
||||||
document.getElementById("loading").style.display = "none";
|
document.getElementById("loading").style.display = "none";
|
||||||
document.getElementById("sections").style.display = "block";
|
showConfirmedOverlay();
|
||||||
document.getElementById("actionbar").style.display = "flex";
|
return null;
|
||||||
renderAll();
|
|
||||||
if (REC._already_confirmed) {
|
|
||||||
document.getElementById("confirm-status").textContent = t("already_confirmed");
|
|
||||||
}
|
}
|
||||||
}).catch(function (err) {
|
return loadStrategistUi().catch(function (err) {
|
||||||
showError(t("load_error") + " " + (err && err.message ? err.message : ""));
|
showError(t("load_error") + " " + (err && err.message ? err.message : ""));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+147
@@ -601,6 +601,150 @@ textarea.text-input { resize: vertical; line-height: 1.5; }
|
|||||||
.subfield { margin-top: 14px; }
|
.subfield { margin-top: 14px; }
|
||||||
.subfield-label { font-size: 12.5px; color: var(--muted); margin-bottom: 7px; }
|
.subfield-label { font-size: 12.5px; color: var(--muted); margin-bottom: 7px; }
|
||||||
|
|
||||||
|
/* ---- Stage 1 template selection ---- */
|
||||||
|
.template-mode-choices {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.template-mode-choice {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 11px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 9px;
|
||||||
|
background: var(--card);
|
||||||
|
color: var(--ink);
|
||||||
|
padding: 11px 13px;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color .12s ease, background .12s ease, box-shadow .12s ease;
|
||||||
|
}
|
||||||
|
.template-mode-choice:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
.template-mode-choice:focus-visible {
|
||||||
|
outline: 2px solid var(--accent);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
.template-mode-choice.selected {
|
||||||
|
border-color: var(--accent);
|
||||||
|
background: var(--accent-soft);
|
||||||
|
box-shadow: 0 0 0 1px var(--accent);
|
||||||
|
}
|
||||||
|
.template-choice-radio {
|
||||||
|
flex: none;
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
margin-top: 2px;
|
||||||
|
border: 2px solid #aeb5bf;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: inset 0 0 0 3px #fff;
|
||||||
|
}
|
||||||
|
.template-mode-choice.selected .template-choice-radio {
|
||||||
|
border-color: var(--accent);
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
.template-choice-copy {
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.template-choice-title {
|
||||||
|
font-size: 13.5px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.template-choice-desc {
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12.5px;
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
.template-selector-panel {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 14px;
|
||||||
|
border-top: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
.template-selector-panel[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.template-selector-hint {
|
||||||
|
margin-bottom: 11px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12.5px;
|
||||||
|
}
|
||||||
|
.template-select-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
gap: 13px 16px;
|
||||||
|
}
|
||||||
|
.template-select-field {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.template-select-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
color: var(--ink);
|
||||||
|
font-size: 12.5px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.template-select {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
height: 40px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 34px 0 11px;
|
||||||
|
background: var(--card);
|
||||||
|
color: var(--ink);
|
||||||
|
font-size: 13.5px;
|
||||||
|
}
|
||||||
|
.template-select:hover:not(:disabled) {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
.template-select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
box-shadow: 0 0 0 2px var(--accent-soft);
|
||||||
|
}
|
||||||
|
.template-select:disabled {
|
||||||
|
background: #f4f6f8;
|
||||||
|
color: #9aa1aa;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.template-select-help {
|
||||||
|
margin-top: 6px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 11.5px;
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
.template-selected-path {
|
||||||
|
margin-top: 7px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.template-selected-path[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.template-selected-path-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 10.5px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.template-selected-path-value {
|
||||||
|
display: block;
|
||||||
|
color: #475569;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.4;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- toggles ---- */
|
/* ---- toggles ---- */
|
||||||
.toggle-row { display: flex; align-items: center; gap: 12px; }
|
.toggle-row { display: flex; align-items: center; gap: 12px; }
|
||||||
.toggle-desc { font-size: 12.5px; color: var(--muted); }
|
.toggle-desc { font-size: 12.5px; color: var(--muted); }
|
||||||
@@ -954,4 +1098,7 @@ textarea.text-input { resize: vertical; line-height: 1.5; }
|
|||||||
.style-preview-card {
|
.style-preview-card {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
.template-mode-choices {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from pathlib import Path
|
|||||||
from typing import TextIO
|
from typing import TextIO
|
||||||
|
|
||||||
from attribution_guard import require_skill_integrity
|
from attribution_guard import require_skill_integrity
|
||||||
|
from workflow_transcript import install_auto_transcript
|
||||||
|
|
||||||
|
|
||||||
_SKILL_DIR = Path(__file__).resolve().parent.parent
|
_SKILL_DIR = Path(__file__).resolve().parent.parent
|
||||||
@@ -79,8 +80,9 @@ def _reconfigure_stream(stream: TextIO) -> TextIO:
|
|||||||
|
|
||||||
|
|
||||||
def configure_utf8_stdio() -> None:
|
def configure_utf8_stdio() -> None:
|
||||||
"""Use UTF-8 for CLI stdout/stderr, including Windows non-UTF-8 locales."""
|
"""Configure CLI streams and enable project-scoped output recording."""
|
||||||
require_skill_integrity()
|
require_skill_integrity()
|
||||||
_require_official_distribution_identity()
|
_require_official_distribution_identity()
|
||||||
sys.stdout = _reconfigure_stream(sys.stdout)
|
sys.stdout = _reconfigure_stream(sys.stdout)
|
||||||
sys.stderr = _reconfigure_stream(sys.stderr)
|
sys.stderr = _reconfigure_stream(sys.stderr)
|
||||||
|
install_auto_transcript()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Chart Candidate Recall
|
# Chart Candidate Recall
|
||||||
|
|
||||||
`chart_recall.py` gives the Strategist a bounded deterministic shortlist. It exposes the full live catalog only when the caller explicitly requests semantic review. It reads `templates/charts/charts_index.json` on every invocation, so the catalog remains the only template registry.
|
`chart_recall.py` gives Default Strategist or the Quick Generate main agent a bounded deterministic shortlist. It exposes the full live catalog only when the caller explicitly requests semantic review. It reads `templates/charts/charts_index.json` on every invocation, so the catalog remains the only template registry.
|
||||||
|
|
||||||
## Recall candidates
|
## Recall candidates
|
||||||
|
|
||||||
@@ -28,23 +28,24 @@ Semantically review the bounded candidates. At `high` / `medium`, retain `no-tem
|
|||||||
| `semantic_fallback` | Full live catalog, present only with `--semantic-fallback`; requires semantic comparison |
|
| `semantic_fallback` | Full live catalog, present only with `--semantic-fallback`; requires semantic comparison |
|
||||||
| `no_template_match` | Explicit fallback; `allowed` stays false for `low` / `none` until `--semantic-fallback` is used |
|
| `no_template_match` | Explicit fallback; `allowed` stays false for `low` / `none` until `--semantic-fallback` is used |
|
||||||
|
|
||||||
The scorer treats the key and the summary's Pick clause as positive evidence and the Skip clause as negative evidence. A term found only in Skip cannot make a candidate eligible, and Skip matches explicitly reduce a candidate's score. Unicode input is NFKC-normalized before matching. The Strategist still applies semantic judgment: inspect the returned candidates, reject candidates whose Skip clause matches, and prefer the most specific valid structure. An empty or low-confidence shortlist requires one `--semantic-fallback` review only when the Strategist is about to keep `no-template-match`.
|
The scorer treats the key and the summary's Pick clause as positive evidence and the Skip clause as negative evidence. A term found only in Skip cannot make a candidate eligible, and Skip matches explicitly reduce a candidate's score. Unicode input is NFKC-normalized before matching. The active profile owner still applies semantic judgment: inspect the returned candidates, reject candidates whose Skip clause matches, and prefer the most specific valid structure. An empty or low-confidence shortlist requires one `--semantic-fallback` review only when the owner is about to keep `no-template-match`.
|
||||||
|
|
||||||
## Validate selected keys
|
## Validate selected keys
|
||||||
|
|
||||||
Validate every selected template key before writing `design_spec.md §VII` or `spec_lock.md page_charts`:
|
Validate every selected template key before Default writes `design_spec.md §VII` / `spec_lock.md page_charts` or Quick opens that key for immediate use:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 skills/ppt-master/scripts/chart_recall.py validate line_chart quadrant_text_bullets
|
python3 skills/ppt-master/scripts/chart_recall.py validate line_chart quadrant_text_bullets
|
||||||
```
|
```
|
||||||
|
|
||||||
The command is read-only. It exits `0` when every key exists and `1` when any key is absent. A `no-template-match` page appears in neither §VII nor `page_charts`; record its chosen fallback in the page's §IX `Visualization` / `Layout` instead.
|
The command is read-only. It exits `0` when every key exists and `1` when any key is absent. A Default `no-template-match` page appears in neither §VII nor `page_charts`; record its chosen fallback in the page's §IX `Visualization` / `Layout`. Quick keeps the fallback only in active context and writes no mapping artifact.
|
||||||
|
|
||||||
## Selection boundary
|
## Selection boundary
|
||||||
|
|
||||||
- Preserve the two-lens review: numeric/data pages and structural-information pages.
|
- Preserve the two-lens review: numeric/data pages and structural-information pages.
|
||||||
- Keep §VII as a positive selection list: record `Page | Template | Usage` for each selected key, and omit the whole section when no candidate is selected.
|
- Default keeps §VII as a positive selection list: record `Page | Template | Usage` for each selected key, and omit the whole section when no candidate is selected.
|
||||||
- Make `Usage` one concise page-local purpose, not geometry or execution instructions; derive `templates/charts/<key>.svg` from the key and keep detailed adaptation in §IX.
|
- Default makes `Usage` one concise page-local purpose, not geometry or execution instructions; derive `templates/charts/<key>.svg` from the key and keep detailed adaptation in §IX.
|
||||||
|
- Quick retains the selected key and immediate page purpose only in active context; do not create §VII, `page_charts`, or a substitute mapping file.
|
||||||
- Never serialize `no-template-match`, an empty table, or a no-reference explanation into §VII.
|
- Never serialize `no-template-match`, an empty table, or a no-reference explanation into §VII.
|
||||||
- Do not serialize returned summaries, paths, or runners-up into new §VII tables. Legacy wider tables remain readable.
|
- Do not serialize returned summaries, paths, or runners-up into new §VII tables. Legacy wider tables remain readable.
|
||||||
- Open the selected `<key>.svg` only as a reference for its mapped page; it does not lock type or geometry. Do not load unrelated catalog SVGs.
|
- Open the selected `<key>.svg` only as a reference for its mapped page; it does not lock type or geometry. Do not load unrelated catalog SVGs.
|
||||||
|
|||||||
@@ -1,14 +1,24 @@
|
|||||||
# Confirm UI — Strategist Confirmation Stage Page
|
# Confirm UI — Strategist and Template Confirmation Page
|
||||||
|
|
||||||
> The interactive, visual surface for [`generate-pptx`](../../workflows/generate-pptx.md) Step 4. Stage 1 is an open communication brief: common purpose paths are prompt text, never checkboxes or a forced single label. Stage 2 offers **≥3 coordinated design directions** and then exposes their component values for deliberate override; color, typography, icons, and generated-image rendering are one system rather than unrelated grids. When a template workspace is active, Stage 2 also shows one editable natural-language template-application plan—never internal mode controls. Generated images inherit the selected deck colors directly—there is no image-palette control. Stage 3 contains production mechanics only. The AI uses one `recommendations.stageN.json` file per stage; the active, unconfirmed stage may be overwritten when the user requests a new recommendation. Confirmed values accumulate into `result.json`. Final confirm saves the result and shuts the server down. The chat path mirrors the same staged semantics.
|
> The interactive surface for [`generate-pptx`](../../workflows/generate-pptx.md)
|
||||||
|
> Step 4. Stage 1 shows the template-independent communication recommendation
|
||||||
|
> and the template/free-design choice on one page and confirms both with one
|
||||||
|
> submission. That submission writes the Strategist contract to `result.json`
|
||||||
|
> and the selection sidecar to `template_selection.json`. The agent then installs
|
||||||
|
> any selected workspace and writes `template_handoff.json`; only afterward does
|
||||||
|
> final Stage 2 read installed template state and confirm the coordinated deck
|
||||||
|
> solution plus production mechanics. The chat path mirrors these boundaries
|
||||||
|
> without fabricating UI receipts.
|
||||||
|
|
||||||
## Authority and Scope
|
## Authority and Scope
|
||||||
|
|
||||||
| Concern | Owner |
|
| Concern | Owner |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
| Stage-1 combined confirmation and post-confirmation installation order | [`generate-pptx.md`](../../workflows/generate-pptx.md) |
|
||||||
|
| Template option/selection schema and page transport | This document |
|
||||||
| Step 4 gate and pipeline order | [`generate-pptx.md`](../../workflows/generate-pptx.md) |
|
| Step 4 gate and pipeline order | [`generate-pptx.md`](../../workflows/generate-pptx.md) |
|
||||||
| Confirm UI schema | This document |
|
| Confirm UI schema | This document |
|
||||||
| Stage 1 / Stage 2 / Stage 3 field membership | This document |
|
| Stage 1 / final Stage 2 field membership | This document |
|
||||||
| Server launch / wait / shutdown behavior | This document |
|
| Server launch / wait / shutdown behavior | This document |
|
||||||
| Port and lock behavior | This document |
|
| Port and lock behavior | This document |
|
||||||
| Chat fallback equivalence | This document |
|
| Chat fallback equivalence | This document |
|
||||||
@@ -24,23 +34,35 @@ starts in chat or UI switches to chat, keep chat for the rest of this run.
|
|||||||
|
|
||||||
| Most recent explicit surface instruction | Branch |
|
| Most recent explicit surface instruction | Branch |
|
||||||
|---|---|
|
|---|---|
|
||||||
| The user explicitly delegates confirmation | Present one complete delegated three-stage summary in chat. Do not launch the page or fabricate `result.json`. |
|
| The user explicitly delegates confirmation | Make the combined Stage-1 communication/template decision, install it, then present one complete final summary. Do not launch the page or fabricate UI receipts. |
|
||||||
| Otherwise, the user asks for or agrees to personally confirm in chat, or declines the confirmation page | Use chat for all three stages. Do not launch the page, run `--wait-only`, or require a UI-authored `result.json`. |
|
| Otherwise, the user asks for or agrees to personally confirm in chat, or declines the confirmation page | Use chat for both Strategist stages; Stage 1 includes the template/free-design choice. Do not launch the page, run `--wait-only`, or require UI-authored receipts. |
|
||||||
| No explicit confirmation-surface instruction exists for this run | Use the page as the default. |
|
| No explicit confirmation-surface instruction exists for this run | Use the page as the default. |
|
||||||
|
|
||||||
Interpret the instruction semantically: “confirm here”, “use the chat window”, or
|
Interpret the instruction semantically: “confirm here”, “use the chat window”, or
|
||||||
“do not open the confirmation page” are sufficient; no literal `chat-only`
|
“do not open the confirmation page” are sufficient; no literal `chat-only`
|
||||||
keyword is required. Invoking a chat-question tool by itself does not select the
|
keyword is required. Invoking a chat-question tool by itself does not select the
|
||||||
chat branch—the user's instruction does. Both branches preserve the same three
|
chat branch—the user's instruction does. Both branches preserve the same
|
||||||
stages and confirmed-value semantics.
|
Stage-1 communication/template decision, installation handoff, and template-aware
|
||||||
|
final Stage 2.
|
||||||
|
|
||||||
|
**Chat/delegated Stage-1 listing**: Author the communication recommendation
|
||||||
|
before reading the four indexes, then present that recommendation together with
|
||||||
|
an explicit free-design/template-mode choice. Only template mode expands the
|
||||||
|
registered candidates and supplied exact roots, and it requires at least one
|
||||||
|
selection. Ordinary requests initialize free design; explicit template intent
|
||||||
|
or any supplied exact root initializes template mode. Exactly one supplied root
|
||||||
|
may also seed that candidate, while multiple roots remain unselected. Under
|
||||||
|
explicit delegation, make the same decision, install
|
||||||
|
it, and only then derive Stage 2. Do not launch the page or fabricate receipts.
|
||||||
|
|
||||||
**Fallback rule**: When no surface was selected before launch, the page is the
|
**Fallback rule**: When no surface was selected before launch, the page is the
|
||||||
default. Use chat when the user answers the always-on handoff in chat, or after
|
default. Use chat when the user answers either always-on handoff in chat, or
|
||||||
launch failure/timeout and one `result.json` re-check. A chat-question tool
|
after launch failure/timeout and one re-check of `result.json` plus the
|
||||||
alone is not a launch failure. Preserve all three stages and keep Stage-1
|
Stage-1-sidecar `template_selection.json` when Stage 1 is active. A chat-question
|
||||||
prompts open-ended.
|
tool alone is not a launch failure. Preserve the combined Stage-1 decision and
|
||||||
|
keep communication prompts open-ended.
|
||||||
|
|
||||||
**In-run UI → chat switch — any stage**: If the user explicitly selects chat
|
**In-run UI → chat switch — any phase or stage**: If the user explicitly selects chat
|
||||||
after the UI server has launched—while `--wait-only` is active, before that wait
|
after the UI server has launched—while `--wait-only` is active, before that wait
|
||||||
starts, or after it times out while the server remains live:
|
starts, or after it times out while the server remains live:
|
||||||
|
|
||||||
@@ -50,52 +72,66 @@ starts, or after it times out while the server remains live:
|
|||||||
2. Run `server.py <project_path> --shutdown` and require that cleanup to
|
2. Run `server.py <project_path> --shutdown` and require that cleanup to
|
||||||
succeed. The browser tab may remain open, but its stopped server makes it
|
succeed. The browser tab may remain open, but its stopped server makes it
|
||||||
inactive.
|
inactive.
|
||||||
3. Re-check `result.json` once for the current stage. Retain only values whose
|
3. Re-check the active receipt once. During Stage 1, require both `result.json`
|
||||||
confirmation was persisted before shutdown; an unsubmitted browser draft is
|
and `template_selection.json` from the same submission; during Stage 2, check
|
||||||
not confirmed.
|
`result.json`. Retain only values persisted before shutdown; an unsubmitted
|
||||||
4. Continue the unresolved current stage and every remaining stage in chat. Do
|
browser draft is not confirmed.
|
||||||
|
4. Continue the unresolved current phase/stage and everything remaining in chat. Do
|
||||||
not call `--wait-only` again, recover the server, or relaunch the page during
|
not call `--wait-only` again, recover the server, or relaunch the page during
|
||||||
this run.
|
this run.
|
||||||
|
|
||||||
**Always-on Stage-1 chat handoff**: Launch the healthy daemon without `--wait`.
|
**Always-on Stage-1 chat handoff**: After writing `template_options.json` and
|
||||||
After it returns, immediately post its actual URL plus one compact, localized
|
template-independent `recommendations.stage1.json`, launch the healthy daemon
|
||||||
summary of the current Stage-1 recommendations: audience, communication intent,
|
without `--wait`. Immediately post its actual URL plus one compact localized
|
||||||
audience outcome, core message, delivery context, artifact afterlife,
|
summary of the current communication recommendation and template choice state:
|
||||||
`content_divergence`, and canvas. Show a blank as “not specified” without
|
audience, communication intent, audience outcome, core message, delivery
|
||||||
changing its value. End with an explicit localized line saying that, if the
|
context, artifact afterlife, `content_divergence`, canvas, and whether the
|
||||||
page did not open or cannot be reached, the user may reply “continue with these
|
default is free design or template mode, including any sole preselected root. Explain
|
||||||
recommendations” or revise the same items directly in chat; the same three-stage
|
that template mode expands the registered-kind and supplied-root selectors.
|
||||||
flow will continue. Only then run `--wait-only --wait-stage stage1`. A chat
|
Show a blank prose value as “not specified” without changing it. End with an
|
||||||
|
explicit localized line saying that, if the page did not open, the user may
|
||||||
|
confirm or revise the same communication and template choices in chat. Only
|
||||||
|
then run `--wait-only --wait-stage stage1`. A chat
|
||||||
reply to that handoff applies the in-run switch above without waiting for
|
reply to that handoff applies the in-run switch above without waiting for
|
||||||
timeout. The handoff is context, not confirmation, and silence confirms
|
timeout. The handoff is context, not confirmation, and silence confirms
|
||||||
nothing. After launch failure/timeout and the required result re-check, present
|
nothing. After launch failure/timeout and the required result re-check, present
|
||||||
the same items as open Stage-1 chat questions and wait for an explicit response.
|
the same combined Stage-1 items as open chat questions and wait explicitly.
|
||||||
|
|
||||||
## `confirm_ui/server.py`
|
## `confirm_ui/server.py`
|
||||||
|
|
||||||
The following launch and wait commands belong to the **UI branch only**:
|
The following launch and wait commands belong to the **UI branch only**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 scripts/confirm_ui/server.py <project_path> --daemon # healthy launch; return for chat handoff
|
python3 scripts/confirm_ui/server.py <project_path> --daemon # launch combined Stage 1
|
||||||
python3 scripts/confirm_ui/server.py <project_path> --wait-only --wait-stage stage1 # Stage 1
|
python3 scripts/confirm_ui/server.py <project_path> --wait-only --wait-stage stage1 # communication + template selection
|
||||||
python3 scripts/confirm_ui/server.py <project_path> --wait-only --wait-stage stage2 # Stage 2: wait for the direction handoff
|
python3 scripts/confirm_ui/server.py <project_path> --complete-template-selection # after Stage-1 free-design closure / template install
|
||||||
python3 scripts/confirm_ui/server.py <project_path> --wait-only # Stage 3: wait for the final result
|
python3 scripts/confirm_ui/server.py <project_path> --wait-only # current final Stage 2
|
||||||
python3 scripts/confirm_ui/server.py <project_path> --daemon --port 5051
|
python3 scripts/confirm_ui/server.py <project_path> --daemon --port 5051
|
||||||
python3 scripts/confirm_ui/server.py <project_path> --no-browser
|
python3 scripts/confirm_ui/server.py <project_path> --no-browser
|
||||||
python3 scripts/confirm_ui/server.py <project_path> --timeout 0 # disable idle auto-shutdown
|
python3 scripts/confirm_ui/server.py <project_path> --timeout 0 # disable idle auto-shutdown
|
||||||
|
python3 scripts/confirm_ui/server.py <project_path> --reset-template-selection # clear prior template sidecars before a fresh UI run
|
||||||
python3 scripts/confirm_ui/server.py <project_path> --shutdown # Step 4 cleanup (idempotent)
|
python3 scripts/confirm_ui/server.py <project_path> --shutdown # Step 4 cleanup (idempotent)
|
||||||
```
|
```
|
||||||
|
|
||||||
- Without `--port`, binds the first free port from `127.0.0.1:5050`; the launch log prints the actual URL. `--port N` is exact and fails when unavailable. Auto-open is suppressed by `--no-browser`.
|
- Without `--port`, binds the first free port from `127.0.0.1:5050`; the launch log prints the actual URL. `--port N` is exact and fails when unavailable. Auto-open is suppressed by `--no-browser`.
|
||||||
- In `--daemon` mode the launcher starts the child with browser opening suppressed, then accepts readiness only when `GET /api/health` identifies this confirm service, project, and child process. It opens the printed `http://127.0.0.1:<port>` URL only after that check.
|
- In `--daemon` mode the launcher starts the child with browser opening suppressed, then accepts readiness only when `GET /api/health` identifies this confirm service, project, and child process. It opens the printed `http://127.0.0.1:<port>` URL only after that check.
|
||||||
- Confirm UI and live preview prefer the same memorable base port but keep separate processes and project-local locks (`.confirm_ui.lock` vs `live_preview/lock.json`). Normal Step 4 cleanup releases the confirm port before Step 6; concurrent projects may use different ports.
|
- Confirm UI and live preview prefer the same memorable base port but keep separate processes and project-local locks (`.confirm_ui.lock` vs `live_preview/lock.json`). Normal Step 4 cleanup releases the confirm port before Step 6; concurrent projects may use different ports.
|
||||||
- `--daemon` starts the Flask process in the background and returns after the health check. Generate uses it without `--wait` so the Stage-1 chat handoff appears before blocking. `--daemon --wait` remains a combined compatibility form. The wait budget defaults to **590 s** (`--wait-timeout`); on timeout the detached server remains live, and the caller re-checks `result.json` once before chat fallback.
|
- `--daemon` starts the Flask process in the background and returns after the health check. Every Default UI run launches directly into combined Stage 1 and keeps the same process live through final Stage 2. The wait budget defaults to **590 s** (`--wait-timeout`); on timeout the detached server remains live, and the caller re-checks both Stage-1 receipts before chat fallback.
|
||||||
- `--wait-only` attaches to the page opened by `--daemon` and blocks until the requested stage. If that stage is already persisted, it returns before recovery, so a fast submit between launch, chat handoff, and wait is not lost. Otherwise, if the recorded server died, it restarts on the recorded/default port. Use `stage1` only for a fresh Stage-1 launch, `stage2` for the complete-solution handoff, and `final` for Stage 3. It keys on stage alone; resume chooses the target from the persisted result instead of restarting Stage 1.
|
- `--wait-only` attaches to the page opened by `--daemon` and blocks until the requested receipt. If it is already persisted, the command returns before recovery, so a fast submit between launch, chat handoff, and wait is not lost. Otherwise, if the recorded server died, it restarts on the recorded/default port. Use `stage1` for the combined communication/template submission and the default/final wait for Stage 2.
|
||||||
|
- `--complete-template-selection` is agent-only. It validates the Stage-1 sidecar and writes the bound `template_handoff.json`; template mode additionally requires project-local `templates/design_spec.md`. Run it after installation/free-design closure and before writing Stage 2. `--reset-template-selection` removes exactly `template_options.json`, `template_selection.json`, and `template_handoff.json`; it does not alter Strategist files, installed template content, or `result.json`. The old `--*-template-phase` names are not aliases.
|
||||||
- `--shutdown` stops a confirm server left running for this project and exits — **idempotent** (a no-op when nothing is running). Tries a graceful `/api/shutdown`, falls back to killing the recorded pid, then clears the lock. Generate Step 4 runs this on every path so the selected port is released before live preview starts.
|
- `--shutdown` stops a confirm server left running for this project and exits — **idempotent** (a no-op when nothing is running). Tries a graceful `/api/shutdown`, falls back to killing the recorded pid, then clears the lock. Generate Step 4 runs this on every path so the selected port is released before live preview starts.
|
||||||
- Refuses to start unless the recommendation file expected from `result.json` exists (initially `<project_path>/confirm_ui/recommendations.stage1.json`; `--shutdown` needs no recommendations).
|
- Every fresh UI run starts with `--reset-template-selection`, then writes valid `<project_path>/confirm_ui/template_options.json` and a newer `recommendations.stage1.json`; `explicit_workspace_roots` is an empty array when no exact root was supplied. Stage 1 writes the bound selection and communication result together. Stage 2 is exposed only when the matching handoff is newer than that selection and its recommendation is newer than the handoff. `--shutdown` needs neither input.
|
||||||
- Per-project lock at `<project_path>/.confirm_ui.lock` — duplicate launches are refused; stale locks (dead pid) are overwritten.
|
- Per-project lock at `<project_path>/.confirm_ui.lock` — duplicate launches are refused; stale locks (dead pid) are overwritten.
|
||||||
- Idle auto-shutdown after 900 s by default; `/api/shutdown` exits gracefully and releases the lock.
|
- Idle auto-shutdown after 900 s by default; `/api/shutdown` exits gracefully and releases the lock.
|
||||||
- `/api/recommendations` and `/api/confirm` strip legacy `template_reuse_scope` and `template_adherence` fields. Those exporter values are never user-facing controls; an active template instead exposes the editable natural-language `template_application` field in Stage 2.
|
- Stage-1 `GET /api/recommendations` embeds the server-built candidate catalog
|
||||||
|
as top-level `template_options`. Its `/api/confirm` submission validates
|
||||||
|
current candidate keys and writes `template_selection.json` beside the pure
|
||||||
|
Strategist `result.json`; there is no independent template-options endpoint,
|
||||||
|
template-confirm endpoint, or template wait stage. The same APIs later serve
|
||||||
|
Stage 2 and strip legacy `template_reuse_scope` / `template_adherence`
|
||||||
|
fields. The completed template handoff is authoritative: `free_design`
|
||||||
|
strips a stray `template_application`, while `templates` exposes that
|
||||||
|
editable natural-language field in Stage 2.
|
||||||
|
|
||||||
Dependency:
|
Dependency:
|
||||||
|
|
||||||
@@ -103,13 +139,179 @@ Dependency:
|
|||||||
pip install flask
|
pip install flask
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Stage-1 template-selection sidecar contract
|
||||||
|
|
||||||
|
Template selection shares the Stage-1 page and submit action but remains a
|
||||||
|
separate artifact from the Strategist contract. Its files live under
|
||||||
|
`<project_path>/confirm_ui/`; selection keys never enter `result.json`.
|
||||||
|
|
||||||
|
### Input — `template_options.json` (created before launch)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"phase": "template",
|
||||||
|
"lang": "zh",
|
||||||
|
"default_mode": "free_design",
|
||||||
|
"explicit_workspace_roots": [
|
||||||
|
"/absolute/path/to/a/project-or-template-workspace"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `schema_version` is exactly `1`; `phase` is exactly `template`.
|
||||||
|
- `lang` is optional, but when present it is a non-empty UI-language string.
|
||||||
|
- `default_mode` is required and is exactly `free_design` or `templates`.
|
||||||
|
Ordinary requests use `free_design`; explicit template intent or any supplied
|
||||||
|
exact root uses `templates`. It initializes the UI but never locks the user.
|
||||||
|
- `explicit_workspace_roots` is required even when empty. Every item is a
|
||||||
|
unique absolute path resolving to an existing directory with
|
||||||
|
`templates/design_spec.md` or compatible legacy `design_spec.md`.
|
||||||
|
- The array supplies candidates for the one specified-root dropdown; it does
|
||||||
|
not authorize selecting several explicit roots in one confirmation.
|
||||||
|
- Do not write library entries into this file. The server reads only
|
||||||
|
`templates/brands/brands_index.json`,
|
||||||
|
`templates/styles/styles_index.json`,
|
||||||
|
`templates/layouts/layouts_index.json`, and
|
||||||
|
`templates/decks/decks_index.json`, derives each direct-child workspace root,
|
||||||
|
and validates that it exists with `templates/design_spec.md`. It never scans
|
||||||
|
kind directories.
|
||||||
|
|
||||||
|
Stage-1 `GET /api/recommendations` embeds this browser catalog as top-level
|
||||||
|
`template_options`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"phase": "template",
|
||||||
|
"lang": "zh",
|
||||||
|
"default_mode": "free_design",
|
||||||
|
"library": {
|
||||||
|
"brand": [],
|
||||||
|
"style": [],
|
||||||
|
"layout": [],
|
||||||
|
"deck": []
|
||||||
|
},
|
||||||
|
"explicit": [],
|
||||||
|
"preselected_keys": [],
|
||||||
|
"options_sha256": "<64 lowercase hex characters>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A library candidate has `key`, `source: "library"`, `kind`, `id`, `label`,
|
||||||
|
`summary`, and canonical absolute `workspace_root`. An unregistered explicit
|
||||||
|
candidate has `key`, `source: "explicit"`, parsed `kind`, `label`, and
|
||||||
|
canonical absolute `workspace_root`. If a supplied root exactly equals a registered canonical
|
||||||
|
root, the server reuses the library candidate/key instead of duplicating it as
|
||||||
|
explicit. Candidate keys are server-owned. The Stage-1 submit payload carries
|
||||||
|
the current `{ "mode": "free_design"|"templates", "selection_keys": [...] }`
|
||||||
|
beside the Strategist fields; the server validates that selection and writes it
|
||||||
|
to the sidecar rather than copying keys into `result.json`.
|
||||||
|
|
||||||
|
When the input supplies exactly one root, `preselected_keys` contains its
|
||||||
|
resolved candidate key as a convenience default, including when exact equality
|
||||||
|
reclassifies it as library. When several roots are supplied, all remain
|
||||||
|
candidates but none is preselected; one specified-root dropdown cannot encode
|
||||||
|
an instruction to use all of them.
|
||||||
|
|
||||||
|
**Page selection model**: Stage 1 first asks the user to choose `Free design` or
|
||||||
|
`Use templates`, initialized from `default_mode` but always switchable. Exactly
|
||||||
|
one supplied root may initialize its candidate as an editable convenience
|
||||||
|
default; multiple roots remain unselected. Only `Use templates` expands the
|
||||||
|
candidate controls: Brand, Style, Layout, and Deck each have one registered
|
||||||
|
single-select dropdown, and Specified has one explicit-root single-select
|
||||||
|
dropdown. Every dropdown includes `None`; template mode cannot submit until at
|
||||||
|
least one is non-empty. Free design clears all dropdowns. Registered kinds may
|
||||||
|
be combined, but each contributes at most one root and the specified channel at
|
||||||
|
most one. Because an explicit candidate carries its parsed kind, it may coexist
|
||||||
|
with one registered root of that kind and enter the two-workspace same-kind
|
||||||
|
conflict gate. Source provenance never grants priority.
|
||||||
|
|
||||||
|
### Output — `template_selection.json` (written with Stage 1)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"phase": "template",
|
||||||
|
"status": "confirmed",
|
||||||
|
"mode": "templates",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"source": "library",
|
||||||
|
"kind": "style",
|
||||||
|
"id": "example_style",
|
||||||
|
"workspace_root": "/canonical/library/root/example_style"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "explicit",
|
||||||
|
"kind": "deck",
|
||||||
|
"workspace_root": "/canonical/unregistered/workspace/root"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"options_sha256": "<64 lowercase hex characters>",
|
||||||
|
"selection_sha256": "<64 lowercase hex characters>",
|
||||||
|
"confirmed_at": "2026-08-04T12:00:00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`mode: "free_design"` requires `selections: []`; `mode: "templates"` requires
|
||||||
|
at least one selection. Roots are unique canonical absolute paths. A library
|
||||||
|
selection contains exactly `source`, `kind`, `id`, and `workspace_root`; an
|
||||||
|
explicit selection contains exactly `source`, `kind`, and `workspace_root`.
|
||||||
|
There is at most one library selection per kind and at most one explicit
|
||||||
|
selection overall; cross-kind composition remains valid, and one explicit plus
|
||||||
|
one library selection may share a kind. The browser
|
||||||
|
cannot submit arbitrary paths because the server resolves posted keys against
|
||||||
|
the catalog it just built. `options_sha256` binds the receipt to the current
|
||||||
|
input, four index files, and resolved candidates. `selection_sha256` binds the
|
||||||
|
mode and canonical sorted selections to that option hash. Every receipt read
|
||||||
|
rebuilds the catalog and rejects option/index drift.
|
||||||
|
|
||||||
|
The Stage-1 submission writes this receipt and the Stage-1 `result.json`
|
||||||
|
together. Generate reads both exactly once after `--wait-only --wait-stage
|
||||||
|
stage1` returns. Free design skips installation. Template mode runs
|
||||||
|
`apply-template-workspace` against all selected roots and waits for complete
|
||||||
|
project-local installation/fusion. Only then does the agent complete the
|
||||||
|
handoff below. Installation resolves template-to-template ownership; Stage 2
|
||||||
|
later evaluates current-project fit from the installed copy. Strategist never
|
||||||
|
reads the source roots.
|
||||||
|
|
||||||
|
### Agent handoff — `template_handoff.json`
|
||||||
|
|
||||||
|
After free design closes or template installation succeeds, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/confirm_ui/server.py <project_path> --complete-template-selection
|
||||||
|
```
|
||||||
|
|
||||||
|
The command writes, and agents must not hand-author:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"phase": "template",
|
||||||
|
"status": "ready",
|
||||||
|
"mode": "templates",
|
||||||
|
"selection_sha256": "<64 lowercase hex characters>",
|
||||||
|
"completed_at": "2026-08-04T12:01:00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The handoff must match the current valid selection. Template mode also requires
|
||||||
|
`<project_path>/templates/design_spec.md`; free design requires no installed
|
||||||
|
spec. Write `recommendations.stage2.json` only after this command succeeds, so
|
||||||
|
its file time is newer than the handoff.
|
||||||
|
|
||||||
## Field shapes
|
## Field shapes
|
||||||
|
|
||||||
|
The following fields belong to the Strategist stages, not to the
|
||||||
|
template-selection receipt.
|
||||||
|
|
||||||
- **Enumerable + custom** — canvas / icons retain blank manual inputs; mode / visual_style instead show a mandatory AI-authored proposal in full, initially unselected and editable after selection. Selected mode / style writes literal `custom` plus its behavior sibling.
|
- **Enumerable + custom** — canvas / icons retain blank manual inputs; mode / visual_style instead show a mandatory AI-authored proposal in full, initially unselected and editable after selection. Selected mode / style writes literal `custom` plus its behavior sibling.
|
||||||
- **Visual examples for hard-to-name choices** — the full-screen confirmation page loads real SVG page samples from `static/style_previews/` for `visual_style`, and renders real sample SVGs from `templates/icons` for `icons`. These thumbnails make style and icon-library choices visually comparable before the user locks them. Preview copy is fixed role text (big title / section title / body / points), not project content from recommendation files, so users compare visual treatment rather than copywriting. These previews are a confirmation aid only: they do not add fields to recommendation stage files or `result.json`, and they do not replace the later Step 6 live preview.
|
- **Visual examples for hard-to-name choices** — the full-screen confirmation page loads real SVG page samples from `static/style_previews/` for `visual_style`, and renders real sample SVGs from `templates/icons` for `icons`. These thumbnails make style and icon-library choices visually comparable before the user locks them. Preview copy is fixed role text (big title / section title / body / points), not project content from recommendation files, so users compare visual treatment rather than copywriting. These previews are a confirmation aid only: they do not add fields to recommendation stage files or `result.json`, and they do not replace the later Step 6 live preview.
|
||||||
- **Image usage multi-select** — image sources are selected as one or more catalog ids: `ai` = AI-generated, `web` = Web-sourced, `provided` = User-provided, `placeholder` = Placeholder, `none` = No images. `none` is exclusive. A confirmed non-`none` set is the allowed acquisition-source boundary, not a requirement to use every selected source; only explicit `image_notes` wording can require a source, asset, or page role. Recommendation and result values may be a legacy single string, but new files should use an array. When several sources are recommended, write the source ids to `recommend.image_usage` and write the actual usage strategy to `image_notes`, not a custom prose value.
|
- **Image usage multi-select** — image sources are selected as one or more catalog ids: `ai` = AI-generated, `web` = Web-sourced, `provided` = User-provided, `placeholder` = Placeholder, `none` = No images. `none` is exclusive. A confirmed non-`none` set is the allowed acquisition-source boundary, not a requirement to use every selected source; only explicit `image_notes` wording can require a source, asset, or page role. Recommendation and result values may be a legacy single string, but new files should use an array. When several sources are recommended, write the source ids to `recommend.image_usage` and write the actual usage strategy to `image_notes`, not a custom prose value.
|
||||||
- **Closed enumerable** — PPT reading mode (`delivery_purpose` compatibility key), formula policy / generation mode / refine spec, plus AI source only when image usage includes `ai`. These have no Custom box; out-of-catalog values snap back to the recommended option.
|
- **Closed enumerable** — PPT reading mode (`delivery_purpose` compatibility key), formula policy / generation mode / refine spec, plus AI source only when image usage includes `ai`. These have no Custom box; out-of-catalog values snap back to the recommended option.
|
||||||
- **Proactive execution booleans** — Stage 3 carries top-level `proactive_speaker_notes`, `proactive_custom_animations`, and `proactive_narration_audio` values. Defaults are `true`, `false`, and `false`, respectively. They control what the Agent does proactively only when the user has not explicitly instructed otherwise; the latest explicit user instruction always wins. These three values are raw confirmation evidence: the UI and server neither couple nor rewrite them, and every boolean combination is valid. When narration audio is enabled, Strategist later resolves the effective Speaker Notes outcome to enabled and records `Narration Audio dependency` as its Design Spec provenance. Disabling proactive custom animation does not suppress the Strategist's advisory motion recommendations.
|
- **Proactive execution booleans** — Final Stage 2 carries top-level `proactive_speaker_notes`, `proactive_custom_animations`, and `proactive_narration_audio` values. Defaults are `true`, `false`, and `false`, respectively. They control what the Agent does proactively only when the user has not explicitly instructed otherwise; the latest explicit user instruction always wins. These three values are raw confirmation evidence: the UI and server neither couple nor rewrite them, and every boolean combination is valid. When narration audio is enabled, Strategist later resolves the effective Speaker Notes outcome to enabled and records `Narration Audio dependency` as its Design Spec provenance. Disabling proactive custom animation does not suppress the Strategist's advisory motion recommendations.
|
||||||
- **Open prose** — `audience`, `communication_intent`, `audience_outcome`, `core_message`, `delivery_context`, `artifact_afterlife`, `content_divergence`, and `page_count`. `communication_intent` may carry several purposes plus priority / sequence; common paths appear only as help text. `delivery_context` states one primary presenter-led / reader-led / hybrid / recorded-self-running context plus optional secondary use; a hybrid recommendation names which context leads. `content_divergence` is the source-treatment axis. `page_count` may be a range here; Strategist resolves the exact §IX roster, leaving Executor no pagination latitude.
|
- **Open prose** — `audience`, `communication_intent`, `audience_outcome`, `core_message`, `delivery_context`, `artifact_afterlife`, `content_divergence`, and `page_count`. `communication_intent` may carry several purposes plus priority / sequence; common paths appear only as help text. `delivery_context` states one primary presenter-led / reader-led / hybrid / recorded-self-running context plus optional secondary use; a hybrid recommendation names which context leads. `content_divergence` is the source-treatment axis. `page_count` may be a range here; Strategist resolves the exact §IX roster, leaving Executor no pagination latitude.
|
||||||
- **Coordinated generative directions** — `design_directions` carries ≥3 safe / shifted / bold candidates. Each candidate bundles visual style, color, typography, icon id, and conditional generated-image rendering. The page can still render legacy top-level `color`, `typography`, and `image_strategy` candidates, but new staged recommendations use the coordinated bundle.
|
- **Coordinated generative directions** — `design_directions` carries ≥3 safe / shifted / bold candidates. Each candidate bundles visual style, color, typography, icon id, and conditional generated-image rendering. The page can still render legacy top-level `color`, `typography`, and `image_strategy` candidates, but new staged recommendations use the coordinated bundle.
|
||||||
|
|
||||||
@@ -125,24 +327,77 @@ The front-end loads `/api/catalogs` (served by the confirm server) and falls bac
|
|||||||
|
|
||||||
## Round-trip data contract
|
## Round-trip data contract
|
||||||
|
|
||||||
Round-trip and session files live under `<project_path>/confirm_ui/`.
|
In the UI branch, round-trip and session files live under
|
||||||
|
`<project_path>/confirm_ui/`. `template_options.json` is prepared beside the
|
||||||
|
Stage-1 recommendation. The Stage-1 submit writes `result.json` and
|
||||||
|
`template_selection.json` together; after installation/free-design closure,
|
||||||
|
`template_handoff.json` unlocks Stage 2. The chat/delegated branch preserves the
|
||||||
|
same logical order without fabricating these UI receipts.
|
||||||
|
|
||||||
### Three-stage flow
|
### Current two-stage flow
|
||||||
|
|
||||||
The page runs as a **three-stage wizard in one browser session**. Each stage has its own Strategist-authored file and top-level `"stage"` selector. The active, unconfirmed stage may be overwritten any number of times when the user asks for a better recommendation; refresh the page to load the replacement. Once the user confirms it, normal progression writes the next stage file rather than repurposing the previous one. The server derives the active filename from `result.json`.
|
The page runs a **two-stage Strategist wizard in one browser session**. Stage 1
|
||||||
|
contains the communication contract and template/free-design controls. Each stage
|
||||||
|
has its own Strategist-authored file and top-level `"stage"` selector. The active,
|
||||||
|
unconfirmed stage may be overwritten any number of times when the user asks for
|
||||||
|
a better recommendation; refresh the page to load the replacement. Once the
|
||||||
|
user confirms it, normal progression writes the next stage file rather than
|
||||||
|
repurposing the previous one. The server derives the active Strategist filename
|
||||||
|
from `result.json`; the bound template handoff is the prerequisite for Stage 2.
|
||||||
|
|
||||||
|
Confirm UI is a one-run surface, not a migration layer. It accepts only the
|
||||||
|
current Stage-1/Stage-2 files. If a project starts UI confirmation again, run
|
||||||
|
`--reset-template-selection`, write fresh `template_options.json`, then author a
|
||||||
|
fresh `recommendations.stage1.json`. Those two inputs start the new UI
|
||||||
|
lifecycle; neither a standalone newer Stage-1 file nor a standalone option file
|
||||||
|
does so.
|
||||||
|
The completed result cannot be reopened or overwritten, and a standalone newer
|
||||||
|
Stage-1 file cannot start its replacement. After Stage 1 writes both receipts,
|
||||||
|
the agent completes a newer bound handoff; `recommendations.stage2.json` must be
|
||||||
|
newer than that handoff and the Stage-1 result. A Stage-2 file left by an earlier
|
||||||
|
run remains inactive. An existing `result.json` outside the current `stage1` /
|
||||||
|
`final` contract also fails closed unless fresh paired inputs start a new run.
|
||||||
|
|
||||||
| Recommendation file | Declared stage | Page renders | Button | On submit |
|
| Recommendation file | Declared stage | Page renders | Button | On submit |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| `recommendations.stage1.json` | `"stage1"` | communication contract — content language; audience; open `communication_intent`; audience outcome; core message / primary delivery context + optional secondary use / artifact afterlife / `content_divergence` (all prose fields may be blank); canvas | **Confirm contract & continue** | writes `result.json` `{ stage: "stage1", status: "stage1-confirmed", <communication contract> }`; the page stays open and polls |
|
| `recommendations.stage1.json` + `template_options.json` | `"stage1"` | communication contract — content language; audience; open `communication_intent`; audience outcome; core message / primary delivery context + optional secondary use / artifact afterlife / `content_divergence` (all prose fields may be blank); canvas; free-design/template mode and conditional candidate selectors | **Confirm contract & template choice** | writes Stage-1 `result.json` plus `template_selection.json` in one submission; the page stays open and polls while the agent installs/completes the handoff |
|
||||||
| `recommendations.stage2.json` | `"stage2"` | complete deck solution — conditional natural-language template application, reading mode, mode, page count, visual direction, color, icons, typography, image usage, generated-image rendering | **Confirm solution & continue** | writes `result.json` `{ stage: "stage2", status: "stage2-confirmed", <contract + solution> }`; the page stays open and polls |
|
| `recommendations.stage2.json` | `"stage2"` | complete deck solution and production — conditional natural-language template application, reading mode, mode, page count, visual direction, color, icons, typography, image usage/rendering, conditional AI acquisition path, formula policy, proactive notes/custom-animation/narration-audio toggles, generation mode, and Design Spec review toggle | **Confirm final plan** | writes `result.json` `{ stage: "final", status: "confirmed", <all fields> }`, then shuts the page down |
|
||||||
| `recommendations.stage3.json` | `"stage3"` | production only — confirmed image-source summary, conditional AI acquisition path, formula policy, proactive notes / custom-animation / narration-audio toggles, generation mode, Design Spec review toggle | **Confirm** | writes `result.json` `{ stage: "final", status: "confirmed", <all fields> }`, then shuts the page down |
|
|
||||||
| `recommendations.json` | stage or legacy no-stage payload | read-only compatibility when no stage-specific file exists | matching legacy behavior | preserves the former staged or single-pass behavior; new projects never create this file |
|
|
||||||
|
|
||||||
The AI launches Stage 1, authors the complete Stage-2 solution once from the user's actual contract, then authors Stage-3 production mechanics once from the confirmed solution. An edit inside the current stage never requests another recommendation. The page preserves earlier answers across transitions. `GET /api/session` is the waiting-state endpoint; `GET /api/recommendations` is `no-store`, and the server folds confirmed earlier-stage choices back into later payloads so refresh / reopen restores the user's actual values—including Stage-2 color, typography, icon, image-source, and rendering choices. Once any stage-specific file exists, the server ignores legacy `recommendations.json` to prevent mixed lifecycles.
|
In the UI branch, the AI authors Stage 1 without reading template candidates,
|
||||||
|
then launches the combined page. In chat/delegated confirmation it authors the
|
||||||
|
same communication recommendation before listing template candidates. After
|
||||||
|
the one Stage-1 confirmation, the AI installs any selection, completes the
|
||||||
|
handoff/equivalent state, then inspects only the project-local template and
|
||||||
|
authors the complete final Stage-2 solution plus production mechanics once from
|
||||||
|
the user's actual communication contract. An edit inside
|
||||||
|
the current stage never requests another recommendation. The page preserves
|
||||||
|
earlier answers across transitions. `GET /api/session` reports `phase:
|
||||||
|
"strategist"` with current Stage 1 from launch; after Stage-1 submission it
|
||||||
|
reports `waiting_agent` until the bound handoff and fresh Stage-2 file exist.
|
||||||
|
`GET /api/recommendations`
|
||||||
|
is `no-store`, and the server folds confirmed earlier-stage choices back into
|
||||||
|
the final Stage-2 payload so an in-run refresh preserves the confirmed Stage-1
|
||||||
|
communication contract. Unsubmitted Stage-2 edits are browser-local and a
|
||||||
|
completed final result is never reopened.
|
||||||
|
|
||||||
**Stage progression guard.** Stages confirm strictly in order. `/api/confirm` accepts only the submit stage matching the active filename and its required predecessor; the declared `stage` must also match the filename. A later file that skips ahead (for example `recommendations.stage3.json` while only Stage 1 is confirmed and Stage 2 is absent) is never rendered: `/api/session` keeps reporting `waiting_agent` with `stage_skip: true`, and `--wait` / `--wait-only` exit `2` if a result skips the stage being awaited. An active template does not exempt Stage 2: its recommendations must include `template_application.value`, and an installed workspace or that field disables the no-stage legacy single-pass path. Legacy single-pass remains available only for non-template compatibility payloads.
|
**Progression guard.** Stage 1 requires current `template_options.json` and a
|
||||||
|
fresh `recommendations.stage1.json`; it does not require a prior selection or
|
||||||
|
handoff. Its one submit must persist a valid Stage-1 result and selection bound
|
||||||
|
to those options. Stage 2 requires a newer `template_handoff.json` bound to that
|
||||||
|
selection and a fresh Stage-2 recommendation. This ordering prevents receipts
|
||||||
|
from an earlier one-run UI from satisfying a new run. Strategist confirms Stage
|
||||||
|
1 → installation/free-design handoff → final Stage 2.
|
||||||
|
`/api/confirm` accepts only the submit stage matching the
|
||||||
|
active filename and its required predecessor; the declared `stage` must also
|
||||||
|
match the filename. A confirmed templates-mode workspace does not exempt final
|
||||||
|
Stage 2: its recommendations must include `template_application.value`.
|
||||||
|
|
||||||
### Input — `recommendations.stage1.json` (created by Strategist before launch)
|
### Input — `recommendations.stage1.json` (created beside template options)
|
||||||
|
|
||||||
|
Author this file before reading candidate index summaries in chat and without
|
||||||
|
reading any template spec, prototype, asset, fused segment owner, or template
|
||||||
|
canvas. `template_options.json` supplies display state only and never changes
|
||||||
|
the communication recommendation.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -178,6 +433,13 @@ The common paths — inform / explain / persuade / decide / align / teach / repo
|
|||||||
|
|
||||||
After Stage 1 is confirmed, create `recommendations.stage2.json` with the complete solution; leave Stage 1 unchanged (the server folds confirmed communication fields back in when serving the page):
|
After Stage 1 is confirmed, create `recommendations.stage2.json` with the complete solution; leave Stage 1 unchanged (the server folds confirmed communication fields back in when serving the page):
|
||||||
|
|
||||||
|
**Stage-2 production contract**: the server rejects the recommendation file
|
||||||
|
unless `recommend.formula_policy`, `recommend.generation_mode`, and boolean
|
||||||
|
`refine_spec.value` are present; `recommend.image_ai_path` is additionally
|
||||||
|
required when `image_usage` includes `ai`. Final submission must retain the
|
||||||
|
corresponding direct values (`formula_policy`, `generation_mode`, boolean
|
||||||
|
`refine_spec`, and conditional `image_ai_path`) or confirmation is rejected.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"stage": "stage2",
|
"stage": "stage2",
|
||||||
@@ -186,13 +448,17 @@ After Stage 1 is confirmed, create `recommendations.stage2.json` with the comple
|
|||||||
"delivery_purpose": "balanced",
|
"delivery_purpose": "balanced",
|
||||||
"mode": "pyramid",
|
"mode": "pyramid",
|
||||||
"visual_style": "swiss-minimal",
|
"visual_style": "swiss-minimal",
|
||||||
"image_usage": ["ai", "provided"]
|
"image_usage": ["ai", "provided"],
|
||||||
|
"image_ai_path": "auto",
|
||||||
|
"formula_policy": "mixed",
|
||||||
|
"generation_mode": "continuous"
|
||||||
},
|
},
|
||||||
"page_count": { "value": "12-15" },
|
"page_count": { "value": "12-15" },
|
||||||
"template_application": {
|
|
||||||
"value": "选用封面、章节页和数据页原型;跳过示例内容页。品牌标识和页脚保留,正文可按当前材料重组。"
|
|
||||||
},
|
|
||||||
"image_notes": { "value": "封面和章节页用 AI 主视觉;产品页优先用户素材。" },
|
"image_notes": { "value": "封面和章节页用 AI 主视觉;产品页优先用户素材。" },
|
||||||
|
"proactive_speaker_notes": { "value": true },
|
||||||
|
"proactive_custom_animations": { "value": false },
|
||||||
|
"proactive_narration_audio": { "value": false },
|
||||||
|
"refine_spec": { "value": false },
|
||||||
"custom_candidates": {
|
"custom_candidates": {
|
||||||
"mode": {
|
"mode": {
|
||||||
"name_zh": "冲突到决策",
|
"name_zh": "冲突到决策",
|
||||||
@@ -241,35 +507,28 @@ After Stage 1 is confirmed, create `recommendations.stage2.json` with the comple
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The example abbreviates the required ≥3 directions. Custom mode/style candidates remain mandatory; only a recommendation containing AI requires the custom image candidate. Stage 2 rejects fewer than three bundles, incomplete six-role palettes, and incomplete heading/body stacks. Legacy grids remain readable only with three complete palettes and complete typography.
|
The example abbreviates the required ≥3 directions. Custom mode/style candidates remain mandatory; only a recommendation containing AI requires the custom image candidate. Final Stage 2 rejects fewer than three bundles, incomplete six-role palettes, or incomplete heading/body stacks. Legacy grids remain readable only with three complete palettes and complete typography.
|
||||||
|
|
||||||
After Stage 2 is confirmed, create `recommendations.stage3.json` with production recommendations only; leave both earlier files unchanged:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"stage": "stage3",
|
|
||||||
"lang": "zh",
|
|
||||||
"recommend": {
|
|
||||||
"image_ai_path": "auto",
|
|
||||||
"formula_policy": "mixed",
|
|
||||||
"generation_mode": "continuous"
|
|
||||||
},
|
|
||||||
"proactive_speaker_notes": { "value": true },
|
|
||||||
"proactive_custom_animations": { "value": false },
|
|
||||||
"proactive_narration_audio": { "value": false },
|
|
||||||
"refine_spec": { "value": false }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `recommend.*` names each recommended id. New mode / style values use a catalog id or literal `custom`; arbitrary prose values are legacy-only. Use `recommend.image_strategy: "custom"` only when an explicit user-supplied image direction should start selected. Missing recommendations fall back to the normal preset. Legacy aliases remain accepted; new files write canonical ids.
|
- `recommend.*` names each recommended id. New mode / style values use a catalog id or literal `custom`; arbitrary prose values are legacy-only. Use `recommend.image_strategy: "custom"` only when an explicit user-supplied image direction should start selected. Missing recommendations fall back to the normal preset. Legacy aliases remain accepted; new files write canonical ids.
|
||||||
- The three proactive-execution fields are top-level boolean `{ "value": ... }` objects, not catalog ids. Omitted fields use `true / false / false` for notes / custom animation / narration audio. These are absence-of-instruction defaults, not permission to override the user's latest explicit request. Preserve all three raw values independently through `result.json`; do not couple or rewrite them. Strategist derives effective Speaker Notes as enabled when audio is `true` and records `Narration Audio dependency` as provenance in the Design Spec. `proactive_custom_animations: false` leaves Strategist animation suggestions unchanged; it only prevents unrequested custom-animation execution.
|
- The three proactive-execution fields are top-level boolean `{ "value": ... }` objects, not catalog ids. Omitted fields use `true / false / false` for notes / custom animation / narration audio. These are absence-of-instruction defaults, not permission to override the user's latest explicit request. Preserve all three raw values independently through `result.json`; do not couple or rewrite them. Strategist derives effective Speaker Notes as enabled when audio is `true` and records `Narration Audio dependency` as provenance in the Design Spec. `proactive_custom_animations: false` leaves Strategist animation suggestions unchanged; it only prevents unrequested custom-animation execution.
|
||||||
- `custom_candidates` is recommendation-only. Mode / style carry localized `name` + `behavior`; conditional image strategy also carries `rendering: "custom"`, `visual`, and `mood`. When a proposal combines or borrows existing catalog entries, the visible behavior names every exact id and Strategist reads every corresponding file before authoring it; a genuinely novel proposal names none. The server rejects missing required candidates; the UI shows full copy, edits it only after selection, rejects a selected blank, and omits unselected candidates from `result.json`. Template-backed proposals obey inherited identity, prototype capacity, and `template_application`.
|
- `custom_candidates` is recommendation-only. Mode / style carry localized `name` + `behavior`; conditional image strategy also carries `rendering: "custom"`, `visual`, and `mood`. When a proposal combines or borrows existing catalog entries, the visible behavior names every exact id and Strategist reads every corresponding file before authoring it; a genuinely novel proposal names none. The server rejects missing required candidates; the UI shows full copy, edits it only after selection, rejects a selected blank, and omits unselected candidates from `result.json`. Template-backed proposals obey inherited identity, prototype capacity, and `template_application`.
|
||||||
- Seed `audience`, `communication_intent`, `audience_outcome`, and `delivery_context` when evidence supports them; users need not supply them, and every Stage-1 prose field may end blank. The contract and `primary_language` stay in `result.json` and `design_spec.md`; `spec_lock.md communication` receives `primary_language`, compact `audience` / `objective` / `core_message`, and reading mode. `communication_intent` may preserve multiple purposes and priority/sequence; never add a `primary_job` enum.
|
- Seed `audience`, `communication_intent`, `audience_outcome`, and `delivery_context` when evidence supports them; users need not supply them, and every Stage-1 prose field may end blank. The contract and `primary_language` stay in `result.json` and `design_spec.md`; `spec_lock.md communication` receives `primary_language`, compact `audience` / `objective` / `core_message`, and reading mode. `communication_intent` may preserve multiple purposes and priority/sequence; never add a `primary_job` enum.
|
||||||
- Do not write `recommend.template_reuse_scope` or `recommend.template_adherence`. Strategist records those internal exporter values later in `spec_lock.md` after inspecting the actual template and current content.
|
- Do not write `recommend.template_reuse_scope` or `recommend.template_adherence`. Strategist records those internal exporter values later in `spec_lock.md` after inspecting the actual template and current content.
|
||||||
- For an active template workspace, write one editable prose field as top-level `template_application.value`. It summarizes actual page/prototype use and preservation/reorganization decisions. Omit it for free design. The UI returns the current string through Stage 2, Stage 3, and final confirmation; Strategist then persists the final effective plan as `- **Template Application**: ...` in `design_spec.md §I`, which Executor reads from the retained Design Spec. Never replace it with internal reuse/adherence ids or a fixed option menu.
|
- For a confirmed templates-mode handoff, write one editable prose field as top-level `template_application.value`. It summarizes **how to use** the already selected project-local template: actual page/prototype use and preservation/reorganization decisions. It never chooses, changes, or reinstalls a workspace. Omit it for free design. The UI returns the current string through final Stage 2; Strategist then persists the final effective plan as `- **Template Application**: ...` in `design_spec.md §I`, which Executor reads from the retained Design Spec. Never replace it with internal reuse/adherence ids or a fixed option menu.
|
||||||
|
|
||||||
|
Template-mode-only Stage-2 fragment:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"template_application": {
|
||||||
|
"value": "选用封面、章节页和数据页原型;跳过示例内容页。品牌标识和页脚保留,正文可按当前材料重组。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
- `recommend.image_usage` should be an array of source ids when more than one source applies, e.g. `["ai", "provided"]`. A single string is still accepted for backward compatibility. Do not write bare `"custom"` and do not encode a mixed-source plan as prose here; write the prose to top-level `image_notes.value`.
|
- `recommend.image_usage` should be an array of source ids when more than one source applies, e.g. `["ai", "provided"]`. A single string is still accepted for backward compatibility. Do not write bare `"custom"` and do not encode a mixed-source plan as prose here; write the prose to top-level `image_notes.value`.
|
||||||
- `image_notes` is the initial strategy note shown under the image source chips. Use it for page-role guidance and constraints: which source applies where, what to avoid, which user assets are authoritative, how realistic / abstract the imagery should be, and what can remain as placeholders. It is intent guidance, not a separate finite option.
|
- `image_notes` is the initial strategy note shown under the image source chips. Use it for page-role guidance and constraints: which source applies where, what to avoid, which user assets are authoritative, how realistic / abstract the imagery should be, and what can remain as placeholders. It is intent guidance, not a separate finite option.
|
||||||
- When confirmed Stage-2 `image_usage` includes `ai`, Stage 3 sets `recommend.image_ai_path` to one of `auto` / `api` / `host-native` / `manual`. Stage 2 never asks for the acquisition mechanism while the user is still deciding the image role.
|
- Final Stage 2 shows and submits `recommend.image_ai_path` as one of `auto` / `api` / `host-native` / `manual` only while its current `image_usage` includes `ai`; changing sources refreshes that production control on the same page.
|
||||||
- **Color candidates carry the user-facing core `palette`**: `background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, and `body_text`. The page renders every role as a labelled swatch with its HEX value visible, and offers per-role override inputs for precise single-role edits, plus a **Custom color card with a free-text box** — the user can describe the palette in words or paste HEX values instead of filling each role; this writes `color: { "name": "custom", "custom": "<text>" }` to `result.json` for the AI to interpret. Legacy `text` is accepted as an alias for `body_text`, but new files should write `body_text`. Strategist derives secondary text, borders, state colors, and visual-style neutral tiers while writing `design_spec.md`, then projects the machine values to `spec_lock.md`; those are not user-facing confirmation choices.
|
- **Color candidates carry the user-facing core `palette`**: `background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, and `body_text`. The page renders every role as a labelled swatch with its HEX value visible, and offers per-role override inputs for precise single-role edits, plus a **Custom color card with a free-text box** — the user can describe the palette in words or paste HEX values instead of filling each role; this writes `color: { "name": "custom", "custom": "<text>" }` to `result.json` for the AI to interpret. Legacy `text` is accepted as an alias for `body_text`, but new files should write `body_text`. Strategist derives secondary text, borders, state colors, and visual-style neutral tiers while writing `design_spec.md`, then projects the machine values to `spec_lock.md`; those are not user-facing confirmation choices.
|
||||||
- **Candidate display text may be multilingual**: color / typography candidates can provide `name_zh` / `name_en` / `name_ja` and `note_zh` / `note_en` / `note_ja`; the page falls back to legacy `name` / `note`. Labels resolve in the page language first, then fall back across the others (a `ja` page: ja → en → zh; zh/en pages keep their zh↔en fallback and try `_ja` last), so when `lang` is `ja` always include the `_ja` variants — otherwise the candidate labels render in English.
|
- **Candidate display text may be multilingual**: color / typography candidates can provide `name_zh` / `name_en` / `name_ja` and `note_zh` / `note_en` / `note_ja`; the page falls back to legacy `name` / `note`. Labels resolve in the page language first, then fall back across the others (a `ja` page: ja → en → zh; zh/en pages keep their zh↔en fallback and try `_ja` last), so when `lang` is `ja` always include the `_ja` variants — otherwise the candidate labels render in English.
|
||||||
- **Typography candidates** use concrete heading/body `primary`; non-English decks also use `english`, while English-primary decks omit it. `cjk` / `latin` remain legacy aliases. Localized `name` labels the pair and `css` only previews. Bundles differ overall; font pairs may repeat without blocking. Fixed pairs require `fixed: true`. Catalog `fonts` supplies language-filtered dropdowns plus Other without limiting recommendations; edits mark Custom and refresh the preview. Include topic samples. PPT baselines are `text` 20 · `balanced` 24 · `presentation` 32 px; cards preserve sizes and submit px.
|
- **Typography candidates** use concrete heading/body `primary`; non-English decks also use `english`, while English-primary decks omit it. `cjk` / `latin` remain legacy aliases. Localized `name` labels the pair and `css` only previews. Bundles differ overall; font pairs may repeat without blocking. Fixed pairs require `fixed: true`. Catalog `fonts` supplies language-filtered dropdowns plus Other without limiting recommendations; edits mark Custom and refresh the preview. Include topic samples. PPT baselines are `text` 20 · `balanced` 24 · `presentation` 32 px; cards preserve sizes and submit px.
|
||||||
@@ -296,7 +555,6 @@ After Stage 2 is confirmed, create `recommendations.stage3.json` with production
|
|||||||
"delivery_context": "Primary: presenter-led 20-minute leadership review; secondary: reader-led approval copy shared afterward",
|
"delivery_context": "Primary: presenter-led 20-minute leadership review; secondary: reader-led approval copy shared afterward",
|
||||||
"artifact_afterlife": "Approval record, hand-off reference, and audit trail",
|
"artifact_afterlife": "Approval record, hand-off reference, and audit trail",
|
||||||
"content_divergence": "freely restructure and expand within the source",
|
"content_divergence": "freely restructure and expand within the source",
|
||||||
"template_application": "选用封面、章节页和数据页原型;跳过示例内容页。品牌标识和页脚保留,正文可按当前材料重组。",
|
|
||||||
"mode": "pyramid",
|
"mode": "pyramid",
|
||||||
"visual_style": "swiss-minimal",
|
"visual_style": "swiss-minimal",
|
||||||
"color": { "name": "...", "palette": { "background": "#...", "secondary_bg": "#...", "primary": "#...", "accent": "#...", "secondary_accent": "#...", "body_text": "#..." } },
|
"color": { "name": "...", "palette": { "background": "#...", "secondary_bg": "#...", "primary": "#...", "accent": "#...", "secondary_accent": "#...", "body_text": "#..." } },
|
||||||
@@ -319,13 +577,26 @@ After Stage 2 is confirmed, create `recommendations.stage3.json` with production
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The shape above is final. The proactive-execution values are independent flat booleans in `result.json`; old recommendations and results that omit them resolve to `true / false / false`. They remain raw evidence even when `proactive_speaker_notes` is `false` and `proactive_narration_audio` is `true`; Strategist owns the effective dependency resolution described above. Selected custom values use `mode: custom` + `mode_behavior`, `visual_style: custom` + `visual_style_behavior`, or `image_strategy.rendering: custom` + `behavior`. During Design Spec and lock authoring, Strategist projects optional `mode_references`, `visual_style_references`, or `image_rendering_references` only when that confirmed behavior actually uses named catalog sources; genuinely novel custom behavior has no reference list. Intermediate writes retain accumulated fields; legacy tier names remain read-compatible.
|
The shape above is final for Strategist confirmation. It intentionally contains
|
||||||
|
no template-selection field: `template_selection.json`, its agent handoff, and
|
||||||
|
the installed project-local state own the parallel Stage-1 decision. The proactive-execution values
|
||||||
|
are independent flat booleans in `result.json`; old recommendations and results
|
||||||
|
that omit them resolve to `true / false / false`. They remain raw evidence even
|
||||||
|
when `proactive_speaker_notes` is `false` and `proactive_narration_audio` is
|
||||||
|
`true`; Strategist owns the effective dependency resolution described above.
|
||||||
|
Selected custom values use `mode: custom` + `mode_behavior`, `visual_style:
|
||||||
|
custom` + `visual_style_behavior`, or `image_strategy.rendering: custom` +
|
||||||
|
`behavior`. During Design Spec and lock authoring, Strategist projects optional
|
||||||
|
`mode_references`, `visual_style_references`, or
|
||||||
|
`image_rendering_references` only when that confirmed behavior actually uses
|
||||||
|
named catalog sources; genuinely novel custom behavior has no reference list.
|
||||||
|
The Stage-1 intermediate write retains the communication contract for Stage 2.
|
||||||
|
|
||||||
**Final-result consumption contract.** A final result is the user-confirmed input contract for the Strategist's Design Spec, not another recommendation input. After the final wait, Generate Step 4 reads the complete final object exactly once and retains it while Strategist writes and audits `design_spec.md` against every explicitly present field. Normal lock authoring and downstream execution do not reopen `result.json`; the completed Design Spec is the durable authority. Only after that audit passes does Strategist author `spec_lock.md` from the Design Spec plus current execution context, selecting stable anchors and routing rather than copying every field or enumerating every legal color/font. Every value must be consumed at the semantic type owned by [`strategist.md`](../../references/strategist.md) §1 and its field owner: do not omit or substitute it, and do not silently strengthen or weaken its type. If a confirmed requirement cannot be honored, the owning workflow reports or pauses under failure recovery; it never deletes the requirement to keep the pipeline moving.
|
**Final-result consumption contract.** A final result is the user-confirmed input contract for the Strategist's Design Spec, not another recommendation input. After the final wait, Generate Step 4 reads the complete final object exactly once and retains it while Strategist writes and audits `design_spec.md` against every explicitly present field. Normal lock authoring and downstream execution do not reopen `result.json`; the completed Design Spec is the durable authority. Only after that audit passes does Strategist author `spec_lock.md` from the Design Spec plus current execution context, selecting stable anchors and routing rather than copying every field or enumerating every legal color/font. Every value must be consumed at the semantic type owned by [`strategist.md`](../../references/strategist.md) §1 and its field owner: do not omit or substitute it, and do not silently strengthen or weaken its type. If a confirmed requirement cannot be honored, the owning workflow reports or pauses under failure recovery; it never deletes the requirement to keep the pipeline moving.
|
||||||
|
|
||||||
- Bespoke mode / style prose lives only in the required behavior sibling; image custom prose lives in `image_strategy.behavior`. Canvas / icons retain free-text edge cases, color / typography retain `name: "custom"`, and image usage remains a source-id array plus `image_notes`.
|
- Bespoke mode / style prose lives only in the required behavior sibling; image custom prose lives in `image_strategy.behavior`. Canvas / icons retain free-text edge cases, color / typography retain `name: "custom"`, and image usage remains a source-id array plus `image_notes`.
|
||||||
- `image_ai_path` and `image_strategy` appear only with `image_usage: ai` and remain confirmed downstream. The page is default; explicit/failure chat fallback keeps identical fields. `image_ai_path` selects the Step 5 path, and [`strategist-image.md`](../../references/strategist-image.md) §2 retains the selected rendering or custom behavior as the deck-level image identity anchor; individual prompts still adapt subject, composition, and atmosphere within it.
|
- `image_ai_path` and `image_strategy` appear only with `image_usage: ai` and remain confirmed downstream. The page is default; explicit/failure chat fallback keeps identical fields. `image_ai_path` selects the Step 5 path, and [`strategist-image.md`](../../references/strategist-image.md) §2 retains the selected rendering or custom behavior as the deck-level image identity anchor; individual prompts still adapt subject, composition, and atmosphere within it.
|
||||||
- After the user clicks the **final Confirm** (Stage 3, or single-pass), the page saves `result.json` and shuts the server down (auto-close). Stage-1 **Confirm contract & continue** and Stage-2 **Confirm solution & continue** keep the page open while it polls for the downstream stage file. The default flow is `--daemon` → Stage-1 chat handoff → `--wait-only --wait-stage stage1` → Stage-2 wait → final wait; the AI reads each result immediately. Chat fallback mirrors the same preset/custom choices. Either way, Step 4 ends with `--shutdown` so a never-confirmed page cannot retain its selected port ahead of Step 6 live preview.
|
- Stage-1 **Confirm contract & template choice** writes the Stage-1 `result.json` and `template_selection.json` together, then keeps the page open while the agent installs the selection. The agent runs `--complete-template-selection`, writes fresh Stage 2 only after that handoff, and the page keeps polling. Stage-2 **Confirm final plan** saves the final `result.json` and shuts the server down (auto-close). The AI reads each receipt at its owning boundary; chat fallback mirrors the same decisions without UI artifacts. Either way, Step 4 ends with `--shutdown` so a never-confirmed page cannot retain its selected port ahead of Step 6 live preview.
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ python3 scripts/project_manager.py page-context-report <project_path>
|
|||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- `init --quick-generate`: only `svg_output/`; no README
|
- `init --quick-generate`: `svg_output/` plus
|
||||||
|
`validation/workflow.log`; no README
|
||||||
- Files outside `projects/` are always copied into `sources/`
|
- Files outside `projects/` are always copied into `sources/`
|
||||||
- `--move` applies only to sources under the repository's `projects/` tree
|
- `--move` applies only to sources under the repository's `projects/` tree
|
||||||
- A directly supplied supported bitmap is also copied into `images/` with a
|
- A directly supplied supported bitmap is also copied into `images/` with a
|
||||||
@@ -146,6 +147,54 @@ python3 scripts/project_manager.py page-context projects/my_presentation_ppt169_
|
|||||||
python3 scripts/project_manager.py page-context-report projects/my_presentation_ppt169_20251116
|
python3 scripts/project_manager.py page-context-report projects/my_presentation_ppt169_20251116
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `workflow_transcript.py` and `workflow_log.py`
|
||||||
|
|
||||||
|
Project initialization creates `validation/workflow.log` and records its own
|
||||||
|
milestone. Run later project-scoped Python tools normally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/<tool>.py <project_path> <args...>
|
||||||
|
```
|
||||||
|
|
||||||
|
Their shared CLI bootstrap discovers the existing project log from the working
|
||||||
|
directory or command arguments. `workflow_transcript.py` records a UTC command
|
||||||
|
envelope plus explicit error/failure and receipt/report lines, bounded
|
||||||
|
warning/OK/stderr samples, limited summary context, and per-run omission counts;
|
||||||
|
no outer launcher or second Python process is used. It leaves full output on
|
||||||
|
the original console instead of copying it into the audit log. Commands before
|
||||||
|
project initialization are not backfilled. Binary-buffer writes, hidden child
|
||||||
|
output, and detached service activity are not recorded; Confirm UI and live
|
||||||
|
preview retain detailed output in their component `server.log` files. Their
|
||||||
|
shared detached-process launcher disables automatic workflow recording in the
|
||||||
|
long-running child while preserving the short foreground launcher's own record.
|
||||||
|
|
||||||
|
For a Python helper whose arguments and working directory do not identify the
|
||||||
|
active project, set the routing signal on the same command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PPT_MASTER_PROJECT_PATH="<project_path>" python3 scripts/<helper>.py <args...>
|
||||||
|
```
|
||||||
|
|
||||||
|
This variable selects only the destination transcript; it does not authorize
|
||||||
|
the helper to read project artifacts or change its ownership.
|
||||||
|
|
||||||
|
Append a manual note only when an important audit detail has no owning command
|
||||||
|
output:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/workflow_log.py <project_path> "<material audit detail>"
|
||||||
|
```
|
||||||
|
|
||||||
|
Suitable notes include a material stage handoff or rework reason, a
|
||||||
|
user-approved exception, or a manual recovery choice. Do not duplicate
|
||||||
|
artifact contents, routine page progress, or private reasoning.
|
||||||
|
|
||||||
|
The log is append-only audit evidence. It is not a complete console transcript,
|
||||||
|
stage, quality, or artifact authority and is not read during normal generation
|
||||||
|
or resume. Inspect it only when the user explicitly requests a run review. An
|
||||||
|
automatic recording failure emits a warning but does not change the Python
|
||||||
|
tool's result; an explicit manual entry that cannot be written exits non-zero.
|
||||||
|
|
||||||
## `project_utils.py`
|
## `project_utils.py`
|
||||||
|
|
||||||
Shared helper module used by other scripts.
|
Shared helper module used by other scripts.
|
||||||
|
|||||||
@@ -36,4 +36,4 @@ The manifest is audit-only (`audit_only: true`, `runtime_consumed: false`); it i
|
|||||||
- **New corpus file** → when no existing category exemption matches it, the audit fails with `LOAD_COVERAGE_GAP` until you add it to the load sets that read it or exempt it with a one-line reason. Exempt only material that never enters role context (for example, a legacy tombstone, generated maintenance asset, maintainer-only doc, or license notice); represent conditional runtime reads as incremental load sets.
|
- **New corpus file** → when no existing category exemption matches it, the audit fails with `LOAD_COVERAGE_GAP` until you add it to the load sets that read it or exempt it with a one-line reason. Exempt only material that never enters role context (for example, a legacy tombstone, generated maintenance asset, maintainer-only doc, or license notice); represent conditional runtime reads as incremental load sets.
|
||||||
- **Intentional duplicate** → run `--json`, copy the finding's `kind`, `fingerprint`, and `paths` into `duplicates.accepted` with a reason. The acceptance identity is all three values, so separate path pairs with identical prose remain independently reviewable. Editing either reported raw block changes its fingerprint; stale acceptance fails with `DUPLICATE_ACCEPTED_STALE`. `--skip-near-duplicates` deliberately leaves accepted near pairs unchecked because that scan did not run.
|
- **Intentional duplicate** → run `--json`, copy the finding's `kind`, `fingerprint`, and `paths` into `duplicates.accepted` with a reason. The acceptance identity is all three values, so separate path pairs with identical prose remain independently reviewable. Editing either reported raw block changes its fingerprint; stale acceptance fails with `DUPLICATE_ACCEPTED_STALE`. `--skip-near-duplicates` deliberately leaves accepted near pairs unchecked because that scan did not run.
|
||||||
- **Schema owner** → every configured field must have a definition signal in its declared owner. One grammar-like non-owner is enough to surface a candidate; split fields into separate owner entries when they belong to different artifacts.
|
- **Schema owner** → every configured field must have a definition signal in its declared owner. One grammar-like non-owner is enough to surface a candidate; split fields into separate owner entries when they belong to different artifacts.
|
||||||
- **Raising a budget** (`budget_policy: current_growth_ceiling` — ceilings ratchet against the current state): legitimate only when a deliberate content addition or a corrected load-set membership moves a file or scenario past its ceiling. Bump to the new actual value plus minimal slack in the same change that causes the increase, and say why in that change's message. Never pre-raise ceilings to make room.
|
- **Budget ceilings** (`budget_policy: fixed_upper_bound`): budgets are stable, deliberately rounded limits rather than mirrors of the current token count. Establish a new ceiling with roughly 10% working headroom and round it up in 250-token increments below 10k, 1k increments below 100k, or 5k increments from 100k upward; the manifest loader enforces those increments. Once set, do not raise or lower a passing ceiling, including to restore headroom after prompt growth. Raise it only after the current audit reports `BUDGET_CORPUS`, `BUDGET_FILE`, or `BUDGET_LOAD_SET` against that exact ceiling; then choose the next rounded limit with comparable headroom, record the overflow-triggering scope in the same change, and leave it unchanged until another actual overflow.
|
||||||
|
|||||||
@@ -624,7 +624,7 @@ Behavior:
|
|||||||
- Native mode is strict about unsupported visual SVG elements: if a visual element cannot be represented or safely preserved, export fails with the SVG file, element tag, and position instead of silently dropping content.
|
- Native mode is strict about unsupported visual SVG elements: if a visual element cannot be represented or safely preserved, export fails with the SVG file, element tag, and position instead of silently dropping content.
|
||||||
- Omitting `--pptx-structure` reads `spec_lock.md`. Free-design, brand-only, and `template_reuse_scope: style` releases declare `mode: flat`, omit Master/Layout mappings and SVG structure metadata, and materialize one clean project-owned Master plus one Blank Layout from the current lock. Deck/layout templates use `mode: structured` only for `template_reuse_scope: mirror|layout`, with complete unique `pptx_masters` / `pptx_layouts` rosters and one `page_pptx_layouts` assignment per page. A template-backed Layout definition may remain unused by pages and still register in the final package.
|
- Omitting `--pptx-structure` reads `spec_lock.md`. Free-design, brand-only, and `template_reuse_scope: style` releases declare `mode: flat`, omit Master/Layout mappings and SVG structure metadata, and materialize one clean project-owned Master plus one Blank Layout from the current lock. Deck/layout templates use `mode: structured` only for `template_reuse_scope: mirror|layout`, with complete unique `pptx_masters` / `pptx_layouts` rosters and one `page_pptx_layouts` assignment per page. A template-backed Layout definition may remain unused by pages and still register in the final package.
|
||||||
- On structured template routes, every page root repeats Master/Layout keys and picker names. Master/Layout fixed visuals are direct semantic atoms. Ordinary layer `<g>` elements are invalid; one validated compact authored-preset `<g>` emitted by `preset_shape_svg.py` is the sole group exception because it compiles to one native shape.
|
- On structured template routes, every page root repeats Master/Layout keys and picker names. Master/Layout fixed visuals are direct semantic atoms. Ordinary layer `<g>` elements are invalid; one validated compact authored-preset `<g>` emitted by `preset_shape_svg.py` is the sole group exception because it compiles to one native shape.
|
||||||
- Every visible direct root `<g>` requires root-coordinate `data-pptx-bounds`; nested bounds are ignored. Frame/native metadata never replaces it; placeholder bounds also define the slot frame. Checker compares root bounds with `viewBox` and only descendant text with that module. Images, shapes, paths, `<use>`, effects, and object frames are excluded. Per side: ≤`1px` ignored, ≤`5%` warns, >`5%` fails. Bounds never clip/reflow.
|
- Every visible direct root `<g>` requires root-coordinate `data-pptx-bounds`; nested bounds are ignored. Frame/native metadata never replaces it; placeholder bounds also define the slot frame. Checker compares root bounds with `viewBox`, descendant text with its module using DrawingML wrapping headroom, and every estimable visible text carrier directly with the root `viewBox` before that headroom. Images, shapes, paths, `<use>`, effects, and object frames are excluded from module containment. Per side, ≤`1px` is ignored; module overflow ≤`5%` warns and >`5%` fails, while larger page text overflow always fails. Bounds never clip/reflow; unestimable visible text warns. A wholly off-canvas direct-root Morph endpoint may opt out of page containment with `data-pptx-morph-staging="true"`; it still needs valid module bounds, retained Morph uses an explicit pair, and partial overflow remains blocking.
|
||||||
- Missing root bounds fails on final pages/templates and under `--template-mode`; references warn until adapted.
|
- Missing root bounds fails on final pages/templates and under `--template-mode`; references warn until adapted.
|
||||||
- On structured template routes, each normal slot is a direct root `<g id>` with semantic type, positive design-zone bounds, and exactly one compatible carrier. Composite `object` slots use explicit proxy binding; zero-slot Layouts are valid. Flat pages keep all SVG objects Slide-local.
|
- On structured template routes, each normal slot is a direct root `<g id>` with semantic type, positive design-zone bounds, and exactly one compatible carrier. Composite `object` slots use explicit proxy binding; zero-slot Layouts are valid. Flat pages keep all SVG objects Slide-local.
|
||||||
- Flat export maps locked typography/colors into a clean project-owned theme/Master, removes stock content placeholders and unused built-in Layouts, retains only the standard date/footer/slide-number capability hooks, and keeps one Blank Layout without promoting Slide content. Structured export additionally creates one reusable Layout per declared key and reopens the package to verify the full Presentation → Master → Layout → Slide graph, fixed-object order, placeholder identities/bounds, carrier bindings, hidden proxies, and zero-slot Layouts.
|
- Flat export maps locked typography/colors into a clean project-owned theme/Master, removes stock content placeholders and unused built-in Layouts, retains only the standard date/footer/slide-number capability hooks, and keeps one Blank Layout without promoting Slide content. Structured export additionally creates one reusable Layout per declared key and reopens the package to verify the full Presentation → Master → Layout → Slide graph, fixed-object order, placeholder identities/bounds, carrier bindings, hidden proxies, and zero-slot Layouts.
|
||||||
@@ -634,6 +634,7 @@ Behavior:
|
|||||||
- `[Content_Types].xml` is generated from the actual media extensions written into the PPTX. Unknown media extensions fail unless Python's `mimetypes` can identify them.
|
- `[Content_Types].xml` is generated from the actual media extensions written into the PPTX. Unknown media extensions fail unless Python's `mimetypes` can identify them.
|
||||||
- Native export writes to a temporary file first and publishes the requested PPTX only after conversion succeeds. A failed conversion does not replace the main output file.
|
- Native export writes to a temporary file first and publishes the requested PPTX only after conversion succeeds. A failed conversion does not replace the main output file.
|
||||||
- `--conversion-trace` without a path writes `validation/<output_stem>.trace.json`. `--conversion-trace <path>` respects the explicit destination; relative paths are resolved from the project root, so `exports/<name>.trace.json` remains available when intentionally requested.
|
- `--conversion-trace` without a path writes `validation/<output_stem>.trace.json`. `--conversion-trace <path>` respects the explicit destination; relative paths are resolved from the project root, so `exports/<name>.trace.json` remains available when intentionally requested.
|
||||||
|
- Formal default and `--quick-generate` release export compute the exact SVG source fingerprint and refuse a missing, unreadable, unsupported, non-final, blocking, stale, or unverifiable final quality report before PPTX creation. A project without `validation/svg_quality_report.json` exits nonzero with the `not-provided` gate status; run the final checker against its current `svg_output/` first. An explicit non-`output` `--source` remains a diagnostic override and bypasses this release gate; postflight still records any verifiable report linkage.
|
||||||
- After publication, native export writes `validation/<output_stem>.report.json`. The report distinguishes authored Slides from internal Layout definitions, reruns ZIP integrity and published Slide-count checks, records slide/layout/master/notes part counts, labels relationship/structured/transition/animation validation as enforced at build time, links the final SVG quality report only when its SHA-256 source fingerprint matches the exact export inputs, and surfaces stale/unverified gates, unresolved template tokens, generic-only font stacks, and external image references. A matching final quality report with introduced warnings yields `passed-with-warnings` and a `quality_introduced_warnings=<N>` receipt instead of a clean `passed` claim.
|
- After publication, native export writes `validation/<output_stem>.report.json`. The report distinguishes authored Slides from internal Layout definitions, reruns ZIP integrity and published Slide-count checks, records slide/layout/master/notes part counts, labels relationship/structured/transition/animation validation as enforced at build time, links the final SVG quality report only when its SHA-256 source fingerprint matches the exact export inputs, and surfaces stale/unverified gates, unresolved template tokens, generic-only font stacks, and external image references. A matching final quality report with introduced warnings yields `passed-with-warnings` and a `quality_introduced_warnings=<N>` receipt instead of a clean `passed` claim.
|
||||||
- By default, a successful command also prints a compact receipt instead of requiring a report read: `[POSTFLIGHT] status=<...> quality_gate=<...> slides=<N> warning_categories=<N>`, followed by one compact line per warning category and the `[PPTX]` / `[REPORT]` paths. Resource-warning lines carry counts; a non-passing quality gate carries its status. Routine agents use this receipt and do not load either complete validation JSON into model context. Full reports remain cold audit artifacts; failure investigation and explicit audits extract only the required fields. `--quiet` keeps suppressing successful-run output.
|
- By default, a successful command also prints a compact receipt instead of requiring a report read: `[POSTFLIGHT] status=<...> quality_gate=<...> slides=<N> warning_categories=<N>`, followed by one compact line per warning category and the `[PPTX]` / `[REPORT]` paths. Resource-warning lines carry counts; a non-passing quality gate carries its status. Routine agents use this receipt and do not load either complete validation JSON into model context. Full reports remain cold audit artifacts; failure investigation and explicit audits extract only the required fields. `--quiet` keeps suppressing successful-run output.
|
||||||
- Before publishing structured template output, export reopens the temporary PPTX and validates the Slide → Layout → Master graph and registrations, Layout identity, placeholder identity, reusable bounds, and prompt/level-one sizes. A mismatch aborts publication. Flat release instead validates its single referenced Master/Layout shell and exact date/footer/slide-number hook roster before packaging.
|
- Before publishing structured template output, export reopens the temporary PPTX and validates the Slide → Layout → Master graph and registrations, Layout identity, placeholder identity, reusable bounds, and prompt/level-one sizes. A mismatch aborts publication. Flat release instead validates its single referenced Master/Layout shell and exact date/footer/slide-number hook roster before packaging.
|
||||||
|
|||||||
+19
-2
@@ -52,6 +52,7 @@ if str(SCRIPTS_DIR) not in sys.path:
|
|||||||
sys.path.insert(0, str(SCRIPTS_DIR))
|
sys.path.insert(0, str(SCRIPTS_DIR))
|
||||||
|
|
||||||
from attribution_guard import require_skill_integrity # noqa: E402
|
from attribution_guard import require_skill_integrity # noqa: E402
|
||||||
|
from workflow_log import append_note # noqa: E402
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from project_utils import (
|
from project_utils import (
|
||||||
@@ -313,7 +314,7 @@ class ProjectManager:
|
|||||||
"- `live_preview/`: browser preview runtime files and history (lock.json, server.log, edits.jsonl, annotations.jsonl)\n"
|
"- `live_preview/`: browser preview runtime files and history (lock.json, server.log, edits.jsonl, annotations.jsonl)\n"
|
||||||
"- `sources/`: source materials and normalized markdown\n"
|
"- `sources/`: source materials and normalized markdown\n"
|
||||||
"- `analysis/`: machine-extracted intermediate analysis (PPTX intake, image_analysis.csv) — the pipeline's canonical must-read source/asset facts\n"
|
"- `analysis/`: machine-extracted intermediate analysis (PPTX intake, image_analysis.csv) — the pipeline's canonical must-read source/asset facts\n"
|
||||||
"- `validation/`: SVG quality reports and PPTX postflight audit reports\n"
|
"- `validation/`: cold workflow audit log, SVG quality reports, and PPTX postflight audit reports\n"
|
||||||
"- `exports/`: final native DrawingML pptx deliverables only (timestamped); `_native_charts_tables.pptx` name with `--native-charts-and-tables`, `_narrated.pptx` name when narration audio is embedded\n"
|
"- `exports/`: final native DrawingML pptx deliverables only (timestamped); `_native_charts_tables.pptx` name with `--native-charts-and-tables`, `_narrated.pptx` name when narration audio is embedded\n"
|
||||||
"- `backup/<timestamp>/`: svg_output/ archive (always written in default-flow mode; safe to delete old timestamps)\n"
|
"- `backup/<timestamp>/`: svg_output/ archive (always written in default-flow mode; safe to delete old timestamps)\n"
|
||||||
),
|
),
|
||||||
@@ -1079,7 +1080,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
init.add_argument(
|
init.add_argument(
|
||||||
"--quick-generate",
|
"--quick-generate",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Create only the svg_output directory and omit README.md",
|
help=(
|
||||||
|
"Create svg_output plus the validation workflow audit log and "
|
||||||
|
"omit README.md"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
import_sources = subparsers.add_parser(
|
import_sources = subparsers.add_parser(
|
||||||
@@ -1164,10 +1168,23 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
if args.quick_generate:
|
if args.quick_generate:
|
||||||
print("1. Generate SVG files into svg_output/")
|
print("1. Generate SVG files into svg_output/")
|
||||||
print("2. Run the Quick Generate final checker and exporter")
|
print("2. Run the Quick Generate final checker and exporter")
|
||||||
|
profile = "quick"
|
||||||
else:
|
else:
|
||||||
print("1. Put source files into sources/ (or use import-sources)")
|
print("1. Put source files into sources/ (or use import-sources)")
|
||||||
print("2. Save your design spec to the project root")
|
print("2. Save your design spec to the project root")
|
||||||
print("3. Generate SVG files into svg_output/")
|
print("3. Generate SVG files into svg_output/")
|
||||||
|
profile = "default"
|
||||||
|
try:
|
||||||
|
append_note(
|
||||||
|
project_path,
|
||||||
|
f"Project initialized: profile={profile}; "
|
||||||
|
f"canvas={args.format}; path={project_path}",
|
||||||
|
)
|
||||||
|
except OSError as exc:
|
||||||
|
print(
|
||||||
|
f"[WARN] Workflow audit unavailable: {exc}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if args.command == "import-sources":
|
if args.command == "import-sources":
|
||||||
|
|||||||
@@ -107,6 +107,17 @@ def _read_utf8(path: Path) -> str:
|
|||||||
raise AuditError(f"Cannot read UTF-8 file {path}: {exc}") from exc
|
raise AuditError(f"Cannot read UTF-8 file {path}: {exc}") from exc
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_fixed_budget(label: str, budget: Any) -> None:
|
||||||
|
"""Require durable rounded ceilings instead of current-count ratchets."""
|
||||||
|
if not isinstance(budget, int) or isinstance(budget, bool) or budget < 1:
|
||||||
|
raise AuditError(f"{label} must be a positive integer")
|
||||||
|
increment = 250 if budget < 10_000 else 1_000 if budget < 100_000 else 5_000
|
||||||
|
if budget % increment:
|
||||||
|
raise AuditError(
|
||||||
|
f"{label} must use a fixed {increment}-token increment, got {budget}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def load_manifest(path: Path) -> dict[str, Any]:
|
def load_manifest(path: Path) -> dict[str, Any]:
|
||||||
"""Load and validate the prompt-audit manifest."""
|
"""Load and validate the prompt-audit manifest."""
|
||||||
try:
|
try:
|
||||||
@@ -128,21 +139,29 @@ def load_manifest(path: Path) -> dict[str, Any]:
|
|||||||
raise AuditError(f"Manifest is missing required key: {key}")
|
raise AuditError(f"Manifest is missing required key: {key}")
|
||||||
if raw["audit_only"] is not True or raw["runtime_consumed"] is not False:
|
if raw["audit_only"] is not True or raw["runtime_consumed"] is not False:
|
||||||
raise AuditError("Manifest must remain audit-only and excluded from runtime loading")
|
raise AuditError("Manifest must remain audit-only and excluded from runtime loading")
|
||||||
if raw["budget_policy"] != "current_growth_ceiling":
|
if raw["budget_policy"] != "fixed_upper_bound":
|
||||||
raise AuditError("Manifest budget_policy must remain current_growth_ceiling")
|
raise AuditError("Manifest budget_policy must remain fixed_upper_bound")
|
||||||
if raw["encoding"] != "o200k_base":
|
if raw["encoding"] != "o200k_base":
|
||||||
raise AuditError("Manifest encoding must remain o200k_base")
|
raise AuditError("Manifest encoding must remain o200k_base")
|
||||||
documents = raw["documents"]
|
documents = raw["documents"]
|
||||||
if not isinstance(documents, dict):
|
if not isinstance(documents, dict):
|
||||||
raise AuditError("documents must be an object")
|
raise AuditError("documents must be an object")
|
||||||
if not isinstance(documents.get("max_tokens"), int) or documents["max_tokens"] < 1:
|
_validate_fixed_budget("documents.max_tokens", documents.get("max_tokens"))
|
||||||
raise AuditError("documents.max_tokens must be a positive integer")
|
|
||||||
for key in ("load_sets", "file_budgets", "duplicates", "coverage"):
|
for key in ("load_sets", "file_budgets", "duplicates", "coverage"):
|
||||||
if key in raw and not isinstance(raw[key], dict):
|
if key in raw and not isinstance(raw[key], dict):
|
||||||
raise AuditError(f"{key} must be an object")
|
raise AuditError(f"{key} must be an object")
|
||||||
for key in ("authority_edges", "registries", "schema_grammars"):
|
for key in ("authority_edges", "registries", "schema_grammars"):
|
||||||
if key in raw and not isinstance(raw[key], list):
|
if key in raw and not isinstance(raw[key], list):
|
||||||
raise AuditError(f"{key} must be an array")
|
raise AuditError(f"{key} must be an array")
|
||||||
|
for file_path, budget in raw.get("file_budgets", {}).items():
|
||||||
|
_validate_fixed_budget(f"file_budgets.{file_path}", budget)
|
||||||
|
for name, load_set in raw.get("load_sets", {}).items():
|
||||||
|
if not isinstance(load_set, dict):
|
||||||
|
raise AuditError(f"load_sets.{name} must be an object")
|
||||||
|
_validate_fixed_budget(
|
||||||
|
f"load_sets.{name}.max_tokens",
|
||||||
|
load_set.get("max_tokens"),
|
||||||
|
)
|
||||||
|
|
||||||
schema_configs = raw.get("schema_grammars", [])
|
schema_configs = raw.get("schema_grammars", [])
|
||||||
for index, config in enumerate(schema_configs):
|
for index, config in enumerate(schema_configs):
|
||||||
@@ -1693,7 +1712,7 @@ def render_text(report: dict[str, Any]) -> str:
|
|||||||
lines = [
|
lines = [
|
||||||
"PPT Master Prompt Audit",
|
"PPT Master Prompt Audit",
|
||||||
"=======================",
|
"=======================",
|
||||||
"Manifest: audit-only | runtime loading: disabled | budgets: current growth ceilings",
|
"Manifest: audit-only | runtime loading: disabled | budgets: fixed upper bounds",
|
||||||
(
|
(
|
||||||
f"Corpus: {summary['files']} files | {summary['tokens']} tokens "
|
f"Corpus: {summary['files']} files | {summary['tokens']} tokens "
|
||||||
f"(budget {summary['max_tokens']})"
|
f"(budget {summary['max_tokens']})"
|
||||||
|
|||||||
+216
-102
@@ -2,7 +2,7 @@
|
|||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"audit_only": true,
|
"audit_only": true,
|
||||||
"runtime_consumed": false,
|
"runtime_consumed": false,
|
||||||
"budget_policy": "current_growth_ceiling",
|
"budget_policy": "fixed_upper_bound",
|
||||||
"encoding": "o200k_base",
|
"encoding": "o200k_base",
|
||||||
"documents": {
|
"documents": {
|
||||||
"include": [
|
"include": [
|
||||||
@@ -12,29 +12,29 @@
|
|||||||
"skills/ppt-master/templates/schemas/*.json"
|
"skills/ppt-master/templates/schemas/*.json"
|
||||||
],
|
],
|
||||||
"exclude": [],
|
"exclude": [],
|
||||||
"max_tokens": 399484
|
"max_tokens": 450000
|
||||||
},
|
},
|
||||||
"file_budgets": {
|
"file_budgets": {
|
||||||
"AGENTS.md": 2400,
|
"AGENTS.md": 2750,
|
||||||
"skills/ppt-master/SKILL.md": 950,
|
"skills/ppt-master/SKILL.md": 1250,
|
||||||
"skills/ppt-master/references/executor-base.md": 8245,
|
"skills/ppt-master/references/executor-base.md": 9250,
|
||||||
"skills/ppt-master/references/executor-structured.md": 5300,
|
"skills/ppt-master/references/executor-structured.md": 5500,
|
||||||
"skills/ppt-master/references/executor-chart.md": 3100,
|
"skills/ppt-master/references/executor-chart.md": 3500,
|
||||||
"skills/ppt-master/references/executor-image.md": 1728,
|
"skills/ppt-master/references/executor-image.md": 2000,
|
||||||
"skills/ppt-master/references/executor-web-image.md": 500,
|
"skills/ppt-master/references/executor-web-image.md": 750,
|
||||||
"skills/ppt-master/references/executor-notes.md": 900,
|
"skills/ppt-master/references/executor-notes.md": 1000,
|
||||||
"skills/ppt-master/references/shared-standards.md": 250,
|
"skills/ppt-master/references/shared-standards.md": 500,
|
||||||
"skills/ppt-master/references/shared-standards-core.md": 11100,
|
"skills/ppt-master/references/shared-standards-core.md": 13000,
|
||||||
"skills/ppt-master/references/svg-effects.md": 13337,
|
"skills/ppt-master/references/svg-effects.md": 15000,
|
||||||
"skills/ppt-master/references/native-data-interface.md": 6200,
|
"skills/ppt-master/references/native-data-interface.md": 6750,
|
||||||
"skills/ppt-master/references/pptx-structure-interface.md": 4300,
|
"skills/ppt-master/references/pptx-structure-interface.md": 4750,
|
||||||
"skills/ppt-master/references/strategist.md": 14487,
|
"skills/ppt-master/references/strategist.md": 16000,
|
||||||
"skills/ppt-master/references/strategist-image.md": 2525,
|
"skills/ppt-master/references/strategist-image.md": 2750,
|
||||||
"skills/ppt-master/references/strategist-template.md": 2100,
|
"skills/ppt-master/references/strategist-template.md": 3000,
|
||||||
"skills/ppt-master/templates/design_spec_reference.md": 3275,
|
"skills/ppt-master/templates/design_spec_reference.md": 3750,
|
||||||
"skills/ppt-master/templates/spec_lock_reference.md": 2375,
|
"skills/ppt-master/templates/spec_lock_reference.md": 2750,
|
||||||
"skills/ppt-master/workflows/generate-pptx.md": 13850,
|
"skills/ppt-master/workflows/generate-pptx.md": 16000,
|
||||||
"skills/ppt-master/workflows/stages/apply-template-workspace.md": 1800
|
"skills/ppt-master/workflows/stages/apply-template-workspace.md": 3500
|
||||||
},
|
},
|
||||||
"load_sets": {
|
"load_sets": {
|
||||||
"bootstrap.routing": {
|
"bootstrap.routing": {
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"skills/ppt-master/SKILL.md",
|
"skills/ppt-master/SKILL.md",
|
||||||
"skills/ppt-master/workflows/routing.md"
|
"skills/ppt-master/workflows/routing.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 5855
|
"max_tokens": 6750
|
||||||
},
|
},
|
||||||
"support.sponsor-recommendation.en": {
|
"support.sponsor-recommendation.en": {
|
||||||
"description": "English sponsor context for explicit user requests for model, AI image model, API/provider, or hosted-service recommendations.",
|
"description": "English sponsor context for explicit user requests for model, AI image model, API/provider, or hosted-service recommendations.",
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/SPONSORS.md"
|
"skills/ppt-master/SPONSORS.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 1250
|
"max_tokens": 1500
|
||||||
},
|
},
|
||||||
"support.sponsor-recommendation.zh": {
|
"support.sponsor-recommendation.zh": {
|
||||||
"description": "Chinese sponsor context for explicit user requests for model, AI image model, API/provider, or hosted-service recommendations.",
|
"description": "Chinese sponsor context for explicit user requests for model, AI image model, API/provider, or hosted-service recommendations.",
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/SPONSORS_CN.md"
|
"skills/ppt-master/SPONSORS_CN.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 1250
|
"max_tokens": 1500
|
||||||
},
|
},
|
||||||
"route.create-template.brand": {
|
"route.create-template.brand": {
|
||||||
"description": "Create Brand path; it authors no SVG and does not activate Template_Designer or SVG modules.",
|
"description": "Create Brand path; it authors no SVG and does not activate Template_Designer or SVG modules.",
|
||||||
@@ -75,7 +75,21 @@
|
|||||||
"skills/ppt-master/templates/README.md",
|
"skills/ppt-master/templates/README.md",
|
||||||
"skills/ppt-master/templates/brands/README.md"
|
"skills/ppt-master/templates/brands/README.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 40600
|
"max_tokens": 36000
|
||||||
|
},
|
||||||
|
"route.create-template.style": {
|
||||||
|
"description": "Create Style path; it authors a roster-free direction/method spec and does not activate Template_Designer or SVG modules.",
|
||||||
|
"scope": "cumulative",
|
||||||
|
"include": [
|
||||||
|
"bootstrap.routing"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"skills/ppt-master/workflows/create-template.md",
|
||||||
|
"skills/ppt-master/workflows/create-template/create-style.md",
|
||||||
|
"skills/ppt-master/templates/README.md",
|
||||||
|
"skills/ppt-master/templates/styles/README.md"
|
||||||
|
],
|
||||||
|
"max_tokens": 37000
|
||||||
},
|
},
|
||||||
"route.create-template.deck": {
|
"route.create-template.deck": {
|
||||||
"description": "Create Deck path through Template_Designer, SVG core, and the structured PPTX interface.",
|
"description": "Create Deck path through Template_Designer, SVG core, and the structured PPTX interface.",
|
||||||
@@ -93,7 +107,7 @@
|
|||||||
"skills/ppt-master/templates/README.md",
|
"skills/ppt-master/templates/README.md",
|
||||||
"skills/ppt-master/templates/decks/README.md"
|
"skills/ppt-master/templates/decks/README.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 58421
|
"max_tokens": 67000
|
||||||
},
|
},
|
||||||
"route.create-template.layout": {
|
"route.create-template.layout": {
|
||||||
"description": "Create Layout path through Template_Designer, SVG core, and the structured PPTX interface.",
|
"description": "Create Layout path through Template_Designer, SVG core, and the structured PPTX interface.",
|
||||||
@@ -111,7 +125,7 @@
|
|||||||
"skills/ppt-master/templates/README.md",
|
"skills/ppt-master/templates/README.md",
|
||||||
"skills/ppt-master/templates/layouts/README.md"
|
"skills/ppt-master/templates/layouts/README.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 58598
|
"max_tokens": 67000
|
||||||
},
|
},
|
||||||
"route.enhance-native-pptx": {
|
"route.enhance-native-pptx": {
|
||||||
"description": "Finished-PPTX native enhancement route.",
|
"description": "Finished-PPTX native enhancement route.",
|
||||||
@@ -122,7 +136,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/workflows/native-enhance-pptx.md"
|
"skills/ppt-master/workflows/native-enhance-pptx.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 9849
|
"max_tokens": 11000
|
||||||
},
|
},
|
||||||
"route.fill-native-pptx": {
|
"route.fill-native-pptx": {
|
||||||
"description": "Raw-PPTX native fill route.",
|
"description": "Raw-PPTX native fill route.",
|
||||||
@@ -133,7 +147,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/workflows/template-fill-pptx.md"
|
"skills/ppt-master/workflows/template-fill-pptx.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 11542
|
"max_tokens": 13000
|
||||||
},
|
},
|
||||||
"route.generate.planning": {
|
"route.generate.planning": {
|
||||||
"description": "Generate-PPTX planning through Strategist, including reference-first whole-document authoring and representative multi-source custom mode/style synthesis; schemas and optional scaffolds are tool-consumed.",
|
"description": "Generate-PPTX planning through Strategist, including reference-first whole-document authoring and representative multi-source custom mode/style synthesis; schemas and optional scaffolds are tool-consumed.",
|
||||||
@@ -170,23 +184,63 @@
|
|||||||
"registry": "visual-styles"
|
"registry": "visual-styles"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"max_tokens": 63121
|
"max_tokens": 70000
|
||||||
},
|
},
|
||||||
"route.generate.quick-generate": {
|
"route.generate.quick-generate": {
|
||||||
"description": "Explicit non-interactive Generate path with conditional source/resource preparation and the complete SVG visual-construction authorities.",
|
"description": "Self-contained one-pass Generate profile with source intake, active-context mode/style selection, conditional resource preparation, and complete flat SVG visual-construction authorities.",
|
||||||
"scope": "cumulative",
|
"scope": "cumulative",
|
||||||
"include": [
|
"include": [
|
||||||
"bootstrap.routing"
|
"bootstrap.routing"
|
||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/workflows/generate-pptx.md",
|
|
||||||
"skills/ppt-master/workflows/profiles/quick-generate.md",
|
"skills/ppt-master/workflows/profiles/quick-generate.md",
|
||||||
"skills/ppt-master/references/artifact-ownership.md",
|
"skills/ppt-master/references/artifact-ownership.md",
|
||||||
"skills/ppt-master/references/shared-standards-core.md",
|
"skills/ppt-master/references/shared-standards-core.md",
|
||||||
"skills/ppt-master/references/svg-effects.md",
|
"skills/ppt-master/references/svg-effects.md",
|
||||||
"skills/ppt-master/references/native-shape-authoring.md"
|
"skills/ppt-master/references/native-shape-authoring.md",
|
||||||
|
"skills/ppt-master/references/semantic-svg.md",
|
||||||
|
"skills/ppt-master/references/modes/_index.md",
|
||||||
|
"skills/ppt-master/references/visual-styles/_index.md",
|
||||||
|
{
|
||||||
|
"glob": "skills/ppt-master/references/modes/*.md",
|
||||||
|
"exclude": [
|
||||||
|
"*/_index.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 55127
|
"select": 3,
|
||||||
|
"load_event": "quick-mode",
|
||||||
|
"registry": "modes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "skills/ppt-master/references/visual-styles/*.md",
|
||||||
|
"exclude": [
|
||||||
|
"*/_index.md"
|
||||||
|
],
|
||||||
|
"select": 3,
|
||||||
|
"load_event": "quick-visual-style",
|
||||||
|
"registry": "visual-styles"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"max_tokens": 80000
|
||||||
|
},
|
||||||
|
"route.generate.quick-generate.beautify": {
|
||||||
|
"description": "Quick Generate with the shared strict 1:1 Beautify profile.",
|
||||||
|
"scope": "cumulative",
|
||||||
|
"include": [
|
||||||
|
"route.generate.quick-generate",
|
||||||
|
"profile.generate.beautify-pptx"
|
||||||
|
],
|
||||||
|
"files": [],
|
||||||
|
"max_tokens": 70000
|
||||||
|
},
|
||||||
|
"route.generate.quick-generate.template-fusion": {
|
||||||
|
"description": "Quick Generate with the maximum Brand, Style, Layout, and Deck exact-root fusion loaded for flat authoring.",
|
||||||
|
"scope": "cumulative",
|
||||||
|
"include": [
|
||||||
|
"route.generate.quick-generate",
|
||||||
|
"stage.generate.template.fusion-brand-style-layout-deck"
|
||||||
|
],
|
||||||
|
"files": [],
|
||||||
|
"max_tokens": 88000
|
||||||
},
|
},
|
||||||
"route.generate.quick-generate.topic-research": {
|
"route.generate.quick-generate.topic-research": {
|
||||||
"description": "Quick Generate with gap-targeted factual research before direct resource preparation and SVG authoring.",
|
"description": "Quick Generate with gap-targeted factual research before direct resource preparation and SVG authoring.",
|
||||||
@@ -196,7 +250,7 @@
|
|||||||
"stage.generate.topic-research"
|
"stage.generate.topic-research"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 56639
|
"max_tokens": 74000
|
||||||
},
|
},
|
||||||
"route.generate.quick-generate.icon": {
|
"route.generate.quick-generate.icon": {
|
||||||
"description": "Quick Generate with project-local bundled icon selection and synchronization.",
|
"description": "Quick Generate with project-local bundled icon selection and synchronization.",
|
||||||
@@ -207,7 +261,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/templates/icons/README.md"
|
"skills/ppt-master/templates/icons/README.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 57039
|
"max_tokens": 74000
|
||||||
},
|
},
|
||||||
"route.generate.quick-generate.in-hand-image": {
|
"route.generate.quick-generate.in-hand-image": {
|
||||||
"description": "Quick Generate with supplied, extracted, or placeholder image resources prepared before SVG authoring.",
|
"description": "Quick Generate with supplied, extracted, or placeholder image resources prepared before SVG authoring.",
|
||||||
@@ -217,7 +271,7 @@
|
|||||||
"stage.generate.executor.image"
|
"stage.generate.executor.image"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 66789
|
"max_tokens": 84000
|
||||||
},
|
},
|
||||||
"route.generate.quick-generate.formula": {
|
"route.generate.quick-generate.formula": {
|
||||||
"description": "Quick Generate with manifest-driven formula rendering and formula-image realization.",
|
"description": "Quick Generate with manifest-driven formula rendering and formula-image realization.",
|
||||||
@@ -229,7 +283,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/scripts/docs/image.md"
|
"skills/ppt-master/scripts/docs/image.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 69975
|
"max_tokens": 87000
|
||||||
},
|
},
|
||||||
"route.generate.quick-generate.ai-two-types": {
|
"route.generate.quick-generate.ai-two-types": {
|
||||||
"description": "Quick Generate with AI resource preparation, representative rendering inputs, and two local image types.",
|
"description": "Quick Generate with AI resource preparation, representative rendering inputs, and two local image types.",
|
||||||
@@ -240,7 +294,7 @@
|
|||||||
"stage.generate.executor.image"
|
"stage.generate.executor.image"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 89894
|
"max_tokens": 110000
|
||||||
},
|
},
|
||||||
"route.generate.quick-generate.web-image": {
|
"route.generate.quick-generate.web-image": {
|
||||||
"description": "Quick Generate with web resource preparation, provenance, attribution, and image execution.",
|
"description": "Quick Generate with web resource preparation, provenance, attribution, and image execution.",
|
||||||
@@ -251,7 +305,22 @@
|
|||||||
"stage.generate.executor.web-image"
|
"stage.generate.executor.web-image"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 74542
|
"max_tokens": 92000
|
||||||
|
},
|
||||||
|
"route.generate.quick-generate.chart": {
|
||||||
|
"description": "Quick Generate with bounded chart recall, chart/table authoring, optional native-data metadata, and in-context coordinate verification before the final checker.",
|
||||||
|
"scope": "cumulative",
|
||||||
|
"include": [
|
||||||
|
"route.generate.quick-generate",
|
||||||
|
"stage.generate.executor.chart",
|
||||||
|
"stage.generate.executor.native-data",
|
||||||
|
"stage.generate.chart-authoring",
|
||||||
|
"stage.generate.verify-charts"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"skills/ppt-master/scripts/docs/chart-recall.md"
|
||||||
|
],
|
||||||
|
"max_tokens": 105000
|
||||||
},
|
},
|
||||||
"route.generate.planning-image": {
|
"route.generate.planning-image": {
|
||||||
"description": "Generate-PPTX planning context after a non-none image source is proposed or confirmed, including the compact layout math and vocabulary.",
|
"description": "Generate-PPTX planning context after a non-none image source is proposed or confirmed, including the compact layout math and vocabulary.",
|
||||||
@@ -261,7 +330,7 @@
|
|||||||
"stage.generate.strategist.image"
|
"stage.generate.strategist.image"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 72961
|
"max_tokens": 81000
|
||||||
},
|
},
|
||||||
"route.generate.planning-formula": {
|
"route.generate.planning-formula": {
|
||||||
"description": "Formula-only planning context, including the shared image/formula layout math and vocabulary.",
|
"description": "Formula-only planning context, including the shared image/formula layout math and vocabulary.",
|
||||||
@@ -271,7 +340,7 @@
|
|||||||
"stage.generate.strategist.image"
|
"stage.generate.strategist.image"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 72961
|
"max_tokens": 81000
|
||||||
},
|
},
|
||||||
"route.generate.planning-ai": {
|
"route.generate.planning-ai": {
|
||||||
"description": "Generate-PPTX planning when AI image direction is available, including representative multi-source custom rendering synthesis.",
|
"description": "Generate-PPTX planning when AI image direction is available, including representative multi-source custom rendering synthesis.",
|
||||||
@@ -291,16 +360,16 @@
|
|||||||
"registry": "image-renderings"
|
"registry": "image-renderings"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"max_tokens": 77203
|
"max_tokens": 86000
|
||||||
},
|
},
|
||||||
"stage.generate.apply-template-workspace": {
|
"stage.generate.apply-template-workspace": {
|
||||||
"description": "Conditional Step 3 workspace validation, installation, and fusion framework.",
|
"description": "Post-Stage-1 Default or Quick exact-root workspace validation, installation, and fusion framework.",
|
||||||
"scope": "incremental",
|
"scope": "incremental",
|
||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/workflows/stages/apply-template-workspace.md",
|
"skills/ppt-master/workflows/stages/apply-template-workspace.md",
|
||||||
"skills/ppt-master/templates/README.md"
|
"skills/ppt-master/templates/README.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 3300
|
"max_tokens": 5000
|
||||||
},
|
},
|
||||||
"stage.generate.template.brand": {
|
"stage.generate.template.brand": {
|
||||||
"description": "Conditional Brand workspace application with one selected Brand spec.",
|
"description": "Conditional Brand workspace application with one selected Brand spec.",
|
||||||
@@ -316,7 +385,23 @@
|
|||||||
"load_event": "template-application"
|
"load_event": "template-application"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"max_tokens": 5200
|
"max_tokens": 7500
|
||||||
|
},
|
||||||
|
"stage.generate.template.style": {
|
||||||
|
"description": "Conditional roster-free Style workspace application.",
|
||||||
|
"scope": "incremental",
|
||||||
|
"include": [
|
||||||
|
"stage.generate.apply-template-workspace"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"skills/ppt-master/templates/styles/README.md",
|
||||||
|
{
|
||||||
|
"glob": "skills/ppt-master/templates/styles/*/templates/design_spec.md",
|
||||||
|
"select": 1,
|
||||||
|
"load_event": "template-application"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"max_tokens": 9000
|
||||||
},
|
},
|
||||||
"stage.generate.template.layout": {
|
"stage.generate.template.layout": {
|
||||||
"description": "Conditional Layout workspace application with one selected Layout spec.",
|
"description": "Conditional Layout workspace application with one selected Layout spec.",
|
||||||
@@ -332,7 +417,7 @@
|
|||||||
"load_event": "template-application"
|
"load_event": "template-application"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"max_tokens": 6400
|
"max_tokens": 9000
|
||||||
},
|
},
|
||||||
"stage.generate.template.deck": {
|
"stage.generate.template.deck": {
|
||||||
"description": "Conditional Deck workspace application with one selected Deck spec.",
|
"description": "Conditional Deck workspace application with one selected Deck spec.",
|
||||||
@@ -348,7 +433,7 @@
|
|||||||
"load_event": "template-application"
|
"load_event": "template-application"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"max_tokens": 6200
|
"max_tokens": 8500
|
||||||
},
|
},
|
||||||
"stage.generate.template.brand-two": {
|
"stage.generate.template.brand-two": {
|
||||||
"description": "Same-kind conflict path with two selected Brand specs.",
|
"description": "Same-kind conflict path with two selected Brand specs.",
|
||||||
@@ -364,16 +449,17 @@
|
|||||||
"load_event": "template-application"
|
"load_event": "template-application"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"max_tokens": 6200
|
"max_tokens": 8500
|
||||||
},
|
},
|
||||||
"stage.generate.template.fusion-brand-layout-deck": {
|
"stage.generate.template.fusion-brand-style-layout-deck": {
|
||||||
"description": "Maximum supported cross-kind fusion with one Brand, Layout, and Deck spec.",
|
"description": "Maximum supported cross-kind fusion with Brand, Style, Layout, and Deck contracts.",
|
||||||
"scope": "incremental",
|
"scope": "incremental",
|
||||||
"include": [
|
"include": [
|
||||||
"stage.generate.apply-template-workspace"
|
"stage.generate.apply-template-workspace"
|
||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/templates/brands/README.md",
|
"skills/ppt-master/templates/brands/README.md",
|
||||||
|
"skills/ppt-master/templates/styles/README.md",
|
||||||
"skills/ppt-master/templates/layouts/README.md",
|
"skills/ppt-master/templates/layouts/README.md",
|
||||||
"skills/ppt-master/templates/decks/README.md",
|
"skills/ppt-master/templates/decks/README.md",
|
||||||
{
|
{
|
||||||
@@ -381,6 +467,11 @@
|
|||||||
"select": 1,
|
"select": 1,
|
||||||
"load_event": "template-application"
|
"load_event": "template-application"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"glob": "skills/ppt-master/templates/styles/*/templates/design_spec.md",
|
||||||
|
"select": 1,
|
||||||
|
"load_event": "template-application"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"glob": "skills/ppt-master/templates/layouts/*/templates/design_spec.md",
|
"glob": "skills/ppt-master/templates/layouts/*/templates/design_spec.md",
|
||||||
"select": 1,
|
"select": 1,
|
||||||
@@ -392,7 +483,7 @@
|
|||||||
"load_event": "template-application"
|
"load_event": "template-application"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"max_tokens": 11000
|
"max_tokens": 16000
|
||||||
},
|
},
|
||||||
"route.generate.flat-no-image": {
|
"route.generate.flat-no-image": {
|
||||||
"description": "Default fixed Generate-PPTX context with no image-acquisition role.",
|
"description": "Default fixed Generate-PPTX context with no image-acquisition role.",
|
||||||
@@ -403,7 +494,7 @@
|
|||||||
"stage.generate.executor.notes"
|
"stage.generate.executor.notes"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 106595
|
"max_tokens": 120000
|
||||||
},
|
},
|
||||||
"route.generate.flat-ai-two-types": {
|
"route.generate.flat-ai-two-types": {
|
||||||
"description": "Generate-PPTX context with AI images, representative multi-source custom rendering, and two local types.",
|
"description": "Generate-PPTX context with AI images, representative multi-source custom rendering, and two local types.",
|
||||||
@@ -416,7 +507,7 @@
|
|||||||
"stage.generate.image.ai-two-types"
|
"stage.generate.image.ai-two-types"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 149290
|
"max_tokens": 165000
|
||||||
},
|
},
|
||||||
"route.generate.flat-in-hand-image": {
|
"route.generate.flat-in-hand-image": {
|
||||||
"description": "Generate-PPTX context with provided, placeholder, or formula images and no acquisition role.",
|
"description": "Generate-PPTX context with provided, placeholder, or formula images and no acquisition role.",
|
||||||
@@ -428,7 +519,7 @@
|
|||||||
"stage.generate.executor.notes"
|
"stage.generate.executor.notes"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 122615
|
"max_tokens": 135000
|
||||||
},
|
},
|
||||||
"route.generate.flat-web-image": {
|
"route.generate.flat-web-image": {
|
||||||
"description": "Generate-PPTX context with web image acquisition.",
|
"description": "Generate-PPTX context with web image acquisition.",
|
||||||
@@ -441,7 +532,7 @@
|
|||||||
"stage.generate.image.web"
|
"stage.generate.image.web"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 129890
|
"max_tokens": 145000
|
||||||
},
|
},
|
||||||
"route.enhance-native-pptx.audio": {
|
"route.enhance-native-pptx.audio": {
|
||||||
"description": "Enhance Native PPTX with the shared narration-audio stage.",
|
"description": "Enhance Native PPTX with the shared narration-audio stage.",
|
||||||
@@ -451,7 +542,7 @@
|
|||||||
"stage.shared.generate-audio"
|
"stage.shared.generate-audio"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 15206
|
"max_tokens": 17000
|
||||||
},
|
},
|
||||||
"route.generate.beautify-flat-no-image": {
|
"route.generate.beautify-flat-no-image": {
|
||||||
"description": "Generate-PPTX 1:1 beautify profile on the flat no-image path.",
|
"description": "Generate-PPTX 1:1 beautify profile on the flat no-image path.",
|
||||||
@@ -461,7 +552,7 @@
|
|||||||
"profile.generate.beautify-pptx"
|
"profile.generate.beautify-pptx"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 113602
|
"max_tokens": 130000
|
||||||
},
|
},
|
||||||
"route.generate.flat-no-image-chart": {
|
"route.generate.flat-no-image-chart": {
|
||||||
"description": "Default flat Generate-PPTX path with chart authoring, native-data replacement, and verification.",
|
"description": "Default flat Generate-PPTX path with chart authoring, native-data replacement, and verification.",
|
||||||
@@ -474,7 +565,7 @@
|
|||||||
"stage.generate.verify-charts"
|
"stage.generate.verify-charts"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 130931
|
"max_tokens": 145000
|
||||||
},
|
},
|
||||||
"route.generate.brand-flat-no-image": {
|
"route.generate.brand-flat-no-image": {
|
||||||
"description": "Brand-preset flat Generate-PPTX path without image acquisition.",
|
"description": "Brand-preset flat Generate-PPTX path without image acquisition.",
|
||||||
@@ -486,7 +577,19 @@
|
|||||||
"stage.generate.template.brand"
|
"stage.generate.template.brand"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 113627
|
"max_tokens": 130000
|
||||||
|
},
|
||||||
|
"route.generate.style-flat-no-image": {
|
||||||
|
"description": "Style-workspace flat Generate-PPTX path without image acquisition.",
|
||||||
|
"scope": "cumulative",
|
||||||
|
"include": [
|
||||||
|
"route.generate.planning-template",
|
||||||
|
"stage.generate.executor.flat",
|
||||||
|
"stage.generate.executor.notes",
|
||||||
|
"stage.generate.template.style"
|
||||||
|
],
|
||||||
|
"files": [],
|
||||||
|
"max_tokens": 130000
|
||||||
},
|
},
|
||||||
"route.generate.deck-structured-no-image": {
|
"route.generate.deck-structured-no-image": {
|
||||||
"description": "Deck-preset structured Generate-PPTX path without image acquisition.",
|
"description": "Deck-preset structured Generate-PPTX path without image acquisition.",
|
||||||
@@ -498,7 +601,7 @@
|
|||||||
"stage.generate.template.deck"
|
"stage.generate.template.deck"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 123564
|
"max_tokens": 140000
|
||||||
},
|
},
|
||||||
"route.generate.layout-structured-no-image": {
|
"route.generate.layout-structured-no-image": {
|
||||||
"description": "Layout-preset structured Generate-PPTX path without image acquisition.",
|
"description": "Layout-preset structured Generate-PPTX path without image acquisition.",
|
||||||
@@ -510,7 +613,7 @@
|
|||||||
"stage.generate.template.layout"
|
"stage.generate.template.layout"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 124013
|
"max_tokens": 140000
|
||||||
},
|
},
|
||||||
"route.generate.topic-only-flat-no-image": {
|
"route.generate.topic-only-flat-no-image": {
|
||||||
"description": "Topic research followed by the default flat no-image Generate-PPTX path.",
|
"description": "Topic research followed by the default flat no-image Generate-PPTX path.",
|
||||||
@@ -520,7 +623,7 @@
|
|||||||
"route.generate.flat-no-image"
|
"route.generate.flat-no-image"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 108107
|
"max_tokens": 120000
|
||||||
},
|
},
|
||||||
"stage.generate.executor.flat": {
|
"stage.generate.executor.flat": {
|
||||||
"description": "Incremental flat Executor core with complete visual-construction authorities and representative multi-source custom mode/style execution.",
|
"description": "Incremental flat Executor core with complete visual-construction authorities and representative multi-source custom mode/style execution.",
|
||||||
@@ -552,7 +655,7 @@
|
|||||||
"allow_repeat": true
|
"allow_repeat": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"max_tokens": 52590
|
"max_tokens": 48000
|
||||||
},
|
},
|
||||||
"stage.generate.executor.structured": {
|
"stage.generate.executor.structured": {
|
||||||
"description": "Structured template execution layered on the flat/shared core.",
|
"description": "Structured template execution layered on the flat/shared core.",
|
||||||
@@ -564,7 +667,7 @@
|
|||||||
"skills/ppt-master/references/executor-structured.md",
|
"skills/ppt-master/references/executor-structured.md",
|
||||||
"skills/ppt-master/references/pptx-structure-interface.md"
|
"skills/ppt-master/references/pptx-structure-interface.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 53090
|
"max_tokens": 58000
|
||||||
},
|
},
|
||||||
"stage.generate.image.ai-two-types": {
|
"stage.generate.image.ai-two-types": {
|
||||||
"description": "Incremental AI-image role with representative multi-source custom rendering and two selected local types.",
|
"description": "Incremental AI-image role with representative multi-source custom rendering and two selected local types.",
|
||||||
@@ -598,7 +701,7 @@
|
|||||||
"registry": "image-type-templates"
|
"registry": "image-type-templates"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"max_tokens": 23125
|
"max_tokens": 26000
|
||||||
},
|
},
|
||||||
"stage.generate.image.web": {
|
"stage.generate.image.web": {
|
||||||
"description": "Incremental web-image acquisition role.",
|
"description": "Incremental web-image acquisition role.",
|
||||||
@@ -607,7 +710,7 @@
|
|||||||
"skills/ppt-master/references/image-base.md",
|
"skills/ppt-master/references/image-base.md",
|
||||||
"skills/ppt-master/references/image-searcher.md"
|
"skills/ppt-master/references/image-searcher.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 7275
|
"max_tokens": 8000
|
||||||
},
|
},
|
||||||
"stage.generate.resume-execute": {
|
"stage.generate.resume-execute": {
|
||||||
"description": "Fresh-session resume plus the flat Executor core and post-SVG notes rules.",
|
"description": "Fresh-session resume plus the flat Executor core and post-SVG notes rules.",
|
||||||
@@ -622,7 +725,7 @@
|
|||||||
"skills/ppt-master/workflows/stages/resume-execute.md",
|
"skills/ppt-master/workflows/stages/resume-execute.md",
|
||||||
"skills/ppt-master/references/artifact-ownership.md"
|
"skills/ppt-master/references/artifact-ownership.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 68863
|
"max_tokens": 77000
|
||||||
},
|
},
|
||||||
"stage.generate.resume-execute-image": {
|
"stage.generate.resume-execute-image": {
|
||||||
"description": "Fresh-session resume when the locked resource plan contains image rows.",
|
"description": "Fresh-session resume when the locked resource plan contains image rows.",
|
||||||
@@ -632,7 +735,7 @@
|
|||||||
"stage.generate.executor.image"
|
"stage.generate.executor.image"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 83890
|
"max_tokens": 89000
|
||||||
},
|
},
|
||||||
"stage.generate.topic-research": {
|
"stage.generate.topic-research": {
|
||||||
"description": "Gap-targeted factual intake stage.",
|
"description": "Gap-targeted factual intake stage.",
|
||||||
@@ -640,15 +743,15 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/workflows/stages/topic-research.md"
|
"skills/ppt-master/workflows/stages/topic-research.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 1800
|
"max_tokens": 2000
|
||||||
},
|
},
|
||||||
"profile.generate.beautify-pptx": {
|
"profile.generate.beautify-pptx": {
|
||||||
"description": "Strict 1:1 wording, order, and page-count generation profile.",
|
"description": "Strict 1:1 wording, order, and page-count profile with Default and explicit Quick branches.",
|
||||||
"scope": "incremental",
|
"scope": "incremental",
|
||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/workflows/profiles/beautify-pptx.md"
|
"skills/ppt-master/workflows/profiles/beautify-pptx.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 7007
|
"max_tokens": 7750
|
||||||
},
|
},
|
||||||
"stage.generate.refine-spec": {
|
"stage.generate.refine-spec": {
|
||||||
"description": "Explicit post-confirmation spec refinement runbook.",
|
"description": "Explicit post-confirmation spec refinement runbook.",
|
||||||
@@ -656,7 +759,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/workflows/stages/refine-spec.md"
|
"skills/ppt-master/workflows/stages/refine-spec.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 1600
|
"max_tokens": 1500
|
||||||
},
|
},
|
||||||
"stage.generate.verify-charts": {
|
"stage.generate.verify-charts": {
|
||||||
"description": "Conditional chart-calibration gate.",
|
"description": "Conditional chart-calibration gate.",
|
||||||
@@ -664,7 +767,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/workflows/stages/verify-charts.md"
|
"skills/ppt-master/workflows/stages/verify-charts.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 6250
|
"max_tokens": 7000
|
||||||
},
|
},
|
||||||
"stage.generate.chart-authoring": {
|
"stage.generate.chart-authoring": {
|
||||||
"description": "Conditional visualization-library authoring guidance.",
|
"description": "Conditional visualization-library authoring guidance.",
|
||||||
@@ -673,7 +776,7 @@
|
|||||||
"skills/ppt-master/templates/charts/README.md",
|
"skills/ppt-master/templates/charts/README.md",
|
||||||
"skills/ppt-master/templates/charts/CHART_STYLE_GUIDE.md"
|
"skills/ppt-master/templates/charts/CHART_STYLE_GUIDE.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 12500
|
"max_tokens": 10000
|
||||||
},
|
},
|
||||||
"stage.generate.visual-review": {
|
"stage.generate.visual-review": {
|
||||||
"description": "Explicit visual-review stage plus its role reference.",
|
"description": "Explicit visual-review stage plus its role reference.",
|
||||||
@@ -682,7 +785,7 @@
|
|||||||
"skills/ppt-master/workflows/stages/visual-review.md",
|
"skills/ppt-master/workflows/stages/visual-review.md",
|
||||||
"skills/ppt-master/references/visual-review.md"
|
"skills/ppt-master/references/visual-review.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 6600
|
"max_tokens": 7750
|
||||||
},
|
},
|
||||||
"stage.generate.live-preview": {
|
"stage.generate.live-preview": {
|
||||||
"description": "Live preview and annotation stage.",
|
"description": "Live preview and annotation stage.",
|
||||||
@@ -690,7 +793,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/workflows/stages/live-preview.md"
|
"skills/ppt-master/workflows/stages/live-preview.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 2700
|
"max_tokens": 3000
|
||||||
},
|
},
|
||||||
"stage.generate.customize-animations": {
|
"stage.generate.customize-animations": {
|
||||||
"description": "Object-animation stage plus animation resolution rules.",
|
"description": "Object-animation stage plus animation resolution rules.",
|
||||||
@@ -702,7 +805,7 @@
|
|||||||
"skills/ppt-master/scripts/docs/pptx-transitions.md",
|
"skills/ppt-master/scripts/docs/pptx-transitions.md",
|
||||||
"skills/ppt-master/scripts/docs/svg-pipeline.md"
|
"skills/ppt-master/scripts/docs/svg-pipeline.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 31324
|
"max_tokens": 35000
|
||||||
},
|
},
|
||||||
"stage.generate.video-motion-plan": {
|
"stage.generate.video-motion-plan": {
|
||||||
"description": "Conditional resolved animation-to-video handoff contract.",
|
"description": "Conditional resolved animation-to-video handoff contract.",
|
||||||
@@ -710,7 +813,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/scripts/docs/video-motion-plan.md"
|
"skills/ppt-master/scripts/docs/video-motion-plan.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 1000
|
"max_tokens": 1250
|
||||||
},
|
},
|
||||||
"stage.generate.animation-options": {
|
"stage.generate.animation-options": {
|
||||||
"description": "Conditional deck-wide transition, auto-advance, and entrance-animation options.",
|
"description": "Conditional deck-wide transition, auto-advance, and entrance-animation options.",
|
||||||
@@ -718,7 +821,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/references/animations.md"
|
"skills/ppt-master/references/animations.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 8037
|
"max_tokens": 9000
|
||||||
},
|
},
|
||||||
"stage.shared.generate-audio": {
|
"stage.shared.generate-audio": {
|
||||||
"description": "Shared narration-audio stage.",
|
"description": "Shared narration-audio stage.",
|
||||||
@@ -726,7 +829,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/workflows/stages/generate-audio.md"
|
"skills/ppt-master/workflows/stages/generate-audio.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 5375
|
"max_tokens": 6000
|
||||||
},
|
},
|
||||||
"governance.failure-recovery": {
|
"governance.failure-recovery": {
|
||||||
"description": "Global stop, retry, and resume policy.",
|
"description": "Global stop, retry, and resume policy.",
|
||||||
@@ -734,7 +837,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/workflows/governance/failure-recovery.md"
|
"skills/ppt-master/workflows/governance/failure-recovery.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 2829
|
"max_tokens": 3250
|
||||||
},
|
},
|
||||||
"stage.shared.conversion-reference": {
|
"stage.shared.conversion-reference": {
|
||||||
"description": "Conditional source-conversion and import compatibility reference.",
|
"description": "Conditional source-conversion and import compatibility reference.",
|
||||||
@@ -742,7 +845,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/scripts/docs/conversion.md"
|
"skills/ppt-master/scripts/docs/conversion.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 6200
|
"max_tokens": 6750
|
||||||
},
|
},
|
||||||
"stage.shared.troubleshooting": {
|
"stage.shared.troubleshooting": {
|
||||||
"description": "Conditional troubleshooting reference for generation failures.",
|
"description": "Conditional troubleshooting reference for generation failures.",
|
||||||
@@ -750,7 +853,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/scripts/docs/troubleshooting.md"
|
"skills/ppt-master/scripts/docs/troubleshooting.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 800
|
"max_tokens": 1000
|
||||||
},
|
},
|
||||||
"stage.create-template.authored-preset": {
|
"stage.create-template.authored-preset": {
|
||||||
"description": "Conditional native preset and Boolean shape authoring reference for Create Layout/Create Deck.",
|
"description": "Conditional native preset and Boolean shape authoring reference for Create Layout/Create Deck.",
|
||||||
@@ -758,7 +861,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/references/native-shape-authoring.md"
|
"skills/ppt-master/references/native-shape-authoring.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 4500
|
"max_tokens": 5000
|
||||||
},
|
},
|
||||||
"route.generate.planning-template": {
|
"route.generate.planning-template": {
|
||||||
"description": "Generate-PPTX planning context after an explicit template workspace is installed.",
|
"description": "Generate-PPTX planning context after an explicit template workspace is installed.",
|
||||||
@@ -768,7 +871,7 @@
|
|||||||
"stage.generate.strategist.template"
|
"stage.generate.strategist.template"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 65197
|
"max_tokens": 73000
|
||||||
},
|
},
|
||||||
"route.generate.planning-template-ai": {
|
"route.generate.planning-template-ai": {
|
||||||
"description": "Explicit template workspace plus confirmed AI-image planning.",
|
"description": "Explicit template workspace plus confirmed AI-image planning.",
|
||||||
@@ -778,7 +881,7 @@
|
|||||||
"route.generate.planning-ai"
|
"route.generate.planning-ai"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"max_tokens": 81175
|
"max_tokens": 88000
|
||||||
},
|
},
|
||||||
"stage.generate.executor.chart": {
|
"stage.generate.executor.chart": {
|
||||||
"description": "Conditional chart/table page execution rules.",
|
"description": "Conditional chart/table page execution rules.",
|
||||||
@@ -786,7 +889,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/references/executor-chart.md"
|
"skills/ppt-master/references/executor-chart.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 3100
|
"max_tokens": 3500
|
||||||
},
|
},
|
||||||
"stage.generate.strategist.image": {
|
"stage.generate.strategist.image": {
|
||||||
"description": "Conditional Strategist image/formula policy with the always-read layout math and compact composition vocabulary.",
|
"description": "Conditional Strategist image/formula policy with the always-read layout math and compact composition vocabulary.",
|
||||||
@@ -796,7 +899,7 @@
|
|||||||
"skills/ppt-master/references/image-layout-spec.md",
|
"skills/ppt-master/references/image-layout-spec.md",
|
||||||
"skills/ppt-master/references/image-layout-patterns.md"
|
"skills/ppt-master/references/image-layout-patterns.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 9852
|
"max_tokens": 11000
|
||||||
},
|
},
|
||||||
"stage.generate.strategist.template": {
|
"stage.generate.strategist.template": {
|
||||||
"description": "Conditional Strategist module for an explicitly installed template workspace.",
|
"description": "Conditional Strategist module for an explicitly installed template workspace.",
|
||||||
@@ -804,7 +907,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/references/strategist-template.md"
|
"skills/ppt-master/references/strategist-template.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 2100
|
"max_tokens": 3000
|
||||||
},
|
},
|
||||||
"stage.generate.executor.native-data": {
|
"stage.generate.executor.native-data": {
|
||||||
"description": "Conditional preset-pattern and native chart/table metadata interface.",
|
"description": "Conditional preset-pattern and native chart/table metadata interface.",
|
||||||
@@ -812,7 +915,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/references/native-data-interface.md"
|
"skills/ppt-master/references/native-data-interface.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 6200
|
"max_tokens": 6750
|
||||||
},
|
},
|
||||||
"stage.generate.executor.image": {
|
"stage.generate.executor.image": {
|
||||||
"description": "Conditional image embedding and execution rules with the always-read layout math and compact composition vocabulary.",
|
"description": "Conditional image embedding and execution rules with the always-read layout math and compact composition vocabulary.",
|
||||||
@@ -823,7 +926,7 @@
|
|||||||
"skills/ppt-master/references/image-layout-patterns.md",
|
"skills/ppt-master/references/image-layout-patterns.md",
|
||||||
"skills/ppt-master/references/svg-image-embedding.md"
|
"skills/ppt-master/references/svg-image-embedding.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 11666
|
"max_tokens": 13000
|
||||||
},
|
},
|
||||||
"stage.generate.executor.web-image": {
|
"stage.generate.executor.web-image": {
|
||||||
"description": "Conditional sourced-image attribution layered on image execution.",
|
"description": "Conditional sourced-image attribution layered on image execution.",
|
||||||
@@ -834,7 +937,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/references/executor-web-image.md"
|
"skills/ppt-master/references/executor-web-image.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 18900
|
"max_tokens": 14000
|
||||||
},
|
},
|
||||||
"stage.generate.executor.notes": {
|
"stage.generate.executor.notes": {
|
||||||
"description": "Post-SVG speaker-notes generation rules.",
|
"description": "Post-SVG speaker-notes generation rules.",
|
||||||
@@ -842,7 +945,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/references/executor-notes.md"
|
"skills/ppt-master/references/executor-notes.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 900
|
"max_tokens": 1000
|
||||||
},
|
},
|
||||||
"stage.generate.executor.native-shape": {
|
"stage.generate.executor.native-shape": {
|
||||||
"description": "Stock PowerPoint shape selection and Boolean materialization authority, always included in Generate Executor contexts.",
|
"description": "Stock PowerPoint shape selection and Boolean materialization authority, always included in Generate Executor contexts.",
|
||||||
@@ -850,7 +953,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/references/native-shape-authoring.md"
|
"skills/ppt-master/references/native-shape-authoring.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 4500
|
"max_tokens": 5000
|
||||||
},
|
},
|
||||||
"stage.shared.svg-effects": {
|
"stage.shared.svg-effects": {
|
||||||
"description": "Advanced SVG paint, effects, transforms, and geometry authority, always included in Generate Executor contexts and conditionally loaded by other SVG-authoring routes.",
|
"description": "Advanced SVG paint, effects, transforms, and geometry authority, always included in Generate Executor contexts and conditionally loaded by other SVG-authoring routes.",
|
||||||
@@ -858,7 +961,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"skills/ppt-master/references/svg-effects.md"
|
"skills/ppt-master/references/svg-effects.md"
|
||||||
],
|
],
|
||||||
"max_tokens": 13337
|
"max_tokens": 15000
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"duplicates": {
|
"duplicates": {
|
||||||
@@ -928,10 +1031,15 @@
|
|||||||
"to": "skills/ppt-master/workflows/routing.md"
|
"to": "skills/ppt-master/workflows/routing.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"concern": "generate-workflow",
|
"concern": "default-generate-workflow",
|
||||||
"from": "skills/ppt-master/workflows/routing.md",
|
"from": "skills/ppt-master/workflows/routing.md",
|
||||||
"to": "skills/ppt-master/workflows/generate-pptx.md"
|
"to": "skills/ppt-master/workflows/generate-pptx.md"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"concern": "quick-generate-workflow",
|
||||||
|
"from": "skills/ppt-master/workflows/routing.md",
|
||||||
|
"to": "skills/ppt-master/workflows/profiles/quick-generate.md"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"concern": "failure-policy",
|
"concern": "failure-policy",
|
||||||
"from": "skills/ppt-master/workflows/generate-pptx.md",
|
"from": "skills/ppt-master/workflows/generate-pptx.md",
|
||||||
@@ -947,6 +1055,11 @@
|
|||||||
"from": "skills/ppt-master/workflows/generate-pptx.md",
|
"from": "skills/ppt-master/workflows/generate-pptx.md",
|
||||||
"to": "skills/ppt-master/workflows/stages/apply-template-workspace.md"
|
"to": "skills/ppt-master/workflows/stages/apply-template-workspace.md"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"concern": "quick-template-application",
|
||||||
|
"from": "skills/ppt-master/workflows/profiles/quick-generate.md",
|
||||||
|
"to": "skills/ppt-master/workflows/stages/apply-template-workspace.md"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"concern": "template-model",
|
"concern": "template-model",
|
||||||
"from": "skills/ppt-master/workflows/stages/apply-template-workspace.md",
|
"from": "skills/ppt-master/workflows/stages/apply-template-workspace.md",
|
||||||
@@ -957,6 +1070,11 @@
|
|||||||
"from": "skills/ppt-master/workflows/stages/apply-template-workspace.md",
|
"from": "skills/ppt-master/workflows/stages/apply-template-workspace.md",
|
||||||
"to": "skills/ppt-master/templates/brands/README.md"
|
"to": "skills/ppt-master/templates/brands/README.md"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"concern": "template-kind-schema",
|
||||||
|
"from": "skills/ppt-master/workflows/stages/apply-template-workspace.md",
|
||||||
|
"to": "skills/ppt-master/templates/styles/README.md"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"concern": "template-kind-schema",
|
"concern": "template-kind-schema",
|
||||||
"from": "skills/ppt-master/workflows/stages/apply-template-workspace.md",
|
"from": "skills/ppt-master/workflows/stages/apply-template-workspace.md",
|
||||||
@@ -1229,10 +1347,6 @@
|
|||||||
"glob": "skills/ppt-master/references/shared-standards.md",
|
"glob": "skills/ppt-master/references/shared-standards.md",
|
||||||
"reason": "Compatibility router only; runtime load sets select the core and triggered modules directly."
|
"reason": "Compatibility router only; runtime load sets select the core and triggered modules directly."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"glob": "skills/ppt-master/scripts/docs/chart-recall.md",
|
|
||||||
"reason": "Maintainer command contract; Strategist core carries the complete runtime recall procedure."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"glob": "skills/ppt-master/templates/scaffolds/*.md",
|
"glob": "skills/ppt-master/templates/scaffolds/*.md",
|
||||||
"reason": "Optional versioned tool-copy assets; normal Generate authoring reads the reference guides and writes complete artifacts from scratch."
|
"reason": "Optional versioned tool-copy assets; normal Generate authoring reads the reference guides and writes complete artifacts from scratch."
|
||||||
|
|||||||
@@ -1,25 +1,28 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Register a brand / layout / deck template into the global template index.
|
"""Register a brand / style / layout / deck template in the global index.
|
||||||
|
|
||||||
Three kinds, three workspace roots, three index files. The shared model lives
|
Four kinds, four workspace roots, four index files. The shared model lives
|
||||||
in ``templates/README.md``; each kind's schema lives in its directory README:
|
in ``templates/README.md``; each kind's schema lives in its directory README:
|
||||||
|
|
||||||
| --kind | Workspace roots | Index file |
|
| --kind | Workspace roots | Index file |
|
||||||
|---------|-------------------------|-------------------------------|
|
|---------|-------------------------|-------------------------------|
|
||||||
| brand | ``templates/brands/`` | ``brands_index.json`` |
|
| brand | ``templates/brands/`` | ``brands_index.json`` |
|
||||||
|
| style | ``templates/styles/`` | ``styles_index.json`` |
|
||||||
| layout | ``templates/layouts/`` | ``layouts_index.json`` |
|
| layout | ``templates/layouts/`` | ``layouts_index.json`` |
|
||||||
| deck | ``templates/decks/`` | ``decks_index.json`` |
|
| deck | ``templates/decks/`` | ``decks_index.json`` |
|
||||||
|
|
||||||
Current workspaces keep ``design_spec.md`` and any SVG roster under
|
Current workspaces keep ``design_spec.md`` and any SVG roster under
|
||||||
``<workspace>/templates/``. Assets live in optional ``images/`` / ``icons/``
|
``<workspace>/templates/``. Assets live in optional ``images/`` / ``icons/``
|
||||||
directories. Explicitly generated review artifacts go to the optional, ignored
|
directories. Explicitly generated review artifacts go to the optional, ignored
|
||||||
``exports/`` directory. Legacy flat roots remain readable.
|
``exports/`` directory. Legacy flat roots remain readable for Brand/Layout/Deck;
|
||||||
|
Style uses only the current nested one-file contract.
|
||||||
|
|
||||||
Index entry schemas (the JSON file is the single source of truth — README
|
Index entry schemas (the JSON file is the single source of truth — README
|
||||||
files describe the kind and usage in prose but do **not** enumerate templates;
|
files describe the kind and usage in prose but do **not** enumerate templates;
|
||||||
discovery happens exclusively against the index file):
|
discovery happens exclusively against the index file):
|
||||||
|
|
||||||
- brand: ``{ summary, primary_color }``
|
- brand: ``{ summary, primary_color }``
|
||||||
|
- style: ``{ summary, keywords[] }``
|
||||||
- layout: ``{ summary, canvas_format, page_count, page_types[] }``
|
- layout: ``{ summary, canvas_format, page_count, page_types[] }``
|
||||||
- deck: ``{ summary, canvas_format, page_count, primary_color }``
|
- deck: ``{ summary, canvas_format, page_count, primary_color }``
|
||||||
|
|
||||||
@@ -28,15 +31,17 @@ Usage::
|
|||||||
python3 scripts/register_template.py <id> --kind deck # default kind=deck
|
python3 scripts/register_template.py <id> --kind deck # default kind=deck
|
||||||
python3 scripts/register_template.py <id> --kind layout
|
python3 scripts/register_template.py <id> --kind layout
|
||||||
python3 scripts/register_template.py <id> --kind brand
|
python3 scripts/register_template.py <id> --kind brand
|
||||||
|
python3 scripts/register_template.py <id> --kind style
|
||||||
python3 scripts/register_template.py --rebuild-all --kind deck
|
python3 scripts/register_template.py --rebuild-all --kind deck
|
||||||
python3 scripts/register_template.py <id> --dry-run
|
python3 scripts/register_template.py <id> --dry-run
|
||||||
|
|
||||||
``--rebuild-all`` rebuilds every entry from scratch within the chosen kind;
|
``--rebuild-all`` rebuilds every entry from scratch within the chosen kind;
|
||||||
recommended for repairing index drift across many templates at once.
|
recommended for repairing index drift across many templates at once.
|
||||||
|
|
||||||
Project-scoped Brand workspaces are validated, not registered, through
|
Project-scoped Brand and Style workspaces are validated, not registered,
|
||||||
``svg_quality_checker.py <workspace>/templates --template-mode``. That entry
|
through ``svg_quality_checker.py <workspace>/templates --template-mode``. That
|
||||||
reuses :func:`validate_brand_workspace`, so the Brand schema has one authority.
|
entry reuses :func:`validate_brand_workspace` or
|
||||||
|
:func:`validate_style_workspace`, so each schema has one authority.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -73,6 +78,12 @@ KIND_CONFIG = {
|
|||||||
"id_key": "brand_id",
|
"id_key": "brand_id",
|
||||||
"needs_svg_roster": False,
|
"needs_svg_roster": False,
|
||||||
},
|
},
|
||||||
|
"style": {
|
||||||
|
"dir": TEMPLATES_DIR / "styles",
|
||||||
|
"index": TEMPLATES_DIR / "styles" / "styles_index.json",
|
||||||
|
"id_key": "style_id",
|
||||||
|
"needs_svg_roster": False,
|
||||||
|
},
|
||||||
"layout": {
|
"layout": {
|
||||||
"dir": TEMPLATES_DIR / "layouts",
|
"dir": TEMPLATES_DIR / "layouts",
|
||||||
"index": TEMPLATES_DIR / "layouts" / "layouts_index.json",
|
"index": TEMPLATES_DIR / "layouts" / "layouts_index.json",
|
||||||
@@ -109,7 +120,130 @@ _BRAND_PROVENANCE_VALUES = {"fact", "approx", "user"}
|
|||||||
_BRAND_ASSET_REF_RE = re.compile(
|
_BRAND_ASSET_REF_RE = re.compile(
|
||||||
r"`((?:\.\./)+(?:images|icons)/[^`]+)`"
|
r"`((?:\.\./)+(?:images|icons)/[^`]+)`"
|
||||||
)
|
)
|
||||||
|
_STYLE_REQUIRED_SECTIONS = (
|
||||||
|
("I", "Style Overview"),
|
||||||
|
("II", "Communication Method"),
|
||||||
|
("III", "Page Role Vocabulary"),
|
||||||
|
("IV", "Evidence & Data Expression"),
|
||||||
|
("V", "Visual System Defaults"),
|
||||||
|
("VI", "Image & Icon Direction"),
|
||||||
|
("VII", "Review Focus"),
|
||||||
|
)
|
||||||
|
_STYLE_FORBIDDEN_SECTIONS = (
|
||||||
|
"Brand Overview",
|
||||||
|
"Template Overview",
|
||||||
|
"Color Scheme",
|
||||||
|
"Typography",
|
||||||
|
"Logo",
|
||||||
|
"Voice & Tone",
|
||||||
|
"Icon Style",
|
||||||
|
"Assets",
|
||||||
|
"Signature Design Elements",
|
||||||
|
"Page Roster",
|
||||||
|
"Placeholder Overrides",
|
||||||
|
)
|
||||||
|
_STYLE_REQUIRED_FIELDS = {
|
||||||
|
"I. Style Overview": (
|
||||||
|
"Style Name",
|
||||||
|
"Best Fit",
|
||||||
|
"Reusable Intent",
|
||||||
|
"Sources",
|
||||||
|
),
|
||||||
|
"II. Communication Method": (
|
||||||
|
"Argument Flow",
|
||||||
|
"Page Message Discipline",
|
||||||
|
"Claim Discipline",
|
||||||
|
),
|
||||||
|
"IV. Evidence & Data Expression": (
|
||||||
|
"Argument Trace",
|
||||||
|
"Charts",
|
||||||
|
"Tables",
|
||||||
|
"Sources",
|
||||||
|
"Native Editability",
|
||||||
|
),
|
||||||
|
"V. Visual System Defaults": (
|
||||||
|
"Composition",
|
||||||
|
"Density",
|
||||||
|
"Decoration",
|
||||||
|
"Color Behavior",
|
||||||
|
"Typography Character",
|
||||||
|
),
|
||||||
|
"VI. Image & Icon Direction": (
|
||||||
|
"Image Usage",
|
||||||
|
"Image Treatment",
|
||||||
|
"Icon Treatment",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
_STYLE_CUSTOM_FIELDS = (
|
||||||
|
(
|
||||||
|
"II. Communication Method",
|
||||||
|
"Preferred Mode",
|
||||||
|
"Mode Behavior",
|
||||||
|
"Mode References",
|
||||||
|
SKILL_DIR / "references" / "modes",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"V. Visual System Defaults",
|
||||||
|
"Preferred Visual Style",
|
||||||
|
"Visual Style Behavior",
|
||||||
|
"Visual Style References",
|
||||||
|
SKILL_DIR / "references" / "visual-styles",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"VI. Image & Icon Direction",
|
||||||
|
"Preferred Image Rendering",
|
||||||
|
"Image Rendering Behavior",
|
||||||
|
"Image Rendering References",
|
||||||
|
SKILL_DIR / "references" / "image-renderings",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
_STYLE_FORBIDDEN_FIELDS = (
|
||||||
|
"Brand Overview",
|
||||||
|
"Template Overview",
|
||||||
|
"Color Scheme",
|
||||||
|
"Typography",
|
||||||
|
"Signature Design Elements",
|
||||||
|
"Page Roster",
|
||||||
|
"Placeholder Overrides",
|
||||||
|
"Target Audience",
|
||||||
|
"Communication Objective",
|
||||||
|
"Desired Outcome",
|
||||||
|
"Core Message",
|
||||||
|
"Delivery Context",
|
||||||
|
"Artifact Afterlife",
|
||||||
|
"Application Context",
|
||||||
|
"Content Outline",
|
||||||
|
"Page Count",
|
||||||
|
"Page Types",
|
||||||
|
"Page Order",
|
||||||
|
"Page Sequence",
|
||||||
|
"Canvas Format",
|
||||||
|
"Canvas Width",
|
||||||
|
"Canvas Height",
|
||||||
|
"Canvas ViewBox",
|
||||||
|
"Replication Mode",
|
||||||
|
"Native Structure Mode",
|
||||||
|
"Master",
|
||||||
|
"Layout",
|
||||||
|
"Placeholders",
|
||||||
|
"Page Assignment",
|
||||||
|
"Icon Inventory",
|
||||||
|
"Image Resources",
|
||||||
|
"Primary Color",
|
||||||
|
"Logo",
|
||||||
|
"Voice & Tone",
|
||||||
|
"Icon Style",
|
||||||
|
"Assets",
|
||||||
|
)
|
||||||
|
_STYLE_ALLOWED_FRONTMATTER_FIELDS = frozenset({
|
||||||
|
"style_id",
|
||||||
|
"kind",
|
||||||
|
"summary",
|
||||||
|
"keywords",
|
||||||
|
})
|
||||||
|
_STYLE_REVIEW_TRIGGER_MARKER = "<!-- visual-review-trigger: explicit-user-only -->"
|
||||||
_HEX_COLOR_RE = re.compile(r"#[0-9A-Fa-f]{6}")
|
_HEX_COLOR_RE = re.compile(r"#[0-9A-Fa-f]{6}")
|
||||||
|
_PORTABLE_STYLE_ID_RE = re.compile(r"^\w[\w.-]*$")
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -240,6 +374,53 @@ def _numbered_section(body: str, title: str) -> str | None:
|
|||||||
return match.group(0) if match else None
|
return match.group(0) if match else None
|
||||||
|
|
||||||
|
|
||||||
|
def _markdown_subsection(body: str, title: str) -> str | None:
|
||||||
|
match = re.search(
|
||||||
|
rf"^###\s+{re.escape(title)}\s*$.*?(?=^#{{2,3}}\s+|\Z)",
|
||||||
|
body,
|
||||||
|
re.MULTILINE | re.DOTALL,
|
||||||
|
)
|
||||||
|
return match.group(0) if match else None
|
||||||
|
|
||||||
|
|
||||||
|
def _markdown_table_rows(section: str) -> list[list[str]]:
|
||||||
|
rows: list[list[str]] = []
|
||||||
|
for line in section.splitlines():
|
||||||
|
if not line.lstrip().startswith("|"):
|
||||||
|
continue
|
||||||
|
cells = [cell.strip() for cell in line.strip().strip("|").split("|")]
|
||||||
|
if not cells or all(re.fullmatch(r":?-{3,}:?", cell) for cell in cells):
|
||||||
|
continue
|
||||||
|
rows.append(cells)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def _style_value_is_substantive(value: str | None) -> bool:
|
||||||
|
if value is None:
|
||||||
|
return False
|
||||||
|
cleaned = _clean_field_value(value)
|
||||||
|
if not cleaned or re.fullmatch(r"<[^>]+>", cleaned):
|
||||||
|
return False
|
||||||
|
return cleaned.casefold() not in {"tbd", "todo", "n/a", "none", "-", "—"}
|
||||||
|
|
||||||
|
|
||||||
|
def _style_field_is_declared(body: str, label: str) -> bool:
|
||||||
|
escaped = re.escape(label)
|
||||||
|
return bool(
|
||||||
|
re.search(
|
||||||
|
rf"^\s*(?:>\s*)?(?:[-*]\s*)?"
|
||||||
|
rf"\*{{0,2}}{escaped}\*{{0,2}}\s*[::]",
|
||||||
|
body,
|
||||||
|
re.MULTILINE | re.IGNORECASE,
|
||||||
|
)
|
||||||
|
or re.search(
|
||||||
|
rf"^\|\s*\*{{0,2}}{escaped}\*{{0,2}}\s*\|",
|
||||||
|
body,
|
||||||
|
re.MULTILINE | re.IGNORECASE,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _validate_brand_spec(
|
def _validate_brand_spec(
|
||||||
expected_template_id: str | None,
|
expected_template_id: str | None,
|
||||||
template_root: Path,
|
template_root: Path,
|
||||||
@@ -376,6 +557,327 @@ def _validate_brand_spec(
|
|||||||
raise SpecParseError(f"invalid brand specification:\n{details}")
|
raise SpecParseError(f"invalid brand specification:\n{details}")
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_style_spec(
|
||||||
|
expected_template_id: str | None,
|
||||||
|
template_root: Path,
|
||||||
|
template_dir: Path,
|
||||||
|
frontmatter: dict,
|
||||||
|
body: str,
|
||||||
|
) -> None:
|
||||||
|
"""Reject Style workspaces outside the roster-free method contract."""
|
||||||
|
errors: list[str] = []
|
||||||
|
|
||||||
|
raw_style_id = frontmatter.get("style_id")
|
||||||
|
declared_id = raw_style_id.strip() if isinstance(raw_style_id, str) else ""
|
||||||
|
if not declared_id:
|
||||||
|
errors.append("frontmatter style_id must be a non-empty string")
|
||||||
|
else:
|
||||||
|
if (
|
||||||
|
_PORTABLE_STYLE_ID_RE.fullmatch(declared_id) is None
|
||||||
|
or declared_id in {".", ".."}
|
||||||
|
or declared_id.endswith(".")
|
||||||
|
):
|
||||||
|
errors.append(
|
||||||
|
"frontmatter style_id must be a filesystem-safe portable slug"
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
expected_template_id is not None
|
||||||
|
and declared_id != expected_template_id
|
||||||
|
):
|
||||||
|
errors.append(
|
||||||
|
"frontmatter style_id must match directory "
|
||||||
|
f"{expected_template_id!r}, got {declared_id!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
raw_kind = frontmatter.get("kind")
|
||||||
|
declared_kind = raw_kind.strip() if isinstance(raw_kind, str) else ""
|
||||||
|
if declared_kind != "style":
|
||||||
|
errors.append(
|
||||||
|
"frontmatter kind must be 'style', "
|
||||||
|
f"got {declared_kind!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
raw_summary = frontmatter.get("summary")
|
||||||
|
if not isinstance(raw_summary, str) or not _style_value_is_substantive(
|
||||||
|
raw_summary
|
||||||
|
):
|
||||||
|
errors.append("frontmatter summary must be a non-empty string")
|
||||||
|
|
||||||
|
non_string_fields = [key for key in frontmatter if not isinstance(key, str)]
|
||||||
|
if non_string_fields:
|
||||||
|
errors.append("style frontmatter field names must be strings")
|
||||||
|
unexpected_fields = sorted(
|
||||||
|
key
|
||||||
|
for key in frontmatter
|
||||||
|
if isinstance(key, str) and key not in _STYLE_ALLOWED_FRONTMATTER_FIELDS
|
||||||
|
)
|
||||||
|
if unexpected_fields:
|
||||||
|
errors.append(
|
||||||
|
"style frontmatter contains unsupported field(s): "
|
||||||
|
+ ", ".join(unexpected_fields)
|
||||||
|
)
|
||||||
|
|
||||||
|
keywords = frontmatter.get("keywords")
|
||||||
|
if (
|
||||||
|
not isinstance(keywords, list)
|
||||||
|
or not 3 <= len(keywords) <= 5
|
||||||
|
or not all(
|
||||||
|
isinstance(item, str) and _style_value_is_substantive(item)
|
||||||
|
for item in keywords
|
||||||
|
)
|
||||||
|
):
|
||||||
|
errors.append(
|
||||||
|
"frontmatter keywords must contain 3-5 non-empty strings"
|
||||||
|
)
|
||||||
|
elif len({item.strip().casefold() for item in keywords}) != len(keywords):
|
||||||
|
errors.append("frontmatter keywords must be unique")
|
||||||
|
|
||||||
|
unexpected_source_entries = sorted(
|
||||||
|
path.relative_to(template_dir).as_posix()
|
||||||
|
+ ("/" if path.is_dir() else "")
|
||||||
|
for path in template_dir.rglob("*")
|
||||||
|
if path.relative_to(template_dir).as_posix() != "design_spec.md"
|
||||||
|
)
|
||||||
|
if unexpected_source_entries:
|
||||||
|
errors.append(
|
||||||
|
"style workspaces must contain only templates/design_spec.md; "
|
||||||
|
"unexpected template entry(s): "
|
||||||
|
+ ", ".join(unexpected_source_entries)
|
||||||
|
)
|
||||||
|
|
||||||
|
if expected_template_id is not None:
|
||||||
|
unexpected_workspace_entries = sorted(
|
||||||
|
path.relative_to(template_root).as_posix()
|
||||||
|
+ ("/" if path.is_dir() else "")
|
||||||
|
for path in template_root.rglob("*")
|
||||||
|
if path.relative_to(template_root).as_posix()
|
||||||
|
not in {"templates", "templates/design_spec.md"}
|
||||||
|
)
|
||||||
|
if unexpected_workspace_entries:
|
||||||
|
errors.append(
|
||||||
|
"library Style workspaces must contain only "
|
||||||
|
"templates/design_spec.md; unexpected workspace entry(s): "
|
||||||
|
+ ", ".join(unexpected_workspace_entries)
|
||||||
|
)
|
||||||
|
|
||||||
|
h1_headings = re.findall(r"^#\s+(.+?)\s*$", body, re.MULTILINE)
|
||||||
|
if len(h1_headings) != 1:
|
||||||
|
errors.append(
|
||||||
|
"style body must contain exactly one document-title H1; got "
|
||||||
|
f"{len(h1_headings)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_headings = [
|
||||||
|
f"{numeral}. {title}" for numeral, title in _STYLE_REQUIRED_SECTIONS
|
||||||
|
]
|
||||||
|
actual_headings = re.findall(r"^##\s+(.+?)\s*$", body, re.MULTILINE)
|
||||||
|
if actual_headings != expected_headings:
|
||||||
|
errors.append(
|
||||||
|
"style body must contain exactly the required I-VII sections in "
|
||||||
|
"order; got: " + (", ".join(actual_headings) or "none")
|
||||||
|
)
|
||||||
|
|
||||||
|
nested_headings = re.findall(
|
||||||
|
r"^(#{3,6})\s+(.+?)\s*$",
|
||||||
|
body,
|
||||||
|
re.MULTILINE,
|
||||||
|
)
|
||||||
|
unexpected_nested_headings = [
|
||||||
|
f"{marks} {title}"
|
||||||
|
for marks, title in nested_headings
|
||||||
|
if len(marks) != 3
|
||||||
|
or title not in {"Fallback Color Scheme", "Fallback Typography"}
|
||||||
|
]
|
||||||
|
if unexpected_nested_headings:
|
||||||
|
errors.append(
|
||||||
|
"style body contains unsupported nested heading(s): "
|
||||||
|
+ ", ".join(unexpected_nested_headings)
|
||||||
|
)
|
||||||
|
allowed_h3 = [title for marks, title in nested_headings if len(marks) == 3]
|
||||||
|
if len(allowed_h3) != len(set(allowed_h3)):
|
||||||
|
errors.append("style fallback subsections must not be repeated")
|
||||||
|
|
||||||
|
for title in _STYLE_FORBIDDEN_SECTIONS:
|
||||||
|
if re.search(
|
||||||
|
rf"^#{{1,6}}\s+(?:[IVX]+\.\s+)?{re.escape(title)}\s*$",
|
||||||
|
body,
|
||||||
|
re.MULTILINE,
|
||||||
|
):
|
||||||
|
errors.append(f"style scope must not declare section: {title}")
|
||||||
|
|
||||||
|
for section_title, labels in _STYLE_REQUIRED_FIELDS.items():
|
||||||
|
section_name = section_title.split(". ", 1)[1]
|
||||||
|
if _numbered_section(body, section_name) is None:
|
||||||
|
continue
|
||||||
|
for label in labels:
|
||||||
|
value = _extract_section_field(body, section_title, [label])
|
||||||
|
if not _style_value_is_substantive(value):
|
||||||
|
errors.append(
|
||||||
|
f"{section_title} must declare a non-empty {label} field"
|
||||||
|
)
|
||||||
|
|
||||||
|
role_section = _numbered_section(body, "Page Role Vocabulary") or ""
|
||||||
|
role_rows = [
|
||||||
|
row
|
||||||
|
for row in _markdown_table_rows(role_section)
|
||||||
|
if row and row[0].casefold() != "role"
|
||||||
|
]
|
||||||
|
if not any(
|
||||||
|
len(row) >= 4
|
||||||
|
and all(_style_value_is_substantive(cell) for cell in row[:4])
|
||||||
|
for row in role_rows
|
||||||
|
):
|
||||||
|
errors.append(
|
||||||
|
"III. Page Role Vocabulary must contain at least one complete "
|
||||||
|
"four-column role row"
|
||||||
|
)
|
||||||
|
|
||||||
|
for (
|
||||||
|
section_title,
|
||||||
|
preferred_label,
|
||||||
|
behavior_label,
|
||||||
|
references_label,
|
||||||
|
catalog_dir,
|
||||||
|
) in _STYLE_CUSTOM_FIELDS:
|
||||||
|
preferred = _extract_section_field(
|
||||||
|
body,
|
||||||
|
section_title,
|
||||||
|
[preferred_label],
|
||||||
|
)
|
||||||
|
behavior = _extract_section_field(
|
||||||
|
body,
|
||||||
|
section_title,
|
||||||
|
[behavior_label],
|
||||||
|
)
|
||||||
|
references = _extract_section_field(
|
||||||
|
body,
|
||||||
|
section_title,
|
||||||
|
[references_label],
|
||||||
|
)
|
||||||
|
preferred_text = _clean_field_value(preferred or "")
|
||||||
|
catalog_ids = {
|
||||||
|
path.stem
|
||||||
|
for path in catalog_dir.glob("*.md")
|
||||||
|
if not path.stem.startswith("_")
|
||||||
|
}
|
||||||
|
is_custom = bool(
|
||||||
|
re.match(r"^custom(?:\b|\s*[:—-])", preferred_text, re.IGNORECASE)
|
||||||
|
)
|
||||||
|
if is_custom and not _style_value_is_substantive(behavior):
|
||||||
|
errors.append(
|
||||||
|
f"{behavior_label} is required when {preferred_label} is custom"
|
||||||
|
)
|
||||||
|
if not is_custom and _style_value_is_substantive(behavior):
|
||||||
|
errors.append(
|
||||||
|
f"{behavior_label} is allowed only when {preferred_label} is custom"
|
||||||
|
)
|
||||||
|
if not is_custom and _style_value_is_substantive(references):
|
||||||
|
errors.append(
|
||||||
|
f"{references_label} is allowed only when "
|
||||||
|
f"{preferred_label} is custom"
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
_style_value_is_substantive(preferred)
|
||||||
|
and not is_custom
|
||||||
|
and preferred_text not in catalog_ids
|
||||||
|
):
|
||||||
|
errors.append(
|
||||||
|
f"{preferred_label} references unknown catalog id "
|
||||||
|
f"{preferred_text!r}"
|
||||||
|
)
|
||||||
|
if is_custom and _style_value_is_substantive(references):
|
||||||
|
catalog_references = [
|
||||||
|
_clean_field_value(item)
|
||||||
|
for item in (references or "").split(",")
|
||||||
|
]
|
||||||
|
if any(not item for item in catalog_references):
|
||||||
|
errors.append(
|
||||||
|
f"{references_label} must be a comma-separated list of "
|
||||||
|
"catalog ids"
|
||||||
|
)
|
||||||
|
duplicates = sorted(
|
||||||
|
item
|
||||||
|
for item in set(catalog_references)
|
||||||
|
if catalog_references.count(item) > 1
|
||||||
|
)
|
||||||
|
if duplicates:
|
||||||
|
errors.append(
|
||||||
|
f"{references_label} repeats catalog id(s): "
|
||||||
|
+ ", ".join(duplicates)
|
||||||
|
)
|
||||||
|
unknown_references = sorted(
|
||||||
|
item
|
||||||
|
for item in set(catalog_references)
|
||||||
|
if item == "custom" or item not in catalog_ids
|
||||||
|
)
|
||||||
|
if unknown_references:
|
||||||
|
errors.append(
|
||||||
|
f"{references_label} references unknown catalog id(s): "
|
||||||
|
+ ", ".join(unknown_references)
|
||||||
|
)
|
||||||
|
|
||||||
|
visual_section = _numbered_section(body, "Visual System Defaults") or ""
|
||||||
|
fallback_colors = _markdown_subsection(body, "Fallback Color Scheme")
|
||||||
|
if fallback_colors is not None:
|
||||||
|
if "### Fallback Color Scheme" not in visual_section:
|
||||||
|
errors.append("Fallback Color Scheme must appear under section V")
|
||||||
|
color_rows = [
|
||||||
|
row
|
||||||
|
for row in _markdown_table_rows(fallback_colors)
|
||||||
|
if row and row[0].casefold() != "role"
|
||||||
|
]
|
||||||
|
if not color_rows:
|
||||||
|
errors.append("Fallback Color Scheme must contain at least one row")
|
||||||
|
for row in color_rows:
|
||||||
|
if (
|
||||||
|
len(row) < 3
|
||||||
|
or not _style_value_is_substantive(row[0])
|
||||||
|
or _HEX_COLOR_RE.fullmatch(row[1].strip("` ")) is None
|
||||||
|
or not _style_value_is_substantive(row[2])
|
||||||
|
):
|
||||||
|
errors.append(
|
||||||
|
"Fallback Color Scheme rows must be Role | #RRGGBB | Purpose"
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
fallback_typography = _markdown_subsection(body, "Fallback Typography")
|
||||||
|
if fallback_typography is not None:
|
||||||
|
if "### Fallback Typography" not in visual_section:
|
||||||
|
errors.append("Fallback Typography must appear under section V")
|
||||||
|
typography_rows = [
|
||||||
|
row
|
||||||
|
for row in _markdown_table_rows(fallback_typography)
|
||||||
|
if row and row[0].casefold() != "role"
|
||||||
|
]
|
||||||
|
if not any(
|
||||||
|
len(row) >= 4
|
||||||
|
and all(_style_value_is_substantive(cell) for cell in row[:4])
|
||||||
|
for row in typography_rows
|
||||||
|
):
|
||||||
|
errors.append(
|
||||||
|
"Fallback Typography must contain at least one complete "
|
||||||
|
"four-column row"
|
||||||
|
)
|
||||||
|
|
||||||
|
review_section = _numbered_section(body, "Review Focus") or ""
|
||||||
|
if review_section.count(_STYLE_REVIEW_TRIGGER_MARKER) != 1:
|
||||||
|
errors.append(
|
||||||
|
"VII. Review Focus must contain exactly one "
|
||||||
|
f"{_STYLE_REVIEW_TRIGGER_MARKER} marker"
|
||||||
|
)
|
||||||
|
review_items = re.findall(r"^[-*]\s+(.+?)\s*$", review_section, re.MULTILINE)
|
||||||
|
if not any(_style_value_is_substantive(item) for item in review_items):
|
||||||
|
errors.append("VII. Review Focus must contain at least one check")
|
||||||
|
|
||||||
|
for label in _STYLE_FORBIDDEN_FIELDS:
|
||||||
|
if _style_field_is_declared(body, label):
|
||||||
|
errors.append(f"style scope must not declare field: {label}")
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
details = "\n".join(f" - {error}" for error in errors)
|
||||||
|
raise SpecParseError(f"invalid style specification:\n{details}")
|
||||||
|
|
||||||
|
|
||||||
def _validate_svg_template_spec(
|
def _validate_svg_template_spec(
|
||||||
kind: str,
|
kind: str,
|
||||||
template_id: str,
|
template_id: str,
|
||||||
@@ -522,6 +1024,11 @@ def _extract_entry(
|
|||||||
"""Build the index entry + extras for a single template."""
|
"""Build the index entry + extras for a single template."""
|
||||||
template_root = template_dir
|
template_root = template_dir
|
||||||
template_dir = _template_content_dir(template_root)
|
template_dir = _template_content_dir(template_root)
|
||||||
|
if kind == "style" and template_dir == template_root:
|
||||||
|
raise SpecParseError(
|
||||||
|
"Style workspaces require templates/design_spec.md; "
|
||||||
|
"legacy-flat design_spec.md is not supported"
|
||||||
|
)
|
||||||
spec_path = template_dir / "design_spec.md"
|
spec_path = template_dir / "design_spec.md"
|
||||||
|
|
||||||
frontmatter, body = _read_spec(spec_path)
|
frontmatter, body = _read_spec(spec_path)
|
||||||
@@ -534,7 +1041,8 @@ def _extract_entry(
|
|||||||
f"expected kind={kind!r} — use --kind {declared_kind} instead"
|
f"expected kind={kind!r} — use --kind {declared_kind} instead"
|
||||||
)
|
)
|
||||||
|
|
||||||
summary = (fm.get("summary") or "").strip()
|
raw_summary = fm.get("summary")
|
||||||
|
summary = raw_summary.strip() if isinstance(raw_summary, str) else ""
|
||||||
if not summary:
|
if not summary:
|
||||||
section_title = (
|
section_title = (
|
||||||
"I. Brand Overview" if kind == "brand" else "I. Template Overview"
|
"I. Brand Overview" if kind == "brand" else "I. Template Overview"
|
||||||
@@ -564,6 +1072,18 @@ def _extract_entry(
|
|||||||
summary=summary,
|
summary=summary,
|
||||||
primary_color=str(primary_color),
|
primary_color=str(primary_color),
|
||||||
)
|
)
|
||||||
|
elif kind == "style":
|
||||||
|
_validate_style_spec(
|
||||||
|
template_id,
|
||||||
|
template_root,
|
||||||
|
template_dir,
|
||||||
|
fm,
|
||||||
|
body,
|
||||||
|
)
|
||||||
|
entry = OrderedDict(
|
||||||
|
summary=summary,
|
||||||
|
keywords=[item.strip() for item in fm["keywords"]],
|
||||||
|
)
|
||||||
elif kind == "layout":
|
elif kind == "layout":
|
||||||
if template_id is None:
|
if template_id is None:
|
||||||
raise SpecParseError("layout validation requires an expected layout_id")
|
raise SpecParseError("layout validation requires an expected layout_id")
|
||||||
@@ -627,6 +1147,22 @@ def validate_brand_workspace(template_root: str | Path) -> dict:
|
|||||||
return _extract_entry("brand", None, Path(template_root))
|
return _extract_entry("brand", None, Path(template_root))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_style_workspace(template_root: str | Path) -> dict:
|
||||||
|
"""Validate a portable Style workspace without registering it.
|
||||||
|
|
||||||
|
Global library roots also enforce directory identity and the one-file
|
||||||
|
package boundary. Project roots keep their unrelated initialized-project
|
||||||
|
scaffolding out of the Style contract.
|
||||||
|
"""
|
||||||
|
root = Path(template_root)
|
||||||
|
expected_id = (
|
||||||
|
root.name
|
||||||
|
if root.resolve().parent == KIND_CONFIG["style"]["dir"].resolve()
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
return _extract_entry("style", expected_id, root)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Index / README writers
|
# Index / README writers
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -664,8 +1200,18 @@ def _enumerate_ids(kind: str) -> list[str]:
|
|||||||
|
|
||||||
|
|
||||||
def _print_completion_card(kind: str, template_id: str, entry: dict, extras: dict) -> None:
|
def _print_completion_card(kind: str, template_id: str, entry: dict, extras: dict) -> None:
|
||||||
pretty_kind = {"layout": "Layout", "deck": "Deck", "brand": "Brand"}[kind]
|
pretty_kind = {
|
||||||
dir_name = {"layout": "layouts", "deck": "decks", "brand": "brands"}[kind]
|
"layout": "Layout",
|
||||||
|
"deck": "Deck",
|
||||||
|
"brand": "Brand",
|
||||||
|
"style": "Style",
|
||||||
|
}[kind]
|
||||||
|
dir_name = {
|
||||||
|
"layout": "layouts",
|
||||||
|
"deck": "decks",
|
||||||
|
"brand": "brands",
|
||||||
|
"style": "styles",
|
||||||
|
}[kind]
|
||||||
print()
|
print()
|
||||||
print(f"## {pretty_kind} Registration Complete")
|
print(f"## {pretty_kind} Registration Complete")
|
||||||
print()
|
print()
|
||||||
@@ -682,7 +1228,7 @@ def _print_completion_card(kind: str, template_id: str, entry: dict, extras: dic
|
|||||||
print(f"**Summary**: {entry.get('summary') or '—'}")
|
print(f"**Summary**: {entry.get('summary') or '—'}")
|
||||||
print("**Index Registration**: Done")
|
print("**Index Registration**: Done")
|
||||||
print()
|
print()
|
||||||
if kind != "brand":
|
if KIND_CONFIG[kind]["needs_svg_roster"]:
|
||||||
pages = extras.get("pages") or []
|
pages = extras.get("pages") or []
|
||||||
page_prefix = extras.get("page_prefix") or ""
|
page_prefix = extras.get("page_prefix") or ""
|
||||||
preview = extras.get("preview") or ""
|
preview = extras.get("preview") or ""
|
||||||
@@ -708,7 +1254,10 @@ def _print_completion_card(kind: str, template_id: str, entry: dict, extras: dic
|
|||||||
def main() -> int:
|
def main() -> int:
|
||||||
require_skill_integrity()
|
require_skill_integrity()
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Register / refresh templates (brand / layout / deck) in the index."
|
description=(
|
||||||
|
"Register / refresh templates (brand / style / layout / deck) "
|
||||||
|
"in the index."
|
||||||
|
)
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"template_id", nargs="?",
|
"template_id", nargs="?",
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import subprocess
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from workflow_transcript import DISABLE_TRANSCRIPT_ENV
|
||||||
|
|
||||||
|
|
||||||
MIN_PORT = 1
|
MIN_PORT = 1
|
||||||
MAX_PORT = 65535
|
MAX_PORT = 65535
|
||||||
@@ -75,7 +77,15 @@ def popen_detached(
|
|||||||
caller's Job Object. ``CREATE_BREAKAWAY_FROM_JOB`` lets the local UI server
|
caller's Job Object. ``CREATE_BREAKAWAY_FROM_JOB`` lets the local UI server
|
||||||
survive after the launcher command returns; when that flag is forbidden, the
|
survive after the launcher command returns; when that flag is forbidden, the
|
||||||
function falls back to the previous detached-process flags.
|
function falls back to the previous detached-process flags.
|
||||||
|
|
||||||
|
Detached service output remains in its component log, so the child receives
|
||||||
|
the shared workflow-transcript opt-out environment flag.
|
||||||
"""
|
"""
|
||||||
|
supplied_env = kwargs.get('env')
|
||||||
|
child_env = dict(os.environ if supplied_env is None else supplied_env)
|
||||||
|
child_env[DISABLE_TRANSCRIPT_ENV] = '1'
|
||||||
|
kwargs['env'] = child_env
|
||||||
|
|
||||||
if os.name != 'nt':
|
if os.name != 'nt':
|
||||||
return subprocess.Popen(args, start_new_session=True, **kwargs)
|
return subprocess.Popen(args, start_new_session=True, **kwargs)
|
||||||
|
|
||||||
|
|||||||
+355
-61
@@ -38,7 +38,10 @@ from .xml_support import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from project_utils import CANVAS_FORMATS, validate_communication_trace
|
from project_utils import (
|
||||||
|
CANVAS_FORMATS,
|
||||||
|
validate_communication_trace,
|
||||||
|
)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("Warning: Unable to import project_utils")
|
print("Warning: Unable to import project_utils")
|
||||||
CANVAS_FORMATS = {}
|
CANVAS_FORMATS = {}
|
||||||
@@ -343,6 +346,7 @@ _NON_VISUAL_SVG_TAGS = frozenset({
|
|||||||
'title',
|
'title',
|
||||||
})
|
})
|
||||||
_BOUNDS_ATTR = 'data-pptx-bounds'
|
_BOUNDS_ATTR = 'data-pptx-bounds'
|
||||||
|
_MORPH_STAGING_ATTR = 'data-pptx-morph-staging'
|
||||||
_BOUNDS_OVERFLOW_TOLERANCE = 1.0
|
_BOUNDS_OVERFLOW_TOLERANCE = 1.0
|
||||||
_BOUNDS_OVERFLOW_ERROR_RATIO = 0.05
|
_BOUNDS_OVERFLOW_ERROR_RATIO = 0.05
|
||||||
_PARAGRAPH_LINE_GAP_MIN_RATIO = 0.9
|
_PARAGRAPH_LINE_GAP_MIN_RATIO = 0.9
|
||||||
@@ -771,30 +775,34 @@ SPARSE_UNDECLARED_FONT_SIZE_MAX_OCCURRENCES = 2
|
|||||||
IMAGE_DOWNSIZE_WARN_RATIO = 4.0
|
IMAGE_DOWNSIZE_WARN_RATIO = 4.0
|
||||||
IMAGE_DOWNSIZE_WARN_MIN_BYTES = 1024 * 1024
|
IMAGE_DOWNSIZE_WARN_MIN_BYTES = 1024 * 1024
|
||||||
|
|
||||||
def _design_spec_is_brand(spec_path: Path) -> bool:
|
def _design_spec_kind(spec_path: Path) -> str | None:
|
||||||
"""Return True when a design_spec.md frontmatter declares ``kind: brand``.
|
"""Return a roster-free ``kind`` declared in design_spec.md frontmatter.
|
||||||
|
|
||||||
Lightweight detector that does not require PyYAML — scans only the
|
Lightweight detector that does not require PyYAML — scans only the
|
||||||
frontmatter block (``---`` delimited) for a ``kind:`` line whose value
|
frontmatter block (``---`` delimited). Used by ``check_directory`` to
|
||||||
contains ``brand``. Used by ``check_directory`` to select Brand schema
|
select schema-only validation for Brand and Style workspaces instead of
|
||||||
validation instead of SVG-roster validation.
|
SVG-roster validation.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
text = spec_path.read_text(encoding='utf-8')
|
text = spec_path.read_text(encoding='utf-8')
|
||||||
except OSError:
|
except OSError:
|
||||||
return False
|
return None
|
||||||
if not text.startswith('---\n'):
|
if not text.startswith('---\n'):
|
||||||
return False
|
return None
|
||||||
end = text.find('\n---\n', 4)
|
end = text.find('\n---\n', 4)
|
||||||
if end == -1:
|
if end == -1:
|
||||||
return False
|
return None
|
||||||
fm_block = text[4:end]
|
fm_block = text[4:end]
|
||||||
for line in fm_block.splitlines():
|
for line in fm_block.splitlines():
|
||||||
stripped = line.strip()
|
stripped = line.strip()
|
||||||
if stripped.startswith('kind:'):
|
match = re.fullmatch(
|
||||||
value = stripped.split(':', 1)[1].strip().strip('"\'')
|
r'''kind\s*:\s*(?:(['"])(brand|style)\1|(brand|style))'''
|
||||||
return value == 'brand'
|
r'''(?:\s+#.*)?\s*''',
|
||||||
return False
|
stripped,
|
||||||
|
)
|
||||||
|
if match:
|
||||||
|
return match.group(2) or match.group(3)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _declared_template_structure_mode(target_path: Path) -> str | None:
|
def _declared_template_structure_mode(target_path: Path) -> str | None:
|
||||||
@@ -1038,7 +1046,7 @@ class SVGQualityChecker:
|
|||||||
# template_mode=True). Each entry is (severity, kind, message) where
|
# template_mode=True). Each entry is (severity, kind, message) where
|
||||||
# severity is 'error' or 'warning'. Printed in print_summary.
|
# severity is 'error' or 'warning'. Printed in print_summary.
|
||||||
self._template_issues: List[Tuple[str, str, str]] = []
|
self._template_issues: List[Tuple[str, str, str]] = []
|
||||||
self._brand_template_checked = False
|
self._spec_only_template_kind: str | None = None
|
||||||
self._animation_issues: List[Tuple[str, str]] = []
|
self._animation_issues: List[Tuple[str, str]] = []
|
||||||
self._illustration_issues: List[Tuple[str, str, str]] = []
|
self._illustration_issues: List[Tuple[str, str, str]] = []
|
||||||
self._communication_trace_issues: List[Tuple[str, str]] = []
|
self._communication_trace_issues: List[Tuple[str, str]] = []
|
||||||
@@ -1479,7 +1487,7 @@ class SVGQualityChecker:
|
|||||||
|
|
||||||
self._check_module_bounds_contract(root, result)
|
self._check_module_bounds_contract(root, result)
|
||||||
self._check_text_output_geometry(root, result)
|
self._check_text_output_geometry(root, result)
|
||||||
self._check_root_module_text_bounds(root, result)
|
self._check_text_bounds(root, result)
|
||||||
self._check_fragmented_paragraph_text(root, result)
|
self._check_fragmented_paragraph_text(root, result)
|
||||||
self._check_unmergeable_leading_text(root, result)
|
self._check_unmergeable_leading_text(root, result)
|
||||||
self._check_nested_positional_tspans(root, result)
|
self._check_nested_positional_tspans(root, result)
|
||||||
@@ -1890,6 +1898,8 @@ class SVGQualityChecker:
|
|||||||
runs: List[Dict],
|
runs: List[Dict],
|
||||||
font_size: float,
|
font_size: float,
|
||||||
parent_by_id: Dict[int, ET.Element],
|
parent_by_id: Dict[int, ET.Element],
|
||||||
|
*,
|
||||||
|
include_headroom: bool = True,
|
||||||
) -> Tuple[float, float, float, float] | None:
|
) -> Tuple[float, float, float, float] | None:
|
||||||
"""Estimate one line's transformed visible bounds in SVG coordinates."""
|
"""Estimate one line's transformed visible bounds in SVG coordinates."""
|
||||||
if any(helper is None for helper in (
|
if any(helper is None for helper in (
|
||||||
@@ -1902,7 +1912,10 @@ class SVGQualityChecker:
|
|||||||
)):
|
)):
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
width = float(_estimate_single_line_text_frame_width(runs))
|
width = float(_estimate_single_line_text_frame_width(
|
||||||
|
runs,
|
||||||
|
include_headroom=include_headroom,
|
||||||
|
))
|
||||||
raw_anchor = (
|
raw_anchor = (
|
||||||
_effective_presentation_value(
|
_effective_presentation_value(
|
||||||
line_el,
|
line_el,
|
||||||
@@ -1946,6 +1959,8 @@ class SVGQualityChecker:
|
|||||||
parent_by_id: Dict[int, ET.Element],
|
parent_by_id: Dict[int, ET.Element],
|
||||||
font_sizes: Dict[int, float],
|
font_sizes: Dict[int, float],
|
||||||
letter_spacings: Dict[int, float],
|
letter_spacings: Dict[int, float],
|
||||||
|
*,
|
||||||
|
include_headroom: bool = True,
|
||||||
) -> Tuple[float, float, float, float] | None:
|
) -> Tuple[float, float, float, float] | None:
|
||||||
"""Estimate one single- or multi-line text carrier's visual bounds."""
|
"""Estimate one single- or multi-line text carrier's visual bounds."""
|
||||||
lines: List[Tuple[ET.Element, float, float, List[Dict], float]] | None
|
lines: List[Tuple[ET.Element, float, float, List[Dict], float]] | None
|
||||||
@@ -1987,6 +2002,7 @@ class SVGQualityChecker:
|
|||||||
line_runs,
|
line_runs,
|
||||||
font_size,
|
font_size,
|
||||||
parent_by_id,
|
parent_by_id,
|
||||||
|
include_headroom=include_headroom,
|
||||||
)
|
)
|
||||||
for line_el, x, y, line_runs, font_size in lines
|
for line_el, x, y, line_runs, font_size in lines
|
||||||
]
|
]
|
||||||
@@ -2151,6 +2167,36 @@ class SVGQualityChecker:
|
|||||||
axes = 'vertical'
|
axes = 'vertical'
|
||||||
return axes, horizontal_ratio, vertical_ratio
|
return axes, horizontal_ratio, vertical_ratio
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _bounds_are_disjoint(
|
||||||
|
first: Tuple[float, float, float, float],
|
||||||
|
second: Tuple[float, float, float, float],
|
||||||
|
) -> bool:
|
||||||
|
"""Return whether two root-coordinate rectangles do not intersect."""
|
||||||
|
left, top, right, bottom = first
|
||||||
|
other_left, other_top, other_right, other_bottom = second
|
||||||
|
return (
|
||||||
|
right <= other_left
|
||||||
|
or left >= other_right
|
||||||
|
or bottom <= other_top
|
||||||
|
or top >= other_bottom
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _is_off_canvas_morph_group(
|
||||||
|
cls,
|
||||||
|
group: ET.Element,
|
||||||
|
canvas: Tuple[float, float, float, float],
|
||||||
|
) -> bool:
|
||||||
|
"""Return whether a group declares one wholly off-canvas Morph state."""
|
||||||
|
if group.get(_MORPH_STAGING_ATTR) != 'true':
|
||||||
|
return False
|
||||||
|
resolved = cls._resolved_root_module_bounds(group)
|
||||||
|
return (
|
||||||
|
resolved is not None
|
||||||
|
and cls._bounds_are_disjoint(resolved[1], canvas)
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _record_bounds_overflow(
|
def _record_bounds_overflow(
|
||||||
cls,
|
cls,
|
||||||
@@ -2194,6 +2240,51 @@ class SVGQualityChecker:
|
|||||||
f'{repair}'
|
f'{repair}'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _record_canvas_text_overflow(
|
||||||
|
cls,
|
||||||
|
result: Dict,
|
||||||
|
*,
|
||||||
|
subject: str,
|
||||||
|
inner: Tuple[float, float, float, float],
|
||||||
|
canvas: Tuple[float, float, float, float],
|
||||||
|
) -> bool:
|
||||||
|
"""Record one page-boundary error and return whether it overflowed."""
|
||||||
|
metrics = cls._bounds_overflow_metrics(inner, canvas)
|
||||||
|
if metrics is None:
|
||||||
|
return False
|
||||||
|
axes, horizontal_ratio, vertical_ratio = metrics
|
||||||
|
left, top, right, bottom = inner
|
||||||
|
canvas_left, canvas_top, canvas_right, canvas_bottom = canvas
|
||||||
|
result['errors'].append(
|
||||||
|
f'{subject} exceeds the root viewBox on the {axes} axis: '
|
||||||
|
f'content ({left:.1f}, {top:.1f})-({right:.1f}, '
|
||||||
|
f'{bottom:.1f}), canvas ({canvas_left:.1f}, '
|
||||||
|
f'{canvas_top:.1f})-({canvas_right:.1f}, '
|
||||||
|
f'{canvas_bottom:.1f}), overflow horizontal '
|
||||||
|
f'{horizontal_ratio:.1%}, vertical {vertical_ratio:.1%}; '
|
||||||
|
'move or reflow the text until its estimated bounds stay on-page'
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _text_diagnostic_label(text_element: ET.Element) -> str:
|
||||||
|
"""Return a locatable label for one SVG text carrier."""
|
||||||
|
label = _element_label(text_element)
|
||||||
|
if (text_element.get('id') or '').strip():
|
||||||
|
return label
|
||||||
|
|
||||||
|
details: List[str] = []
|
||||||
|
raw_x = (text_element.get('x') or '').strip()
|
||||||
|
raw_y = (text_element.get('y') or '').strip()
|
||||||
|
if raw_x or raw_y:
|
||||||
|
details.append(f'x={raw_x or "?"}, y={raw_y or "?"}')
|
||||||
|
snippet = re.sub(r'\s+', ' ', ''.join(text_element.itertext())).strip()
|
||||||
|
if snippet:
|
||||||
|
preview = snippet[:20] + ('…' if len(snippet) > 20 else '')
|
||||||
|
details.append(f'text={preview!r}')
|
||||||
|
return f'{label} ({"; ".join(details)})' if details else label
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _is_hidden_element(
|
def _is_hidden_element(
|
||||||
element: ET.Element,
|
element: ET.Element,
|
||||||
@@ -2330,6 +2421,72 @@ class SVGQualityChecker:
|
|||||||
'only on <g> layout modules'
|
'only on <g> layout modules'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for element in root.iter():
|
||||||
|
raw_staging = element.get(_MORPH_STAGING_ATTR)
|
||||||
|
if raw_staging is None:
|
||||||
|
continue
|
||||||
|
label = _element_label(element)
|
||||||
|
if raw_staging != 'true':
|
||||||
|
result['errors'].append(
|
||||||
|
f'{label} {_MORPH_STAGING_ATTR} must equal "true"; '
|
||||||
|
'set the exact value or remove the marker'
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if _local_name(element) != 'g':
|
||||||
|
result['errors'].append(
|
||||||
|
f'{label} {_MORPH_STAGING_ATTR} is valid only on <g>; '
|
||||||
|
'move it to the enclosing ordinary direct-root group'
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if parent_by_id.get(id(element)) is not root:
|
||||||
|
result['errors'].append(
|
||||||
|
f'{label} {_MORPH_STAGING_ATTR} requires a direct-root '
|
||||||
|
'<g>; move the marked group directly under <svg> or remove '
|
||||||
|
'the marker'
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if not (element.get('id') or '').strip():
|
||||||
|
result['errors'].append(
|
||||||
|
f'{label} {_MORPH_STAGING_ATTR} requires a stable non-empty '
|
||||||
|
'id; add an id to the marked direct-root group'
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
incompatible = [
|
||||||
|
attribute
|
||||||
|
for attribute in (
|
||||||
|
'data-pptx-layer',
|
||||||
|
'data-pptx-placeholder',
|
||||||
|
)
|
||||||
|
if element.get(attribute) is not None
|
||||||
|
]
|
||||||
|
if incompatible:
|
||||||
|
result['errors'].append(
|
||||||
|
f'{label} {_MORPH_STAGING_ATTR} cannot be combined with '
|
||||||
|
f'{", ".join(incompatible)}; use an ordinary Slide-local '
|
||||||
|
'group or remove the marker'
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
resolved = self._resolved_root_module_bounds(element)
|
||||||
|
if resolved is None:
|
||||||
|
result['errors'].append(
|
||||||
|
f'{label} {_MORPH_STAGING_ATTR} requires valid '
|
||||||
|
f'{_BOUNDS_ATTR}; add or fix positive root-coordinate '
|
||||||
|
'x y width height bounds'
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if canvas is None:
|
||||||
|
result['errors'].append(
|
||||||
|
f'{label} {_MORPH_STAGING_ATTR} cannot verify an off-canvas '
|
||||||
|
'endpoint without a valid root viewBox; fix the root viewBox'
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if not self._bounds_are_disjoint(resolved[1], canvas):
|
||||||
|
result['errors'].append(
|
||||||
|
f'{label} {_MORPH_STAGING_ATTR} requires wholly off-canvas '
|
||||||
|
f'{_BOUNDS_ATTR}; move the full bounds outside the root '
|
||||||
|
'viewBox or remove the marker from partially visible content'
|
||||||
|
)
|
||||||
|
|
||||||
missing: List[str] = []
|
missing: List[str] = []
|
||||||
root_groups = [
|
root_groups = [
|
||||||
child
|
child
|
||||||
@@ -2362,6 +2519,8 @@ class SVGQualityChecker:
|
|||||||
resolved = self._resolved_root_module_bounds(group)
|
resolved = self._resolved_root_module_bounds(group)
|
||||||
if resolved is None or canvas is None:
|
if resolved is None or canvas is None:
|
||||||
continue
|
continue
|
||||||
|
if self._is_off_canvas_morph_group(group, canvas):
|
||||||
|
continue
|
||||||
attribute, bounds = resolved
|
attribute, bounds = resolved
|
||||||
self._record_bounds_overflow(
|
self._record_bounds_overflow(
|
||||||
result,
|
result,
|
||||||
@@ -2387,12 +2546,12 @@ class SVGQualityChecker:
|
|||||||
'data-pptx-frame or native chart/table coordinates'
|
'data-pptx-frame or native chart/table coordinates'
|
||||||
)
|
)
|
||||||
|
|
||||||
def _check_root_module_text_bounds(
|
def _check_text_bounds(
|
||||||
self,
|
self,
|
||||||
root: ET.Element,
|
root: ET.Element,
|
||||||
result: Dict,
|
result: Dict,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Grade visible text against its direct root module subcanvas."""
|
"""Validate visible text against page and root-module bounds."""
|
||||||
helpers = (
|
helpers = (
|
||||||
_estimate_single_line_text_frame_width,
|
_estimate_single_line_text_frame_width,
|
||||||
_parse_project_font_weight,
|
_parse_project_font_weight,
|
||||||
@@ -2418,19 +2577,19 @@ class SVGQualityChecker:
|
|||||||
for child in list(parent)
|
for child in list(parent)
|
||||||
}
|
}
|
||||||
unchanged_groups = self._unchanged_txbody_group_ids(root)
|
unchanged_groups = self._unchanged_txbody_group_ids(root)
|
||||||
root_groups = [
|
viewbox = _parse_viewbox_values(root.get('viewBox') or '')
|
||||||
child
|
canvas = None
|
||||||
for child in list(root)
|
if viewbox is not None:
|
||||||
if _local_name(child) == 'g'
|
x, y, width, height = viewbox
|
||||||
]
|
canvas = (x, y, x + width, y + height)
|
||||||
for module in root_groups:
|
|
||||||
if self._is_hidden_element(module, parent_by_id):
|
estimated_by_id: Dict[
|
||||||
continue
|
int,
|
||||||
resolved_module = self._resolved_root_module_bounds(module)
|
Tuple[float, float, float, float],
|
||||||
if resolved_module is None:
|
] = {}
|
||||||
continue
|
page_overflow_text_ids: set[int] = set()
|
||||||
boundary_attribute, boundary = resolved_module
|
unverified: List[str] = []
|
||||||
for text_element in module.iter(f'{{{SVG_NS}}}text'):
|
for text_element in root.iter(f'{{{SVG_NS}}}text'):
|
||||||
if self._has_ancestor_id(
|
if self._has_ancestor_id(
|
||||||
text_element,
|
text_element,
|
||||||
parent_by_id,
|
parent_by_id,
|
||||||
@@ -2439,7 +2598,7 @@ class SVGQualityChecker:
|
|||||||
continue
|
continue
|
||||||
if self._has_non_visual_ancestor(
|
if self._has_non_visual_ancestor(
|
||||||
text_element,
|
text_element,
|
||||||
module,
|
root,
|
||||||
parent_by_id,
|
parent_by_id,
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
@@ -2456,12 +2615,86 @@ class SVGQualityChecker:
|
|||||||
parent_by_id,
|
parent_by_id,
|
||||||
font_sizes,
|
font_sizes,
|
||||||
letter_spacings,
|
letter_spacings,
|
||||||
|
include_headroom=True,
|
||||||
)
|
)
|
||||||
|
if estimated is not None:
|
||||||
|
estimated_by_id[id(text_element)] = estimated
|
||||||
|
|
||||||
|
if (
|
||||||
|
canvas is None
|
||||||
|
or self._has_zero_opacity(text_element, parent_by_id)
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
page_estimated = self._estimated_text_bounds(
|
||||||
|
text_element,
|
||||||
|
parent_by_id,
|
||||||
|
font_sizes,
|
||||||
|
letter_spacings,
|
||||||
|
include_headroom=False,
|
||||||
|
)
|
||||||
|
if page_estimated is None:
|
||||||
|
unverified.append(self._text_diagnostic_label(text_element))
|
||||||
|
continue
|
||||||
|
direct_child = text_element
|
||||||
|
parent = parent_by_id.get(id(direct_child))
|
||||||
|
while parent is not None and parent is not root:
|
||||||
|
direct_child = parent
|
||||||
|
parent = parent_by_id.get(id(direct_child))
|
||||||
|
morph_staging = (
|
||||||
|
parent is root
|
||||||
|
and _local_name(direct_child) == 'g'
|
||||||
|
and self._is_off_canvas_morph_group(
|
||||||
|
direct_child,
|
||||||
|
canvas,
|
||||||
|
)
|
||||||
|
and self._bounds_are_disjoint(page_estimated, canvas)
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
not morph_staging
|
||||||
|
and self._record_canvas_text_overflow(
|
||||||
|
result,
|
||||||
|
subject=self._text_diagnostic_label(text_element),
|
||||||
|
inner=page_estimated,
|
||||||
|
canvas=canvas,
|
||||||
|
)
|
||||||
|
):
|
||||||
|
page_overflow_text_ids.add(id(text_element))
|
||||||
|
|
||||||
|
if unverified:
|
||||||
|
sample = ', '.join(unverified[:3])
|
||||||
|
suffix = (
|
||||||
|
''
|
||||||
|
if len(unverified) <= 3
|
||||||
|
else f', +{len(unverified) - 3} more'
|
||||||
|
)
|
||||||
|
result['warnings'].append(
|
||||||
|
'Cannot verify root viewBox bounds for visible text with '
|
||||||
|
f'unsupported or unresolved geometry: {sample}{suffix}; use '
|
||||||
|
'supported explicit text positioning when page fit matters'
|
||||||
|
)
|
||||||
|
|
||||||
|
root_groups = [
|
||||||
|
child
|
||||||
|
for child in list(root)
|
||||||
|
if _local_name(child) == 'g'
|
||||||
|
]
|
||||||
|
for module in root_groups:
|
||||||
|
if self._is_hidden_element(module, parent_by_id):
|
||||||
|
continue
|
||||||
|
resolved_module = self._resolved_root_module_bounds(module)
|
||||||
|
if resolved_module is None:
|
||||||
|
continue
|
||||||
|
boundary_attribute, boundary = resolved_module
|
||||||
|
for text_element in module.iter(f'{{{SVG_NS}}}text'):
|
||||||
|
if id(text_element) in page_overflow_text_ids:
|
||||||
|
continue
|
||||||
|
estimated = estimated_by_id.get(id(text_element))
|
||||||
if estimated is None:
|
if estimated is None:
|
||||||
continue
|
continue
|
||||||
self._record_bounds_overflow(
|
self._record_bounds_overflow(
|
||||||
result,
|
result,
|
||||||
subject=_element_label(text_element),
|
subject=self._text_diagnostic_label(text_element),
|
||||||
inner=estimated,
|
inner=estimated,
|
||||||
container=(
|
container=(
|
||||||
f'{_element_label(module)} {boundary_attribute}'
|
f'{_element_label(module)} {boundary_attribute}'
|
||||||
@@ -4719,20 +4952,23 @@ class SVGQualityChecker:
|
|||||||
self.issue_types['Input issues'] += 1
|
self.issue_types['Input issues'] += 1
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Brand-only workspaces have no SVG roster. Validate their portable
|
# Brand and Style workspaces have no SVG roster. Validate their
|
||||||
# identity schema through the same authority used by library
|
# portable contracts through the same authority used by library
|
||||||
# registration, while keeping project scope independent of global
|
# registration, while keeping project scope independent of global
|
||||||
# indexes and directory names.
|
# indexes and directory names.
|
||||||
if self.template_mode and dir_path.is_dir():
|
if self.template_mode and dir_path.is_dir():
|
||||||
nested_spec = dir_path / 'templates' / 'design_spec.md'
|
nested_spec = dir_path / 'templates' / 'design_spec.md'
|
||||||
spec = nested_spec if nested_spec.is_file() else dir_path / 'design_spec.md'
|
spec = nested_spec if nested_spec.is_file() else dir_path / 'design_spec.md'
|
||||||
if spec.exists() and _design_spec_is_brand(spec):
|
spec_kind = _design_spec_kind(spec) if spec.exists() else None
|
||||||
self._brand_template_checked = True
|
if spec_kind in {'brand', 'style'}:
|
||||||
|
self._spec_only_template_kind = spec_kind
|
||||||
self.summary['total'] += 1
|
self.summary['total'] += 1
|
||||||
brand_valid = True
|
spec_valid = True
|
||||||
|
pretty_kind = spec_kind.title()
|
||||||
print(
|
print(
|
||||||
f"[INFO] Brand directory detected (kind: brand) — "
|
f"[INFO] {pretty_kind} directory detected "
|
||||||
f"validating design_spec.md and referenced assets."
|
f"(kind: {spec_kind}) — "
|
||||||
|
f"validating its portable workspace contract."
|
||||||
)
|
)
|
||||||
workspace_root = (
|
workspace_root = (
|
||||||
spec.parent.parent
|
spec.parent.parent
|
||||||
@@ -4743,23 +4979,28 @@ class SVGQualityChecker:
|
|||||||
from register_template import (
|
from register_template import (
|
||||||
SpecParseError,
|
SpecParseError,
|
||||||
validate_brand_workspace,
|
validate_brand_workspace,
|
||||||
|
validate_style_workspace,
|
||||||
)
|
)
|
||||||
validate_brand_workspace(workspace_root)
|
validator = {
|
||||||
|
'brand': validate_brand_workspace,
|
||||||
|
'style': validate_style_workspace,
|
||||||
|
}[spec_kind]
|
||||||
|
validator(workspace_root)
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
brand_valid = False
|
spec_valid = False
|
||||||
self._template_issues.append((
|
self._template_issues.append((
|
||||||
'error',
|
'error',
|
||||||
'brand_contract',
|
f'{spec_kind}_contract',
|
||||||
f"Brand schema validator could not be imported: {exc}",
|
f"{pretty_kind} schema validator could not be imported: {exc}",
|
||||||
))
|
))
|
||||||
except (OSError, SpecParseError) as exc:
|
except (OSError, SpecParseError) as exc:
|
||||||
brand_valid = False
|
spec_valid = False
|
||||||
self._template_issues.append((
|
self._template_issues.append((
|
||||||
'error',
|
'error',
|
||||||
'brand_contract',
|
f'{spec_kind}_contract',
|
||||||
str(exc),
|
str(exc),
|
||||||
))
|
))
|
||||||
if brand_valid:
|
if spec_valid:
|
||||||
self.summary['passed'] += 1
|
self.summary['passed'] += 1
|
||||||
return self.results
|
return self.results
|
||||||
|
|
||||||
@@ -4873,7 +5114,6 @@ class SVGQualityChecker:
|
|||||||
('error', message)
|
('error', message)
|
||||||
for message in validate_communication_trace(project_path)
|
for message in validate_communication_trace(project_path)
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.results
|
return self.results
|
||||||
|
|
||||||
def _check_pptx_structure_contract(
|
def _check_pptx_structure_contract(
|
||||||
@@ -6636,6 +6876,8 @@ class SVGQualityChecker:
|
|||||||
print(
|
print(
|
||||||
f" [ERROR] With errors: {self.summary['errors']} ({self._percentage(self.summary['errors'])}%)")
|
f" [ERROR] With errors: {self.summary['errors']} ({self._percentage(self.summary['errors'])}%)")
|
||||||
|
|
||||||
|
self._print_provenance_category_summary()
|
||||||
|
|
||||||
if self.issue_types:
|
if self.issue_types:
|
||||||
print(f"\nIssue categories:")
|
print(f"\nIssue categories:")
|
||||||
for issue_type, count in sorted(self.issue_types.items(), key=lambda x: x[1], reverse=True):
|
for issue_type, count in sorted(self.issue_types.items(), key=lambda x: x[1], reverse=True):
|
||||||
@@ -6675,6 +6917,36 @@ class SVGQualityChecker:
|
|||||||
print(f" 4. foreignObject: Use <text> + <tspan> for manual line breaks")
|
print(f" 4. foreignObject: Use <text> + <tspan> for manual line breaks")
|
||||||
print(f" 5. Font issues: use PPT-safe exported typefaces (e.g. Microsoft YaHei / Arial / Consolas)")
|
print(f" 5. Font issues: use PPT-safe exported typefaces (e.g. Microsoft YaHei / Arial / Consolas)")
|
||||||
|
|
||||||
|
def _print_provenance_category_summary(self):
|
||||||
|
"""Print compact JSON-equivalent counts for token-safe gate handling."""
|
||||||
|
categories = self._provenance_categories()
|
||||||
|
rows = (
|
||||||
|
(
|
||||||
|
'blocking',
|
||||||
|
len(categories['blocking']),
|
||||||
|
'hard findings; gate also requires exit 0',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'introduced',
|
||||||
|
len(categories['introduced']),
|
||||||
|
'advisory; new or changed',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'inherited',
|
||||||
|
len(categories['inherited']),
|
||||||
|
'informational; prototype-identical',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'source-import',
|
||||||
|
_source_import_warning_count(categories['source_import']),
|
||||||
|
'informational; source-conversion loss',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
print("\nProvenance categories:")
|
||||||
|
for name, count, note in rows:
|
||||||
|
print(f" {f'{name}: {count}':<20} {note}")
|
||||||
|
|
||||||
def _print_animation_summary(self):
|
def _print_animation_summary(self):
|
||||||
"""Print animations.json validation issues if present."""
|
"""Print animations.json validation issues if present."""
|
||||||
if not self._animation_issues:
|
if not self._animation_issues:
|
||||||
@@ -6747,7 +7019,7 @@ class SVGQualityChecker:
|
|||||||
from ``main`` agrees), warnings under ``warnings``. Both are listed
|
from ``main`` agrees), warnings under ``warnings``. Both are listed
|
||||||
per file so the user can act on them directly.
|
per file so the user can act on them directly.
|
||||||
"""
|
"""
|
||||||
if not self._template_issues and not self._brand_template_checked:
|
if not self._template_issues and self._spec_only_template_kind is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
errors = [item for item in self._template_issues if item[0] == 'error']
|
errors = [item for item in self._template_issues if item[0] == 'error']
|
||||||
@@ -6762,10 +7034,11 @@ class SVGQualityChecker:
|
|||||||
print(f" Warnings ({len(warnings)}):")
|
print(f" Warnings ({len(warnings)}):")
|
||||||
for _sev, kind, msg in warnings:
|
for _sev, kind, msg in warnings:
|
||||||
print(f" [{kind}] {msg}")
|
print(f" [{kind}] {msg}")
|
||||||
if self._brand_template_checked and not errors:
|
if self._spec_only_template_kind is not None and not errors:
|
||||||
print(" Brand design_spec.md schema and asset references passed.")
|
pretty_kind = self._spec_only_template_kind.title()
|
||||||
|
print(f" {pretty_kind} design_spec.md contract passed.")
|
||||||
if not errors:
|
if not errors:
|
||||||
if not self._brand_template_checked:
|
if self._spec_only_template_kind is None:
|
||||||
print(" No structural roster issues.")
|
print(" No structural roster issues.")
|
||||||
print(" Conventional placeholder-name hints may be declared through "
|
print(" Conventional placeholder-name hints may be declared through "
|
||||||
"'placeholders:' frontmatter. Placeholder bounds are mandatory "
|
"'placeholders:' frontmatter. Placeholder bounds are mandatory "
|
||||||
@@ -6927,14 +7200,13 @@ class SVGQualityChecker:
|
|||||||
|
|
||||||
print(f"\n[REPORT] Check report exported: {output_file}")
|
print(f"\n[REPORT] Check report exported: {output_file}")
|
||||||
|
|
||||||
def export_json_report(
|
def _provenance_categories(self) -> Dict[str, object]:
|
||||||
self,
|
"""Classify every issue by provenance.
|
||||||
output_file: str,
|
|
||||||
*,
|
Single source for the JSON report's ``categories`` block and the
|
||||||
target: str,
|
terminal summary, so the console and the report never disagree about
|
||||||
stage: str,
|
what blocks a release export.
|
||||||
) -> None:
|
"""
|
||||||
"""Write a machine-readable quality report with provenance classes."""
|
|
||||||
self._apply_aggregated_issue_counts()
|
self._apply_aggregated_issue_counts()
|
||||||
introduced: List[Dict[str, str]] = []
|
introduced: List[Dict[str, str]] = []
|
||||||
blocking: List[Dict[str, str]] = []
|
blocking: List[Dict[str, str]] = []
|
||||||
@@ -6991,6 +7263,28 @@ class SVGQualityChecker:
|
|||||||
else:
|
else:
|
||||||
introduced.append(item)
|
introduced.append(item)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'blocking': blocking,
|
||||||
|
'introduced': introduced,
|
||||||
|
'inherited': inherited,
|
||||||
|
'project_issues': project_issues,
|
||||||
|
'source_import': dict(self._source_import_summary),
|
||||||
|
}
|
||||||
|
|
||||||
|
def export_json_report(
|
||||||
|
self,
|
||||||
|
output_file: str,
|
||||||
|
*,
|
||||||
|
target: str,
|
||||||
|
stage: str,
|
||||||
|
) -> None:
|
||||||
|
"""Write a machine-readable quality report with provenance classes."""
|
||||||
|
categories = self._provenance_categories()
|
||||||
|
blocking = categories['blocking']
|
||||||
|
introduced = categories['introduced']
|
||||||
|
inherited = categories['inherited']
|
||||||
|
project_issues = categories['project_issues']
|
||||||
|
|
||||||
# Keep the legacy `drift` JSON field for report compatibility. Its
|
# Keep the legacy `drift` JSON field for report compatibility. Its
|
||||||
# colors/fonts entries are informational anchor comparisons; sparse
|
# colors/fonts entries are informational anchor comparisons; sparse
|
||||||
# size entries are informational until their third occurrence.
|
# size entries are informational until their third occurrence.
|
||||||
@@ -7001,7 +7295,7 @@ class SVGQualityChecker:
|
|||||||
}
|
}
|
||||||
for category, values in self._anchor_value_summary.items()
|
for category, values in self._anchor_value_summary.items()
|
||||||
}
|
}
|
||||||
source_import = dict(self._source_import_summary)
|
source_import = categories['source_import']
|
||||||
payload = {
|
payload = {
|
||||||
'schema': 'ppt-master.svg-quality-report.v1',
|
'schema': 'ppt-master.svg-quality-report.v1',
|
||||||
'stage': stage,
|
'stage': stage,
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ def print_usage() -> None:
|
|||||||
print(" --quick-generate Validate lockless flat Quick Generate SVGs;")
|
print(" --quick-generate Validate lockless flat Quick Generate SVGs;")
|
||||||
print(" ignore design_spec.md and spec_lock.md.")
|
print(" ignore design_spec.md and spec_lock.md.")
|
||||||
print(" --template-mode Validate a template workspace's templates/ directory:")
|
print(" --template-mode Validate a template workspace's templates/ directory:")
|
||||||
print(" Brand validates design_spec.md and referenced assets;")
|
print(" Brand/Style validate their portable workspace contracts;")
|
||||||
print(" Layout/Deck glob *.svg directly, skip spec_lock checks,")
|
print(" Layout/Deck glob *.svg directly, skip spec_lock checks,")
|
||||||
print(" enforce roster consistency, and emit placeholder hints.")
|
print(" enforce roster consistency, and emit placeholder hints.")
|
||||||
print(" native_structure_mode: structured also enables complete")
|
print(" native_structure_mode: structured also enables complete")
|
||||||
|
|||||||
+7
-2
@@ -2091,10 +2091,15 @@ def _estimate_text_runs_width(
|
|||||||
|
|
||||||
def estimate_single_line_text_frame_width(
|
def estimate_single_line_text_frame_width(
|
||||||
runs: list[dict[str, Any]],
|
runs: list[dict[str, Any]],
|
||||||
|
*,
|
||||||
|
include_headroom: bool = True,
|
||||||
) -> float:
|
) -> float:
|
||||||
"""Estimate the content width used by one generated DrawingML textbox."""
|
"""Estimate one DrawingML textbox width with optional safety headroom."""
|
||||||
content_runs, bullet = _extract_text_bullet(runs)
|
content_runs, bullet = _extract_text_bullet(runs)
|
||||||
width = _estimate_text_runs_width(content_runs)
|
width = _estimate_text_runs_width(
|
||||||
|
content_runs,
|
||||||
|
include_headroom=include_headroom,
|
||||||
|
)
|
||||||
if bullet:
|
if bullet:
|
||||||
font_size = (
|
font_size = (
|
||||||
float(content_runs[0].get('font_size', 16))
|
float(content_runs[0].get('font_size', 16))
|
||||||
|
|||||||
+16
-8
@@ -1284,8 +1284,8 @@ Recorded narration:
|
|||||||
)
|
)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# Native DrawingML is the only PPTX product. ``-s`` remains an explicit
|
# Native DrawingML is the only PPTX product. A non-output ``-s`` remains a
|
||||||
# diagnostic source override; standard export always reads svg_output/.
|
# diagnostic source override; default and explicit output use release rules.
|
||||||
native_source = args.source or 'output'
|
native_source = args.source or 'output'
|
||||||
native_files, native_source_dir = find_svg_files(
|
native_files, native_source_dir = find_svg_files(
|
||||||
project_path,
|
project_path,
|
||||||
@@ -1311,19 +1311,27 @@ Recorded narration:
|
|||||||
print("Error: No SVG files found", file=sys.stderr)
|
print("Error: No SVG files found", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if args.quick_generate:
|
release_quality_gate = args.quick_generate or args.source in {None, 'output'}
|
||||||
|
if release_quality_gate:
|
||||||
source_fingerprint = _svg_source_fingerprint(native_files)
|
source_fingerprint = _svg_source_fingerprint(native_files)
|
||||||
quality = _quality_report_context(project_path, source_fingerprint)
|
quality = _quality_report_context(project_path, source_fingerprint)
|
||||||
quality_gate, _ = _quality_gate_status(quality)
|
quality_gate, _ = _quality_gate_status(quality)
|
||||||
if quality_gate != 'passed':
|
if quality_gate != 'passed':
|
||||||
print(
|
export_mode = (
|
||||||
"Error: --quick-generate requires a passing final SVG quality "
|
'--quick-generate'
|
||||||
f"report for the current svg_output/; found {quality_gate}.",
|
if args.quick_generate
|
||||||
file=sys.stderr,
|
else 'default release export'
|
||||||
)
|
)
|
||||||
|
print(
|
||||||
|
f"Error: {export_mode} requires a passing final SVG quality "
|
||||||
|
f"report for the current {native_source_dir}/; found "
|
||||||
|
f"{quality_gate}.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
quick_flag = ' --quick-generate' if args.quick_generate else ''
|
||||||
print(
|
print(
|
||||||
"Run: python3 skills/ppt-master/scripts/svg_quality_checker.py "
|
"Run: python3 skills/ppt-master/scripts/svg_quality_checker.py "
|
||||||
f'"{project_path}" --quick-generate --stage final --json',
|
f'"{project_path}"{quick_flag} --stage final --json',
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
PPT Master - Workflow Log
|
||||||
|
|
||||||
|
Append one manually selected important event to a project's cold audit log.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 scripts/workflow_log.py <project_path> <message>
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
python3 scripts/workflow_log.py projects/demo "Strategist handoff complete"
|
||||||
|
python3 scripts/workflow_log.py projects/demo "Reworked P07 after source-label mismatch"
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
None (standard library only)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import uuid
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
_SCRIPTS_DIR = Path(__file__).resolve().parent
|
||||||
|
if str(_SCRIPTS_DIR) not in sys.path:
|
||||||
|
sys.path.insert(0, str(_SCRIPTS_DIR))
|
||||||
|
|
||||||
|
from console_encoding import configure_utf8_stdio # noqa: E402
|
||||||
|
|
||||||
|
WORKFLOW_LOG_RELATIVE_PATH = Path("validation/workflow.log")
|
||||||
|
|
||||||
|
|
||||||
|
def _utc_timestamp() -> str:
|
||||||
|
"""Return a compact UTC timestamp."""
|
||||||
|
return (
|
||||||
|
datetime.now(timezone.utc)
|
||||||
|
.isoformat(timespec="milliseconds")
|
||||||
|
.replace("+00:00", "Z")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def workflow_log_path(project_path: str | Path) -> Path:
|
||||||
|
"""Return the canonical workflow-log path for a project."""
|
||||||
|
return Path(project_path) / WORKFLOW_LOG_RELATIVE_PATH
|
||||||
|
|
||||||
|
|
||||||
|
def append_note(project_path: str | Path, message: str) -> Path:
|
||||||
|
"""Append one important event without interpreting workflow state."""
|
||||||
|
log_path = workflow_log_path(project_path)
|
||||||
|
log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
record = (
|
||||||
|
f"\n=== {_utc_timestamp()} NOTE id={uuid.uuid4().hex[:12]} ===\n"
|
||||||
|
f"{message.rstrip()}\n"
|
||||||
|
)
|
||||||
|
with log_path.open(
|
||||||
|
"a",
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
|
newline="",
|
||||||
|
) as handle:
|
||||||
|
handle.write(record)
|
||||||
|
handle.flush()
|
||||||
|
return log_path
|
||||||
|
|
||||||
|
|
||||||
|
def build_parser() -> argparse.ArgumentParser:
|
||||||
|
"""Build the command-line parser."""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Append one important event to validation/workflow.log.",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
)
|
||||||
|
parser.add_argument("project_path", help="Existing project directory")
|
||||||
|
parser.add_argument("message", help="Concise important event")
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str] | None = None) -> int:
|
||||||
|
"""Run the CLI entry point."""
|
||||||
|
configure_utf8_stdio()
|
||||||
|
parser = build_parser()
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
project_path = Path(args.project_path)
|
||||||
|
if not project_path.is_dir():
|
||||||
|
parser.error(f"project path does not exist: {project_path}")
|
||||||
|
|
||||||
|
message = args.message.strip()
|
||||||
|
if not message:
|
||||||
|
parser.error("message must not be empty")
|
||||||
|
try:
|
||||||
|
log_path = append_note(project_path, message)
|
||||||
|
except OSError as exc:
|
||||||
|
print(
|
||||||
|
f"[ERROR] Could not append workflow event to "
|
||||||
|
f"{workflow_log_path(project_path)}: {exc}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
print(f"[OK] Workflow event appended: {log_path}", file=sys.stderr)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,343 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
PPT Master - Automatic Workflow Transcript
|
||||||
|
|
||||||
|
Internal runtime helper that records a project-scoped Python tool's command
|
||||||
|
envelope and material text outcomes in an existing project workflow log.
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
None (standard library only)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
from collections import deque
|
||||||
|
from collections.abc import Iterable
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
WORKFLOW_LOG_RELATIVE_PATH = Path("validation/workflow.log")
|
||||||
|
PROJECT_PATH_ENV = "PPT_MASTER_PROJECT_PATH"
|
||||||
|
DISABLE_TRANSCRIPT_ENV = "PPT_MASTER_DISABLE_WORKFLOW_TRANSCRIPT"
|
||||||
|
_EXCLUDED_ENTRYPOINTS = {"workflow_log.py"}
|
||||||
|
_CRITICAL_MARKERS = ("[ERROR]", "[FAIL]")
|
||||||
|
_ALWAYS_RETAIN_MARKERS = (
|
||||||
|
"[POSTFLIGHT]",
|
||||||
|
"[PPTX]",
|
||||||
|
"[REPORT]",
|
||||||
|
"[Done]",
|
||||||
|
"[OK] Done",
|
||||||
|
)
|
||||||
|
_SUMMARY_MARKER = "[SUMMARY]"
|
||||||
|
_SUMMARY_STOP_MARKERS = ("[TIP]",)
|
||||||
|
_WARNING_MARKERS = ("[WARN]", "[WARNING]")
|
||||||
|
_OK_MARKER = "[OK]"
|
||||||
|
_WARNING_SAMPLE_LIMIT = 4
|
||||||
|
_OK_SAMPLE_LIMIT = 1
|
||||||
|
_STDERR_SAMPLE_LIMIT = 8
|
||||||
|
_STDERR_TAIL_LIMIT = 4
|
||||||
|
_CRITICAL_CONTEXT_LIMIT = 4
|
||||||
|
_SUMMARY_CONTEXT_LIMIT = 12
|
||||||
|
_ACTIVE_TRANSCRIPT: _AutomaticTranscript | None = None
|
||||||
|
|
||||||
|
|
||||||
|
def _utc_timestamp() -> str:
|
||||||
|
"""Return a compact UTC timestamp."""
|
||||||
|
return (
|
||||||
|
datetime.now(timezone.utc)
|
||||||
|
.isoformat(timespec="milliseconds")
|
||||||
|
.replace("+00:00", "Z")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _candidate_directory(value: str) -> Path | None:
|
||||||
|
"""Resolve one CLI value to an existing directory candidate."""
|
||||||
|
if not value or value.startswith("-") or "://" in value:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
raw_path = Path(value)
|
||||||
|
except (OSError, ValueError):
|
||||||
|
return None
|
||||||
|
if not raw_path.exists() and raw_path.parent == Path("."):
|
||||||
|
return None
|
||||||
|
path = raw_path
|
||||||
|
if not path.is_absolute():
|
||||||
|
path = Path.cwd() / path
|
||||||
|
while not path.exists() and path != path.parent:
|
||||||
|
path = path.parent
|
||||||
|
if path.is_file():
|
||||||
|
path = path.parent
|
||||||
|
return path.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def _find_project_root(argv: list[str]) -> Path | None:
|
||||||
|
"""Find the first ancestor that already owns a workflow log."""
|
||||||
|
explicit_project = os.environ.get(PROJECT_PATH_ENV, "").strip()
|
||||||
|
values = [explicit_project, *argv[1:], str(Path.cwd())]
|
||||||
|
for value in values:
|
||||||
|
directory = _candidate_directory(value)
|
||||||
|
if directory is None:
|
||||||
|
continue
|
||||||
|
for candidate in (directory, *directory.parents):
|
||||||
|
if (candidate / WORKFLOW_LOG_RELATIVE_PATH).is_file():
|
||||||
|
return candidate
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class _AutomaticTranscript:
|
||||||
|
"""Append a bounded command/outcome audit without changing the owning tool."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
handle: TextIO,
|
||||||
|
original_stderr: TextIO,
|
||||||
|
argv: list[str],
|
||||||
|
) -> None:
|
||||||
|
self.handle = handle
|
||||||
|
self.original_stderr = original_stderr
|
||||||
|
self.argv = argv
|
||||||
|
self.run_id = uuid.uuid4().hex[:12]
|
||||||
|
self.started_clock = time.monotonic()
|
||||||
|
self.pending = {"stdout": "", "stderr": ""}
|
||||||
|
self.observed_lines = 0
|
||||||
|
self.retained_lines = 0
|
||||||
|
self.omitted_lines = 0
|
||||||
|
self.warning_samples = 0
|
||||||
|
self.ok_samples = 0
|
||||||
|
self.stderr_samples = 0
|
||||||
|
self.stderr_tail: deque[str] = deque(maxlen=_STDERR_TAIL_LIMIT)
|
||||||
|
self.critical_context_remaining = 0
|
||||||
|
self.summary_context_remaining = 0
|
||||||
|
self.current_record_stream: str | None = None
|
||||||
|
self.lock = threading.RLock()
|
||||||
|
self.enabled = True
|
||||||
|
|
||||||
|
def _disable(self, exc: OSError | UnicodeError | ValueError) -> None:
|
||||||
|
"""Stop transcript writes without changing tool execution."""
|
||||||
|
self.enabled = False
|
||||||
|
try:
|
||||||
|
self.original_stderr.write(
|
||||||
|
f"[WARN] Workflow audit recording stopped: {exc}\n"
|
||||||
|
)
|
||||||
|
self.original_stderr.flush()
|
||||||
|
except (OSError, ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _write(self, text: str) -> None:
|
||||||
|
"""Append and flush one complete audit record."""
|
||||||
|
if not self.enabled:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
self.handle.write(text)
|
||||||
|
self.handle.flush()
|
||||||
|
except (OSError, UnicodeError, ValueError) as exc:
|
||||||
|
self._disable(exc)
|
||||||
|
|
||||||
|
def start(self) -> None:
|
||||||
|
"""Write the Python-command envelope."""
|
||||||
|
self._write(
|
||||||
|
f"\n=== {_utc_timestamp()} PYTHON run={self.run_id} ===\n"
|
||||||
|
f"cwd: {Path.cwd()}\n"
|
||||||
|
f"argv: {json.dumps(self.argv, ensure_ascii=False)}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_decoration(line: str) -> bool:
|
||||||
|
"""Return whether a non-empty line is only visual separator glyphs."""
|
||||||
|
stripped = line.strip()
|
||||||
|
return bool(stripped) and set(stripped) <= {"-", "=", "_"}
|
||||||
|
|
||||||
|
def _should_retain(self, stream_name: str, line: str) -> bool:
|
||||||
|
"""Select a bounded set of explicit outcomes for the cold audit log."""
|
||||||
|
stripped = line.strip()
|
||||||
|
if not stripped or self._is_decoration(stripped):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if _SUMMARY_MARKER in stripped:
|
||||||
|
self.summary_context_remaining = _SUMMARY_CONTEXT_LIMIT
|
||||||
|
return True
|
||||||
|
if any(marker in stripped for marker in _SUMMARY_STOP_MARKERS):
|
||||||
|
self.summary_context_remaining = 0
|
||||||
|
return False
|
||||||
|
if self.summary_context_remaining > 0:
|
||||||
|
self.summary_context_remaining -= 1
|
||||||
|
return True
|
||||||
|
if any(marker in stripped for marker in _CRITICAL_MARKERS):
|
||||||
|
self.critical_context_remaining = _CRITICAL_CONTEXT_LIMIT
|
||||||
|
return True
|
||||||
|
if self.critical_context_remaining > 0:
|
||||||
|
self.critical_context_remaining -= 1
|
||||||
|
return True
|
||||||
|
if any(marker in stripped for marker in _ALWAYS_RETAIN_MARKERS):
|
||||||
|
return True
|
||||||
|
if any(marker in stripped for marker in _WARNING_MARKERS):
|
||||||
|
if self.warning_samples < _WARNING_SAMPLE_LIMIT:
|
||||||
|
self.warning_samples += 1
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
if _OK_MARKER in stripped:
|
||||||
|
if self.ok_samples < _OK_SAMPLE_LIMIT:
|
||||||
|
self.ok_samples += 1
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
if stream_name == "stderr" and self.stderr_samples < _STDERR_SAMPLE_LIMIT:
|
||||||
|
self.stderr_samples += 1
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _record_line(self, stream_name: str, line: str) -> None:
|
||||||
|
"""Record one material logical line or account for its omission."""
|
||||||
|
if not line.strip():
|
||||||
|
return
|
||||||
|
self.observed_lines += 1
|
||||||
|
if not self._should_retain(stream_name, line):
|
||||||
|
self.omitted_lines += 1
|
||||||
|
if stream_name == "stderr" and not self._is_decoration(line):
|
||||||
|
self.stderr_tail.append(line)
|
||||||
|
return
|
||||||
|
if stream_name == "stderr":
|
||||||
|
self.stderr_tail.clear()
|
||||||
|
self.retained_lines += 1
|
||||||
|
if self.current_record_stream != stream_name:
|
||||||
|
self._write(f"{stream_name}:\n")
|
||||||
|
self.current_record_stream = stream_name
|
||||||
|
self._write(f" {line}\n")
|
||||||
|
|
||||||
|
def write_stream(self, stream_name: str, text: str) -> None:
|
||||||
|
"""Buffer fragments and write complete logical lines."""
|
||||||
|
if not text or not self.enabled:
|
||||||
|
return
|
||||||
|
with self.lock:
|
||||||
|
pending = self.pending[stream_name] + text
|
||||||
|
while "\n" in pending:
|
||||||
|
line, pending = pending.split("\n", 1)
|
||||||
|
self._record_line(stream_name, line)
|
||||||
|
self.pending[stream_name] = pending
|
||||||
|
|
||||||
|
def flush_stream(self, stream_name: str) -> None:
|
||||||
|
"""Flush a partial logical line when the owning stream flushes."""
|
||||||
|
with self.lock:
|
||||||
|
if not self.enabled:
|
||||||
|
self.pending[stream_name] = ""
|
||||||
|
return
|
||||||
|
pending = self.pending[stream_name]
|
||||||
|
if pending:
|
||||||
|
self._record_line(stream_name, pending)
|
||||||
|
self.pending[stream_name] = ""
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
"""Flush pending output and close the command envelope."""
|
||||||
|
with self.lock:
|
||||||
|
if not self.enabled:
|
||||||
|
return
|
||||||
|
self.flush_stream("stdout")
|
||||||
|
self.flush_stream("stderr")
|
||||||
|
if self.stderr_tail:
|
||||||
|
self._write("stderr-tail:\n")
|
||||||
|
for line in self.stderr_tail:
|
||||||
|
self._write(f" {line}\n")
|
||||||
|
self.retained_lines += len(self.stderr_tail)
|
||||||
|
self.omitted_lines -= len(self.stderr_tail)
|
||||||
|
elapsed_ms = int((time.monotonic() - self.started_clock) * 1000)
|
||||||
|
self._write(
|
||||||
|
f"=== {_utc_timestamp()} END run={self.run_id} "
|
||||||
|
f"elapsed_ms={elapsed_ms} output_lines={self.observed_lines} "
|
||||||
|
f"retained={self.retained_lines} omitted={self.omitted_lines} ===\n"
|
||||||
|
)
|
||||||
|
self.enabled = False
|
||||||
|
try:
|
||||||
|
self.handle.close()
|
||||||
|
except (OSError, ValueError) as exc:
|
||||||
|
self._disable(exc)
|
||||||
|
|
||||||
|
|
||||||
|
class _TeeTextIO:
|
||||||
|
"""Forward text writes while offering them to the audit recorder."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
primary: TextIO,
|
||||||
|
transcript: _AutomaticTranscript,
|
||||||
|
stream_name: str,
|
||||||
|
) -> None:
|
||||||
|
self.primary = primary
|
||||||
|
self.transcript = transcript
|
||||||
|
self.stream_name = stream_name
|
||||||
|
self.lock = threading.RLock()
|
||||||
|
|
||||||
|
def write(self, text: str) -> int:
|
||||||
|
"""Write to the original stream, then mirror the same text."""
|
||||||
|
with self.lock:
|
||||||
|
written = self.primary.write(text)
|
||||||
|
self.transcript.write_stream(self.stream_name, text)
|
||||||
|
return written
|
||||||
|
|
||||||
|
def writelines(self, lines: Iterable[str]) -> None:
|
||||||
|
"""Forward a sequence through the ordinary write path."""
|
||||||
|
for line in lines:
|
||||||
|
self.write(line)
|
||||||
|
|
||||||
|
def flush(self) -> None:
|
||||||
|
"""Flush the original stream without splitting a logical log line."""
|
||||||
|
with self.lock:
|
||||||
|
self.primary.flush()
|
||||||
|
|
||||||
|
def __getattr__(self, name: str) -> object:
|
||||||
|
"""Delegate the remaining text-stream interface."""
|
||||||
|
return getattr(self.primary, name)
|
||||||
|
|
||||||
|
|
||||||
|
def install_auto_transcript(argv: list[str] | None = None) -> Path | None:
|
||||||
|
"""Record a project-scoped Python command when its project log exists."""
|
||||||
|
global _ACTIVE_TRANSCRIPT
|
||||||
|
|
||||||
|
effective_argv = list(sys.argv if argv is None else argv)
|
||||||
|
if _ACTIVE_TRANSCRIPT is not None:
|
||||||
|
return None
|
||||||
|
if os.environ.get(DISABLE_TRANSCRIPT_ENV):
|
||||||
|
return None
|
||||||
|
if Path(effective_argv[0]).name in _EXCLUDED_ENTRYPOINTS:
|
||||||
|
return None
|
||||||
|
|
||||||
|
project_root = _find_project_root(effective_argv)
|
||||||
|
if project_root is None:
|
||||||
|
return None
|
||||||
|
log_path = project_root / WORKFLOW_LOG_RELATIVE_PATH
|
||||||
|
original_stdout = sys.stdout
|
||||||
|
original_stderr = sys.stderr
|
||||||
|
try:
|
||||||
|
handle = log_path.open(
|
||||||
|
"a",
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
|
newline="",
|
||||||
|
)
|
||||||
|
except OSError as exc:
|
||||||
|
try:
|
||||||
|
original_stderr.write(
|
||||||
|
f"[WARN] Workflow audit unavailable ({log_path}): {exc}\n"
|
||||||
|
)
|
||||||
|
original_stderr.flush()
|
||||||
|
except (OSError, ValueError):
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
transcript = _AutomaticTranscript(
|
||||||
|
handle,
|
||||||
|
original_stderr,
|
||||||
|
[sys.executable, *effective_argv],
|
||||||
|
)
|
||||||
|
transcript.start()
|
||||||
|
_ACTIVE_TRANSCRIPT = transcript
|
||||||
|
sys.stdout = _TeeTextIO(original_stdout, transcript, "stdout")
|
||||||
|
sys.stderr = _TeeTextIO(original_stderr, transcript, "stderr")
|
||||||
|
atexit.register(transcript.close)
|
||||||
|
return log_path
|
||||||
@@ -2,27 +2,31 @@
|
|||||||
|
|
||||||
## Reusable template kinds
|
## Reusable template kinds
|
||||||
|
|
||||||
Brand, Layout, and Deck are independent template kinds, not stages of one
|
Brand, Style, Layout, and Deck are independent template kinds, not stages of one
|
||||||
inheritance hierarchy.
|
inheritance hierarchy.
|
||||||
|
|
||||||
| Kind | Owns | Does not own | Discovery index |
|
| Kind | Owns | Does not own | Discovery index |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| [`brands/`](./brands/) | Identity: color, typography, logo, voice, icon style | Page structure or SVG roster | [`brands_index.json`](./brands/brands_index.json) |
|
| [`brands/`](./brands/) | Identity: color, typography, logo, voice, icon style | Page structure or SVG roster | [`brands_index.json`](./brands/brands_index.json) |
|
||||||
|
| [`styles/`](./styles/) | Direction/method: reusable communication method, visual language, composition rhythm, and information-expression defaults | Official brand identity, current-project application, page structure, or SVG roster | [`styles_index.json`](./styles/styles_index.json) |
|
||||||
| [`layouts/`](./layouts/) | Brand-neutral structure: canvas, Master/Layout graph, page types, slots, SVG roster | Brand identity or a recurring communication application | [`layouts_index.json`](./layouts/layouts_index.json) |
|
| [`layouts/`](./layouts/) | Brand-neutral structure: canvas, Master/Layout graph, page types, slots, SVG roster | Brand identity or a recurring communication application | [`layouts_index.json`](./layouts/layouts_index.json) |
|
||||||
| [`decks/`](./decks/) | A recurring presentation family: application contract + integrated identity + structure | — | [`decks_index.json`](./decks/decks_index.json) |
|
| [`decks/`](./decks/) | A recurring presentation family: application contract + integrated identity + structure | — | [`decks_index.json`](./decks/decks_index.json) |
|
||||||
|
|
||||||
A brand is not “a layout minus its pages”: it owns a different segment. Use a
|
A brand is not “a layout minus its pages”, and a Style is not a roster-free
|
||||||
brand for identity with free page composition, a layout for brand-neutral
|
Deck: each owns a different segment. Use a brand for identity with free page
|
||||||
structure whose identity and communication purpose remain downstream
|
composition, a Style for reusable direction/method without identity truth or
|
||||||
decisions, and a deck for a recurring presentation family with an explicit
|
page prototypes, a layout for brand-neutral structure whose identity and
|
||||||
application contract.
|
communication purpose remain downstream decisions, and a deck for a recurring
|
||||||
|
presentation family with an explicit application contract.
|
||||||
|
|
||||||
PowerPoint package objects are compilation targets, not additional template
|
PowerPoint package objects are compilation targets, not additional template
|
||||||
kinds. Theme values and identity assets are projected from resolved identity
|
kinds. Theme values and identity assets are projected from resolved identity
|
||||||
rules supplied by Brand, Deck, or the current project; Layout rules project
|
rules supplied by Brand, Deck, or the current project; Layout rules project
|
||||||
into Master/Layout/Placeholder topology, semantic text roles, and
|
into Master/Layout/Placeholder topology, semantic text roles, and
|
||||||
spatial behavior; Deck combines both with descriptive recurring-application
|
spatial behavior; Deck combines both with descriptive recurring-application
|
||||||
context and actual prototype examples. Downstream AI planning decides which
|
context and actual prototype examples. Style rules guide communication method,
|
||||||
|
visual language, composition, and information expression; they do not create a
|
||||||
|
PowerPoint package object or override resolved Brand/Deck identity. Downstream AI planning decides which
|
||||||
prototypes and content to use, then records the required exporter values.
|
prototypes and content to use, then records the required exporter values.
|
||||||
A compiled Slide Master may therefore contain both
|
A compiled Slide Master may therefore contain both
|
||||||
structural geometry and brand visuals even though their source rules remain
|
structural geometry and brand visuals even though their source rules remain
|
||||||
@@ -31,21 +35,37 @@ separately owned.
|
|||||||
New workspaces always enter [`Create Template`](../workflows/create-template.md),
|
New workspaces always enter [`Create Template`](../workflows/create-template.md),
|
||||||
which keeps the fixed route name and dispatches exactly one child workflow:
|
which keeps the fixed route name and dispatches exactly one child workflow:
|
||||||
[`Create Brand`](../workflows/create-template/create-brand.md),
|
[`Create Brand`](../workflows/create-template/create-brand.md),
|
||||||
|
[`Create Style`](../workflows/create-template/create-style.md),
|
||||||
[`Create Layout`](../workflows/create-template/create-layout.md), or
|
[`Create Layout`](../workflows/create-template/create-layout.md), or
|
||||||
[`Create Deck`](../workflows/create-template/create-deck.md).
|
[`Create Deck`](../workflows/create-template/create-deck.md).
|
||||||
|
|
||||||
The indexes are discovery aids only. [`generate-pptx`](../workflows/generate-pptx.md)
|
The four indexes are the complete library-discovery source for Default
|
||||||
Step 3 activates a template only from an explicit workspace-root path supplied
|
[`generate-pptx`](../workflows/generate-pptx.md) Stage-1 template selection.
|
||||||
by the user or an exact validated Create Template handoff.
|
Step 3 prepares candidate input without interaction or reading template
|
||||||
|
content. The Stage-1 page confirms the communication contract together with an
|
||||||
|
explicit free-design/template choice; only template mode expands these indexes.
|
||||||
|
Exact roots supplied for the run or handed off by Create Template appear as
|
||||||
|
specified candidates. Ordinary requests default to free design; explicit
|
||||||
|
template intent or any supplied root defaults to template mode. Exactly one root
|
||||||
|
may be preselected, while multiple roots remain unselected candidates. The user
|
||||||
|
can always switch modes. The page accepts one registered choice per kind plus
|
||||||
|
one supplied-root choice. A registered exact root is `library`; any other exact
|
||||||
|
root is `explicit`. After that combined confirmation,
|
||||||
|
[`apply-template-workspace`](../workflows/stages/apply-template-workspace.md)
|
||||||
|
validates, fuses, and installs every selected workspace into the current
|
||||||
|
project before Stage 2 starts. Template-aware reading begins in final Stage 2 from
|
||||||
|
that project-local copy. Quick skips the page, applies supplied exact roots, and
|
||||||
|
otherwise uses free design.
|
||||||
|
|
||||||
## Orthogonal contracts
|
## Orthogonal contracts
|
||||||
|
|
||||||
| Axis | Values | Meaning |
|
| Axis | Values | Meaning |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Template kind | `brand` / `layout` / `deck` | Which reusable contract the package owns: identity, brand-neutral structure, or a complete recurring application |
|
| Template kind | `brand` / `style` / `layout` / `deck` | Which reusable contract the package owns: identity, direction/method, brand-neutral structure, or a complete recurring application |
|
||||||
|
| Selection source | `library` / `explicit` | Step-3 discovery provenance only: exact index-derived root or exact unregistered root; it does not change template semantics |
|
||||||
| Internal creation strategy | `standard` / `fidelity` / `mirror` | AI-derived Create Layout/Create Deck implementation: newly author a compact or broad roster, or materialize validated source-package facts into a new workspace; persisted for tools, never presented as a required user choice |
|
| Internal creation strategy | `standard` / `fidelity` / `mirror` | AI-derived Create Layout/Create Deck implementation: newly author a compact or broad roster, or materialize validated source-package facts into a new workspace; persisted for tools, never presented as a required user choice |
|
||||||
| Internal application plan | `template_reuse_scope` plus optional `template_adherence` | Strategist derives literal, structural, or style-only use and any strict/adaptive exporter behavior after inspecting the installed template and current content |
|
| Internal application plan | `template_reuse_scope` plus optional `template_adherence` | Strategist derives literal, structural, or style-only use and any strict/adaptive exporter behavior after inspecting the installed template and current content |
|
||||||
| PPTX structure | `flat` / `structured` | Derived application plans that use template structure compile declared Masters and Layouts; style-only, brand-only, and free design remain Slide-local |
|
| PPTX structure | `flat` / `structured` | Derived application plans that use template structure compile declared Masters and Layouts; Style-only, style-scope, brand-only, and free design remain Slide-local. Style fused with Layout/Deck does not change the non-Style structure plan. |
|
||||||
|
|
||||||
These axes must not be used as synonyms or exposed as a user mode matrix. In
|
These axes must not be used as synonyms or exposed as a user mode matrix. In
|
||||||
particular, a mirror-created deck is still an ordinary reusable `deck` package
|
particular, a mirror-created deck is still an ordinary reusable `deck` package
|
||||||
@@ -59,7 +79,7 @@ initialized project:
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
<template_workspace>/
|
<template_workspace>/
|
||||||
├── templates/ # design_spec.md, SVG prototypes, optional native_payloads.json.gz store
|
├── templates/ # design_spec.md; optional Layout/Deck SVG prototypes and native_payloads.json.gz store
|
||||||
├── images/ # optional bitmaps
|
├── images/ # optional bitmaps
|
||||||
├── icons/
|
├── icons/
|
||||||
│ └── imported/ # optional imported vectors, one canonical copy
|
│ └── imported/ # optional imported vectors, one canonical copy
|
||||||
@@ -68,10 +88,19 @@ initialized project:
|
|||||||
|
|
||||||
Empty optional directories are omitted. Template SVGs reference bitmaps through
|
Empty optional directories are omitted. Template SVGs reference bitmaps through
|
||||||
`../images/<name>` and imported vectors through `data-icon="imported/<name>"`.
|
`../images/<name>` and imported vectors through `data-icon="imported/<name>"`.
|
||||||
|
Style narrows this shared routing shape to `templates/design_spec.md` only and
|
||||||
|
does not carry asset or review payloads; initialized-project sibling scaffolding
|
||||||
|
may exist but is not Style input.
|
||||||
The conditional [`apply-template-workspace`](../workflows/stages/apply-template-workspace.md)
|
The conditional [`apply-template-workspace`](../workflows/stages/apply-template-workspace.md)
|
||||||
stage owns installation and fusion: it consumes `templates/`, `images/`, and
|
stage owns installation and fusion after a non-free Default Stage-1 selection or
|
||||||
`icons/` and ignores `exports/`. Compatible legacy-flat packages remain
|
on the Quick exact-root branch. Brand/Layout/Deck consume package-owned
|
||||||
readable; directory shape alone does not indicate legacy Master/Layout semantics.
|
`templates/`, `images/`, and `icons/`; Style consumes only
|
||||||
|
`templates/design_spec.md` and ignores sibling project scaffolding. Every kind
|
||||||
|
ignores `exports/`. Compatible legacy-flat Brand/Layout/Deck packages remain
|
||||||
|
readable; Style has no legacy-flat form, and directory shape alone does not
|
||||||
|
indicate legacy Master/Layout semantics. Default Strategist, Quick's current
|
||||||
|
agent, and all later consumers use the installed project-local files, never the
|
||||||
|
original library or explicit root.
|
||||||
|
|
||||||
## Design specification references
|
## Design specification references
|
||||||
|
|
||||||
@@ -81,7 +110,7 @@ authoring; their schemas own machine validation. Files under `scaffolds/` are
|
|||||||
optional overwrite-safe CLI conveniences, not Generate-route starting artifacts.
|
optional overwrite-safe CLI conveniences, not Generate-route starting artifacts.
|
||||||
Reusable template `design_spec.md` files are
|
Reusable template `design_spec.md` files are
|
||||||
deliberately smaller: they contain portable metadata and only the identity,
|
deliberately smaller: they contain portable metadata and only the identity,
|
||||||
structure, or application rules owned by that package. General SVG rules live
|
direction/method, structure, or application rules owned by that package. General SVG rules live
|
||||||
in [`shared-standards-core.md`](../references/shared-standards-core.md), with
|
in [`shared-standards-core.md`](../references/shared-standards-core.md), with
|
||||||
effects and PowerPoint interfaces loaded only when triggered.
|
effects and PowerPoint interfaces loaded only when triggered.
|
||||||
|
|
||||||
|
|||||||
@@ -2,20 +2,27 @@
|
|||||||
|
|
||||||
This directory holds **brand-only templates**: identity bundles (color / typography / logo / voice / icon style) without an SVG page roster. Strategist locks the brand's identity segment as truth; Executor designs pages freely under those constraints.
|
This directory holds **brand-only templates**: identity bundles (color / typography / logo / voice / icon style) without an SVG page roster. Strategist locks the brand's identity segment as truth; Executor designs pages freely under those constraints.
|
||||||
|
|
||||||
Brand is one of three template kinds in the library — alongside
|
Brand is one of four template kinds in the library — alongside
|
||||||
[`layouts/`](../layouts/) (brand-neutral structure) and [`decks/`](../decks/)
|
[`styles/`](../styles/) (direction/method defaults), [`layouts/`](../layouts/)
|
||||||
(a recurring application with integrated identity and structure). The shared
|
(brand-neutral structure), and [`decks/`](../decks/) (a recurring application
|
||||||
kind and workspace model lives in the parent [`README.md`](../README.md).
|
with integrated identity and structure). The shared kind and workspace model
|
||||||
|
lives in the parent [`README.md`](../README.md).
|
||||||
|
|
||||||
## How brands are consumed
|
## How brands are consumed
|
||||||
|
|
||||||
Brand application follows the common explicit-path trigger in
|
Brand application follows the parent README's Default Stage-1
|
||||||
[`generate-pptx`](../../workflows/generate-pptx.md) Step 3. The conditional
|
[`generate-pptx`](../../workflows/generate-pptx.md) template-choice contract.
|
||||||
|
Its Brand choices come only from `brands_index.json`; no
|
||||||
|
directory scan or bare-name match is allowed. A supplied exact root appears in
|
||||||
|
the same selector, defaults Stage 1 to template mode, and preselects that
|
||||||
|
specific candidate only when it is the sole supplied root.
|
||||||
|
Registered exact roots are `library`; other exact roots remain `explicit`. The conditional
|
||||||
[`apply-template-workspace`](../../workflows/stages/apply-template-workspace.md)
|
[`apply-template-workspace`](../../workflows/stages/apply-template-workspace.md)
|
||||||
stage owns path normalization, portable-root installation, multi-kind fusion,
|
stage owns path normalization, portable-root installation, multi-kind fusion,
|
||||||
same-kind conflict resolution, and provenance. This file owns only the Brand
|
same-kind conflict resolution, and provenance after Stage 1 and before Stage 2. Template-aware
|
||||||
schema. `brands_index.json` is discovery-only; listing brands never advances
|
reading begins in final Stage 2 from the installed project-local copy. This file owns
|
||||||
the pipeline.
|
only the Brand schema. Quick applies a supplied exact Brand root directly and
|
||||||
|
otherwise uses free design.
|
||||||
|
|
||||||
## Creating a new brand
|
## Creating a new brand
|
||||||
|
|
||||||
@@ -50,4 +57,9 @@ Logo filenames are descriptive, not contractual — `templates/design_spec.md`
|
|||||||
|
|
||||||
[brands_index.json](./brands_index.json) is a slim machine-readable map (`brand_id → { summary, primary_color }`). Refresh it with `register_template.py --kind brand <brand_id>` after a brand is created or edited. Registration rejects incomplete frontmatter, mismatched IDs, page SVGs, missing required identity sections, invalid or inconsistent colors/provenance, and broken workspace-local asset references.
|
[brands_index.json](./brands_index.json) is a slim machine-readable map (`brand_id → { summary, primary_color }`). Refresh it with `register_template.py --kind brand <brand_id>` after a brand is created or edited. Registration rejects incomplete frontmatter, mismatched IDs, page SVGs, missing required identity sections, invalid or inconsistent colors/provenance, and broken workspace-local asset references.
|
||||||
|
|
||||||
Listing the index does not trigger any pipeline action — Generate Step 3 triggers only on an explicit directory path supplied by the user or an exact validated Create Template handoff, regardless of whether the brand appears in the index.
|
The Default Stage-1 template controls read this index as their complete
|
||||||
|
registered-brand catalog; chat discovery reads the same file and returns exact
|
||||||
|
workspace roots. Choosing an entry and submitting Stage 1 runs installation.
|
||||||
|
Exact directory
|
||||||
|
paths and validated Create Template handoffs remain supported, while a bare ID
|
||||||
|
never resolves implicitly.
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ This directory contains the standardized SVG visualization templates used by PPT
|
|||||||
|
|
||||||
[`charts_index.json`](./charts_index.json) is the single source of truth for the library: total count + one selection-rule `summary` per template (format: `"Pick for X. Skip if Y (use other_key)."`). [`chart_recall.py`](../../scripts/chart_recall.py) can recall a bounded candidate set from this live registry without maintaining a second category or keyword index.
|
[`charts_index.json`](./charts_index.json) is the single source of truth for the library: total count + one selection-rule `summary` per template (format: `"Pick for X. Skip if Y (use other_key)."`). [`chart_recall.py`](../../scripts/chart_recall.py) can recall a bounded candidate set from this live registry without maintaining a second category or keyword index.
|
||||||
|
|
||||||
For one planned page, provide 3-8 English semantic content-shape tags, then inspect the positive-scoring summaries plus the explicit `no-template-match` option. The requested limit is a cap, not a padding target, and lexical confidence never expands the output automatically. At `high` / `medium`, the Strategist may keep `no-template-match` when no bounded candidate fits. At `low` / `none`, a fitting candidate needs no expansion, but the Strategist must rerun the same query once with `--semantic-fallback` before keeping `no-template-match`. A selected result creates one `Page | Template | Usage` row in §VII and one matching `page_charts` entry; its path is derived from the key, while Usage is concise page-local intent and detailed adaptation remains in §IX. The negative result stays inside planning: it creates no §VII row or `page_charts` entry, and the chosen fallback is described in the page's §IX block. See [`chart-recall.md`](../../scripts/docs/chart-recall.md). The Generate workflow remains the authority for when Strategist uses this helper; maintainers may still open the registry directly when editing or auditing the catalog.
|
For one page, provide 3-8 English semantic content-shape tags, then inspect the positive-scoring summaries plus the explicit `no-template-match` option. The requested limit is a cap, not a padding target, and lexical confidence never expands the output automatically. At `high` / `medium`, retain `no-template-match` when no bounded candidate fits. At `low` / `none`, a fitting candidate needs no expansion, but rerun the same query once with `--semantic-fallback` before keeping `no-template-match`. In Default Generate, a selected result creates one `Page | Template | Usage` row in §VII and one matching `page_charts` entry; its path is derived from the key, while Usage is concise page-local intent and detailed adaptation remains in §IX. Quick Generate keeps the selected key/purpose only in active context and writes no mapping artifact. A negative result creates no row in either profile. See [`chart-recall.md`](../../scripts/docs/chart-recall.md). The Generate workflow remains the authority for when the active profile uses this helper; maintainers may still open the registry directly when editing or auditing the catalog.
|
||||||
|
|
||||||
## Authoring contract
|
## Authoring contract
|
||||||
|
|
||||||
[`CHART_STYLE_GUIDE.md`](./CHART_STYLE_GUIDE.md) owns readable standalone SVG, semantic and structural fidelity, root-group bounds, data encoding, and neutral previews. The active Design Spec §IX and source data own the generated page's semantics; `spec_lock.md` owns stable project anchors. A selected SVG is a page-local structural reference, while its type, styling, frames, grouping, item count, capacity, and geometry remain adaptable.
|
[`CHART_STYLE_GUIDE.md`](./CHART_STYLE_GUIDE.md) owns readable standalone SVG, semantic and structural fidelity, root-group bounds, data encoding, and neutral previews. Default Design Spec §IX or the Quick active-context page decision plus source data own the generated page's semantics; Default `spec_lock.md` owns stable project anchors. A selected SVG is a page-local structural reference, while its type, styling, frames, grouping, item count, capacity, and geometry remain adaptable.
|
||||||
|
|
||||||
## Visualization output model
|
## Visualization output model
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ below. Conceptual diagrams and frameworks are never labeled as native charts.
|
|||||||
|
|
||||||
## Native Chart/Table replacement markers
|
## Native Chart/Table replacement markers
|
||||||
|
|
||||||
Supported data chart templates include a `<g data-pptx-replace-with="chart">` marker by default, and pure text-grid table templates include the table form. These are capability examples, not project decisions: a generated page keeps and rewrites the marker only when its Design Spec §IX page block says `Native-ready: yes`; `no` omits it. Legacy specs may supply the decision in the matching §VII row when the page block has no field. The default SVG export path remains shape-based DrawingML. With `--native-charts-and-tables`, prepared groups become native PowerPoint Chart/Table objects from their JSON metadata. The legacy `--native-objects` spelling remains a compatibility alias.
|
Supported data chart templates include a `<g data-pptx-replace-with="chart">` marker by default, and pure text-grid table templates include the table form. These are capability examples, not project decisions: Default keeps and rewrites the marker only when its Design Spec §IX page block says `Native-ready: yes` (legacy §VII fallback only); Quick makes the same independent-object decision in active context before drawing. Otherwise omit it. The default SVG export path remains shape-based DrawingML. With `--native-charts-and-tables`, prepared groups become native PowerPoint Chart/Table objects from their JSON metadata. The legacy `--native-objects` spelling remains a compatibility alias.
|
||||||
|
|
||||||
`--native-charts-and-tables` is an explicit native-object opt-in and may be lossy or visually normalized. Replacement-local value labels, center KPIs, callouts, quadrant notes, fixed axis ranges, and custom binning/splits may not survive native replacement unless represented by the payload. ChartEx palette entries do survive when supplied through valid payload colors, but this does not preserve every ChartEx style detail. Detectable information-loss risks should be reported as warnings, not handled by disabling an otherwise supported replacement marker. Review those warnings and compare the native-object export with the default shape-based export before delivery.
|
`--native-charts-and-tables` is an explicit native-object opt-in and may be lossy or visually normalized. Replacement-local value labels, center KPIs, callouts, quadrant notes, fixed axis ranges, and custom binning/splits may not survive native replacement unless represented by the payload. ChartEx palette entries do survive when supplied through valid payload colors, but this does not preserve every ChartEx style detail. Detectable information-loss risks should be reported as warnings, not handled by disabling an otherwise supported replacement marker. Review those warnings and compare the native-object export with the default shape-based export before delivery.
|
||||||
|
|
||||||
@@ -71,4 +71,4 @@ Replacement payloads must include explicit `name`, `x`, `y`, `width`, and `heigh
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Execution loads only selected keys under [`executor-chart.md`](../../references/executor-chart.md) §1 and reuses unchanged path + SHA. Files are named after the `key` field in `charts_index.json` (e.g. `column_chart.svg`, `quadrant_bubble_scatter.svg`). Templates are named by visual structure, not by business-model name — terms like SWOT, BCG, PEST, OKR, Porter's Five Forces, and Value Chain are translated into semantic content-shape tags before candidate recall.
|
Execution loads only selected keys under [`executor-chart.md`](../../references/executor-chart.md) §1 and reuses an unchanged file within the active context. Files are named after the `key` field in `charts_index.json` (e.g. `column_chart.svg`, `quadrant_bubble_scatter.svg`). Templates are named by visual structure, not by business-model name — terms like SWOT, BCG, PEST, OKR, Porter's Five Forces, and Value Chain are translated into semantic content-shape tags before candidate recall.
|
||||||
|
|||||||
@@ -29,14 +29,26 @@ Overview when application fit must be judged in detail.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Trigger and fusion
|
## Selection and fusion
|
||||||
|
|
||||||
Selection uses the common explicit-path trigger in
|
Selection follows the parent README's Default Stage-1
|
||||||
[`generate-pptx`](../../workflows/generate-pptx.md) Step 3. Supplying a bare ID
|
[`generate-pptx`](../../workflows/generate-pptx.md) template-choice contract.
|
||||||
or reading the discovery index does not trigger template use. The conditional
|
Its Deck choices come only from `decks_index.json`; no
|
||||||
|
directory scan or bare-ID/style-phrase match is allowed. A supplied exact root
|
||||||
|
appears in the same selector, defaults Stage 1 to template mode, and preselects
|
||||||
|
that specific candidate only when it is the sole supplied root. Registered
|
||||||
|
exact roots are `library`; other exact roots remain
|
||||||
|
`explicit`. Choosing and confirming an entry runs the conditional
|
||||||
[`apply-template-workspace`](../../workflows/stages/apply-template-workspace.md)
|
[`apply-template-workspace`](../../workflows/stages/apply-template-workspace.md)
|
||||||
stage owns path normalization, compatibility checks, installation, and fusion.
|
stage, which owns path normalization, compatibility checks, installation, and
|
||||||
This file owns the Deck schema and application-context boundary.
|
fusion after Stage 1 and before Stage 2. Template-aware reading begins in final Stage 2 from the
|
||||||
|
installed project-local copy.
|
||||||
|
Quick applies a supplied exact Deck root directly and otherwise uses free
|
||||||
|
design. It uses installed prototypes as flat authoring inputs; reusable
|
||||||
|
native Master/Layout compilation remains a default lock-backed capability.
|
||||||
|
This file owns the Deck schema and application-context boundary. Chat discovery
|
||||||
|
reads the same index and returns exact roots; a bare ID never resolves
|
||||||
|
implicitly.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -133,5 +145,6 @@ does not register globally. Empty optional directories are omitted.
|
|||||||
3. Run `template_preview_pptx.py` when review is requested and always when the roster declares multiple Masters.
|
3. Run `template_preview_pptx.py` when review is requested and always when the roster declares multiple Masters.
|
||||||
4. In library scope, register with `register_template.py <id> --kind deck`.
|
4. In library scope, register with `register_template.py <id> --kind deck`.
|
||||||
|
|
||||||
See also [`layouts/`](../layouts/) for structure-only packages and
|
See also [`styles/`](../styles/) for direction/method packages,
|
||||||
|
[`layouts/`](../layouts/) for structure-only packages, and
|
||||||
[`brands/`](../brands/) for identity-only packages.
|
[`brands/`](../brands/) for identity-only packages.
|
||||||
|
|||||||
+6
-6
@@ -47,9 +47,9 @@ Start with this exact heading order:
|
|||||||
| AI Image Acquisition Path | <confirmed path or not applicable> |
|
| AI Image Acquisition Path | <confirmed path or not applicable> |
|
||||||
| Generation Mode | <continuous or split> |
|
| Generation Mode | <continuous or split> |
|
||||||
| Spec Refinement | <enabled or disabled> |
|
| Spec Refinement | <enabled or disabled> |
|
||||||
| Speaker Notes | <enabled or disabled> — <explicit user instruction, Stage 3 proactive policy, compatibility default, or enabled Narration Audio dependency> |
|
| Speaker Notes | <enabled or disabled> — <explicit user instruction, final Stage-2 proactive policy, workflow default, or enabled Narration Audio dependency> |
|
||||||
| Custom Animations | <enabled or disabled> — <explicit instruction and object/all-motion scope, Stage 3 proactive policy, or compatibility default> |
|
| Custom Animations | <enabled or disabled> — <explicit instruction and object/all-motion scope, final Stage-2 proactive policy, or workflow default> |
|
||||||
| Narration Audio | <enabled or disabled> — <explicit user instruction, Stage 3 proactive policy, or compatibility default> |
|
| Narration Audio | <enabled or disabled> — <explicit user instruction, final Stage-2 proactive policy, or workflow default> |
|
||||||
| Created Date | <YYYY-MM-DD> |
|
| Created Date | <YYYY-MM-DD> |
|
||||||
|
|
||||||
## II. Canvas Specification
|
## II. Canvas Specification
|
||||||
@@ -133,11 +133,11 @@ Use these exact subsections and field shapes:
|
|||||||
- **Primary bundled library**: <one of chunk-filled / tabler-filled / tabler-outline / phosphor-duotone, or none>
|
- **Primary bundled library**: <one of chunk-filled / tabler-filled / tabler-outline / phosphor-duotone, or none>
|
||||||
- **Brand-logo library**: <simple-icons when selected for real brand marks; omit otherwise>
|
- **Brand-logo library**: <simple-icons when selected for real brand marks; omit otherwise>
|
||||||
|
|
||||||
| Purpose | Icon Path | Page |
|
| Icon Path | Suitable Scenarios |
|
||||||
| --- | --- | --- |
|
| --- | --- |
|
||||||
```
|
```
|
||||||
|
|
||||||
Preserve Title/Body characters and resolved stacks; omit blank Typography upgrade and never place it in a stack. For each justified recurring family override, add the role to Font Plan plus `- **<Role> stack**: <complete ordered stack>`. Possible roles are `Annotation`, `Footer`, `Footnote`, `Data`, `Emphasis`, `Quote`, and `Code`; add only recurring, intentional differences. Add non-locked `Role rationale` only for an extra family. Do not collapse distinct Title/Body stacks or discard a declared optional role. Each Font Size Hierarchy value is a role anchor: Executor may vary one occurrence `±2px`; a short non-structural Hero/Display size may stay unlisted only while the same value is planned at most twice, and its third occurrence needs a named row. Add every recurring palette role and typography-size anchor established by the plan; do not enumerate one-off paint or font-family garnish. For confirmed custom directions, add the applicable `Mode References`, `Mode Behavior`, `Visual Style References`, and `Visual Style Behavior` lines under Theme Style. Include `Stroke Width` under §VI only for a stroke library. `simple-icons` may accompany the one primary bundled library and is recorded only when real brand marks were selected. The icon table records planned usage, but user-provided, template-carried, imported, custom, and other prepared SVGs under the project `icons/` directory remain usable without being forced into that stylistic selection. Leave the §VI table empty when no icons are used.
|
Preserve Title/Body characters and resolved stacks; omit blank Typography upgrade and never place it in a stack. For each justified recurring family override, add the role to Font Plan plus `- **<Role> stack**: <complete ordered stack>`. Possible roles are `Annotation`, `Footer`, `Footnote`, `Data`, `Emphasis`, `Quote`, and `Code`; add only recurring, intentional differences. Add non-locked `Role rationale` only for an extra family. Do not collapse distinct Title/Body stacks or discard a declared optional role. Each Font Size Hierarchy value is a role anchor: Executor may vary one occurrence `±2px`; a short non-structural Hero/Display size may stay unlisted only while the same value is planned at most twice, and its third occurrence needs a named row. Add every recurring palette role and typography-size anchor established by the plan; do not enumerate one-off paint or font-family garnish. For confirmed custom directions, add the applicable `Mode References`, `Mode Behavior`, `Visual Style References`, and `Visual Style Behavior` lines under Theme Style. Include `Stroke Width` under §VI only for a stroke library. `simple-icons` may accompany the one primary bundled library and is recorded only when real brand marks were selected. The icon table records the curated synced pool and broad semantic scenarios, not exact page placement or mandatory use. User-provided, template-carried, imported, custom, and other prepared SVGs under the project `icons/` directory remain usable without being forced into that bundled selection. Leave the §VI table empty when no bundled or brand icons are prepared.
|
||||||
|
|
||||||
When §VIII contains any `Acquire Via: ai` row, add this subsection under §III and preserve the complete confirmed AI direction:
|
When §VIII contains any `Acquire Via: ai` row, add this subsection under §III and preserve the complete confirmed AI direction:
|
||||||
|
|
||||||
|
|||||||
@@ -44,14 +44,26 @@ kind of rule is never a mirror operation.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Trigger and identity boundary
|
## Selection and identity boundary
|
||||||
|
|
||||||
Selection uses the common explicit-path trigger in
|
Selection follows the parent README's Default Stage-1
|
||||||
[`generate-pptx`](../../workflows/generate-pptx.md) Step 3. Supplying a bare ID
|
[`generate-pptx`](../../workflows/generate-pptx.md) template-choice contract.
|
||||||
or reading the discovery index does not trigger template use. The conditional
|
Its Layout choices come only from `layouts_index.json`; no
|
||||||
|
directory scan or bare-ID/style-phrase match is allowed. A supplied exact root
|
||||||
|
appears in the same selector, defaults Stage 1 to template mode, and preselects
|
||||||
|
that specific candidate only when it is the sole supplied root. Registered
|
||||||
|
exact roots are `library`; other exact roots remain `explicit`.
|
||||||
|
Choosing and confirming an entry runs the conditional
|
||||||
[`apply-template-workspace`](../../workflows/stages/apply-template-workspace.md)
|
[`apply-template-workspace`](../../workflows/stages/apply-template-workspace.md)
|
||||||
stage owns path normalization, compatibility checks, installation, and fusion.
|
stage, which owns path normalization, compatibility checks, installation, and
|
||||||
This file owns the Layout schema and its identity/application boundary.
|
fusion after Stage 1 and before Stage 2. Template-aware reading begins in final Stage 2 from the
|
||||||
|
installed project-local copy.
|
||||||
|
Quick applies a supplied exact Layout root directly and otherwise uses free
|
||||||
|
design. It uses installed prototypes as flat authoring inputs; reusable
|
||||||
|
native Master/Layout compilation remains a default lock-backed capability.
|
||||||
|
This file owns the Layout schema and its identity/application boundary. Chat
|
||||||
|
discovery reads the same index and returns exact roots; a bare ID never resolves
|
||||||
|
implicitly.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -146,3 +158,5 @@ omitted.
|
|||||||
General SVG/PPT rules remain authoritative in
|
General SVG/PPT rules remain authoritative in
|
||||||
[`shared-standards-core.md`](../../references/shared-standards-core.md) and
|
[`shared-standards-core.md`](../../references/shared-standards-core.md) and
|
||||||
[`pptx-structure-interface.md`](../../references/pptx-structure-interface.md).
|
[`pptx-structure-interface.md`](../../references/pptx-structure-interface.md).
|
||||||
|
See [`styles/`](../styles/) when reusable method and visual direction should be
|
||||||
|
combined with this structure without becoming identity truth.
|
||||||
|
|||||||
+3
-3
@@ -95,9 +95,9 @@
|
|||||||
|
|
||||||
## VI. Icon Usage Specification
|
## VI. Icon Usage Specification
|
||||||
|
|
||||||
| Purpose | Icon Path | Page |
|
| Icon Path | Suitable Scenarios |
|
||||||
| --- | --- | --- |
|
| --- | --- |
|
||||||
| [fill] | [fill] | [fill] |
|
| [fill] | [fill] |
|
||||||
|
|
||||||
## VIII. Image Resource List
|
## VIII. Image Resource List
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ After Generate Step 4 Gate 1, read the completed Design Spec and current page/re
|
|||||||
| `visual_style` | `visual_style` | Preset or `custom` |
|
| `visual_style` | `visual_style` | Preset or `custom` |
|
||||||
| `colors` | Stable semantic color roles | Core identity and recurring roles only; contextual SVG paints need no row; `image_rendering` appears only for AI images |
|
| `colors` | Stable semantic color roles | Core identity and recurring roles only; contextual SVG paints need no row; `image_rendering` appears only for AI images |
|
||||||
| `typography` | `font_family`, `body`, `title` | Core family/size anchors; new locks also write explicit `title_family` and `body_family`; size anchors are unitless px numbers |
|
| `typography` | `font_family`, `body`, `title` | Core family/size anchors; new locks also write explicit `title_family` and `body_family`; size anchors are unitless px numbers |
|
||||||
| `icons` | `library`, `inventory` | `library` is the Strategist's primary bundled style choice or `none`; `simple-icons/*` may be selected alone or accompany it; `inventory` records the planned bundled selection rather than all usable project-local icons; `stroke_width` is conditional |
|
| `icons` | `library`, `inventory` | `library` is the Strategist's primary bundled style choice or `none`; `simple-icons/*` may be selected alone or accompany it; `inventory` indexes the curated synced bundled pool rather than page usage or all usable project-local icons; `stroke_width` is conditional |
|
||||||
| `page_rhythm` | One `P<NN>` row per page | Values: `anchor`, `dense`, `breathing` |
|
| `page_rhythm` | One `P<NN>` row per page | Values: `anchor`, `dense`, `breathing` |
|
||||||
| `pptx_structure` | `mode` | Values: `flat`, `structured` |
|
| `pptx_structure` | `mode` | Values: `flat`, `structured` |
|
||||||
| `forbidden` | Literal list items | General standards stay in their owning reference |
|
| `forbidden` | Literal list items | General standards stay in their owning reference |
|
||||||
@@ -89,7 +89,7 @@ New locks always write `title_family` and `body_family`, even when their values
|
|||||||
|
|
||||||
- `font_family`, `title_family`, `body_family`, and every optional `<role>_family` use one non-empty PPT-safe exported family stack. `font_family` is the body/default compatibility stack, not permission to erase role differences.
|
- `font_family`, `title_family`, `body_family`, and every optional `<role>_family` use one non-empty PPT-safe exported family stack. `font_family` is the body/default compatibility stack, not permission to erase role differences.
|
||||||
- Every non-family `typography` value is a positive finite unitless px anchor. Intermediate values need no lock row when they stay within the mapped role's anchor `±2px`. At most two occurrences of one undeclared short non-structural Hero/Display size may remain sparse; a third occurrence or any structural use requires Design Spec repair and a named anchor.
|
- Every non-family `typography` value is a positive finite unitless px anchor. Intermediate values need no lock row when they stay within the mapped role's anchor `±2px`. At most two occurrences of one undeclared short non-structural Hero/Display size may remain sparse; a third occurrence or any structural use requires Design Spec repair and a named anchor.
|
||||||
- `icons.library` records the primary stylistic library selected from `chunk-filled`, `tabler-filled`, `tabler-outline`, or `phosphor-duotone`, or `none` when no generic bundled icons are selected. Selected `simple-icons/*` brand marks may appear alone or alongside it in `inventory` without becoming a stylistic library. The inventory records planned bundled choices, while every SVG already under `<project_path>/icons/` remains valid prepared execution material.
|
- `icons.library` records the primary stylistic library selected from `chunk-filled`, `tabler-filled`, `tabler-outline`, or `phosphor-duotone`, or `none` when no generic bundled icons are selected. Selected `simple-icons/*` brand marks may appear alone or alongside it in `inventory` without becoming a stylistic library. The inventory indexes the curated synced bundled pool without assigning page usage; every SVG already under `<project_path>/icons/` remains valid prepared execution material.
|
||||||
- `objective` grammar: one concise sentence preserving the deck goal and audience success condition.
|
- `objective` grammar: one concise sentence preserving the deck goal and audience success condition.
|
||||||
- `image_rendering` grammar: one catalog id, or `custom` with `image_rendering_behavior`.
|
- `image_rendering` grammar: one catalog id, or `custom` with `image_rendering_behavior`.
|
||||||
- `images`: `- <key>: <path> | source=<via> | pattern=<layout> | crop=<adaptive|no-crop>`; e.g. `- p04: images/a.png | source=user | pattern=full-height image beside the evidence | crop=no-crop`. Use the canonical `images/<filename>` path; `source` and `crop` exactly project §VIII, while `pattern` preserves its non-empty normalized free-form suggestion and any optional hierarchical catalog ids. The pattern remains a recommendation for Executor recall, not a geometry or realization lock. Omit unplaced sheets.
|
- `images`: `- <key>: <path> | source=<via> | pattern=<layout> | crop=<adaptive|no-crop>`; e.g. `- p04: images/a.png | source=user | pattern=full-height image beside the evidence | crop=no-crop`. Use the canonical `images/<filename>` path; `source` and `crop` exactly project §VIII, while `pattern` preserves its non-empty normalized free-form suggestion and any optional hierarchical catalog ids. The pattern remains a recommendation for Executor recall, not a geometry or realization lock. Omit unplaced sheets.
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# Style Workspaces
|
||||||
|
|
||||||
|
**Style = a roster-free reusable communication method plus coordinated design defaults.** It can carry argument flow, page-role vocabulary, evidence and data-expression discipline, visual-system defaults, image/icon direction, and additional review focus. It does not own a current project's communication contract, reusable brand identity, page geometry, SVG prototypes, or a recurring application contract.
|
||||||
|
|
||||||
|
Style is a fourth independent template kind alongside [`brands/`](../brands/), [`layouts/`](../layouts/), and [`decks/`](../decks/). It is not a replacement for the mode or visual-style catalogs.
|
||||||
|
|
||||||
|
## Axis Separation
|
||||||
|
|
||||||
|
| Axis | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| Template `kind: style` | A portable workspace that coordinates reusable method and non-binding design defaults |
|
||||||
|
| Final Stage-2 `mode` | The current deck's confirmed narrative and persuasion skeleton |
|
||||||
|
| Final Stage-2 `visual_style` | The current deck's confirmed shape, composition, whitespace, typography-character, and texture lock |
|
||||||
|
| Internal `template_reuse_scope: style` | A flat current-project export plan that reuses no Master/Layout structure |
|
||||||
|
|
||||||
|
These names are separate contracts. Style-only and Style + Brand naturally produce a flat application plan, while Style fused with a Layout or Deck may use structured reuse. `kind: style` therefore never forces the internal reuse scope when another workspace supplies structure.
|
||||||
|
|
||||||
|
## Selection, Precedence, and Fusion
|
||||||
|
|
||||||
|
Selection follows the parent README's Default Stage-1
|
||||||
|
[`generate-pptx`](../../workflows/generate-pptx.md) template-choice contract.
|
||||||
|
Its Style choices come only from `styles_index.json`; no
|
||||||
|
directory scan or bare-name match is allowed. A supplied exact root appears in
|
||||||
|
the same selector, defaults Stage 1 to template mode, and preselects that
|
||||||
|
specific candidate only when it is the sole supplied root. A
|
||||||
|
consulting label or visual description remains a brief and does not activate
|
||||||
|
this workspace. A non-free confirmation runs the
|
||||||
|
common installation/fusion stage after Stage 1 and before Stage 2; template-aware reading begins
|
||||||
|
in final Stage 2 from the project-local copy. Quick applies a supplied exact
|
||||||
|
Style root directly and otherwise uses free design; its current agent reads the
|
||||||
|
installed copy before authoring flat pages.
|
||||||
|
|
||||||
|
| Decision | Precedence |
|
||||||
|
|---|---|
|
||||||
|
| Current project communication contract, mode, visual style, palette, typography, images, and icons | Latest explicit user instruction and confirmed project values |
|
||||||
|
| Exact identity values | Brand, then Deck identity; both override overlapping Style fallback values |
|
||||||
|
| Reusable communication method and evidence discipline | Style, applied only where compatible with the current project contract |
|
||||||
|
| Reusable structure | Compatible Layout, then Deck structure; Style never supplies structure |
|
||||||
|
| Recurring application context | Deck, subordinate to the current project's Stage-1 communication contract |
|
||||||
|
|
||||||
|
Style fallback values seed the final Stage-2 solution when the corresponding decision remains open. They are not identity truth and do not bypass confirmation. If a Style method and a Deck application contract materially conflict, surface the mismatch; do not silently weaken either one.
|
||||||
|
|
||||||
|
## `design_spec.md` Contract
|
||||||
|
|
||||||
|
The frontmatter is intentionally small:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
style_id: <slug>
|
||||||
|
kind: style
|
||||||
|
summary: <one-line reusable method and design-default fit>
|
||||||
|
keywords: [<three-to-five discovery tags>]
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
The seven required body sections are:
|
||||||
|
|
||||||
|
| § | Title | Owned content |
|
||||||
|
|---|---|---|
|
||||||
|
| I | Style Overview | Display name, best fit, reusable intent, and provenance |
|
||||||
|
| II | Communication Method | Argument flow, page-message discipline, claim treatment, and an optional mode seed |
|
||||||
|
| III | Page Role Vocabulary | Semantic roles with communication jobs, evidence obligations, and non-geometric composition tendencies |
|
||||||
|
| IV | Evidence & Data Expression | Claim/evidence trace, chart/table/source behavior, and native-editability preference |
|
||||||
|
| V | Visual System Defaults | Composition, density, decoration, color behavior, typography character, and optional visual-style/fallback seeds |
|
||||||
|
| VI | Image & Icon Direction | Rendering, usage, framing, and icon treatment without asset selection |
|
||||||
|
| VII | Review Focus | Extra checks used only after the user explicitly activates visual review |
|
||||||
|
|
||||||
|
`Fallback Color Scheme` and `Fallback Typography` are optional subsections under §V. They remain lower-priority defaults, never Brand identity. A preset mode, visual style, or image rendering resolves to a real ID in its matching catalog. A custom seed includes its behavior prose and lists only catalog references actually used as comma-separated IDs; use `Mode References`, `Visual Style References`, or `Image Rendering References` respectively.
|
||||||
|
|
||||||
|
Section VII contains exactly one non-localized `<!-- visual-review-trigger: explicit-user-only -->` marker. Its surrounding explanation and checks may use the user's language; the marker lets validation enforce that Review Focus is advisory and never activates visual review.
|
||||||
|
|
||||||
|
**Forbidden — identity, structure, or application ownership**:
|
||||||
|
|
||||||
|
- Do not write `primary_color`, official color provenance, Logo, Voice & Tone, Icon Style, canvas fields, page count/types, `replication_mode`, `native_structure_mode`, or placeholder fields.
|
||||||
|
- Do not write Template Overview, Signature Design Elements, Page Roster, SVG filenames, Master/Layout identities, slot geometry, fixed page sequences, or reusable application audience/outcome rules.
|
||||||
|
- Do not write the current project's audience, objective, outcome, core message, delivery context, artifact afterlife, content outline, page assignments, icon inventory, or image-resource list.
|
||||||
|
|
||||||
|
## Workspace and Creation
|
||||||
|
|
||||||
|
Every Style workspace contains one portable source file and no page or asset payload:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<template_workspace>/
|
||||||
|
└── templates/
|
||||||
|
└── design_spec.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not create empty `images/`, `icons/`, or `exports/` directories. Existing initialized-project scaffolding may remain untouched but is not Style output.
|
||||||
|
|
||||||
|
1. Enter [`workflows/create-template.md`](../../workflows/create-template.md), which dispatches method/default output to [`create-style.md`](../../workflows/create-template/create-style.md).
|
||||||
|
2. Validate with `svg_quality_checker.py --template-mode`.
|
||||||
|
3. In library scope, register with `register_template.py <id> --kind style`.
|
||||||
|
|
||||||
|
The discovery source of truth is [`styles_index.json`](./styles_index.json).
|
||||||
|
Each entry is `style_id → { summary, keywords }`; the index never duplicates
|
||||||
|
the full method or defaults. The Default Stage-1 template controls read this
|
||||||
|
file as their complete registered-Style catalog, and chat discovery returns
|
||||||
|
exact roots from the same entries. Choosing an entry and submitting Stage 1
|
||||||
|
activates installation;
|
||||||
|
reading a name in ordinary prose does not.
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
style_id: consulting-decision
|
||||||
|
kind: style
|
||||||
|
summary: Answer-first, evidence-led decision-document method with restrained analytical design defaults.
|
||||||
|
keywords: [consulting, decision-support, answer-first, evidence, analytical]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Consulting Decision — Style Specification
|
||||||
|
|
||||||
|
> Method and design defaults only. No project communication contract, brand identity, page structure, or SVG prototypes.
|
||||||
|
|
||||||
|
## I. Style Overview
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|---|---|
|
||||||
|
| Style Name | Consulting Decision |
|
||||||
|
| Best Fit | Decision-oriented explanations, analytical reviews, recommendations, and evidence-heavy business documents |
|
||||||
|
| Reusable Intent | Make each page deliver a decision-relevant answer with traceable proof while leaving identity, geometry, and the current communication contract open to the project |
|
||||||
|
| Sources | [GitHub Issue #241](https://github.com/hugohe3/ppt-master/issues/241), opened 2026-07-22; reusable brief confirmed 2026-08-04 |
|
||||||
|
|
||||||
|
## II. Communication Method
|
||||||
|
|
||||||
|
- **Preferred Mode**: pyramid
|
||||||
|
- **Argument Flow**: Define the governing decision or question, the overall answer, its key supporting arguments, the evidence required for each argument, and any unresolved assumptions or evidence gaps. Maintain the trace `overall answer → key support → page message → evidence`; adapt the sequence to the current project instead of imposing a fixed roster.
|
||||||
|
- **Page Message Discipline**: For every planned page, identify one governing question, answer it through an assertion title or equally dominant message, and place the supporting proof visibly beneath or beside that answer. Use supporting subquestions only when their relationship matters; avoid topic-only titles.
|
||||||
|
- **Claim Discipline**: Keep facts, assumptions, implications, and recommendations semantically distinct. Cite facts, name uncertainty in assumptions, derive implications from visible evidence, and pair recommendations with their rationale and action. Keep recommendation and implication wording consistent across the deck; never promote an unsupported claim to a conclusion.
|
||||||
|
|
||||||
|
## III. Page Role Vocabulary
|
||||||
|
|
||||||
|
| Role | Communication Job | Evidence Obligation | Composition Tendency |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Executive synthesis | State the governing decision and overall answer | Show the few supports that make the answer credible and identify any material gap | Lead with the answer, then group proof by supporting argument |
|
||||||
|
| Recommendation | Specify the action and why it is preferable | Connect the action to diagnosis, expected effect, dependencies, and trade-offs | Keep the action dominant and its rationale visibly adjacent |
|
||||||
|
| Situation / complication / resolution | Establish context, surface the tension, and resolve the governing question | Distinguish observed conditions from the interpretation that creates the tension | Let the contrast between current reality and the answer carry the page |
|
||||||
|
| Driver decomposition | Explain what determines an outcome or decision | Use distinct supported branches; preserve real overlap or acknowledged gaps | Make the governing relationship primary and branches easy to compare |
|
||||||
|
| Current-state diagnosis | Identify the condition that matters and its causes | Separate observation from interpretation and tie each diagnosis to evidence | Pair each finding with the proof that establishes it |
|
||||||
|
| Comparison / benchmark | Clarify a decision through alternatives, peers, periods, or standards | State basis, units, period, and comparability; never invent a benchmark | Align comparable evidence and emphasize only decision-relevant differences |
|
||||||
|
| Process / operating model | Explain how work, ownership, or decisions flow | Show actors, handoffs, dependencies, controls, and failure points that the source supports | Prioritize causal or operational flow over decorative process art |
|
||||||
|
| Roadmap | Translate the recommendation into sequenced action | Connect phases to outcomes, dependencies, milestones, and decision gates | Make progression and ownership scannable without turning the page into a calendar ornament |
|
||||||
|
| Risk / mitigation | Expose uncertainty and the response it requires | Pair each risk with likelihood or trigger evidence, impact, mitigation, and owner when known | Keep risk and response in direct visual correspondence |
|
||||||
|
| Appendix / evidence | Preserve detail needed to audit or deepen the argument | Retain source, period, method, definitions, and limitations | Allow higher density while keeping the claim-to-source path legible |
|
||||||
|
|
||||||
|
## IV. Evidence & Data Expression
|
||||||
|
|
||||||
|
- **Argument Trace**: Every page message must trace back to one key support for the overall answer and forward to visible proof. Keep missing evidence or unresolved assumptions explicit instead of concealing the gap with confident wording.
|
||||||
|
- **Charts**: Choose the chart from the decision question and comparison logic. Use direct labels where practical, annotate the decision-relevant change, retain units and source lines, and remove legends, gridlines, effects, or chart furniture that do not aid interpretation. Separate observation from interpretation and never invent a baseline, peer, target, or trend.
|
||||||
|
- **Tables**: Derive columns and row groups from the comparison or decision logic. Align units and periods, preserve hierarchy, distinguish facts from assumptions, and emphasize only the differences that affect the answer. Use color only when it carries a declared meaning.
|
||||||
|
- **Sources**: Place citations close to the claim or data they support and retain source, period, scope, and measurement basis when available. Label estimates, proxies, and unresolved uncertainty; do not present unattributed or unsupported statements as facts.
|
||||||
|
- **Native Editability**: Prefer editable native charts, tables, and business shapes when the supported interface fits the intended object and editability is useful. Otherwise retain a legible editable shape-based representation rather than sacrificing fidelity or meaning.
|
||||||
|
|
||||||
|
## V. Visual System Defaults
|
||||||
|
|
||||||
|
- **Preferred Visual Style**: swiss-minimal
|
||||||
|
- **Composition**: Build the page around the message-to-proof relationship. Favor a few primary content regions—normally one to three—while allowing more when the argument genuinely needs them. Maintain one clear scan path, keep evidence dominant, and never detach a recommendation or implication into a decorative conclusion box that competes with its proof.
|
||||||
|
- **Density**: Support dense business material through concise assertions, disciplined alignment, compact but legible labels, and whitespace that separates reasoning layers. Let synthesis and recommendation pages breathe; let evidence and appendix pages tighten under a consistent grid without compressing below readable scale.
|
||||||
|
- **Decoration**: Use flat planes, hairline rules, restrained emphasis, and minimal elevation. Avoid ornamental cards, gradients, glows, decorative badges, and filled callout boxes that do not encode hierarchy, grouping, or evidence.
|
||||||
|
- **Color Behavior**: Start from a neutral field and reserve limited accents for semantic focus, change, risk, or recommendation. Use coherent tints for related series instead of rainbow coding. Any confirmed Brand or Deck identity replaces these tendencies; never imply a consultancy-specific or trademarked palette.
|
||||||
|
- **Typography Character**: Use a compact, authoritative sans-serif hierarchy suited to dense business documents across languages. Assertion titles are decisive, body text and annotations remain economical, and hierarchy comes from weight, scale, alignment, and spacing rather than ornamental containers. Exact families and locale coverage remain current-project or resolved identity decisions.
|
||||||
|
|
||||||
|
## VI. Image & Icon Direction
|
||||||
|
|
||||||
|
- **Preferred Image Rendering**: minimalist-swiss
|
||||||
|
- **Image Usage**: Use images only when they provide evidence, necessary context, or a causal explanation that shapes the decision. Default to sparse imagery; keep data and business structures editable instead of replacing them with decorative illustrations.
|
||||||
|
- **Image Treatment**: Crop for the evidentiary subject, use restrained framing or a functional scrim, and retain a nearby caption or source when the image supports a claim. Avoid gratuitous full bleed, synthetic text inside images, and atmospheric imagery that weakens the argument.
|
||||||
|
- **Icon Treatment**: Use one coherent icon family with consistent stroke or fill treatment, only when an icon clarifies a role, state, or relationship. Avoid logos, consultancy-specific marks, decorative icon grids, and mixed visual languages; actual icon selection remains a project decision.
|
||||||
|
|
||||||
|
## VII. Review Focus
|
||||||
|
<!-- visual-review-trigger: explicit-user-only -->
|
||||||
|
> Apply this section only after the user explicitly activates visual review. It never triggers that stage.
|
||||||
|
|
||||||
|
- The intended answer is identifiable quickly at the rendered slide size.
|
||||||
|
- The governing question is actually answered rather than repeated as a topic.
|
||||||
|
- Visible evidence supports the message and is spatially connected to it.
|
||||||
|
- Facts, assumptions, implications, and recommendations remain distinguishable and semantically consistent.
|
||||||
|
- Direct labels, sources, hierarchy, and annotations remain legible at the rendered slide size.
|
||||||
|
- Dense pages retain one clear scan path and do not hide overflow or structural ambiguity.
|
||||||
|
- No unsupported claim is presented as established evidence; unresolved gaps remain visible.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"consulting-decision": {
|
||||||
|
"summary": "Answer-first, evidence-led decision-document method with restrained analytical design defaults.",
|
||||||
|
"keywords": [
|
||||||
|
"consulting",
|
||||||
|
"decision-support",
|
||||||
|
"answer-first",
|
||||||
|
"evidence",
|
||||||
|
"analytical"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
description: Create Template entry workflow and shared contract for the Create Brand, Create Layout, and Create Deck sub-workflows.
|
description: Create Template entry workflow and shared contract for the Create Brand, Create Style, Create Layout, and Create Deck sub-workflows.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Create Template Workflow
|
# Create Template Workflow
|
||||||
|
|
||||||
> **Fixed entry name**: user-facing template creation always enters **Create Template**. This workflow selects exactly one child workflow — [`create-brand.md`](./create-template/create-brand.md), [`create-layout.md`](./create-template/create-layout.md), or [`create-deck.md`](./create-template/create-deck.md) — and owns their shared execution contract.
|
> **Fixed entry name**: user-facing template creation always enters **Create Template**. This workflow selects exactly one child workflow — [`create-brand.md`](./create-template/create-brand.md), [`create-style.md`](./create-template/create-style.md), [`create-layout.md`](./create-template/create-layout.md), or [`create-deck.md`](./create-template/create-deck.md) — and owns their shared execution contract.
|
||||||
>
|
>
|
||||||
> **Role invoked for Create Layout/Create Deck**: [Template_Designer](../references/template-designer.md)
|
> **Role invoked for Create Layout/Create Deck**: [Template_Designer](../references/template-designer.md)
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ Create one reusable template workspace under either the **global template librar
|
|||||||
|
|
||||||
**Hard rule — one workspace routing contract**: Output scope changes only the workspace parent and index registration. Both scopes use required `templates/`, optional `images/` / `icons/`, and optional on-demand `exports/`, with the same relative asset references and validation command. Create Template must not create an optional directory or placeholder file solely to retain an empty path. An initialized project may already contain empty `images/`, `icons/`, or `exports/` scaffolding; leave it untouched, do not count it as template output, and omit the path from completion unless this workflow wrote or adopted a real file there. Do not maintain a library-only self-contained-flat package branch or a project-only thin-bundle branch.
|
**Hard rule — one workspace routing contract**: Output scope changes only the workspace parent and index registration. Both scopes use required `templates/`, optional `images/` / `icons/`, and optional on-demand `exports/`, with the same relative asset references and validation command. Create Template must not create an optional directory or placeholder file solely to retain an empty path. An initialized project may already contain empty `images/`, `icons/`, or `exports/` scaffolding; leave it untouched, do not count it as template output, and omit the path from completion unless this workflow wrote or adopted a real file there. Do not maintain a library-only self-contained-flat package branch or a project-only thin-bundle branch.
|
||||||
|
|
||||||
> **Boundary against template-fill and in-place structure edits**: Create Template does not fill content into a PPTX, add Master/Layout structure to an existing PPTX/SVG, or directly output the user's final generated deck. It authors a separate reusable workspace; an optional PPTX is review evidence only. To generate a deck, return the workspace root to [`generate-pptx`](./generate-pptx.md) Step 3 and author new SVG pages from it. A project-scoped workspace is already installed at that project's Step 3 path and is consumed in place.
|
> **Boundary against template-fill and in-place structure edits**: Create Template does not fill content into a PPTX, add Master/Layout structure to an existing PPTX/SVG, or directly output the user's final generated deck. It authors a separate reusable workspace; an optional PPTX is review evidence only. To generate a deck, return the workspace root as an exact candidate to [`generate-pptx`](./generate-pptx.md) Step 3, confirm it with Stage 1, then author new SVG pages from the installed state. A project-scoped workspace selected for its own project is consumed in place after that confirmation.
|
||||||
|
|
||||||
## Child Workflow Dispatch
|
## Child Workflow Dispatch
|
||||||
|
|
||||||
@@ -25,10 +25,11 @@ Create Template is the fixed user-facing entry and common contract. It selects o
|
|||||||
| Child workflow | Select when | Library-scope output | Exclusive responsibility |
|
| Child workflow | Select when | Library-scope output | Exclusive responsibility |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| [`Create Brand`](./create-template/create-brand.md) | Reuse identity only: colors, typography, logo, voice, and icon style | `templates/brands/<brand_id>/` | Identity analysis and identity-only `design_spec.md`; no SVG roster |
|
| [`Create Brand`](./create-template/create-brand.md) | Reuse identity only: colors, typography, logo, voice, and icon style | `templates/brands/<brand_id>/` | Identity analysis and identity-only `design_spec.md`; no SVG roster |
|
||||||
|
| [`Create Style`](./create-template/create-style.md) | Reuse a communication method and visual direction without identity truth or page prototypes | `templates/styles/<style_id>/` | Method, page-role vocabulary, evidence/data expression, visual defaults, image/icon direction, and advisory review focus; no SVG roster |
|
||||||
| [`Create Layout`](./create-template/create-layout.md) | Reuse a brand-neutral structural skeleton without a recurring communication application | `templates/layouts/<layout_id>/` | Canvas, page grammar, semantic text roles, Master/Layout/slot contract, and SVG roster; no brand identity or application contract |
|
| [`Create Layout`](./create-template/create-layout.md) | Reuse a brand-neutral structural skeleton without a recurring communication application | `templates/layouts/<layout_id>/` | Canvas, page grammar, semantic text roles, Master/Layout/slot contract, and SVG roster; no brand identity or application contract |
|
||||||
| [`Create Deck`](./create-template/create-deck.md) | Reuse a branded structural system or a recurring presentation application | `templates/decks/<deck_id>/` | Descriptive application context, integrated identity/structure, and SVG roster |
|
| [`Create Deck`](./create-template/create-deck.md) | Reuse a branded structural system or a recurring presentation application | `templates/decks/<deck_id>/` | Descriptive application context, integrated identity/structure, and SVG roster |
|
||||||
|
|
||||||
Select Create Brand only for identity-only intent. Select Create Layout only when identity remains downstream-selectable and the reusable artifact does not prescribe communication objectives, audience outcomes, a required narrative sequence, or scenario-specific starting content. Select Create Deck when structure carries brand identity or reusable application semantics. A complete source PPTX alone does not determine the kind: classify only the stable rules worth reusing. Ask one discriminator question only when the user's requested reusable artifact is genuinely ambiguous; once selected, enter that child workflow and do not repeat route selection inside its confirmation gate.
|
Select Create Brand only for identity-only intent. Select Create Style when the portable value is a communication method, evidence discipline, and visual direction but there is no official identity, page geometry, or prototype roster to retain. Select Create Layout only when identity remains downstream-selectable and the reusable artifact does not prescribe communication objectives, audience outcomes, a required narrative sequence, or scenario-specific starting content. Select Create Deck when structure carries brand identity or reusable application semantics. A complete source PPTX alone does not determine the kind: classify only the stable rules worth reusing. Ask one discriminator question only when the user's requested reusable artifact is genuinely ambiguous; once selected, enter that child workflow and do not repeat route selection inside its confirmation gate.
|
||||||
|
|
||||||
See [`templates/README.md`](../templates/README.md) for the shared kind and
|
See [`templates/README.md`](../templates/README.md) for the shared kind and
|
||||||
workspace model. Downstream template application and fusion remain owned by
|
workspace model. Downstream template application and fusion remain owned by
|
||||||
@@ -54,7 +55,12 @@ Both scopes write this contract:
|
|||||||
└── exports/ # conditional; required package evidence for multi-Master templates
|
└── exports/ # conditional; required package evidence for multi-Master templates
|
||||||
```
|
```
|
||||||
|
|
||||||
The review PPTX is derived evidence, not a source template asset. Create `exports/` only when a review deck is requested or the template declares more than one Master; multi-Master templates require the package-level gate in Step 6. Template application reads `templates/` plus any existing `images/` and `icons/`; it never copies or consumes `exports/`. Library `exports/` directories are Git-ignored.
|
Create Style narrows the shared root to `templates/design_spec.md` only. It
|
||||||
|
does not write or adopt images, icons, native payloads, or review exports;
|
||||||
|
pre-existing empty project scaffolding remains untouched and is not Style
|
||||||
|
output.
|
||||||
|
|
||||||
|
The review PPTX is derived evidence, not a source template asset. Create `exports/` only when a review deck is requested or the template declares more than one Master; multi-Master templates require the package-level gate in Step 6. Brand/Layout/Deck application reads `templates/` plus any package-owned `images/` and `icons/`; Style reads only `templates/design_spec.md`. No kind copies or consumes `exports/`. Library `exports/` directories are Git-ignored.
|
||||||
|
|
||||||
For `project`, `target_project` is required and must be an existing project initialized by `project_manager.py init`. Before the first final-output write, run one complete preflight. Apply the same collision checks to a library workspace; the only difference is that its root is under the global kind directory:
|
For `project`, `target_project` is required and must be an existing project initialized by `project_manager.py init`. Before the first final-output write, run one complete preflight. Apply the same collision checks to a library workspace; the only difference is that its root is under the global kind directory:
|
||||||
|
|
||||||
@@ -78,11 +84,11 @@ After dispatch, the selected child workflow executes these shared steps with its
|
|||||||
|
|
||||||
## Step 1: Reference Bundle Intake & Analysis
|
## Step 1: Reference Bundle Intake & Analysis
|
||||||
|
|
||||||
Run every applicable input branch for the reference bundle the user supplied. A bundle may contain one source, several files of one type, multiple source types, direct text in the conversation, or no external file. This step produces analysis artefacts only — it does **not** create the final template directory, write `design_spec.md`, or touch any template index. When Create Brand was selected, follow that child workflow's analysis rules and do not run page-topology analysis merely because the reference is a PPTX/PDF.
|
Run every applicable input branch for the reference bundle the user supplied. A bundle may contain one source, several files of one type, multiple source types, direct text in the conversation, or no external file. This step produces analysis artefacts only — it does **not** create the final template directory, write `design_spec.md`, or touch any template index. When Create Brand or Create Style was selected, follow that child workflow's analysis rules and do not run page-topology analysis merely because the reference is a PPTX/PDF.
|
||||||
|
|
||||||
### Input source taxonomy
|
### Input source taxonomy
|
||||||
|
|
||||||
The rows are evidence channels, not mutually exclusive routes. Run every matching row and retain source-level provenance. The internal-strategy column applies to Create Layout/Create Deck. Create Brand uses the same reference formats for identity evidence but has no replication strategy, SVG roster, or native-structure path.
|
The rows are evidence channels, not mutually exclusive routes. Run every matching row and retain source-level provenance. The internal-strategy column applies to Create Layout/Create Deck. Create Brand uses the same reference formats for identity evidence, while Create Style extracts portable method and direction; neither has a replication strategy, SVG roster, or native-structure path.
|
||||||
|
|
||||||
| Type | What the user supplied | Tool / read path | Internal strategies supported by the evidence |
|
| Type | What the user supplied | Tool / read path | Internal strategies supported by the evidence |
|
||||||
|------|-------------------------|------------------|-----------------------------------------------|
|
|------|-------------------------|------------------|-----------------------------------------------|
|
||||||
@@ -92,6 +98,15 @@ The rows are evidence channels, not mutually exclusive routes. Run every matchin
|
|||||||
| **D** Text / document / website / asset references | Direct conversation text, pasted requirements, Markdown/TXT, DOCX/PDF/HTML/URL, brand/design manuals, or supplied logo/icon/font assets | Use direct text as-is; read plain text/Markdown; convert supported documents/URLs with `source_to_md.py` into a temporary analysis workspace; inventory explicit assets | `standard` only by itself |
|
| **D** Text / document / website / asset references | Direct conversation text, pasted requirements, Markdown/TXT, DOCX/PDF/HTML/URL, brand/design manuals, or supplied logo/icon/font assets | Use direct text as-is; read plain text/Markdown; convert supported documents/URLs with `source_to_md.py` into a temporary analysis workspace; inventory explicit assets | `standard` only by itself |
|
||||||
| **E** No reference material | A template request with no external source and no substantive brief yet | Skip analysis; collect every required value in Steps 2–3 | `standard` only |
|
| **E** No reference material | A template request with no external source and no substantive brief yet | Skip analysis; collect every required value in Steps 2–3 | `standard` only |
|
||||||
|
|
||||||
|
**Roster-free child boundary**: Create Brand and Create Style do not enter the
|
||||||
|
structured import/materialization path below. Their child workflows may inspect
|
||||||
|
PPTX/PDF pages, extracted text, screenshots, or other sources as evidence, but
|
||||||
|
they do not preserve or derive canvas, page count/order, Master/Layout identity,
|
||||||
|
placeholder geometry, or an SVG roster. Create Brand extracts identity truth;
|
||||||
|
Create Style extracts only portable communication method and visual defaults.
|
||||||
|
The Type A/Type B authoring-IR and `standard` / `fidelity` / `mirror` mechanics
|
||||||
|
below are Create Layout/Create Deck concerns.
|
||||||
|
|
||||||
| Bundle rule | Behavior |
|
| Bundle rule | Behavior |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Combine channels | `standard` may use every confirmed visual, textual, documentary, web, and asset source together. Do not force the user to choose one source type. |
|
| Combine channels | `standard` may use every confirmed visual, textual, documentary, web, and asset source together. Do not force the user to choose one source type. |
|
||||||
@@ -269,6 +284,13 @@ checks from `authoring_manifest.json`.
|
|||||||
|
|
||||||
Before composing Step 2, extract the template's reusable norms from the previous content. These norms are not generic design advice; they are the source deck's observable operating rules, and they must flow into `design_spec.md`.
|
Before composing Step 2, extract the template's reusable norms from the previous content. These norms are not generic design advice; they are the source deck's observable operating rules, and they must flow into `design_spec.md`.
|
||||||
|
|
||||||
|
This table applies to Create Layout/Create Deck. Create Brand extracts only the
|
||||||
|
identity subset defined by its child workflow. Create Style instead extracts
|
||||||
|
argument flow, page-message/evidence discipline, open page-role vocabulary,
|
||||||
|
data-expression rules, composition/density rhythm, visual defaults, and
|
||||||
|
image/icon direction. It must discard source-specific audience, objective,
|
||||||
|
page order/count, page mappings, canvas, and native structure.
|
||||||
|
|
||||||
| Norm area | Extract from | Record as |
|
| Norm area | Extract from | Record as |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Canvas / page geometry | `manifest.json` slide size, SVG `width` / `height` / `viewBox` | `[fact]` canvas format, pixel dimensions, source `viewBox`, and aspect ratio |
|
| Canvas / page geometry | `manifest.json` slide size, SVG `width` / `height` / `viewBox` | `[fact]` canvas format, pixel dimensions, source `viewBox`, and aspect ratio |
|
||||||
@@ -349,6 +371,7 @@ Inventory explicitly supplied logo, icon, font, and other brand/design assets. R
|
|||||||
Extract only what the source actually states:
|
Extract only what the source actually states:
|
||||||
|
|
||||||
- Identity rules: colors, typography, logo usage, voice, icon style, and explicit exclusions.
|
- Identity rules: colors, typography, logo usage, voice, icon style, and explicit exclusions.
|
||||||
|
- Style method: argument flow, evidence discipline, reusable page-role vocabulary, information hierarchy, composition/density rhythm, visual defaults, image/icon direction, and optional review focus. Do not retain the source's audience, objective, page sequence, page count, or page-specific resource choices as Style rules.
|
||||||
- Structure rules: canvas, page types, grids, zones, placeholders, density, image behavior, and requested variants.
|
- Structure rules: canvas, page types, grids, zones, placeholders, density, image behavior, and requested variants.
|
||||||
- Deck application: recurring situations, intended audiences/outcomes, delivery or reading assumptions, representative narrative/page roles, examples, and negative requirements. Do not convert these observations into mandatory future-use policy.
|
- Deck application: recurring situations, intended audiences/outcomes, delivery or reading assumptions, representative narrative/page roles, examples, and negative requirements. Do not convert these observations into mandatory future-use policy.
|
||||||
|
|
||||||
@@ -356,7 +379,7 @@ Treat an explicit value authored by the user as `[decision]` regardless of carri
|
|||||||
|
|
||||||
### 1E. No reference material
|
### 1E. No reference material
|
||||||
|
|
||||||
Skip the analysis. Step 2 will list every Required item as `[decision]`; nothing is fact-derivable from a non-existent source. Create Brand may emit an incomplete empty skeleton only under its explicit child-workflow rule; Create Layout/Create Deck still require the shared confirmation gate before authoring a `standard` workspace.
|
Skip the analysis. Step 2 will list every Required item as `[decision]`; nothing is fact-derivable from a non-existent source. Create Brand may emit an incomplete empty skeleton only under its explicit child-workflow rule. Create Style, Create Layout, and Create Deck still require the shared confirmation gate before authoring their workspace.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -377,14 +400,15 @@ Compose one concise natural-language proposal that states the template the AI in
|
|||||||
|---|---|
|
|---|---|
|
||||||
| Output scope | Recommended `library` (default) plus `project`; explain that both use the same portable workspace routing and only the parent path / global registration differ |
|
| Output scope | Recommended `library` (default) plus `project`; explain that both use the same portable workspace routing and only the parent path / global registration differ |
|
||||||
| Target project | Required only for `project`; show the exact initialized project workspace path, not a project nickname |
|
| Target project | Required only for `project`; show the exact initialized project workspace path, not a project nickname |
|
||||||
| Selected child workflow | Echo the already-dispatched Create Brand, Create Layout, or Create Deck workflow; do not reopen kind selection inside the brief |
|
| Selected child workflow | Echo the already-dispatched Create Brand, Create Style, Create Layout, or Create Deck workflow; do not reopen kind selection inside the brief |
|
||||||
|
| Method and direction | Create Style only. Summarize the portable communication method, evidence discipline, page-role vocabulary, information design, visual defaults, image/icon direction, and advisory review focus. Do not include a current audience/outcome, page order/count, canvas, or prototype plan. |
|
||||||
| Category | Create Layout/Create Deck only. State the one discovery category inferred from the intended artifact. For Layout, a scenario category records geometric fit only and never grants application ownership. |
|
| Category | Create Layout/Create Deck only. State the one discovery category inferred from the intended artifact. For Layout, a scenario category records geometric fit only and never grants application ownership. |
|
||||||
| Application context | Create Deck only. Summarize the recurring presentation family, likely audiences/outcomes, delivery/reading assumptions, and representative page roles. This is descriptive selection context, not a rule saying which template pages or visible content future projects must keep. |
|
| Application context | Create Deck only. Summarize the recurring presentation family, likely audiences/outcomes, delivery/reading assumptions, and representative page roles. This is descriptive selection context, not a rule saying which template pages or visible content future projects must keep. |
|
||||||
| Theme direction | Create Layout/Create Deck only. Describe the intended light/dark/mixed behavior in plain language. Create Brand records identity colors instead and does not own a page theme mode. |
|
| Theme direction | Create Layout/Create Deck only. Describe the intended light/dark/mixed behavior in plain language. Create Brand records identity colors instead and does not own a page theme mode. |
|
||||||
| Canvas | Create Layout/Create Deck only. State the recommended canvas with exact pixel size and `viewBox`; do not enumerate same-ratio alternatives unless the user asks or the evidence is genuinely ambiguous. |
|
| Canvas | Create Layout/Create Deck only. State the recommended canvas with exact pixel size and `viewBox`; do not enumerate same-ratio alternatives unless the user asks or the evidence is genuinely ambiguous. |
|
||||||
| Creation plan | Create Layout/Create Deck only. Describe what will be preserved, what will be rebuilt, how broad the prototype roster will be, and how native structure will be handled. The AI derives the internal `replication_mode` from this prose after confirmation; never ask the user to select `standard`, `fidelity`, or `mirror`. |
|
| Creation plan | Create Layout/Create Deck only. Describe what will be preserved, what will be rebuilt, how broad the prototype roster will be, and how native structure will be handled. The AI derives the internal `replication_mode` from this prose after confirmation; never ask the user to select `standard`, `fidelity`, or `mirror`. |
|
||||||
| Native structure plan | Create Layout/Create Deck only. State the planned Master/Layout/slot result. For newly authored output, show the planned Master families and reject one-Master-per-Layout organization or equivalent duplicate Masters. For literal preservation, summarize the exact supported source facts that will be mapped into the new workspace. |
|
| Native structure plan | Create Layout/Create Deck only. State the planned Master/Layout/slot result. For newly authored output, show the planned Master families and reject one-Master-per-Layout organization or equivalent duplicate Masters. For literal preservation, summarize the exact supported source facts that will be mapped into the new workspace. |
|
||||||
| Asset bundling | Recommended included assets, plus excluded candidate assets with a one-line reason when reference assets exist |
|
| Asset bundling | Create Brand/Create Layout/Create Deck only. Recommend included assets, plus excluded candidate assets with a one-line reason when reference assets exist. Create Style records textual provenance only and writes no asset payload. |
|
||||||
|
|
||||||
Items to surface:
|
Items to surface:
|
||||||
|
|
||||||
@@ -395,22 +419,25 @@ Items to surface:
|
|||||||
| New template ID | Yes | `[decision]` when supplied; otherwise propose a filesystem-safe ASCII slug as `[suggested]`. In library scope it also becomes the matching index key |
|
| New template ID | Yes | `[decision]` when supplied; otherwise propose a filesystem-safe ASCII slug as `[suggested]`. In library scope it also becomes the matching index key |
|
||||||
| Template display name | Yes | `[decision]` when supplied; otherwise `[suggested]`, often from `manifest.json.source.name` for type A |
|
| Template display name | Yes | `[decision]` when supplied; otherwise `[suggested]`, often from `manifest.json.source.name` for type A |
|
||||||
| Category | Create Layout/Create Deck only | `[decision]` when explicit; otherwise `[derived]` for indexing — Create Deck: `brand` / `general` / `scenario` / `government` / `special`; Create Layout: `general` / `scenario` / `government` / `special` |
|
| Category | Create Layout/Create Deck only | `[decision]` when explicit; otherwise `[derived]` for indexing — Create Deck: `brand` / `general` / `scenario` / `government` / `special`; Create Layout: `general` / `scenario` / `government` / `special` |
|
||||||
| Applicable scenarios | Yes | Create Brand: identity use cases. Create Layout: content shapes and delivery settings its geometry can support, without communication or narrative ownership. Create Deck: recurring presentation situations inside the application contract. `[suggested]` from analysis unless explicitly authored or externally sourced; user confirms. |
|
| Applicable scenarios | Yes | Create Brand: identity use cases. Create Style: broad best-fit discovery context only, without binding a target audience, outcome, or recurring application. Create Layout: content shapes and delivery settings its geometry can support, without communication or narrative ownership. Create Deck: recurring presentation situations inside the application contract. `[suggested]` from analysis unless explicitly authored or externally sourced; user confirms. |
|
||||||
| Application context and representative page roles | Create Deck only | `[decision]` when supplied explicitly; otherwise `[suggested]` from recurring source patterns. Describe the source and intended family without assigning required/optional/repeatable status or fixed/replaceable/example-only policy. |
|
| Application context and representative page roles | Create Deck only | `[decision]` when supplied explicitly; otherwise `[suggested]` from recurring source patterns. Describe the source and intended family without assigning required/optional/repeatable status or fixed/replaceable/example-only policy. |
|
||||||
| Identity tone or structural summary | Yes | Create Brand/Create Deck: identity tone. Create Layout: structural use case and density/rhythm summary only. |
|
| Identity, method, or structural summary | Yes | Create Brand/Create Deck: identity tone. Create Style: communication method and visual-default summary. Create Layout: structural use case and density/rhythm summary only. |
|
||||||
|
| Communication method and evidence discipline | Create Style only | `[decision]` when explicit; otherwise `[suggested]` from repeated source behavior. State argument flow, message/evidence discipline, page-role vocabulary, and data-expression rules without fixing a page sequence. |
|
||||||
|
| Visual-system defaults | Create Style only | `[decision]` when explicit; otherwise `[suggested]` from evidence. Palette, typography, mode, and visual-style values remain overrideable seeds, never Brand identity truth or direct final Stage-2 locks. |
|
||||||
|
| Review focus | Create Style only | `[decision]` when explicit; otherwise `[suggested]`. These checks apply only if the user separately enables visual review; the Style cannot trigger that stage. |
|
||||||
| Theme mode | Create Layout/Create Deck only | A: `[fact]` from `manifest.json` background colors. B: `[fact]` from SVG `fill`. C: `[suggested]` from visual estimate. D: `[fact]` from an independently identified external authority, `[decision]` from user-authored text in any carrier, otherwise `[suggested]`. E: `[decision]`. |
|
| Theme mode | Create Layout/Create Deck only | A: `[fact]` from `manifest.json` background colors. B: `[fact]` from SVG `fill`. C: `[suggested]` from visual estimate. D: `[fact]` from an independently identified external authority, `[decision]` from user-authored text in any carrier, otherwise `[suggested]`. E: `[decision]`. |
|
||||||
| Canvas format and dimensions | Create Layout/Create Deck only | A/B: `[fact]` from slide size or SVG `width` / `height` / `viewBox`; show `canvas_format`, `canvas_width`, `canvas_height`, `canvas_viewbox`, and `source_viewbox`. C: `[suggested]` from image aspect ratio. D: `[fact]` from an independently identified external authority or `[decision]` from user-authored text in any carrier when specified. E: `[decision]`, default `ppt169` (`1280x720`, `0 0 1280 720`). |
|
| Canvas format and dimensions | Create Layout/Create Deck only | A/B: `[fact]` from slide size or SVG `width` / `height` / `viewBox`; show `canvas_format`, `canvas_width`, `canvas_height`, `canvas_viewbox`, and `source_viewbox`. C: `[suggested]` from image aspect ratio. D: `[fact]` from an independently identified external authority or `[decision]` from user-authored text in any carrier when specified. E: `[decision]`, default `ppt169` (`1280x720`, `0 0 1280 720`). |
|
||||||
| Internal creation strategy | Create Layout/Create Deck only | `[derived]` from the confirmed natural-language plan and evidence. `standard` is the compact authored implementation, `fidelity` requires A/B page evidence for broader source-aligned coverage, and `mirror` requires A or B with a complete explicit structure contract for literal materialization. Create Layout mirror additionally requires a brand-neutral and application-neutral source. Persist `replication_mode` for tools, not as a user-facing mode. |
|
| Internal creation strategy | Create Layout/Create Deck only | `[derived]` from the confirmed natural-language plan and evidence. `standard` is the compact authored implementation, `fidelity` requires A/B page evidence for broader source-aligned coverage, and `mirror` requires A or B with a complete explicit structure contract for literal materialization. Create Layout mirror additionally requires a brand-neutral and application-neutral source. Persist `replication_mode` for tools, not as a user-facing mode. |
|
||||||
| Native structure facts | Create Layout/Create Deck with Type A or structured Type B | `[fact]` from `native_structure.json` / source SVG contract: master/layout counts, parentage, page assignments, placeholder identities, and multi-master status. Mirror preserves these validated facts in the new workspace; authored modes do not use them as output topology. |
|
| Native structure facts | Create Layout/Create Deck with Type A or structured Type B | `[fact]` from `native_structure.json` / source SVG contract: master/layout counts, parentage, page assignments, placeholder identities, and multi-master status. Mirror preserves these validated facts in the new workspace; authored modes do not use them as output topology. |
|
||||||
| Structure ownership plan | Create Layout/Create Deck only | `[derived]` from the requested result. Authored output creates new Master/Layout ownership, including the reusable-family reason for every additional Master; literal preservation maps source ownership without synthesis. Every Master must own at least one emitted Layout and every Layout must have at least one emitted prototype. |
|
| Structure ownership plan | Create Layout/Create Deck only | `[derived]` from the requested result. Authored output creates new Master/Layout ownership, including the reusable-family reason for every additional Master; literal preservation maps source ownership without synthesis. Every Master must own at least one emitted Layout and every Layout must have at least one emitted prototype. |
|
||||||
| Reference treatment | Create Layout/Create Deck when a reference exists | `[derived]` per page from the user's prose: closely reproduce geometry/decoration where requested, otherwise adapt the reference into the newly authored system. Literal materialization preserves supported source facts mechanically. |
|
| Reference treatment | Create Layout/Create Deck when a reference exists | `[derived]` per page from the user's prose: closely reproduce geometry/decoration where requested, otherwise adapt the reference into the newly authored system. Literal materialization preserves supported source facts mechanically. |
|
||||||
| Basic template norms | Yes when reference exists | Create Brand uses the identity fields and provenance rules from its child workflow. Create Layout/Create Deck use `[fact]` / `[suggested]` layout grammar, image system, density rhythm, page roster semantics, and asset policy from Step 1. |
|
| Basic template norms | Yes when reference exists | Create Brand uses the identity fields and provenance rules from its child workflow. Create Style uses portable method, page-role, evidence/data, composition/density, visual-default, and image/icon rules while discarding project-specific context. Create Layout/Create Deck use `[fact]` / `[suggested]` layout grammar, image system, density rhythm, page roster semantics, and asset policy from Step 1. |
|
||||||
| Reference source | Optional | already known if Step 1 ran |
|
| Reference source | Optional | already known if Step 1 ran |
|
||||||
| Theme color | Create Brand/Create Deck only | A: `[fact]` from theme XML. B: `[fact]` from dominant SVG `fill`. C: `[suggested]` from visual estimate (HEX is approximate). D: `[fact]` from an independently identified external authority, `[decision]` from user-authored text in any carrier, otherwise `[suggested]`. E: `[decision]`. Create Layout may use neutral preview paint but stores no identity color. |
|
| Theme color | Create Brand/Create Deck only | A: `[fact]` from theme XML. B: `[fact]` from dominant SVG `fill`. C: `[suggested]` from visual estimate (HEX is approximate). D: `[fact]` from an independently identified external authority, `[decision]` from user-authored text in any carrier, otherwise `[suggested]`. E: `[decision]`. Create Layout may use neutral preview paint but stores no identity color. |
|
||||||
| Fonts | Create Brand/Create Deck only | A: `[fact]` from `manifest.json`. B: `[fact]` from SVG `font-family`. C: font family is not derivable — use `[decision]` if the user supplies one. D: `[fact]` from an independently identified external authority or `[decision]` from user-authored text in any carrier. E: `[decision]` when a custom stack is wanted. Create Layout stores no typeface identity or final type scale; its structural text roles, alignment, wrapping, and capacity remain part of page grammar. |
|
| Fonts | Create Brand/Create Deck only | A: `[fact]` from `manifest.json`. B: `[fact]` from SVG `font-family`. C: font family is not derivable — use `[decision]` if the user supplies one. D: `[fact]` from an independently identified external authority or `[decision]` from user-authored text in any carrier. E: `[decision]` when a custom stack is wanted. Create Layout stores no typeface identity or final type scale; its structural text roles, alignment, wrapping, and capacity remain part of page grammar. |
|
||||||
| Design style | Optional | `[suggested]` from analysis |
|
| Design style | Required for Create Style; optional otherwise | `[decision]` when explicit; otherwise `[suggested]` from analysis. For Style this is an overrideable visual-direction seed, not an exact lookup token or identity lock. |
|
||||||
| Assets list | Optional | A: `[fact]` from `assets/` listing; user picks which to bundle. B/C/D: retain each file's source and let the user confirm adoption. E: none. |
|
| Assets list | Optional for Create Brand/Create Layout/Create Deck; N/A for Create Style | A: `[fact]` from `assets/` listing; user picks which to bundle. B/C/D: retain each file's source and let the user confirm adoption. E: none. Style may cite the reference textually but never adopts an asset. |
|
||||||
| Keywords | Yes | `[suggested]` from analysis (3–5 short tags); user confirms |
|
| Keywords | Create Style/Create Layout/Create Deck only | `[suggested]` from analysis (3–5 short tags); user confirms. Create Brand has no keywords field or keyword index payload. |
|
||||||
|
|
||||||
When the bundle includes Type A for Create Layout/Create Deck, also include in this message:
|
When the bundle includes Type A for Create Layout/Create Deck, also include in this message:
|
||||||
|
|
||||||
@@ -420,7 +447,7 @@ When the bundle includes Type A for Create Layout/Create Deck, also include in t
|
|||||||
|
|
||||||
The user replies with corrections, additions, or "all good".
|
The user replies with corrections, additions, or "all good".
|
||||||
|
|
||||||
> **Persist the portable brief into `design_spec.md`**. In Step 4, declare a YAML frontmatter block with the child-specific ID key (`brand_id`, `deck_id`, or `layout_id`) and only fields owned by that child. Create Brand follows its identity schema. Create Layout/Create Deck persist the confirmed portable fields (`kind`, `category`, `summary`, `keywords`, `primary_color` for deck, `page_types` for layout, `canvas_format`, `canvas_width`, `canvas_height`, `canvas_viewbox`, `source_viewbox`, `replication_mode`, `native_structure_mode`, etc.). `replication_mode` is the AI-derived implementation record, not a user selection. A Deck writes descriptive application context in Template Overview and factual prototype descriptions in Page Roster rather than duplicating that prose into frontmatter. Do not persist a generic `template_id` field: it is the parent workflow's cross-kind name, not a registrar schema key. Do not persist the execution-only `output_scope` or `target_project` fields. In library scope, `register_template.py` reads this frontmatter in Step 7 so the brief flows directly into the index without the AI re-deriving it from prose.
|
> **Persist the portable brief into `design_spec.md`**. In Step 4, declare a YAML frontmatter block with the child-specific ID key (`brand_id`, `style_id`, `deck_id`, or `layout_id`) and only fields owned by that child. Create Brand follows its identity schema. Create Style persists only `style_id`, `kind`, `summary`, and `keywords`; its method and direction live in the required body sections, and it writes no canvas, category, identity, application, replication, native-structure, page-count, or roster fields. Create Layout/Create Deck persist the confirmed portable fields (`kind`, `category`, `summary`, `keywords`, `primary_color` for deck, `page_types` for layout, `canvas_format`, `canvas_width`, `canvas_height`, `canvas_viewbox`, `source_viewbox`, `replication_mode`, `native_structure_mode`, etc.). `replication_mode` is the AI-derived implementation record, not a user selection. A Deck writes descriptive application context in Template Overview and factual prototype descriptions in Page Roster rather than duplicating that prose into frontmatter. Do not persist a generic `template_id` field: it is the parent workflow's cross-kind name, not a registrar schema key. Do not persist the execution-only `output_scope` or `target_project` fields. In library scope, `register_template.py` reads this frontmatter in Step 7 so the brief flows directly into the index without the AI re-deriving it from prose.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -444,7 +471,8 @@ Skipping this gate — including silently inferring values from reference files,
|
|||||||
- [ ] For Create Layout/Create Deck, the AI-derived internal strategy is consistent with the bundle evidence (`fidelity` requires A/B page evidence; `mirror` requires A or structured B; C/D/E channels alone permit only `standard`); Create Layout mirror evidence is already brand-neutral and application-neutral
|
- [ ] For Create Layout/Create Deck, the AI-derived internal strategy is consistent with the bundle evidence (`fidelity` requires A/B page evidence; `mirror` requires A or structured B; C/D/E channels alone permit only `standard`); Create Layout mirror evidence is already brand-neutral and application-neutral
|
||||||
- [ ] Every supplied visual, textual, documentary, web, and asset channel has been analyzed or explicitly excluded; mixed-input conflicts are surfaced rather than silently resolved
|
- [ ] Every supplied visual, textual, documentary, web, and asset channel has been analyzed or explicitly excluded; mixed-input conflicts are surfaced rather than silently resolved
|
||||||
- [ ] For Create Layout/Create Deck mirror, the source graph and supported geometry are complete; every source Layout absent from the source-slide roster is planned as a definition-only prototype, and any genuinely missing/unsupported facts were reported; Create Layout mirror contains no retained brand identity or reusable application policy
|
- [ ] For Create Layout/Create Deck mirror, the source graph and supported geometry are complete; every source Layout absent from the source-slide roster is planned as a definition-only prototype, and any genuinely missing/unsupported facts were reported; Create Layout mirror contains no retained brand identity or reusable application policy
|
||||||
- [ ] Child-specific norms from prior content have been surfaced and accepted, or explicitly marked N/A when no reference exists: identity/provenance for Create Brand; layout/image/density/asset behavior for Create Layout/Create Deck
|
- [ ] Child-specific norms from prior content have been surfaced and accepted, or explicitly marked N/A when no reference exists: identity/provenance for Create Brand; communication/evidence/visual-direction behavior for Create Style; layout/image/density/asset behavior for Create Layout/Create Deck
|
||||||
|
- [ ] For Create Style, the portable communication method, open page-role vocabulary, evidence/data rules, visual defaults, image/icon direction, and advisory review focus are confirmed; project-specific audience/outcome/page sequence and all identity/structure fields remain N/A
|
||||||
- [ ] For Create Layout/Create Deck, the plan makes structure ownership explicit: authored output creates a new structure without source-topology distillation; literal materialization maps validated source ownership one-to-one into a new workspace
|
- [ ] For Create Layout/Create Deck, the plan makes structure ownership explicit: authored output creates a new structure without source-topology distillation; literal materialization maps validated source ownership one-to-one into a new workspace
|
||||||
- [ ] For Create Deck, the recurring presentation family, intended audiences/outcomes, and representative page roles are understood without turning them into mandatory page/content policies; for Create Layout, no application contract or brand identity has leaked into the structure brief
|
- [ ] For Create Deck, the recurring presentation family, intended audiences/outcomes, and representative page roles are understood without turning them into mandatory page/content policies; for Create Layout, no application contract or brand identity has leaked into the structure brief
|
||||||
- [ ] For Create Brand, all required identity fields from its child workflow are confirmed and canvas/replication/native-structure fields remain N/A
|
- [ ] For Create Brand, all required identity fields from its child workflow are confirmed and canvas/replication/native-structure fields remain N/A
|
||||||
@@ -481,6 +509,8 @@ The preflight is atomic at the Create Template parent level: discover and settle
|
|||||||
|
|
||||||
**Create Brand branch**: continue in [`create-brand.md`](./create-template/create-brand.md) §3 with the confirmed identity brief and resolved `<template_workspace>`. Then return to the Create Brand branch in Step 5. Do not invoke Template_Designer, create SVGs, or apply the Create Layout/Create Deck-only material below.
|
**Create Brand branch**: continue in [`create-brand.md`](./create-template/create-brand.md) §3 with the confirmed identity brief and resolved `<template_workspace>`. Then return to the Create Brand branch in Step 5. Do not invoke Template_Designer, create SVGs, or apply the Create Layout/Create Deck-only material below.
|
||||||
|
|
||||||
|
**Create Style branch**: continue in [`create-style.md`](./create-template/create-style.md) §3 with the confirmed method/direction brief and resolved `<template_workspace>`. Then return to the Create Style branch in Step 5. Do not invoke Template_Designer, create SVGs, retain source page topology, or apply the Create Layout/Create Deck-only material below.
|
||||||
|
|
||||||
**Create Layout/Create Deck branch**: continue in the selected child workflow, switch to the Template_Designer role, and generate per role definition. The role input is the finalized brief from Step 3 plus the analysis bundle from Step 1, including the accepted basic template norms.
|
**Create Layout/Create Deck branch**: continue in the selected child workflow, switch to the Template_Designer role, and generate per role definition. The role input is the finalized brief from Step 3 plus the analysis bundle from Step 1, including the accepted basic template norms.
|
||||||
|
|
||||||
When the bundle includes Type A, pass the following internal package to the role:
|
When the bundle includes Type A, pass the following internal package to the role:
|
||||||
@@ -650,6 +680,27 @@ python3 skills/ppt-master/scripts/register_template.py <brand_id> --kind brand -
|
|||||||
|
|
||||||
After Create Brand validation passes, skip the Create Layout/Create Deck-only remainder of this step and all of Step 6; continue at Step 7.
|
After Create Brand validation passes, skip the Create Layout/Create Deck-only remainder of this step and all of Step 6; continue at Step 7.
|
||||||
|
|
||||||
|
**Create Style branch**: run the child workflow's §4 semantic checklist, then
|
||||||
|
the shared validator in both scopes. The validator detects `kind: style` and
|
||||||
|
mechanically enforces the frontmatter, section/field shape, conditional custom
|
||||||
|
and fallback values, portable ID, and one-file roster-free package boundary.
|
||||||
|
The child checklist remains authoritative for semantic scope and provenance.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 skills/ppt-master/scripts/svg_quality_checker.py "<template_workspace>/templates" --template-mode
|
||||||
|
```
|
||||||
|
|
||||||
|
In `library` scope, additionally run the registrar dry-run so `style_id` is
|
||||||
|
checked against the library directory/index key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 skills/ppt-master/scripts/register_template.py <style_id> --kind style --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
After Create Style validation passes, skip the Create Layout/Create Deck-only
|
||||||
|
remainder of this step and all of Step 6; continue at Step 7. Review Focus is
|
||||||
|
advisory content only and never activates the Generate visual-review stage.
|
||||||
|
|
||||||
**Create Layout/Create Deck branch**: set `<template_source>` to `<template_workspace>/templates/` in both scopes.
|
**Create Layout/Create Deck branch**: set `<template_source>` to `<template_workspace>/templates/` in both scopes.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -713,7 +764,7 @@ This step is a **hard gate**. Do not generate a review PPTX, register, or hand t
|
|||||||
|
|
||||||
## Step 6: Template Review PPTX and Multi-Master Package Gate
|
## Step 6: Template Review PPTX and Multi-Master Package Gate
|
||||||
|
|
||||||
**Trigger — Create Layout/Create Deck only**: Run when the user requests a PowerPoint review file **or** when the validated SVG roster declares more than one unique Master key. A multi-Master template requires this step even when no review artifact was requested. A one-Master template may skip directly to Step 7 when the user did not request a review file. Create Brand always skips this step because it owns no SVG roster or native structure.
|
**Trigger — Create Layout/Create Deck only**: Run when the user requests a PowerPoint review file **or** when the validated SVG roster declares more than one unique Master key. A multi-Master template requires this step even when no review artifact was requested. A one-Master template may skip directly to Step 7 when the user did not request a review file. Create Brand and Create Style always skip this step because they own no SVG roster or native structure.
|
||||||
|
|
||||||
Export the complete SVG roster, one prototype per slide, from the workspace root:
|
Export the complete SVG roster, one prototype per slide, from the workspace root:
|
||||||
|
|
||||||
@@ -753,7 +804,7 @@ Branch on the confirmed output scope:
|
|||||||
| Scope | Action |
|
| Scope | Action |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `library` | Run the registrar below after Step 5 passes and Step 6 also passes whenever it was requested or required by a multi-Master roster |
|
| `library` | Run the registrar below after Step 5 passes and Step 6 also passes whenever it was requested or required by a multi-Master roster |
|
||||||
| `project` | Skip the registrar entirely. Do not edit `decks_index.json`, `layouts_index.json`, or any library README; continue to Step 8 with index status `Not registered (project workspace)` |
|
| `project` | Skip the registrar entirely. Do not edit any global template index or library README; continue to Step 8 with index status `Not registered (project workspace)` |
|
||||||
|
|
||||||
Run the unified registrar with the kind flag; it derives the corresponding index entry from `templates/design_spec.md` (frontmatter when present, prose fallback otherwise) plus the actual `templates/*.svg` file list. The registrar retains read compatibility with old flat library packages; new creation never writes that shape:
|
Run the unified registrar with the kind flag; it derives the corresponding index entry from `templates/design_spec.md` (frontmatter when present, prose fallback otherwise) plus the actual `templates/*.svg` file list. The registrar retains read compatibility with old flat library packages; new creation never writes that shape:
|
||||||
|
|
||||||
@@ -761,6 +812,9 @@ Run the unified registrar with the kind flag; it derives the corresponding index
|
|||||||
# For brand
|
# For brand
|
||||||
python3 skills/ppt-master/scripts/register_template.py <template_id> --kind brand
|
python3 skills/ppt-master/scripts/register_template.py <template_id> --kind brand
|
||||||
|
|
||||||
|
# For style
|
||||||
|
python3 skills/ppt-master/scripts/register_template.py <template_id> --kind style
|
||||||
|
|
||||||
# For deck
|
# For deck
|
||||||
python3 skills/ppt-master/scripts/register_template.py <template_id> --kind deck
|
python3 skills/ppt-master/scripts/register_template.py <template_id> --kind deck
|
||||||
|
|
||||||
@@ -775,14 +829,37 @@ Outputs by kind (the JSON index is the single source of truth — READMEs descri
|
|||||||
| `deck` | `templates/decks/decks_index.json` |
|
| `deck` | `templates/decks/decks_index.json` |
|
||||||
| `layout` | `templates/layouts/layouts_index.json` |
|
| `layout` | `templates/layouts/layouts_index.json` |
|
||||||
| `brand` | `templates/brands/brands_index.json` |
|
| `brand` | `templates/brands/brands_index.json` |
|
||||||
|
| `style` | `templates/styles/styles_index.json` |
|
||||||
|
|
||||||
The completion card's file roster is collected by globbing `templates/*.svg` in the workspace. Legacy flat packages still use their root `*.svg` roster.
|
The Layout/Deck completion card's file roster is collected by globbing
|
||||||
|
`templates/*.svg` in the workspace. Brand/Style cards are spec-only. Legacy
|
||||||
|
flat Layout/Deck packages still use their root `*.svg` roster.
|
||||||
|
|
||||||
The index file is a **discovery index** — it lets the AI answer "what templates are available?" by listing names and workspace-root paths. It is **not** consulted to trigger [`generate-pptx`](./generate-pptx.md) Step 3. Step 3 triggers on an explicit workspace-root path supplied by the user, regardless of whether that path is registered. An unregistered workspace still works when the user gives its path; it just will not appear in discovery listings.
|
The index file is the complete **registered-library discovery source** for
|
||||||
|
Default [`generate-pptx`](./generate-pptx.md#step-3-template-candidate-preparation)
|
||||||
|
Stage-1 template controls. Step 3 prepares their candidate input without
|
||||||
|
interaction; the controls read only the four kind indexes, while chat discovery
|
||||||
|
uses the same entries to list exact workspace-root paths. Neither path scans
|
||||||
|
template directories. Selecting a registered entry and submitting Stage 1
|
||||||
|
activates installation. An exact unregistered workspace supplied by the user,
|
||||||
|
or the exact validated root handed off by this route in the current
|
||||||
|
conversation, appears as a specified candidate and is preselected when it is
|
||||||
|
the only supplied root; it remains labelled `explicit` and does not enter the
|
||||||
|
library catalog. If an explicit root exactly matches a registered canonical
|
||||||
|
root, it may be displayed as `library`. Bare names and style phrases are never
|
||||||
|
resolved implicitly or used to preselect a template.
|
||||||
|
|
||||||
> **Recommended for new templates**: declare a YAML frontmatter block at the top of `design_spec.md`. The registrar prefers it over prose extraction:
|
> **Recommended for new templates**: declare a YAML frontmatter block at the top of `design_spec.md`. The registrar prefers it over prose extraction:
|
||||||
>
|
>
|
||||||
> ```yaml
|
> ```yaml
|
||||||
|
> # style example
|
||||||
|
> ---
|
||||||
|
> style_id: consulting_analytical
|
||||||
|
> kind: style
|
||||||
|
> summary: Answer-first, evidence-led decision-document defaults without page prototypes or brand identity.
|
||||||
|
> keywords: [consulting, decision-support, evidence, analytical]
|
||||||
|
> ---
|
||||||
|
>
|
||||||
> # deck example
|
> # deck example
|
||||||
> ---
|
> ---
|
||||||
> deck_id: my_deck
|
> deck_id: my_deck
|
||||||
@@ -829,11 +906,14 @@ The index file is a **discovery index** — it lets the AI answer "what template
|
|||||||
> To rebuild every entry at once (e.g. after editing many specs), run:
|
> To rebuild every entry at once (e.g. after editing many specs), run:
|
||||||
>
|
>
|
||||||
> ```bash
|
> ```bash
|
||||||
|
> python3 skills/ppt-master/scripts/register_template.py --kind style --rebuild-all
|
||||||
> python3 skills/ppt-master/scripts/register_template.py --kind deck --rebuild-all
|
> python3 skills/ppt-master/scripts/register_template.py --kind deck --rebuild-all
|
||||||
> python3 skills/ppt-master/scripts/register_template.py --kind layout --rebuild-all
|
> python3 skills/ppt-master/scripts/register_template.py --kind layout --rebuild-all
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
README files describe each kind in prose only — they do not list templates. Discovery happens against the JSON index file; the registrar does not touch READMEs.
|
README files describe each kind in prose only — they do not list templates.
|
||||||
|
The Default Stage-1 template controls and chat discovery read the JSON index files; the
|
||||||
|
registrar does not touch READMEs.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -845,14 +925,14 @@ Produce one scope-aware, evidence-driven completion card for either location:
|
|||||||
## Template Creation Complete
|
## Template Creation Complete
|
||||||
|
|
||||||
**Template Name**: <template_id> (<display_name>)
|
**Template Name**: <template_id> (<display_name>)
|
||||||
**Kind**: brand | layout | deck
|
**Kind**: brand | style | layout | deck
|
||||||
**Output Scope**: library | project
|
**Output Scope**: library | project
|
||||||
**Workspace Path**: `<template_workspace>/`
|
**Workspace Path**: `<template_workspace>/`
|
||||||
**Template Source**: `<template_workspace>/templates/`
|
**Template Source**: `<template_workspace>/templates/`
|
||||||
**Bitmap Path**: `<template_workspace>/images/` ← omit when no bitmap was written or adopted
|
**Bitmap Path**: `<template_workspace>/images/` ← omit when no bitmap was written or adopted
|
||||||
**Imported Vector Path**: `<template_workspace>/icons/imported/` ← omit when no imported vector was written or adopted
|
**Imported Vector Path**: `<template_workspace>/icons/imported/` ← omit when no imported vector was written or adopted
|
||||||
**Review PPTX**: `<template_workspace>/exports/<template_id>_template_preview.pptx` ← Create Layout/Create Deck only; omit for Create Brand and when an optional one-Master review was not requested
|
**Review PPTX**: `<template_workspace>/exports/<template_id>_template_preview.pptx` ← Create Layout/Create Deck only; omit for Create Brand/Create Style and when an optional one-Master review was not requested
|
||||||
**Primary Color**: <hex> ← Create Brand/Create Deck only; omit for Create Layout
|
**Primary Color**: <hex> ← Create Brand/Create Deck only; omit for Create Style/Create Layout
|
||||||
**Index Registration**: Done | Not registered (project workspace)
|
**Index Registration**: Done | Not registered (project workspace)
|
||||||
|
|
||||||
### Files Included
|
### Files Included
|
||||||
@@ -867,9 +947,26 @@ Produce one scope-aware, evidence-driven completion card for either location:
|
|||||||
| `exports/<template_id>_template_preview.pptx` | Verified, when requested or required for multi-Master |
|
| `exports/<template_id>_template_preview.pptx` | Verified, when requested or required for multi-Master |
|
||||||
```
|
```
|
||||||
|
|
||||||
For Create Brand, replace the SVG/review rows with `templates/design_spec.md` plus only the real `images/*` / `icons/*` assets. Its completion card must explicitly state `SVG roster: N/A` and `Native structure: N/A`.
|
For Create Brand, replace the SVG/review rows with
|
||||||
|
`templates/design_spec.md` plus only real identity assets. For Create Style,
|
||||||
|
list only `templates/design_spec.md`. Both completion cards must explicitly
|
||||||
|
state `SVG roster: N/A` and `Native structure: N/A`; Style must also state
|
||||||
|
`Visual review trigger: N/A (advisory focus only)`.
|
||||||
|
|
||||||
The next Generate PPTX Step 3 input is the exact `<template_workspace>/` root in either scope. Step 3 resolves its `templates/design_spec.md`, ignores `exports/`, and copies or consumes `templates/` plus any existing `images/` and `icons/` as one unit. It then authors new `svg_output/` pages under the template contract and exports a new PPTX. Neither the reference PPTX/SVG nor the template prototypes are upgraded in place. A legacy-flat package root remains readable only when its semantic SVG contract is current; otherwise create a new workspace through this route.
|
The exact `<template_workspace>/` root in either scope is the
|
||||||
|
current-conversation handoff to Default Generate Step 3. It appears as the
|
||||||
|
specified candidate, defaults Stage 1 to template mode, and is preselected only
|
||||||
|
when it is the sole supplied root. After Stage 1 confirms it, the application
|
||||||
|
stage resolves `templates/design_spec.md` and always ignores `exports/`.
|
||||||
|
Brand/Layout/Deck copy or consume package-owned `templates/` plus any existing
|
||||||
|
`images/` and `icons/`; Style consumes only `templates/design_spec.md` and
|
||||||
|
ignores sibling project scaffolding. It then authors new `svg_output/` pages
|
||||||
|
under the template contract and exports a new PPTX. Neither the reference
|
||||||
|
PPTX/SVG nor the template prototypes are upgraded in place. A legacy-flat
|
||||||
|
Brand/Layout/Deck package root remains readable only when it satisfies its
|
||||||
|
current kind contract; for Layout/Deck that includes the structured SVG
|
||||||
|
contract. Style has no legacy-flat form. Otherwise create a new workspace
|
||||||
|
through this route.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -886,11 +983,11 @@ The next Generate PPTX Step 3 input is the exact `<template_workspace>/` root in
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
1. **SVG technical constraints**: Create Layout/Create Deck load [shared-standards-core.md](../references/shared-standards-core.md) plus [pptx-structure-interface.md](../references/pptx-structure-interface.md), and load [svg-effects.md](../references/svg-effects.md) only when the authored design uses those effects. Create Brand authors no SVG and loads none of these SVG modules. Do not restate the contracts in the template's `design_spec.md`.
|
1. **SVG technical constraints**: Create Layout/Create Deck load [shared-standards-core.md](../references/shared-standards-core.md) plus [pptx-structure-interface.md](../references/pptx-structure-interface.md), and load [svg-effects.md](../references/svg-effects.md) only when the authored design uses those effects. Create Brand and Create Style author no SVG and load none of these SVG modules. Do not restate the contracts in the template's `design_spec.md`.
|
||||||
2. **Color consistency**: Create Deck SVG files must use the same color scheme as `design_spec.md §II Color Scheme`; Create Layout owns no identity colors, and Create Brand owns no SVG files
|
2. **Color consistency**: Create Deck SVG files must use the same color scheme as `design_spec.md §II Color Scheme`; Create Layout owns no identity colors, Create Style owns only overrideable visual defaults, and Create Brand/Create Style own no SVG files
|
||||||
3. **Native-object mapping**: Treat Theme/Master/Layout/Placeholder as compiled PowerPoint objects, not template kinds. Layout owns topology and placement, Brand owns identity values/assets, and Deck adds descriptive recurring-application context.
|
3. **Native-object mapping**: Treat Theme/Master/Layout/Placeholder as compiled PowerPoint objects, not template kinds. Layout owns topology and placement, Brand owns identity values/assets, Style owns portable direction/method defaults, and Deck adds descriptive recurring-application context.
|
||||||
4. **Placeholder convention**: `{{}}` format only; default names listed in [Placeholder Reference](../references/template-designer.md#4-placeholder-reference-canonical-convention-overridable-per-template). Override per template via `placeholders:` frontmatter when needed.
|
4. **Placeholder convention**: `{{}}` format only; default names listed in [Placeholder Reference](../references/template-designer.md#4-placeholder-reference-canonical-convention-overridable-per-template). Override per template via `placeholders:` frontmatter when needed.
|
||||||
5. **Discovery requirement**: A library template is discoverable only after `register_template.py` has been run against it (Step 7). A project-scoped workspace intentionally stays out of global discovery and is consumed by its explicit workspace-root path.
|
5. **Discovery requirement**: A library template appears in the Default Stage-1 template selector and chat discovery only after `register_template.py` has updated its kind index (Step 7). A project-scoped workspace intentionally stays out of the library catalog and is consumed as an exact `explicit` workspace-root candidate. Step 3 only prepares candidates; Stage 1 confirms communication plus free design/template use together, and only a non-free confirmed selection installs a workspace before Stage 2.
|
||||||
6. **Review output**: Generate `exports/<template_id>_template_preview.pptx` on request and always for a multi-Master template. It is derived local evidence, never a source input during template application, and library exports stay Git-ignored.
|
6. **Review output**: Generate `exports/<template_id>_template_preview.pptx` on request and always for a multi-Master template. It is derived local evidence, never a source input during template application, and library exports stay Git-ignored. Brand and Style never generate this preview; Style Review Focus cannot activate visual review.
|
||||||
|
|
||||||
> **Full role specification**: [template-designer.md](../references/template-designer.md)
|
> **Full role specification**: [template-designer.md](../references/template-designer.md)
|
||||||
|
|||||||
+1
-1
@@ -127,4 +127,4 @@ python3 skills/ppt-master/scripts/register_template.py <deck_id> --kind deck --d
|
|||||||
python3 skills/ppt-master/scripts/register_template.py <deck_id> --kind deck
|
python3 skills/ppt-master/scripts/register_template.py <deck_id> --kind deck
|
||||||
```
|
```
|
||||||
|
|
||||||
For project scope, skip both commands. The exact workspace root becomes the next Generate PPTX Step 3 input; any separately supplied Brand or Layout workspace overrides the corresponding complete segment downstream without mutating this deck workspace.
|
For project scope, skip both commands. The exact workspace root becomes the next Generate PPTX Step 3 input; any separately supplied Brand, Style, or Layout workspace overrides the corresponding complete segment downstream without mutating this deck workspace.
|
||||||
|
|||||||
+185
@@ -0,0 +1,185 @@
|
|||||||
|
---
|
||||||
|
description: Create Style child workflow for a reusable communication method and visual-default workspace without page prototypes.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Create Style Workflow
|
||||||
|
|
||||||
|
Enter this child workflow only after [`Create Template`](../create-template.md) dispatches `kind: style`.
|
||||||
|
|
||||||
|
## Responsibility Boundary
|
||||||
|
|
||||||
|
| Owner | Responsibilities |
|
||||||
|
|---|---|
|
||||||
|
| Create Template | Child-workflow dispatch plus the shared `library` / `project` scope, confirmation gate, collision preflight, registration, completion, and Generate PPTX handoff contract |
|
||||||
|
| Create Style | Reusable communication method, page-role vocabulary, evidence discipline, visual-system defaults, image/icon direction, review focus, and the roster-free `design_spec.md` |
|
||||||
|
|
||||||
|
**Hard rule — child workflow, not a top-level route**: Create Style executes only inside Create Template. It uses the parent workflow's single shared confirmation/preflight/registration contract and never creates a competing entry route or second confirmation gate.
|
||||||
|
|
||||||
|
**Hard rule — method and defaults only**: A Style owns a reusable way to argue, express evidence, and coordinate non-binding design defaults. It owns no current-project communication contract, reusable brand identity, page geometry, canvas, SVG prototype, Master/Layout graph, placeholder contract, application contract, or visible asset inventory.
|
||||||
|
|
||||||
|
**Hard rule — no page prototypes**: A Style workspace contains only `templates/design_spec.md`. Do not create page SVGs, a review PPTX, or empty `images/`, `icons/`, or `exports/` directories.
|
||||||
|
|
||||||
|
## Invocation Points
|
||||||
|
|
||||||
|
1. Use §1–2 below for Style analysis and brief fields, then execute Create Template Steps 2–3 with those child-owned fields.
|
||||||
|
2. After Create Template Step 4 resolves and preflights `<template_workspace>`, use §3 to materialize the confirmed Style.
|
||||||
|
3. Run §4, then return its evidence to Create Template Steps 5, 7, and 8. Create Style always skips the shared structured-preview step.
|
||||||
|
|
||||||
|
## 1. Style Input Interpretation
|
||||||
|
|
||||||
|
Use every supplied reference only as evidence for reusable method and design defaults:
|
||||||
|
|
||||||
|
| Evidence | May inform | Must not become |
|
||||||
|
|---|---|---|
|
||||||
|
| Direct brief, text, document, or website | Argument flow, claim discipline, page-role vocabulary, data-expression rules, and review focus | The current project's audience, objective, outline, page count, or source claims |
|
||||||
|
| PPTX, PDF, image, or SVG reference | Visual-system tendencies, density, decoration, image treatment, and icon treatment | A copied page roster, canvas contract, Master/Layout graph, or fixed geometry |
|
||||||
|
| Brand or organization material | A lower-priority fallback direction when the user explicitly wants it generalized | Official identity truth, logos, proprietary palettes, brand voice, or trademarked presentation rules |
|
||||||
|
| Existing mode, visual-style, or image-rendering catalog entry | A preferred catalog seed plus a concise Style-owned overlay | A duplicated copy of the catalog file |
|
||||||
|
|
||||||
|
Preserve source provenance in `Style Overview`. Keep exact user-authored method decisions distinct from AI-derived defaults. Reject organization-confidential examples and do not generalize proprietary frameworks into a reusable Style.
|
||||||
|
|
||||||
|
**Reference — not a constraint**: A Style may prefer a catalog mode, visual style, image rendering, fallback palette, or fallback font stack. These values seed the normal Stage-2 solution; they are not execution locks and never bypass user confirmation.
|
||||||
|
|
||||||
|
## 2. Style Brief and Schema
|
||||||
|
|
||||||
|
Add these child-owned requirements to Create Template Step 2:
|
||||||
|
|
||||||
|
| Field | Requirement |
|
||||||
|
|---|---|
|
||||||
|
| Style ID and display name | Required; `style_id` is a filesystem-safe portable slug (prefer ASCII for interoperability) |
|
||||||
|
| Best fit | Required; describe reusable decision, explanation, or expression situations without binding a target audience or outcome |
|
||||||
|
| Reusable intent | Required; state what the method and design defaults should consistently achieve |
|
||||||
|
| Communication method | Required; argument flow, page-message discipline, and claim/evidence treatment; a preferred mode is optional |
|
||||||
|
| Page-role vocabulary | Required; reusable semantic roles and their jobs, evidence obligations, and composition tendencies; no order or inclusion policy |
|
||||||
|
| Evidence and data expression | Required; chart, table, source, and editability guidance without numeric content quotas |
|
||||||
|
| Visual-system defaults | Required; composition, density, decoration, color behavior, and typography character; catalog seeds and literal fallbacks are optional |
|
||||||
|
| Image and icon direction | Required; rendering, usage, and treatment defaults without asset inventory or page mapping |
|
||||||
|
| Review focus | Required; extra checks to apply only if the user explicitly activates visual review |
|
||||||
|
|
||||||
|
Write this roster-free schema:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
style_id: <confirmed slug>
|
||||||
|
kind: style
|
||||||
|
summary: <one-line reusable method and design-default fit>
|
||||||
|
keywords: [<three-to-five discovery tags>]
|
||||||
|
---
|
||||||
|
|
||||||
|
# <Style Name> — Style Specification
|
||||||
|
|
||||||
|
> Method and design defaults only. No project communication contract, brand identity, page structure, or SVG prototypes.
|
||||||
|
|
||||||
|
## I. Style Overview
|
||||||
|
| Property | Value |
|
||||||
|
|---|---|
|
||||||
|
| Style Name | <display name> |
|
||||||
|
| Best Fit | <reusable selection context> |
|
||||||
|
| Reusable Intent | <stable method/design outcome> |
|
||||||
|
| Sources | <source URLs, bundled references, or user brief; include date/version when known> |
|
||||||
|
|
||||||
|
## II. Communication Method
|
||||||
|
- **Preferred Mode**: <catalog id or custom; omit when none>
|
||||||
|
- **Mode References**: <catalog ids actually used by a custom seed; omit when none>
|
||||||
|
- **Mode Behavior**: <required for custom; omit for a preset>
|
||||||
|
- **Argument Flow**: <reusable reasoning progression>
|
||||||
|
- **Page Message Discipline**: <relationship among question, title, message, and proof>
|
||||||
|
- **Claim Discipline**: <treatment of facts, assumptions, implications, and recommendations>
|
||||||
|
|
||||||
|
## III. Page Role Vocabulary
|
||||||
|
| Role | Communication Job | Evidence Obligation | Composition Tendency |
|
||||||
|
|---|---|---|---|
|
||||||
|
| <semantic role> | <job> | <proof requirement> | <non-geometric tendency> |
|
||||||
|
|
||||||
|
## IV. Evidence & Data Expression
|
||||||
|
- **Argument Trace**: <claim-to-evidence relationship>
|
||||||
|
- **Charts**: <selection, labeling, annotation, and decoration behavior>
|
||||||
|
- **Tables**: <comparison, hierarchy, and emphasis behavior>
|
||||||
|
- **Sources**: <citation and uncertainty treatment>
|
||||||
|
- **Native Editability**: <when editable data/native shapes are preferred>
|
||||||
|
|
||||||
|
## V. Visual System Defaults
|
||||||
|
- **Preferred Visual Style**: <catalog id or custom; omit when none>
|
||||||
|
- **Visual Style References**: <catalog ids actually used by a custom seed; omit when none>
|
||||||
|
- **Visual Style Behavior**: <required for custom; omit for a preset>
|
||||||
|
- **Composition**: <page-scale relationships without fixed geometry>
|
||||||
|
- **Density**: <information and whitespace rhythm>
|
||||||
|
- **Decoration**: <shape, rule, elevation, and ornament behavior>
|
||||||
|
- **Color Behavior**: <role and contrast behavior; no identity claim>
|
||||||
|
- **Typography Character**: <hierarchy and register; no identity claim>
|
||||||
|
|
||||||
|
### Fallback Color Scheme
|
||||||
|
| Role | HEX | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| <role> | #RRGGBB | <fallback use> |
|
||||||
|
|
||||||
|
### Fallback Typography
|
||||||
|
| Role | Primary | Fallback Tail | Character |
|
||||||
|
|---|---|---|---|
|
||||||
|
| <role> | <family> | <ordered fallbacks> | <typographic job> |
|
||||||
|
|
||||||
|
## VI. Image & Icon Direction
|
||||||
|
- **Preferred Image Rendering**: <catalog id or custom; omit when none>
|
||||||
|
- **Image Rendering References**: <catalog ids actually used by a custom seed; omit when none>
|
||||||
|
- **Image Rendering Behavior**: <required for custom; omit for a preset>
|
||||||
|
- **Image Usage**: <semantic role and frequency tendency>
|
||||||
|
- **Image Treatment**: <crop, framing, overlay, and caption behavior>
|
||||||
|
- **Icon Treatment**: <shape/stroke/fill behavior; actual library and inventory remain Stage-2 decisions>
|
||||||
|
|
||||||
|
## VII. Review Focus
|
||||||
|
<!-- visual-review-trigger: explicit-user-only -->
|
||||||
|
> Apply this section only after the user explicitly activates visual review. It never triggers that stage.
|
||||||
|
|
||||||
|
- <style-specific answer, evidence, hierarchy, legibility, or scan-path check>
|
||||||
|
```
|
||||||
|
|
||||||
|
`Fallback Color Scheme` and `Fallback Typography` are conditional; omit either subsection when the Style has no literal fallback values. Exact fallback colors use `#RRGGBB`. A supplied Brand or Deck identity replaces overlapping fallback colors, font families, voice, and icon identity as one identity decision; it does not erase the Style's communication method or evidence discipline.
|
||||||
|
|
||||||
|
`Preferred Mode`, `Preferred Visual Style`, and `Preferred Image Rendering` are recommendation seeds. The current project's confirmed Stage-2 values remain authoritative. A preset value must be a real ID in its matching catalog. For `custom`, retain only real catalog references actually used as a comma-separated ID list and include the matching behavior prose.
|
||||||
|
|
||||||
|
`Page Role Vocabulary` is a semantic vocabulary, not a Page Roster. Do not assign order, required/optional/repeatable status, page count, filenames, Master/Layout identities, slots, or fixed/replaceable/example-only content policy.
|
||||||
|
|
||||||
|
## 3. Materialize the Confirmed Style
|
||||||
|
|
||||||
|
Create Template supplies an already resolved and collision-checked `<template_workspace>`. Write only:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<template_workspace>/
|
||||||
|
└── templates/
|
||||||
|
└── design_spec.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not create or adopt images, icons, SVGs, native payloads, or review exports. References remain textual provenance; they are not portable Style assets.
|
||||||
|
|
||||||
|
## 4. Style Validation
|
||||||
|
|
||||||
|
Return these facts to Create Template:
|
||||||
|
|
||||||
|
- `templates/design_spec.md` contains non-empty `style_id`, `kind: style`, `summary`, and three-to-five `keywords`; no other frontmatter field exists.
|
||||||
|
- `style_id` matches the confirmed workspace ID in library scope.
|
||||||
|
- Required sections I–VII exist; preset seeds resolve to real catalog IDs, while custom seeds include behavior prose and only real comma-separated catalog references.
|
||||||
|
- No `*.svg`, optional asset directory, review export, or native payload was created.
|
||||||
|
- No `primary_color`, canvas, page-count, page-type, replication, native-structure, Master/Layout, placeholder, Page Roster, or Signature Design Elements field exists.
|
||||||
|
- No current-project target audience, communication objective/outcome, delivery context, artifact afterlife, content outline, page assignment, icon inventory, or image-resource mapping exists.
|
||||||
|
- Brand-only identity sections (`Brand Overview`, `Color Scheme`, `Typography`, `Logo`, `Voice & Tone`, and `Icon Style`) and Deck-only `Template Overview` are absent. Conditional fallback subsections remain explicitly named `Fallback Color Scheme` and `Fallback Typography`.
|
||||||
|
- `Review Focus` contains exactly one `<!-- visual-review-trigger: explicit-user-only -->` marker; its localized prose explains the same boundary, and the section cannot activate visual review by itself.
|
||||||
|
|
||||||
|
For both scopes, Create Template Step 5 validates the portable Style contract without registration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 skills/ppt-master/scripts/svg_quality_checker.py "<template_workspace>/templates" --template-mode
|
||||||
|
```
|
||||||
|
|
||||||
|
For `library` scope, additionally validate the directory/index identity with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 skills/ppt-master/scripts/register_template.py <style_id> --kind style --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
After that gate passes, Create Template Step 7 registers with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 skills/ppt-master/scripts/register_template.py <style_id> --kind style
|
||||||
|
```
|
||||||
|
|
||||||
|
For `project` scope, run only the shared validator, skip both registrar commands, and report `Not registered (project workspace)`. Downstream consumption always uses the explicit workspace root through Generate PPTX Step 3; a bare Style name or ordinary style description never activates it.
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
---
|
---
|
||||||
description: Generate PPTX route authority for source intake, planning, SVG authoring, quality gates, and native PPTX export.
|
description: Default Generate PPTX authority for source intake, planning, SVG authoring, quality gates, and native PPTX export.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Generate PPTX Route
|
# Generate PPTX Route
|
||||||
|
|
||||||
> Load only after [`routing.md`](./routing.md) selects Generate PPTX. This file owns the route's Step 1–7 sequence, gates, role switching, and mandatory commands.
|
> Load only after [`routing.md`](./routing.md) selects Default Generate or its
|
||||||
|
> Beautify profile. This file owns that runtime's Step 1–7 sequence, gates, role
|
||||||
|
> switching, and mandatory commands. Explicit Quick loads its own profile instead.
|
||||||
|
|
||||||
**Default Core Pipeline**: `Initial Materials → [Fact Research] → Create Project → [Template] → Strategist Structured Plan → [Image Acquisition] → Executor Live Preview → Quality Check → Post-processing → Export`
|
**Default Core Pipeline**: `Initial Materials → [Fact Research] → Create Project → Template Candidate Preparation → Stage-1 Communication + Template Confirmation → [Template Installation] → Stage-2 Solution → [Image Acquisition] → Executor Live Preview → Quality Check → Post-processing → Export`
|
||||||
|
|
||||||
**Generate-specific execution discipline**:
|
**Generate-specific execution discipline**:
|
||||||
|
|
||||||
@@ -15,21 +17,9 @@ description: Generate PPTX route authority for source intake, planning, SVG auth
|
|||||||
- `preset_shape_svg.py` and `shape_boolean_svg.py` may provide only their documented stdout fragment(s) after the main agent chooses the object's role, operands, paint, and z-order; neither helper chooses layout or writes a page.
|
- `preset_shape_svg.py` and `shape_boolean_svg.py` may provide only their documented stdout fragment(s) after the main agent chooses the object's role, operands, paint, and z-order; neither helper chooses layout or writes a page.
|
||||||
- Gate checklists are internal verification, not user-facing output. On success, continue automatically and emit at most one compact status line when useful; on failure, report only the blocking items and required recovery.
|
- Gate checklists are internal verification, not user-facing output. On success, continue automatically and emit at most one compact status line when useful; on failure, report only the blocking items and required recovery.
|
||||||
|
|
||||||
### Quick Generate Profile Short Circuit
|
**Profile boundary**: Explicit Quick is selected before runtime authority
|
||||||
|
loading and never enters this file. Beautify enters this file only when its
|
||||||
For an explicit quick/fast, skip-strategy, or direct-SVG request, follow
|
request does not explicitly select Quick.
|
||||||
[`quick-generate.md`](./profiles/quick-generate.md). It runs applicable source
|
|
||||||
conversion/research and project-local resource preparation, lets the current
|
|
||||||
agent decide content/visual/resource details in active context, then
|
|
||||||
hand-authors SVG, runs one lockless final checker, and exports the final PPTX.
|
|
||||||
It skips Strategist, Confirm UI, Design Spec/lock, the first-page gate, and
|
|
||||||
`finalize_svg.py`.
|
|
||||||
|
|
||||||
**Hard rule — no implicit downgrade or page cap**: page count neither selects
|
|
||||||
nor blocks quick generation. Source preparation, images, icons, formulas, and
|
|
||||||
their manifests remain valid. All exporter capabilities remain available when
|
|
||||||
requested or agent-selected; use their existing prerequisites. Structured
|
|
||||||
template reuse still requires the default lock-backed pipeline.
|
|
||||||
|
|
||||||
### SVG Page-Design Boundary
|
### SVG Page-Design Boundary
|
||||||
|
|
||||||
@@ -52,7 +42,7 @@ template reuse still requires the default lock-backed pipeline.
|
|||||||
| Artifact ownership | [`artifact-ownership.md`](../references/artifact-ownership.md) | Owns fact channels, source/derived artifact boundaries, and regeneration rules |
|
| Artifact ownership | [`artifact-ownership.md`](../references/artifact-ownership.md) | Owns fact channels, source/derived artifact boundaries, and regeneration rules |
|
||||||
| Failure recovery | [`failure-recovery.md`](./governance/failure-recovery.md) | Owns stop/continue policy and resume pointers |
|
| Failure recovery | [`failure-recovery.md`](./governance/failure-recovery.md) | Owns stop/continue policy and resume pointers |
|
||||||
| Confirm UI details | [`confirm_ui.md`](../scripts/docs/confirm_ui.md) | Owns the JSON schema, launcher behavior, staged-result contract, port strategy, and chat fallback details |
|
| Confirm UI details | [`confirm_ui.md`](../scripts/docs/confirm_ui.md) | Owns the JSON schema, launcher behavior, staged-result contract, port strategy, and chat fallback details |
|
||||||
| Explicit template workspace | [`apply-template-workspace.md`](./stages/apply-template-workspace.md) | Owns Step 3 validation, installation, and fusion; load only when Step 3's explicit-path trigger fires |
|
| Confirmed template application | [`apply-template-workspace.md`](./stages/apply-template-workspace.md) | Owns validation, installation, and fusion after Stage 1 confirms library or explicit workspace roots; skip for confirmed free design |
|
||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
|
|
||||||
@@ -116,6 +106,36 @@ After reading direct and converted content, assess factual sufficiency:
|
|||||||
python3 ${SKILL_DIR}/scripts/project_manager.py init <project_name> --format <format>
|
python3 ${SKILL_DIR}/scripts/project_manager.py init <project_name> --format <format>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Project initialization creates `<project_path>/validation/workflow.log` and
|
||||||
|
records the initialization milestone. After the project exists, run each
|
||||||
|
project-scoped Python tool normally. The shared CLI bootstrap automatically
|
||||||
|
records its command envelope and a bounded set of material outcome lines in
|
||||||
|
that log; no wrapper command is required. Full console output is not copied.
|
||||||
|
Detached Confirm UI and live-preview processes retain their detailed output in
|
||||||
|
their existing component logs.
|
||||||
|
|
||||||
|
When a Python helper serves the active deck but neither its arguments nor its
|
||||||
|
working directory identifies the project, provide the routing signal on that
|
||||||
|
same command — still one Python process:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PPT_MASTER_PROJECT_PATH="<project_path>" python3 ${SKILL_DIR}/scripts/<helper>.py <args...>
|
||||||
|
```
|
||||||
|
|
||||||
|
When an important audit detail has no owning command output — for example a
|
||||||
|
material stage handoff or rework reason, a user-approved exception, or a manual
|
||||||
|
recovery choice — the active role may append one concise note:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 ${SKILL_DIR}/scripts/workflow_log.py <project_path> "<material audit detail>"
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes are selective and non-authoritative. Do not duplicate artifact contents,
|
||||||
|
routine page progress, or chain-of-thought; current artifacts and gate results
|
||||||
|
still determine stage and readiness. The transcript is cold audit evidence:
|
||||||
|
never read it during normal generation; open it only when the user explicitly
|
||||||
|
asks to review the run.
|
||||||
|
|
||||||
Format options must be named with concrete dimensions. Default: `ppt169` = `1280x720`, `viewBox="0 0 1280 720"`. Other examples: `ppt43` = `1024x768`, `story` = `1080x1920`, `banner` = `1920x1080`. For the full format list, see `references/canvas-formats.md`.
|
Format options must be named with concrete dimensions. Default: `ppt169` = `1280x720`, `viewBox="0 0 1280 720"`. Other examples: `ppt43` = `1024x768`, `story` = `1080x1920`, `banner` = `1920x1080`. For the full format list, see `references/canvas-formats.md`.
|
||||||
|
|
||||||
Import source content (choose based on the situation):
|
Import source content (choose based on the situation):
|
||||||
@@ -143,42 +163,95 @@ Direct supported bitmap inputs follow both boundaries: the original is archived
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Step 3: Template Option
|
### Step 3: Template Candidate Preparation
|
||||||
|
|
||||||
🚧 **GATE**: Step 2 complete; project directory structure is ready.
|
**Scope**: Every Default Generate run. This is internal preparation only: do not
|
||||||
|
open a page, ask a question, wait for a receipt, select a workspace, read a
|
||||||
|
template spec/prototype, or install anything. Quick resolves exact supplied
|
||||||
|
roots or free design inside its profile and skips this Step.
|
||||||
|
|
||||||
**Default — free design**: Proceed directly to Step 4. Do not query any `*_index.json`, ask about templates, suggest a local template, or fuzzy-match a name from content, brand mentions, or style language.
|
Prepare the candidate boundary that Stage 1 will confirm. Registered candidates
|
||||||
|
come from exactly these discovery sources:
|
||||||
|
|
||||||
**Explicit-path trigger only**: Load and run [`apply-template-workspace.md`](./stages/apply-template-workspace.md) only when either condition is true:
|
- `templates/brands/brands_index.json`
|
||||||
|
- `templates/styles/styles_index.json`
|
||||||
|
- `templates/layouts/layouts_index.json`
|
||||||
|
- `templates/decks/decks_index.json`
|
||||||
|
|
||||||
- The user supplied one or more explicit workspace-root paths.
|
Derive each library root as `templates/<kind_dir>/<id>/` from its index entry.
|
||||||
- Create Template completed in the current conversation and handed off its exact validated workspace root.
|
Never scan kind directories, infer unregistered entries, or resolve a bare name,
|
||||||
|
brand mention, or style phrase to a path. Preserve every exact root supplied for
|
||||||
|
this run. A registered-root equality match remains `library`; every other exact
|
||||||
|
root remains `explicit`. Candidate provenance never changes later validation,
|
||||||
|
fusion, or precedence.
|
||||||
|
|
||||||
Bare names, style descriptions, brand mentions, vague template intent, and silence do not trigger the runbook. There is no slug lookup or fuzzy path resolution.
|
Resolve the confirmation surface under
|
||||||
|
[`confirm_ui.md`](../scripts/docs/confirm_ui.md). In the UI branch, run
|
||||||
|
`--reset-template-selection`, then write
|
||||||
|
`<project_path>/confirm_ui/template_options.json` with schema version `1`,
|
||||||
|
`phase: "template"`, the UI language, and all supplied exact roots as absolute
|
||||||
|
`explicit_workspace_roots`; use an empty array when none were supplied. Also
|
||||||
|
write required `default_mode`: `templates` when the user explicitly asks to use
|
||||||
|
or browse templates or supplies any exact root, otherwise `free_design`. The
|
||||||
|
server reads the four indexes itself. Do not launch it yet. In chat/delegated
|
||||||
|
confirmation, retain the same candidate boundary in context and create no UI
|
||||||
|
artifact.
|
||||||
|
|
||||||
**Raw PPTX boundary**: A raw PPTX remains valid source material, but it is not a Step 3 workspace. Raw PPTX plus new content uses [`template-fill-pptx`](./template-fill-pptx.md). To create a reusable workspace, run [`create-template`](./create-template.md), then return with the generated root. Never add Master/Layout/placeholder structure directly to an existing PPTX or SVG project.
|
Stage 1 initializes from `default_mode`, but the user can switch modes. Template
|
||||||
|
mode alone expands the candidates and must eventually select at least one
|
||||||
|
workspace. Exactly one supplied root may be preselected as an editable default;
|
||||||
|
multiple supplied roots remain unselected candidates. `free_design` selects none.
|
||||||
|
|
||||||
> “What templates exist?” is out-of-band Q&A. List indexed workspace paths, then stop; listing does not trigger Step 3. The user must send an explicit path.
|
**Raw PPTX boundary**: A raw PPTX remains valid source material, but it is not a
|
||||||
|
template workspace candidate. Raw PPTX plus new content uses
|
||||||
|
[`template-fill-pptx`](./template-fill-pptx.md). To create a reusable workspace,
|
||||||
|
run [`create-template`](./create-template.md), then return with the generated
|
||||||
|
root. Never add Master/Layout/placeholder structure directly to an existing
|
||||||
|
PPTX or SVG project.
|
||||||
|
|
||||||
**✅ Checkpoint**: Free design selected without loading template details, or the conditional template runbook completed and `<project_path>/templates/` plus any portable assets are ready.
|
**✅ Checkpoint**: Candidate input is ready for the combined Stage-1
|
||||||
|
confirmation. No template has been selected, read, validated, fused, or
|
||||||
|
installed. Proceed to Step 4 without a user-visible stop.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Step 4: Strategist Phase (MANDATORY in the default pipeline)
|
### Step 4: Strategist Phase (MANDATORY in the default pipeline)
|
||||||
|
|
||||||
🚧 **GATE**: Step 3 complete; default free-design path taken, or (if triggered) template files copied or confirmed in place in the project.
|
🚧 **GATE**: Source preparation and Step-3 candidate preparation are
|
||||||
|
complete. No template content has entered planning context and no template has
|
||||||
|
been installed. Stage 1 has not started before this point.
|
||||||
|
|
||||||
|
**Hard rule — Stage 1 is template-independent**: Author every Stage-1
|
||||||
|
communication recommendation from the user's current request, source facts,
|
||||||
|
conversation constraints, and project-initialization state only. Candidate
|
||||||
|
paths, index summaries, template specs/prototypes/assets, and template canvas
|
||||||
|
are not recommendation evidence. Author the communication proposal before any
|
||||||
|
chat-branch catalog listing. The project initialization canvas remains the
|
||||||
|
Stage-1 starting value unless the current user/source context changes it.
|
||||||
|
Template inspection and current-project fit begin only after Stage 1 confirms
|
||||||
|
both the communication contract and template/free-design choice and any selected
|
||||||
|
workspace has been installed.
|
||||||
|
|
||||||
|
At Step-4 entry, load the always-required planning context directly in one
|
||||||
|
batch: the role core, every canonical content-type source file defined below,
|
||||||
|
and the compact structured analysis facts already present. These inputs are
|
||||||
|
known required reads; do not route or rediscover them one file at a time. For a
|
||||||
|
multi-deck `source_profile.json`, read its compact `decks[]` digests in that
|
||||||
|
batch and open a deck's larger identity/slide-library files only when the
|
||||||
|
specific need below arises.
|
||||||
|
|
||||||
First, read the role core, then only the modules triggered by the current plan:
|
|
||||||
```
|
```
|
||||||
Read references/strategist.md
|
Read references/strategist.md
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Then load only the extra role modules triggered by the current plan:
|
||||||
|
|
||||||
| Deterministic trigger | Additional Strategist reference |
|
| Deterministic trigger | Additional Strategist reference |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Step 3 installed an explicit Brand/Layout/Deck workspace | `references/strategist-template.md` |
|
| Stage 1 is confirmed and its template choice installed a selected Brand/Style/Layout/Deck workspace into this project | `references/strategist-template.md` before Stage 2 |
|
||||||
| The core's proposed Stage 2 `image_usage` contains a source other than `none`, the user supplied an explicit non-`none` image constraint, or formula-worthy content activates formula planning | `references/strategist-image.md` + `references/image-layout-spec.md` + `references/image-layout-patterns.md` before authoring image renderings, production detail, formula resources, or §VIII |
|
| The core's proposed Stage 2 `image_usage` contains a source other than `none`, the user supplied an explicit non-`none` image constraint, or formula-worthy content activates formula planning | `references/strategist-image.md` + `references/image-layout-spec.md` + `references/image-layout-patterns.md` before authoring image renderings, production detail, formula resources, or §VIII |
|
||||||
|
|
||||||
Core chooses Stage-2 sources. Load it before Stage 2 for non-`none`, or after confirmation if `none` changes; do not backfill candidates. Retain for confirmed non-`none` or formulas; otherwise write no image rows. Bare template/style names do not load the template module.
|
Core chooses Stage-2 sources. Load it before Stage 2 for non-`none`, or after confirmation if `none` changes; do not backfill candidates. Retain for confirmed non-`none` or formulas; otherwise write no image rows. Only an installed project-local template state loads the template module, and only after Stage 1 is confirmed; a bare template/style name does not.
|
||||||
|
|
||||||
> ⚠️ **Mandatory artifact gates**: after final confirmation, author complete `design_spec.md` from `${SKILL_DIR}/templates/design_spec_reference.md`. After Gate 1 and any refinement approval, author `spec_lock.md` from `${SKILL_DIR}/templates/spec_lock_reference.md` plus approved Design Spec/context. Author each new artifact once without placeholders or `scaffold-*` (manual-only). Schema validity does not prove semantic fidelity.
|
> ⚠️ **Mandatory artifact gates**: after final confirmation, author complete `design_spec.md` from `${SKILL_DIR}/templates/design_spec_reference.md`. After Gate 1 and any refinement approval, author `spec_lock.md` from `${SKILL_DIR}/templates/spec_lock_reference.md` plus approved Design Spec/context. Author each new artifact once without placeholders or `scaffold-*` (manual-only). Schema validity does not prove semantic fidelity.
|
||||||
|
|
||||||
@@ -190,57 +263,91 @@ Core chooses Stage-2 sources. Load it before Stage 2 for non-`none`, or after co
|
|||||||
|
|
||||||
**Confirmation orchestration**: field meaning and recommendation logic belong to the active Strategist modules; [`confirm_ui.md`](../scripts/docs/confirm_ui.md) owns the JSON schema, server lifecycle, staged-result contract, port behavior, and equivalent chat fallback.
|
**Confirmation orchestration**: field meaning and recommendation logic belong to the active Strategist modules; [`confirm_ui.md`](../scripts/docs/confirm_ui.md) owns the JSON schema, server lifecycle, staged-result contract, port behavior, and equivalent chat fallback.
|
||||||
|
|
||||||
⛔ **BLOCKING**: Unless explicitly delegated, the three-stage Strategist confirmation is the single always-on user gate. An enabled `refine_spec` adds the one conditional chat gate after Design Spec Gate 1. In the UI branch, keep Stage 1/2 handoffs in one turn and author the next stage after each wait. In the chat branch, wait for an explicit user response at each stage. Author each stage once; submitted values—including blanks or unusual overrides—are authoritative.
|
⛔ **BLOCKING**: The two-stage Strategist confirmation is the always-on user
|
||||||
|
gate unless explicitly delegated. Stage 1 confirms the communication contract
|
||||||
|
and, on the same screen or in the same chat turn, exactly one template mode:
|
||||||
|
`free_design` or `templates`. Only `templates` expands the four registered-kind
|
||||||
|
selectors plus supplied exact-root candidates, and it requires at least one
|
||||||
|
selection. Final Stage 2 confirms the complete deck solution plus production
|
||||||
|
mechanics only after the Stage-1 choice is installed or its free-design handoff
|
||||||
|
is complete. An enabled `refine_spec` adds the one conditional chat gate after
|
||||||
|
Design Spec Gate 1. Author each stage once; submitted values—including blanks or
|
||||||
|
unusual overrides—are authoritative.
|
||||||
|
|
||||||
**Confirmation ownership and surface**: Only the user confirms. Before any
|
**Confirmation ownership and surface**: Only the user confirms. Before any
|
||||||
server command, apply `confirm_ui.md`'s surface decision to this run's most
|
confirmation server command, apply
|
||||||
recent explicit surface instruction and retain that branch as the owner
|
`confirm_ui.md`'s surface
|
||||||
specifies. A natural-language request or agreement to personally confirm in
|
decision to this run's most recent explicit surface instruction and retain that
|
||||||
chat, or to avoid the page, selects the chat branch without a magic keyword;
|
branch as the owner specifies. A natural-language request or agreement to
|
||||||
skip `--daemon`, every `--wait-only`, and UI `result.json`. Explicit delegation
|
personally confirm in chat, or to avoid the page, selects the chat branch without
|
||||||
is a separate higher-priority branch. With no surface instruction, fresh Stage
|
a magic keyword; skip UI launch/wait commands and UI-authored result state.
|
||||||
1 uses the default UI branch: launch, post the required chat handoff, then wait.
|
Explicit delegation is a separate higher-priority branch. With no surface
|
||||||
A chat-question tool alone does not replace that default. The agent may write
|
instruction, use the default UI branch. A chat-question tool alone does not
|
||||||
recommendations, operate the server, and read state, but MUST NOT call
|
replace that default. The agent may author recommendations, operate the
|
||||||
`/api/confirm`, automate submission, synthesize a payload, or write/replace
|
server, read state, and apply a selected template, but MUST NOT confirm on the
|
||||||
`result.json`. Delegation applies only to this run: show the complete
|
user's behalf, automate submission, synthesize a payload, or write/replace user
|
||||||
three-stage summary and never fabricate UI results. Silence confirms nothing.
|
result state. Delegation applies only to this run: make the Stage-1 communication
|
||||||
|
and template decision, install any selection, then derive and show the complete
|
||||||
|
Stage-2 summary without fabricating UI results. Silence confirms nothing.
|
||||||
|
|
||||||
**UI branch files and completion evidence:**
|
**UI branch files and completion evidence:**
|
||||||
|
|
||||||
| Stage file (the active unconfirmed stage may be overwritten) | Strategist writes | Completion evidence |
|
| Input file (only the active unconfirmed Strategist stage may be overwritten) | Agent writes | Completion evidence |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `confirm_ui/recommendations.stage1.json` | Communication contract, `content_divergence`, and canvas only | `status: stage1-confirmed` |
|
| `confirm_ui/template_options.json` | Candidate schema/language plus supplied exact roots; library entries remain server-owned index data | Stage-1 submission writes user-owned `template_selection.json` with `phase: template`, `status: confirmed` |
|
||||||
| `confirm_ui/recommendations.stage2.json` | Complete deck solution from the confirmed contract; never skip for a template | `status: stage2-confirmed` |
|
| `confirm_ui/recommendations.stage1.json` | Communication contract, `content_divergence`, and canvas only; no template-derived recommendation | The same submission writes `result.json` with `status: stage1-confirmed` |
|
||||||
| `confirm_ui/recommendations.stage3.json` | Production mechanics only: conditional AI path, formula policy, generation mode, refine-spec, proactive speaker notes, custom animations, and narration audio | `stage: final`, `status: confirmed` |
|
| `confirm_ui/template_handoff.json` | Only through `--complete-template-selection`, after the Stage-1 selection and free-design closure or successful installation | `status: ready`, bound to the current selection hash; prerequisite for Stage 2 |
|
||||||
|
| `confirm_ui/recommendations.stage2.json` | `stage: stage2`; complete deck solution plus conditional AI path, formula policy, generation mode, refine-spec, proactive speaker notes, custom animations, and narration audio | `stage: final`, `status: confirmed` |
|
||||||
|
|
||||||
If the user rejects the current recommendation before confirming it, regenerate by overwriting that same stage file and have the page refresh; do not create revision-suffixed files. This never authorizes one stage file to carry another stage's payload.
|
If the user rejects the current recommendation before confirming it, regenerate by overwriting that same stage file and have the page refresh; do not create revision-suffixed files. This never authorizes one stage file to carry another stage's payload.
|
||||||
|
|
||||||
**UI branch only** — create `confirm_ui/recommendations.stage1.json`, then:
|
**UI branch only** — Step 3 wrote `template_options.json` but did not launch or
|
||||||
|
wait. Create `confirm_ui/recommendations.stage1.json` without reading template
|
||||||
|
candidate content, then launch the combined Stage-1 page and post
|
||||||
|
`confirm_ui.md`'s required communication + template-choice summary/fallback:
|
||||||
|
|
||||||
1. Run in order:
|
```bash
|
||||||
|
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --daemon
|
||||||
|
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --wait-only --wait-stage stage1
|
||||||
|
```
|
||||||
|
|
||||||
|
The single Stage-1 submission writes both `result.json` and
|
||||||
|
`template_selection.json`; neither replaces the other. Read each exactly once.
|
||||||
|
Require a confirmed communication result and either `free_design` with no roots
|
||||||
|
or `templates` with at least one server-resolved root.
|
||||||
|
|
||||||
|
1. For `templates`, load and run
|
||||||
|
[`apply-template-workspace.md`](./stages/apply-template-workspace.md) against
|
||||||
|
every confirmed exact root. It validates/fuses them and installs one
|
||||||
|
project-local state under `templates/` plus any real `images/` and `icons/`.
|
||||||
|
For `free_design`, skip installation. Then bind the completed state:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --daemon
|
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --complete-template-selection
|
||||||
# Post confirm_ui.md's actual URL + Stage-1 summary/chat fallback here.
|
|
||||||
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --wait-only --wait-stage stage1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Read Stage 1. Derive proposed image sources, load the triggered image-planning bundle above, and apply `strategist-template.md` when active. Create `confirm_ui/recommendations.stage2.json` without changing Stage 1, then wait:
|
This agent-only command writes `template_handoff.json`; do not hand-author
|
||||||
|
it. The server requires this handoff before Stage 2.
|
||||||
|
|
||||||
```bash
|
2. Only now inspect installed template state and apply
|
||||||
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --wait-only --wait-stage stage2
|
`strategist-template.md` when active. Derive the complete deck solution and
|
||||||
```
|
production defaults, loading the triggered image-planning bundle above.
|
||||||
|
Create `confirm_ui/recommendations.stage2.json` without changing Stage 1;
|
||||||
3. Read Stage 2; load the image bundle if it newly confirms non-`none`. Create `confirm_ui/recommendations.stage3.json` without changing prior stages and wait:
|
declare `stage: "stage2"`, then wait for the final confirmation:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --wait-only
|
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --wait-only
|
||||||
```
|
```
|
||||||
|
|
||||||
4. After the final wait returns, read the complete `result.json` exactly once and retain that object through Design Spec authoring and its fidelity audit. Proceed only when it carries `stage: final` and `status: confirmed`. Do not reopen the file during normal lock authoring or downstream execution. On a non-zero wait, this same single read determines whether the persisted result succeeded before using the documented chat fallback. A stage-skip result returns to the missing stage; it is not a browser failure.
|
3. After the final wait returns, read the complete `result.json` exactly once
|
||||||
|
and retain that object through Design Spec authoring and its fidelity audit.
|
||||||
|
Proceed only when it carries `stage: final` and `status: confirmed`. Do not
|
||||||
|
reopen the file during normal lock authoring or downstream execution. On a
|
||||||
|
non-zero wait, this same single read determines whether the persisted result
|
||||||
|
succeeded before using the documented chat fallback. A stage-skip result
|
||||||
|
returns to the missing stage; it is not a browser failure.
|
||||||
|
|
||||||
5. After final confirmation or chat fallback, always release the server:
|
4. After final confirmation or chat fallback, always release the server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --shutdown
|
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --shutdown
|
||||||
@@ -251,13 +358,19 @@ apply `confirm_ui.md`'s in-run switch procedure. Continue the unresolved current
|
|||||||
stage and all remaining stages in chat; do not enter UI interruption recovery
|
stage and all remaining stages in chat; do not enter UI interruption recovery
|
||||||
or relaunch the server.
|
or relaunch the server.
|
||||||
|
|
||||||
**Chat branch** — run the same three stages in chat with explicit user
|
**Chat branch** — present the template mode and Stage-1 communication contract
|
||||||
responses, retaining one visible cumulative confirmation summary as the
|
together and wait for one explicit response. Show registered candidates only
|
||||||
equivalent final state; do not create or require a UI result. If the user
|
when the user chooses `templates`; supplied exact roots remain available in that
|
||||||
explicitly delegated confirmation, consolidate the same three stages into one
|
expanded choice. Initialize free design for an ordinary request and template
|
||||||
AI-authored summary. Otherwise the no-selection UI branch uses the always-on
|
mode for explicit template intent or any exact root; with exactly one root it
|
||||||
Stage-1 chat handoff, which keeps direct-chat fallback visible without replacing
|
may also be the preselected candidate, while multiple roots remain unselected.
|
||||||
UI confirmation.
|
Do not create UI receipts
|
||||||
|
or call `--complete-template-selection`. After confirmation, install/fuse any
|
||||||
|
selected roots (or close free design) and retain that completed state in context
|
||||||
|
as the Stage-2 gate. Then run final Stage 2 in chat and retain one visible
|
||||||
|
cumulative summary as the equivalent final state. Under explicit delegation,
|
||||||
|
make the same Stage-1 decision, install it, derive Stage 2, and present one
|
||||||
|
complete AI-authored summary.
|
||||||
|
|
||||||
⛔ **GATE — final state → Design Spec → conditional review → lock.** Consume every present final value once into the complete, audited `design_spec.md` under [`strategist.md`](../references/strategist.md) §6.2. Preserve each owning semantic type and all production, typography, image-source, and `image_notes` obligations; acceptance never turns a Reference/Permission into a Literal. Do not reopen `result.json`.
|
⛔ **GATE — final state → Design Spec → conditional review → lock.** Consume every present final value once into the complete, audited `design_spec.md` under [`strategist.md`](../references/strategist.md) §6.2. Preserve each owning semantic type and all production, typography, image-source, and `image_notes` obligations; acceptance never turns a Reference/Permission into a Literal. Do not reopen `result.json`.
|
||||||
|
|
||||||
@@ -265,25 +378,25 @@ With `refine_spec: true`, run [`refine-spec`](stages/refine-spec.md) after Gate
|
|||||||
|
|
||||||
After the review closes, author `spec_lock.md` from the approved Design Spec and context. Preserve identity/refinements, every recurring typography role, reusable routing anchors, and each placed image's source/layout suggestion/crop policy; omit page-local garnish and never write a separate image palette. Apply `strategist-template.md` §3 when active. Unhonorable requirements follow [`failure-recovery.md`](governance/failure-recovery.md).
|
After the review closes, author `spec_lock.md` from the approved Design Spec and context. Preserve identity/refinements, every recurring typography role, reusable routing anchors, and each placed image's source/layout suggestion/crop policy; omit page-local garnish and never write a separate image palette. Apply `strategist-template.md` §3 when active. Unhonorable requirements follow [`failure-recovery.md`](governance/failure-recovery.md).
|
||||||
|
|
||||||
**Conditional — split-mode note** (not a separate confirmation): after listing the Strategist confirmation stage details, append one short line (rendered in the user's language, prefixed with 💡) only when the confirmed mode is `split` or upstream-load signals make a fresh execution context materially useful. Judge those signals from recommended page count, source-material bulk, and substantial `topic-research` web-fetch accumulation:
|
**Conditional — split-mode note** (not a separate confirmation): after listing the Strategist confirmation stage details, append one short line (rendered in the user's language, prefixed with 💡) only when the confirmed mode is `split` or upstream-load signals make a fresh execution context materially useful. Judge those signals from recommended page count, source-material bulk, and research material actually retained in this chat. Raw fetches performed by a successful isolated `topic-research` worker do not count; substantial local-fallback fetches or unusually large imported research artifacts do.
|
||||||
|
|
||||||
| Signal read | Line content |
|
| Signal read | Line content |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Heavy (long page count / bulky sources / heavy web-fetch accumulation) | State estimated page count and large source size; recommend switching to [split mode](stages/resume-execute.md) after Step 5 — stop this chat, open a fresh window and input `继续生成 projects/<project_name>` to enter the execution session (SVG generation + export); no response or "continue" = default continuous mode. |
|
| Heavy (long page count / bulky sources / heavy retained research context) | State the applicable heavy signals; recommend switching to [split mode](stages/resume-execute.md) after Step 5 — stop this chat, open a fresh window and input `继续生成 projects/<project_name>` to enter the execution session (SVG generation + export); no response or "continue" = default continuous mode. |
|
||||||
| Explicit `split` selection | Confirm that planning will stop after Step 5 and give the `继续生成 projects/<project_name>` handoff command. |
|
| Explicit `split` selection | Confirm that planning will stop after Step 5 and give the `继续生成 projects/<project_name>` handoff command. |
|
||||||
|
|
||||||
For the normal/default `continuous` path, print no split-mode reminder and proceed automatically. Confirm UI still exposes the generation-mode toggle and records it in `result.json`; a chat fallback captures the same choice in its confirmation summary without adding a separate reminder.
|
For the normal/default `continuous` path, print no split-mode reminder and proceed automatically. Confirm UI still exposes the generation-mode toggle and records it in `result.json`; a chat fallback captures the same choice in its confirmation summary without adding a separate reminder.
|
||||||
|
|
||||||
**Mandatory — spec-refinement note** (not another Confirm UI stage): after confirmation details and any split-mode line, append one localized 💡 line offering review of the complete Design Spec before the lock; any part may be revised in chat until explicit approval. Default OFF; only explicit chat opt-in or `refine_spec: true` runs [`refine-spec`](stages/refine-spec.md) after Gate 1. Confirm UI records the toggle; chat fallback prints the same line.
|
**Mandatory — spec-refinement note** (not another Confirm UI stage): after confirmation details and any split-mode line, append one localized 💡 line offering review of the complete Design Spec before the lock; any part may be revised in chat until explicit approval. Default OFF; only explicit chat opt-in or `refine_spec: true` runs [`refine-spec`](stages/refine-spec.md) after Gate 1. Confirm UI records the toggle; chat fallback prints the same line.
|
||||||
|
|
||||||
**Formula policy**: Stage 3 confirms `mixed`, `render-all`, or `text-only`. When rendering is required, load the image-planning bundle even if `image_usage` is `none`, then follow [`strategist-image.md`](../references/strategist-image.md)'s formula-resource contract. `text-only` creates no formula image rows.
|
**Formula policy**: Final Stage 2 confirms `mixed`, `render-all`, or `text-only`. When rendering is required, load the image-planning bundle even if `image_usage` is `none`, then follow [`strategist-image.md`](../references/strategist-image.md)'s formula-resource contract. `text-only` creates no formula image rows.
|
||||||
|
|
||||||
**Proactive production decisions**: Stage 3 records
|
**Proactive production decisions**: Final Stage 2 records
|
||||||
`proactive_speaker_notes`, `proactive_custom_animations`, and
|
`proactive_speaker_notes`, `proactive_custom_animations`, and
|
||||||
`proactive_narration_audio`. They control only what the agent initiates when the
|
`proactive_narration_audio`. They control only what the agent initiates when the
|
||||||
user has not already given an explicit instruction. Resolve each effective
|
user has not already given an explicit instruction. Resolve each effective
|
||||||
outcome as latest explicit user instruction → Stage 3 value → compatibility
|
outcome as latest explicit user instruction → final Stage-2 value → workflow
|
||||||
default `true` / `false` / `false`. Stage 3 Narration Audio enabled raises a
|
default `true` / `false` / `false`. Final Stage-2 Narration Audio enabled raises a
|
||||||
non-explicitly-disabled Speaker Notes outcome to enabled and names that
|
non-explicitly-disabled Speaker Notes outcome to enabled and names that
|
||||||
dependency in its provenance without rewriting the raw proactive preference.
|
dependency in its provenance without rewriting the raw proactive preference.
|
||||||
Persist the resolved effective outcomes plus provenance as the `Speaker Notes`,
|
Persist the resolved effective outcomes plus provenance as the `Speaker Notes`,
|
||||||
@@ -321,7 +434,7 @@ For a new project, use the reference-first whole-document sequence:
|
|||||||
|
|
||||||
Final state → initial Design Spec mismatch, approved Design Spec/context → lock mismatch, or an unapplied revision blocks despite schema validity. `validate` does not prove fidelity. Repair from retained confirmation before refinement; during it, preserve unaffected values and apply explicit revisions. After approval, derive the lock from that Design Spec/context. Resume/refine edits existing files, never scaffolds. Fresh recovery alone may reread persisted final evidence once.
|
Final state → initial Design Spec mismatch, approved Design Spec/context → lock mismatch, or an unapplied revision blocks despite schema validity. `validate` does not prove fidelity. Repair from retained confirmation before refinement; during it, preserve unaffected values and apply explicit revisions. After approval, derive the lock from that Design Spec/context. Resume/refine edits existing files, never scaffolds. Fresh recovery alone may reread persisted final evidence once.
|
||||||
|
|
||||||
**✅ Internal checkpoint — Phase deliverables complete**: facts read; confirmation consumed once; Design Spec passed Gate 1; enabled refinement approved; lock derived from it; split handling resolved; communication and every §IX `Audience move` validated. Do not print this checklist; auto-proceed.
|
**✅ Internal checkpoint — Phase deliverables complete**: facts read; confirmation consumed once; final Stage-2 production fields resolved (formula policy, generation mode, refine-spec, proactive choices, and conditional AI path); Design Spec passed Gate 1; enabled refinement approved; lock derived from it; split handling resolved; communication and every §IX `Audience move` validated. Do not print this checklist; auto-proceed.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -395,17 +508,21 @@ Workflow:
|
|||||||
|
|
||||||
**Artifact ownership**: `svg_output/` is the author source, `svg_final/` is derived, and image facts come from the regenerated `analysis/image_analysis.csv`; see [`references/artifact-ownership.md`](../references/artifact-ownership.md).
|
**Artifact ownership**: `svg_output/` is the author source, `svg_final/` is derived, and image facts come from the regenerated `analysis/image_analysis.csv`; see [`references/artifact-ownership.md`](../references/artifact-ownership.md).
|
||||||
|
|
||||||
Read the execution references for this deck's locked `mode` + `visual_style` (from `spec_lock.md`):
|
Read the execution references for this deck's locked `mode` + `visual_style`
|
||||||
|
(from `spec_lock.md`). Load this fixed required block directly as one batch;
|
||||||
|
do not route among its files one at a time:
|
||||||
```
|
```
|
||||||
Read references/executor-base.md # REQUIRED: flat/shared execution core
|
Read references/executor-base.md # REQUIRED: flat/shared execution core
|
||||||
Read references/shared-standards-core.md # REQUIRED: SVG compatibility core
|
Read references/shared-standards-core.md # REQUIRED: SVG compatibility + shared aesthetic/leading baseline
|
||||||
Read references/svg-effects.md # REQUIRED: advanced visual effects and construction vocabulary
|
Read references/svg-effects.md # REQUIRED: Visual Job Router + effects/construction vocabulary
|
||||||
Read references/native-shape-authoring.md # REQUIRED: native-shape selection and Boolean construction
|
Read references/native-shape-authoring.md # REQUIRED: native-shape selection and Boolean construction
|
||||||
Read references/semantic-svg.md # REQUIRED: semantic metadata boundary
|
Read references/semantic-svg.md # REQUIRED: semantic metadata boundary
|
||||||
Read references/modes/<resolved-id>.md # one preset id, or each `mode_references` id
|
Read references/modes/<resolved-id>.md # one preset id, or each `mode_references` id
|
||||||
Read references/visual-styles/<resolved-id>.md # one preset id, or each `visual_style_references` id
|
Read references/visual-styles/<resolved-id>.md # one preset id, or each `visual_style_references` id
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Keep the core's shared visual-quality defaults and `svg-effects.md` §6.1 Visual Job Router active during page authoring; they are not passive compatibility reading. Explicit user/template requirements and the locked style override compatible aesthetic defaults, never technical Required / Forbidden boundaries.
|
||||||
|
|
||||||
> Read only the always-on references above plus the conditionally triggered modules below. A preset reads its one locked file. For `mode: custom` or `visual_style: custom`, read every exact file named by the optional `mode_references` / `visual_style_references`, then synthesize those sources under the corresponding behavior. If the reference field is absent, the direction is genuinely novel: read no preset file and follow the behavior directly. Never infer adjacent references or glob `modes/` / `visual-styles/`.
|
> Read only the always-on references above plus the conditionally triggered modules below. A preset reads its one locked file. For `mode: custom` or `visual_style: custom`, read every exact file named by the optional `mode_references` / `visual_style_references`, then synthesize those sources under the corresponding behavior. If the reference field is absent, the direction is genuinely novel: read no preset file and follow the behavior directly. Never infer adjacent references or glob `modes/` / `visual-styles/`.
|
||||||
|
|
||||||
| Deterministic trigger | Additional references |
|
| Deterministic trigger | Additional references |
|
||||||
@@ -464,7 +581,7 @@ group on both pages.
|
|||||||
|
|
||||||
`template_reuse_scope: mirror|layout` pages MUST start from the complete `page_layouts` SVG, keep inherited visible objects, and preserve root Master/Layout identity plus stable atoms/slots. Strict preserves that reusable contract; under `layout`, the once-loaded Design Spec's `Template Application` may still authorize carrier text/tspan reflow inside unchanged slot bounds. Adaptive uses the current or new Layout key/name already declared by Strategist. If construction proves that fixed atoms or slot topology/bounds must change, stop and return upstream for Strategist to repair the owning plan and lock, validate and read back the affected fragments, then resume; Executor never mutates `spec_lock.md`. `mirror` changes only visible text values while preserving text/tspan topology and attributes. `style` follows the flat paragraph below without structure metadata.
|
`template_reuse_scope: mirror|layout` pages MUST start from the complete `page_layouts` SVG, keep inherited visible objects, and preserve root Master/Layout identity plus stable atoms/slots. Strict preserves that reusable contract; under `layout`, the once-loaded Design Spec's `Template Application` may still authorize carrier text/tspan reflow inside unchanged slot bounds. Adaptive uses the current or new Layout key/name already declared by Strategist. If construction proves that fixed atoms or slot topology/bounds must change, stop and return upstream for Strategist to repair the owning plan and lock, validate and read back the affected fragments, then resume; Executor never mutates `spec_lock.md`. `mirror` changes only visible text values while preserving text/tspan topology and attributes. `style` follows the flat paragraph below without structure metadata.
|
||||||
|
|
||||||
`template_reuse_scope: style`, free-design, and brand-only pages use `pptx_structure.mode: flat`. Draw the complete page directly: keep backgrounds, repeated chrome, headings, text, images, and decoration as ordinary Slide-local SVG content. Do not plan `pptx_masters` / `pptx_layouts` / `page_pptx_layouts`, do not add root Master/Layout identity, and do not add `data-pptx-layer` or `data-pptx-placeholder` metadata. Group logical content normally with top-level `<g id>` elements. Export materializes one clean project-owned Master plus one Blank Layout, applies the locked theme colors/fonts/title-body defaults, removes stock content placeholders and unused built-in Layouts, and retains only the standard date/footer/slide-number capability hooks. It does not promote or deduplicate page content.
|
`template_reuse_scope: style`, Style-only, free-design, and brand-only pages use `pptx_structure.mode: flat`. A Style-only workspace always derives `template_reuse_scope: style`; Style never supplies prototype mappings. When fused with Layout/Deck, Style changes only Direction / method and follows the selected non-Style structure plan. On a flat page, draw the complete page directly: keep backgrounds, repeated chrome, headings, text, images, and decoration as ordinary Slide-local SVG content. Do not plan `pptx_masters` / `pptx_layouts` / `page_pptx_layouts`, do not add root Master/Layout identity, and do not add `data-pptx-layer` or `data-pptx-placeholder` metadata. Group logical content normally with top-level `<g id>` elements. Export materializes one clean project-owned Master plus one Blank Layout, applies the locked theme colors/fonts/title-body defaults, removes stock content placeholders and unused built-in Layouts, and retains only the standard date/footer/slide-number capability hooks. It does not promote or deduplicate page content.
|
||||||
|
|
||||||
Do not duplicate specialized identity with `data-pptx-role`. Add it only to structural page-frame objects whose package, page-number, or animation behavior is not already expressed by `data-pptx-layer`, `data-pptx-placeholder`, or `data-pptx-replace-with`; such an element needs a stable unique `id`. Do not add generic content roles to ordinary titles, body text, cards, KPIs, diagrams, charts, icons, or images. Full contract: [`references/semantic-svg.md`](../references/semantic-svg.md).
|
Do not duplicate specialized identity with `data-pptx-role`. Add it only to structural page-frame objects whose package, page-number, or animation behavior is not already expressed by `data-pptx-layer`, `data-pptx-placeholder`, or `data-pptx-replace-with`; such an element needs a stable unique `id`. Do not add generic content roles to ordinary titles, body text, cards, KPIs, diagrams, charts, icons, or images. Full contract: [`references/semantic-svg.md`](../references/semantic-svg.md).
|
||||||
|
|
||||||
@@ -592,7 +709,7 @@ stage preserves or produces `<project_path>/animations.json`, keep the base comm
|
|||||||
the exporter reads the sidecar automatically. Explicit motion flags override
|
the exporter reads the sidecar automatically. Explicit motion flags override
|
||||||
the corresponding sidecar default/slide fields, while group overrides remain
|
the corresponding sidecar default/slide fields, while group overrides remain
|
||||||
unless `-a none` hard-disables object motion. Exception: explicit Custom
|
unless `-a none` hard-disables object motion. Exception: explicit Custom
|
||||||
Animations disable keeps the sidecar and appends `-a none`; Stage 3 `false`
|
Animations disable keeps the sidecar and appends `-a none`; final Stage-2 `false`
|
||||||
does neither. Only explicit all-motion disable uses `--no-animations`.
|
does neither. Only explicit all-motion disable uses `--no-animations`.
|
||||||
Otherwise do not mix deck-wide flags with a sidecar. With no motion input or
|
Otherwise do not mix deck-wide flags with a sidecar. With no motion input or
|
||||||
sidecar, preserve `fade` / `none`.
|
sidecar, preserve `fade` / `none`.
|
||||||
@@ -603,7 +720,7 @@ sidecar, preserve `fade` / `none`.
|
|||||||
- `validation/<project_name>_<timestamp>.report.json` with `passed` or `passed-with-warnings` package/resource postflight status
|
- `validation/<project_name>_<timestamp>.report.json` with `passed` or `passed-with-warnings` package/resource postflight status
|
||||||
- `validation/<project_name>_<timestamp>.trace.json` when bare `--conversion-trace` is enabled; an explicit `--conversion-trace <path>` uses that destination instead
|
- `validation/<project_name>_<timestamp>.trace.json` when bare `--conversion-trace` is enabled; an explicit `--conversion-trace <path>` uses that destination instead
|
||||||
|
|
||||||
The compact `[POSTFLIGHT]` receipt prints `status`, `quality_gate`, Slide count, warning-category counts, and PPTX/report paths. Disclose material warnings. Do not open or `cat` the complete report on routine success; use targeted field extraction only for failure investigation, an explicit audit request, or information absent from the receipt. A failed report or missing PPTX is not success. Retain its report path for later Generate narration (`deck_motion` handoff).
|
Before creating the PPTX, the exporter independently requires the current matching `final` quality report; a missing, unreadable, unsupported, non-final, blocking, stale, or unverifiable report exits nonzero. The compact `[POSTFLIGHT]` receipt prints `status`, `quality_gate`, Slide count, warning-category counts, and PPTX/report paths. Disclose material warnings. Do not open or `cat` the complete report on routine success; use targeted field extraction only for failure investigation, an explicit audit request, or information absent from the receipt. A failed report or missing PPTX is not success. Retain its report path for later Generate narration (`deck_motion` handoff).
|
||||||
|
|
||||||
## ✅ Generate PPTX Complete
|
## ✅ Generate PPTX Complete
|
||||||
|
|
||||||
|
|||||||
+6
-7
@@ -17,7 +17,7 @@ Global stop/continue rules for all four top-level routes, plus concrete failure
|
|||||||
| Confirm UI launch failure | No | Re-check `confirm_ui/result.json` once, then use chat fallback | No | [`generate-pptx`](../generate-pptx.md) Step 4 chat confirmation |
|
| Confirm UI launch failure | No | Re-check `confirm_ui/result.json` once, then use chat fallback | No | [`generate-pptx`](../generate-pptx.md) Step 4 chat confirmation |
|
||||||
| Confirm UI wait timeout | No, if no final result yet | Re-check `result.json` once; keep server cleanup mandatory | Only if user still wants the page | Step 4 same stage or chat fallback |
|
| Confirm UI wait timeout | No, if no final result yet | Re-check `result.json` once; keep server cleanup mandatory | Only if user still wants the page | Step 4 same stage or chat fallback |
|
||||||
| User explicitly switches from Confirm UI to chat during any stage | Yes until the unresolved current stage is confirmed | Follow [`confirm_ui.md`](../../scripts/docs/confirm_ui.md)'s in-run switch, retain persisted confirmed stages, then continue the current and remaining stages in chat; never relaunch UI | Confirm in chat unless explicitly delegated | Step 4 current chat stage |
|
| User explicitly switches from Confirm UI to chat during any stage | Yes until the unresolved current stage is confirmed | Follow [`confirm_ui.md`](../../scripts/docs/confirm_ui.md)'s in-run switch, retain persisted confirmed stages, then continue the current and remaining stages in chat; never relaunch UI | Confirm in chat unless explicitly delegated | Step 4 current chat stage |
|
||||||
| Confirm UI Stage 1 completed then unexpectedly interrupted while UI remains selected | Yes until Stage 2 is written/confirmed | Read existing Stage 1 `result.json`, create `recommendations.stage2.json` without changing Stage 1, then `--wait-only --wait-stage stage2` | Usually no | Step 4 Stage 2 write/wait |
|
| Confirm UI Stage 1 completed then unexpectedly interrupted while UI remains selected | Yes until final Stage 2 is written/confirmed | Read existing Stage 1 `result.json`, derive a fresh `recommendations.stage2.json` without changing Stage 1, then `--wait-only` for final confirmation | Usually no | Step 4 final Stage 2 write/wait |
|
||||||
| Missing final confirmation | Yes | None | User must confirm or change the values | Step 4 final confirmation |
|
| Missing final confirmation | Yes | None | User must confirm or change the values | Step 4 final confirmation |
|
||||||
| Final confirmed value or a later explicit user override is missing, changed, substituted, or weakened in `design_spec.md` | Yes | Repair from the retained final-confirmation object plus any newer explicit instruction; only a fresh recovery turn with no retained state reads persisted final evidence once | Only when the effective value genuinely cannot be honored | Step 4 Gate 1 — confirmation fidelity |
|
| Final confirmed value or a later explicit user override is missing, changed, substituted, or weakened in `design_spec.md` | Yes | Repair from the retained final-confirmation object plus any newer explicit instruction; only a fresh recovery turn with no retained state reads persisted final evidence once | Only when the effective value genuinely cannot be honored | Step 4 Gate 1 — confirmation fidelity |
|
||||||
| `spec_lock.md` changes confirmed identity or omits a required execution anchor/routing decision | Yes | Re-author the affected lock rows from the completed Design Spec and current context; do not enumerate page-local literals | No unless the Design Spec itself is incomplete | Step 4 Gate 2 — lock context fidelity |
|
| `spec_lock.md` changes confirmed identity or omits a required execution anchor/routing decision | Yes | Re-author the affected lock rows from the completed Design Spec and current context; do not enumerate page-local literals | No unless the Design Spec itself is incomplete | Step 4 Gate 2 — lock context fidelity |
|
||||||
@@ -39,7 +39,7 @@ Global stop/continue rules for all four top-level routes, plus concrete failure
|
|||||||
| Step 7 image readiness missing manual files | Yes | None for manual assets; list required filenames and prompts | Yes | Step 7 image readiness gate |
|
| Step 7 image readiness missing manual files | Yes | None for manual assets; list required filenames and prompts | Yes | Step 7 image readiness gate |
|
||||||
| `total_md_split.py` failure while speaker notes are enabled | Yes | Fix notes format/path, rerun only Step 7.1 | Usually no | Step 7.1 |
|
| `total_md_split.py` failure while speaker notes are enabled | Yes | Fix notes format/path, rerun only Step 7.1 | Usually no | Step 7.1 |
|
||||||
| `finalize_svg.py` failure | Yes | Fix SVG/assets, rerun Step 7.2 | Only if source asset is missing | Step 7.2 |
|
| `finalize_svg.py` failure | Yes | Fix SVG/assets, rerun Step 7.2 | Only if source asset is missing | Step 7.2 |
|
||||||
| `svg_to_pptx.py` failure | Yes | Fix conversion issue, rerun Step 7.3 | Only if required artifact is missing | Step 7.3 |
|
| `svg_to_pptx.py` failure | Yes | If no current matching passing final SVG quality report exists, obtain the complete blocking issue set from the final checker as needed, fix it, rerun the checker against the updated `svg_output/`, and proceed to Step 7.3 only after it reports `passed`; otherwise fix the conversion issue and rerun Step 7.3 | Only if a required artifact is missing | Step 6 final quality gate or Step 7.3 |
|
||||||
| Export succeeds but user wants direct browser edits re-exported | No | Rerun Step 7.2 and Step 7.3 after applied edits | No | Post-export live-preview handling |
|
| Export succeeds but user wants direct browser edits re-exported | No | Rerun Step 7.2 and Step 7.3 after applied edits | No | Post-export live-preview handling |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -60,8 +60,8 @@ Global stop/continue rules for all four top-level routes, plus concrete failure
|
|||||||
|
|
||||||
**Forbidden — silent downgrade**: Do not skip a required gate because a downstream command might tolerate the missing file, and do not change a confirmed execution value merely to keep the route moving. Fix, pause, or request a new decision at the owning boundary.
|
**Forbidden — silent downgrade**: Do not skip a required gate because a downstream command might tolerate the missing file, and do not change a confirmed execution value merely to keep the route moving. Fix, pause, or request a new decision at the owning boundary.
|
||||||
|
|
||||||
**Proactive production compatibility**: Keep Stage 3 raw fields as evidence.
|
**Proactive production resolution**: Keep final Stage-2 raw fields as evidence.
|
||||||
Resolve durable outcomes as explicit instruction → Stage 3 → legacy defaults
|
Resolve durable outcomes as explicit instruction → final Stage 2 → workflow defaults
|
||||||
`enabled` / `disabled` / `disabled`. Audio raises Notes only when Notes is not
|
`enabled` / `disabled` / `disabled`. Audio raises Notes only when Notes is not
|
||||||
explicitly disabled; an explicit notes-off/audio-on conflict stops at
|
explicitly disabled; an explicit notes-off/audio-on conflict stops at
|
||||||
Generate's one-question dependency gate. Keep raw values unchanged and record
|
Generate's one-question dependency gate. Keep raw values unchanged and record
|
||||||
@@ -79,13 +79,12 @@ in-run switch and resumes the unresolved stage in chat without relaunching UI.
|
|||||||
|
|
||||||
| Last good state | Resume from |
|
| Last good state | Resume from |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Stage 1 confirmation exists, Stage 2 missing, and UI remains selected | Create `recommendations.stage2.json` without changing Stage 1, then `confirm_ui/server.py <project> --wait-only --wait-stage stage2` |
|
| Stage 1 confirmation exists, final Stage 2 is missing or unconfirmed, and UI remains selected | Derive a fresh `recommendations.stage2.json` from confirmed Stage 1 and current inputs without changing Stage 1, then run `confirm_ui/server.py <project> --wait-only` for final confirmation. |
|
||||||
| Stage 2 confirmation exists, final confirmation missing, and UI remains selected | Resume [`generate-pptx`](../generate-pptx.md) Step 4 confirmation orchestration at Stage 3: derive production mechanics from the confirmed solution, create `recommendations.stage3.json` without changing earlier stages, then perform the final wait. |
|
|
||||||
| Final confirmation evidence exists; `design_spec.md` is missing, with or without a surviving `spec_lock.md` | Return to Generate Step 4 and [`strategist.md`](../../references/strategist.md) §6.2; read final evidence once into the fresh context, read [`design_spec_reference.md`](../../templates/design_spec_reference.md), author the complete `design_spec.md` from scratch using that state plus source analysis, and pass Gate 1. Then read [`spec_lock_reference.md`](../../templates/spec_lock_reference.md) and re-author the complete `spec_lock.md` from the audited Design Spec plus current context, replacing any orphan lock. Never reconstruct the Design Spec from an orphan lock or retain orphan-lock choices as authority. |
|
| Final confirmation evidence exists; `design_spec.md` is missing, with or without a surviving `spec_lock.md` | Return to Generate Step 4 and [`strategist.md`](../../references/strategist.md) §6.2; read final evidence once into the fresh context, read [`design_spec_reference.md`](../../templates/design_spec_reference.md), author the complete `design_spec.md` from scratch using that state plus source analysis, and pass Gate 1. Then read [`spec_lock_reference.md`](../../templates/spec_lock_reference.md) and re-author the complete `spec_lock.md` from the audited Design Spec plus current context, replacing any orphan lock. Never reconstruct the Design Spec from an orphan lock or retain orphan-lock choices as authority. |
|
||||||
| Final confirmation evidence exists; `design_spec.md` exists and `spec_lock.md` missing | Return to Generate Step 4; in this fresh recovery context read final evidence once to audit the existing Design Spec, then read [`spec_lock_reference.md`](../../templates/spec_lock_reference.md) and author the complete lock from the audited Design Spec plus current context. |
|
| Final confirmation evidence exists; `design_spec.md` exists and `spec_lock.md` missing | Return to Generate Step 4; in this fresh recovery context read final evidence once to audit the existing Design Spec, then read [`spec_lock_reference.md`](../../templates/spec_lock_reference.md) and author the complete lock from the audited Design Spec plus current context. |
|
||||||
| Final confirmation evidence and both planning artifacts exist, but Gate 1 fails | In a fresh recovery context read final evidence once, repair `design_spec.md`, then re-author every affected lock row. Do not reopen recommendations or infer a replacement from the current lock. |
|
| Final confirmation evidence and both planning artifacts exist, but Gate 1 fails | In a fresh recovery context read final evidence once, repair `design_spec.md`, then re-author every affected lock row. Do not reopen recommendations or infer a replacement from the current lock. |
|
||||||
| Gate 1 passes but Gate 2 fails | Keep the Design Spec unchanged and re-author only the mismatched lock anchors/routing rows from it plus current context. |
|
| Gate 1 passes but Gate 2 fails | Keep the Design Spec unchanged and re-author only the mismatched lock anchors/routing rows from it plus current context. |
|
||||||
| No final confirmation evidence is available | Resume Step 4 from the latest stage evidenced by `confirm_ui/result.json`; if no stage is persisted, restart Step 4 at Stage 1. Do not infer confirmed choices from partial planning artifacts. |
|
| No final confirmation evidence is available | If `confirm_ui/result.json` proves Stage 1 confirmation, resume at final Stage 2; otherwise restart Step 4 at Stage 1. Do not infer confirmed choices from partial planning artifacts. |
|
||||||
| `design_spec.md` and `spec_lock.md` complete, split mode selected | [`resume-execute`](../stages/resume-execute.md) |
|
| `design_spec.md` and `spec_lock.md` complete, split mode selected | [`resume-execute`](../stages/resume-execute.md) |
|
||||||
| Images acquired but SVGs not started | [`generate-pptx`](../generate-pptx.md) Step 6 |
|
| Images acquired but SVGs not started | [`generate-pptx`](../generate-pptx.md) Step 6 |
|
||||||
| SVGs complete and checker passed; effective Speaker Notes outcome enabled and notes missing | Step 6 Logic Construction |
|
| SVGs complete and checker passed; effective Speaker Notes outcome enabled and notes missing | Step 6 Logic Construction |
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ Maintainer-only inventory for adding, moving, or removing workflow documents. Ru
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `beautify-pptx` | Generation profile | [`profiles/beautify-pptx.md`](./profiles/beautify-pptx.md) | Generate PPTX |
|
| `beautify-pptx` | Generation profile | [`profiles/beautify-pptx.md`](./profiles/beautify-pptx.md) | Generate PPTX |
|
||||||
| `quick-generate` | Generation profile | [`profiles/quick-generate.md`](./profiles/quick-generate.md) | Generate PPTX direct SVG-to-PPTX short circuit |
|
| `quick-generate` | Generation profile | [`profiles/quick-generate.md`](./profiles/quick-generate.md) | Generate PPTX direct SVG-to-PPTX short circuit |
|
||||||
| `apply-template-workspace` | Template-input stage | [`stages/apply-template-workspace.md`](./stages/apply-template-workspace.md) | Generate Step 3 |
|
| `apply-template-workspace` | Template-input stage | [`stages/apply-template-workspace.md`](./stages/apply-template-workspace.md) | Non-free Default Stage-1 selection after confirmation, or Quick direct exact-root input |
|
||||||
| `create-brand` | Template child workflow | [`create-template/create-brand.md`](./create-template/create-brand.md) | Create Template |
|
| `create-brand` | Template child workflow | [`create-template/create-brand.md`](./create-template/create-brand.md) | Create Template |
|
||||||
|
| `create-style` | Template child workflow | [`create-template/create-style.md`](./create-template/create-style.md) | Create Template |
|
||||||
| `create-layout` | Template child workflow | [`create-template/create-layout.md`](./create-template/create-layout.md) | Create Template |
|
| `create-layout` | Template child workflow | [`create-template/create-layout.md`](./create-template/create-layout.md) | Create Template |
|
||||||
| `create-deck` | Template child workflow | [`create-template/create-deck.md`](./create-template/create-deck.md) | Create Template |
|
| `create-deck` | Template child workflow | [`create-template/create-deck.md`](./create-template/create-deck.md) | Create Template |
|
||||||
| `topic-research` | Factual-preparation stage | [`stages/topic-research.md`](./stages/topic-research.md) | Inside Generate Step 1 |
|
| `topic-research` | Factual-preparation stage | [`stages/topic-research.md`](./stages/topic-research.md) | Inside Generate Step 1 |
|
||||||
|
|||||||
+48
-12
@@ -1,15 +1,21 @@
|
|||||||
---
|
---
|
||||||
description: Main-generation profile for 1:1, content-faithful re-layout of an existing deck through the standard SVG pipeline.
|
description: Generate profile for 1:1, content-faithful re-layout of an existing deck through Default or explicit Quick execution.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Beautify PPTX (Re-layout) Profile
|
# Beautify PPTX (Re-layout) Profile
|
||||||
|
|
||||||
> Main-pipeline profile, not a top-level route. [`template-fill-pptx.md`](../template-fill-pptx.md) reuses a deck's design and swaps in new content; this profile keeps a deck's content and redoes its layout.
|
> Generate profile, not a top-level route. [`template-fill-pptx.md`](../template-fill-pptx.md) reuses a deck's design and swaps in new content; this profile keeps a deck's content and redoes its layout.
|
||||||
|
|
||||||
Re-lays-out an existing `.pptx`: the text is preserved **verbatim**, the source deck's visual identity (palette / fonts) is **inherited as truth**, and only layout, hierarchy, and whitespace are redesigned. Output is a brand-new native deck generated through the standard SVG pipeline — not a patch over the original.
|
Re-lays-out an existing `.pptx`: the text is preserved **verbatim**, the source deck's visual identity (palette / fonts) is **inherited as truth**, and only layout, hierarchy, and whitespace are redesigned. Output is a brand-new native deck generated through the standard SVG pipeline — not a patch over the original.
|
||||||
|
|
||||||
**Trigger**: the user supplies a `.pptx` and asks to beautify / re-layout / 重新排版 / 美化 while keeping the content. Explicit intent + a provided file only; never auto-infer.
|
**Trigger**: the user supplies a `.pptx` and asks to beautify / re-layout / 重新排版 / 美化 while keeping the content. Explicit intent + a provided file only; never auto-infer.
|
||||||
|
|
||||||
|
**Hard rule — select one runtime before continuing**: when the same request
|
||||||
|
also meets [`quick-generate.md`](./quick-generate.md)'s explicit trigger, load
|
||||||
|
that runtime and do not load `generate-pptx.md`. Otherwise load
|
||||||
|
[`generate-pptx.md`](../generate-pptx.md) and do not load Quick. The 1:1
|
||||||
|
Beautify constraints in this file apply in either runtime.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. When to Run
|
## 1. When to Run
|
||||||
@@ -22,11 +28,11 @@ Re-lays-out an existing `.pptx`: the text is preserved **verbatim**, the source
|
|||||||
|
|
||||||
**Hard rule — content is frozen**: every text string from the source is preserved exactly (no add / remove / reword / reorder). Beautification freedom lives only in layout, hierarchy, spacing, and visual rhythm.
|
**Hard rule — content is frozen**: every text string from the source is preserved exactly (no add / remove / reword / reorder). Beautification freedom lives only in layout, hierarchy, spacing, and visual rhythm.
|
||||||
|
|
||||||
**Hard rule — not a patch, not a fill**: this regenerates a native deck through Strategist → Executor → export ([`generate-pptx`](../generate-pptx.md) Steps 4–7). It does **not** edit the source file in place, and it is **not** [`template-fill-pptx`](../template-fill-pptx.md) (which clones source slides and replaces text). It also does not parse an arbitrary third-party template for text-only substitution (the rejected #53 direction) — it builds every page from scratch.
|
**Hard rule — not a patch, not a fill**: this regenerates a native deck through the selected Default or Quick SVG → PPTX runtime. It does **not** edit the source file in place, and it is **not** [`template-fill-pptx`](../template-fill-pptx.md) (which clones source slides and replaces text). It also does not parse an arbitrary third-party template for text-only substitution (the rejected #53 direction) — it builds every page from scratch.
|
||||||
|
|
||||||
**Distinct from mirror templates**: `replication_mode: mirror` ([`executor-structured.md`](../../references/executor-structured.md) §1.1) keeps layout + visuals verbatim and edits text. Beautify is the inverse — content verbatim, layout redone, identity inherited.
|
**Distinct from mirror templates**: `replication_mode: mirror` ([`executor-structured.md`](../../references/executor-structured.md) §1.1) keeps layout + visuals verbatim and edits text. Beautify is the inverse — content verbatim, layout redone, identity inherited.
|
||||||
|
|
||||||
**When this profile is wrong — re-architecture belongs to the default main pipeline**: this profile preserves the source's page count and page order 1:1. It is for "keep this deck, just lay it out better". When the user instead wants the original page breakdown reconsidered — merge / split / reorder pages, re-outline the structure, build a *better deck* from the same content rather than a prettier version of the same pages — do not activate this profile. This includes re-pagination for fit: "keep every word but split a crowded page so it reads better" changes page count. Convert the deck with [`ppt_to_md`](../../scripts/source_to_md/ppt_to_md.py) and run the default main pipeline, where the Strategist re-architects the outline freely from the extracted content. The deciding question: is the source's page split information to preserve, or just the previous author's structure to improve? Preserve → activate this profile; improve → default main pipeline.
|
**When this profile is wrong — re-architecture belongs to ordinary Generate**: this profile preserves the source's page count and page order 1:1. It is for "keep this deck, just lay it out better". When the user instead wants the original page breakdown reconsidered — merge / split / reorder pages, re-outline the structure, build a *better deck* from the same content rather than a prettier version of the same pages — do not activate this profile. This includes re-pagination for fit: "keep every word but split a crowded page so it reads better" changes page count. Convert the deck with [`ppt_to_md`](../../scripts/source_to_md/ppt_to_md.py) and use ordinary Quick when Quick was explicit, otherwise the Default main pipeline. The deciding question: is the source's page split information to preserve, or just the previous author's structure to improve? Preserve → activate this profile; improve → ordinary Generate in the selected runtime.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -52,10 +58,18 @@ Match the canvas to the source so 1:1 pages and paste-back align. Determine the
|
|||||||
| other | nearest format in [`canvas-formats.md`](../../references/canvas-formats.md); record the source pixel size in the spec |
|
| other | nearest format in [`canvas-formats.md`](../../references/canvas-formats.md); record the source pixel size in the spec |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Default runtime:
|
||||||
python3 ${SKILL_DIR}/scripts/project_manager.py init <project_name> --format <format>
|
python3 ${SKILL_DIR}/scripts/project_manager.py init <project_name> --format <format>
|
||||||
|
|
||||||
|
# Quick runtime instead:
|
||||||
|
python3 ${SKILL_DIR}/scripts/project_manager.py init <project_name> --format <format> --quick-generate
|
||||||
|
|
||||||
|
# Both runtimes then import once:
|
||||||
python3 ${SKILL_DIR}/scripts/project_manager.py import-sources <project_path> <source.pptx>
|
python3 ${SKILL_DIR}/scripts/project_manager.py import-sources <project_path> <source.pptx>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Run exactly one `init` command: the Quick form only when Quick was selected.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Extract Identity and Data; Assemble Inventory
|
## 4. Extract Identity and Data; Assemble Inventory
|
||||||
@@ -132,12 +146,26 @@ If `images/image_manifest.json` does not exist because the source deck has no ex
|
|||||||
- [x] `analysis/<stem>.slide_library.json` holds chart + table data and SmartArt semantic structure for regeneration
|
- [x] `analysis/<stem>.slide_library.json` holds chart + table data and SmartArt semantic structure for regeneration
|
||||||
- [x] `analysis/source_profile.json` (multi-deck index) summarizes the source facts in its `decks[]` entry
|
- [x] `analysis/source_profile.json` (multi-deck index) summarizes the source facts in its `decks[]` entry
|
||||||
- [x] `analysis/beautify_inventory.json` ledgers per-slide text / images / data + ignored + needs-confirmation
|
- [x] `analysis/beautify_inventory.json` ledgers per-slide text / images / data + ignored + needs-confirmation
|
||||||
- [ ] **Next**: Step 5 — Beautify Plan (recommend & confirm)
|
- [ ] **Next**: Step 5 — resolve Beautify decisions in the selected runtime
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Beautify Plan — Recommend & Confirm
|
## 5. Beautify Decisions
|
||||||
|
|
||||||
|
### Quick branch
|
||||||
|
|
||||||
|
When Quick was selected, do not run the Default confirmation flow below. Apply
|
||||||
|
the same inventory interpretation, source-identity judgment, and body-size
|
||||||
|
method documented in this section, but make the decisions directly in the
|
||||||
|
active context. Explicit user requirements remain authoritative; otherwise use
|
||||||
|
the source identity as the default. Resolve `ignored` and `needs_confirmation`
|
||||||
|
without creating a confirmation payload, Design Spec, lock, or substitute
|
||||||
|
plan. If a flagged complex object cannot be regenerated without losing frozen
|
||||||
|
facts, stop as a hard prerequisite instead of simplifying it. Then continue to
|
||||||
|
the Quick branch in §6.
|
||||||
|
|
||||||
|
### Default branch — Recommend & Confirm
|
||||||
|
|
||||||
⛔ **BLOCKING**: the scope is not hard-coded — same spirit as the Strategist confirmation stage. Recommend each item below from what the deck actually contains (the Step 4 inventory), present the plan, and **wait for the user to confirm or adjust** before writing any spec. Use Generate Step 4's selected surface for the full visual confirmation; keep the structural-scope decisions in chat. Values confirmed through either channel are honored identically.
|
⛔ **BLOCKING**: the scope is not hard-coded — same spirit as the Strategist confirmation stage. Recommend each item below from what the deck actually contains (the Step 4 inventory), present the plan, and **wait for the user to confirm or adjust** before writing any spec. Use Generate Step 4's selected surface for the full visual confirmation; keep the structural-scope decisions in chat. Values confirmed through either channel are honored identically.
|
||||||
|
|
||||||
@@ -166,10 +194,10 @@ This step has two halves:
|
|||||||
**Visual re-confirm — full confirmation seeded from the source**:
|
**Visual re-confirm — full confirmation seeded from the source**:
|
||||||
|
|
||||||
Apply [`generate-pptx`](../generate-pptx.md) Step 4's surface decision first. In
|
Apply [`generate-pptx`](../generate-pptx.md) Step 4's surface decision first. In
|
||||||
the default UI branch, use the three
|
the default UI branch, use
|
||||||
`<project_path>/confirm_ui/recommendations.stageN.json` files at the same staged
|
`<project_path>/confirm_ui/recommendations.stage1.json` and
|
||||||
handoffs and launch the same confirm server. In the chat branch, present the
|
`recommendations.stage2.json` at the same two handoffs and launch the same
|
||||||
same three stages and fields without launching the server or requiring
|
confirm server. In the chat branch, present the same two stages and fields without launching the server or requiring
|
||||||
`result.json`. The active, unconfirmed UI stage may be overwritten for a
|
`result.json`. The active, unconfirmed UI stage may be overwritten for a
|
||||||
requested regeneration; normal progression leaves confirmed earlier stages
|
requested regeneration; normal progression leaves confirmed earlier stages
|
||||||
intact. Do **not** hide fields: seed **every** targeted-confirmation field with
|
intact. Do **not** hide fields: seed **every** targeted-confirmation field with
|
||||||
@@ -229,7 +257,15 @@ On confirmation, enter [`generate-pptx`](../generate-pptx.md) Step 4 as Strategi
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Executor + Export
|
## 6. Author + Export
|
||||||
|
|
||||||
|
**Quick**: follow [`quick-generate.md`](./quick-generate.md) §3–4. The
|
||||||
|
Beautify inventory is the exact page roster and frozen-content contract; keep
|
||||||
|
its source order, hand-author every page, run the lockless Quick final checker,
|
||||||
|
and export with `--quick-generate`. Do not run Confirm UI, write a Design Spec
|
||||||
|
or lock, run the Default first-page gate, or call `finalize_svg.py`.
|
||||||
|
|
||||||
|
**Default**: run the standard pipeline as follows.
|
||||||
|
|
||||||
Run the standard pipeline ([`generate-pptx`](../generate-pptx.md) Steps 6–7). The Executor re-lays-out each page — hierarchy, spacing, alignment, page rhythm — using the semantic anchors in `spec_lock.md` plus current page/source/template context; valid page-local colors, gradients, effects, and export-safe display faces need not be added to the lock. It regenerates charts / tables as native SVG from the extracted data and re-lays-out the source pictures.
|
Run the standard pipeline ([`generate-pptx`](../generate-pptx.md) Steps 6–7). The Executor re-lays-out each page — hierarchy, spacing, alignment, page rhythm — using the semantic anchors in `spec_lock.md` plus current page/source/template context; valid page-local colors, gradients, effects, and export-safe display faces need not be added to the lock. It regenerates charts / tables as native SVG from the extracted data and re-lays-out the source pictures.
|
||||||
|
|
||||||
@@ -258,7 +294,7 @@ python3 ${SKILL_DIR}/scripts/source_to_md/ppt_to_md.py <project_path>/exports/<o
|
|||||||
|
|
||||||
- [x] Content + data values verbatim (read-back Markdown matches the source)
|
- [x] Content + data values verbatim (read-back Markdown matches the source)
|
||||||
- [x] 1:1 page count preserved
|
- [x] 1:1 page count preserved
|
||||||
- [x] Source colors + fonts inherited as locked truth
|
- [x] Source-derived or explicitly overridden colors + fonts applied consistently
|
||||||
- [x] Charts / tables regenerated as native SVG in the inherited style
|
- [x] Charts / tables regenerated as native SVG in the inherited style
|
||||||
- [x] Native PPTX exported to `exports/`
|
- [x] Native PPTX exported to `exports/`
|
||||||
```
|
```
|
||||||
|
|||||||
+221
-44
@@ -1,12 +1,13 @@
|
|||||||
---
|
---
|
||||||
description: Generate profile for agent-decided source and resource preparation, direct SVG authoring, and final PPTX delivery without Strategist or confirmation artifacts.
|
description: One-pass Generate profile for agent-decided preparation, direct SVG authoring, and final PPTX delivery without durable planning or confirmation artifacts.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Quick Generate Profile
|
# Quick Generate Profile
|
||||||
|
|
||||||
> Generate-PPTX profile, not a top-level route. It removes the separate
|
> Generate-PPTX profile, not a top-level route. The current main agent completes
|
||||||
> Strategist and confirmation phase; it does not remove the facts, resources,
|
> one uninterrupted run without a separate Strategist/confirmation handoff or a
|
||||||
> or export capabilities needed to build the final deck.
|
> resumable design record. This removes interaction and traceability, not the
|
||||||
|
> facts, resources, or authoring capabilities needed to build the final deck.
|
||||||
|
|
||||||
**Trigger**: the user explicitly requests quick/fast generation, asks to skip
|
**Trigger**: the user explicitly requests quick/fast generation, asks to skip
|
||||||
strategy/confirmation, or directs the agent to proceed to SVG and export.
|
strategy/confirmation, or directs the agent to proceed to SVG and export.
|
||||||
@@ -20,15 +21,23 @@ Page count alone never activates or blocks this profile.
|
|||||||
|---|---|
|
|---|---|
|
||||||
| Authority | Follow every explicit user requirement as stated; decide every unspecified choice directly without asking |
|
| Authority | Follow every explicit user requirement as stated; decide every unspecified choice directly without asking |
|
||||||
| Interaction | The current main agent decides content, design, resources, and implementation without Strategist, Confirm UI, or approval stops |
|
| Interaction | The current main agent decides content, design, resources, and implementation without Strategist, Confirm UI, or approval stops |
|
||||||
|
| Execution memory | Keep routine page, visual, and resource decisions only in the current active context; losing that context restarts Quick instead of reconstructing a plan from project files |
|
||||||
| Inputs | Any supported Generate input; convert/import sources and run bounded factual research when the input requires them |
|
| Inputs | Any supported Generate input; convert/import sources and run bounded factual research when the input requires them |
|
||||||
|
| Templates | Directly validate and install at most one exact workspace root per kind supplied for this run; when none are supplied, use free design without catalog selection or Confirm UI |
|
||||||
| Resources | Prepare every project-local image, icon, formula, and required provenance/manifest artifact before the referencing SVG is authored |
|
| Resources | Prepare every project-local image, icon, formula, and required provenance/manifest artifact before the referencing SVG is authored |
|
||||||
| Planning artifacts | Do not create `design_spec.md`, `spec_lock.md`, confirmation payloads, or a second persisted strategy |
|
| Planning artifacts | Do not author a root project `design_spec.md`, `spec_lock.md`, confirmation payloads, or any substitute planning artifact; an installed `templates/design_spec.md` remains template input |
|
||||||
|
| Traceability | Operational resource manifests, checker reports, postflight, and bounded Python command/outcome audit entries may remain, but they do not record the AI's design reasoning or form a resumable generation history |
|
||||||
| Delivery | Hand-author the resolved SVG roster, run one lockless final checker, skip `finalize_svg.py`, and export the final native PPTX through `--quick-generate` |
|
| Delivery | Hand-author the resolved SVG roster, run one lockless final checker, skip `finalize_svg.py`, and export the final native PPTX through `--quick-generate` |
|
||||||
|
|
||||||
**Hard rule — speed removes interaction, not material**: all ordinary source,
|
**Artifact ownership**: follow
|
||||||
research, resource-preparation, analysis, and export capabilities remain
|
[`artifact-ownership.md`](../../references/artifact-ownership.md) for source,
|
||||||
available when needed; the missing planning contract relaxes design constraints
|
fact, author, derived, and regeneration boundaries. Quick changes the planning
|
||||||
only.
|
handoff, not those artifact roles.
|
||||||
|
|
||||||
|
**Hard rule — speed removes interaction and durable planning, not capability**:
|
||||||
|
all ordinary source, research, visual-carrier, resource-preparation, analysis,
|
||||||
|
authoring, and export capabilities remain available when they serve the deck.
|
||||||
|
This is capability availability, not a requirement to use every carrier.
|
||||||
|
|
||||||
Explicit user facts, wording, choices, exclusions, and permission boundaries
|
Explicit user facts, wording, choices, exclusions, and permission boundaries
|
||||||
still win. For every unspecified routine choice, decide directly and continue;
|
still win. For every unspecified routine choice, decide directly and continue;
|
||||||
@@ -41,7 +50,7 @@ Pause only for user interruption or an unresolved hard prerequisite.
|
|||||||
notes, custom object animations, and narration start off. The current agent may
|
notes, custom object animations, and narration start off. The current agent may
|
||||||
enable any ordinary capability when the request or deck benefits; use its
|
enable any ordinary capability when the request or deck benefits; use its
|
||||||
normal inputs, flags, and prerequisites without asking for approval. Quick
|
normal inputs, flags, and prerequisites without asking for approval. Quick
|
||||||
never creates or reads a Design Spec or lock to enable it.
|
never creates or reads a root project Design Spec or lock to enable it.
|
||||||
|
|
||||||
**Mandatory — discover motion before deciding whether to load it**: scan this
|
**Mandatory — discover motion before deciding whether to load it**: scan this
|
||||||
compact gate once; do not load the full execution reference when the defaults
|
compact gate once; do not load the full execution reference when the defaults
|
||||||
@@ -64,46 +73,178 @@ point; a before-authoring signal always overrides a before-export-only timing.
|
|||||||
|
|
||||||
## 2. Source and Resource Preparation
|
## 2. Source and Resource Preparation
|
||||||
|
|
||||||
Run [`generate-pptx.md`](../generate-pptx.md) Step 1 when applicable. Initialize
|
Prepare source facts before initialization:
|
||||||
the minimal workspace with:
|
|
||||||
|
| Input | Action |
|
||||||
|
|---|---|
|
||||||
|
| Topic or requirements without supporting facts | Run [`topic-research`](../stages/topic-research.md) immediately and retain its Markdown supplement plus fact-provenance JSON for import |
|
||||||
|
| PDF / DOCX / Office document / XLSX / XLSM / PPTX / EPUB / HTML / LaTeX / RST / web URL | Run `python3 ${SKILL_DIR}/scripts/source_to_md.py <file_or_URL_or_dir> [<file_or_URL_or_dir> ...]` |
|
||||||
|
| CSV / TSV | Read directly as a plain-text table source |
|
||||||
|
| Markdown or direct conversation text | Read directly |
|
||||||
|
|
||||||
|
The conversion dispatcher writes standard Markdown plus its conversion profile
|
||||||
|
beside each local source by default. Use `-t <type>` only when detection is
|
||||||
|
ambiguous and `-o` only for a required output path; with several or directory
|
||||||
|
inputs, `-o` names an output directory. A PPTX is converted to Markdown here and
|
||||||
|
receives its project analysis during the import step below.
|
||||||
|
|
||||||
|
After reading every direct and converted source, assess factual sufficiency:
|
||||||
|
|
||||||
|
| Material state | Action |
|
||||||
|
|---|---|
|
||||||
|
| The requested outcome is supported | Continue |
|
||||||
|
| A required externally verifiable claim remains unsupported | Run [`topic-research`](../stages/topic-research.md) for those gaps only |
|
||||||
|
| Closed corpus / source-only / no external enrichment | Stay within the supplied material |
|
||||||
|
|
||||||
|
**Sufficiency test**: research only when the requested outcome would otherwise
|
||||||
|
require inventing, omitting, or leaving unsupported an externally verifiable
|
||||||
|
claim. File presence or length does not establish sufficiency. Research gathers
|
||||||
|
facts only; image acquisition remains part of the resource preparation below.
|
||||||
|
|
||||||
|
Before initialization, resolve exactly one template branch:
|
||||||
|
|
||||||
|
- **Direct template application**: one or more exact current workspace roots
|
||||||
|
were supplied in the request, or Create Template returned an exact validated
|
||||||
|
root in the current conversation. Accept at most one root per declared kind.
|
||||||
|
Before initialization, load
|
||||||
|
[`apply-template-workspace`](../stages/apply-template-workspace.md), normalize
|
||||||
|
each supplied root, read only the matching spec frontmatter needed to resolve
|
||||||
|
its kind/canvas, and run that stage's read-only schema/structured preflight.
|
||||||
|
Do not scan the library, fuzzy-match a name, or open a selector. Explicit user
|
||||||
|
canvas wins; otherwise use the selected structure owner (Layout before Deck)
|
||||||
|
canvas when present, then fall back to `ppt169`.
|
||||||
|
- **Free design**: no exact root was supplied. Continue immediately with the
|
||||||
|
requested canvas or `ppt169`. A bare template name, brand mention, style
|
||||||
|
phrase, or vague request to choose a template is ordinary brief input, not a
|
||||||
|
workspace reference.
|
||||||
|
|
||||||
|
Neither branch creates anything under `confirm_ui/` or executes
|
||||||
|
`confirm_ui/server.py`. Initialize the minimal workspace with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ${SKILL_DIR}/scripts/project_manager.py init <project_name> \
|
python3 ${SKILL_DIR}/scripts/project_manager.py init <project_name> \
|
||||||
--format <format> --quick-generate
|
--format <format> --quick-generate
|
||||||
```
|
```
|
||||||
|
|
||||||
It creates only `svg_output/` and no root README. Add capability inputs only
|
It creates `svg_output/` plus the cold
|
||||||
when triggered; checker/exporter create `validation/`, `exports/`, and the
|
`validation/workflow.log` command/outcome audit log, and no root README. After
|
||||||
default-path `backup/`. With source files, continue with Step 2
|
this command, run project-scoped Python tools directly; their shared CLI
|
||||||
`import-sources`; it creates the triggered input directories. Never scaffold a
|
bootstrap records command envelopes, material tagged outcomes, bounded status
|
||||||
Design Spec or lock. Use a new path, or verify that an existing path's
|
samples, and omission counts. A concise manual entry is allowed only for a
|
||||||
`svg_output/` is empty; Quick ignores any existing `design_spec.md` or
|
material stage handoff, rework reason, user-approved exception, or manual
|
||||||
|
recovery choice that has no owning command output; do not record routine page
|
||||||
|
progress, artifact contents, or private reasoning.
|
||||||
|
Never read the log during ordinary Quick execution; open it only for an
|
||||||
|
explicit user-requested run review. Add
|
||||||
|
capability inputs only when triggered; later tools create `exports/` and the
|
||||||
|
default-path `backup/`.
|
||||||
|
|
||||||
|
With file-based sources, import the original inputs, converted outputs, and any
|
||||||
|
research pair together:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 ${SKILL_DIR}/scripts/project_manager.py import-sources \
|
||||||
|
<project_path> <source_files_or_dirs...> [<converted_outputs...>] \
|
||||||
|
[projects/<research_slug>.md projects/<research_slug>.facts.json]
|
||||||
|
```
|
||||||
|
|
||||||
|
Only inputs already under the repository's `projects/` tree move into the
|
||||||
|
target project; every external path is copied and remains untouched. Use
|
||||||
|
`--copy` when a projects-local input must also remain in place. When conversion
|
||||||
|
wrote Markdown beside the original source, pass that source path or directory
|
||||||
|
once; when `-o` wrote it elsewhere, pass both locations. Direct supported bitmap
|
||||||
|
inputs are archived under `sources/` and copied collision-safely into `images/`.
|
||||||
|
|
||||||
|
For each imported PPTX, `import-sources` automatically writes
|
||||||
|
`analysis/<stem>.identity.json`, `analysis/<stem>.slide_library.json`, and the
|
||||||
|
multi-deck `analysis/source_profile.json` index. Read that index as source facts
|
||||||
|
and open a per-deck artifact only when the current task needs its additional
|
||||||
|
detail; these facts are recommendations, not replica constraints. Distinct PPTX
|
||||||
|
stems may coexist, and re-importing one stem replaces only that deck's entry.
|
||||||
|
|
||||||
|
Conversion companion manifests may place extracted SVG/EMF/WMF assets into the
|
||||||
|
project resource flow. Preserve EMF/WMF as vector references and never convert
|
||||||
|
them to PNG; browser preview may be blank while native PPTX export remains the
|
||||||
|
source of truth. Standalone SVG/EMF/WMF inputs remain source assets unless such
|
||||||
|
a manifest supplies their display metadata.
|
||||||
|
|
||||||
|
Never scaffold a Design Spec or lock. Use a new path, or verify that an existing
|
||||||
|
path's `svg_output/` is empty; Quick ignores any existing `design_spec.md` or
|
||||||
`spec_lock.md`.
|
`spec_lock.md`.
|
||||||
|
|
||||||
|
The audit log is an operational tool record only. It does not capture direct
|
||||||
|
SVG authoring, active-context design choices, or private reasoning and cannot be
|
||||||
|
used to resume or reconstruct a Quick run.
|
||||||
|
|
||||||
|
For the direct-template branch, continue with
|
||||||
|
[`apply-template-workspace`](../stages/apply-template-workspace.md) after
|
||||||
|
initialization against only the preflighted roots. The user's request is the
|
||||||
|
selection authority; there is no template confirmation receipt or handoff. The
|
||||||
|
stage installs and fuses the workspaces into `<project_path>/templates/` plus
|
||||||
|
the project-local asset pools. All later reads use that installed state, never
|
||||||
|
the original roots.
|
||||||
|
|
||||||
|
Before writing P01, read the installed template spec once and, for Layout/Deck,
|
||||||
|
inspect the relevant SVG prototypes. Apply Brand identity, Style
|
||||||
|
direction/method, the selected structure owner's useful prototype geometry,
|
||||||
|
and Deck application context directly in the active context under the existing
|
||||||
|
segment precedence. Follow explicit instructions about literal or visual-only
|
||||||
|
use; otherwise decide which prototypes to use, skip, repeat, reorder, or adapt
|
||||||
|
while authoring. Persist no separate template-application artifact. If no
|
||||||
|
template was installed, make the same design choices freely.
|
||||||
|
|
||||||
|
**One-pass decision boundary**: resolve only what is needed to author this deck
|
||||||
|
in the current context. Do not print a strategy summary, create a planning
|
||||||
|
checkpoint, or persist a page/resource plan.
|
||||||
|
|
||||||
Before writing P01, resolve in active context:
|
Before writing P01, resolve in active context:
|
||||||
|
|
||||||
- the slide roster, canvas, visual direction, palette, typography, and wording;
|
- the exact slide roster and one compact core message for every page, used to choose its composition and hierarchy;
|
||||||
|
- the canvas, visual direction, palette, wording, and one concrete typography plan using installed font families, with stable size anchors for title, body, annotation, and every other recurring role the roster uses; explicit user, template, or resolved-style requirements may call for a deliberate exception;
|
||||||
|
- an ordinary body-content frame and a density judgment for every page, adapted to the canvas and any user / template / style geometry; use `anchor`, `dense`, `breathing`, or an equivalent active-context distinction instead of one uniform fill level;
|
||||||
|
- for each page not bound to literal supplied geometry, a primary visual zone and page-scale composition direction tied to its core message; use cards or equal grids when the content relationship calls for them, not as the automatic page grammar;
|
||||||
- when useful, one transient deck-level visual motif with an identity or
|
- when useful, one transient deck-level visual motif with an identity or
|
||||||
communication job, a recognizable invariant, and planned variation across
|
communication job, a recognizable invariant, and deliberate variation across
|
||||||
applicable page roles; omit it when restraint serves the deck better;
|
applicable page roles; omit it when restraint serves the deck better;
|
||||||
- a transient resource roster with page, filename, purpose, visual intent,
|
- the resource decisions needed for immediate preparation. Required operational
|
||||||
acquisition path, crop behavior, and status. For an image/formula, include
|
image/formula manifests may carry filenames, page relationship, status, and
|
||||||
its page relationship plus any subject position, focus, quiet region, or
|
generation/crop/focal cues, but do not create a general resource roster or an
|
||||||
overlay-safety cue that must exist before SVG authoring;
|
icon-to-page assignment;
|
||||||
- the implementation path for each resource. An explicit user path wins;
|
- the implementation path for each resource. An explicit user path wins;
|
||||||
otherwise choose the registered automatic/default path without another
|
otherwise choose the registered automatic/default path without another
|
||||||
interaction.
|
interaction.
|
||||||
|
|
||||||
Prepare only the resource paths that the roster triggers:
|
**Mandatory — capability scan, not a coverage quota**: for every page, consider
|
||||||
|
the complete carrier menu once and choose only the forms that communicate its
|
||||||
|
content best. A decision to use none of a carrier is valid; skipping the scan
|
||||||
|
because Quick is expected to be faster is not.
|
||||||
|
|
||||||
|
| Communication job | Available carrier |
|
||||||
|
|---|---|
|
||||||
|
| Real subject, place, product, evidence, atmosphere, or scene benefits from visual grounding | Supplied/extracted, web, AI, or sliced image |
|
||||||
|
| A compact semantic cue clarifies a category, process, KPI, state, navigation item, or real brand | Prepared project-local icon |
|
||||||
|
| Editable geometry can express a relationship, flow, emphasis, callout, symbol, or diagram | Basic SVG primitive, exact Office preset, Boolean result, then necessary freeform |
|
||||||
|
| Values encode comparison, trend, distribution, composition, relationship, or a text grid | Data chart or table, with optional native Chart/Table metadata when its object model is useful |
|
||||||
|
| Mathematical notation is clearer as typeset math than ordinary text | Rendered formula asset |
|
||||||
|
| Typography, spacing, and simple geometry already carry the message | Use no additional visual carrier |
|
||||||
|
|
||||||
|
Prepare only the resource paths needed by the decided pages:
|
||||||
|
|
||||||
| Resource | Required preparation |
|
| Resource | Required preparation |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Supplied/extracted image | Copy the selected file into `images/`; preserve its factual/provenance context and use the measured file rather than an invented substitute |
|
| Supplied/extracted image | Copy the selected file into `images/`; preserve its factual/provenance context and use the measured file rather than an invented substitute |
|
||||||
| Bundled/custom icon | Follow the [icon library contract](../../templates/icons/README.md), resolve the selected SVG under project `icons/`, and use `icon_sync.py` for bundled icons |
|
| Bundled/custom icon | Follow the [icon library contract](../../templates/icons/README.md), choose one coherent primary library, sync a useful project pool covering recurring semantics and likely page-local needs without assigning icons to pages, and choose from that prepared pool during SVG authoring |
|
||||||
| Formula | Follow the [`latex_render.py` contract](../../scripts/docs/image.md), write `images/formula_manifest.json`, run the renderer, and keep the rendered PNG under `images/` |
|
| Formula | Follow the [`latex_render.py` contract](../../scripts/docs/image.md), write `images/formula_manifest.json`, run the renderer, and keep the rendered PNG under `images/` |
|
||||||
| AI image | Follow `image-base.md` + `image-generator.md`; keep `image_prompts.json` and its human-readable sidecar |
|
| AI image | Follow `image-base.md` + `image-generator.md`; keep `image_prompts.json` and its human-readable sidecar |
|
||||||
| Web image | Follow `image-base.md` + `image-searcher.md`; keep query/status data and `image_sources.json`, including any required on-slide attribution |
|
| Web image | Follow `image-base.md` + `image-searcher.md`; keep query/status data and `image_sources.json`, including any required on-slide attribution |
|
||||||
| Illustration slice | Generate or obtain the parent sheet, run `slice_images.py`, and place only the resulting element files |
|
| Illustration slice | Generate or obtain the parent sheet, run `slice_images.py`, and place only the resulting element files |
|
||||||
|
| Data chart/table | Keep source values and the chosen page treatment in active context; load the chart/table authorities in §3 before drawing and write native replacement metadata only when selected |
|
||||||
|
|
||||||
|
**Image inspection boundary**: acquisition-time suitability review follows the
|
||||||
|
owning AI/web/slice reference. Once resources reach terminal status, SVG
|
||||||
|
authoring follows `executor-image.md`'s narrow placement inspection: inspect only
|
||||||
|
one specifically ambiguous `Existing`/`Sourced` asset and never routinely reopen
|
||||||
|
`Generated` outputs.
|
||||||
|
|
||||||
After image resources change, run `analyze_images.py` so
|
After image resources change, run `analyze_images.py` so
|
||||||
`analysis/image_analysis.csv` reflects the files that SVG authoring will use.
|
`analysis/image_analysis.csv` reflects the files that SVG authoring will use.
|
||||||
@@ -121,12 +262,23 @@ silently replace it with unrelated material.
|
|||||||
|
|
||||||
## 3. Direct SVG Authoring
|
## 3. Direct SVG Authoring
|
||||||
|
|
||||||
Always read
|
Always read the following fixed authoring references directly in one batch; do
|
||||||
|
not route among them one file at a time:
|
||||||
[`shared-standards-core.md`](../../references/shared-standards-core.md),
|
[`shared-standards-core.md`](../../references/shared-standards-core.md),
|
||||||
[`svg-effects.md`](../../references/svg-effects.md), and
|
[`svg-effects.md`](../../references/svg-effects.md),
|
||||||
[`native-shape-authoring.md`](../../references/native-shape-authoring.md). Do
|
[`native-shape-authoring.md`](../../references/native-shape-authoring.md),
|
||||||
not load `executor-base.md`: its persisted-plan prerequisites do not apply to
|
[`semantic-svg.md`](../../references/semantic-svg.md),
|
||||||
this profile. For any image/formula, always read
|
[`modes/_index.md`](../../references/modes/_index.md), and
|
||||||
|
[`visual-styles/_index.md`](../../references/visual-styles/_index.md). Resolve
|
||||||
|
one narrative mode and one visual style from explicit user/template requirements
|
||||||
|
or the current content, keep that choice only in active context, and read every
|
||||||
|
exact preset source actually used once. A genuinely novel custom direction
|
||||||
|
follows its resolved behavior without inventing a nearby preset.
|
||||||
|
|
||||||
|
Do not load `executor-base.md`: it owns Default's persisted-plan handoff,
|
||||||
|
first-page gate, and completion routing. Excluding that file is not a capability
|
||||||
|
exclusion; Quick loads the shared and conditional execution authorities here
|
||||||
|
directly. For any image/formula, always read
|
||||||
[`executor-image.md`](../../references/executor-image.md),
|
[`executor-image.md`](../../references/executor-image.md),
|
||||||
[`image-layout-spec.md`](../../references/image-layout-spec.md),
|
[`image-layout-spec.md`](../../references/image-layout-spec.md),
|
||||||
[`image-layout-patterns.md`](../../references/image-layout-patterns.md), and
|
[`image-layout-patterns.md`](../../references/image-layout-patterns.md), and
|
||||||
@@ -135,21 +287,38 @@ this profile. For any image/formula, always read
|
|||||||
web image. Load [`canvas-formats.md`](../../references/canvas-formats.md) only
|
web image. Load [`canvas-formats.md`](../../references/canvas-formats.md) only
|
||||||
for a non-default canvas.
|
for a non-default canvas.
|
||||||
|
|
||||||
|
| Deterministic trigger | Additional authority |
|
||||||
|
|---|---|
|
||||||
|
| Any data chart or text-grid table, including mini/inset charts and sparklines | [`executor-chart.md`](../../references/executor-chart.md); use bounded [`chart_recall.py`](../../scripts/docs/chart-recall.md) only when a reusable visualization reference would help |
|
||||||
|
| Preset pattern or selected PowerPoint-native Chart/Table replacement | [`native-data-interface.md`](../../references/native-data-interface.md) before drawing the object |
|
||||||
|
| Any data-driven chart geometry | [`verify-charts.md`](../stages/verify-charts.md) after the complete roster and before the one final checker |
|
||||||
|
|
||||||
|
Keep the core's shared visual-quality / leading defaults and `svg-effects.md` §6.1 Visual Job Router active while authoring. Explicit user/template requirements and the resolved style override compatible aesthetic defaults, never technical Required / Forbidden boundaries.
|
||||||
|
|
||||||
|
**Per-page execution anchors**: apply the transient core-message, typography-role, body-frame, density, and composition anchors resolved in §2 while authoring; they guide the current run without creating a persisted planning artifact.
|
||||||
|
|
||||||
Use one zero-padded filename width sized for the resolved roster, such as
|
Use one zero-padded filename width sized for the resolved roster, such as
|
||||||
`01_cover.svg` through `12_end.svg` or `001_cover.svg` through `120_end.svg`.
|
`01_cover.svg` through `12_end.svg` or `001_cover.svg` through `120_end.svg`.
|
||||||
Never reuse pages from another run: the exporter publishes every SVG discovered
|
Never reuse pages from another run: the exporter publishes every SVG discovered
|
||||||
under `svg_output/`.
|
under `svg_output/`.
|
||||||
|
|
||||||
**Canvas**: unless the user specifies another canvas, use `ppt169` with
|
**Canvas**: use the canvas resolved in §2: explicit user choice, otherwise the
|
||||||
`viewBox="0 0 1280 720"`. For another requested registered format, load
|
selected Layout/Deck structure-owner canvas, otherwise `ppt169` with
|
||||||
|
`viewBox="0 0 1280 720"`. For another registered format, load
|
||||||
[`canvas-formats.md`](../../references/canvas-formats.md) and use its exact
|
[`canvas-formats.md`](../../references/canvas-formats.md) and use its exact
|
||||||
viewBox. The first SVG establishes the export canvas; every remaining page must
|
viewBox. Template canvas is a default, not a compatibility gate; an explicit
|
||||||
match it exactly.
|
user canvas may adapt the installed visual system. The first SVG establishes
|
||||||
|
the export canvas; every remaining page must match it exactly.
|
||||||
|
|
||||||
**Structure**: author flat, Slide-local SVG only. Include the complete visible
|
**Structure**: author flat, Slide-local SVG only, including when a Layout or
|
||||||
page and all resource references in each SVG; set one root
|
Deck workspace is installed. In that branch, visibly realize the resolved
|
||||||
|
template rules and prototype geometry in the complete pages; do not fall back to
|
||||||
|
free design or merely explain how the template could be used. Include the
|
||||||
|
complete visible page and all resource references in each SVG; set one root
|
||||||
`data-pptx-page-role` from `cover`, `toc`, `section`, `content`, or `ending`,
|
`data-pptx-page-role` from `cover`, `toc`, `section`, `content`, or `ending`,
|
||||||
and omit Master/Layout/layer/placeholder metadata.
|
and omit Master/Layout/layer/placeholder metadata. A request that specifically
|
||||||
|
requires reusable native Master/Layout/placeholder output is incompatible with
|
||||||
|
the lockless Quick exporter and must use the default lock-backed profile.
|
||||||
|
|
||||||
**Typography**: name an installed concrete font family in the SVG; do not depend
|
**Typography**: name an installed concrete font family in the SVG; do not depend
|
||||||
on a lock or generated font asset.
|
on a lock or generated font asset.
|
||||||
@@ -159,16 +328,22 @@ order. Use P01 as the visual anchor and continue directly through the remaining
|
|||||||
pages without a first-page checker or confirmation stop. When a motif was
|
pages without a first-page checker or confirmation stop. When a motif was
|
||||||
resolved, reuse it selectively and vary scale, crop, density, position, or
|
resolved, reuse it selectively and vary scale, crop, density, position, or
|
||||||
content interaction instead of cloning one ornament. Keep this choice only in
|
content interaction instead of cloning one ornament. Keep this choice only in
|
||||||
active context; create no planning artifact or approval stop. After the complete
|
active context; create no planning artifact or approval stop. After every page
|
||||||
roster exists, run the one final checker below. Apply other supporting tools and
|
exists, run the one final checker below. Apply other supporting tools and
|
||||||
stages only when their capability is actually needed.
|
stages only when their capability is actually needed.
|
||||||
|
|
||||||
|
This is not a resume protocol. If the active context is lost before delivery,
|
||||||
|
start a clean Quick run rather than inferring an unfinished plan from the files
|
||||||
|
already present.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Export
|
## 4. Export
|
||||||
|
|
||||||
After every page and required referenced resource exists, run the lockless
|
After every page and required referenced resource exists, run the Quick branch
|
||||||
final SVG check:
|
of [`verify-charts`](../stages/verify-charts.md) when any data-driven chart was
|
||||||
|
authored. Complete all coordinate repairs first; then run the one lockless final
|
||||||
|
SVG check:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ${SKILL_DIR}/scripts/svg_quality_checker.py <project_path> \
|
python3 ${SKILL_DIR}/scripts/svg_quality_checker.py <project_path> \
|
||||||
@@ -199,10 +374,12 @@ or lock.
|
|||||||
## ✅ Quick Generate Complete
|
## ✅ Quick Generate Complete
|
||||||
|
|
||||||
- [x] All required source/resource preparation is complete
|
- [x] All required source/resource preparation is complete
|
||||||
|
- [x] One mode and visual style were resolved, and every catalog source actually used was read
|
||||||
|
- [x] Every page considered the complete visual-carrier menu without a coverage quota
|
||||||
- [x] Resolved SVG pages and their project-local references exist
|
- [x] Resolved SVG pages and their project-local references exist
|
||||||
|
- [x] Every triggered capability-specific preparation and pre-checker verification completed
|
||||||
- [x] The lockless final SVG quality report passes and matches the current SVGs
|
- [x] The lockless final SVG quality report passes and matches the current SVGs
|
||||||
- [x] Every selected optional export capability completed
|
|
||||||
- [x] One native PPTX exists under `exports/` or the explicit output path
|
- [x] One native PPTX exists under `exports/` or the explicit output path
|
||||||
- [x] No Strategist, confirmation, Design Spec, or lock artifact was created
|
- [x] No Strategist, confirmation, root project Design Spec, or lock artifact was created
|
||||||
- [ ] **Next**: Report the PPTX path
|
- [ ] **Next**: Report the PPTX path
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Route selection authority for PPT Master. Select exactly one top-level route, th
|
|||||||
|
|
||||||
**Hard rule**: If this file conflicts with a route summary elsewhere in the
|
**Hard rule**: If this file conflicts with a route summary elsewhere in the
|
||||||
Skill package or in a repository-level user-facing document, this file wins for
|
Skill package or in a repository-level user-facing document, this file wins for
|
||||||
route selection. After selection, the route authority owns execution.
|
route selection. After selection, the active runtime authority owns execution.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -30,8 +30,8 @@ route selection. After selection, the route authority owns execution.
|
|||||||
|
|
||||||
| Route | Request shape | Authority | Preconditions | Mutation model | Output contract |
|
| Route | Request shape | Authority | Preconditions | Mutation model | Output contract |
|
||||||
|---|---|---|---|---|---|
|
|---|---|---|---|---|---|
|
||||||
| Generate PPTX | Create a new presentation; regenerate an existing deck visually; use source material or a topic; optionally apply an explicit template workspace | [`generate-pptx`](./generate-pptx.md) | Source facts exist or research can gather them; explicit quick intent activates its profile | Author new SVG pages and export a new PPTX | Default: spec, lock, SVG, validation, and PPTX; Quick: optional source/resource artifacts, no spec/lock, SVG, and one PPTX |
|
| Generate PPTX | Create a new presentation; regenerate an existing deck visually; use source material or a topic; optionally select a registered library template or supply an explicit workspace | Beautify: [`beautify-pptx`](./profiles/beautify-pptx.md), which selects Default or Quick; ordinary Default: [`generate-pptx`](./generate-pptx.md); ordinary explicit Quick: [`quick-generate`](./profiles/quick-generate.md) | Source facts exist or research can gather them; explicit quick intent activates its runtime | Author new SVG pages and export a new PPTX | Default: spec, lock, SVG, validation, and PPTX; Quick: optional source/resource artifacts, no spec/lock, SVG, and one PPTX |
|
||||||
| Create Template | Create a reusable brand/layout/deck template from one or more PPTX/SVG files, images/PDFs, direct or file-based text, documents/websites, brand assets, or a mixed reference bundle | [`create-template`](./create-template.md) | A reusable-template request exists; reference material is optional, and project scope additionally requires an initialized target project | Author a new portable workspace; never modify any reference file in place | Workspace with required `templates/`, optional `images/` / `icons/`, and optional review `exports/` |
|
| Create Template | Create a reusable brand/style/layout/deck template from one or more PPTX/SVG files, images/PDFs, direct or file-based text, documents/websites, brand assets, or a mixed reference bundle | [`create-template`](./create-template.md) | A reusable-template request exists; reference material is optional, and project scope additionally requires an initialized target project | Author a new portable workspace; never modify any reference file in place | Workspace with required `templates/`, optional `images/` / `icons/`, and optional review `exports/` |
|
||||||
| Fill Native PPTX | Use a raw PPTX's native slide shells and replace/fill content | [`template-fill-pptx`](./template-fill-pptx.md) | Source PPTX plus new material/topic | Clone and patch PPTX through OOXML; no SVG pipeline | New filled PPTX in project `exports/` |
|
| Fill Native PPTX | Use a raw PPTX's native slide shells and replace/fill content | [`template-fill-pptx`](./template-fill-pptx.md) | Source PPTX plus new material/topic | Clone and patch PPTX through OOXML; no SVG pipeline | New filled PPTX in project `exports/` |
|
||||||
| Enhance Native PPTX | Keep a finished PPTX's visible slides stable while adding notes, audio, timings, or transitions | [`native-enhance-pptx`](./native-enhance-pptx.md) | Finished source PPTX exists | Append/update scoped OOXML parts; no slide regeneration | New enhanced PPTX in project `exports/` |
|
| Enhance Native PPTX | Keep a finished PPTX's visible slides stable while adding notes, audio, timings, or transitions | [`native-enhance-pptx`](./native-enhance-pptx.md) | Finished source PPTX exists | Append/update scoped OOXML parts; no slide regeneration | New enhanced PPTX in project `exports/` |
|
||||||
|
|
||||||
@@ -41,11 +41,12 @@ route selection. After selection, the route authority owns execution.
|
|||||||
|
|
||||||
| Request condition | Generate-route behavior |
|
| Request condition | Generate-route behavior |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Explicit quick/fast, skip-strategy, or direct SVG-to-PPTX intent | Activate [`quick-generate`](./profiles/quick-generate.md): prepare sources/resources as needed, let the current agent decide without interaction, omit Strategist/confirmation/spec/lock, hand-author SVG, run the lockless final checker, and export the final PPTX |
|
| Existing PPTX must preserve wording, page count, and page order 1:1 | Activate [`beautify-pptx`](./profiles/beautify-pptx.md); it selects `quick-generate` when that profile's explicit trigger also matches, otherwise `generate-pptx` |
|
||||||
|
| Explicit quick/fast, skip-strategy, or direct SVG-to-PPTX intent without Beautify | Load [`quick-generate`](./profiles/quick-generate.md) directly without loading `generate-pptx.md`: prepare sources/resources as needed, let the current agent decide without interaction, directly apply at most one exact workspace root per kind supplied for this run, otherwise use free design, omit Strategist/Confirm UI/spec/lock, hand-author SVG, run the lockless final checker, and export the final PPTX |
|
||||||
| Topic only, or supplied sources leave planning-critical factual gaps | Run [`topic-research`](./stages/topic-research.md) inside the selected Generate profile's source preparation: immediately for topic-only input, or after conversion and reading for source-backed input; research only the identified gaps |
|
| Topic only, or supplied sources leave planning-critical factual gaps | Run [`topic-research`](./stages/topic-research.md) inside the selected Generate profile's source preparation: immediately for topic-only input, or after conversion and reading for source-backed input; research only the identified gaps |
|
||||||
| Existing PPTX may be split, merged, dropped, reordered, or re-outlined | Treat the PPTX as source content through [`generate-pptx`](./generate-pptx.md) Step 1 and its PPTX intake; continue the default pipeline unless explicit Quick Generate intent selected that profile |
|
| Existing PPTX may be split, merged, dropped, reordered, or re-outlined | Treat the PPTX as source content through the selected Generate authority's source intake; continue the default pipeline unless explicit Quick Generate intent selected that profile |
|
||||||
| Existing PPTX must preserve wording, page count, and page order 1:1 | Activate the [`beautify-pptx`](./profiles/beautify-pptx.md) profile inside the main pipeline |
|
| Default Generate reaches planning | Step 3 prepares template candidates without interaction. Stage 1 then confirms the communication contract and free-design/template choice together; only a confirmed non-free choice runs [`apply-template-workspace`](./stages/apply-template-workspace.md) before Stage 2 |
|
||||||
| Explicit current brand/layout/deck workspace root | Enter [`generate-pptx`](./generate-pptx.md) Step 3 and conditionally load [`apply-template-workspace`](./stages/apply-template-workspace.md); consume the workspace root, never only its inner `templates/` directory |
|
| Explicit current brand/style/layout/deck workspace root | Default Generate preserves the exact path as a Stage-1 template candidate; Quick Generate validates and installs it directly without Steps 3–4 or Confirm UI. Classify it as `library` only when its normalized root exactly matches a registered index entry; otherwise retain `explicit`. Consume the workspace root, never only its inner `templates/` directory |
|
||||||
| Split-mode project resumes in a fresh chat | Run [`resume-execute`](./stages/resume-execute.md) inside the active Generate route |
|
| Split-mode project resumes in a fresh chat | Run [`resume-execute`](./stages/resume-execute.md) inside the active Generate route |
|
||||||
| Existing generated project needs a deck-wide `colors.*` or universal `typography.font_family` substitution | Stay in Generate; load [`update_spec.py`](../scripts/docs/update_spec.md), honor its supported-key boundary, then rerun the final quality gate and Step 7 export |
|
| Existing generated project needs a deck-wide `colors.*` or universal `typography.font_family` substitution | Stay in Generate; load [`update_spec.py`](../scripts/docs/update_spec.md), honor its supported-key boundary, then rerun the final quality gate and Step 7 export |
|
||||||
| User explicitly requests spec refinement | Run [`refine-spec`](./stages/refine-spec.md) after Design Spec Gate 1 and before lock Gate 2 |
|
| User explicitly requests spec refinement | Run [`refine-spec`](./stages/refine-spec.md) after Design Spec Gate 1 and before lock Gate 2 |
|
||||||
@@ -56,14 +57,20 @@ route selection. After selection, the route authority owns execution.
|
|||||||
| `<project_path>/animations.json` already exists, the user explicitly requests per-slide/object-level animation control, or the effective Custom Animations outcome in `design_spec.md §I` is enabled | Run [`customize-animations`](./stages/customize-animations.md) after the final SVG quality gate and any enabled speaker-note pass, before Generate Step 7. A §IX `Motion suggestion` informs an active pass but never triggers it alone |
|
| `<project_path>/animations.json` already exists, the user explicitly requests per-slide/object-level animation control, or the effective Custom Animations outcome in `design_spec.md §I` is enabled | Run [`customize-animations`](./stages/customize-animations.md) after the final SVG quality gate and any enabled speaker-note pass, before Generate Step 7. A §IX `Motion suggestion` informs an active pass but never triggers it alone |
|
||||||
| Generate PPTX receives an explicit narration request or has effective Narration Audio enabled in `design_spec.md §I`; Enhance Native PPTX has a confirmed `audio.enabled: true` module | Run [`generate-audio`](./stages/generate-audio.md) after the owning route's notes/export readiness; Generate audio implies effective Speaker Notes enabled |
|
| Generate PPTX receives an explicit narration request or has effective Narration Audio enabled in `design_spec.md §I`; Enhance Native PPTX has a confirmed `audio.enabled: true` module | Run [`generate-audio`](./stages/generate-audio.md) after the owning route's notes/export readiness; Generate audio implies effective Speaker Notes enabled |
|
||||||
|
|
||||||
**Hard rule — profile, not fifth route**: The 1:1 beautify behavior uses the same Strategist → Executor → SVG export lifecycle as Generate PPTX. It changes content/page invariants; it does not define a separate artifact lifecycle.
|
**Hard rule — profile, not fifth route**: Beautify changes content/page
|
||||||
|
invariants, then selects one existing Generate runtime: explicit Quick intent
|
||||||
|
uses Quick; otherwise it uses Default. It does not define a separate artifact
|
||||||
|
lifecycle or load both runtimes.
|
||||||
|
|
||||||
**Hard rule — direct-generation profile, not a fifth route**: `quick-generate`
|
**Hard rule — direct-generation profile, not a fifth route**: `quick-generate`
|
||||||
stays inside Generate PPTX but owns an explicit SVG → PPTX short circuit. Page
|
stays inside Generate PPTX but owns an explicit SVG → PPTX short circuit. Page
|
||||||
count alone never activates or blocks it. Conversion, bounded research, and
|
count alone never activates or blocks it. Conversion, bounded research, and
|
||||||
project-local resources remain available. Package capabilities may be requested
|
project-local resources remain available. Package capabilities may be requested
|
||||||
or agent-selected. Structured template reuse requires the default lock-backed
|
or agent-selected. Quick may consume exact Brand/Style/Layout/Deck workspaces as
|
||||||
Generate pipeline.
|
flat authoring inputs; compiling reusable Master/Layout/placeholder structure
|
||||||
|
still requires the default lock-backed Generate pipeline. Once selected, Quick
|
||||||
|
is the complete runtime procedure and never loads `generate-pptx.md`; Default
|
||||||
|
never loads `quick-generate.md`. Beautify may select either one, but not both.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -72,20 +79,20 @@ Generate pipeline.
|
|||||||
**Hard rule — no direct structure grafting**: An existing PPTX or SVG is never upgraded in place by adding Master/Layout/placeholder structure. If reusable native structure is required:
|
**Hard rule — no direct structure grafting**: An existing PPTX or SVG is never upgraded in place by adding Master/Layout/placeholder structure. If reusable native structure is required:
|
||||||
|
|
||||||
1. Run [`create-template`](./create-template.md) to produce a separate validated workspace.
|
1. Run [`create-template`](./create-template.md) to produce a separate validated workspace.
|
||||||
2. Pass that workspace root to [`generate-pptx`](./generate-pptx.md) Step 3.
|
2. Pass that workspace root to [`generate-pptx`](./generate-pptx.md) as a Stage-1 template candidate.
|
||||||
3. Author new structured SVG pages whose Master/Layout contract exists from their first generated draft.
|
3. Author new structured SVG pages whose Master/Layout contract exists from their first generated draft.
|
||||||
4. Export a new PPTX from those pages.
|
4. Export a new PPTX from those pages.
|
||||||
|
|
||||||
When a PPTX already contains native Master/Layout parts, `create-template` mirror may read and preserve those existing package facts in the new workspace. It does not infer missing historical intent. An incomplete or legacy SVG package may guide `standard` / `fidelity` visually, but it is not mutated into a structured template and cannot claim source-topology recovery.
|
When a PPTX already contains native Master/Layout parts, `create-template` mirror may read and preserve those existing package facts in the new workspace. It does not infer missing historical intent. An incomplete or legacy SVG package may guide `standard` / `fidelity` visually, but it is not mutated into a structured template and cannot claim source-topology recovery.
|
||||||
|
|
||||||
**Hard rule — no automatic structure upgrade**: Free-design and brand-only generation remains `pptx_structure.mode: flat`. Repeated Slide-local objects never trigger `structured`, Master/Layout promotion, placeholder inference, or deduplication. The minimal Master plus Blank Layout emitted by flat export is package scaffolding, not an inferred reusable design master.
|
**Hard rule — no automatic structure upgrade**: Free-design, brand-only, and style-only generation remains `pptx_structure.mode: flat`. Repeated Slide-local objects never trigger `structured`, Master/Layout promotion, placeholder inference, or deduplication. The minimal Master plus Blank Layout emitted by flat export is package scaffolding, not an inferred reusable design master.
|
||||||
|
|
||||||
| Input | Route behavior |
|
| Input | Route behavior |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Raw PPTX called a template + new content | Fill Native PPTX unless the user explicitly asks for a reusable template workspace |
|
| Raw PPTX called a template + new content | Fill Native PPTX unless the user explicitly asks for a reusable template workspace |
|
||||||
| Any supported reference bundle or direct-text brief + reusable template request | Create Template |
|
| Any supported reference bundle or direct-text brief + reusable template request | Create Template |
|
||||||
| Current template workspace root + content | [`generate-pptx`](./generate-pptx.md) Step 3 |
|
| Current template workspace root + content | [`generate-pptx`](./generate-pptx.md) Stage-1 template choice |
|
||||||
| Legacy-flat root with current `design_spec.md` and current SVG contract | [`apply-template-workspace`](./stages/apply-template-workspace.md) compatibility reader |
|
| Legacy-flat Brand/Layout/Deck root satisfying its current kind contract; Layout/Deck also require current structured SVGs | [`apply-template-workspace`](./stages/apply-template-workspace.md) compatibility reader; Style has no flat form |
|
||||||
| Semantic-legacy or incomplete structured package | Create a new workspace through Create Template; do not migrate in place |
|
| Semantic-legacy or incomplete structured package | Create a new workspace through Create Template; do not migrate in place |
|
||||||
| Request to add a master directly to an existing PPTX/SVG | Unsupported; explain the Create Template → Generate PPTX lifecycle |
|
| Request to add a master directly to an existing PPTX/SVG | Unsupported; explain the Create Template → Generate PPTX lifecycle |
|
||||||
|
|
||||||
@@ -96,14 +103,17 @@ When a PPTX already contains native Master/Layout parts, `create-template` mirro
|
|||||||
| Selected kind | Behavior |
|
| Selected kind | Behavior |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `brand` | Dispatch to [`create-brand`](./create-template/create-brand.md); write identity only and no SVG roster |
|
| `brand` | Dispatch to [`create-brand`](./create-template/create-brand.md); write identity only and no SVG roster |
|
||||||
|
| `style` | Dispatch to [`create-style`](./create-template/create-style.md); write reusable communication method and design direction only, with no SVG roster or native structure |
|
||||||
| `layout` | Dispatch to [`create-layout`](./create-template/create-layout.md); author brand-neutral, application-neutral structure and an SVG roster |
|
| `layout` | Dispatch to [`create-layout`](./create-template/create-layout.md); author brand-neutral, application-neutral structure and an SVG roster |
|
||||||
| `deck` | Dispatch to [`create-deck`](./create-template/create-deck.md); author descriptive recurring-application context with integrated identity, structure, and an SVG roster |
|
| `deck` | Dispatch to [`create-deck`](./create-template/create-deck.md); author descriptive recurring-application context with integrated identity, structure, and an SVG roster |
|
||||||
|
|
||||||
Create Template remains the fixed route name and owns the shared contract. These three documents are mutually exclusive child workflows, not additional top-level routes.
|
Create Template remains the fixed route name and owns the shared contract. These four documents are mutually exclusive child workflows, not additional top-level routes.
|
||||||
|
|
||||||
**Hard rule — classify reusable rules, not source completeness**: A complete
|
**Hard rule — classify reusable rules, not source completeness**: A complete
|
||||||
PPTX does not automatically select Deck. Use Brand when only identity is
|
PPTX does not automatically select Deck. Use Brand when only identity is
|
||||||
stable; use Layout when structure is brand-neutral and the communication
|
stable; use Style when reusable communication method and design direction
|
||||||
|
should travel without identity truth, page prototypes, or native
|
||||||
|
structure; use Layout when structure is brand-neutral and the communication
|
||||||
application stays downstream-defined; use Deck when structure carries identity
|
application stays downstream-defined; use Deck when structure carries identity
|
||||||
or reusable scenario/content semantics.
|
or reusable scenario/content semantics.
|
||||||
|
|
||||||
@@ -120,20 +130,35 @@ Object animation for generated SVG projects uses the animation stage. Native PPT
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Template Name Boundary
|
## 7. Template Selection Boundary
|
||||||
|
|
||||||
| User input | Behavior |
|
| User input | Behavior |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Explicit current workspace root containing `templates/design_spec.md` | Enter [`generate-pptx`](./generate-pptx.md) Step 3 |
|
| Default Generate | Step 3 prepares candidates only; Stage 1 confirms one communication contract plus either free design or template use in the same interaction |
|
||||||
| Bare template/brand name or style label | Do not resolve it to a local path; treat it as a style brief |
|
| Explicit current workspace root containing `templates/design_spec.md` | Preserve it as a Stage-1 candidate and initialize template mode; preselect that specific candidate only when it is the sole supplied root. An exact registered-root match may be displayed as `library` |
|
||||||
| “What templates exist?” | List indexed workspace paths as Q&A; do not advance a route |
|
| No exact workspace root and no explicit template intent | Initialize Stage 1 to free design; the user may switch to template mode and select an indexed workspace |
|
||||||
|
| Explicit template intent or any exact workspace root | Initialize Stage 1 to template mode; exactly one root may be preselected, while multiple roots remain unselected candidates |
|
||||||
|
| Bare template/brand name or style label without an explicit template-use request | Do not resolve it to a local path or preselect a template; treat it as a style brief. An explicit request to use templates still initializes template mode, but leaves the specific candidate for the user to choose |
|
||||||
|
| “What templates exist?” in chat | List indexed workspace paths; Stage 1 still requires an explicit free-design/template choice |
|
||||||
|
|
||||||
For that Q&A only, read the matching discovery indexes:
|
The default UI and chat discovery read only these indexes. Never scan the
|
||||||
|
corresponding directories to construct or supplement the catalog:
|
||||||
|
|
||||||
| Kind | Discovery index |
|
| Kind | Discovery index |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Brand | [`brands_index.json`](../templates/brands/brands_index.json) |
|
| Brand | [`brands_index.json`](../templates/brands/brands_index.json) |
|
||||||
|
| Style | [`styles_index.json`](../templates/styles/styles_index.json) |
|
||||||
| Layout | [`layouts_index.json`](../templates/layouts/layouts_index.json) |
|
| Layout | [`layouts_index.json`](../templates/layouts/layouts_index.json) |
|
||||||
| Deck | [`decks_index.json`](../templates/decks/decks_index.json) |
|
| Deck | [`decks_index.json`](../templates/decks/decks_index.json) |
|
||||||
|
|
||||||
**Forbidden — fuzzy resolution**: Never resolve a bare name to a local template directory on the user's behalf. The explicit workspace root is the only Step 3 template trigger, except the exact validated workspace handed off by Create Template in the current conversation.
|
**Hard rule — one Stage-1 confirmation, delayed template reading**: Author the
|
||||||
|
communication recommendation without reading candidate workspaces. Stage 1
|
||||||
|
confirms that contract and the template/free-design choice together. Only then
|
||||||
|
validate/install selected roots and complete the handoff. Stage 2 waits for that
|
||||||
|
handoff, reads only the installed project-local state, and decides how to apply
|
||||||
|
it; it never reselects a template.
|
||||||
|
|
||||||
|
**Forbidden — fuzzy resolution**: Never resolve a bare name to a local template
|
||||||
|
directory on the user's behalf. A library choice comes from an index-derived
|
||||||
|
root; an unregistered workspace requires an explicit root, including the exact
|
||||||
|
validated workspace handed off by Create Template in the current conversation.
|
||||||
|
|||||||
+77
-31
@@ -1,26 +1,47 @@
|
|||||||
---
|
---
|
||||||
description: Conditional Generate-PPTX runbook for validating, installing, or fusing explicit Brand, Layout, and Deck workspaces.
|
description: Generate-PPTX runbook for validating, installing, or fusing selected Brand, Style, Layout, and Deck workspaces.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Apply Template Workspace Stage
|
# Apply Template Workspace Stage
|
||||||
|
|
||||||
> Run only from [`generate-pptx.md`](../generate-pptx.md) Step 3 after the user supplies an explicit workspace-root path, or after Create Template hands off its exact validated workspace root in the current conversation. Never load this stage for free design, a bare template name, a style description, or a brand mention.
|
> Run from [`generate-pptx.md`](../generate-pptx.md) Step 4 only after Stage 1 confirms at least one exact template workspace. [`quick-generate`](../profiles/quick-generate.md) enters only for exact roots or a current Create Template handoff. Never load for free design, bare names, or style descriptions. This stage applies the completed Stage-1 selection; it never chooses a workspace or changes the communication contract.
|
||||||
|
|
||||||
## 1. Gate and Normalize Inputs
|
## 1. Gate and Normalize Inputs
|
||||||
|
|
||||||
🚧 **GATE**: Every external input resolves to one of these current contracts:
|
🚧 **GATE**: Either Default Stage 1 confirmed a non-free template selection, or
|
||||||
|
Quick received exact roots directly from the
|
||||||
|
user/current Create Template handoff. In Quick, that explicit input is the complete selection authority: do
|
||||||
|
not launch Confirm UI or create `template_options.json`,
|
||||||
|
`template_selection.json`, or `template_handoff.json`. Every selected input must
|
||||||
|
resolve to one of these current contracts:
|
||||||
|
|
||||||
| Input shape | Spec and SVG source | Asset source |
|
| Input shape | Spec and SVG source | Asset source |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Current workspace root | `<root>/templates/design_spec.md` and `<root>/templates/` | Existing `<root>/images/` and `<root>/icons/` |
|
| Current workspace root | `<root>/templates/design_spec.md` and `<root>/templates/` | Existing `<root>/images/` and `<root>/icons/` |
|
||||||
| Compatible legacy-flat root | `<root>/design_spec.md` and current-contract SVGs under `<root>/` | Package-local files |
|
| Compatible legacy-flat Brand/Layout/Deck root | `<root>/design_spec.md`; Layout/Deck also require current-contract SVGs under `<root>/` | Package-local files |
|
||||||
| Current Create Template handoff | Its exact validated library or project workspace root | Existing portable sibling `images/` and `icons/`; already installed only when the root is the target project |
|
| Current Create Template handoff | Its exact validated library or project workspace root | Existing portable sibling `images/` and `icons/`; already installed only when the root is the target project |
|
||||||
|
|
||||||
The spec frontmatter MUST declare `kind: brand`, `kind: layout`, or `kind: deck`. Do not accept only another project's inner `templates/` directory because that omits sibling assets.
|
The spec frontmatter MUST declare `kind: brand`, `kind: style`, `kind: layout`, or `kind: deck`. Do not accept only another project's inner `templates/` directory because that omits sibling assets.
|
||||||
|
|
||||||
**Hard rule — raw source boundary**: A raw PPTX is not a Step 3 workspace. Raw PPTX plus new content uses [`template-fill-pptx`](../template-fill-pptx.md). When the user wants reusable SVG/template generation, run [`create-template`](../create-template.md) first and return with its validated workspace root. Never add Master/Layout/placeholder structure directly to an existing PPTX or SVG project.
|
**Selection-source classification**:
|
||||||
|
|
||||||
**Compatibility gate**: Reject semantic-legacy or incomplete structured packages, including old baseline/distillation metadata, incomplete Master identity, or legacy direct atomic placeholders. Create a new current workspace through Create Template; use the original PPTX when native topology must be preserved. A legacy-flat directory is readable only when its SVG contract is current.
|
| Source label | Resolution rule |
|
||||||
|
|---|---|
|
||||||
|
| `library` | The normalized root exactly equals `templates/<kind_dir>/<id>/` derived from an entry in that kind's `*_index.json` |
|
||||||
|
| `explicit` | The user or Create Template supplied an exact workspace root that is not registered at that canonical index-derived root |
|
||||||
|
|
||||||
|
Read library choices only from `brands_index.json`, `styles_index.json`,
|
||||||
|
`layouts_index.json`, and `decks_index.json`. Never scan kind directories or
|
||||||
|
promote an unregistered directory into the UI catalog. An explicit root remains
|
||||||
|
valid without index membership; exact equality with a registered root may be
|
||||||
|
reported as `library`. The label changes discovery provenance only, never schema
|
||||||
|
validation, fusion precedence, or installation behavior.
|
||||||
|
|
||||||
|
**Selection cardinality**: Default Stage 1 permits one registered root per kind plus one explicit root; its explicit root may pair with a same-kind library root under §5.2. Quick has no page or catalog selection and accepts at most one supplied exact root per declared kind (four roots total). Kinds compose freely in both profiles. Reject larger default receipts server-side; require an oversized or duplicate-kind Quick input to converge in chat before installation, never through Confirm UI.
|
||||||
|
|
||||||
|
**Hard rule — raw source boundary**: A raw PPTX is not a template workspace. Raw PPTX plus new content uses [`template-fill-pptx`](../template-fill-pptx.md). When the user wants reusable SVG/template generation, run [`create-template`](../create-template.md) first; its validated workspace-root handoff becomes a Stage-1 candidate and is preselected only when it is the sole supplied root. Never add Master/Layout/placeholder structure directly to an existing PPTX or SVG project.
|
||||||
|
|
||||||
|
**Compatibility gate**: Reject semantic-legacy or incomplete structured packages, including old baseline/distillation metadata, incomplete Master identity, or legacy direct atomic placeholders. Create a new current workspace through Create Template; use the original PPTX when native topology must be preserved. A legacy-flat Brand/Layout/Deck directory is readable only when it satisfies its current kind contract; Layout/Deck also require a current structured SVG contract. Style has no legacy-flat form and always requires `<root>/templates/design_spec.md`.
|
||||||
|
|
||||||
## 2. Read the Matching Schema
|
## 2. Read the Matching Schema
|
||||||
|
|
||||||
@@ -29,14 +50,27 @@ Read [`templates/README.md`](../../templates/README.md), then only the README fo
|
|||||||
| Kind | Schema | Owned segment |
|
| Kind | Schema | Owned segment |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `brand` | [`templates/brands/README.md`](../../templates/brands/README.md) | Identity: color, typography, logo, voice/tone, icon style |
|
| `brand` | [`templates/brands/README.md`](../../templates/brands/README.md) | Identity: color, typography, logo, voice/tone, icon style |
|
||||||
|
| `style` | [`templates/styles/README.md`](../../templates/styles/README.md) | Direction/method: reusable communication method, visual language, composition, and information-expression defaults |
|
||||||
| `layout` | [`templates/layouts/README.md`](../../templates/layouts/README.md) | Structure: canvas, page structure, semantic text roles, page types, SVG roster |
|
| `layout` | [`templates/layouts/README.md`](../../templates/layouts/README.md) | Structure: canvas, page structure, semantic text roles, page types, SVG roster |
|
||||||
| `deck` | [`templates/decks/README.md`](../../templates/decks/README.md) | Application plus integrated identity and structure |
|
| `deck` | [`templates/decks/README.md`](../../templates/decks/README.md) | Application plus integrated identity and structure |
|
||||||
|
|
||||||
A Layout created with `mirror` remains eligible only when its source contract is brand-neutral and application-neutral. Keep a branded or application-bearing source as a Deck, or re-author it as Layout through `standard` / `fidelity`; do not remove those semantics through mirror.
|
A Layout created with `mirror` remains eligible only when its source contract is brand-neutral and application-neutral. Keep a branded or application-bearing source as a Deck, or re-author it as Layout through `standard` / `fidelity`; do not remove those semantics through mirror.
|
||||||
|
|
||||||
|
Before mapping any current workspace, run its shared package validator from the
|
||||||
|
workspace root. This is the same schema authority used during creation and
|
||||||
|
library registration; Brand/Style pass without SVG, while Layout/Deck validate
|
||||||
|
their roster and structure:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 skills/ppt-master/scripts/svg_quality_checker.py "<workspace_root>/templates" --template-mode
|
||||||
|
```
|
||||||
|
|
||||||
|
Any error blocks installation. A compatible legacy-flat root uses its own root
|
||||||
|
as the checker target.
|
||||||
|
|
||||||
## 3. Structured Preflight
|
## 3. Structured Preflight
|
||||||
|
|
||||||
Before copying a Deck or Layout workspace, inspect every SVG root and slot:
|
Before copying a Deck or Layout workspace, inspect every SVG root and slot. Brand and Style workspaces are roster-free and skip this structured preflight:
|
||||||
|
|
||||||
- Every page declares root Master/Layout keys and PowerPoint picker names.
|
- Every page declares root Master/Layout keys and PowerPoint picker names.
|
||||||
- Master/Layout visuals are direct atoms, not generic layer `<g>` wrappers.
|
- Master/Layout visuals are direct atoms, not generic layer `<g>` wrappers.
|
||||||
@@ -49,8 +83,9 @@ Before copying a Deck or Layout workspace, inspect every SVG root and slot:
|
|||||||
| Kind | Install behavior |
|
| Kind | Install behavior |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `brand` | Install `templates/` plus existing `images/` and `icons/`; ignore `exports/`. Identity is constrained; structure remains free. |
|
| `brand` | Install `templates/` plus existing `images/` and `icons/`; ignore `exports/`. Identity is constrained; structure remains free. |
|
||||||
| `layout` | Install the same portable roots. Expose the actual reusable structure; Strategist later inspects the prototypes and derives the application plan automatically. |
|
| `style` | Install `templates/design_spec.md` only. Ignore sibling project scaffolding and reject a library Style carrying asset/review payloads. Expose reusable direction/method without identity truth, page prototypes, or native structure. Default Style-only and Style + Brand derive `template_reuse_scope: style` and stay flat; Style + Layout/Deck follows the selected structure plan. Quick always realizes the resolved combination as flat pages. A Style workspace never activates visual review. |
|
||||||
| `deck` | Install the same portable roots. Expose descriptive application context, identity, structure, and the actual prototype roster; Strategist compares them with the current communication contract and content, then derives the application plan automatically. |
|
| `layout` | Install the same portable roots. Expose the actual reusable structure; Default Strategist later inspects the prototypes, while Quick's current agent uses them for immediate flat authoring decisions in active context. |
|
||||||
|
| `deck` | Install the same portable roots. Expose descriptive application context, identity, structure, and the actual prototype roster; Default Strategist or Quick's current agent compares them with the current communication contract and content, then derives the applicable plan. |
|
||||||
|
|
||||||
For a compatible legacy-flat package, route SVG/spec/non-bitmaps to project `templates/`, bitmaps to project `images/`, and declared icons to project `icons/`. Do not infer legacy Master/Layout semantics from the flat directory shape.
|
For a compatible legacy-flat package, route SVG/spec/non-bitmaps to project `templates/`, bitmaps to project `images/`, and declared icons to project `icons/`. Do not infer legacy Master/Layout semantics from the flat directory shape.
|
||||||
|
|
||||||
@@ -63,55 +98,66 @@ For a compatible legacy-flat package, route SVG/spec/non-bitmaps to project `tem
|
|||||||
|
|
||||||
If the normalized source root equals the target project root, consume it in place and copy nothing. An in-place workspace cannot participate in multi-path fusion. Ignore source `exports/`; it contains review artifacts, not portable template inputs. Empty optional roots remain absent.
|
If the normalized source root equals the target project root, consume it in place and copy nothing. An in-place workspace cannot participate in multi-path fusion. Ignore source `exports/`; it contains review artifacts, not portable template inputs. Empty optional roots remain absent.
|
||||||
|
|
||||||
|
**Hard rule — project-local consumer boundary**: After installation/fusion,
|
||||||
|
Default template-aware Strategist work in final Stage 2, Quick's current
|
||||||
|
agent before direct authoring, and every later role read only
|
||||||
|
`<project_path>/templates/` and the project-local `images/` / `icons/` pools. The original library or external root
|
||||||
|
is installation input, not a later prompt source. If source and target are the
|
||||||
|
same project root, that in-place root already satisfies this boundary.
|
||||||
|
|
||||||
Template SVGs are authoring prototypes, not export-time overlays. The generated page remains complete in `svg_output/`; `page_layouts` selects the complete prototype and its explicit structure contract for authoring.
|
Template SVGs are authoring prototypes, not export-time overlays. The generated page remains complete in `svg_output/`; `page_layouts` selects the complete prototype and its explicit structure contract for authoring.
|
||||||
|
Quick instead realizes the selected prototypes into complete flat, Slide-local
|
||||||
|
SVGs and never writes `page_layouts` or Master/Layout/placeholder metadata.
|
||||||
|
|
||||||
|
|
||||||
## 5. Fuse Multiple Workspace Paths
|
## 5. Fuse Multiple Workspace Paths
|
||||||
|
|
||||||
Multi-path fusion supports different kinds, or at most two workspaces of the same kind. Resolve all segment and asset conflicts before writing any target file.
|
Fuse different kinds or, in Default only, at most two same-kind roots. Resolve template segment/asset conflicts before writing. Never reinterpret, predict, or revise the confirmed Stage-1 communication contract here. Default obtains any additional material conflict decision through the active chat channel after Stage 1; this conditional resolution does not reopen template selection. Quick follows explicit conflict instructions; an unresolved material compatibility conflict is a hard prerequisite handled in chat, never by launching Confirm UI or by using path order.
|
||||||
|
|
||||||
### 5.1 Different Kinds
|
### 5.1 Different Kinds
|
||||||
|
|
||||||
Use segment-level integer replacement; do not mix fields implicitly:
|
Resolve four whole template segments. This table selects the installed starting owner; current user instructions and the caller's later consuming plan still govern project use:
|
||||||
|
|
||||||
| Combination | Identity from | Structure from | Application from |
|
| Segment | Starting owner |
|
||||||
|---|---|---|---|
|
|---|---|
|
||||||
| brand only | brand | free design | none |
|
| Identity | Brand, otherwise Deck, otherwise unresolved until the consuming plan (Default final Stage 2 or Quick active context). Style color/type/icon/image values are direction candidates, never identity truth. |
|
||||||
| layout only | free design | layout | none |
|
| Structure | A compatible Layout, otherwise Deck, otherwise unresolved/free design until the consuming plan. Style owns no canvas, prototype, Master/Layout, slot, or page mapping. |
|
||||||
| deck only | deck | deck | deck |
|
| Reusable application context | Deck only when present. Preserve it for the consuming comparison; it never becomes the current project's application contract. |
|
||||||
| brand + layout | brand | layout | current Stage-1 communication contract |
|
| Direction / method | Style when present, otherwise unresolved until the consuming plan. Actual Deck prototypes and Signature facts may inform compatibility, but Deck does not own the Style-only method segment. |
|
||||||
| brand + deck | brand | deck | deck |
|
|
||||||
| layout + deck | deck | compatible layout | deck |
|
|
||||||
| brand + layout + deck | brand | compatible layout | deck |
|
|
||||||
|
|
||||||
Before Layout overrides Deck structure, verify that every required application/narrative/content role fits the Layout's page roles, slot types, and capacity. On mismatch, stop and surface exactly three remedies: retain Deck structure, select another Layout, or explicitly revise the application contract.
|
Replace each selected segment wholesale; do not mix its fields implicitly. Brand or Deck identity replaces any identity-adjacent defaults carried by Style. A Style direction may adapt to that resolved identity, but cannot relabel its candidates as official brand facts.
|
||||||
|
|
||||||
Field-level micro-adjustments such as a primary-color override are not Step 3 fusion. Carry them into the normal Strategist confirmation fields.
|
Before Layout overrides Deck structure, compare Deck's reusable roles with Layout roles, slots, and capacity. On mismatch, offer exactly three remedies: retain Deck structure, select another Layout, or omit Deck. Default resolves only this template-to-template conflict and must not reinterpret the confirmed Stage-1 communication contract; Quick compares against the current request/content and treats any unresolved material mismatch as a chat hard prerequisite.
|
||||||
|
|
||||||
|
Before Style overlays Deck guidance, verify that its method serves Deck's reusable context and fits the selected structure. On mismatch, require omitting Style or choosing a compatible Style/structure; never silently weaken a segment. Default final Stage 2 separately checks the result against the confirmed project contract; Quick checks it against the current request/content before authoring.
|
||||||
|
|
||||||
|
Field-level micro-adjustments such as a primary-color override are not workspace fusion. Default carries them into the normal final Stage-2 confirmation fields; Quick treats explicit adjustments as direct active-context authoring constraints.
|
||||||
|
|
||||||
### 5.2 Same Kind
|
### 5.2 Same Kind
|
||||||
|
|
||||||
Do not use path order as priority. Report every segment-level difference and ask the user to choose workspace A, workspace B, or select per segment. Only the per-segment choice opens a segment-by-segment resolution. Do not resolve field-level conflicts here. Three or more same-kind paths require the user to converge to at most two.
|
Default only: do not use path order as priority. Report every segment-level difference and ask the user to choose workspace A, workspace B, or select per segment. Only the per-segment choice opens a segment-by-segment resolution. Two Style workspaces conflict over the complete Direction / method segment. Do not resolve field-level conflicts here. Three or more same-kind paths require the user to converge to at most two. Quick accepts one root per kind and therefore never enters this subsection.
|
||||||
|
|
||||||
### 5.3 Fused Provenance
|
### 5.3 Fused Provenance
|
||||||
|
|
||||||
Write one final `<project>/templates/design_spec.md`. Immediately under its H1, record every source kind/path, base or override role, and user-resolved segment conflict:
|
Write one final `<project>/templates/design_spec.md`. Immediately under its H1, record every source kind/path, base or override role, and resolved segment conflict with its explicit authority:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
> **Fused from:**
|
> **Fused from:**
|
||||||
> - deck: `templates/decks/example/` (base)
|
> - deck: `templates/decks/example/` (base)
|
||||||
> - brand: `templates/brands/example/` (identity override)
|
> - brand: `templates/brands/example/` (identity override)
|
||||||
|
> - style: `templates/styles/example/` (direction/method override)
|
||||||
> - layout: `templates/layouts/example/` (structure override)
|
> - layout: `templates/layouts/example/` (structure override)
|
||||||
> - conflicts resolved: Color Scheme from brand (user selected A)
|
> - conflicts resolved: Color Scheme from brand (user selected A)
|
||||||
```
|
```
|
||||||
|
|
||||||
Single-path installs do not add provenance. Set fused frontmatter `kind` from the resulting capability: `deck` when identity and structure are both present, `layout` for structure only, or `brand` for identity only. A project-local Brand + Layout fusion uses `kind: deck` for routing but is not automatically a reusable library Deck; its application remains current-project context.
|
Single-path installs do not add provenance. Set fused frontmatter `kind` from the non-Style capability: `deck` when identity and structure are both present, `layout` for structure only, or `brand` for identity only. Use `kind: style` only for Style-only input. Adding Style to Brand, Layout, Deck, or Brand + Layout does not change that existing capability label; its Direction / method ownership stays explicit in the fused provenance and body. A project-local Brand + Layout fusion uses `kind: deck` for routing but is not automatically a reusable library Deck; its application remains current-project context.
|
||||||
|
|
||||||
**Completion receipt**: Report `roots=<normalized roots>; kinds=<kind per root>; install=<in-place|copied>; final_spec=<project_path>/templates/design_spec.md`.
|
**Completion receipt**: Report `roots=<normalized roots>; sources=<library|explicit per root>; kinds=<kind per root>; segments=identity:<owner>,structure:<owner>,application_context:<owner>,direction:<owner>; install=<in-place|copied>; final_spec=<project_path>/templates/design_spec.md`.
|
||||||
|
|
||||||
## ✅ Template Workspace Applied
|
## ✅ Template Workspace Applied
|
||||||
|
|
||||||
- [x] Every input was an explicit root satisfying a listed workspace contract or the exact current Create Template handoff
|
- [x] Every selected input was an index-derived library root or an exact explicit/Create Template root satisfying a listed workspace contract
|
||||||
- [x] Kind schemas and structured SVG contracts passed preflight
|
- [x] Every kind schema passed preflight; structured SVG checks ran only for Layout/Deck inputs
|
||||||
- [x] All collisions and fusion conflicts were resolved before one atomic install
|
- [x] All collisions and fusion conflicts were resolved before one atomic install
|
||||||
- [x] `<project_path>/templates/` and any portable sibling assets are complete
|
- [x] `<project_path>/templates/` and any portable sibling assets are complete and are the only downstream template source
|
||||||
- [ ] **Next**: Return to [`generate-pptx.md`](../generate-pptx.md) Step 4
|
- [ ] **Next**: Default completes the template-selection handoff and continues [`generate-pptx.md`](../generate-pptx.md) Step 4 Stage 2; Quick returns to [`quick-generate`](../profiles/quick-generate.md) §2
|
||||||
|
|||||||
+3
-3
@@ -27,9 +27,9 @@ description: Optional post-processing stage for per-slide and per-object animati
|
|||||||
| No existing sidecar; user only wants deck-wide page transitions, auto-advance, or one per-element object animation policy | Do not run; apply [`animations.md`](../../references/animations.md) with exporter flags such as `-a auto` or `-a emphasis_spin` |
|
| No existing sidecar; user only wants deck-wide page transitions, auto-advance, or one per-element object animation policy | Do not run; apply [`animations.md`](../../references/animations.md) with exporter flags such as `-a auto` or `-a emphasis_spin` |
|
||||||
| `svg_output/*.svg` is missing | Complete the main Executor phase first |
|
| `svg_output/*.svg` is missing | Complete the main Executor phase first |
|
||||||
|
|
||||||
**Decision precedence**: latest explicit instruction → Stage 3 policy →
|
**Decision precedence**: latest explicit instruction → final Stage-2 policy →
|
||||||
compatibility default `false`; provenance stays in Design Spec §I, never the
|
workflow default `false`; provenance stays in Design Spec §I, never the
|
||||||
lock. Stage 3 `false` blocks creation, not an existing sidecar. Existing
|
lock. Final Stage-2 `false` blocks creation, not an existing sidecar. Existing
|
||||||
sidecars enter this stage; explicit disables follow the table without deletion.
|
sidecars enter this stage; explicit disables follow the table without deletion.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+1
-1
@@ -240,7 +240,7 @@ The edge command writes each MP3 and its internal page SRT from the same `edge-t
|
|||||||
| Both are absent | Create no sidecar; inherit the base report's deck motion |
|
| Both are absent | Create no sidecar; inherit the base report's deck motion |
|
||||||
|
|
||||||
Generate passes the base report through `--inherit-motion-from`: inherited
|
Generate passes the base report through `--inherit-motion-from`: inherited
|
||||||
`-a none` preserves explicit objects-off, while Stage 3 `false` does not.
|
`-a none` preserves explicit objects-off, while final Stage-2 `false` does not.
|
||||||
Only explicit all-motion-off uses `--no-animations`. Invalid reports block;
|
Only explicit all-motion-off uses `--no-animations`. Invalid reports block;
|
||||||
audio duration plus padding owns final advance.
|
audio duration plus padding owns final advance.
|
||||||
|
|
||||||
|
|||||||
+9
-2
@@ -8,6 +8,13 @@ description: Main-pipeline control stage for resuming execution in a fresh chat
|
|||||||
|
|
||||||
This stage is **context-independent**: it owns the execution session starting from a fresh chat — no upstream conversation context required. Persisted project artifacts replace the planning session's confirmation dialogue and image-acquisition history.
|
This stage is **context-independent**: it owns the execution session starting from a fresh chat — no upstream conversation context required. Persisted project artifacts replace the planning session's confirmation dialogue and image-acquisition history.
|
||||||
|
|
||||||
|
`validation/workflow.log` is a cold command/outcome audit log with optional
|
||||||
|
important manual entries, not persisted planning state. Do not open or replay
|
||||||
|
it while resuming. Use the real artifacts in Step 1 to establish current state;
|
||||||
|
inspect the log only when the user explicitly asks to review the prior run. Run
|
||||||
|
inherited Python commands normally; their shared CLI bootstrap records a
|
||||||
|
bounded material outcome selection automatically, not the full console stream.
|
||||||
|
|
||||||
## When to Run
|
## When to Run
|
||||||
|
|
||||||
The user opens a new chat and gives a phrase that names a project path and signals continuation. Recognize any of:
|
The user opens a new chat and gives a phrase that names a project path and signals continuation. Recognize any of:
|
||||||
@@ -52,7 +59,7 @@ Then jump to `### Step 6: Executor Phase` and run the documented pipeline:
|
|||||||
|
|
||||||
- Read the complete project Design Spec, then the complete `spec_lock.md`, once to establish the fresh execution context
|
- Read the complete project Design Spec, then the complete `spec_lock.md`, once to establish the fresh execution context
|
||||||
- Resolve the effective Speaker Notes, Custom Animations, and Narration Audio
|
- Resolve the effective Speaker Notes, Custom Animations, and Narration Audio
|
||||||
outcomes from `design_spec.md §I`. Missing legacy outcomes use
|
outcomes from `design_spec.md §I`. Missing outcomes use the workflow defaults
|
||||||
`enabled` / `disabled` / `disabled`; these production decisions never come
|
`enabled` / `disabled` / `disabled`; these production decisions never come
|
||||||
from `spec_lock.md`
|
from `spec_lock.md`
|
||||||
- If resuming mid-deck, read the latest completed SVG and current image metadata when images are used
|
- If resuming mid-deck, read the latest completed SVG and current image metadata when images are used
|
||||||
@@ -71,7 +78,7 @@ Then jump to `### Step 6: Executor Phase` and run the documented pipeline:
|
|||||||
|
|
||||||
Reload the Generate authority and required execution references; do not reconstruct or replay the earlier planning conversation.
|
Reload the Generate authority and required execution references; do not reconstruct or replay the earlier planning conversation.
|
||||||
|
|
||||||
If the user gives a newer explicit instruction after Stage 3, update only the
|
If the user gives a newer explicit instruction after final Stage 2, update only the
|
||||||
affected effective outcome and provenance in `design_spec.md §I`, then resume at
|
affected effective outcome and provenance in `design_spec.md §I`, then resume at
|
||||||
its owning step. Do not reopen Confirm UI or add the decision to
|
its owning step. Do not reopen Confirm UI or add the decision to
|
||||||
`spec_lock.md`. Before writing, apply Generate's single notes/audio dependency
|
`spec_lock.md`. Before writing, apply Generate's single notes/audio dependency
|
||||||
|
|||||||
+33
-11
@@ -4,7 +4,12 @@ description: Generate source-intake stage that fills externally verifiable factu
|
|||||||
|
|
||||||
# Topic Research Stage
|
# Topic Research Stage
|
||||||
|
|
||||||
> Factual preparation inside [`generate-pptx`](../generate-pptx.md) source intake. Default Generate hands its output to Strategist; Quick Generate keeps it with the current agent. Run immediately for topic-only input, or after supplied material is converted and read when it leaves planning-critical factual gaps. Output is a research supplement plus stable fact provenance for project import.
|
> Factual preparation inside the active Generate profile's source intake.
|
||||||
|
> Default Generate hands its output to Strategist; Quick Generate's main agent
|
||||||
|
> consumes the same output. Run immediately for topic-only input, or after
|
||||||
|
> supplied material is converted and read when it leaves planning-critical
|
||||||
|
> factual gaps. Output is a research supplement plus stable fact provenance for
|
||||||
|
> project import.
|
||||||
|
|
||||||
This stage supplies facts needed to build the requested deck. It does not select,
|
This stage supplies facts needed to build the requested deck. It does not select,
|
||||||
download, or generate images. Default Generate resolves image selection in the
|
download, or generate images. Default Generate resolves image selection in the
|
||||||
@@ -18,7 +23,7 @@ phase without adding a confirmation gate.
|
|||||||
|---|---|
|
|---|---|
|
||||||
| Topic or requirements with no supporting facts | Research the factual baseline needed for the requested outcome |
|
| Topic or requirements with no supporting facts | Research the factual baseline needed for the requested outcome |
|
||||||
| Supplied files or chat content cover only part of the requested outcome | After conversion and reading, research only the identified externally verifiable gaps |
|
| Supplied files or chat content cover only part of the requested outcome | After conversion and reading, research only the identified externally verifiable gaps |
|
||||||
| Supplied material already supports the requested outcome | Skip this stage and continue Generate Step 1 |
|
| Supplied material already supports the requested outcome | Skip this stage and continue the active Generate profile's source preparation |
|
||||||
| User requires a closed corpus, source-only transformation, or no external enrichment | Skip this stage and keep planning within supplied material |
|
| User requires a closed corpus, source-only transformation, or no external enrichment | Skip this stage and keep planning within supplied material |
|
||||||
|
|
||||||
**Sufficiency test**: a gap exists when the active content owner would otherwise need to invent, omit, or leave unsupported an externally verifiable claim required by the user's requested outcome. File presence, source length, and a generic topic taxonomy do not decide sufficiency.
|
**Sufficiency test**: a gap exists when the active content owner would otherwise need to invent, omit, or leave unsupported an externally verifiable claim required by the user's requested outcome. File presence, source length, and a generic topic taxonomy do not decide sufficiency.
|
||||||
@@ -55,9 +60,24 @@ confirmation stage.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Execution Context
|
||||||
|
|
||||||
|
**Default — isolated research when available**: The main agent owns the sufficiency decision and gap brief. When the current AI editor supports and permits an isolated subagent with web/fetch access and write access to the declared outputs, dispatch exactly one research worker. Otherwise the main agent runs Steps 2–3 locally.
|
||||||
|
|
||||||
|
| Actor | Contract |
|
||||||
|
|---|---|
|
||||||
|
| Main agent | Supply the topic/outcome, baseline or relevant source paths, declared gaps, output language, two exact unused output paths, and this stage's absolute path as execution authority; use paths instead of pasting source bodies when possible |
|
||||||
|
| Research worker | Read the supplied stage file completely, then follow Steps 2–3 using the brief and declared source paths as its baseline; limit project writes to the two output artifacts; acquire no images and make no deck-planning or design decisions |
|
||||||
|
|
||||||
|
**Hard rule — isolate retrieval, not research**: Raw page content and fetch transcripts stay in the worker context. The 250-word limit applies only to its chat receipt: return `status`, exact artifact paths, covered/unresolved gap counts, external-fact count, and material conflicts. It does not cap or replace the two artifacts. After validation and import, the active content owner reads the complete imported research supplement and fact-provenance JSON into the main context before planning or direct SVG authoring; never use the receipt or validation summary as content.
|
||||||
|
|
||||||
|
**Validation**: Before import, the main agent verifies both exact files exist, the Markdown contains `## Research Brief` and `## Sources`, the JSON parses with schema `ppt-master.fact-provenance.v1` and unique sequential IDs, and the two files agree. Return an invalid pair to the research worker for owning-artifact repair; use main-context web research only when isolated execution is unavailable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Step 2: Gather factual sources
|
## Step 2: Gather factual sources
|
||||||
|
|
||||||
Use the web search and fetch tools supplied by the current IDE. If none are available, pause and ask the user for authoritative URLs covering the declared gaps, then fetch each with:
|
Use the web search and fetch tools available in the active research context. An isolated worker without them returns `blocked: web-tools-unavailable`. If no usable research context has search/fetch tools, the main agent pauses and asks the user for authoritative URLs covering the declared gaps, then fetches each with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ${SKILL_DIR}/scripts/source_to_md/web_to_md.py <URL>
|
python3 ${SKILL_DIR}/scripts/source_to_md/web_to_md.py <URL>
|
||||||
@@ -121,23 +141,25 @@ IDs are immutable within the file. Correct a claim under the same ID; never reus
|
|||||||
|
|
||||||
## Hand-off
|
## Hand-off
|
||||||
|
|
||||||
Import the research supplement and provenance alongside any user-supplied sources in Generate Step 2:
|
Import the research supplement and provenance alongside any user-supplied
|
||||||
|
sources through the active profile's source intake:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ${SKILL_DIR}/scripts/project_manager.py import-sources projects/<project_name> [<source_paths...>] projects/<research_slug>.md projects/<research_slug>.facts.json
|
python3 ${SKILL_DIR}/scripts/project_manager.py import-sources projects/<project_name> [<source_paths...>] projects/<research_slug>.md projects/<research_slug>.facts.json
|
||||||
```
|
```
|
||||||
|
|
||||||
The Research Brief remains evidence-facing context, not a locked presentation
|
The imported pair remains evidence-facing context, not a locked presentation
|
||||||
contract. Default Generate has Strategist read the imported material, confirm
|
contract. Default Generate has Strategist read both files completely before
|
||||||
the complete contract, and select the content, page roster, and image resource
|
confirmation and use them to select the content, page roster, and image resource
|
||||||
plan before Step 5 acquisition. Quick Generate has the current agent read the
|
plan. Quick Generate has the current agent read both completely before its
|
||||||
same facts, decide those routine details in active context, and continue to its
|
active-context content, design, and resource decisions.
|
||||||
resource-preparation phase.
|
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
## ✅ Topic Research Complete
|
## ✅ Topic Research Complete
|
||||||
|
- [x] Research execution: <isolated worker | main-context fallback>
|
||||||
- [x] Research supplement: `projects/<research_slug>.md` (N declared gaps covered)
|
- [x] Research supplement: `projects/<research_slug>.md` (N declared gaps covered)
|
||||||
- [x] Fact provenance: `projects/<research_slug>.facts.json` (N external facts)
|
- [x] Fact provenance: `projects/<research_slug>.facts.json` (N external facts)
|
||||||
|
- [x] Artifact contract validated: `## Research Brief`, `## Sources`, `ppt-master.fact-provenance.v1`, unique sequential IDs, and Markdown/JSON agreement
|
||||||
- [x] No images acquired inside this factual-research stage
|
- [x] No images acquired inside this factual-research stage
|
||||||
- [ ] **Next**: return to [`generate-pptx`](../generate-pptx.md) Step 1, then import all source artifacts in Step 2
|
- [ ] **Next**: Default returns to [`generate-pptx`](../generate-pptx.md) Step 2; Quick returns to [`quick-generate`](../profiles/quick-generate.md) §2. Import all source artifacts, then fully read the imported research pair before planning or direct SVG authoring
|
||||||
```
|
```
|
||||||
|
|||||||
+51
-12
@@ -6,23 +6,49 @@ description: Conditional quality-gate stage for chart coordinate verification.
|
|||||||
|
|
||||||
> Conditional Generate-PPTX quality stage. Run after a deck containing data charts has finished SVG generation, before post-processing & export. Catches the 10–50 px coordinate errors AI models routinely introduce when mapping data to pixel positions.
|
> Conditional Generate-PPTX quality stage. Run after a deck containing data charts has finished SVG generation, before post-processing & export. Catches the 10–50 px coordinate errors AI models routinely introduce when mapping data to pixel positions.
|
||||||
|
|
||||||
This stage is **context-independent**: it reads `design_spec.md` and the generated SVGs, then runs the calculator script — no upstream conversation context required. Safe to invoke in a fresh session.
|
In Default Generate this stage is **context-independent**: it reads
|
||||||
|
`design_spec.md` and the generated SVGs, then runs the calculator script. The
|
||||||
|
lockless Quick branch is deliberately context-dependent: run it in the same
|
||||||
|
active session from the page decisions just authored. If that context is lost,
|
||||||
|
restart Quick rather than inventing a page plan from finished files.
|
||||||
|
|
||||||
## When to Run
|
## When to Run
|
||||||
|
|
||||||
- The deck contains one or more data visualization charts where source values determine SVG geometry: bar lengths/heights, point positions, arc angles, polygon vertices, connector endpoints, bubble centers/radii, or flow widths/paths.
|
- The deck contains one or more data visualization charts where source values determine SVG geometry: bar lengths/heights, point positions, arc angles, polygon vertices, connector endpoints, bubble centers/radii, or flow widths/paths.
|
||||||
- SVGs are generated to `<project_path>/svg_output/` and `svg_quality_checker.py` has passed.
|
- SVGs are generated to `<project_path>/svg_output/`. Default enters from its declared quality-gate order; Quick runs this stage before its one lockless final checker.
|
||||||
- Post-processing (`finalize_svg.py`, `svg_to_pptx.py`) has **not yet** run.
|
- Post-processing (`finalize_svg.py`, `svg_to_pptx.py`) has **not yet** run.
|
||||||
|
|
||||||
The calculator has direct CLI models for simple bars, lines/scatter, pie/donut, radar, and grid layouts. Composite/derived charts are **not automatically out of scope**: if their geometry reduces to repeated direct calculations, include them as `decomposable-calc`; if the calculator has no layout model but the SVG geometry is still data-driven, include them as `manual-verify` so they are not silently skipped.
|
The calculator has direct CLI models for simple bars, lines/scatter, pie/donut, radar, and grid layouts. Composite/derived charts are **not automatically out of scope**: if their geometry reduces to repeated direct calculations, include them as `decomposable-calc`; if the calculator has no layout model but the SVG geometry is still data-driven, include them as `manual-verify` so they are not silently skipped.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Step 1: Build the page list from the design spec
|
## Step 1: Build the page list from the active profile authority
|
||||||
|
|
||||||
Read `<project_path>/design_spec.md` §IX Content Outline as the authoritative page roster and include every page whose `Visualization` explicitly declares SVG geometry driven by data values. Cross-check §VII when present to resolve a selected catalog key; absence from §VII means only that no reusable reference was selected. For legacy specs, a real §VII data-chart row may enumerate the page when its §IX block predates the explicit data-driven declaration. Classify each included page into exactly one mode:
|
| Active profile | Page-list authority |
|
||||||
|
|---|---|
|
||||||
|
| Default Generate | `design_spec.md §IX` plus the legacy §VII fallback below |
|
||||||
|
| Quick Generate | The still-active page decisions that produced the SVGs, cross-checked against every `chart-plot-area` marker; no Design Spec, lock, or substitute planning artifact is created |
|
||||||
|
|
||||||
Incidental microvisuals not promoted in the §IX page plan are not inferred into this list. If one requires coordinate verification, repair that page's §IX `Visualization` first so the Strategist-owned plan remains authoritative.
|
For Default, read `<project_path>/design_spec.md` §IX Content Outline as the
|
||||||
|
authoritative page roster and include every page whose `Visualization`
|
||||||
|
explicitly declares SVG geometry driven by data values. Cross-check §VII when
|
||||||
|
present to resolve a selected catalog key; absence from §VII means only that no
|
||||||
|
reusable reference was selected. For legacy specs, a real §VII data-chart row
|
||||||
|
may enumerate the page when its §IX block predates the explicit data-driven
|
||||||
|
declaration.
|
||||||
|
|
||||||
|
For Quick, enumerate every page the current agent just authored with
|
||||||
|
value-driven geometry, then search `svg_output/` once for `chart-plot-area` and
|
||||||
|
compare the marker pages one-for-one with that active list. Add a missing marker
|
||||||
|
before continuing; investigate an unexpected marker instead of silently adding
|
||||||
|
or dropping a page. Keep the list in active context only.
|
||||||
|
|
||||||
|
Classify each included page into exactly one mode:
|
||||||
|
|
||||||
|
Incidental microvisuals not promoted under the active profile authority are not
|
||||||
|
inferred into this list. Default repairs that page's §IX `Visualization` first;
|
||||||
|
Quick makes the promotion decision immediately in active context and updates
|
||||||
|
the SVG marker before verification.
|
||||||
|
|
||||||
| Mode | `charts_index.json` keys | Notes |
|
| Mode | `charts_index.json` keys | Notes |
|
||||||
|------|--------------------------|-------|
|
|------|--------------------------|-------|
|
||||||
@@ -50,9 +76,13 @@ P11 11_share_split.svg type=pie mode=direct-calc
|
|||||||
P15 15_pareto.svg type=pareto mode=decomposable-calc
|
P15 15_pareto.svg type=pareto mode=decomposable-calc
|
||||||
```
|
```
|
||||||
|
|
||||||
If §VII is absent, continue from §IX; this is the normal state when all chart pages use custom structures. Do NOT guess from SVG content when §IX declares no data-driven page—that reintroduces the silent-skip failure this stage was built to eliminate.
|
In Default, if §VII is absent, continue from §IX; this is the normal state when
|
||||||
|
all chart pages use custom structures. Do not guess from SVG content when §IX
|
||||||
|
declares no data-driven page. In Quick, do not treat marker search as a
|
||||||
|
replacement for the still-active authoring decisions; use it as the required
|
||||||
|
cross-check.
|
||||||
|
|
||||||
If the filtered list is empty, output `verify-charts: spec declares no data-driven chart geometry, nothing to verify` and stop.
|
If the filtered list is empty, output `verify-charts: active profile declares no data-driven chart geometry, nothing to verify` and stop.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -102,12 +132,17 @@ For each page in the Step 1 list:
|
|||||||
|
|
||||||
6. **Scale-aware comparison.** Compare calculator output against the SVG's existing coordinates. Before declaring a mismatch, verify that every calculator invocation used the same axis range, plot area, center/radius, start angle, or size scale that the SVG visually declares. For `calc bar`, the output header must show `Value scale: axis ticks (...)` when the SVG has explicit ticks; if it shows `auto (max*1.1)`, go back to step 4 and re-run with the correct `--value-range`. **Do NOT update the SVG with mismatched-scale output.** Only update SVG attributes when the scale is confirmed to match and coordinates genuinely differ. Update by hand (do NOT use regex / bulk replacement — coordinates are positional and easy to swap incorrectly).
|
6. **Scale-aware comparison.** Compare calculator output against the SVG's existing coordinates. Before declaring a mismatch, verify that every calculator invocation used the same axis range, plot area, center/radius, start angle, or size scale that the SVG visually declares. For `calc bar`, the output header must show `Value scale: axis ticks (...)` when the SVG has explicit ticks; if it shows `auto (max*1.1)`, go back to step 4 and re-run with the correct `--value-range`. **Do NOT update the SVG with mismatched-scale output.** Only update SVG attributes when the scale is confirmed to match and coordinates genuinely differ. Update by hand (do NOT use regex / bulk replacement — coordinates are positional and easy to swap incorrectly).
|
||||||
|
|
||||||
After updating any page, re-run the quality checker on the project to confirm nothing broke:
|
After updating any page, follow the active profile's checker order. Default
|
||||||
|
reruns its quality checker to confirm nothing broke:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 skills/ppt-master/scripts/svg_quality_checker.py <project_path>
|
python3 skills/ppt-master/scripts/svg_quality_checker.py <project_path>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Quick completes every chart comparison/repair first, then returns to
|
||||||
|
`quick-generate.md` §4 and runs its one lockless final checker. Do not insert a
|
||||||
|
checker call between Quick chart pages.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Stacked recipe
|
## Stacked recipe
|
||||||
@@ -211,7 +246,7 @@ python3 skills/ppt-master/scripts/svg_position_calculator.py calc line \
|
|||||||
**Bubble chart / quadrant bubble scatter** — partial calculator support:
|
**Bubble chart / quadrant bubble scatter** — partial calculator support:
|
||||||
|
|
||||||
1. Use `calc line` to verify bubble centers (`cx/cy`) from the X/Y values and axis ticks.
|
1. Use `calc line` to verify bubble centers (`cx/cy`) from the X/Y values and axis ticks.
|
||||||
2. Verify radius only if `design_spec.md`, `spec_lock.md`, or SVG comments declare a size scale such as `radius = sqrt(value) * k` or explicit min/max radius mapping.
|
2. Verify radius only if `design_spec.md`, `spec_lock.md`, the Quick active-context decision, or SVG comments declare a size scale such as `radius = sqrt(value) * k` or explicit min/max radius mapping.
|
||||||
3. If the size scale is missing, record `radius=manual (scale missing)` and inspect relative ordering by hand.
|
3. If the size scale is missing, record `radius=manual (scale missing)` and inspect relative ordering by hand.
|
||||||
|
|
||||||
**Progress bar / gauge / funnel — formula-verify** (no calc call needed):
|
**Progress bar / gauge / funnel — formula-verify** (no calc call needed):
|
||||||
@@ -231,7 +266,9 @@ python3 skills/ppt-master/scripts/svg_position_calculator.py calc line \
|
|||||||
|
|
||||||
## Step 3: Per-page receipt
|
## Step 3: Per-page receipt
|
||||||
|
|
||||||
Output one line per page from the Step 1 list. Receipt count MUST equal Step 1 list length — that is the gate-closing artifact.
|
Output one line per page from the Step 1 list. Receipt count MUST equal Step 1
|
||||||
|
list length — that is the gate-closing evidence. Quick does not persist these
|
||||||
|
lines as a generation plan or resume record.
|
||||||
|
|
||||||
```
|
```
|
||||||
verify-charts: 03_market_share.svg | type=bar | mode=direct-calc | scale=0-100 (from ticks) | calc=ran | svg=updated
|
verify-charts: 03_market_share.svg | type=bar | mode=direct-calc | scale=0-100 (from ticks) | calc=ran | svg=updated
|
||||||
@@ -258,5 +295,7 @@ verify-charts: 19_flow.svg | type=sankey | mode=manual-verify | link widths cons
|
|||||||
|
|
||||||
## After verification
|
## After verification
|
||||||
|
|
||||||
Continue with [`generate-pptx`](../generate-pptx.md) Step 7. That authority owns
|
Default continues with [`generate-pptx`](../generate-pptx.md) Step 7. Quick
|
||||||
the serial commands, gates, and success criteria for post-processing and export.
|
returns to [`quick-generate`](../profiles/quick-generate.md) §4 for its one final
|
||||||
|
checker and direct export. Those authorities own the remaining serial commands,
|
||||||
|
gates, and success criteria.
|
||||||
|
|||||||
+7
-4
@@ -6,7 +6,7 @@ description: Optional quality-gate stage for per-page rubric-based visual review
|
|||||||
|
|
||||||
> Optional Generate-PPTX quality stage. Goal: reduce human iteration by letting AI subagents visually self-check each rendered slide against a fixed rubric and apply atomic position/spacing fixes.
|
> Optional Generate-PPTX quality stage. Goal: reduce human iteration by letting AI subagents visually self-check each rendered slide against a fixed rubric and apply atomic position/spacing fixes.
|
||||||
>
|
>
|
||||||
> Reads `<project>/svg_output/<page>.svg` and a pre-rendered PNG of each slide, then either applies a fix or flags `needs_human`. **Never touches** brand decisions, layout structure, or other files.
|
> Reads `<project>/svg_output/<page>.svg` and a pre-rendered PNG of each slide, then either applies a fix or flags `needs_human`. When an installed/fused Style exists, it also reads that workspace's `Review Focus` as supplemental acceptance context. **Never touches** brand decisions, layout structure, or other files.
|
||||||
>
|
>
|
||||||
> This stage is **context-independent** — invokable in a fresh chat session with only `<project_path>` as input. No upstream conversation context required.
|
> This stage is **context-independent** — invokable in a fresh chat session with only `<project_path>` as input. No upstream conversation context required.
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ description: Optional quality-gate stage for per-page rubric-based visual review
|
|||||||
|
|
||||||
This is an **optional auxiliary loop**, opt-in only. The [`generate-pptx`](../generate-pptx.md) Step 1–7 pipeline does not invoke it; trigger only when the user explicitly asks for a visual re-pass on the generated SVGs before export.
|
This is an **optional auxiliary loop**, opt-in only. The [`generate-pptx`](../generate-pptx.md) Step 1–7 pipeline does not invoke it; trigger only when the user explicitly asks for a visual re-pass on the generated SVGs before export.
|
||||||
|
|
||||||
**Token cost**: each batch subagent re-reads the rubric + `design_spec.md` + `spec_lock.md` and processes K SVG+PNG pairs. For a 20-page deck with K=5, expect on the order of 100–150K additional input tokens on top of the main generation run.
|
**Token cost**: each batch subagent re-reads the rubric + `design_spec.md` + `spec_lock.md` + the short Style Review Focus when present, and processes K SVG+PNG pairs. For a 20-page deck with K=5, expect on the order of 100–150K additional input tokens on top of the main generation run.
|
||||||
|
|
||||||
## When to Run
|
## When to Run
|
||||||
|
|
||||||
@@ -73,7 +73,9 @@ If any page comes back with `"all_background": true` in the JSON summary, that p
|
|||||||
|
|
||||||
## Step 2 — Spawn the review team
|
## Step 2 — Spawn the review team
|
||||||
|
|
||||||
Create a team and dispatch one orchestrator agent. The orchestrator partitions the N pages into batches of ≤ K pages (default **K = 5**) and spawns one subagent per batch **in parallel** (single message, `ceil(N/K)` parallel `Agent` calls). Each batch subagent reads the fixed inputs (rubric + `design_spec.md` + `spec_lock.md`) **once**, then iterates over its assigned pages sequentially.
|
Create a team and dispatch one orchestrator agent. The orchestrator partitions the N pages into batches of ≤ K pages (default **K = 5**) and spawns one subagent per batch **in parallel** (single message, `ceil(N/K)` parallel `Agent` calls). Each batch subagent reads the fixed inputs (rubric + `design_spec.md` + `spec_lock.md` + conditional Style Review Focus) **once**, then iterates over its assigned pages sequentially.
|
||||||
|
|
||||||
|
Before dispatch, inspect `<project>/templates/design_spec.md`. If it declares `kind: style` or its fused provenance contains an active Style segment, read only `## VII. Review Focus` once and include those checks in every batch prompt. Otherwise pass no Style supplement. This lookup never triggers visual review; it runs only after the user has already activated this stage. The supplement cannot weaken the fixed rubric or widen edit permissions.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
TeamCreate(team_name="visual-review-<project>", agent_type="orchestrator")
|
TeamCreate(team_name="visual-review-<project>", agent_type="orchestrator")
|
||||||
@@ -92,8 +94,9 @@ The orchestrator prompt must be self-contained and is the **single** place where
|
|||||||
- Batch size `K` (default 5; raise to 10 for token-sensitive runs on large decks, lower to 3 for high-fidelity short decks — see rubric §6.1)
|
- Batch size `K` (default 5; raise to 10 for token-sensitive runs on large decks, lower to 3 for high-fidelity short decks — see rubric §6.1)
|
||||||
- Iteration budget per page (default 1; 2 only for high-stakes / final-cut runs — see [Appendix: Iteration loop](#appendix-iteration-loop-opt-in))
|
- Iteration budget per page (default 1; 2 only for high-stakes / final-cut runs — see [Appendix: Iteration loop](#appendix-iteration-loop-opt-in))
|
||||||
- Path to the rubric: `skills/ppt-master/references/visual-review.md`
|
- Path to the rubric: `skills/ppt-master/references/visual-review.md`
|
||||||
|
- Style Review Focus excerpt, only when the conditional lookup above found one; preserve its wording and source path
|
||||||
- Dispatch contract reference: rubric [§6](../../references/visual-review.md#6-dispatch--messaging-contract) (batched parallel spawn, self-contained prompts, mandatory `SendMessage` on idle, anonymous-name tolerance)
|
- Dispatch contract reference: rubric [§6](../../references/visual-review.md#6-dispatch--messaging-contract) (batched parallel spawn, self-contained prompts, mandatory `SendMessage` on idle, anonymous-name tolerance)
|
||||||
- Subagent forbid list: do not edit any other page, `design_spec.md`, `spec_lock.md`, `animations.json`, `image_prompts.json`, or `images/`
|
- Subagent forbid list: do not edit any other page, `design_spec.md`, `spec_lock.md`, `templates/design_spec.md`, `animations.json`, `image_prompts.json`, or `images/`
|
||||||
|
|
||||||
**Host compatibility**: `TeamCreate` and `SendMessage` are Claude-Code-specific multi-agent primitives. On hosts without those primitives (Cursor, VS Code + Copilot, Codebuddy, etc.) the main agent processes batches sequentially — same partitioning, same per-batch prompts, no parallel dispatch. Token savings from shared fixed inputs still apply; wall-clock time grows roughly N/K-fold.
|
**Host compatibility**: `TeamCreate` and `SendMessage` are Claude-Code-specific multi-agent primitives. On hosts without those primitives (Cursor, VS Code + Copilot, Codebuddy, etc.) the main agent processes batches sequentially — same partitioning, same per-batch prompts, no parallel dispatch. Token savings from shared fixed inputs still apply; wall-clock time grows roughly N/K-fold.
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
"sourceId": "shadcn",
|
"sourceId": "shadcn",
|
||||||
"repo": "https://github.com/shadcn-ui/ui.git",
|
"repo": "https://github.com/shadcn-ui/ui.git",
|
||||||
"ref": "main",
|
"ref": "main",
|
||||||
"commit": "f0cad1d9f836b56b3705f09c76142861b09ec71c",
|
"commit": "9846e22ce52c723554742860a0dbd3e5cf19b573",
|
||||||
"adapter": "claude-skill",
|
"adapter": "claude-skill",
|
||||||
"sourcePath": "skills/shadcn",
|
"sourcePath": "skills/shadcn",
|
||||||
"syncedAt": "2026-08-03T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ui-ux-pro-max",
|
"name": "ui-ux-pro-max",
|
||||||
"version": "2.11.0",
|
"version": "2.13.0",
|
||||||
"description": "面向界面设计、视觉风格、配色、字体、可访问性和前端技术栈的 UI/UX 设计智能库。",
|
"description": "面向界面设计、视觉风格、配色、字体、可访问性和前端技术栈的 UI/UX 设计智能库。",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "NextLevelBuilder"
|
"name": "NextLevelBuilder"
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
"sourceId": "ui-ux-pro-max",
|
"sourceId": "ui-ux-pro-max",
|
||||||
"repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git",
|
"repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git",
|
||||||
"ref": "main",
|
"ref": "main",
|
||||||
"commit": "4d140cf8ff6842de13213c7214eff3810371beb2",
|
"commit": "abb7f2fd5a083fa1ff55c326a963ff0d95c33f99",
|
||||||
"adapter": "claude-skill",
|
"adapter": "claude-skill",
|
||||||
"sourcePath": ".claude/skills/ui-ux-pro-max",
|
"sourcePath": ".claude/skills/ui-ux-pro-max",
|
||||||
"syncedAt": "2026-08-03T16:00:00Z"
|
"syncedAt": "2026-08-06T16:00:02Z"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user