slides-grab 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-ko.md +8 -6
- package/README.md +8 -6
- package/bin/ppt-agent.js +119 -6
- package/package.json +6 -2
- package/runtimes/claude-code/agents/design-critic-agent.md +23 -0
- package/runtimes/codex/agents/slides-grab-design-critic.md +22 -0
- package/scripts/build-viewer.js +67 -5
- package/scripts/design-gate.js +241 -0
- package/scripts/html2png.js +246 -0
- package/scripts/install-runtime.js +216 -0
- package/skills/slides-grab/SKILL.md +19 -14
- package/skills/slides-grab/references/presentation-workflow-reference.md +8 -6
- package/skills/slides-grab-card-news/SKILL.md +1 -1
- package/skills/slides-grab-design/SKILL.md +19 -11
- package/skills/slides-grab-design/references/design-gate.md +349 -0
- package/skills/slides-grab-design/references/design-rules.md +12 -3
- package/skills/slides-grab-design/references/design-system-full.md +4 -4
- package/skills/slides-grab-design/references/detailed-design-rules.md +9 -0
- package/skills/slides-grab-export/SKILL.md +10 -7
- package/skills/slides-grab-export/references/export-rules.md +3 -0
- package/skills/slides-grab-export/references/pptx-skill-reference.md +7 -42
- package/skills/slides-grab-plan/SKILL.md +6 -3
- package/skills/slides-grab-plan/references/plan-workflow-reference.md +14 -14
- package/src/design-diversity-data.js +6932 -0
- package/src/design-gate-report.js +244 -0
- package/src/design-gate-state.js +294 -0
- package/src/design-styles.js +82 -2
- package/src/editor/codex-edit.js +26 -1
- package/src/editor/editor.html +1 -1
- package/src/editor/js/model-registry.js +1 -1
- package/src/validation/core.js +76 -0
- package/templates/design-styles/README.md +2 -1
- package/templates/design-styles/preview.html +1088 -6
package/README-ko.md
CHANGED
|
@@ -54,11 +54,13 @@ CLI와 공유 에이전트 스킬만 사용하려면 npm 패키지를 설치하
|
|
|
54
54
|
```bash
|
|
55
55
|
npm install slides-grab
|
|
56
56
|
npx playwright install chromium
|
|
57
|
-
npx
|
|
57
|
+
npx slides-grab install-skills --target all --scope user
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
이 방법은 일반적인 사용에 충분합니다. slides-grab 자체를 수정하거나 기여하려는 경우에만 저장소를 클론하세요.
|
|
61
61
|
|
|
62
|
+
이 명령은 같은 공유 Agent Skills와 얇은 런타임 adapter를 Codex 런타임과 Claude Code 런타임 위치에 모두 설치합니다. 패키지된 워크플로우는 번들된 `SKILL.md`, `references/`, `slides-grab` CLI만 사용합니다. `slides-grab pdf`, `slides-grab convert`, `slides-grab figma` export는 현재 슬라이드 파일에 대한 최신 `slides-grab design-gate` `proceed` 기록이 없으면 차단됩니다.
|
|
63
|
+
|
|
62
64
|
## 왜 slides-grab인가요?
|
|
63
65
|
|
|
64
66
|
많은 AI 도구가 슬라이드 HTML을 생성하지만, 사용자가 화면에서 **수정하고 싶은 부분을 직접 가리키고** 그 자리에서 반복 편집할 수 있게 해 주는 도구는 드뭅니다. slides-grab은 다음 흐름을 제공합니다.
|
|
@@ -72,7 +74,7 @@ npx skills add ./node_modules/slides-grab -g -a codex -a claude-code --yes --cop
|
|
|
72
74
|
|
|
73
75
|
워크플로 명령은 `--slides-dir <path>`를 지원하며 기본값은 `slides`입니다.
|
|
74
76
|
|
|
75
|
-
새 클론에서는 `--help`, `list-templates`, `list-styles`, `preview-styles` 같은 탐색 명령은 덱 없이도 동작합니다. `edit`, `build-viewer`, `validate`, `
|
|
77
|
+
새 클론에서는 `--help`, `list-templates`, `list-styles`, `preview-styles` 같은 탐색 명령은 덱 없이도 동작합니다. `edit`, `build-viewer`, `validate`, `png`, `design-gate`는 `slide-*.html` 파일이 들어 있는 슬라이드 작업공간이 필요하고, `convert`, `pdf`, `figma`는 추가로 최신 `Proceed` design gate가 필요합니다.
|
|
76
78
|
|
|
77
79
|
```bash
|
|
78
80
|
slides-grab edit # 시각적 슬라이드 편집기 실행
|
|
@@ -90,13 +92,13 @@ slides-grab image --prompt "..." # 로컬 슬라이드 이미지 생성
|
|
|
90
92
|
slides-grab fetch-video --url <youtube-url> --slides-dir decks/my-deck # yt-dlp로 동영상 에셋 다운로드
|
|
91
93
|
slides-grab tldraw # .tldr 다이어그램을 슬라이드 크기의 로컬 SVG로 렌더링
|
|
92
94
|
slides-grab list-templates # 사용 가능한 슬라이드 템플릿 표시
|
|
93
|
-
slides-grab list-styles #
|
|
94
|
-
slides-grab preview-styles #
|
|
95
|
+
slides-grab list-styles # 기본으로 선택 가능한 92개 디자인 스타일 표시 (3개 소스-앨리어스를 포함해 95개 해석 가능; --all로 전체 표시)
|
|
96
|
+
slides-grab preview-styles # 95개 스타일 미리보기 갤러리를 브라우저에서 열기
|
|
95
97
|
```
|
|
96
98
|
|
|
97
99
|
## 디자인 스타일 모음
|
|
98
100
|
|
|
99
|
-
slides-grab은 [corazzon/pptx-design-styles](https://github.com/corazzon/pptx-design-styles)에서 파생된 30개
|
|
101
|
+
slides-grab은 [corazzon/pptx-design-styles](https://github.com/corazzon/pptx-design-styles)에서 파생된 30개 스타일, slides-grab 고유 스타일 5개, [epoko77-ai/design-diversity](https://github.com/epoko77-ai/design-diversity)에서 파생된 PPT 팩 60개를 포함해 총 95개 디자인 스타일을 제공합니다. 60개 design-diversity PPT 팩은 분류됩니다 — 직접 중복은 빌트인에 앨리어스 처리(기본 숨김)되고, 유사 중복은 `relatedStyleIds`로 연결되며, 완전히 새 팩은 추가됩니다. 따라서 92개만 기본으로 선택 가능하며 95개 모두 해석 가능합니다(`list-styles --all`로 앨리어스 확인). 에이전트에게 특정 스타일을 요청하거나 완전히 커스텀 디자인을 요청할 수 있습니다.
|
|
100
102
|
|
|
101
103
|
```bash
|
|
102
104
|
slides-grab list-styles
|
|
@@ -229,7 +231,7 @@ npm install slides-grab
|
|
|
229
231
|
Vercel Agent Skills로 공유 에이전트 스킬을 설치하려면 다음을 실행하세요.
|
|
230
232
|
|
|
231
233
|
```bash
|
|
232
|
-
npx skills add ./node_modules/slides-grab -g -a codex -a claude-code --yes --copy
|
|
234
|
+
npx skills add ./node_modules/slides-grab -g -a codex -a claude-code --yes --copy --full-depth
|
|
233
235
|
```
|
|
234
236
|
|
|
235
237
|
이 npm 설치 경로는 일반적인 사용에 충분합니다. slides-grab 자체를 수정하거나 기여하려는 경우에만 저장소를 클론하세요.
|
package/README.md
CHANGED
|
@@ -64,9 +64,11 @@ npm ci && npx playwright install chromium
|
|
|
64
64
|
```bash
|
|
65
65
|
npm install slides-grab
|
|
66
66
|
npx playwright install chromium
|
|
67
|
-
npx
|
|
67
|
+
npx slides-grab install-skills --target all --scope user
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
This installs the same shared Agent Skills plus lightweight runtime adapters into Codex and Claude Code locations. The packaged workflow relies on bundled `SKILL.md` files, bundled `references/`, and the `slides-grab` CLI. Exports through `slides-grab pdf`, `slides-grab convert`, and `slides-grab figma` are blocked until `slides-grab design-gate` records a fresh `proceed` receipt for the current slide files.
|
|
71
|
+
|
|
70
72
|
## Why This Project?
|
|
71
73
|
|
|
72
74
|
There are many AI tools that generate slide HTML. Almost none let you **visually point at what you want changed** and iterate in-place. slides-grab fills that gap:
|
|
@@ -80,7 +82,7 @@ There are many AI tools that generate slide HTML. Almost none let you **visually
|
|
|
80
82
|
|
|
81
83
|
Workflow commands support `--slides-dir <path>` (default: `slides`).
|
|
82
84
|
|
|
83
|
-
On a fresh clone, the discovery commands (`--help`, `list-templates`, `list-styles`, and `preview-styles`) work without a deck. `edit`, `build-viewer`, `validate`, `
|
|
85
|
+
On a fresh clone, the discovery commands (`--help`, `list-templates`, `list-styles`, and `preview-styles`) work without a deck. `edit`, `build-viewer`, `validate`, `png`, and `design-gate` require an existing slides workspace containing `slide-*.html`; `convert`, `pdf`, and `figma` additionally require a fresh `Proceed` design gate.
|
|
84
86
|
|
|
85
87
|
```bash
|
|
86
88
|
slides-grab edit # Launch visual slide editor
|
|
@@ -98,13 +100,13 @@ slides-grab image --prompt "..." # Generate a local slide image with god-tibo
|
|
|
98
100
|
slides-grab fetch-video --url <youtube-url> --slides-dir decks/my-deck # Download a local video asset with yt-dlp
|
|
99
101
|
slides-grab tldraw # Render a .tldr diagram into a slide-sized local SVG asset
|
|
100
102
|
slides-grab list-templates # Show available slide templates
|
|
101
|
-
slides-grab list-styles # Show
|
|
102
|
-
slides-grab preview-styles # Open the
|
|
103
|
+
slides-grab list-styles # Show 92 selectable design styles by default (95 resolvable incl. 3 source-aliases; --all shows all)
|
|
104
|
+
slides-grab preview-styles # Open the 95-style visual gallery in browser
|
|
103
105
|
```
|
|
104
106
|
|
|
105
107
|
## Design Style Collections
|
|
106
108
|
|
|
107
|
-
slides-grab bundles
|
|
109
|
+
slides-grab bundles 95 design styles: 30 derived from [corazzon/pptx-design-styles](https://github.com/corazzon/pptx-design-styles), 5 slides-grab originals, and 60 PPT packs derived from [epoko77-ai/design-diversity](https://github.com/epoko77-ai/design-diversity). The 60 design-diversity PPT packs are classified — direct duplicates are aliased to builtins (hidden by default), near-duplicates are linked via `relatedStyleIds`, and net-new packs are added — so 92 are selectable by default while all 95 remain resolvable (use `list-styles --all` to see aliases). Agents can also create fully custom designs beyond the bundled collection.
|
|
108
110
|
|
|
109
111
|
```bash
|
|
110
112
|
slides-grab list-styles # Browse the catalog
|
|
@@ -237,7 +239,7 @@ npm install slides-grab
|
|
|
237
239
|
Install shared agent skills with Vercel Agent Skills:
|
|
238
240
|
|
|
239
241
|
```bash
|
|
240
|
-
npx skills add ./node_modules/slides-grab -g -a codex -a claude-code --yes --copy
|
|
242
|
+
npx skills add ./node_modules/slides-grab -g -a codex -a claude-code --yes --copy --full-depth
|
|
241
243
|
```
|
|
242
244
|
|
|
243
245
|
This npm-install path is enough for normal usage. Clone the repo only when you want to modify or contribute to `slides-grab` itself.
|
package/bin/ppt-agent.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getFigmaImportCaveats,
|
|
10
10
|
getFigmaManualImportInstructions,
|
|
11
11
|
} from '../src/figma.js';
|
|
12
|
+
import { assertDesignGateReady } from '../src/design-gate-state.js';
|
|
12
13
|
|
|
13
14
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
15
|
const packageRoot = resolve(__dirname, '..');
|
|
@@ -64,6 +65,17 @@ async function runCommand(relativePath, args = []) {
|
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
async function ensureDesignGateForExport(slidesDir, label) {
|
|
69
|
+
try {
|
|
70
|
+
await assertDesignGateReady(resolve(process.cwd(), slidesDir), { label });
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error(`[slides-grab] ${error.message}`);
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
67
79
|
function collectRepeatedOption(value, previous = []) {
|
|
68
80
|
return [...previous, value];
|
|
69
81
|
}
|
|
@@ -109,6 +121,66 @@ program
|
|
|
109
121
|
await runCommand('scripts/validate-slides.js', args);
|
|
110
122
|
});
|
|
111
123
|
|
|
124
|
+
program
|
|
125
|
+
.command('design-gate')
|
|
126
|
+
.description('Record the required visual QA gate evidence before export')
|
|
127
|
+
.option('--slides-dir <path>', 'Slide directory', 'slides')
|
|
128
|
+
.option('--slide-mode <mode>', 'Slide mode: presentation or card-news', 'presentation')
|
|
129
|
+
.option('--resolution <preset>', 'PNG evidence resolution preset: 720p, 1080p, 1440p, 2160p, or 4k', '2160p')
|
|
130
|
+
.requiredOption('--verdict <verdict>', 'Gate verdict: proceed, revise, or rethink')
|
|
131
|
+
.requiredOption('--pass-a-report <path>', 'Pass A review report file')
|
|
132
|
+
.requiredOption('--pass-b-report <path>', 'Pass B review report file')
|
|
133
|
+
.option('--output-dir <path>', 'PNG evidence directory (default: <slides-dir>/.slides-grab/gate-preview)')
|
|
134
|
+
.action(async (options = {}) => {
|
|
135
|
+
const args = [
|
|
136
|
+
'--slides-dir',
|
|
137
|
+
options.slidesDir,
|
|
138
|
+
'--slide-mode',
|
|
139
|
+
options.slideMode,
|
|
140
|
+
'--resolution',
|
|
141
|
+
options.resolution,
|
|
142
|
+
'--verdict',
|
|
143
|
+
options.verdict,
|
|
144
|
+
'--pass-a-report',
|
|
145
|
+
options.passAReport,
|
|
146
|
+
'--pass-b-report',
|
|
147
|
+
options.passBReport,
|
|
148
|
+
];
|
|
149
|
+
if (options.outputDir) {
|
|
150
|
+
args.push('--output-dir', String(options.outputDir));
|
|
151
|
+
}
|
|
152
|
+
await runCommand('scripts/design-gate.js', args);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
function registerInstallSkillsCommand(commandName) {
|
|
156
|
+
program
|
|
157
|
+
.command(commandName)
|
|
158
|
+
.description('Install slides-grab skills and lightweight runtime adapters for Codex and Claude Code')
|
|
159
|
+
.option('--target <target>', 'Runtime target: all, codex, or claude-code', 'all')
|
|
160
|
+
.option('--runtime <target>', 'Alias for --target')
|
|
161
|
+
.option('--scope <scope>', 'Install scope: project or user', 'project')
|
|
162
|
+
.option('--project-dir <path>', 'Project directory for project scope')
|
|
163
|
+
.option('--target-root <path>', 'Root directory for user-style installs')
|
|
164
|
+
.option('--dry-run', 'Print planned writes without copying')
|
|
165
|
+
.option('--json', 'Print JSON result')
|
|
166
|
+
.action(async (options = {}) => {
|
|
167
|
+
const args = [
|
|
168
|
+
'--target',
|
|
169
|
+
String(options.runtime || options.target),
|
|
170
|
+
'--scope',
|
|
171
|
+
String(options.scope),
|
|
172
|
+
];
|
|
173
|
+
if (options.projectDir) args.push('--project-dir', String(options.projectDir));
|
|
174
|
+
if (options.targetRoot) args.push('--target-root', String(options.targetRoot));
|
|
175
|
+
if (options.dryRun) args.push('--dry-run');
|
|
176
|
+
if (options.json) args.push('--json');
|
|
177
|
+
await runCommand('scripts/install-runtime.js', args);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
registerInstallSkillsCommand('install-skills');
|
|
182
|
+
registerInstallSkillsCommand('install-runtime');
|
|
183
|
+
|
|
112
184
|
program
|
|
113
185
|
.command('convert')
|
|
114
186
|
.description('Convert slide HTML files to experimental / unstable PPTX')
|
|
@@ -117,6 +189,7 @@ program
|
|
|
117
189
|
.option('--mode <mode>', 'Slide mode: presentation or card-news', 'presentation')
|
|
118
190
|
.option('--resolution <preset>', 'Raster size preset: 720p, 1080p, 1440p, 2160p, or 4k (default: 2160p)')
|
|
119
191
|
.action(async (options = {}) => {
|
|
192
|
+
if (!(await ensureDesignGateForExport(options.slidesDir, 'PPTX export'))) return;
|
|
120
193
|
const args = ['--slides-dir', options.slidesDir, '--mode', options.mode];
|
|
121
194
|
if (options.output) {
|
|
122
195
|
args.push('--output', String(options.output));
|
|
@@ -136,6 +209,7 @@ program
|
|
|
136
209
|
.option('--slide-mode <mode>', 'Slide mode: presentation or card-news', 'presentation')
|
|
137
210
|
.option('--resolution <preset>', 'Capture raster size preset: 720p, 1080p, 1440p, 2160p, or 4k (default: 2160p in capture mode)')
|
|
138
211
|
.action(async (options = {}) => {
|
|
212
|
+
if (!(await ensureDesignGateForExport(options.slidesDir, 'PDF export'))) return;
|
|
139
213
|
const args = ['--slides-dir', options.slidesDir];
|
|
140
214
|
if (options.output) {
|
|
141
215
|
args.push('--output', String(options.output));
|
|
@@ -196,6 +270,7 @@ program
|
|
|
196
270
|
.option('--mode <mode>', 'Slide mode: presentation or card-news', 'presentation')
|
|
197
271
|
.addHelpText('after', figmaHelpText)
|
|
198
272
|
.action(async (options = {}) => {
|
|
273
|
+
if (!(await ensureDesignGateForExport(options.slidesDir, 'Figma export'))) return;
|
|
199
274
|
const args = ['--slides-dir', options.slidesDir, '--mode', options.mode];
|
|
200
275
|
if (options.output) {
|
|
201
276
|
args.push('--output', String(options.output));
|
|
@@ -295,10 +370,12 @@ program
|
|
|
295
370
|
program
|
|
296
371
|
.command('list-styles')
|
|
297
372
|
.description('List bundled design styles agents and users can reference during slide generation')
|
|
298
|
-
.
|
|
373
|
+
.option('--all', 'Include source-alias styles that resolve to a builtin (hidden by default)')
|
|
374
|
+
.action(async (options = {}) => {
|
|
299
375
|
try {
|
|
300
|
-
const { listDesignStyles } = await import('../src/design-styles.js');
|
|
301
|
-
const
|
|
376
|
+
const { listDesignStyles, listSelectableDesignStyles } = await import('../src/design-styles.js');
|
|
377
|
+
const showAll = Boolean(options.all);
|
|
378
|
+
const styles = showAll ? listDesignStyles() : listSelectableDesignStyles();
|
|
302
379
|
|
|
303
380
|
if (styles.length === 0) {
|
|
304
381
|
console.log('No bundled design styles found.');
|
|
@@ -307,11 +384,33 @@ program
|
|
|
307
384
|
|
|
308
385
|
console.log('Available design styles:\n');
|
|
309
386
|
for (const style of styles) {
|
|
310
|
-
|
|
387
|
+
let tag;
|
|
388
|
+
if (style.classification === 'builtin') {
|
|
389
|
+
tag = '[builtin]';
|
|
390
|
+
} else if (style.classification === 'source-variant') {
|
|
391
|
+
tag = '[variant]';
|
|
392
|
+
} else if (style.classification === 'source-alias') {
|
|
393
|
+
tag = `[alias -> ${style.aliasOf}]`;
|
|
394
|
+
} else {
|
|
395
|
+
tag = '[new]';
|
|
396
|
+
}
|
|
397
|
+
console.log(` ${style.id.padEnd(38)} ${tag} ${style.title}`);
|
|
311
398
|
console.log(` ${style.mood} · ${style.bestFor}`);
|
|
399
|
+
if (style.classification === 'source-variant' && Array.isArray(style.relatedStyleIds) && style.relatedStyleIds.length) {
|
|
400
|
+
console.log(` related: ${style.relatedStyleIds.join(', ')}`);
|
|
401
|
+
}
|
|
312
402
|
}
|
|
313
403
|
|
|
314
|
-
|
|
404
|
+
const totalResolvable = listDesignStyles().length;
|
|
405
|
+
if (!showAll) {
|
|
406
|
+
const hiddenCount = totalResolvable - styles.length;
|
|
407
|
+
console.log(`\nTotal: ${styles.length} selectable styles`);
|
|
408
|
+
console.log(`${hiddenCount} source-alias slug(s) hidden (resolve to a builtin); use --all to show all ${totalResolvable} resolvable styles`);
|
|
409
|
+
} else {
|
|
410
|
+
const selectableCount = listSelectableDesignStyles().length;
|
|
411
|
+
const aliasCount = totalResolvable - selectableCount;
|
|
412
|
+
console.log(`\nTotal: ${styles.length} resolvable styles (${selectableCount} selectable, ${aliasCount} aliases)`);
|
|
413
|
+
}
|
|
315
414
|
console.log('Preview: slides-grab preview-styles [--style <id>]');
|
|
316
415
|
} catch (error) {
|
|
317
416
|
reportCliError(error);
|
|
@@ -320,7 +419,7 @@ program
|
|
|
320
419
|
|
|
321
420
|
program
|
|
322
421
|
.command('preview-styles')
|
|
323
|
-
.description('Print the path to the bundled
|
|
422
|
+
.description('Print the path to the bundled 95-style visual preview gallery')
|
|
324
423
|
.action(async () => {
|
|
325
424
|
try {
|
|
326
425
|
const { getPreviewHtmlPath } = await import('../src/design-styles.js');
|
|
@@ -418,6 +517,12 @@ program
|
|
|
418
517
|
console.log(`# Bundled style: ${style.title} (${style.id})`);
|
|
419
518
|
console.log(`Mood: ${style.mood}`);
|
|
420
519
|
console.log(`Best for: ${style.bestFor}`);
|
|
520
|
+
if (style.source?.repo) console.log(`Source: ${style.source.repo}`);
|
|
521
|
+
if (style.source?.url) console.log(`Source URL: ${style.source.url}`);
|
|
522
|
+
if (style.classification) console.log(`Classification: ${style.classification}`);
|
|
523
|
+
if (style.aliasOf) console.log(`Alias of: ${style.aliasOf}`);
|
|
524
|
+
if (Array.isArray(style.aliases) && style.aliases.length) console.log(`Aliases: ${style.aliases.join(', ')}`);
|
|
525
|
+
if (Array.isArray(style.relatedStyleIds) && style.relatedStyleIds.length) console.log(`Related styles: ${style.relatedStyleIds.join(', ')}`);
|
|
421
526
|
if (Array.isArray(style.background)) {
|
|
422
527
|
console.log('\n## Background');
|
|
423
528
|
for (const b of style.background) console.log(`- ${b}`);
|
|
@@ -434,6 +539,14 @@ program
|
|
|
434
539
|
console.log('\n## Layout');
|
|
435
540
|
for (const l of style.layout) console.log(`- ${l}`);
|
|
436
541
|
}
|
|
542
|
+
if (Array.isArray(style.signature)) {
|
|
543
|
+
console.log('\n## Signature');
|
|
544
|
+
for (const s of style.signature) console.log(`- ${s}`);
|
|
545
|
+
}
|
|
546
|
+
if (Array.isArray(style.avoid)) {
|
|
547
|
+
console.log('\n## Avoid');
|
|
548
|
+
for (const a of style.avoid) console.log(`- ${a}`);
|
|
549
|
+
}
|
|
437
550
|
}
|
|
438
551
|
} catch (error) {
|
|
439
552
|
reportCliError(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slides-grab",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Agent-first presentation framework — plan, design, and visually edit HTML slides with Claude Code or Codex, then export to PDF or experimental/unstable PPTX/Figma formats",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vkehfdl1",
|
|
@@ -39,10 +39,14 @@
|
|
|
39
39
|
"scripts/generate-image.js",
|
|
40
40
|
"src/god-tibo-imagen.js",
|
|
41
41
|
"scripts/figma-export.js",
|
|
42
|
+
"scripts/design-gate.js",
|
|
42
43
|
"scripts/html2pdf.js",
|
|
44
|
+
"scripts/html2png.js",
|
|
43
45
|
"scripts/html2pptx.js",
|
|
46
|
+
"scripts/install-runtime.js",
|
|
44
47
|
"scripts/render-tldraw.js",
|
|
45
48
|
"scripts/validate-slides.js",
|
|
49
|
+
"runtimes/",
|
|
46
50
|
"skills/",
|
|
47
51
|
"src/",
|
|
48
52
|
"templates/",
|
|
@@ -56,7 +60,7 @@
|
|
|
56
60
|
"build:showcase": "node showcase/scripts/build-manifest.js",
|
|
57
61
|
"validate": "node scripts/validate-slides.js",
|
|
58
62
|
"convert": "node convert.cjs",
|
|
59
|
-
"test": "node --test --test-concurrency=1 tests/docs/readme-ko.test.js tests/design/design-styles.test.js tests/design/design-md-parser.test.js tests/editor/editor-codex-edit.test.js tests/editor/edit-subprocess-abort.test.js tests/editor/editor-server.test.js tests/editor/editor-server-orphan-prevention.test.js tests/editor/editor-model-dispatch.test.js tests/god-tibo/god-tibo.test.js tests/nano-banana/nano-banana.test.js tests/pdf/html2pdf.test.js tests/pdf/html2pdf.e2e.test.js tests/figma/figma-export.test.js tests/image-contract/image-contract.test.js tests/tldraw/render-tldraw.test.js tests/validation/validate-slides.test.js tests/viewer/build-viewer.test.js tests/skills/installable-skills.test.js tests/video/download-video.test.js",
|
|
63
|
+
"test": "node --test --test-concurrency=1 tests/docs/readme-ko.test.js tests/design/design-styles.test.js tests/design/design-md-parser.test.js tests/design-gate/design-gate.test.js tests/runtime/install-runtime.test.js tests/editor/editor-codex-edit.test.js tests/editor/edit-subprocess-abort.test.js tests/editor/editor-server.test.js tests/editor/editor-server-orphan-prevention.test.js tests/editor/editor-model-dispatch.test.js tests/god-tibo/god-tibo.test.js tests/nano-banana/nano-banana.test.js tests/pdf/html2pdf.test.js tests/pdf/html2pdf.e2e.test.js tests/figma/figma-export.test.js tests/image-contract/image-contract.test.js tests/tldraw/render-tldraw.test.js tests/validation/validate-slides.test.js tests/viewer/build-viewer.test.js tests/skills/installable-skills.test.js tests/video/download-video.test.js",
|
|
60
64
|
"test:e2e": "node --test tests/editor/editor-ui.e2e.test.js tests/editor/editor-concurrency.e2e.test.js"
|
|
61
65
|
},
|
|
62
66
|
"dependencies": {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-critic-agent
|
|
3
|
+
description: Run the slides-grab design gate before export.
|
|
4
|
+
tools: Read, Grep, Glob, Bash, Task
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Use the canonical gate in `skills/slides-grab-design/references/design-gate.md`.
|
|
8
|
+
|
|
9
|
+
Required workflow:
|
|
10
|
+
|
|
11
|
+
1. Run `slides-grab validate --slides-dir <slides-dir>`.
|
|
12
|
+
2. Render evidence with `slides-grab png --slides-dir <slides-dir> --output-dir <slides-dir>/.slides-grab/gate-preview`.
|
|
13
|
+
3. Produce two read-only review reports:
|
|
14
|
+
- Pass A: System Contract / Constraint Integrity.
|
|
15
|
+
- Pass B: Audience Impact / Expressive Readability.
|
|
16
|
+
Each `Proceed` report must use the CLI-enforced structure from `skills/slides-grab-design/references/design-gate.md`: role title, `VERDICT: PASS`, confidence, rendered PNG evidence filenames, current `slide-*.html: <sha256>` fingerprints, `Unresolved Critical: 0`, `Blocking findings: None`, findings table, and all required checks marked `PASS`.
|
|
17
|
+
4. If both passes conclude Proceed, record the gate with:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
slides-grab design-gate --slides-dir <slides-dir> --verdict proceed --pass-a-report <pass-a.md> --pass-b-report <pass-b.md>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
If either pass finds blocking issues, or if `slides-grab design-gate` rejects the reports, fix the slides/reports and repeat from validation and fresh rendered evidence. Do not run `slides-grab pdf`, `slides-grab convert`, or `slides-grab figma` until the CLI gate records `proceed`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: slides-grab-design-critic
|
|
3
|
+
description: Run the slides-grab design gate before export.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use the canonical gate in `skills/slides-grab-design/references/design-gate.md`.
|
|
7
|
+
|
|
8
|
+
Required workflow:
|
|
9
|
+
|
|
10
|
+
1. Run `slides-grab validate --slides-dir <slides-dir>`.
|
|
11
|
+
2. Render evidence with `slides-grab png --slides-dir <slides-dir> --output-dir <slides-dir>/.slides-grab/gate-preview`.
|
|
12
|
+
3. Produce two read-only review reports:
|
|
13
|
+
- Pass A: System Contract / Constraint Integrity.
|
|
14
|
+
- Pass B: Audience Impact / Expressive Readability.
|
|
15
|
+
Each `Proceed` report must use the CLI-enforced structure from `skills/slides-grab-design/references/design-gate.md`: role title, `VERDICT: PASS`, confidence, rendered PNG evidence filenames, current `slide-*.html: <sha256>` fingerprints, `Unresolved Critical: 0`, `Blocking findings: None`, findings table, and all required checks marked `PASS`.
|
|
16
|
+
4. If both passes conclude Proceed, record the gate with:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
slides-grab design-gate --slides-dir <slides-dir> --verdict proceed --pass-a-report <pass-a.md> --pass-b-report <pass-b.md>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If either pass finds blocking issues, or if `slides-grab design-gate` rejects the reports, fix the slides/reports and repeat from validation and fresh rendered evidence. Do not run `slides-grab pdf`, `slides-grab convert`, or `slides-grab figma` until the CLI gate records `proceed`.
|
package/scripts/build-viewer.js
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
import { readFileSync, writeFileSync, readdirSync } from 'fs';
|
|
12
12
|
import { createRequire } from 'node:module';
|
|
13
|
-
import { join, resolve } from 'path';
|
|
14
|
-
import { fileURLToPath } from 'url';
|
|
13
|
+
import { dirname, extname, join, resolve } from 'path';
|
|
14
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
15
15
|
|
|
16
16
|
import { buildSlideRuntimeHtml } from '../src/image-contract.js';
|
|
17
17
|
|
|
@@ -24,6 +24,25 @@ const {
|
|
|
24
24
|
} = require('../src/slide-mode.cjs');
|
|
25
25
|
|
|
26
26
|
const DEFAULT_SLIDES_DIR = 'slides';
|
|
27
|
+
const LOCAL_ASSET_PREFIX = './assets/';
|
|
28
|
+
|
|
29
|
+
// Deliberate sandbox decision: allow-scripts (so Chart.js and other in-slide JS
|
|
30
|
+
// render in the viewer) WITHOUT allow-same-origin. Omitting allow-same-origin gives
|
|
31
|
+
// each iframe an opaque origin so first-party slide content cannot reach the parent
|
|
32
|
+
// viewer document. Because relative URLs break under an opaque origin, local
|
|
33
|
+
// ./assets/ references are inlined as data: URLs in loadSlides()/inlineLocalAssetsForSrcdoc.
|
|
34
|
+
const SLIDE_FRAME_SANDBOX = 'allow-scripts';
|
|
35
|
+
|
|
36
|
+
const MIME_BY_EXTENSION = new Map([
|
|
37
|
+
['.avif', 'image/avif'],
|
|
38
|
+
['.gif', 'image/gif'],
|
|
39
|
+
['.jpg', 'image/jpeg'],
|
|
40
|
+
['.jpeg', 'image/jpeg'],
|
|
41
|
+
['.mp4', 'video/mp4'],
|
|
42
|
+
['.png', 'image/png'],
|
|
43
|
+
['.svg', 'image/svg+xml'],
|
|
44
|
+
['.webp', 'image/webp'],
|
|
45
|
+
]);
|
|
27
46
|
|
|
28
47
|
function printUsage() {
|
|
29
48
|
process.stdout.write(
|
|
@@ -127,18 +146,61 @@ export function escapeForSrcdoc(html) {
|
|
|
127
146
|
}
|
|
128
147
|
|
|
129
148
|
export function loadSlides(slidesDir) {
|
|
149
|
+
const slideBaseHref = pathToFileURL(`${resolve(slidesDir)}/`).href;
|
|
130
150
|
return findSlideFiles(slidesDir).map((file) => {
|
|
131
|
-
const
|
|
151
|
+
const slidePath = join(slidesDir, file);
|
|
152
|
+
const html = inlineLocalAssetsForSrcdoc(readFileSync(slidePath, 'utf-8'), slidePath);
|
|
132
153
|
return {
|
|
133
154
|
file,
|
|
134
155
|
html: buildSlideRuntimeHtml(html, {
|
|
135
|
-
baseHref:
|
|
156
|
+
baseHref: slideBaseHref,
|
|
136
157
|
slideFile: file,
|
|
137
158
|
}),
|
|
138
159
|
};
|
|
139
160
|
});
|
|
140
161
|
}
|
|
141
162
|
|
|
163
|
+
function getMimeType(filePath) {
|
|
164
|
+
return MIME_BY_EXTENSION.get(extname(filePath).toLowerCase()) || 'application/octet-stream';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function toDataUrl(filePath) {
|
|
168
|
+
const bytes = readFileSync(filePath);
|
|
169
|
+
return `data:${getMimeType(filePath)};base64,${bytes.toString('base64')}`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function inlineLocalAssetsForSrcdoc(html, slidePath) {
|
|
173
|
+
const resolveAsset = (source) => {
|
|
174
|
+
if (!source.startsWith(LOCAL_ASSET_PREFIX)) {
|
|
175
|
+
return source;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
return toDataUrl(resolve(dirname(slidePath), source));
|
|
180
|
+
} catch {
|
|
181
|
+
return source;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
return html
|
|
186
|
+
.replace(/\b(src|poster)=("([^"]*)"|'([^']*)')/gi, (match, attribute, quoted, doubleValue, singleValue) => {
|
|
187
|
+
const value = doubleValue ?? singleValue ?? '';
|
|
188
|
+
const nextValue = resolveAsset(value);
|
|
189
|
+
if (nextValue === value) {
|
|
190
|
+
return match;
|
|
191
|
+
}
|
|
192
|
+
const quote = quoted.startsWith("'") ? "'" : '"';
|
|
193
|
+
return `${attribute}=${quote}${nextValue}${quote}`;
|
|
194
|
+
})
|
|
195
|
+
.replace(/url\(\s*(['"]?)(\.\/assets\/[^'")]+)\1\s*\)/gi, (match, quote, source) => {
|
|
196
|
+
const nextValue = resolveAsset(source);
|
|
197
|
+
if (nextValue === source) {
|
|
198
|
+
return match;
|
|
199
|
+
}
|
|
200
|
+
return `url("${nextValue}")`;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
142
204
|
export function buildViewerHtml(slides, { slideMode = DEFAULT_SLIDE_MODE } = {}) {
|
|
143
205
|
const { framePt } = getSlideModeConfig(slideMode);
|
|
144
206
|
|
|
@@ -261,7 +323,7 @@ export function buildViewerHtml(slides, { slideMode = DEFAULT_SLIDE_MODE } = {})
|
|
|
261
323
|
|
|
262
324
|
<div class="slide-viewport" id="viewport">
|
|
263
325
|
<div class="slide-scaler" id="scaler">
|
|
264
|
-
${slides.map((s, i) => ` <iframe class="slide-frame${i === 0 ? ' active' : ''}" data-slide="${i + 1}" srcdoc="${escapeForSrcdoc(s.html)}" sandbox="
|
|
326
|
+
${slides.map((s, i) => ` <iframe class="slide-frame${i === 0 ? ' active' : ''}" data-slide="${i + 1}" srcdoc="${escapeForSrcdoc(s.html)}" sandbox="${SLIDE_FRAME_SANDBOX}"></iframe>`).join('\n')}
|
|
265
327
|
</div>
|
|
266
328
|
</div>
|
|
267
329
|
</div>
|